device_id_fetcher.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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/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 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( 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 57DeviceIDFetcher::DeviceIDFetcher(int render_process_id) 58 : in_progress_(false), render_process_id_(render_process_id) { 59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 60} 61 62DeviceIDFetcher::~DeviceIDFetcher() {} 63 64bool 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 81void 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 91base::FilePath DeviceIDFetcher::GetLegacyDeviceIDPath( 92 const base::FilePath& profile_path) { 93 return profile_path.AppendASCII(kDRMIdentifierFile); 94} 95 96void 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 139void 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. 180void 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 202void 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