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