| 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 #import <Foundation/Foundation.h> | |
| 8 #include <Security/Security.h> | |
| 9 | |
| 10 #include <algorithm> | |
| 11 #include <string> | |
| 12 #include <vector> | |
| 13 | |
| 14 #include "base/basictypes.h" | |
| 15 #include "base/mac/foundation_util.h" | |
| 16 #include "base/mac/scoped_cftyperef.h" | |
| 17 #include "base/memory/scoped_ptr.h" | |
| 18 #include "base/metrics/histogram.h" | |
| 19 #include "base/strings/stringprintf.h" | |
| 20 #include "base/strings/sys_string_conversions.h" | |
| 21 #include "chrome/browser/mac/security_wrappers.h" | |
| 22 | |
| 23 namespace chrome { | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 // Returns the requirement string embedded within a SecTrustedApplicationRef, | |
| 28 // or an empty string on error. | |
| 29 std::string RequirementStringForApplication( | |
| 30 SecTrustedApplicationRef application); | |
| 31 | |
| 32 // Returns the set of requirement strings that ought to be reauthorized. In a | |
| 33 // bundled application, the requirement string from |application| will also be | |
| 34 // added to the hard-coded list. This allows an at-launch reauthorization to | |
| 35 // re-reauthorize anything done by a previous at-update reauthorization. | |
| 36 // Although items reauthorized during the at-update step will work properly in | |
| 37 // every way, they contain a reference to the missing reauthorization stub | |
| 38 // executable from the disk image in the Keychain, resulting in no icon and | |
| 39 // a weird name like "com.google" (non-Canary) or "com.google.Chrome" | |
| 40 // (Canary). Because reauthorization is controlled by a preference that limits | |
| 41 // it to a single successful run at update and a single successful run at | |
| 42 // launch, protection already exists against perpetually reauthorizing items. | |
| 43 // This addition exists simply to make the Keychain Access UI match | |
| 44 // expectations. | |
| 45 std::vector<std::string> GetRequirementMatches( | |
| 46 SecTrustedApplicationRef application); | |
| 47 | |
| 48 // Reauthorizes an ACL by examining all of the applications it names, and upon | |
| 49 // finding any whose requirement matches any element of requirement_matches, | |
| 50 // replaces them with this_application. At most one instance of | |
| 51 // this_application will be added to the ACL. Subsequent applications whose | |
| 52 // requirement matches any element of requirement_matches will be removed from | |
| 53 // the ACL. Only the ACL is changed, nothing is written to disk. Returns true | |
| 54 // if any reauthorization is performed and thus acl is modified, and false | |
| 55 // otherwise. | |
| 56 bool ReauthorizeACL( | |
| 57 SecACLRef acl, | |
| 58 const std::vector<std::string>& requirement_matches, | |
| 59 SecTrustedApplicationRef this_application); | |
| 60 | |
| 61 // Reauthorizes a list of ACLs by calling ReauthorizeACL for each ACL in the | |
| 62 // list. Only the ACL list is changed, nothing is written to disk. Returns | |
| 63 // true if ReauthorizeTrue returns true for any ACL in acl_list, indicating | |
| 64 // that at least one ACL in acl_list was modified and thus at least one child | |
| 65 // child of acl_list was reauthorized. | |
| 66 bool ReauthorizeACLList( | |
| 67 CFArrayRef acl_list, | |
| 68 const std::vector<std::string>& requirement_matches, | |
| 69 SecTrustedApplicationRef this_application); | |
| 70 | |
| 71 // Reauthorizes a SecKeychainItemRef by calling ReauthorizeACLList to perform | |
| 72 // reauthorization on all ACLs that it contains. Nothing is written to disk. | |
| 73 // If any reauthorization was performed, returns a CrSKeychainItemAndAccess | |
| 74 // object containing the item and its access information. Otherwise, returns | |
| 75 // NULL. | |
| 76 CrSKeychainItemAndAccess* KCItemToKCItemAndReauthorizedAccess( | |
| 77 SecKeychainItemRef item, | |
| 78 const std::vector<std::string>& requirement_matches, | |
| 79 SecTrustedApplicationRef this_application); | |
| 80 | |
| 81 // Reauthorizes multiple Keychain items by calling | |
| 82 // KCItemToKCItemAndReauthorizedAccess for each item returned by a Keychain | |
| 83 // search. Nothing is written to disk. Reauthorized items are returned. | |
| 84 std::vector<CrSKeychainItemAndAccess> KCSearchToKCItemsAndReauthorizedAccesses( | |
| 85 SecKeychainSearchRef search, | |
| 86 const std::vector<std::string>& requirement_matches, | |
| 87 SecTrustedApplicationRef this_application); | |
| 88 | |
| 89 // Given a SecKeychainAttributeList, strips out any zero-length attributes and | |
| 90 // returns a vector containing the remaining attributes. | |
| 91 std::vector<SecKeychainAttribute> KCAttributesWithoutZeroLength( | |
| 92 SecKeychainAttributeList* old_attribute_list); | |
| 93 | |
| 94 // Given a CrSKeychainItemAndAccess that has had its access field | |
| 95 // reauthorized, places the reauthorized form into the Keychain by deleting | |
| 96 // the old item and replacing it with a new one whose access policy matches | |
| 97 // the reauthorized form. The new item is written to disk and becomes part of | |
| 98 // the Keychain, replacing what had been there previously. | |
| 99 void WriteKCItemAndReauthorizedAccess( | |
| 100 const CrSKeychainItemAndAccess& item_and_reauthorized_access); | |
| 101 | |
| 102 // Given a vector of CrSKeychainItemAndAccess objects, places the reauthorized | |
| 103 // forms of all of them into the Keychain by calling | |
| 104 // WriteKCItemAndReauthorizedAccess for each. The new items are written to | |
| 105 // disk and become part of the Keychain, replacing what had been there | |
| 106 // previously. | |
| 107 void WriteKCItemsAndReauthorizedAccesses( | |
| 108 const std::vector<CrSKeychainItemAndAccess>& | |
| 109 items_and_reauthorized_accesses); | |
| 110 | |
| 111 } // namespace | |
| 112 | |
| 113 void KeychainReauthorize() { | |
| 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::ScopedCFTypeRef<SecKeychainSearchRef> search( | |
| 122 CrSKeychainSearchCreateFromAttributes(NULL, CSSM_DL_DB_RECORD_ANY, NULL)); | |
| 123 | |
| 124 base::ScopedCFTypeRef<SecTrustedApplicationRef> this_application( | |
| 125 CrSTrustedApplicationCreateFromPath(NULL)); | |
| 126 | |
| 127 std::vector<std::string> requirement_matches = | |
| 128 GetRequirementMatches(this_application); | |
| 129 | |
| 130 std::vector<CrSKeychainItemAndAccess> items_and_reauthorized_accesses = | |
| 131 KCSearchToKCItemsAndReauthorizedAccesses(search, | |
| 132 requirement_matches, | |
| 133 this_application); | |
| 134 | |
| 135 WriteKCItemsAndReauthorizedAccesses(items_and_reauthorized_accesses); | |
| 136 } | |
| 137 | |
| 138 void KeychainReauthorizeIfNeeded(NSString* pref_key, int max_tries) { | |
| 139 NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults]; | |
| 140 int pref_value = [user_defaults integerForKey:pref_key]; | |
| 141 | |
| 142 if (pref_value < max_tries) { | |
| 143 if (pref_value > 0) { | |
| 144 // Logs the number of previous tries that didn't complete. | |
| 145 if (base::mac::AmIBundled()) { | |
| 146 UMA_HISTOGRAM_COUNTS("OSX.KeychainReauthorizeIfNeeded", pref_value); | |
| 147 } else { | |
| 148 UMA_HISTOGRAM_COUNTS("OSX.KeychainReauthorizeIfNeededAtUpdate", | |
| 149 pref_value); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 ++pref_value; | |
| 154 [user_defaults setInteger:pref_value forKey:pref_key]; | |
| 155 [user_defaults synchronize]; | |
| 156 | |
| 157 KeychainReauthorize(); | |
| 158 | |
| 159 [user_defaults setInteger:max_tries forKey:pref_key]; | |
| 160 NSString* success_pref_key = [pref_key stringByAppendingString:@"Success"]; | |
| 161 [user_defaults setBool:YES forKey:success_pref_key]; | |
| 162 [user_defaults synchronize]; | |
| 163 | |
| 164 // Logs the try number (1, 2) that succeeded. | |
| 165 if (base::mac::AmIBundled()) { | |
| 166 UMA_HISTOGRAM_COUNTS("OSX.KeychainReauthorizeIfNeededSuccess", | |
| 167 pref_value); | |
| 168 } else { | |
| 169 UMA_HISTOGRAM_COUNTS("OSX.KeychainReauthorizeIfNeededAtUpdateSuccess", | |
| 170 pref_value); | |
| 171 } | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 namespace { | |
| 176 | |
| 177 std::string RequirementStringForApplication( | |
| 178 SecTrustedApplicationRef application) { | |
| 179 base::ScopedCFTypeRef<SecRequirementRef> requirement( | |
| 180 CrSTrustedApplicationCopyRequirement(application)); | |
| 181 base::ScopedCFTypeRef<CFStringRef> requirement_string_cf( | |
| 182 CrSRequirementCopyString(requirement, kSecCSDefaultFlags)); | |
| 183 if (!requirement_string_cf) { | |
| 184 return std::string(); | |
| 185 } | |
| 186 | |
| 187 std::string requirement_string = | |
| 188 base::SysCFStringRefToUTF8(requirement_string_cf); | |
| 189 | |
| 190 return requirement_string; | |
| 191 } | |
| 192 | |
| 193 std::vector<std::string> GetRequirementMatches( | |
| 194 SecTrustedApplicationRef application) { | |
| 195 // See the designated requirement for a signed released build: | |
| 196 // codesign -d -r- "Google Chrome.app" | |
| 197 // | |
| 198 // Export the certificates from a signed released build: | |
| 199 // codesign -v --extract-certificates=/tmp/cert. "Google Chrome.app" | |
| 200 // (The extracted leaf certificate is at /tmp/cert.0; intermediates and root | |
| 201 // are at successive numbers.) | |
| 202 // | |
| 203 // Show some information about the exported certificates: | |
| 204 // openssl x509 -inform DER -in /tmp/cert.0 -noout -text -fingerprint | |
| 205 // (The "SHA1 Fingerprint" value printed by -fingerprint should match the | |
| 206 // hash used in a codesign designated requirement after allowing for obvious | |
| 207 // formatting differences.) | |
| 208 | |
| 209 const char* const kIdentifierMatches[] = { | |
| 210 #if defined(GOOGLE_CHROME_BUILD) | |
| 211 "com.google.Chrome", | |
| 212 "com.google.Chrome.canary", | |
| 213 #else | |
| 214 "org.chromium.Chromium", | |
| 215 #endif | |
| 216 }; | |
| 217 | |
| 218 const char* const kLeafCertificateHashMatches[] = { | |
| 219 // Only official released builds of Google Chrome have ever been signed | |
| 220 // (with a certificate that anyone knows about or cares about). | |
| 221 #if defined(GOOGLE_CHROME_BUILD) | |
| 222 // This is the new certificate that has not yet been used to sign Chrome, | |
| 223 // but will be. Once used, the reauthorization code will become obsolete | |
| 224 // until it's needed for some other purpose in the future. | |
| 225 // Subject: UID=EQHXZ8M8AV, CN=Developer ID Application: Google Inc., | |
| 226 // OU=EQHXZ8M8AV, O=Google Inc., C=US | |
| 227 // Issuer: CN=Developer ID Certification Authority, | |
| 228 // OU=Apple Certification Authority, O=Apple Inc., C=US | |
| 229 // Validity: 2012-04-26 14:10:10 UTC to 2017-04-27 14:10:10 UTC | |
| 230 // "85cee8254216185620ddc8851c7a9fc4dfe120ef", | |
| 231 | |
| 232 // This certificate was used on 2011-12-20 and 2011-12-21, but the "since | |
| 233 // 2010-07-19" one below was restored afterwards as an interim fix to the | |
| 234 // Keychain authorization problem. See http://crbug.com/108238 and | |
| 235 // http://crbug.com/62605. | |
| 236 // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, | |
| 237 // OU=Digital ID Class 3 - Java Object Signing, CN=Google Inc | |
| 238 // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, | |
| 239 // OU=Terms of use at https://www.verisign.com/rpa (c)10, | |
| 240 // CN=VeriSign Class 3 Code Signing 2010 CA | |
| 241 // Validity: 2011-11-14 00:00:00 UTC to 2014-11-13 23:59:59 UTC | |
| 242 "06c92bec3bbf32068cb9208563d004169448ee21", | |
| 243 | |
| 244 // This certificate has been used since 2010-07-19, except for the brief | |
| 245 // period when the certificate above was used. | |
| 246 // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, | |
| 247 // OU=Digital ID Class 3 - Java Object Signing, CN=Google Inc | |
| 248 // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, | |
| 249 // OU=Terms of use at https://www.verisign.com/rpa (c)09, | |
| 250 // CN=VeriSign Class 3 Code Signing 2009-2 CA | |
| 251 // Validity: 2010-02-22 00:00:00 UTC to 2012-02-22 23:59:59 UTC | |
| 252 "9481882581d8178db8b1649c0eaa4f9eb11288f0", | |
| 253 | |
| 254 // This certificate was used for all public Chrome releases prior to | |
| 255 // 2010-07-19. | |
| 256 // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, | |
| 257 // OU=Digital ID Class 3 - Netscape Object Signing, CN=Google Inc | |
| 258 // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, | |
| 259 // OU=Terms of use at https://www.verisign.com/rpa (c)04, | |
| 260 // CN=VeriSign Class 3 Code Signing 2004 CA | |
| 261 // Validity: 2007-06-19 00:00:00 UTC to 2010-06-18 23:59:59 UTC | |
| 262 "fe5008fe0da7a2033816752d6eafe95214f5a7e1", | |
| 263 #endif | |
| 264 }; | |
| 265 | |
| 266 std::vector<std::string> requirement_matches; | |
| 267 requirement_matches.reserve(arraysize(kIdentifierMatches) * | |
| 268 ARRAYSIZE_UNSAFE(kLeafCertificateHashMatches)); | |
| 269 | |
| 270 for (size_t identifier_index = 0; | |
| 271 identifier_index < arraysize(kIdentifierMatches); | |
| 272 ++identifier_index) { | |
| 273 for (size_t leaf_certificate_hash_index = 0; | |
| 274 leaf_certificate_hash_index < | |
| 275 ARRAYSIZE_UNSAFE(kLeafCertificateHashMatches); | |
| 276 ++leaf_certificate_hash_index) { | |
| 277 requirement_matches.push_back(base::StringPrintf( | |
| 278 "identifier \"%s\" and certificate leaf = H\"%s\"", | |
| 279 kIdentifierMatches[identifier_index], | |
| 280 kLeafCertificateHashMatches[leaf_certificate_hash_index])); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 if (application && base::mac::AmIBundled()) { | |
| 285 std::string application_requirement = | |
| 286 RequirementStringForApplication(application); | |
| 287 requirement_matches.push_back(application_requirement); | |
| 288 } | |
| 289 | |
| 290 return requirement_matches; | |
| 291 } | |
| 292 | |
| 293 std::vector<CrSKeychainItemAndAccess> KCSearchToKCItemsAndReauthorizedAccesses( | |
| 294 SecKeychainSearchRef search, | |
| 295 const std::vector<std::string>& requirement_matches, | |
| 296 SecTrustedApplicationRef this_application) { | |
| 297 std::vector<CrSKeychainItemAndAccess> items_and_accesses; | |
| 298 | |
| 299 base::ScopedCFTypeRef<SecKeychainItemRef> item; | |
| 300 while (item.reset(CrSKeychainSearchCopyNext(search)), item) { | |
| 301 scoped_ptr<CrSKeychainItemAndAccess> item_and_access( | |
| 302 KCItemToKCItemAndReauthorizedAccess(item, | |
| 303 requirement_matches, | |
| 304 this_application)); | |
| 305 | |
| 306 if (item_and_access.get()) { | |
| 307 items_and_accesses.push_back(*item_and_access); | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 return items_and_accesses; | |
| 312 } | |
| 313 | |
| 314 CrSKeychainItemAndAccess* KCItemToKCItemAndReauthorizedAccess( | |
| 315 SecKeychainItemRef item, | |
| 316 const std::vector<std::string>& requirement_matches, | |
| 317 SecTrustedApplicationRef this_application) { | |
| 318 if (!CrSKeychainItemTestAccess(item)) { | |
| 319 return NULL; | |
| 320 } | |
| 321 | |
| 322 base::ScopedCFTypeRef<SecAccessRef> access(CrSKeychainItemCopyAccess(item)); | |
| 323 base::ScopedCFTypeRef<CFArrayRef> acl_list(CrSAccessCopyACLList(access)); | |
| 324 if (!acl_list) { | |
| 325 return NULL; | |
| 326 } | |
| 327 | |
| 328 bool acl_list_modified = ReauthorizeACLList(acl_list, | |
| 329 requirement_matches, | |
| 330 this_application); | |
| 331 if (!acl_list_modified) { | |
| 332 return NULL; | |
| 333 } | |
| 334 | |
| 335 return new CrSKeychainItemAndAccess(item, access); | |
| 336 } | |
| 337 | |
| 338 bool ReauthorizeACLList( | |
| 339 CFArrayRef acl_list, | |
| 340 const std::vector<std::string>& requirement_matches, | |
| 341 SecTrustedApplicationRef this_application) { | |
| 342 bool acl_list_modified = false; | |
| 343 | |
| 344 CFIndex acl_count = CFArrayGetCount(acl_list); | |
| 345 for (CFIndex acl_index = 0; acl_index < acl_count; ++acl_index) { | |
| 346 SecACLRef acl = base::mac::CFCast<SecACLRef>( | |
| 347 CFArrayGetValueAtIndex(acl_list, acl_index)); | |
| 348 if (!acl) { | |
| 349 continue; | |
| 350 } | |
| 351 | |
| 352 if (ReauthorizeACL(acl, requirement_matches, this_application)) { | |
| 353 acl_list_modified = true; | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 return acl_list_modified; | |
| 358 } | |
| 359 | |
| 360 bool ReauthorizeACL( | |
| 361 SecACLRef acl, | |
| 362 const std::vector<std::string>& requirement_matches, | |
| 363 SecTrustedApplicationRef this_application) { | |
| 364 scoped_ptr<CrSACLSimpleContents> acl_simple_contents( | |
| 365 CrSACLCopySimpleContents(acl)); | |
| 366 if (!acl_simple_contents.get() || | |
| 367 !acl_simple_contents->application_list) { | |
| 368 return false; | |
| 369 } | |
| 370 | |
| 371 CFMutableArrayRef application_list_mutable = NULL; | |
| 372 bool added_this_application = false; | |
| 373 | |
| 374 CFIndex application_count = | |
| 375 CFArrayGetCount(acl_simple_contents->application_list); | |
| 376 for (CFIndex application_index = 0; | |
| 377 application_index < application_count; | |
| 378 ++application_index) { | |
| 379 SecTrustedApplicationRef application = | |
| 380 base::mac::CFCast<SecTrustedApplicationRef>( | |
| 381 CFArrayGetValueAtIndex(acl_simple_contents->application_list, | |
| 382 application_index)); | |
| 383 std::string requirement_string = | |
| 384 RequirementStringForApplication(application); | |
| 385 if (requirement_string.empty()) { | |
| 386 continue; | |
| 387 } | |
| 388 | |
| 389 if (std::find(requirement_matches.begin(), | |
| 390 requirement_matches.end(), | |
| 391 requirement_string) != requirement_matches.end()) { | |
| 392 if (!application_list_mutable) { | |
| 393 application_list_mutable = | |
| 394 CFArrayCreateMutableCopy(NULL, | |
| 395 application_count, | |
| 396 acl_simple_contents->application_list); | |
| 397 acl_simple_contents->application_list.reset( | |
| 398 application_list_mutable); | |
| 399 } | |
| 400 | |
| 401 if (!added_this_application) { | |
| 402 CFArraySetValueAtIndex(application_list_mutable, | |
| 403 application_index, | |
| 404 this_application); | |
| 405 added_this_application = true; | |
| 406 } else { | |
| 407 // Even though it's more bookkeeping to walk a list in the forward | |
| 408 // direction when there are removals, it's done here anyway to | |
| 409 // keep this_application at the position of the first match. | |
| 410 CFArrayRemoveValueAtIndex(application_list_mutable, | |
| 411 application_index); | |
| 412 --application_index; | |
| 413 --application_count; | |
| 414 } | |
| 415 } | |
| 416 } | |
| 417 | |
| 418 if (!application_list_mutable) { | |
| 419 return false; | |
| 420 } | |
| 421 | |
| 422 if (!CrSACLSetSimpleContents(acl, *acl_simple_contents.get())) { | |
| 423 return false; | |
| 424 } | |
| 425 | |
| 426 return true; | |
| 427 } | |
| 428 | |
| 429 void WriteKCItemsAndReauthorizedAccesses( | |
| 430 const std::vector<CrSKeychainItemAndAccess>& | |
| 431 items_and_reauthorized_accesses) { | |
| 432 for (std::vector<CrSKeychainItemAndAccess>::const_iterator iterator = | |
| 433 items_and_reauthorized_accesses.begin(); | |
| 434 iterator != items_and_reauthorized_accesses.end(); | |
| 435 ++iterator) { | |
| 436 WriteKCItemAndReauthorizedAccess(*iterator); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 void WriteKCItemAndReauthorizedAccess( | |
| 441 const CrSKeychainItemAndAccess& item_and_reauthorized_access) { | |
| 442 SecKeychainItemRef old_item = item_and_reauthorized_access.item(); | |
| 443 base::ScopedCFTypeRef<SecKeychainRef> keychain( | |
| 444 CrSKeychainItemCopyKeychain(old_item)); | |
| 445 | |
| 446 ScopedCrSKeychainItemAttributesAndData old_attributes_and_data( | |
| 447 CrSKeychainItemCopyAttributesAndData(keychain, old_item)); | |
| 448 if (!old_attributes_and_data.get()) { | |
| 449 return; | |
| 450 } | |
| 451 | |
| 452 // CrSKeychainItemCreateFromContent (SecKeychainItemCreateFromContent) | |
| 453 // returns errKCNoSuchAttr (errSecNoSuchAttr) when asked to add an item of | |
| 454 // type kSecPrivateKeyItemClass. This would happen after the original | |
| 455 // private key was deleted, resulting in data loss. I can't figure out how | |
| 456 // SecKeychainItemCreateFromContent wants private keys added. Skip them, | |
| 457 // only doing the reauthorization for Keychain item types known to work, | |
| 458 // the item types expected to be used by most users and those that are | |
| 459 // synced. See http://crbug.com/130738 and | |
| 460 // http://lists.apple.com/archives/apple-cdsa/2006/Jan/msg00025.html . | |
| 461 switch (old_attributes_and_data.item_class()) { | |
| 462 case kSecInternetPasswordItemClass: | |
| 463 case kSecGenericPasswordItemClass: | |
| 464 break; | |
| 465 default: | |
| 466 return; | |
| 467 } | |
| 468 | |
| 469 // SecKeychainItemCreateFromContent fails if any attribute is zero-length, | |
| 470 // but old_attributes_and_data can contain zero-length attributes. Create | |
| 471 // a new attribute list devoid of zero-length attributes. | |
| 472 // | |
| 473 // This is awkward: only the logic to build the | |
| 474 // std::vector<SecKeychainAttribute> is in KCAttributesWithoutZeroLength | |
| 475 // because the storage used for the new attribute list (the vector) needs to | |
| 476 // persist through the lifetime of this function. | |
| 477 // KCAttributesWithoutZeroLength doesn't return a | |
| 478 // CrSKeychainItemAttributesAndData (which could be held here in a | |
| 479 // ScopedCrSKeychainItemAttributesAndData) because it's more convenient to | |
| 480 // build the attribute list using std::vector and point the data at the copy | |
| 481 // in old_attributes_and_data, thus making nothing in new_attributes a | |
| 482 // strongly-held reference. | |
| 483 std::vector<SecKeychainAttribute> new_attributes = | |
| 484 KCAttributesWithoutZeroLength(old_attributes_and_data.attribute_list()); | |
| 485 SecKeychainAttributeList new_attribute_list; | |
| 486 new_attribute_list.count = new_attributes.size(); | |
| 487 new_attribute_list.attr = | |
| 488 new_attribute_list.count ? &new_attributes[0] : NULL; | |
| 489 CrSKeychainItemAttributesAndData new_attributes_and_data = | |
| 490 *old_attributes_and_data.get(); | |
| 491 new_attributes_and_data.attribute_list = &new_attribute_list; | |
| 492 | |
| 493 // Delete the item last, to give everything else above a chance to bail | |
| 494 // out early, and to ensure that the old item is still present while it | |
| 495 // may still be used by the above code. | |
| 496 if (!CrSKeychainItemDelete(old_item)) { | |
| 497 return; | |
| 498 } | |
| 499 | |
| 500 base::ScopedCFTypeRef<SecKeychainItemRef> new_item( | |
| 501 CrSKeychainItemCreateFromContent(new_attributes_and_data, | |
| 502 keychain, | |
| 503 item_and_reauthorized_access.access())); | |
| 504 } | |
| 505 | |
| 506 std::vector<SecKeychainAttribute> KCAttributesWithoutZeroLength( | |
| 507 SecKeychainAttributeList* old_attribute_list) { | |
| 508 UInt32 old_attribute_count = old_attribute_list->count; | |
| 509 std::vector<SecKeychainAttribute> new_attributes; | |
| 510 new_attributes.reserve(old_attribute_count); | |
| 511 for (UInt32 old_attribute_index = 0; | |
| 512 old_attribute_index < old_attribute_count; | |
| 513 ++old_attribute_index) { | |
| 514 SecKeychainAttribute* attribute = | |
| 515 &old_attribute_list->attr[old_attribute_index]; | |
| 516 if (attribute->length) { | |
| 517 new_attributes.push_back(*attribute); | |
| 518 } | |
| 519 } | |
| 520 | |
| 521 return new_attributes; | |
| 522 } | |
| 523 | |
| 524 } // namespace | |
| 525 | |
| 526 } // namespace chrome | |
| OLD | NEW |