tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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