OLD | NEW |
---|---|
(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 void KeychainReauthorize() { | |
26 // See the designated requirement for a signed released build: | |
27 // codesign -d -r- "Google Chrome.app" | |
28 // | |
29 // Export the certificates from a signed released build: | |
30 // codesign -v --extract-certificates=/tmp/cert. "Google Chrome.app" | |
31 // (The extracted leaf certificate is at /tmp/cert.0; intermediates and root | |
32 // are at successive numbers.) | |
33 // | |
34 // Show some information about the exported certificates: | |
35 // openssl x509 -inform DER -in /tmp/cert.0 -noout -text -fingerprint | |
36 // (The "SHA1 Fingerprint" value printed by -fingerprint should match the | |
37 // hash used in a codesign designated requirement after allowing for obvious | |
38 // formatting differences.) | |
39 | |
40 const char* const kIdentifierMatches[] = { | |
41 #if defined(GOOGLE_CHROME_BUILD) | |
42 "com.google.Chrome", | |
43 "com.google.Chrome.canary", | |
44 #else | |
45 "org.chromium.Chromium", | |
46 #endif | |
47 }; | |
48 | |
49 const char* const kLeafCertificateHashMatches[] = { | |
50 // Only official released builds of Google Chrome have ever been signed | |
51 // (with a certificate that anyone knows about or cares about). | |
52 #if defined(GOOGLE_CHROME_BUILD) | |
53 // This is the new certificate that has not yet been used to sign Chrome, | |
54 // but will be. Once used, the reauthorization code will become obsolete | |
55 // until it's needed for some other purpose in the future. | |
56 // Subject: UID=EQHXZ8M8AV, CN=Developer ID Application: Google Inc., | |
57 // OU=EQHXZ8M8AV, O=Google Inc., C=US | |
58 // Issuer: CN=Developer ID Certification Authority, | |
59 // OU=Apple Certification Authority, O=Apple Inc., C=US | |
60 // Validity: 2012-04-26 14:10:10 UTC to 2017-04-27 14:10:10 UTC | |
61 // "85cee8254216185620ddc8851c7a9fc4dfe120ef", | |
62 | |
63 // This certificate was used on 2011-12-20 and 2011-12-21, but the one | |
64 // below was restored afterwards as an interim fix to the Keychain | |
stuartmorgan
2012/05/10 06:50:54
"the one below" is ambiguous, especially since it
| |
65 // authorization problem. | |
stuartmorgan
2012/05/10 06:50:54
Worth a bug ref? I'm guessing there are about thre
| |
66 // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, | |
67 // OU=Digital ID Class 3 - Java Object Signing, CN=Google Inc | |
68 // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, | |
69 // OU=Terms of use at https://www.verisign.com/rpa (c)10, | |
70 // CN=VeriSign Class 3 Code Signing 2010 CA | |
71 // Validity: 2011-11-14 00:00:00 UTC to 2014-11-13 23:59:59 UTC | |
72 "06c92bec3bbf32068cb9208563d004169448ee21", | |
73 | |
74 // This certificate has been used since 2010-07-19, except for the brief | |
75 // period when the certificate above was used. | |
76 // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, | |
77 // OU=Digital ID Class 3 - Java Object Signing, CN=Google Inc | |
78 // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, | |
79 // OU=Terms of use at https://www.verisign.com/rpa (c)09, | |
80 // CN=VeriSign Class 3 Code Signing 2009-2 CA | |
81 // Validity: 2010-02-22 00:00:00 UTC to 2012-02-22 23:59:59 UTC | |
82 "9481882581d8178db8b1649c0eaa4f9eb11288f0", | |
83 | |
84 // This certificate was used for all public Chrome releases prior to | |
85 // 2010-07-19. | |
86 // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, | |
87 // OU=Digital ID Class 3 - Netscape Object Signing, CN=Google Inc | |
88 // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, | |
89 // OU=Terms of use at https://www.verisign.com/rpa (c)04, | |
90 // CN=VeriSign Class 3 Code Signing 2004 CA | |
91 // Validity: 2007-06-19 00:00:00 UTC to 2010-06-18 23:59:59 UTC | |
92 "fe5008fe0da7a2033816752d6eafe95214f5a7e1", | |
93 #endif | |
94 }; | |
95 | |
96 std::vector<std::string> requirement_matches; | |
97 requirement_matches.reserve(arraysize(kIdentifierMatches) * | |
98 ARRAYSIZE_UNSAFE(kLeafCertificateHashMatches)); | |
99 | |
100 for (size_t identifier_index = 0; | |
101 identifier_index < arraysize(kIdentifierMatches); | |
102 ++identifier_index) { | |
103 for (size_t leaf_certificate_hash_index = 0; | |
104 leaf_certificate_hash_index < | |
105 ARRAYSIZE_UNSAFE(kLeafCertificateHashMatches); | |
106 ++leaf_certificate_hash_index) { | |
107 requirement_matches.push_back(base::StringPrintf( | |
108 "identifier \"%s\" and certificate leaf = H\"%s\"", | |
109 kIdentifierMatches[identifier_index], | |
110 kLeafCertificateHashMatches[leaf_certificate_hash_index])); | |
111 } | |
112 } | |
113 | |
114 ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(FALSE); | |
115 | |
116 // Apple's documentation (Keychain Services Reference, Constants/Mac OS X | |
117 // Keychain Services API Constants/Keychain Item Class Constants) says to | |
118 // use CSSM_DL_DB_RECORD_ALL_KEYS, but that doesn't work. | |
119 // CSSM_DL_DB_RECORD_ANY (as used by SecurityTool's keychain-dump) does | |
120 // work. | |
121 base::mac::ScopedCFTypeRef<SecKeychainSearchRef> search( | |
122 CrSKeychainSearchCreateFromAttributes(NULL, | |
123 CSSM_DL_DB_RECORD_ANY, | |
124 NULL)); | |
125 | |
126 base::mac::ScopedCFTypeRef<SecTrustedApplicationRef> this_application; | |
127 | |
128 std::vector<CrSKeychainItemAndAccess> items; | |
129 | |
130 base::mac::ScopedCFTypeRef<SecKeychainItemRef> item; | |
131 while (item.reset(CrSKeychainSearchCopyNext(search)), item) { | |
132 if (!CrSKeychainItemTestAccess(item)) { | |
133 continue; | |
134 } | |
stuartmorgan
2012/05/10 06:50:54
Chromium style discourages single-line braces; it
| |
135 | |
136 base::mac::ScopedCFTypeRef<SecAccessRef> access( | |
137 CrSKeychainItemCopyAccess(item)); | |
138 base::mac::ScopedCFTypeRef<CFArrayRef> acl_list( | |
139 CrSAccessCopyACLList(access)); | |
140 if (!acl_list) { | |
141 continue; | |
142 } | |
143 | |
144 bool acl_list_modified = false; | |
145 | |
146 CFIndex acl_count = CFArrayGetCount(acl_list); | |
147 for (CFIndex acl_index = 0; acl_index < acl_count; ++acl_index) { | |
stuartmorgan
2012/05/10 06:50:54
The layers of if statements in a double for-loop i
| |
148 SecACLRef acl = base::mac::CFCast<SecACLRef>( | |
149 CFArrayGetValueAtIndex(acl_list, acl_index)); | |
150 scoped_ptr<CrSACLSimpleContents> acl_simple_contents( | |
151 CrSACLCopySimpleContents(acl)); | |
152 if (!acl_simple_contents.get() || | |
153 !acl_simple_contents->application_list) { | |
154 continue; | |
155 } | |
156 | |
157 CFMutableArrayRef application_list_mutable = NULL; | |
158 bool added_this_application = false; | |
159 | |
160 CFIndex application_count = | |
161 CFArrayGetCount(acl_simple_contents->application_list); | |
162 for (CFIndex application_index = 0; | |
163 application_index < application_count; | |
164 ++application_index) { | |
165 SecTrustedApplicationRef application = | |
166 base::mac::CFCast<SecTrustedApplicationRef>( | |
167 CFArrayGetValueAtIndex(acl_simple_contents->application_list, | |
168 application_index)); | |
169 base::mac::ScopedCFTypeRef<SecRequirementRef> requirement( | |
170 CrSTrustedApplicationCopyRequirement(application)); | |
171 base::mac::ScopedCFTypeRef<CFStringRef> requirement_string_cf( | |
172 CrSRequirementCopyString(requirement, kSecCSDefaultFlags)); | |
173 if (!requirement_string_cf) { | |
174 continue; | |
175 } | |
176 | |
177 std::string requirement_string = | |
178 base::SysCFStringRefToUTF8(requirement_string_cf); | |
179 if (std::find(requirement_matches.begin(), | |
180 requirement_matches.end(), | |
181 requirement_string) != requirement_matches.end()) { | |
182 if (!application_list_mutable) { | |
183 application_list_mutable = | |
184 CFArrayCreateMutableCopy(NULL, | |
185 application_count, | |
186 acl_simple_contents->application_list); | |
187 acl_simple_contents->application_list.reset( | |
188 application_list_mutable); | |
189 } | |
190 | |
191 if (!this_application) { | |
192 this_application.reset(CrSTrustedApplicationCreateFromPath(NULL)); | |
stuartmorgan
2012/05/10 06:50:54
Is it really expensive enough to do this (and unli
| |
193 } | |
194 | |
195 if (!added_this_application) { | |
stuartmorgan
2012/05/10 06:50:54
I find
if (!foo)
else
to generally be less rea
Mark Mentovai
2012/05/14 21:28:45
stuartmorgan wrote:
| |
196 CFArraySetValueAtIndex(application_list_mutable, | |
197 application_index, | |
198 this_application); | |
199 added_this_application = true; | |
200 } else { | |
201 // Even though it's more bookkeeping to walk a list in the forward | |
202 // direction when there are removals, it's done here anyway to | |
203 // keep this_application at the position of the first match. | |
204 CFArrayRemoveValueAtIndex(application_list_mutable, | |
205 application_index); | |
206 --application_index; | |
207 --application_count; | |
208 } | |
209 } | |
210 } | |
211 | |
212 if (application_list_mutable) { | |
213 if (!CrSACLSetSimpleContents(acl, *acl_simple_contents.get())) { | |
214 continue; | |
215 } | |
216 | |
217 acl_list_modified = true; | |
218 } | |
219 } | |
220 | |
221 if (acl_list_modified) { | |
222 items.push_back(CrSKeychainItemAndAccess(item, access)); | |
223 } | |
224 } | |
225 | |
226 for (std::vector<CrSKeychainItemAndAccess>::const_iterator iterator = | |
227 items.begin(); | |
228 iterator != items.end(); | |
229 ++iterator) { | |
230 SecKeychainItemRef old_item = iterator->item(); | |
231 base::mac::ScopedCFTypeRef<SecKeychainRef> keychain( | |
232 CrSKeychainItemCopyKeychain(old_item)); | |
233 | |
234 ScopedCrSKeychainItemAttributesAndData old_attributes_and_data( | |
235 CrSKeychainItemCopyAttributesAndData(keychain, old_item)); | |
236 if (!old_attributes_and_data.get()) { | |
237 continue; | |
238 } | |
239 | |
240 // SecKeychainItemCreateFromContent fails if any attribute is zero-length, | |
241 // but old_attributes_and_data can contain zero-length attributes. Create | |
242 // a new attribute list devoid of zero-length attributes. | |
stuartmorgan
2012/05/10 06:50:54
Could this whole step be a helper function?
| |
243 UInt32 old_attribute_count = | |
244 old_attributes_and_data.attribute_list()->count; | |
245 std::vector<SecKeychainAttribute> new_attributes; | |
246 new_attributes.reserve(old_attribute_count); | |
247 for (UInt32 old_attribute_index = 0; | |
248 old_attribute_index < old_attribute_count; | |
249 ++old_attribute_index) { | |
250 SecKeychainAttribute* attribute = | |
251 &old_attributes_and_data.attribute_list()->attr[old_attribute_index]; | |
252 if (attribute->length) { | |
253 new_attributes.push_back(*attribute); | |
254 } | |
255 } | |
256 | |
257 SecKeychainAttributeList new_attribute_list; | |
258 new_attribute_list.count = new_attributes.size(); | |
259 new_attribute_list.attr = | |
260 new_attribute_list.count ? &new_attributes[0] : NULL; | |
261 | |
262 CrSKeychainItemAttributesAndData new_attributes_and_data = | |
263 *old_attributes_and_data.get(); | |
264 new_attributes_and_data.attribute_list = &new_attribute_list; | |
265 | |
266 // Delete the item last, to give everything else above a chance to bail | |
267 // out early, and to ensure that the old item is still present while it | |
268 // may still be used by the above code. | |
269 if (!CrSKeychainItemDelete(old_item)) { | |
270 continue; | |
271 } | |
272 | |
273 base::mac::ScopedCFTypeRef<SecKeychainItemRef> new_item( | |
274 CrSKeychainItemCreateFromContent(new_attributes_and_data, | |
275 keychain, | |
276 iterator->access())); | |
277 } | |
278 } | |
279 | |
280 } // namespace mac | |
281 } // namespace browser | |
282 } // namespace chrome | |
OLD | NEW |