// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include // Note: The original machine_id_mac.cc code is in namespace rlz_lib below. // It depends on some external files, which would bring in a log of Chromium // code if imported as well. // Instead only the necessary code has been extracted from the relevant files, // and further combined and reduced to limit the maintenance burden. // [Extracted from base/logging.h] #define DCHECK assert namespace base { // [Extracted from base/mac/scoped_typeref.h and base/mac/scoped_cftyperef.h] template class ScopedCFTypeRef { public: typedef T element_type; explicit ScopedCFTypeRef(T object) : object_(object) { } ScopedCFTypeRef(const ScopedCFTypeRef& that) = delete; ScopedCFTypeRef(ScopedCFTypeRef&& that) = delete; ~ScopedCFTypeRef() { if (object_) CFRelease(object_); } ScopedCFTypeRef& operator=(const ScopedCFTypeRef& that) = delete; ScopedCFTypeRef& operator=(ScopedCFTypeRef&& that) = delete; operator T() const { return object_; } // ScopedCFTypeRef<>::release() is like scoped_ptr<>::release. It is NOT // a wrapper for CFRelease(). T release() { T temp = object_; object_ = NULL; return temp; } private: T object_; }; namespace mac { // [Extracted from base/mac/scoped_ioobject.h] // Just like ScopedCFTypeRef but for io_object_t and subclasses. template class ScopedIOObject { public: typedef IOT element_type; explicit ScopedIOObject(IOT object = IO_OBJECT_NULL) : object_(object) { } ~ScopedIOObject() { if (object_) IOObjectRelease(object_); } ScopedIOObject(const ScopedIOObject&) = delete; void operator=(const ScopedIOObject&) = delete; void reset(IOT object = IO_OBJECT_NULL) { if (object_) IOObjectRelease(object_); object_ = object; } operator IOT() const { return object_; } private: IOT object_; }; // [Extracted from base/mac/foundation_util.h] template T CFCast(const CFTypeRef& cf_val); template<> CFDataRef CFCast(const CFTypeRef& cf_val) { if (cf_val == NULL) { return NULL; } if (CFGetTypeID(cf_val) == CFDataGetTypeID()) { return (CFDataRef)(cf_val); } return NULL; } template<> CFStringRef CFCast(const CFTypeRef& cf_val) { if (cf_val == NULL) { return NULL; } if (CFGetTypeID(cf_val) == CFStringGetTypeID()) { return (CFStringRef)(cf_val); } return NULL; } } // namespace mac // [Extracted from base/strings/sys_string_conversions_mac.mm] static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8; template static StringType CFStringToSTLStringWithEncodingT(CFStringRef cfstring, CFStringEncoding encoding) { CFIndex length = CFStringGetLength(cfstring); if (length == 0) return StringType(); CFRange whole_string = CFRangeMake(0, length); CFIndex out_size; CFIndex converted = CFStringGetBytes(cfstring, whole_string, encoding, 0, // lossByte false, // isExternalRepresentation NULL, // buffer 0, // maxBufLen &out_size); if (converted == 0 || out_size == 0) return StringType(); // out_size is the number of UInt8-sized units needed in the destination. // A buffer allocated as UInt8 units might not be properly aligned to // contain elements of StringType::value_type. Use a container for the // proper value_type, and convert out_size by figuring the number of // value_type elements per UInt8. Leave room for a NUL terminator. typename StringType::size_type elements = out_size * sizeof(UInt8) / sizeof(typename StringType::value_type) + 1; std::vector out_buffer(elements); converted = CFStringGetBytes(cfstring, whole_string, encoding, 0, // lossByte false, // isExternalRepresentation reinterpret_cast(&out_buffer[0]), out_size, NULL); // usedBufLen if (converted == 0) return StringType(); out_buffer[elements - 1] = '\0'; return StringType(&out_buffer[0], elements - 1); } std::string SysCFStringRefToUTF8(CFStringRef ref) { return CFStringToSTLStringWithEncodingT(ref, kNarrowStringEncoding); } } // namespace base namespace rlz_lib { namespace { // See http://developer.apple.com/library/mac/#technotes/tn1103/_index.html // The caller is responsible for freeing |matching_services|. bool FindEthernetInterfaces(io_iterator_t* matching_services) { base::ScopedCFTypeRef matching_dict( IOServiceMatching(kIOEthernetInterfaceClass)); if (!matching_dict) return false; base::ScopedCFTypeRef primary_interface( CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); if (!primary_interface) return false; CFDictionarySetValue( primary_interface, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); CFDictionarySetValue( matching_dict, CFSTR(kIOPropertyMatchKey), primary_interface); kern_return_t kern_result = IOServiceGetMatchingServices( kIOMasterPortDefault, matching_dict.release(), matching_services); return kern_result == KERN_SUCCESS; } bool GetMACAddressFromIterator(io_iterator_t primary_interface_iterator, uint8_t* buffer, size_t buffer_size) { if (buffer_size < kIOEthernetAddressSize) return false; bool success = false; bzero(buffer, buffer_size); base::mac::ScopedIOObject primary_interface; for (primary_interface.reset(IOIteratorNext(primary_interface_iterator)); primary_interface; primary_interface.reset(IOIteratorNext(primary_interface_iterator))) { io_object_t primary_interface_parent; kern_return_t kern_result = IORegistryEntryGetParentEntry( primary_interface, kIOServicePlane, &primary_interface_parent); base::mac::ScopedIOObject primary_interface_parent_deleter( primary_interface_parent); success = kern_result == KERN_SUCCESS; if (!success) continue; base::ScopedCFTypeRef mac_data( IORegistryEntryCreateCFProperty(primary_interface_parent, CFSTR(kIOMACAddress), kCFAllocatorDefault, 0)); CFDataRef mac_data_data = base::mac::CFCast(mac_data); if (mac_data_data) { CFDataGetBytes( mac_data_data, CFRangeMake(0, kIOEthernetAddressSize), buffer); } } return success; } bool GetMacAddress(unsigned char* buffer, size_t size) { io_iterator_t primary_interface_iterator; if (!FindEthernetInterfaces(&primary_interface_iterator)) return false; bool result = GetMACAddressFromIterator( primary_interface_iterator, buffer, size); IOObjectRelease(primary_interface_iterator); return result; } CFStringRef CopySerialNumber() { base::mac::ScopedIOObject expert_device( IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"))); if (!expert_device) return NULL; base::ScopedCFTypeRef serial_number( IORegistryEntryCreateCFProperty(expert_device, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)); CFStringRef serial_number_cfstring = base::mac::CFCast(serial_number.release()); if (!serial_number_cfstring) return NULL; return serial_number_cfstring; } } // namespace bool GetRawMachineId(std::vector* data, int* more_data) { uint8_t mac_address[kIOEthernetAddressSize]; std::string id; if (GetMacAddress(mac_address, sizeof(mac_address))) { id += "mac:"; static const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; for (int i = 0; i < kIOEthernetAddressSize; ++i) { uint8_t byte = mac_address[i]; id += hex[byte >> 4]; id += hex[byte & 0xF]; } } // A MAC address is enough to uniquely identify a machine, but it's only 6 // bytes, 3 of which are manufacturer-determined. To make brute-forcing the // SHA1 of this harder, also append the system's serial number. CFStringRef serial = CopySerialNumber(); if (serial) { if (!id.empty()) { id += ' '; } id += "serial:"; id += base::SysCFStringRefToUTF8(serial); CFRelease(serial); } // Get the contents of the string 'id' as a bunch of bytes. data->assign(&id[0], &id[id.size()]); // On windows, this is set to the volume id. Since it's not scrambled before // being sent, just set it to 1. *more_data = 1; return true; } } // namespace rlz_lib