#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <CommonCrypto/CommonDigest.h>
#include <AssertMacros.h>
#include "EncodedSourceID.h"
#define ACCOUNT_NAME_MAX_LEN 255
#define ENCODED_ID_MAX_LEN 31
#define SUCCESS 1
#define FAILURE 0
static int GenerateIDString(char *idBuffer, size_t idBufferLen);
static int PrimaryMACAddressFromSystem(char *addressBuffer, size_t addressBufferLen);
static kern_return_t GetPrimaryMACAddress(UInt8 *MACAddress);
static kern_return_t FindPrimaryEthernetInterfaces(io_iterator_t *matchingServices);
int base64Encode(const void *inSourceData, size_t inSourceSize,
void *inEncodedDataBuffer, size_t inEncodedDataBufferSize,
size_t *outEncodedSize);
unsigned char base64EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int GetEncodedSourceID(char encodedIdBuffer[32]) {
char idStr[ACCOUNT_NAME_MAX_LEN + (kIOEthernetAddressSize * 2) + 2];
int status = FAILURE;
CC_MD5_CTX md5_ctx;
size_t b64Len = 0;
int result;
unsigned char MD5Value[CC_MD2_DIGEST_LENGTH];
if (encodedIdBuffer == NULL) return FAILURE;
encodedIdBuffer[0] = '\0';
if (GenerateIDString(idStr, sizeof(idStr))) {
CC_MD5_Init(&md5_ctx);
CC_MD5_Update(&md5_ctx, idStr, (CC_LONG)strlen(idStr));
CC_MD5_Final(MD5Value, &md5_ctx);
result = base64Encode(MD5Value, CC_MD2_DIGEST_LENGTH,
encodedIdBuffer, 32,
&b64Len);
if (result != 0)
goto out;
if (b64Len > 0) {
if (b64Len > ENCODED_ID_MAX_LEN)
encodedIdBuffer[ENCODED_ID_MAX_LEN] = '\0';
else
encodedIdBuffer[b64Len] = '\0';
status = SUCCESS;
}
}
out:
return status;
}
static int GenerateIDString(char *idBuffer, size_t idBufferLen) {
char addressString[(kIOEthernetAddressSize * 2) + 1];
const char *accountName = NULL;
if (idBuffer == NULL) return FAILURE;
idBuffer[0] = '\0';
accountName = getlogin();
if ( (accountName==NULL) || (strlen(accountName)<=0) ) {
return FAILURE;
}
if (PrimaryMACAddressFromSystem(addressString, sizeof(addressString)) == SUCCESS) {
snprintf(idBuffer, idBufferLen, "%s:%s", accountName, addressString);
return SUCCESS;
}
else {
return FAILURE;
}
}
static int PrimaryMACAddressFromSystem(char *addressBuffer, size_t addressBufferLen) {
int status = FAILURE, len = kIOEthernetAddressSize;
UInt8 MACAddress[ kIOEthernetAddressSize ];
kern_return_t kernResult = KERN_FAILURE;
if (addressBuffer == NULL) return status;
kernResult = GetPrimaryMACAddress(MACAddress);
if (KERN_SUCCESS == kernResult) {
char *bufPtr = addressBuffer;
const UInt8 *addrPtr = &MACAddress[0];
while ( len-- ) {
bufPtr += snprintf( bufPtr, addressBufferLen, "%2.2x", *addrPtr++ );
}
if (strlen(addressBuffer) > 0) {
status = SUCCESS;
}
}
return status;
}
static kern_return_t GetPrimaryMACAddress(UInt8 *MACAddress)
{
io_object_t intfService;
io_object_t controllerService;
kern_return_t kernResult = KERN_FAILURE;
io_iterator_t intfIterator;
kernResult = FindPrimaryEthernetInterfaces(&intfIterator);
if (kernResult != KERN_SUCCESS)
return kernResult;
bzero(MACAddress, kIOEthernetAddressSize);
while ((intfService = IOIteratorNext(intfIterator))) {
CFTypeRef MACAddressAsCFData;
kernResult = IORegistryEntryGetParentEntry( intfService,
kIOServicePlane,
&controllerService );
if (kernResult == KERN_SUCCESS) {
MACAddressAsCFData = IORegistryEntryCreateCFProperty( controllerService,
CFSTR(kIOMACAddress),
kCFAllocatorDefault, 0);
if (MACAddressAsCFData) {
CFDataGetBytes(MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress);
CFRelease(MACAddressAsCFData);
}
IOObjectRelease(controllerService);
}
IOObjectRelease(intfService);
}
IOObjectRelease(intfIterator);
return kernResult;
}
static kern_return_t FindPrimaryEthernetInterfaces(io_iterator_t *matchingServices)
{
kern_return_t kernResult;
mach_port_t masterPort;
CFMutableDictionaryRef matchingDict;
CFMutableDictionaryRef propertyMatchDict;
kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
if (kernResult != KERN_SUCCESS) {
return kernResult;
}
matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);
if (matchingDict) {
propertyMatchDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (propertyMatchDict) {
CFDictionarySetValue(propertyMatchDict, CFSTR(kIOPrimaryInterface), kCFBooleanTrue);
CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
CFRelease(propertyMatchDict);
}
}
kernResult = IOServiceGetMatchingServices(masterPort, matchingDict, matchingServices);
return kernResult;
}
int base64Encode(const void *inSourceData, size_t inSourceSize,
void *inEncodedDataBuffer, size_t inEncodedDataBufferSize,
size_t *outEncodedSize)
{
int err;
const unsigned char *src;
const unsigned char *end;
unsigned char *dst;
size_t encodedSize;
src = (const unsigned char *) inSourceData;
end = src + inSourceSize;
dst = (unsigned char *) inEncodedDataBuffer;
err = 0;
encodedSize = ( ( inSourceSize + 2 ) / 3 ) * 4;
require_action_quiet( encodedSize <= inEncodedDataBufferSize, exit, err = EINVAL );
while( ( end - src ) >= 3 )
{
dst[ 0 ] = base64EncodeTable[ src[ 0 ] >> 2 ];
dst[ 1 ] = base64EncodeTable[ ( ( src[ 0 ] & 0x03 ) << 4 ) + ( src[ 1 ] >> 4 ) ];
dst[ 2 ] = base64EncodeTable[ ( ( src[ 1 ] & 0x0F ) << 2 ) + ( src[ 2 ] >> 6 ) ];
dst[ 3 ] = base64EncodeTable[ src[ 2 ] & 0x3F ];
src += 3;
dst += 4;
}
switch( end - src )
{
case 1:
dst[ 0 ] = base64EncodeTable[ src[ 0 ] >> 2 ];
dst[ 1 ] = base64EncodeTable[ ( src[ 0 ] & 0x03 ) << 4 ];
dst[ 2 ] = '=';
dst[ 3 ] = '=';
dst += 4;
break;
case 2:
dst[ 0 ] = base64EncodeTable[ src[ 0 ] >> 2 ];
dst[ 1 ] = base64EncodeTable[ ( ( src[ 0 ] & 0x03 ) << 4 ) + ( src[ 1 ] >> 4 ) ];
dst[ 2 ] = base64EncodeTable[ ( src[ 1 ] & 0x0F ) << 2 ];
dst[ 3 ] = '=';
dst += 4;
break;
default:
break;
}
exit:
if( outEncodedSize )
*outEncodedSize = (size_t)( dst - ( (unsigned char *) inEncodedDataBuffer ) );
return( err );
}