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