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 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 = 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 = 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