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