root/chrome/browser/prefs/tracked/pref_hash_calculator_helper_win.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. IsValidMacAddress
  2. ProcessAdapterAddress
  3. ProcessInterfaceRow
  4. mac_address
  5. ProcessPhysicalAddress
  6. GetMacAddressFromGetAdaptersAddresses
  7. GetMacAddressFromGetIfTable2
  8. GetLegacyDeviceId

// Copyright 2014 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 "chrome/browser/prefs/tracked/pref_hash_calculator_helper.h"

// Implementation fully duplicated from
// chrome/browser/extensions/api/music_manager_private/device_id_win.cc (see
// pref_hash_calculator_helper_win.h for motivation).

// Note: The order of header includes is important, as we want both pre-Vista
// and post-Vista data structures to be defined, specifically
// PIP_ADAPTER_ADDRESSES and PMIB_IF_ROW2.
#include <winsock2.h>
#include <ws2def.h>
#include <ws2ipdef.h>
#include <iphlpapi.h>

#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/scoped_native_library.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
#include "net/base/net_util.h"

namespace {

bool IsValidMacAddress(const void* bytes, size_t size) {
  const size_t MAC_LENGTH = 6;
  const size_t OUI_LENGTH = 3;
  struct InvalidMacEntry {
      size_t size;
      unsigned char address[MAC_LENGTH];
  };

  // VPN, virtualization, tethering, bluetooth, etc.
  static const InvalidMacEntry kInvalidAddresses[] = {
    // Empty address
    {MAC_LENGTH, {0, 0, 0, 0, 0, 0}},
    // VMware
    {OUI_LENGTH, {0x00, 0x50, 0x56}},
    {OUI_LENGTH, {0x00, 0x05, 0x69}},
    {OUI_LENGTH, {0x00, 0x0c, 0x29}},
    {OUI_LENGTH, {0x00, 0x1c, 0x14}},
    // VirtualBox
    {OUI_LENGTH, {0x08, 0x00, 0x27}},
    // PdaNet
    {MAC_LENGTH, {0x00, 0x26, 0x37, 0xbd, 0x39, 0x42}},
    // Cisco AnyConnect VPN
    {MAC_LENGTH, {0x00, 0x05, 0x9a, 0x3c, 0x7a, 0x00}},
    // Marvell sometimes uses this as a dummy address
    {MAC_LENGTH, {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
    // Apple uses this across machines for Bluetooth ethernet adapters.
    {MAC_LENGTH-1, {0x65, 0x90, 0x07, 0x42, 0xf1}},
    // Juniper uses this for their Virtual Adapter, the other 4 bytes are
    // reassigned at every boot. 00-ff-xx is not assigned to anyone.
    {2, {0x00, 0xff}},
    // T-Mobile Wireless Ethernet
    {MAC_LENGTH, {0x00, 0xa0, 0xc6, 0x00, 0x00, 0x00}},
    // Generic Bluetooth device
    {MAC_LENGTH, {0x00, 0x15, 0x83, 0x3d, 0x0a, 0x57}},
    // RAS Async Adapter
    {MAC_LENGTH, {0x20, 0x41, 0x53, 0x59, 0x4e, 0xff}},
    // Qualcomm USB ethernet adapter
    {MAC_LENGTH, {0x00, 0xa0, 0xc6, 0x00, 0x00, 0x00}},
    // Windows VPN
    {MAC_LENGTH, {0x00, 0x53, 0x45, 0x00, 0x00, 0x00}},
    // Bluetooth
    {MAC_LENGTH, {0x00, 0x1f, 0x81, 0x00, 0x08, 0x30}},
    {MAC_LENGTH, {0x00, 0x1b, 0x10, 0x00, 0x2a, 0xec}},
    {MAC_LENGTH, {0x00, 0x15, 0x83, 0x15, 0xa3, 0x10}},
    {MAC_LENGTH, {0x00, 0x15, 0x83, 0x07, 0xC6, 0x5A}},
    {MAC_LENGTH, {0x00, 0x1f, 0x81, 0x00, 0x02, 0x00}},
    {MAC_LENGTH, {0x00, 0x1f, 0x81, 0x00, 0x02, 0xdd}},
    // Ceton TV tuner
    {MAC_LENGTH, {0x00, 0x22, 0x2c, 0xff, 0xff, 0xff}},
    // Check Point VPN
    {MAC_LENGTH, {0x54, 0x55, 0x43, 0x44, 0x52, 0x09}},
    {MAC_LENGTH, {0x54, 0xEF, 0x14, 0x71, 0xE4, 0x0E}},
    {MAC_LENGTH, {0x54, 0xBA, 0xC6, 0xFF, 0x74, 0x10}},
    // Cisco VPN
    {MAC_LENGTH, {0x00, 0x05, 0x9a, 0x3c, 0x7a, 0x00}},
    // Cisco VPN
    {MAC_LENGTH, {0x00, 0x05, 0x9a, 0x3c, 0x78, 0x00}},
    // Intel USB cell modem
    {MAC_LENGTH, {0x00, 0x1e, 0x10, 0x1f, 0x00, 0x01}},
    // Microsoft tethering
    {MAC_LENGTH, {0x80, 0x00, 0x60, 0x0f, 0xe8, 0x00}},
    // Nortel VPN
    {MAC_LENGTH, {0x44, 0x45, 0x53, 0x54, 0x42, 0x00}},
    // AEP VPN
    {MAC_LENGTH, {0x00, 0x30, 0x70, 0x00, 0x00, 0x01}},
    // Positive VPN
    {MAC_LENGTH, {0x00, 0x02, 0x03, 0x04, 0x05, 0x06}},
    // Bluetooth
    {MAC_LENGTH, {0x00, 0x15, 0x83, 0x0B, 0x13, 0xC0}},
    // Kerio Virtual Network Adapter
    {MAC_LENGTH, {0x44, 0x45, 0x53, 0x54, 0x4f, 0x53}},
    // Sierra Wireless cell modems.
    {OUI_LENGTH, {0x00, 0xA0, 0xD5}},
    // FRITZ!web DSL
    {MAC_LENGTH, {0x00, 0x04, 0x0E, 0xFF, 0xFF, 0xFF}},
    // VirtualPC
    {MAC_LENGTH, {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}},
    // Bluetooth
    {MAC_LENGTH, {0x00, 0x1F, 0x81, 0x00, 0x01, 0x00}},
    {MAC_LENGTH, {0x00, 0x30, 0x91, 0x10, 0x00, 0x26}},
    {MAC_LENGTH, {0x00, 0x25, 0x00, 0x5A, 0xC3, 0xD0}},
    {MAC_LENGTH, {0x00, 0x15, 0x83, 0x0C, 0xBF, 0xEB}},
    // Huawei cell modem
    {MAC_LENGTH, {0x58, 0x2C, 0x80, 0x13, 0x92, 0x63}},
    // Fortinet VPN
    {OUI_LENGTH, {0x00, 0x09, 0x0F}},
    // Realtek
    {MAC_LENGTH, {0x00, 0x00, 0x00, 0x00, 0x00, 0x30}},
    // Other rare dupes.
    {MAC_LENGTH, {0x00, 0x11, 0xf5, 0x0d, 0x8a, 0xe8}}, // Atheros
    {MAC_LENGTH, {0x00, 0x20, 0x07, 0x01, 0x16, 0x06}}, // Atheros
    {MAC_LENGTH, {0x0d, 0x0b, 0x00, 0x00, 0xe0, 0x00}}, // Atheros
    {MAC_LENGTH, {0x90, 0x4c, 0xe5, 0x0b, 0xc8, 0x8e}}, // Atheros
    {MAC_LENGTH, {0x00, 0x1c, 0x23, 0x38, 0x49, 0xa4}}, // Broadcom
    {MAC_LENGTH, {0x00, 0x12, 0x3f, 0x82, 0x7c, 0x32}}, // Broadcom
    {MAC_LENGTH, {0x00, 0x11, 0x11, 0x32, 0xc3, 0x77}}, // Broadcom
    {MAC_LENGTH, {0x00, 0x24, 0xd6, 0xae, 0x3e, 0x39}}, // Microsoft
    {MAC_LENGTH, {0x00, 0x0f, 0xb0, 0x3a, 0xb4, 0x80}}, // Realtek
    {MAC_LENGTH, {0x08, 0x10, 0x74, 0xa1, 0xda, 0x1b}}, // Realtek
    {MAC_LENGTH, {0x00, 0x21, 0x9b, 0x2a, 0x0a, 0x9c}}, // Realtek
  };

  if (size != MAC_LENGTH) {
    return false;
  }

  if (static_cast<const unsigned char *>(bytes)[0] & 0x02) {
    // Locally administered.
    return false;
  }

  // Note: Use ARRAYSIZE_UNSAFE() instead of arraysize() because InvalidMacEntry
  // is declared inside this function.
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInvalidAddresses); ++i) {
    size_t count = kInvalidAddresses[i].size;
    if (memcmp(kInvalidAddresses[i].address, bytes, count) == 0) {
        return false;
    }
  }
  return true;
}

class MacAddressProcessor {
 public:
  MacAddressProcessor() : found_index_(ULONG_MAX) {}

  // Iterate through the interfaces, looking for the valid MAC address with the
  // lowest IfIndex.
  void ProcessAdapterAddress(PIP_ADAPTER_ADDRESSES address) {
    if (address->IfType == IF_TYPE_TUNNEL)
      return;

    ProcessPhysicalAddress(address->IfIndex,
                           address->PhysicalAddress,
                           address->PhysicalAddressLength);
  }

  void ProcessInterfaceRow(const PMIB_IF_ROW2 row) {
    if (row->Type == IF_TYPE_TUNNEL ||
        !row->InterfaceAndOperStatusFlags.HardwareInterface) {
      return;
    }

    ProcessPhysicalAddress(row->InterfaceIndex,
                           row->PhysicalAddress,
                           row->PhysicalAddressLength);
  }

  std::string mac_address() const { return found_mac_address_; }

 private:
  void ProcessPhysicalAddress(NET_IFINDEX index,
                              const void* bytes,
                              size_t size) {
    if (index >= found_index_ || size == 0)
      return;

    if (!IsValidMacAddress(bytes, size))
      return;

    found_mac_address_ = StringToLowerASCII(base::HexEncode(bytes, size));
    found_index_ = index;
  }

  std::string found_mac_address_;
  NET_IFINDEX found_index_;
};

std::string GetMacAddressFromGetAdaptersAddresses() {
  base::ThreadRestrictions::AssertIOAllowed();

  // MS recommends a default size of 15k.
  ULONG bufferSize = 15 * 1024;
  // Disable as much as we can, since all we want is MAC addresses.
  ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER |
                GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_MULTICAST |
                GAA_FLAG_SKIP_UNICAST;
  std::vector<unsigned char> buffer(bufferSize);
  PIP_ADAPTER_ADDRESSES adapterAddresses =
      reinterpret_cast<PIP_ADAPTER_ADDRESSES>(&buffer.front());

  DWORD result = GetAdaptersAddresses(AF_UNSPEC, flags, 0,
                                      adapterAddresses, &bufferSize);
  if (result == ERROR_BUFFER_OVERFLOW) {
    buffer.resize(bufferSize);
    adapterAddresses =
        reinterpret_cast<PIP_ADAPTER_ADDRESSES>(&buffer.front());
    result = GetAdaptersAddresses(AF_UNSPEC, flags, 0,
                                  adapterAddresses, &bufferSize);
  }

  if (result != NO_ERROR) {
    VLOG(ERROR) << "GetAdapatersAddresses failed with error " << result;
    return "";
  }

  MacAddressProcessor processor;
  for (; adapterAddresses != NULL; adapterAddresses = adapterAddresses->Next) {
    processor.ProcessAdapterAddress(adapterAddresses);
  }
  return processor.mac_address();
}

std::string GetMacAddressFromGetIfTable2() {
  base::ThreadRestrictions::AssertIOAllowed();

  // This is available on Vista+ only.
  base::ScopedNativeLibrary library(base::FilePath(L"Iphlpapi.dll"));

  typedef DWORD (NETIOAPI_API_ *GetIfTablePtr)(PMIB_IF_TABLE2*);
  typedef void (NETIOAPI_API_ *FreeMibTablePtr)(PMIB_IF_TABLE2);

  GetIfTablePtr getIfTable = reinterpret_cast<GetIfTablePtr>(
      library.GetFunctionPointer("GetIfTable2"));
  FreeMibTablePtr freeMibTablePtr = reinterpret_cast<FreeMibTablePtr>(
      library.GetFunctionPointer("FreeMibTable"));
  if (getIfTable == NULL || freeMibTablePtr == NULL) {
    VLOG(ERROR) << "Could not get proc addresses for machine identifier.";
    return "";
  }

  PMIB_IF_TABLE2  ifTable = NULL;
  DWORD result = getIfTable(&ifTable);
  if (result != NO_ERROR || ifTable == NULL) {
    VLOG(ERROR) << "GetIfTable failed with error " << result;
    return "";
  }

  MacAddressProcessor processor;
  for (size_t i = 0; i < ifTable->NumEntries; i++) {
    processor.ProcessInterfaceRow(&(ifTable->Table[i]));
  }

  if (ifTable != NULL) {
    freeMibTablePtr(ifTable);
    ifTable = NULL;
  }
  return processor.mac_address();
}

}  // namespace

// Returns the legacy device ID which was used involuntarily to generate hashes
// in M33 (crbug.com/350028). The algorithm to compute the legacy device ID was
// duplicated from the Windows implementation of GetRawDeviceId() inside
// extensions::api::GetDeviceId() for M33 where it originated from. It was
// deemed undesirable to move the code to a common path for re-use in this code
// as GetLegacyDeviceId() is meant to forever reproduce the ID that was
// generated on M33 and shouldn't otherwise evolve with the implementation of
// extensions::api::GetDeviceId() on trunk.
// TODO(gab): Delete this method when the vast majority of the population has
// migrated over to hashes based on the modern ID.
std::string GetLegacyDeviceId(const std::string& modern_device_id) {
  base::ThreadRestrictions::AssertIOAllowed();

  // Just as in extensions::api::GetMacAddressCallback(), return the
  // concatenation of |mac_address| and |modern_device_id|. Return the empty
  // string if either of them is empty.

  if (modern_device_id.empty())
    return std::string();

  // Get MAC address as in extensions::api::GetMacAddressCallback(): prefering
  // GetMacAddressFromGetAdaptersAddresses() over
  // GetMacAddressFromGetIfTable2() if the former can find a valid MAC address.
  std::string mac_address = GetMacAddressFromGetAdaptersAddresses();
  if (mac_address.empty()) {
    mac_address = GetMacAddressFromGetIfTable2();
    if (mac_address.empty())
      return std::string();
  }

  return mac_address + modern_device_id;
}

/* [<][>][^][v][top][bottom][index][help] */