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

Side by Side Diff: chrome/browser/mac/keychain_reauthorize.cc

Issue 10344009: Implement Keychain reauthorization (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « chrome/browser/mac/keychain_reauthorize.h ('k') | chrome/browser/mac/security_wrappers.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/mac/keychain_reauthorize.h"
6
7 #include <Security/Security.h>
8
9 #include <algorithm>
10 #include <string>
11 #include <vector>
12
13 #include "base/basictypes.h"
14 #include "base/mac/foundation_util.h"
15 #include "base/mac/scoped_cftyperef.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/stringprintf.h"
18 #include "base/sys_string_conversions.h"
19 #include "chrome/browser/mac/security_wrappers.h"
20
21 namespace chrome {
22 namespace browser {
23 namespace mac {
24
25 namespace {
26
27 // Returns the set of requirement strings that ought to be reauthorized.
28 std::vector<std::string> RequirementMatches();
29
30 // Reauthorizes an ACL by examining all of the applications it names, and upon
31 // finding any whose requirement matches any element of requirement_matches,
32 // replaces them with this_application. At most one instance of
33 // this_application will be added to the ACL. Subsequent applications whose
34 // requirement matches any element of requirement_matches will be removed from
35 // the ACL. Only the ACL is changed, nothing is written to disk. Returns true
36 // if any reauthorization is performed and thus acl is modified, and false
37 // otherwise.
38 bool ReauthorizeACL(
39 SecACLRef acl,
40 const std::vector<std::string>& requirement_matches,
41 SecTrustedApplicationRef this_application);
42
43 // Reauthorizes a list of ACLs by calling ReauthorizeACL for each ACL in the
44 // list. Only the ACL list is changed, nothing is written to disk. Returns
45 // true if ReauthorizeTrue returns true for any ACL in acl_list, indicating
46 // that at least one ACL in acl_list was modified and thus at least one child
47 // child of acl_list was reauthorized.
48 bool ReauthorizeACLList(
49 CFArrayRef acl_list,
50 const std::vector<std::string>& requirement_matches,
51 SecTrustedApplicationRef this_application);
52
53 // Reauthorizes a SecKeychainItemRef by calling ReauthorizeACLList to perform
54 // reauthorization on all ACLs that it contains. Nothing is written to disk.
55 // If any reauthorization was performed, returns a CrSKeychainItemAndAccess
56 // object containing the item and its access information. Otherwise, returns
57 // NULL.
58 CrSKeychainItemAndAccess* KCItemToKCItemAndReauthorizedAccess(
59 SecKeychainItemRef item,
60 const std::vector<std::string>& requirement_matches,
61 SecTrustedApplicationRef this_application);
62
63 // Reauthorizes multiple Keychain items by calling
64 // KCItemToKCItemAndReauthorizedAccess for each item returned by a Keychain
65 // search. Nothing is written to disk. Reauthorized items are returned.
66 std::vector<CrSKeychainItemAndAccess> KCSearchToKCItemsAndReauthorizedAccesses(
67 SecKeychainSearchRef search,
68 const std::vector<std::string>& requirement_matches,
69 SecTrustedApplicationRef this_application);
70
71 // Given a SecKeychainAttributeList, strips out any zero-length attributes and
72 // returns a vector containing the remaining attributes.
73 std::vector<SecKeychainAttribute> KCAttributesWithoutZeroLength(
74 SecKeychainAttributeList* old_attribute_list);
75
76 // Given a CrSKeychainItemAndAccess that has had its access field
77 // reauthorized, places the reauthorized form into the Keychain by deleting
78 // the old item and replacing it with a new one whose access policy matches
79 // the reauthorized form. The new item is written to disk and becomes part of
80 // the Keychain, replacing what had been there previously.
81 void WriteKCItemAndReauthorizedAccess(
82 const CrSKeychainItemAndAccess& item_and_reauthorized_access);
83
84 // Given a vector of CrSKeychainItemAndAccess objects, places the reauthorized
85 // forms of all of them into the Keychain by calling
86 // WriteKCItemAndReauthorizedAccess for each. The new items are written to
87 // disk and become part of the Keychain, replacing what had been there
88 // previously.
89 void WriteKCItemsAndReauthorizedAccesses(
90 const std::vector<CrSKeychainItemAndAccess>&
91 items_and_reauthorized_accesses);
92
93 } // namespace
94
95 void KeychainReauthorize() {
96 ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(FALSE);
97
98 // Apple's documentation (Keychain Services Reference, Constants/Mac OS X
99 // Keychain Services API Constants/Keychain Item Class Constants) says to
100 // use CSSM_DL_DB_RECORD_ALL_KEYS, but that doesn't work.
101 // CSSM_DL_DB_RECORD_ANY (as used by SecurityTool's keychain-dump) does
102 // work.
103 base::mac::ScopedCFTypeRef<SecKeychainSearchRef> search(
104 CrSKeychainSearchCreateFromAttributes(NULL,
105 CSSM_DL_DB_RECORD_ANY,
106 NULL));
107
108 std::vector<std::string> requirement_matches =
109 RequirementMatches();
110
111 base::mac::ScopedCFTypeRef<SecTrustedApplicationRef> this_application(
112 CrSTrustedApplicationCreateFromPath(NULL));
113
114 std::vector<CrSKeychainItemAndAccess> items_and_reauthorized_accesses =
115 KCSearchToKCItemsAndReauthorizedAccesses(search,
116 requirement_matches,
117 this_application);
118
119 WriteKCItemsAndReauthorizedAccesses(items_and_reauthorized_accesses);
120 }
121
122 namespace {
123
124 std::vector<std::string> RequirementMatches() {
125 // See the designated requirement for a signed released build:
126 // codesign -d -r- "Google Chrome.app"
127 //
128 // Export the certificates from a signed released build:
129 // codesign -v --extract-certificates=/tmp/cert. "Google Chrome.app"
130 // (The extracted leaf certificate is at /tmp/cert.0; intermediates and root
131 // are at successive numbers.)
132 //
133 // Show some information about the exported certificates:
134 // openssl x509 -inform DER -in /tmp/cert.0 -noout -text -fingerprint
135 // (The "SHA1 Fingerprint" value printed by -fingerprint should match the
136 // hash used in a codesign designated requirement after allowing for obvious
137 // formatting differences.)
138
139 const char* const kIdentifierMatches[] = {
140 #if defined(GOOGLE_CHROME_BUILD)
141 "com.google.Chrome",
142 "com.google.Chrome.canary",
143 #else
144 "org.chromium.Chromium",
145 #endif
146 };
147
148 const char* const kLeafCertificateHashMatches[] = {
149 // Only official released builds of Google Chrome have ever been signed
150 // (with a certificate that anyone knows about or cares about).
151 #if defined(GOOGLE_CHROME_BUILD)
152 // This is the new certificate that has not yet been used to sign Chrome,
153 // but will be. Once used, the reauthorization code will become obsolete
154 // until it's needed for some other purpose in the future.
155 // Subject: UID=EQHXZ8M8AV, CN=Developer ID Application: Google Inc.,
156 // OU=EQHXZ8M8AV, O=Google Inc., C=US
157 // Issuer: CN=Developer ID Certification Authority,
158 // OU=Apple Certification Authority, O=Apple Inc., C=US
159 // Validity: 2012-04-26 14:10:10 UTC to 2017-04-27 14:10:10 UTC
160 // "85cee8254216185620ddc8851c7a9fc4dfe120ef",
161
162 // This certificate was used on 2011-12-20 and 2011-12-21, but the "since
163 // 2010-07-19" one below was restored afterwards as an interim fix to the
164 // Keychain authorization problem. See http://crbug.com/108238 and
165 // http://crbug.com/62605.
166 // Subject: C=US, ST=California, L=Mountain View, O=Google Inc,
167 // OU=Digital ID Class 3 - Java Object Signing, CN=Google Inc
168 // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network,
169 // OU=Terms of use at https://www.verisign.com/rpa (c)10,
170 // CN=VeriSign Class 3 Code Signing 2010 CA
171 // Validity: 2011-11-14 00:00:00 UTC to 2014-11-13 23:59:59 UTC
172 "06c92bec3bbf32068cb9208563d004169448ee21",
173
174 // This certificate has been used since 2010-07-19, except for the brief
175 // period when the certificate above was used.
176 // Subject: C=US, ST=California, L=Mountain View, O=Google Inc,
177 // OU=Digital ID Class 3 - Java Object Signing, CN=Google Inc
178 // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network,
179 // OU=Terms of use at https://www.verisign.com/rpa (c)09,
180 // CN=VeriSign Class 3 Code Signing 2009-2 CA
181 // Validity: 2010-02-22 00:00:00 UTC to 2012-02-22 23:59:59 UTC
182 "9481882581d8178db8b1649c0eaa4f9eb11288f0",
183
184 // This certificate was used for all public Chrome releases prior to
185 // 2010-07-19.
186 // Subject: C=US, ST=California, L=Mountain View, O=Google Inc,
187 // OU=Digital ID Class 3 - Netscape Object Signing, CN=Google Inc
188 // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network,
189 // OU=Terms of use at https://www.verisign.com/rpa (c)04,
190 // CN=VeriSign Class 3 Code Signing 2004 CA
191 // Validity: 2007-06-19 00:00:00 UTC to 2010-06-18 23:59:59 UTC
192 "fe5008fe0da7a2033816752d6eafe95214f5a7e1",
193 #endif
194 };
195
196 std::vector<std::string> requirement_matches;
197 requirement_matches.reserve(arraysize(kIdentifierMatches) *
198 ARRAYSIZE_UNSAFE(kLeafCertificateHashMatches));
199
200 for (size_t identifier_index = 0;
201 identifier_index < arraysize(kIdentifierMatches);
202 ++identifier_index) {
203 for (size_t leaf_certificate_hash_index = 0;
204 leaf_certificate_hash_index <
205 ARRAYSIZE_UNSAFE(kLeafCertificateHashMatches);
206 ++leaf_certificate_hash_index) {
207 requirement_matches.push_back(base::StringPrintf(
208 "identifier \"%s\" and certificate leaf = H\"%s\"",
209 kIdentifierMatches[identifier_index],
210 kLeafCertificateHashMatches[leaf_certificate_hash_index]));
211 }
212 }
213
214 return requirement_matches;
215 }
216
217 std::vector<CrSKeychainItemAndAccess> KCSearchToKCItemsAndReauthorizedAccesses(
218 SecKeychainSearchRef search,
219 const std::vector<std::string>& requirement_matches,
220 SecTrustedApplicationRef this_application) {
221 std::vector<CrSKeychainItemAndAccess> items_and_accesses;
222
223 base::mac::ScopedCFTypeRef<SecKeychainItemRef> item;
224 while (item.reset(CrSKeychainSearchCopyNext(search)), item) {
225 scoped_ptr<CrSKeychainItemAndAccess> item_and_access(
226 KCItemToKCItemAndReauthorizedAccess(item,
227 requirement_matches,
228 this_application));
229
230 if (item_and_access.get()) {
231 items_and_accesses.push_back(*item_and_access);
232 }
233 }
234
235 return items_and_accesses;
236 }
237
238 CrSKeychainItemAndAccess* KCItemToKCItemAndReauthorizedAccess(
239 SecKeychainItemRef item,
240 const std::vector<std::string>& requirement_matches,
241 SecTrustedApplicationRef this_application) {
242 if (!CrSKeychainItemTestAccess(item)) {
243 return NULL;
244 }
245
246 base::mac::ScopedCFTypeRef<SecAccessRef> access(
247 CrSKeychainItemCopyAccess(item));
248 base::mac::ScopedCFTypeRef<CFArrayRef> acl_list(
249 CrSAccessCopyACLList(access));
250 if (!acl_list) {
251 return NULL;
252 }
253
254 bool acl_list_modified = ReauthorizeACLList(acl_list,
255 requirement_matches,
256 this_application);
257 if (!acl_list_modified) {
258 return NULL;
259 }
260
261 return new CrSKeychainItemAndAccess(item, access);
262 }
263
264 bool ReauthorizeACLList(
265 CFArrayRef acl_list,
266 const std::vector<std::string>& requirement_matches,
267 SecTrustedApplicationRef this_application) {
268 bool acl_list_modified = false;
269
270 CFIndex acl_count = CFArrayGetCount(acl_list);
271 for (CFIndex acl_index = 0; acl_index < acl_count; ++acl_index) {
272 SecACLRef acl = base::mac::CFCast<SecACLRef>(
273 CFArrayGetValueAtIndex(acl_list, acl_index));
274 if (!acl) {
275 continue;
276 }
277
278 if (ReauthorizeACL(acl, requirement_matches, this_application)) {
279 acl_list_modified = true;
280 }
281 }
282
283 return acl_list_modified;
284 }
285
286 bool ReauthorizeACL(
287 SecACLRef acl,
288 const std::vector<std::string>& requirement_matches,
289 SecTrustedApplicationRef this_application) {
290 scoped_ptr<CrSACLSimpleContents> acl_simple_contents(
291 CrSACLCopySimpleContents(acl));
292 if (!acl_simple_contents.get() ||
293 !acl_simple_contents->application_list) {
294 return false;
295 }
296
297 CFMutableArrayRef application_list_mutable = NULL;
298 bool added_this_application = false;
299
300 CFIndex application_count =
301 CFArrayGetCount(acl_simple_contents->application_list);
302 for (CFIndex application_index = 0;
303 application_index < application_count;
304 ++application_index) {
305 SecTrustedApplicationRef application =
306 base::mac::CFCast<SecTrustedApplicationRef>(
307 CFArrayGetValueAtIndex(acl_simple_contents->application_list,
308 application_index));
309 base::mac::ScopedCFTypeRef<SecRequirementRef> requirement(
310 CrSTrustedApplicationCopyRequirement(application));
311 base::mac::ScopedCFTypeRef<CFStringRef> requirement_string_cf(
312 CrSRequirementCopyString(requirement, kSecCSDefaultFlags));
313 if (!requirement_string_cf) {
314 continue;
315 }
316
317 std::string requirement_string =
318 base::SysCFStringRefToUTF8(requirement_string_cf);
319 if (std::find(requirement_matches.begin(),
320 requirement_matches.end(),
321 requirement_string) != requirement_matches.end()) {
322 if (!application_list_mutable) {
323 application_list_mutable =
324 CFArrayCreateMutableCopy(NULL,
325 application_count,
326 acl_simple_contents->application_list);
327 acl_simple_contents->application_list.reset(
328 application_list_mutable);
329 }
330
331 if (!added_this_application) {
332 CFArraySetValueAtIndex(application_list_mutable,
333 application_index,
334 this_application);
335 added_this_application = true;
336 } else {
337 // Even though it's more bookkeeping to walk a list in the forward
338 // direction when there are removals, it's done here anyway to
339 // keep this_application at the position of the first match.
340 CFArrayRemoveValueAtIndex(application_list_mutable,
341 application_index);
342 --application_index;
343 --application_count;
344 }
345 }
346 }
347
348 if (!application_list_mutable) {
349 return false;
350 }
351
352 if (!CrSACLSetSimpleContents(acl, *acl_simple_contents.get())) {
353 return false;
354 }
355
356 return true;
357 }
358
359 void WriteKCItemsAndReauthorizedAccesses(
360 const std::vector<CrSKeychainItemAndAccess>&
361 items_and_reauthorized_accesses) {
362 for (std::vector<CrSKeychainItemAndAccess>::const_iterator iterator =
363 items_and_reauthorized_accesses.begin();
364 iterator != items_and_reauthorized_accesses.end();
365 ++iterator) {
366 WriteKCItemAndReauthorizedAccess(*iterator);
367 }
368 }
369
370 void WriteKCItemAndReauthorizedAccess(
371 const CrSKeychainItemAndAccess& item_and_reauthorized_access) {
372 SecKeychainItemRef old_item = item_and_reauthorized_access.item();
373 base::mac::ScopedCFTypeRef<SecKeychainRef> keychain(
374 CrSKeychainItemCopyKeychain(old_item));
375
376 ScopedCrSKeychainItemAttributesAndData old_attributes_and_data(
377 CrSKeychainItemCopyAttributesAndData(keychain, old_item));
378 if (!old_attributes_and_data.get()) {
379 return;
380 }
381
382 // SecKeychainItemCreateFromContent fails if any attribute is zero-length,
383 // but old_attributes_and_data can contain zero-length attributes. Create
384 // a new attribute list devoid of zero-length attributes.
385 //
386 // This is awkward: only the logic to build the
387 // std::vector<SecKeychainAttribute> is in KCAttributesWithoutZeroLength
388 // because the storage used for the new attribute list (the vector) needs to
389 // persist through the lifetime of this function.
390 // KCAttributesWithoutZeroLength doesn't return a
391 // CrSKeychainItemAttributesAndData (which could be held here in a
392 // ScopedCrSKeychainItemAttributesAndData) because it's more convenient to
393 // build the attribute list using std::vector and point the data at the copy
394 // in old_attributes_and_data, thus making nothing in new_attributes a
395 // strongly-held reference.
396 std::vector<SecKeychainAttribute> new_attributes =
397 KCAttributesWithoutZeroLength(old_attributes_and_data.attribute_list());
398 SecKeychainAttributeList new_attribute_list;
399 new_attribute_list.count = new_attributes.size();
400 new_attribute_list.attr =
401 new_attribute_list.count ? &new_attributes[0] : NULL;
402 CrSKeychainItemAttributesAndData new_attributes_and_data =
403 *old_attributes_and_data.get();
404 new_attributes_and_data.attribute_list = &new_attribute_list;
405
406 // Delete the item last, to give everything else above a chance to bail
407 // out early, and to ensure that the old item is still present while it
408 // may still be used by the above code.
409 if (!CrSKeychainItemDelete(old_item)) {
410 return;
411 }
412
413 base::mac::ScopedCFTypeRef<SecKeychainItemRef> new_item(
414 CrSKeychainItemCreateFromContent(new_attributes_and_data,
415 keychain,
416 item_and_reauthorized_access.access()));
417 }
418
419 std::vector<SecKeychainAttribute> KCAttributesWithoutZeroLength(
420 SecKeychainAttributeList* old_attribute_list) {
421 UInt32 old_attribute_count = old_attribute_list->count;
422 std::vector<SecKeychainAttribute> new_attributes;
423 new_attributes.reserve(old_attribute_count);
424 for (UInt32 old_attribute_index = 0;
425 old_attribute_index < old_attribute_count;
426 ++old_attribute_index) {
427 SecKeychainAttribute* attribute =
428 &old_attribute_list->attr[old_attribute_index];
429 if (attribute->length) {
430 new_attributes.push_back(*attribute);
431 }
432 }
433
434 return new_attributes;
435 }
436
437 } // namespace
438
439 } // namespace mac
440 } // namespace browser
441 } // namespace chrome
OLDNEW
« no previous file with comments | « chrome/browser/mac/keychain_reauthorize.h ('k') | chrome/browser/mac/security_wrappers.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698