File: owner_settings_service.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (247 lines) | stat: -rw-r--r-- 8,451 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/ownership/owner_settings_service.h"

#include <cryptohi.h>
#include <keyhi.h>
#include <stdint.h>

#include <utility>

#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/task_runner.h"
#include "base/values.h"
#include "components/ownership/owner_key_util.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "crypto/scoped_nss_types.h"

namespace em = enterprise_management;

namespace ownership {

namespace {

using ScopedSGNContext = std::unique_ptr<
    SGNContext,
    crypto::NSSDestroyer1<SGNContext, SGN_DestroyContext, PR_TRUE>>;

crypto::ScopedSECItem SignPolicy(
    const scoped_refptr<ownership::PrivateKey>& private_key,
    base::span<const uint8_t> policy,
    const em::PolicyFetchRequest::SignatureType signature_type) {
  SECOidTag algorithm;
  switch (signature_type) {
    case em::PolicyFetchRequest::SHA1_RSA:
      algorithm = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
      break;
    case em::PolicyFetchRequest::SHA256_RSA:
      algorithm = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
      break;
    case em::PolicyFetchRequest::NONE:
      NOTREACHED();
  }

  crypto::ScopedSECItem sign_result(SECITEM_AllocItem(nullptr, nullptr, 0));

  if (SEC_SignData(sign_result.get(), policy.data(), policy.size(),
                   private_key->key(), algorithm) != SECSuccess) {
    return nullptr;
  }
  return sign_result;
}

// |public_key| is included in the |policy|
// if the ChromeSideOwnerKeyGeneration Feature is enabled. |private_key|
// actually signs the |policy| (must belong to the same key pair as
// |public_key|).
std::unique_ptr<em::PolicyFetchResponse> AssembleAndSignPolicy(
    std::unique_ptr<em::PolicyData> policy,
    scoped_refptr<ownership::PublicKey> public_key,
    scoped_refptr<ownership::PrivateKey> private_key) {
  DCHECK(private_key->key());

  // Assemble the policy.
  std::unique_ptr<em::PolicyFetchResponse> policy_response(
      new em::PolicyFetchResponse());

  policy_response->set_new_public_key(public_key->data().data(),
                                      public_key->data().size());

  if (!policy->SerializeToString(policy_response->mutable_policy_data())) {
    LOG(ERROR) << "Failed to encode policy payload.";
    return nullptr;
  }

  // Retry signature generation several times. At the low level this is
  // performed by Chaps which implements the PKCS#11 interface. It's expected
  // that some operations might fail because PKCS#11 sessions can get closed. In
  // such cases the caller should retry the request. This is not expected to
  // happen too often, 5 attempts should be enough.
  int attempt_counter = 0;
  constexpr int kMaxSignatureAttempts = 5;
  crypto::ScopedSECItem signature_item;

  em::PolicyFetchRequest::SignatureType signature_type =
      base::FeatureList::IsEnabled(ownership::kOwnerSettingsWithSha256)
          ? em::PolicyFetchRequest::SHA256_RSA
          : em::PolicyFetchRequest::SHA1_RSA;
  do {
    ++attempt_counter;
    signature_item = SignPolicy(
        private_key, base::as_byte_span(policy_response->policy_data()),
        signature_type);
  } while (!signature_item && attempt_counter < kMaxSignatureAttempts);

  if (!signature_item) {
    LOG(ERROR) << "Failed to create policy signature.";
    return nullptr;
  }

  policy_response->mutable_policy_data_signature()->assign(
      reinterpret_cast<const char*>(signature_item->data), signature_item->len);
  if (base::FeatureList::IsEnabled(ownership::kOwnerSettingsWithSha256)) {
    policy_response->set_policy_data_signature_type(signature_type);
  }
  return policy_response;
}

}  // namespace

BASE_FEATURE(kOwnerSettingsWithSha256,
             "OwnerSettingsWithSha256",
             base::FEATURE_ENABLED_BY_DEFAULT);

OwnerSettingsService::OwnerSettingsService(
    const scoped_refptr<ownership::OwnerKeyUtil>& owner_key_util)
    : owner_key_util_(owner_key_util) {}

OwnerSettingsService::~OwnerSettingsService() {
  DCHECK(thread_checker_.CalledOnValidThread());
}

void OwnerSettingsService::AddObserver(Observer* observer) {
  if (observer && !observers_.HasObserver(observer))
    observers_.AddObserver(observer);
}

void OwnerSettingsService::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

bool OwnerSettingsService::IsReady() {
  DCHECK(thread_checker_.CalledOnValidThread());
  return private_key_.get();
}

bool OwnerSettingsService::IsOwner() {
  DCHECK(thread_checker_.CalledOnValidThread());
  return private_key_.get() && private_key_->key();
}

void OwnerSettingsService::IsOwnerAsync(IsOwnerCallback callback) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (private_key_.get()) {
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, base::BindOnce(std::move(callback), IsOwner()));
  } else {
    pending_is_owner_callbacks_.push_back(std::move(callback));
  }
}

bool OwnerSettingsService::AssembleAndSignPolicyAsync(
    base::TaskRunner* task_runner,
    std::unique_ptr<em::PolicyData> policy,
    AssembleAndSignPolicyAsyncCallback callback) {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (!task_runner || !IsOwner())
    return false;
  // |public_key_| is explicitly forwarded down to
  // |OwnerSettingsServiceAsh::OnSignedPolicyStored()| to make sure that only
  // the key that was actually included in a policy gets marked as persisted
  // (theoretically a different key can be re-assigned to |public_key_| in
  // between the async calls).
  return task_runner->PostTaskAndReplyWithResult(
      FROM_HERE,
      base::BindOnce(&AssembleAndSignPolicy, std::move(policy), public_key_,
                     private_key_),
      base::BindOnce(std::move(callback), public_key_));
}

bool OwnerSettingsService::SetBoolean(const std::string& setting, bool value) {
  DCHECK(thread_checker_.CalledOnValidThread());
  base::Value in_value(value);
  return Set(setting, in_value);
}

bool OwnerSettingsService::SetInteger(const std::string& setting, int value) {
  DCHECK(thread_checker_.CalledOnValidThread());
  base::Value in_value(value);
  return Set(setting, in_value);
}

bool OwnerSettingsService::SetDouble(const std::string& setting, double value) {
  DCHECK(thread_checker_.CalledOnValidThread());
  base::Value in_value(value);
  return Set(setting, in_value);
}

void OwnerSettingsService::RunPendingIsOwnerCallbacksForTesting(bool is_owner) {
  std::vector<IsOwnerCallback> is_owner_callbacks;
  is_owner_callbacks.swap(pending_is_owner_callbacks_);
  for (auto& callback : is_owner_callbacks)
    std::move(callback).Run(is_owner);
}

void OwnerSettingsService::Shutdown() {
  for (auto& observer : observers_) {
    observer.OnServiceShutdown();
  }
}

bool OwnerSettingsService::SetString(const std::string& setting,
                                     const std::string& value) {
  DCHECK(thread_checker_.CalledOnValidThread());
  base::Value in_value(value);
  return Set(setting, in_value);
}

void OwnerSettingsService::ReloadKeypair() {
  ReloadKeypairImpl(
      base::BindOnce(&OwnerSettingsService::OnKeypairLoaded, as_weak_ptr()));
}

void OwnerSettingsService::OnKeypairLoaded(
    scoped_refptr<PublicKey> public_key,
    scoped_refptr<PrivateKey> private_key) {
  DCHECK(thread_checker_.CalledOnValidThread());

  // The pointers themself should not be null to indicate that the keys finished
  // loading (even if unsuccessfully). Absence of the actual data inside can
  // indicate that the keys are unavailable.
  public_key_ =
      public_key ? public_key
                 : base::MakeRefCounted<ownership::PublicKey>(
                       /*is_persisted=*/false, /*data=*/std::vector<uint8_t>());
  private_key_ = private_key
                     ? private_key
                     : base::MakeRefCounted<ownership::PrivateKey>(nullptr);

  std::vector<IsOwnerCallback> is_owner_callbacks;
  is_owner_callbacks.swap(pending_is_owner_callbacks_);

  const bool is_owner = IsOwner();
  for (auto& callback : is_owner_callbacks)
    std::move(callback).Run(is_owner);

  OnPostKeypairLoadedActions();
}

}  // namespace ownership