Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(178)

Side by Side Diff: chrome/browser/chromeos/platform_keys/platform_keys_service.cc

Issue 905523002: platformKeys: Add per-extension sign permissions. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@pks_sign_task
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h" 5 #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h"
6 6
7 #include "base/base64.h" 7 #include "base/base64.h"
8 #include "base/callback.h" 8 #include "base/callback.h"
9 #include "base/callback_helpers.h" 9 #include "base/callback_helpers.h"
10 #include "base/values.h" 10 #include "base/values.h"
11 #include "chrome/browser/chromeos/platform_keys/platform_keys.h" 11 #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
12 #include "content/public/browser/browser_thread.h" 12 #include "content/public/browser/browser_thread.h"
13 #include "extensions/browser/state_store.h" 13 #include "extensions/browser/state_store.h"
14 #include "net/cert/x509_certificate.h" 14 #include "net/cert/x509_certificate.h"
15 15
16 using content::BrowserThread; 16 using content::BrowserThread;
17 17
18 namespace chromeos { 18 namespace chromeos {
19 19
20 struct PlatformKeysService::KeyEntry {
21 // The base64 encoded DER of a X.509 Subject Public Key Info.
Andrew T Wilson (Slow) 2015/02/18 19:53:35 Nit: "the..." -> "a base64-encoded DER"
pneubeck (no reviews) 2015/02/19 11:08:40 Done.
22 std::string spki_b64;
23
24 // True if the key can be used once for singing.
25 bool sign_once = false;
kaliamoorthi 2015/02/18 17:04:09 Why do we have two bools for sign_once and sign_un
pneubeck (no reviews) 2015/02/19 11:08:40 These two permissions origin from the two differen
26
27 // True if the key can be used for signing unlimited often.
Andrew T Wilson (Slow) 2015/02/18 19:53:35 nit: "signing an unlimited number of times" - "unl
pneubeck (no reviews) 2015/02/19 11:08:39 Done.
28 bool sign_unlimited = false;
29 };
30
20 namespace { 31 namespace {
21 32
22 const char kErrorKeyNotAllowedForSigning[] = 33 const char kErrorKeyNotAllowedForSigning[] =
23 "This key is not allowed for signing. Either it was used for signing " 34 "This key is not allowed for signing. Either it was used for signing "
24 "before or it was not correctly generated."; 35 "before or it was not correctly generated.";
36
37 // The key at which platform key specific data is stored in each extension's
38 // state store.
39 // From older versions of ChromeOS, this key can hold a list of base64 encoded
Andrew T Wilson (Slow) 2015/02/18 19:53:34 nit: base64-encoded
pneubeck (no reviews) 2015/02/19 11:08:40 Done.
40 // SPKIs. A key can be used for signing at most once if it is part of that list
41 // and removed from that list afterwards.
42 //
43 // The current format of data that is written to the PlatformKeys field is a
44 // list of serialized KeyEntry objects:
45 // { 'SPKI': string ,
46 // 'signOnce': bool ,
47 // 'signUnlimited': bool
48 // }
49 //
50 // Do not change this constant as clients will lose their existing state.
25 const char kStateStorePlatformKeys[] = "PlatformKeys"; 51 const char kStateStorePlatformKeys[] = "PlatformKeys";
52 const char kStateStoreSPKI[] = "SPKI";
53 const char kStateStoreSignOnce[] = "signOnce";
54 const char kStateStoreSignUnlimited[] = "signUnlimited";
26 55
27 scoped_ptr<base::StringValue> GetPublicKeyValue( 56 scoped_ptr<PlatformKeysService::KeyEntries> KeyEntriesFromState(
28 const std::string& public_key_spki_der) { 57 const base::Value& state) {
29 std::string public_key_spki_der_b64; 58 scoped_ptr<PlatformKeysService::KeyEntries> new_entries(
30 base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64); 59 new PlatformKeysService::KeyEntries);
31 return make_scoped_ptr(new base::StringValue(public_key_spki_der_b64)); 60
61 const base::ListValue* entries = nullptr;
62 if (!state.GetAsList(&entries)) {
63 LOG(ERROR) << "Found a state store of wrong type.";
64 return new_entries.Pass();
65 }
66 for (const base::Value* entry : *entries) {
67 if (!entry) {
68 LOG(ERROR) << "Found invalid NULL entry in PlatformKeys state store.";
69 continue;
70 }
71
72 PlatformKeysService::KeyEntry new_entry;
73 const base::DictionaryValue* dict_entry = nullptr;
74 if (entry->GetAsString(&new_entry.spki_b64)) {
Andrew T Wilson (Slow) 2015/02/18 19:53:35 You talk about this above, but not bad to explain
pneubeck (no reviews) 2015/02/19 11:08:39 Done.
75 new_entry.sign_once = true;
76 } else if (entry->GetAsDictionary(&dict_entry)) {
77 dict_entry->GetStringWithoutPathExpansion(kStateStoreSPKI,
78 &new_entry.spki_b64);
79 dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignOnce,
80 &new_entry.sign_once);
81 dict_entry->GetBooleanWithoutPathExpansion(kStateStoreSignUnlimited,
82 &new_entry.sign_unlimited);
83 } else {
84 LOG(ERROR) << "Found invalid entry of type " << entry->GetType()
85 << " in PlatformKeys state store.";
86 continue;
87 }
88 new_entries->push_back(new_entry);
89 }
90 return new_entries.Pass();
91 }
92
93 scoped_ptr<base::ListValue> KeyEntriesToState(
94 const PlatformKeysService::KeyEntries& entries) {
95 scoped_ptr<base::ListValue> new_state(new base::ListValue);
96 for (const PlatformKeysService::KeyEntry& entry : entries) {
97 scoped_ptr<base::DictionaryValue> new_entry(new base::DictionaryValue);
98
Andrew T Wilson (Slow) 2015/02/18 19:53:35 nit:permissions, not premissions.
pneubeck (no reviews) 2015/02/19 11:08:40 Done.
99 // Drop entries that the extension doesn't have any premissions for anymore.
100 if (!entry.sign_once && !entry.sign_unlimited)
kaliamoorthi 2015/02/18 17:04:09 Move this above the new.
pneubeck (no reviews) 2015/02/19 11:08:39 Done.
101 continue;
102 new_entry->SetStringWithoutPathExpansion(kStateStoreSPKI, entry.spki_b64);
103 if (entry.sign_once) {
kaliamoorthi 2015/02/18 17:04:09 Is there a reason why the bool is written only whe
pneubeck (no reviews) 2015/02/19 11:08:40 added a comment that defaults values are not store
104 new_entry->SetBooleanWithoutPathExpansion(kStateStoreSignOnce,
105 entry.sign_once);
106 }
107 if (entry.sign_unlimited) {
108 new_entry->SetBooleanWithoutPathExpansion(kStateStoreSignUnlimited,
109 entry.sign_unlimited);
110 }
111 new_state->Append(new_entry.release());
112 }
113 return new_state.Pass();
114 }
115
116 // Searches |platform_keys| for an entry for |public_key_spki_der_b64|. If found
117 // returns a pointer to it, otherwise returns null.
118 PlatformKeysService::KeyEntry* GetMatchingEntry(
119 const std::string& public_key_spki_der_b64,
120 PlatformKeysService::KeyEntries* platform_keys) {
121 for (PlatformKeysService::KeyEntry& entry : *platform_keys) {
122 if (entry.spki_b64 == public_key_spki_der_b64)
Andrew T Wilson (Slow) 2015/02/18 19:53:35 Thing to remember here is that DER encodings are n
pneubeck (no reviews) 2015/02/19 11:08:40 Good point. I added a clarification that DER is ac
123 return &entry;
124 }
125 return nullptr;
32 } 126 }
33 127
34 } // namespace 128 } // namespace
35 129
36 class PlatformKeysService::Task { 130 class PlatformKeysService::Task {
37 public: 131 public:
38 Task() {} 132 Task() {}
39 virtual ~Task() {} 133 virtual ~Task() {}
40 virtual void Start() = 0; 134 virtual void Start() = 0;
41 virtual bool IsDone() = 0; 135 virtual bool IsDone() = 0;
42 136
43 private: 137 private:
44 DISALLOW_ASSIGN(Task); 138 DISALLOW_ASSIGN(Task);
45 }; 139 };
46 140
47 class PlatformKeysService::PermissionUpdateTask : public Task { 141 class PlatformKeysService::PermissionUpdateTask : public Task {
48 public: 142 public:
49 enum class Step { 143 enum class Step {
50 READ_PLATFORM_KEYS, 144 READ_PLATFORM_KEYS,
51 WRITE_UPDATE_AND_CALLBACK, 145 WRITE_UPDATE_AND_CALLBACK,
52 DONE, 146 DONE,
53 }; 147 };
54 148
55 // Creates a task that reads the current permission for an extension to access 149 // Creates a task that reads the current permission for an extension to access
56 // a certain key. Afterwards it updates and persists the permission to the new 150 // a certain key. Afterwards it updates and persists the permission to the new
57 // value |new_permission_value|. |callback| will be run after the permission 151 // value |new_permission_value|. |callback| will be run after the permission
58 // was persisted. The old permission value is then accessible through 152 // was persisted. The old permission values are then available through
59 // old_permission_value(). 153 // old_key_entry().
60 PermissionUpdateTask(const bool new_permission_value, 154 PermissionUpdateTask(const SignPermission permission,
155 const bool new_permission_value,
61 const std::string& public_key_spki_der, 156 const std::string& public_key_spki_der,
62 const std::string& extension_id, 157 const std::string& extension_id,
63 base::Callback<void(Task*)> callback, 158 base::Callback<void(Task*)> callback,
64 PlatformKeysService* service) 159 PlatformKeysService* service)
65 : new_permission_value_(new_permission_value), 160 : permission_(permission),
161 new_permission_value_(new_permission_value),
66 public_key_spki_der_(public_key_spki_der), 162 public_key_spki_der_(public_key_spki_der),
67 extension_id_(extension_id), 163 extension_id_(extension_id),
68 callback_(callback), 164 callback_(callback),
69 service_(service), 165 service_(service),
70 weak_factory_(this) {} 166 weak_factory_(this) {}
71 167
72 ~PermissionUpdateTask() override {} 168 ~PermissionUpdateTask() override {}
73 169
74 void Start() override { 170 void Start() override {
75 CHECK(next_step_ == Step::READ_PLATFORM_KEYS); 171 CHECK(next_step_ == Step::READ_PLATFORM_KEYS);
76 DoStep(); 172 DoStep();
77 } 173 }
78 174
79 bool IsDone() override { return next_step_ == Step::DONE; } 175 bool IsDone() override { return next_step_ == Step::DONE; }
80 176
81 // The original permission value before setting the new value 177 // The original key entry before setting the new value |new_permission_value|.
kaliamoorthi 2015/02/18 17:04:09 This is confusing, original key entry and new perm
pneubeck (no reviews) 2015/02/19 11:08:40 Done.
82 // |new_permission_value|. 178 const KeyEntry& old_key_entry() { return old_key_entry_; }
83 bool old_permission_value() { return old_permission_value_; }
84 179
85 private: 180 private:
86 void DoStep() { 181 void DoStep() {
87 switch (next_step_) { 182 switch (next_step_) {
88 case Step::READ_PLATFORM_KEYS: 183 case Step::READ_PLATFORM_KEYS:
89 next_step_ = Step::WRITE_UPDATE_AND_CALLBACK; 184 next_step_ = Step::WRITE_UPDATE_AND_CALLBACK;
90 ReadPlatformKeys(); 185 ReadPlatformKeys();
91 return; 186 return;
92 case Step::WRITE_UPDATE_AND_CALLBACK: 187 case Step::WRITE_UPDATE_AND_CALLBACK:
93 next_step_ = Step::DONE; 188 next_step_ = Step::DONE;
(...skipping 12 matching lines...) Expand all
106 } 201 }
107 202
108 // Reads the PlatformKeys value from the extension's state store and calls 203 // Reads the PlatformKeys value from the extension's state store and calls
109 // back to GotPlatformKeys(). 204 // back to GotPlatformKeys().
110 void ReadPlatformKeys() { 205 void ReadPlatformKeys() {
111 service_->GetPlatformKeysOfExtension( 206 service_->GetPlatformKeysOfExtension(
112 extension_id_, base::Bind(&PermissionUpdateTask::GotPlatformKeys, 207 extension_id_, base::Bind(&PermissionUpdateTask::GotPlatformKeys,
113 weak_factory_.GetWeakPtr())); 208 weak_factory_.GetWeakPtr()));
114 } 209 }
115 210
116 void GotPlatformKeys(scoped_ptr<base::ListValue> platform_keys) { 211 void GotPlatformKeys(scoped_ptr<KeyEntries> platform_keys) {
117 platform_keys_ = platform_keys.Pass(); 212 platform_keys_ = platform_keys.Pass();
118 DoStep(); 213 DoStep();
119 } 214 }
120 215
121 // Returns whether the extension has permission to use the key for signing 216 // Persists the existing KeyEntry in |old_key_entry_|, updates the entry with
122 // according to the PlatformKeys value read from the extensions state store. 217 // the new permission and persists it to the extension's state store if it was
123 // Invalidates the key if it was found to be valid. 218 // changed.
124 void WriteUpdate() { 219 void WriteUpdate() {
125 scoped_ptr<base::StringValue> key_value( 220 DCHECK(platform_keys_);
126 GetPublicKeyValue(public_key_spki_der_));
127 221
128 base::ListValue::const_iterator it = platform_keys_->Find(*key_value); 222 std::string public_key_spki_der_b64;
129 old_permission_value_ = it != platform_keys_->end(); 223 base::Base64Encode(public_key_spki_der_, &public_key_spki_der_b64);
130 if (old_permission_value_ == new_permission_value_)
131 return;
132 224
133 if (new_permission_value_) 225 KeyEntry* matching_entry =
134 platform_keys_->Append(key_value.release()); 226 GetMatchingEntry(public_key_spki_der_b64, platform_keys_.get());
135 else
136 platform_keys_->Remove(*key_value, nullptr);
137 227
138 service_->SetPlatformKeysOfExtension(extension_id_, platform_keys_.Pass()); 228 if (!matching_entry) {
229 platform_keys_->push_back(KeyEntry());
230 matching_entry = &platform_keys_->back();
231 matching_entry->spki_b64 = public_key_spki_der_b64;
232 } else if (permission_ == SignPermission::ONCE && new_permission_value_) {
233 // The one-time sign permission is supposed to be granted once per key
234 // during generation. Generated keys should be unique and thus this case
235 // should never occur.
236 LOG(ERROR) << "Requested one-time sign permission on existing key.";
Andrew T Wilson (Slow) 2015/02/18 19:53:34 Should this be a CHECK()? Or at least a DCHECK()?
pneubeck (no reviews) 2015/02/19 11:08:40 'should never occur'... so DCHECK (according to th
237 }
238 old_key_entry_ = *matching_entry;
239
240 bool* permission_value = nullptr;
241 switch (permission_) {
242 case SignPermission::ONCE:
243 permission_value = &matching_entry->sign_once;
244 break;
245 case SignPermission::UNLIMITED:
246 permission_value = &matching_entry->sign_unlimited;
247 break;
248 }
kaliamoorthi 2015/02/18 17:04:09 Should there be a default unreached(); return here
pneubeck (no reviews) 2015/02/19 11:08:40 The defensive thing is to not use default. As it i
249
250 if (*permission_value != new_permission_value_) {
251 *permission_value = new_permission_value_;
252 service_->SetPlatformKeysOfExtension(extension_id_, *platform_keys_);
253 }
139 } 254 }
140 255
141 Step next_step_ = Step::READ_PLATFORM_KEYS; 256 Step next_step_ = Step::READ_PLATFORM_KEYS;
142 scoped_ptr<base::ListValue> platform_keys_; 257 KeyEntry old_key_entry_;
143 bool old_permission_value_ = false;
144 258
259 const SignPermission permission_;
145 const bool new_permission_value_; 260 const bool new_permission_value_;
146 const std::string public_key_spki_der_; 261 const std::string public_key_spki_der_;
147 const std::string extension_id_; 262 const std::string extension_id_;
263 scoped_ptr<KeyEntries> platform_keys_;
148 base::Callback<void(Task*)> callback_; 264 base::Callback<void(Task*)> callback_;
149 PlatformKeysService* const service_; 265 PlatformKeysService* const service_;
150 base::WeakPtrFactory<PermissionUpdateTask> weak_factory_; 266 base::WeakPtrFactory<PermissionUpdateTask> weak_factory_;
151 267
152 DISALLOW_COPY_AND_ASSIGN(PermissionUpdateTask); 268 DISALLOW_COPY_AND_ASSIGN(PermissionUpdateTask);
153 }; 269 };
154 270
155 class PlatformKeysService::SignTask : public Task { 271 class PlatformKeysService::SignTask : public Task {
156 public: 272 public:
157 enum class Step { 273 enum class Step {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
191 } 307 }
192 bool IsDone() override { return next_step_ == Step::DONE; } 308 bool IsDone() override { return next_step_ == Step::DONE; }
193 309
194 private: 310 private:
195 void DoStep() { 311 void DoStep() {
196 switch (next_step_) { 312 switch (next_step_) {
197 case Step::UPDATE_PERMISSION: 313 case Step::UPDATE_PERMISSION:
198 next_step_ = Step::SIGN_OR_ABORT; 314 next_step_ = Step::SIGN_OR_ABORT;
199 UpdatePermission(); 315 UpdatePermission();
200 return; 316 return;
201 case Step::SIGN_OR_ABORT: 317 case Step::SIGN_OR_ABORT: {
202 next_step_ = Step::DONE; 318 next_step_ = Step::DONE;
203 if (!service_->permission_check_enabled_ || 319 bool sign_granted = permission_update_->old_key_entry().sign_once ||
204 permission_update_->old_permission_value()) { 320 permission_update_->old_key_entry().sign_unlimited;
321 if (sign_granted) {
205 Sign(); 322 Sign();
206 } else { 323 } else {
207 callback_.Run(std::string() /* no signature */, 324 if (!callback_.is_null()) {
Andrew T Wilson (Slow) 2015/02/18 19:53:35 Is this necessary? I thought calling Run() on a nu
pneubeck (no reviews) 2015/02/19 11:08:40 no it's not: CallbackBase::CallbackBase(BindState
208 kErrorKeyNotAllowedForSigning); 325 callback_.Run(std::string() /* no signature */,
326 kErrorKeyNotAllowedForSigning);
327 }
209 DoStep(); 328 DoStep();
210 } 329 }
211 return; 330 return;
331 }
212 case Step::DONE: 332 case Step::DONE:
213 service_->TaskFinished(this); 333 service_->TaskFinished(this);
214 // |this| might be invalid now. 334 // |this| might be invalid now.
215 return; 335 return;
216 } 336 }
217 } 337 }
218 338
219 // Reads the current permission of the extension with |extension_id_| for key 339 // Reads the current permission of the extension with |extension_id_| for key
220 // |params_->public_key| and updates the permission to disable further 340 // |params_->public_key| and updates the permission to disable further
221 // signing operations with that key. 341 // signing operations with that key.
222 void UpdatePermission() { 342 void UpdatePermission() {
223 permission_update_.reset(new PermissionUpdateTask( 343 permission_update_.reset(new PermissionUpdateTask(
224 false /* new permission value */, public_key_, extension_id_, 344 SignPermission::ONCE, false /* new permission value */, public_key_,
345 extension_id_,
225 base::Bind(&SignTask::DidUpdatePermission, weak_factory_.GetWeakPtr()), 346 base::Bind(&SignTask::DidUpdatePermission, weak_factory_.GetWeakPtr()),
226 service_)); 347 service_));
227 permission_update_->Start(); 348 permission_update_->Start();
228 } 349 }
229 350
230 void DidUpdatePermission(Task* /* task */) { DoStep(); } 351 void DidUpdatePermission(Task* /* task */) { DoStep(); }
231 352
232 // Starts the actual signing operation and afterwards passes the signature (or 353 // Starts the actual signing operation and afterwards passes the signature (or
233 // error) to |callback_|. 354 // error) to |callback_|.
234 void Sign() { 355 void Sign() {
235 if (sign_direct_pkcs_padded_) { 356 if (sign_direct_pkcs_padded_) {
236 platform_keys::subtle::SignRSAPKCS1Raw( 357 platform_keys::subtle::SignRSAPKCS1Raw(
237 token_id_, data_, public_key_, 358 token_id_, data_, public_key_,
238 base::Bind(&SignTask::DidSign, weak_factory_.GetWeakPtr()), 359 base::Bind(&SignTask::DidSign, weak_factory_.GetWeakPtr()),
239 service_->browser_context_); 360 service_->browser_context_);
240 } else { 361 } else {
241 platform_keys::subtle::SignRSAPKCS1Digest( 362 platform_keys::subtle::SignRSAPKCS1Digest(
242 token_id_, data_, public_key_, hash_algorithm_, 363 token_id_, data_, public_key_, hash_algorithm_,
243 base::Bind(&SignTask::DidSign, weak_factory_.GetWeakPtr()), 364 base::Bind(&SignTask::DidSign, weak_factory_.GetWeakPtr()),
244 service_->browser_context_); 365 service_->browser_context_);
245 } 366 }
246 } 367 }
247 368
248 void DidSign(const std::string& signature, const std::string& error_message) { 369 void DidSign(const std::string& signature, const std::string& error_message) {
249 callback_.Run(signature, error_message); 370 callback_.Run(signature, error_message);
250 DoStep(); 371 DoStep();
251 } 372 }
252 373
253 Step next_step_ = Step::UPDATE_PERMISSION; 374 Step next_step_ = Step::UPDATE_PERMISSION;
254 scoped_ptr<base::ListValue> platform_keys_; 375 scoped_ptr<KeyEntries> platform_keys_;
255 scoped_ptr<PermissionUpdateTask> permission_update_; 376 scoped_ptr<PermissionUpdateTask> permission_update_;
256 377
257 const std::string token_id_; 378 const std::string token_id_;
258 const std::string data_; 379 const std::string data_;
259 const std::string public_key_; 380 const std::string public_key_;
260 381
261 // If true, |data_| will not be hashed before signing. Only PKCS#1 v1.5 382 // If true, |data_| will not be hashed before signing. Only PKCS#1 v1.5
262 // padding will be applied before signing. 383 // padding will be applied before signing.
263 // If false, |hash_algorithm_| is set to a value != NONE. 384 // If false, |hash_algorithm_| is set to a value != NONE.
264 const bool sign_direct_pkcs_padded_; 385 const bool sign_direct_pkcs_padded_;
265 const platform_keys::HashAlgorithm hash_algorithm_; 386 const platform_keys::HashAlgorithm hash_algorithm_;
266 const std::string extension_id_; 387 const std::string extension_id_;
267 const SignCallback callback_; 388 const SignCallback callback_;
268 PlatformKeysService* const service_; 389 PlatformKeysService* const service_;
269 base::WeakPtrFactory<SignTask> weak_factory_; 390 base::WeakPtrFactory<SignTask> weak_factory_;
270 391
271 DISALLOW_COPY_AND_ASSIGN(SignTask); 392 DISALLOW_COPY_AND_ASSIGN(SignTask);
272 }; 393 };
273 394
395 class PlatformKeysService::SelectTask : public Task {
396 public:
397 enum class Step {
398 GET_MATCHING_CERTS,
399 SELECT_CERTS,
400 READ_PLATFORM_KEYS,
401 UPDATE_PERMISSION,
402 FILTER_BY_PERMISSIONS,
403 DONE,
404 };
405
406 // This task determines all known client certs matching |request|. If
407 // |interactive| is true, calls |service->select_delegate_->Select()| to
408 // select a cert from all matches. The extension with |extension_id| will be
409 // granted unlimited sign permission for the selected cert.
410 // Finally, either the selection or, if |interactive| is false, matching certs
411 // that the extension has permission for are passed to |callback|.
412 SelectTask(const platform_keys::ClientCertificateRequest& request,
413 bool interactive,
414 const std::string& extension_id,
415 const SelectCertificatesCallback& callback,
416 PlatformKeysService* service)
417 : request_(request),
418 interactive_(interactive),
419 extension_id_(extension_id),
420 callback_(callback),
421 service_(service),
422 weak_factory_(this) {}
423 ~SelectTask() override {}
424
425 void Start() override {
426 CHECK(next_step_ == Step::GET_MATCHING_CERTS);
427 DoStep();
428 }
429 bool IsDone() override { return next_step_ == Step::DONE; }
430
431 private:
432 void DoStep() {
433 switch (next_step_) {
434 case Step::GET_MATCHING_CERTS:
435 next_step_ = Step::SELECT_CERTS;
436 GetMatchingCerts();
437 return;
438 case Step::SELECT_CERTS:
439 next_step_ = Step::UPDATE_PERMISSION;
440 SelectCerts();
441 return;
442 case Step::UPDATE_PERMISSION:
443 next_step_ = Step::READ_PLATFORM_KEYS;
444 UpdatePermission();
445 return;
446 case Step::READ_PLATFORM_KEYS:
447 next_step_ = Step::FILTER_BY_PERMISSIONS;
448 ReadPlatformKeys();
449 return;
450 case Step::FILTER_BY_PERMISSIONS:
451 next_step_ = Step::DONE;
452 FilterSelectionByPermission();
453 return;
454 case Step::DONE:
455 service_->TaskFinished(this);
456 // |this| might be invalid now.
457 return;
458 }
459 }
460
461 // Retrieves all certificates matching |request_|. Will call back to
462 // |GotMatchingCerts()|.
463 void GetMatchingCerts() {
464 platform_keys::subtle::SelectClientCertificates(
465 request_,
466 base::Bind(&SelectTask::GotMatchingCerts, weak_factory_.GetWeakPtr()),
467 service_->browser_context_);
468 }
469
470 // If the certificate request could be processed successfully, |matches| will
471 // contain the list of matching certificates (maybe empty) and |error_message|
472 // will be empty. If an error occurred, |matches| will be null and
473 // |error_message| contain an error message.
474 void GotMatchingCerts(scoped_ptr<net::CertificateList> matches,
475 const std::string& error_message) {
476 if (!error_message.empty()) {
477 next_step_ = Step::DONE;
478 callback_.Run(nullptr /* no certificates */, error_message);
479 DoStep();
480 return;
481 }
482 matches_.swap(*matches);
483 DoStep();
484 }
485
486 // If |interactive_|, calls |service_->select_delegate_->Select()| to select a
487 // cert from |matches_|, which will be stored in |selected_cert_|.
488 // Will call back to |GotSelection()|.
489 void SelectCerts() {
490 if (!interactive_ || matches_.empty()) {
kaliamoorthi 2015/02/18 17:04:09 This seem to unnecessarily continue through the re
pneubeck (no reviews) 2015/02/19 11:08:40 I did this to make the transitions as constant as
491 // Don't show a select dialog in non-interactive calls or if no
492 // certificate is matching.
493 DoStep();
494 return;
495 }
496 service_->select_delegate_->Select(
497 extension_id_, matches_,
498 base::Bind(&SelectTask::GotSelection, base::Unretained(this)));
499 }
500
501 // Will be called by |SelectCerts()| with the selected cert or null if no cert
502 // was selected.
503 void GotSelection(scoped_refptr<net::X509Certificate> selected_cert) {
504 selected_cert_ = selected_cert;
505 DoStep();
506 }
507
508 // Updates the extension's state store about unlimited sign permission for the
509 // selected cert. Does nothing if no cert was selected or if |interactive_| is
510 // false.
511 // Will call back to |DidUpdatePermission()|.
512 void UpdatePermission() {
513 if (!interactive_ || !selected_cert_) {
514 DoStep();
515 return;
516 }
517 const std::string public_key_spki_der(
518 platform_keys::GetSubjectPublicKeyInfo(selected_cert_));
519 permission_update_.reset(new PermissionUpdateTask(
520 SignPermission::UNLIMITED, true /* new permission value */,
521 public_key_spki_der, extension_id_,
522 base::Bind(&SelectTask::DidUpdatePermission, base::Unretained(this)),
523 service_));
524 permission_update_->Start();
525 }
526
527 void DidUpdatePermission(Task* /* task */) { DoStep(); }
528
529 // Reads the PlatformKeys value from the extension's state store and calls
530 // back to GotPlatformKeys().
531 void ReadPlatformKeys() {
532 service_->GetPlatformKeysOfExtension(
533 extension_id_,
534 base::Bind(&SelectTask::GotPlatformKeys, weak_factory_.GetWeakPtr()));
535 }
536
537 void GotPlatformKeys(scoped_ptr<KeyEntries> platform_keys) {
538 platform_keys_ = platform_keys.Pass();
539 DoStep();
540 }
541
542 // Filters from all matches (if not interactive) or from the selection (if
543 // interactive), the certificates that the extension has unlimited sign
544 // permission for. Passes the filtered certs to |callback_|.
545 // Note: In the interactive case this should filter exactly the selected cert
546 // and checking the permissions again is not striclty necessary. However, by
547 // running the filtering additionally, ensures that the permissions were
548 // updated correctly.
Andrew T Wilson (Slow) 2015/02/18 19:53:35 What does "ensures that the permissions were updat
pneubeck (no reviews) 2015/02/19 11:08:40 Done.
549 void FilterSelectionByPermission() {
550 scoped_ptr<net::CertificateList> selection(new net::CertificateList);
551 if (interactive_) {
552 if (selected_cert_)
553 selection->push_back(selected_cert_);
554 } else {
555 selection->assign(matches_.begin(), matches_.end());
556 }
557
558 scoped_ptr<net::CertificateList> filtered_certs(new net::CertificateList);
559 for (scoped_refptr<net::X509Certificate> selected_cert : *selection) {
560 const std::string public_key_spki_der(
561 platform_keys::GetSubjectPublicKeyInfo(selected_cert));
562 std::string public_key_spki_der_b64;
563 base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64);
564
565 KeyEntry* matching_entry =
566 GetMatchingEntry(public_key_spki_der_b64, platform_keys_.get());
567 if (!matching_entry || !matching_entry->sign_unlimited)
568 continue;
569
570 filtered_certs->push_back(selected_cert);
571 }
572 callback_.Run(filtered_certs.Pass(), std::string() /* no error */);
573 DoStep();
574 }
575
576 Step next_step_ = Step::GET_MATCHING_CERTS;
577 scoped_ptr<KeyEntries> platform_keys_;
578 scoped_ptr<PermissionUpdateTask> permission_update_;
579
580 net::CertificateList matches_;
581 scoped_refptr<net::X509Certificate> selected_cert_;
582 platform_keys::ClientCertificateRequest request_;
583 const bool interactive_;
584 const std::string extension_id_;
585 const SelectCertificatesCallback callback_;
586 PlatformKeysService* const service_;
587 base::WeakPtrFactory<SelectTask> weak_factory_;
588
589 DISALLOW_COPY_AND_ASSIGN(SelectTask);
590 };
591
592 PlatformKeysService::SelectDelegate::SelectDelegate() {
593 }
594
595 PlatformKeysService::SelectDelegate::~SelectDelegate() {
596 }
597
274 PlatformKeysService::PlatformKeysService( 598 PlatformKeysService::PlatformKeysService(
275 content::BrowserContext* browser_context, 599 content::BrowserContext* browser_context,
276 extensions::StateStore* state_store) 600 extensions::StateStore* state_store)
277 : browser_context_(browser_context), 601 : browser_context_(browser_context),
278 state_store_(state_store), 602 state_store_(state_store),
279 weak_factory_(this) { 603 weak_factory_(this) {
280 DCHECK(state_store); 604 DCHECK(state_store);
281 } 605 }
282 606
283 PlatformKeysService::~PlatformKeysService() { 607 PlatformKeysService::~PlatformKeysService() {
284 } 608 }
285 609
286 void PlatformKeysService::DisablePermissionCheckForTesting() { 610 void PlatformKeysService::SetSelectDelegate(
287 permission_check_enabled_ = false; 611 scoped_ptr<SelectDelegate> delegate) {
612 select_delegate_ = delegate.Pass();
Andrew T Wilson (Slow) 2015/02/18 19:53:35 DCHECK(interactive_) might be appropriate here? Or
pneubeck (no reviews) 2015/02/19 11:08:39 There's some confusion about the scoped of interac
613 }
614
615 void PlatformKeysService::GrantUnlimitedSignPermission(
616 const std::string& extension_id,
617 scoped_refptr<net::X509Certificate> cert) {
618 const std::string public_key_spki_der(
619 platform_keys::GetSubjectPublicKeyInfo(cert));
620
621 StartOrQueueTask(make_scoped_ptr(new PermissionUpdateTask(
622 SignPermission::UNLIMITED, true /* new permission value */,
623 public_key_spki_der, extension_id,
624 base::Bind(&PlatformKeysService::TaskFinished, base::Unretained(this)),
625 this)));
288 } 626 }
289 627
290 void PlatformKeysService::GenerateRSAKey(const std::string& token_id, 628 void PlatformKeysService::GenerateRSAKey(const std::string& token_id,
291 unsigned int modulus_length, 629 unsigned int modulus_length,
292 const std::string& extension_id, 630 const std::string& extension_id,
293 const GenerateKeyCallback& callback) { 631 const GenerateKeyCallback& callback) {
294 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 632 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
295 633
296 platform_keys::subtle::GenerateRSAKey( 634 platform_keys::subtle::GenerateRSAKey(
297 token_id, modulus_length, 635 token_id, modulus_length,
(...skipping 21 matching lines...) Expand all
319 const std::string& extension_id, 657 const std::string& extension_id,
320 const SignCallback& callback) { 658 const SignCallback& callback) {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 659 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
322 StartOrQueueTask(make_scoped_ptr(new SignTask( 660 StartOrQueueTask(make_scoped_ptr(new SignTask(
323 token_id, data, public_key, true /* sign directly without hashing */, 661 token_id, data, public_key, true /* sign directly without hashing */,
324 platform_keys::HASH_ALGORITHM_NONE, extension_id, callback, this))); 662 platform_keys::HASH_ALGORITHM_NONE, extension_id, callback, this)));
325 } 663 }
326 664
327 void PlatformKeysService::SelectClientCertificates( 665 void PlatformKeysService::SelectClientCertificates(
328 const platform_keys::ClientCertificateRequest& request, 666 const platform_keys::ClientCertificateRequest& request,
667 bool interactive,
kaliamoorthi 2015/02/18 17:04:09 Can't you deduce this by the fact that SelectDeleg
pneubeck (no reviews) 2015/02/19 11:08:40 see reply to Drew's comment above.
329 const std::string& extension_id, 668 const std::string& extension_id,
330 const SelectCertificatesCallback& callback) { 669 const SelectCertificatesCallback& callback) {
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 670 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
332 671 StartOrQueueTask(make_scoped_ptr(
333 platform_keys::subtle::SelectClientCertificates( 672 new SelectTask(request, interactive, extension_id, callback, this)));
334 request,
335 base::Bind(&PlatformKeysService::SelectClientCertificatesCallback,
336 weak_factory_.GetWeakPtr(), extension_id, callback),
337 browser_context_);
338 } 673 }
339 674
340 void PlatformKeysService::StartOrQueueTask(scoped_ptr<Task> task) { 675 void PlatformKeysService::StartOrQueueTask(scoped_ptr<Task> task) {
341 tasks_.push(make_linked_ptr(task.release())); 676 tasks_.push(make_linked_ptr(task.release()));
342 if (tasks_.size() == 1) 677 if (tasks_.size() == 1)
343 tasks_.front()->Start(); 678 tasks_.front()->Start();
344 } 679 }
345 680
346 void PlatformKeysService::TaskFinished(Task* task) { 681 void PlatformKeysService::TaskFinished(Task* task) {
347 DCHECK(!tasks_.empty()); 682 DCHECK(!tasks_.empty());
(...skipping 12 matching lines...) Expand all
360 const std::string& extension_id, 695 const std::string& extension_id,
361 const GetPlatformKeysCallback& callback) { 696 const GetPlatformKeysCallback& callback) {
362 state_store_->GetExtensionValue( 697 state_store_->GetExtensionValue(
363 extension_id, kStateStorePlatformKeys, 698 extension_id, kStateStorePlatformKeys,
364 base::Bind(&PlatformKeysService::GotPlatformKeysOfExtension, 699 base::Bind(&PlatformKeysService::GotPlatformKeysOfExtension,
365 weak_factory_.GetWeakPtr(), extension_id, callback)); 700 weak_factory_.GetWeakPtr(), extension_id, callback));
366 } 701 }
367 702
368 void PlatformKeysService::SetPlatformKeysOfExtension( 703 void PlatformKeysService::SetPlatformKeysOfExtension(
369 const std::string& extension_id, 704 const std::string& extension_id,
370 scoped_ptr<base::ListValue> platform_keys) { 705 const KeyEntries& platform_keys) {
371 state_store_->SetExtensionValue(extension_id, kStateStorePlatformKeys, 706 state_store_->SetExtensionValue(extension_id, kStateStorePlatformKeys,
372 platform_keys.Pass()); 707 KeyEntriesToState(platform_keys));
373 } 708 }
374 709
375 void PlatformKeysService::GeneratedKey(const std::string& extension_id, 710 void PlatformKeysService::GeneratedKey(const std::string& extension_id,
376 const GenerateKeyCallback& callback, 711 const GenerateKeyCallback& callback,
377 const std::string& public_key_spki_der, 712 const std::string& public_key_spki_der,
378 const std::string& error_message) { 713 const std::string& error_message) {
379 if (!error_message.empty()) { 714 if (!error_message.empty()) {
380 callback.Run(std::string() /* no public key */, error_message); 715 callback.Run(std::string() /* no public key */, error_message);
381 return; 716 return;
382 } 717 }
383 718
384 StartOrQueueTask(make_scoped_ptr(new PermissionUpdateTask( 719 StartOrQueueTask(make_scoped_ptr(new PermissionUpdateTask(
385 true /* new permission value */, public_key_spki_der, extension_id, 720 SignPermission::ONCE, true /* new permission value */,
721 public_key_spki_der, extension_id,
386 base::Bind(&PlatformKeysService::RegisteredGeneratedKey, 722 base::Bind(&PlatformKeysService::RegisteredGeneratedKey,
387 base::Unretained(this), callback, public_key_spki_der), 723 base::Unretained(this), callback, public_key_spki_der),
388 this))); 724 this)));
389 } 725 }
390 726
391 void PlatformKeysService::RegisteredGeneratedKey( 727 void PlatformKeysService::RegisteredGeneratedKey(
392 const GenerateKeyCallback& callback, 728 const GenerateKeyCallback& callback,
393 const std::string& public_key_spki_der, 729 const std::string& public_key_spki_der,
394 Task* task) { 730 Task* task) {
395 callback.Run(public_key_spki_der, std::string() /* no error */); 731 callback.Run(public_key_spki_der, std::string() /* no error */);
396 TaskFinished(task); 732 TaskFinished(task);
397 } 733 }
398 734
399 void PlatformKeysService::SelectClientCertificatesCallback(
400 const std::string& extension_id,
401 const SelectCertificatesCallback& callback,
402 scoped_ptr<net::CertificateList> matches,
403 const std::string& error_message) {
404 if (permission_check_enabled_)
405 matches->clear();
406
407 // TODO(pneubeck): Remove all certs that the extension doesn't have access to.
408 callback.Run(matches.Pass(), error_message);
409 }
410 735
411 void PlatformKeysService::GotPlatformKeysOfExtension( 736 void PlatformKeysService::GotPlatformKeysOfExtension(
412 const std::string& extension_id, 737 const std::string& extension_id,
413 const GetPlatformKeysCallback& callback, 738 const GetPlatformKeysCallback& callback,
414 scoped_ptr<base::Value> value) { 739 scoped_ptr<base::Value> value) {
415 if (!value) 740 scoped_ptr<KeyEntries> key_entries(new KeyEntries);
416 value.reset(new base::ListValue); 741 if (value)
742 key_entries = KeyEntriesFromState(*value);
417 743
418 base::ListValue* keys = NULL; 744 callback.Run(key_entries.Pass());
419 if (!value->GetAsList(&keys)) {
420 LOG(ERROR) << "Found a value of wrong type.";
421
422 keys = new base::ListValue;
423 value.reset(keys);
424 }
425
426 ignore_result(value.release());
427 callback.Run(make_scoped_ptr(keys));
428 } 745 }
429 746
430 } // namespace chromeos 747 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698