1// Copyright 2014 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/chromeos/ownership/owner_settings_service_chromeos.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/callback.h"
12#include "base/command_line.h"
13#include "base/prefs/pref_service.h"
14#include "base/threading/thread_checker.h"
15#include "chrome/browser/chrome_notification_types.h"
16#include "chrome/browser/chromeos/profiles/profile_helper.h"
17#include "chrome/browser/chromeos/settings/cros_settings.h"
18#include "chrome/browser/chromeos/settings/session_manager_operation.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chromeos/dbus/dbus_thread_manager.h"
21#include "chromeos/tpm_token_loader.h"
22#include "components/ownership/owner_key_util.h"
23#include "components/policy/core/common/cloud/cloud_policy_constants.h"
24#include "content/public/browser/browser_thread.h"
25#include "content/public/browser/notification_details.h"
26#include "content/public/browser/notification_service.h"
27#include "content/public/browser/notification_source.h"
28#include "content/public/common/content_switches.h"
29#include "crypto/nss_util.h"
30#include "crypto/nss_util_internal.h"
31#include "crypto/rsa_private_key.h"
32#include "crypto/scoped_nss_types.h"
33#include "crypto/signature_creator.h"
34
35namespace em = enterprise_management;
36
37using content::BrowserThread;
38using ownership::OwnerKeyUtil;
39using ownership::PrivateKey;
40using ownership::PublicKey;
41
42namespace chromeos {
43
44namespace {
45
46DeviceSettingsService* g_device_settings_service_for_testing = NULL;
47
48bool IsOwnerInTests(const std::string& user_id) {
49  if (user_id.empty() ||
50      !CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestType) ||
51      !CrosSettings::IsInitialized()) {
52    return false;
53  }
54  const base::Value* value = CrosSettings::Get()->GetPref(kDeviceOwner);
55  if (!value || value->GetType() != base::Value::TYPE_STRING)
56    return false;
57  return static_cast<const base::StringValue*>(value)->GetString() == user_id;
58}
59
60void LoadPrivateKeyByPublicKey(
61    const scoped_refptr<OwnerKeyUtil>& owner_key_util,
62    scoped_refptr<PublicKey> public_key,
63    const std::string& username_hash,
64    const base::Callback<void(const scoped_refptr<PublicKey>& public_key,
65                              const scoped_refptr<PrivateKey>& private_key)>&
66        callback) {
67  crypto::EnsureNSSInit();
68  crypto::ScopedPK11Slot slot =
69      crypto::GetPublicSlotForChromeOSUser(username_hash);
70  scoped_refptr<PrivateKey> private_key(new PrivateKey(
71      owner_key_util->FindPrivateKeyInSlot(public_key->data(), slot.get())));
72  BrowserThread::PostTask(BrowserThread::UI,
73                          FROM_HERE,
74                          base::Bind(callback, public_key, private_key));
75}
76
77void LoadPrivateKey(
78    const scoped_refptr<OwnerKeyUtil>& owner_key_util,
79    const std::string username_hash,
80    const base::Callback<void(const scoped_refptr<PublicKey>& public_key,
81                              const scoped_refptr<PrivateKey>& private_key)>&
82        callback) {
83  std::vector<uint8> public_key_data;
84  scoped_refptr<PublicKey> public_key;
85  if (!owner_key_util->ImportPublicKey(&public_key_data)) {
86    scoped_refptr<PrivateKey> private_key;
87    BrowserThread::PostTask(BrowserThread::UI,
88                            FROM_HERE,
89                            base::Bind(callback, public_key, private_key));
90    return;
91  }
92  public_key = new PublicKey();
93  public_key->data().swap(public_key_data);
94  bool rv = BrowserThread::PostTask(BrowserThread::IO,
95                                    FROM_HERE,
96                                    base::Bind(&LoadPrivateKeyByPublicKey,
97                                               owner_key_util,
98                                               public_key,
99                                               username_hash,
100                                               callback));
101  if (!rv) {
102    // IO thread doesn't exists in unit tests, but it's safe to use NSS from
103    // BlockingPool in unit tests.
104    LoadPrivateKeyByPublicKey(
105        owner_key_util, public_key, username_hash, callback);
106  }
107}
108
109bool DoesPrivateKeyExistAsyncHelper(
110    const scoped_refptr<OwnerKeyUtil>& owner_key_util) {
111  std::vector<uint8> public_key;
112  if (!owner_key_util->ImportPublicKey(&public_key))
113    return false;
114  scoped_ptr<crypto::RSAPrivateKey> key(
115      crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key));
116  bool is_owner = key.get() != NULL;
117  return is_owner;
118}
119
120// Checks whether NSS slots with private key are mounted or
121// not. Responds via |callback|.
122void DoesPrivateKeyExistAsync(
123    const scoped_refptr<OwnerKeyUtil>& owner_key_util,
124    const OwnerSettingsServiceChromeOS::IsOwnerCallback& callback) {
125  if (!owner_key_util.get()) {
126    callback.Run(false);
127    return;
128  }
129  scoped_refptr<base::TaskRunner> task_runner =
130      BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
131          base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
132  base::PostTaskAndReplyWithResult(
133      task_runner.get(),
134      FROM_HERE,
135      base::Bind(&DoesPrivateKeyExistAsyncHelper, owner_key_util),
136      callback);
137}
138
139DeviceSettingsService* GetDeviceSettingsService() {
140  if (g_device_settings_service_for_testing)
141    return g_device_settings_service_for_testing;
142  return DeviceSettingsService::IsInitialized() ? DeviceSettingsService::Get()
143                                                : NULL;
144}
145
146}  // namespace
147
148OwnerSettingsServiceChromeOS::OwnerSettingsServiceChromeOS(
149    Profile* profile,
150    const scoped_refptr<OwnerKeyUtil>& owner_key_util)
151    : ownership::OwnerSettingsService(owner_key_util),
152      profile_(profile),
153      waiting_for_profile_creation_(true),
154      waiting_for_tpm_token_(true),
155      weak_factory_(this) {
156  if (TPMTokenLoader::IsInitialized()) {
157    TPMTokenLoader::TPMTokenStatus tpm_token_status =
158        TPMTokenLoader::Get()->IsTPMTokenEnabled(
159            base::Bind(&OwnerSettingsServiceChromeOS::OnTPMTokenReady,
160                       weak_factory_.GetWeakPtr()));
161    waiting_for_tpm_token_ =
162        tpm_token_status == TPMTokenLoader::TPM_TOKEN_STATUS_UNDETERMINED;
163  }
164
165  if (DBusThreadManager::IsInitialized() &&
166      DBusThreadManager::Get()->GetSessionManagerClient()) {
167    DBusThreadManager::Get()->GetSessionManagerClient()->AddObserver(this);
168  }
169
170  registrar_.Add(this,
171                 chrome::NOTIFICATION_PROFILE_CREATED,
172                 content::Source<Profile>(profile_));
173}
174
175OwnerSettingsServiceChromeOS::~OwnerSettingsServiceChromeOS() {
176  DCHECK(thread_checker_.CalledOnValidThread());
177  if (DBusThreadManager::IsInitialized() &&
178      DBusThreadManager::Get()->GetSessionManagerClient()) {
179    DBusThreadManager::Get()->GetSessionManagerClient()->RemoveObserver(this);
180  }
181}
182
183void OwnerSettingsServiceChromeOS::OnTPMTokenReady(
184    bool /* tpm_token_enabled */) {
185  DCHECK(thread_checker_.CalledOnValidThread());
186  waiting_for_tpm_token_ = false;
187
188  // TPMTokenLoader initializes the TPM and NSS database which is necessary to
189  // determine ownership. Force a reload once we know these are initialized.
190  ReloadKeypair();
191}
192
193void OwnerSettingsServiceChromeOS::SignAndStorePolicyAsync(
194    scoped_ptr<em::PolicyData> policy,
195    const base::Closure& callback) {
196  DCHECK(thread_checker_.CalledOnValidThread());
197  SignAndStoreSettingsOperation* operation = new SignAndStoreSettingsOperation(
198      base::Bind(&OwnerSettingsServiceChromeOS::HandleCompletedOperation,
199                 weak_factory_.GetWeakPtr(),
200                 callback),
201      policy.Pass());
202  operation->set_owner_settings_service(weak_factory_.GetWeakPtr());
203  pending_operations_.push_back(operation);
204  if (pending_operations_.front() == operation)
205    StartNextOperation();
206}
207
208void OwnerSettingsServiceChromeOS::Observe(
209    int type,
210    const content::NotificationSource& source,
211    const content::NotificationDetails& details) {
212  DCHECK(thread_checker_.CalledOnValidThread());
213  if (type != chrome::NOTIFICATION_PROFILE_CREATED) {
214    NOTREACHED();
215    return;
216  }
217
218  Profile* profile = content::Source<Profile>(source).ptr();
219  if (profile != profile_) {
220    NOTREACHED();
221    return;
222  }
223
224  waiting_for_profile_creation_ = false;
225  ReloadKeypair();
226}
227
228void OwnerSettingsServiceChromeOS::OwnerKeySet(bool success) {
229  DCHECK(thread_checker_.CalledOnValidThread());
230  if (success)
231    ReloadKeypair();
232}
233
234// static
235void OwnerSettingsServiceChromeOS::IsOwnerForSafeModeAsync(
236    const std::string& user_hash,
237    const scoped_refptr<OwnerKeyUtil>& owner_key_util,
238    const IsOwnerCallback& callback) {
239  CHECK(chromeos::LoginState::Get()->IsInSafeMode());
240
241  // Make sure NSS is initialized and NSS DB is loaded for the user before
242  // searching for the owner key.
243  BrowserThread::PostTaskAndReply(
244      BrowserThread::IO,
245      FROM_HERE,
246      base::Bind(base::IgnoreResult(&crypto::InitializeNSSForChromeOSUser),
247                 user_hash,
248                 ProfileHelper::GetProfilePathByUserIdHash(user_hash)),
249      base::Bind(&DoesPrivateKeyExistAsync, owner_key_util, callback));
250}
251
252// static
253void OwnerSettingsServiceChromeOS::SetDeviceSettingsServiceForTesting(
254    DeviceSettingsService* device_settings_service) {
255  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256  g_device_settings_service_for_testing = device_settings_service;
257}
258
259void OwnerSettingsServiceChromeOS::OnPostKeypairLoadedActions() {
260  DCHECK(thread_checker_.CalledOnValidThread());
261
262  user_id_ = profile_->GetProfileName();
263  const bool is_owner = IsOwner() || IsOwnerInTests(user_id_);
264  if (is_owner && GetDeviceSettingsService())
265    GetDeviceSettingsService()->InitOwner(user_id_, weak_factory_.GetWeakPtr());
266}
267
268void OwnerSettingsServiceChromeOS::ReloadKeypairImpl(const base::Callback<
269    void(const scoped_refptr<PublicKey>& public_key,
270         const scoped_refptr<PrivateKey>& private_key)>& callback) {
271  DCHECK(thread_checker_.CalledOnValidThread());
272
273  if (waiting_for_profile_creation_ || waiting_for_tpm_token_)
274    return;
275  scoped_refptr<base::TaskRunner> task_runner =
276      BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
277          base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
278  task_runner->PostTask(
279      FROM_HERE,
280      base::Bind(&LoadPrivateKey,
281                 owner_key_util_,
282                 ProfileHelper::GetUserIdHashFromProfile(profile_),
283                 callback));
284}
285
286void OwnerSettingsServiceChromeOS::StartNextOperation() {
287  DeviceSettingsService* service = GetDeviceSettingsService();
288  if (!pending_operations_.empty() && service &&
289      service->session_manager_client()) {
290    pending_operations_.front()->Start(
291        service->session_manager_client(), owner_key_util_, public_key_);
292  }
293}
294
295void OwnerSettingsServiceChromeOS::HandleCompletedOperation(
296    const base::Closure& callback,
297    SessionManagerOperation* operation,
298    DeviceSettingsService::Status status) {
299  DCHECK_EQ(operation, pending_operations_.front());
300
301  DeviceSettingsService* service = GetDeviceSettingsService();
302  if (status == DeviceSettingsService::STORE_SUCCESS) {
303    service->set_policy_data(operation->policy_data().Pass());
304    service->set_device_settings(operation->device_settings().Pass());
305  }
306
307  if ((operation->public_key().get() && !public_key_.get()) ||
308      (operation->public_key().get() && public_key_.get() &&
309       operation->public_key()->data() != public_key_->data())) {
310    // Public part changed so we need to reload private part too.
311    ReloadKeypair();
312    content::NotificationService::current()->Notify(
313        chrome::NOTIFICATION_OWNERSHIP_STATUS_CHANGED,
314        content::Source<OwnerSettingsServiceChromeOS>(this),
315        content::NotificationService::NoDetails());
316  }
317  service->OnSignAndStoreOperationCompleted(status);
318  if (!callback.is_null())
319    callback.Run();
320
321  pending_operations_.pop_front();
322  delete operation;
323  StartNextOperation();
324}
325
326}  // namespace chromeos
327