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