device_id_fetcher.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/browser/renderer_host/pepper/pepper_flash_device_id_host.h"
12#include "chrome/common/pref_names.h"
13#if defined(OS_CHROMEOS)
14#include "chromeos/cryptohome/cryptohome_library.h"
15#endif
16#include "components/user_prefs/pref_registry_syncable.h"
17#include "content/public/browser/browser_context.h"
18#include "content/public/browser/browser_ppapi_host.h"
19#include "content/public/browser/browser_thread.h"
20#include "content/public/browser/render_process_host.h"
21#include "crypto/encryptor.h"
22#include "crypto/random.h"
23#include "crypto/sha2.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
40}  // namespace
41
42DeviceIDFetcher::DeviceIDFetcher(const IDCallback& callback,
43                                 PP_Instance instance)
44    : callback_(callback),
45      instance_(instance) {
46  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
47}
48
49DeviceIDFetcher::~DeviceIDFetcher() {
50}
51
52void DeviceIDFetcher::Start(BrowserPpapiHost* browser_host) {
53  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
54
55  int process_id = 0;
56  int unused = 0;
57  browser_host->GetRenderViewIDsForInstance(instance_,
58                                            &process_id,
59                                            &unused);
60  BrowserThread::PostTask(
61      BrowserThread::UI, FROM_HERE,
62      base::Bind(&DeviceIDFetcher::CheckPrefsOnUIThread, this, process_id));
63}
64
65// static
66void DeviceIDFetcher::RegisterUserPrefs(
67    user_prefs::PrefRegistrySyncable* prefs) {
68  // TODO(wad): Once UI is connected, a final default can be set. At that point
69  // change this pref from UNSYNCABLE to SYNCABLE.
70  prefs->RegisterBooleanPref(prefs::kEnableDRM,
71                             true,
72                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
73  prefs->RegisterStringPref(
74      prefs::kDRMSalt,
75      "",
76      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
77}
78
79// static
80base::FilePath DeviceIDFetcher::GetLegacyDeviceIDPath(
81    const base::FilePath& profile_path) {
82  return profile_path.AppendASCII(kDRMIdentifierFile);
83}
84
85// TODO(raymes): Change this to just return the device id salt and call it with
86// PostTaskAndReply once the legacy ChromeOS codepath is removed.
87void DeviceIDFetcher::CheckPrefsOnUIThread(int process_id) {
88  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
89
90  Profile* profile = NULL;
91  RenderProcessHost* render_process_host =
92      RenderProcessHost::FromID(process_id);
93  if (render_process_host && render_process_host->GetBrowserContext()) {
94    profile = Profile::FromBrowserContext(
95        render_process_host->GetBrowserContext());
96  }
97
98  if (!profile ||
99      profile->IsOffTheRecord() ||
100      !profile->GetPrefs()->GetBoolean(prefs::kEnableDRM)) {
101    RunCallbackOnIOThread(std::string());
102    return;
103  }
104
105  // Check if the salt pref is set. If it isn't, set it.
106  std::string salt = profile->GetPrefs()->GetString(prefs::kDRMSalt);
107  if (salt.empty()) {
108    uint8_t salt_bytes[kSaltLength];
109    crypto::RandBytes(salt_bytes, arraysize(salt_bytes));
110    // Since it will be stored in a string pref, convert it to hex.
111    salt = base::HexEncode(salt_bytes, arraysize(salt_bytes));
112    profile->GetPrefs()->SetString(prefs::kDRMSalt, salt);
113  }
114
115#if defined(OS_CHROMEOS)
116  // Try the legacy path first for ChromeOS. We pass the new salt in as well
117  // in case the legacy id doesn't exist.
118  BrowserThread::PostBlockingPoolTask(FROM_HERE,
119      base::Bind(&DeviceIDFetcher::ComputeOnBlockingPool, this,
120                 profile->GetPath(), salt));
121#else
122  BrowserThread::PostTask(
123      BrowserThread::IO, FROM_HERE,
124      base::Bind(&DeviceIDFetcher::ComputeOnIOThread, this, salt));
125#endif
126}
127
128
129void DeviceIDFetcher::ComputeOnIOThread(const std::string& salt) {
130  std::vector<uint8> salt_bytes;
131  if (!base::HexStringToBytes(salt, &salt_bytes))
132    salt_bytes.clear();
133
134  // Build the identifier as follows:
135  // SHA256(machine-id||service||SHA256(machine-id||service||salt))
136  std::string machine_id = GetMachineID();
137  if (machine_id.empty() || salt_bytes.size() != kSaltLength) {
138    NOTREACHED();
139    RunCallbackOnIOThread(std::string());
140    return;
141  }
142
143  char id_buf[256 / 8];  // 256-bits for SHA256
144  std::string input = machine_id;
145  input.append(kDRMIdentifierFile);
146  input.append(salt_bytes.begin(), salt_bytes.end());
147  crypto::SHA256HashString(input, &id_buf, sizeof(id_buf));
148  std::string id = StringToLowerASCII(
149      base::HexEncode(reinterpret_cast<const void*>(id_buf), sizeof(id_buf)));
150  input = machine_id;
151  input.append(kDRMIdentifierFile);
152  input.append(id);
153  crypto::SHA256HashString(input, &id_buf, sizeof(id_buf));
154  id = StringToLowerASCII(base::HexEncode(
155        reinterpret_cast<const void*>(id_buf),
156        sizeof(id_buf)));
157
158  RunCallbackOnIOThread(id);
159}
160
161// TODO(raymes): This is temporary code to migrate ChromeOS devices to the new
162// scheme for generating device IDs. Delete this once we are sure most ChromeOS
163// devices have been migrated.
164void DeviceIDFetcher::ComputeOnBlockingPool(const base::FilePath& profile_path,
165                                            const std::string& salt) {
166  std::string id;
167  // First check if the legacy device ID file exists on ChromeOS. If it does, we
168  // should just return that.
169  base::FilePath id_path = GetLegacyDeviceIDPath(profile_path);
170  if (file_util::PathExists(id_path)) {
171    if (file_util::ReadFileToString(id_path, &id) && !id.empty()) {
172      RunCallbackOnIOThread(id);
173      return;
174    }
175  }
176  // If we didn't find an ID, go back to the new code path to generate an ID.
177  BrowserThread::PostTask(
178      BrowserThread::IO, FROM_HERE,
179      base::Bind(&DeviceIDFetcher::ComputeOnIOThread, this, salt));
180}
181
182
183void DeviceIDFetcher::RunCallbackOnIOThread(const std::string& id) {
184  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
185    BrowserThread::PostTask(
186        BrowserThread::IO, FROM_HERE,
187        base::Bind(&DeviceIDFetcher::RunCallbackOnIOThread, this, id));
188    return;
189  }
190  callback_.Run(id);
191}
192
193std::string DeviceIDFetcher::GetMachineID() {
194#if defined(OS_WIN) && defined(ENABLE_RLZ)
195  std::string result;
196  rlz_lib::GetMachineId(&result);
197  return result;
198#elif defined(OS_CHROMEOS)
199  chromeos::CryptohomeLibrary* c_home = chromeos::CryptohomeLibrary::Get();
200  return c_home->GetSystemSalt();
201#else
202  // Not implemented for other platforms.
203  NOTREACHED();
204  return "";
205#endif
206}
207
208}  // namespace chrome
209