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