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