Home | History | Annotate | Download | only in pepper
      1 // Copyright (c) 2013 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 "chrome/browser/renderer_host/pepper/device_id_fetcher.h"
      6 
      7 #include "base/files/file_util.h"
      8 #include "base/prefs/pref_service.h"
      9 #include "base/strings/string_number_conversions.h"
     10 #include "chrome/browser/profiles/profile.h"
     11 #include "chrome/common/pref_names.h"
     12 #if defined(OS_CHROMEOS)
     13 #include "chromeos/cryptohome/system_salt_getter.h"
     14 #endif
     15 #include "components/pref_registry/pref_registry_syncable.h"
     16 #include "content/public/browser/browser_context.h"
     17 #include "content/public/browser/browser_ppapi_host.h"
     18 #include "content/public/browser/browser_thread.h"
     19 #include "content/public/browser/render_process_host.h"
     20 #include "crypto/encryptor.h"
     21 #include "crypto/random.h"
     22 #include "crypto/sha2.h"
     23 #include "ppapi/c/pp_errors.h"
     24 #if defined(ENABLE_RLZ)
     25 #include "rlz/lib/machine_id.h"
     26 #endif
     27 
     28 using content::BrowserPpapiHost;
     29 using content::BrowserThread;
     30 using content::RenderProcessHost;
     31 
     32 namespace chrome {
     33 
     34 namespace {
     35 
     36 const char kDRMIdentifierFile[] = "Pepper DRM ID.0";
     37 
     38 const uint32_t kSaltLength = 32;
     39 
     40 void GetMachineIDAsync(
     41     const base::Callback<void(const std::string&)>& callback) {
     42 #if defined(OS_WIN) && defined(ENABLE_RLZ)
     43   std::string result;
     44   rlz_lib::GetMachineId(&result);
     45   callback.Run(result);
     46 #elif defined(OS_CHROMEOS)
     47   chromeos::SystemSaltGetter::Get()->GetSystemSalt(callback);
     48 #else
     49   // Not implemented for other platforms.
     50   NOTREACHED();
     51   callback.Run(std::string());
     52 #endif
     53 }
     54 
     55 }  // namespace
     56 
     57 DeviceIDFetcher::DeviceIDFetcher(int render_process_id)
     58     : in_progress_(false), render_process_id_(render_process_id) {
     59   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     60 }
     61 
     62 DeviceIDFetcher::~DeviceIDFetcher() {}
     63 
     64 bool DeviceIDFetcher::Start(const IDCallback& callback) {
     65   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     66 
     67   if (in_progress_)
     68     return false;
     69 
     70   in_progress_ = true;
     71   callback_ = callback;
     72 
     73   BrowserThread::PostTask(
     74       BrowserThread::UI,
     75       FROM_HERE,
     76       base::Bind(&DeviceIDFetcher::CheckPrefsOnUIThread, this));
     77   return true;
     78 }
     79 
     80 // static
     81 void DeviceIDFetcher::RegisterProfilePrefs(
     82     user_prefs::PrefRegistrySyncable* prefs) {
     83   prefs->RegisterBooleanPref(prefs::kEnableDRM,
     84                              true,
     85                              user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
     86   prefs->RegisterStringPref(
     87       prefs::kDRMSalt, "", user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
     88 }
     89 
     90 // static
     91 base::FilePath DeviceIDFetcher::GetLegacyDeviceIDPath(
     92     const base::FilePath& profile_path) {
     93   return profile_path.AppendASCII(kDRMIdentifierFile);
     94 }
     95 
     96 void DeviceIDFetcher::CheckPrefsOnUIThread() {
     97   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     98 
     99   Profile* profile = NULL;
    100   RenderProcessHost* render_process_host =
    101       RenderProcessHost::FromID(render_process_id_);
    102   if (render_process_host && render_process_host->GetBrowserContext()) {
    103     profile =
    104         Profile::FromBrowserContext(render_process_host->GetBrowserContext());
    105   }
    106 
    107   if (!profile || profile->IsOffTheRecord() ||
    108       !profile->GetPrefs()->GetBoolean(prefs::kEnableDRM)) {
    109     RunCallbackOnIOThread(std::string(), PP_ERROR_NOACCESS);
    110     return;
    111   }
    112 
    113   // Check if the salt pref is set. If it isn't, set it.
    114   std::string salt = profile->GetPrefs()->GetString(prefs::kDRMSalt);
    115   if (salt.empty()) {
    116     uint8_t salt_bytes[kSaltLength];
    117     crypto::RandBytes(salt_bytes, arraysize(salt_bytes));
    118     // Since it will be stored in a string pref, convert it to hex.
    119     salt = base::HexEncode(salt_bytes, arraysize(salt_bytes));
    120     profile->GetPrefs()->SetString(prefs::kDRMSalt, salt);
    121   }
    122 
    123 #if defined(OS_CHROMEOS)
    124   // Try the legacy path first for ChromeOS. We pass the new salt in as well
    125   // in case the legacy id doesn't exist.
    126   BrowserThread::PostBlockingPoolTask(
    127       FROM_HERE,
    128       base::Bind(&DeviceIDFetcher::LegacyComputeOnBlockingPool,
    129                  this,
    130                  profile->GetPath(),
    131                  salt));
    132 #else
    133   // Get the machine ID and call ComputeOnUIThread with salt + machine_id.
    134   GetMachineIDAsync(
    135       base::Bind(&DeviceIDFetcher::ComputeOnUIThread, this, salt));
    136 #endif
    137 }
    138 
    139 void DeviceIDFetcher::ComputeOnUIThread(const std::string& salt,
    140                                         const std::string& machine_id) {
    141   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    142 
    143   if (machine_id.empty()) {
    144     LOG(ERROR) << "Empty machine id";
    145     RunCallbackOnIOThread(std::string(), PP_ERROR_FAILED);
    146     return;
    147   }
    148 
    149   // Build the identifier as follows:
    150   // SHA256(machine-id||service||SHA256(machine-id||service||salt))
    151   std::vector<uint8> salt_bytes;
    152   if (!base::HexStringToBytes(salt, &salt_bytes))
    153     salt_bytes.clear();
    154   if (salt_bytes.size() != kSaltLength) {
    155     LOG(ERROR) << "Unexpected salt bytes length: " << salt_bytes.size();
    156     RunCallbackOnIOThread(std::string(), PP_ERROR_FAILED);
    157     return;
    158   }
    159 
    160   char id_buf[256 / 8];  // 256-bits for SHA256
    161   std::string input = machine_id;
    162   input.append(kDRMIdentifierFile);
    163   input.append(salt_bytes.begin(), salt_bytes.end());
    164   crypto::SHA256HashString(input, &id_buf, sizeof(id_buf));
    165   std::string id = base::StringToLowerASCII(
    166       base::HexEncode(reinterpret_cast<const void*>(id_buf), sizeof(id_buf)));
    167   input = machine_id;
    168   input.append(kDRMIdentifierFile);
    169   input.append(id);
    170   crypto::SHA256HashString(input, &id_buf, sizeof(id_buf));
    171   id = base::StringToLowerASCII(
    172       base::HexEncode(reinterpret_cast<const void*>(id_buf), sizeof(id_buf)));
    173 
    174   RunCallbackOnIOThread(id, PP_OK);
    175 }
    176 
    177 // TODO(raymes): This is temporary code to migrate ChromeOS devices to the new
    178 // scheme for generating device IDs. Delete this once we are sure most ChromeOS
    179 // devices have been migrated.
    180 void DeviceIDFetcher::LegacyComputeOnBlockingPool(
    181     const base::FilePath& profile_path,
    182     const std::string& salt) {
    183   std::string id;
    184   // First check if the legacy device ID file exists on ChromeOS. If it does, we
    185   // should just return that.
    186   base::FilePath id_path = GetLegacyDeviceIDPath(profile_path);
    187   if (base::PathExists(id_path)) {
    188     if (base::ReadFileToString(id_path, &id) && !id.empty()) {
    189       RunCallbackOnIOThread(id, PP_OK);
    190       return;
    191     }
    192   }
    193   // If we didn't find an ID, get the machine ID and call the new code path to
    194   // generate an ID.
    195   BrowserThread::PostTask(
    196       BrowserThread::UI,
    197       FROM_HERE,
    198       base::Bind(&GetMachineIDAsync,
    199                  base::Bind(&DeviceIDFetcher::ComputeOnUIThread, this, salt)));
    200 }
    201 
    202 void DeviceIDFetcher::RunCallbackOnIOThread(const std::string& id,
    203                                             int32_t result) {
    204   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
    205     BrowserThread::PostTask(
    206         BrowserThread::IO,
    207         FROM_HERE,
    208         base::Bind(&DeviceIDFetcher::RunCallbackOnIOThread, this, id, result));
    209     return;
    210   }
    211   in_progress_ = false;
    212   callback_.Run(id, result);
    213 }
    214 
    215 }  // namespace chrome
    216