machine_id_mac.cc (9961B)
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 #include <stddef.h> 11 #include <stdint.h> 12 13 #include <vector> 14 #include <string> 15 // Note: The original machine_id_mac.cc code is in namespace rlz_lib below. 16 // It depends on some external files, which would bring in a log of Chromium 17 // code if imported as well. 18 // Instead only the necessary code has been extracted from the relevant files, 19 // and further combined and reduced to limit the maintenance burden. 20 21 // [Extracted from base/logging.h] 22 #define DCHECK assert 23 24 namespace base { 25 26 // [Extracted from base/mac/scoped_typeref.h and base/mac/scoped_cftyperef.h] 27 template<typename T> 28 class ScopedCFTypeRef { 29 public: 30 typedef T element_type; 31 32 explicit ScopedCFTypeRef(T object) 33 : object_(object) { 34 } 35 36 ScopedCFTypeRef(const ScopedCFTypeRef<T>& that) = delete; 37 ScopedCFTypeRef(ScopedCFTypeRef<T>&& that) = delete; 38 39 ~ScopedCFTypeRef() { 40 if (object_) 41 CFRelease(object_); 42 } 43 44 ScopedCFTypeRef& operator=(const ScopedCFTypeRef<T>& that) = delete; 45 ScopedCFTypeRef& operator=(ScopedCFTypeRef<T>&& that) = delete; 46 47 operator T() const { 48 return object_; 49 } 50 51 // ScopedCFTypeRef<>::release() is like scoped_ptr<>::release. It is NOT 52 // a wrapper for CFRelease(). 53 T release() { 54 T temp = object_; 55 object_ = NULL; 56 return temp; 57 } 58 59 private: 60 T object_; 61 }; 62 63 namespace mac { 64 65 // [Extracted from base/mac/scoped_ioobject.h] 66 // Just like ScopedCFTypeRef but for io_object_t and subclasses. 67 template<typename IOT> 68 class ScopedIOObject { 69 public: 70 typedef IOT element_type; 71 72 explicit ScopedIOObject(IOT object = IO_OBJECT_NULL) 73 : object_(object) { 74 } 75 76 ~ScopedIOObject() { 77 if (object_) 78 IOObjectRelease(object_); 79 } 80 81 ScopedIOObject(const ScopedIOObject&) = delete; 82 void operator=(const ScopedIOObject&) = delete; 83 84 void reset(IOT object = IO_OBJECT_NULL) { 85 if (object_) 86 IOObjectRelease(object_); 87 object_ = object; 88 } 89 90 operator IOT() const { 91 return object_; 92 } 93 94 private: 95 IOT object_; 96 }; 97 98 // [Extracted from base/mac/foundation_util.h] 99 template<typename T> 100 T CFCast(const CFTypeRef& cf_val); 101 102 template<> 103 CFDataRef 104 CFCast<CFDataRef>(const CFTypeRef& cf_val) { 105 if (cf_val == NULL) { 106 return NULL; 107 } 108 if (CFGetTypeID(cf_val) == CFDataGetTypeID()) { 109 return (CFDataRef)(cf_val); 110 } 111 return NULL; 112 } 113 114 template<> 115 CFStringRef 116 CFCast<CFStringRef>(const CFTypeRef& cf_val) { 117 if (cf_val == NULL) { 118 return NULL; 119 } 120 if (CFGetTypeID(cf_val) == CFStringGetTypeID()) { 121 return (CFStringRef)(cf_val); 122 } 123 return NULL; 124 } 125 126 } // namespace mac 127 128 // [Extracted from base/strings/sys_string_conversions_mac.mm] 129 static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8; 130 131 template<typename StringType> 132 static StringType CFStringToSTLStringWithEncodingT(CFStringRef cfstring, 133 CFStringEncoding encoding) { 134 CFIndex length = CFStringGetLength(cfstring); 135 if (length == 0) 136 return StringType(); 137 138 CFRange whole_string = CFRangeMake(0, length); 139 CFIndex out_size; 140 CFIndex converted = CFStringGetBytes(cfstring, 141 whole_string, 142 encoding, 143 0, // lossByte 144 false, // isExternalRepresentation 145 NULL, // buffer 146 0, // maxBufLen 147 &out_size); 148 if (converted == 0 || out_size == 0) 149 return StringType(); 150 151 // out_size is the number of UInt8-sized units needed in the destination. 152 // A buffer allocated as UInt8 units might not be properly aligned to 153 // contain elements of StringType::value_type. Use a container for the 154 // proper value_type, and convert out_size by figuring the number of 155 // value_type elements per UInt8. Leave room for a NUL terminator. 156 typename StringType::size_type elements = 157 out_size * sizeof(UInt8) / sizeof(typename StringType::value_type) + 1; 158 159 std::vector<typename StringType::value_type> out_buffer(elements); 160 converted = CFStringGetBytes(cfstring, 161 whole_string, 162 encoding, 163 0, // lossByte 164 false, // isExternalRepresentation 165 reinterpret_cast<UInt8*>(&out_buffer[0]), 166 out_size, 167 NULL); // usedBufLen 168 if (converted == 0) 169 return StringType(); 170 171 out_buffer[elements - 1] = '\0'; 172 return StringType(&out_buffer[0], elements - 1); 173 } 174 175 std::string SysCFStringRefToUTF8(CFStringRef ref) 176 { 177 return CFStringToSTLStringWithEncodingT<std::string>(ref, 178 kNarrowStringEncoding); 179 } 180 181 } // namespace base 182 183 namespace rlz_lib { 184 185 namespace { 186 187 // See http://developer.apple.com/library/mac/#technotes/tn1103/_index.html 188 189 // The caller is responsible for freeing |matching_services|. 190 bool FindEthernetInterfaces(io_iterator_t* matching_services) { 191 base::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict( 192 IOServiceMatching(kIOEthernetInterfaceClass)); 193 if (!matching_dict) 194 return false; 195 196 base::ScopedCFTypeRef<CFMutableDictionaryRef> primary_interface( 197 CFDictionaryCreateMutable(kCFAllocatorDefault, 198 0, 199 &kCFTypeDictionaryKeyCallBacks, 200 &kCFTypeDictionaryValueCallBacks)); 201 if (!primary_interface) 202 return false; 203 204 CFDictionarySetValue( 205 primary_interface, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); 206 CFDictionarySetValue( 207 matching_dict, CFSTR(kIOPropertyMatchKey), primary_interface); 208 209 kern_return_t kern_result = IOServiceGetMatchingServices( 210 kIOMasterPortDefault, matching_dict.release(), matching_services); 211 212 return kern_result == KERN_SUCCESS; 213 } 214 215 bool GetMACAddressFromIterator(io_iterator_t primary_interface_iterator, 216 uint8_t* buffer, size_t buffer_size) { 217 if (buffer_size < kIOEthernetAddressSize) 218 return false; 219 220 bool success = false; 221 222 bzero(buffer, buffer_size); 223 base::mac::ScopedIOObject<io_object_t> primary_interface; 224 for (primary_interface.reset(IOIteratorNext(primary_interface_iterator)); 225 primary_interface; 226 primary_interface.reset(IOIteratorNext(primary_interface_iterator))) { 227 io_object_t primary_interface_parent; 228 kern_return_t kern_result = IORegistryEntryGetParentEntry( 229 primary_interface, kIOServicePlane, &primary_interface_parent); 230 base::mac::ScopedIOObject<io_object_t> primary_interface_parent_deleter( 231 primary_interface_parent); 232 success = kern_result == KERN_SUCCESS; 233 234 if (!success) 235 continue; 236 237 base::ScopedCFTypeRef<CFTypeRef> mac_data( 238 IORegistryEntryCreateCFProperty(primary_interface_parent, 239 CFSTR(kIOMACAddress), 240 kCFAllocatorDefault, 241 0)); 242 CFDataRef mac_data_data = base::mac::CFCast<CFDataRef>(mac_data); 243 if (mac_data_data) { 244 CFDataGetBytes( 245 mac_data_data, CFRangeMake(0, kIOEthernetAddressSize), buffer); 246 } 247 } 248 249 return success; 250 } 251 252 bool GetMacAddress(unsigned char* buffer, size_t size) { 253 io_iterator_t primary_interface_iterator; 254 if (!FindEthernetInterfaces(&primary_interface_iterator)) 255 return false; 256 bool result = GetMACAddressFromIterator( 257 primary_interface_iterator, buffer, size); 258 IOObjectRelease(primary_interface_iterator); 259 return result; 260 } 261 262 CFStringRef CopySerialNumber() { 263 base::mac::ScopedIOObject<io_service_t> expert_device( 264 IOServiceGetMatchingService(kIOMasterPortDefault, 265 IOServiceMatching("IOPlatformExpertDevice"))); 266 if (!expert_device) 267 return NULL; 268 269 base::ScopedCFTypeRef<CFTypeRef> serial_number( 270 IORegistryEntryCreateCFProperty(expert_device, 271 CFSTR(kIOPlatformSerialNumberKey), 272 kCFAllocatorDefault, 273 0)); 274 CFStringRef serial_number_cfstring = 275 base::mac::CFCast<CFStringRef>(serial_number.release()); 276 if (!serial_number_cfstring) 277 return NULL; 278 279 return serial_number_cfstring; 280 } 281 282 } // namespace 283 284 bool GetRawMachineId(std::vector<uint8_t>* data, int* more_data) { 285 uint8_t mac_address[kIOEthernetAddressSize]; 286 287 std::string id; 288 if (GetMacAddress(mac_address, sizeof(mac_address))) { 289 id += "mac:"; 290 static const char hex[] = 291 { '0', '1', '2', '3', '4', '5', '6', '7', 292 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 293 for (int i = 0; i < kIOEthernetAddressSize; ++i) { 294 uint8_t byte = mac_address[i]; 295 id += hex[byte >> 4]; 296 id += hex[byte & 0xF]; 297 } 298 } 299 300 // A MAC address is enough to uniquely identify a machine, but it's only 6 301 // bytes, 3 of which are manufacturer-determined. To make brute-forcing the 302 // SHA1 of this harder, also append the system's serial number. 303 CFStringRef serial = CopySerialNumber(); 304 if (serial) { 305 if (!id.empty()) { 306 id += ' '; 307 } 308 id += "serial:"; 309 id += base::SysCFStringRefToUTF8(serial); 310 CFRelease(serial); 311 } 312 313 // Get the contents of the string 'id' as a bunch of bytes. 314 data->assign(&id[0], &id[id.size()]); 315 316 // On windows, this is set to the volume id. Since it's not scrambled before 317 // being sent, just set it to 1. 318 *more_data = 1; 319 return true; 320 } 321 322 } // namespace rlz_lib