1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <CoreFoundation/CoreFoundation.h> 6 #include <IOKit/IOKitLib.h> 7 #include <IOKit/network/IOEthernetController.h> 8 #include <IOKit/network/IOEthernetInterface.h> 9 #include <IOKit/network/IONetworkInterface.h> 10 11 #include "base/logging.h" 12 #include "base/mac/foundation_util.h" 13 #include "base/mac/scoped_cftyperef.h" 14 #include "base/mac/scoped_ioobject.h" 15 #include "base/strings/string16.h" 16 #include "base/strings/stringprintf.h" 17 #include "base/strings/sys_string_conversions.h" 18 #include "base/strings/utf_string_conversions.h" 19 20 namespace rlz_lib { 21 22 namespace { 23 24 // See http://developer.apple.com/library/mac/#technotes/tn1103/_index.html 25 26 // The caller is responsible for freeing |matching_services|. 27 bool FindEthernetInterfaces(io_iterator_t* matching_services) { 28 base::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict( 29 IOServiceMatching(kIOEthernetInterfaceClass)); 30 if (!matching_dict) 31 return false; 32 33 base::ScopedCFTypeRef<CFMutableDictionaryRef> primary_interface( 34 CFDictionaryCreateMutable(kCFAllocatorDefault, 35 0, 36 &kCFTypeDictionaryKeyCallBacks, 37 &kCFTypeDictionaryValueCallBacks)); 38 if (!primary_interface) 39 return false; 40 41 CFDictionarySetValue( 42 primary_interface, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); 43 CFDictionarySetValue( 44 matching_dict, CFSTR(kIOPropertyMatchKey), primary_interface); 45 46 kern_return_t kern_result = IOServiceGetMatchingServices( 47 kIOMasterPortDefault, matching_dict.release(), matching_services); 48 49 return kern_result == KERN_SUCCESS; 50 } 51 52 bool GetMACAddressFromIterator(io_iterator_t primary_interface_iterator, 53 uint8_t* buffer, size_t buffer_size) { 54 if (buffer_size < kIOEthernetAddressSize) 55 return false; 56 57 bool success = false; 58 59 bzero(buffer, buffer_size); 60 base::mac::ScopedIOObject<io_object_t> primary_interface; 61 while (primary_interface.reset(IOIteratorNext(primary_interface_iterator)), 62 primary_interface) { 63 io_object_t primary_interface_parent; 64 kern_return_t kern_result = IORegistryEntryGetParentEntry( 65 primary_interface, kIOServicePlane, &primary_interface_parent); 66 base::mac::ScopedIOObject<io_object_t> primary_interface_parent_deleter( 67 primary_interface_parent); 68 success = kern_result == KERN_SUCCESS; 69 70 if (!success) 71 continue; 72 73 base::ScopedCFTypeRef<CFTypeRef> mac_data( 74 IORegistryEntryCreateCFProperty(primary_interface_parent, 75 CFSTR(kIOMACAddress), 76 kCFAllocatorDefault, 77 0)); 78 CFDataRef mac_data_data = base::mac::CFCast<CFDataRef>(mac_data); 79 if (mac_data_data) { 80 CFDataGetBytes( 81 mac_data_data, CFRangeMake(0, kIOEthernetAddressSize), buffer); 82 } 83 } 84 85 return success; 86 } 87 88 bool GetMacAddress(unsigned char* buffer, size_t size) { 89 io_iterator_t primary_interface_iterator; 90 if (!FindEthernetInterfaces(&primary_interface_iterator)) 91 return false; 92 bool result = GetMACAddressFromIterator( 93 primary_interface_iterator, buffer, size); 94 IOObjectRelease(primary_interface_iterator); 95 return result; 96 } 97 98 CFStringRef CopySerialNumber() { 99 base::mac::ScopedIOObject<io_service_t> expert_device( 100 IOServiceGetMatchingService(kIOMasterPortDefault, 101 IOServiceMatching("IOPlatformExpertDevice"))); 102 if (!expert_device) 103 return NULL; 104 105 base::ScopedCFTypeRef<CFTypeRef> serial_number( 106 IORegistryEntryCreateCFProperty(expert_device, 107 CFSTR(kIOPlatformSerialNumberKey), 108 kCFAllocatorDefault, 109 0)); 110 CFStringRef serial_number_cfstring = 111 base::mac::CFCast<CFStringRef>(serial_number); 112 if (!serial_number_cfstring) 113 return NULL; 114 115 ignore_result(serial_number.release()); 116 return serial_number_cfstring; 117 } 118 119 } // namespace 120 121 bool GetRawMachineId(base::string16* data, int* more_data) { 122 uint8_t mac_address[kIOEthernetAddressSize]; 123 124 data->clear(); 125 if (GetMacAddress(mac_address, sizeof(mac_address))) { 126 *data += base::ASCIIToUTF16( 127 base::StringPrintf("mac:%02x%02x%02x%02x%02x%02x", 128 mac_address[0], mac_address[1], mac_address[2], 129 mac_address[3], mac_address[4], mac_address[5])); 130 } 131 132 // A MAC address is enough to uniquely identify a machine, but it's only 6 133 // bytes, 3 of which are manufacturer-determined. To make brute-forcing the 134 // SHA1 of this harder, also append the system's serial number. 135 CFStringRef serial = CopySerialNumber(); 136 if (serial) { 137 if (!data->empty()) 138 *data += base::UTF8ToUTF16(" "); 139 *data += base::UTF8ToUTF16("serial:") + base::SysCFStringRefToUTF16(serial); 140 CFRelease(serial); 141 } 142 143 // On windows, this is set to the volume id. Since it's not scrambled before 144 // being sent, just set it to 1. 145 *more_data = 1; 146 return true; 147 } 148 149 } // namespace rlz_lib 150