From f0c58de58e969865a768f042c0798a8a5ab93d8f Mon Sep 17 00:00:00 2001 From: "mark@chromium.org" Date: Tue, 15 May 2012 22:11:28 +0000 Subject: Activate Keychain reauthorization. If the KeychainReauthorizeInAppMay2012 preference doesn't exist or is less than 2, Keychain reauthorization is performed at start-up. On successful reauthorization, this preference will be set to 2. If unsuccessful, it will be incremented by 1, giving a maximum of 2 unsuccessful attempts to perform this procedure before getting on with life. BUG=108238 TEST=none Review URL: https://chromiumcodereview.appspot.com/10310174 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@137265 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/mac/keychain_reauthorize.cc | 441 --------------------------- chrome/browser/mac/keychain_reauthorize.h | 25 ++ chrome/browser/mac/keychain_reauthorize.mm | 469 +++++++++++++++++++++++++++++ 3 files changed, 494 insertions(+), 441 deletions(-) delete mode 100644 chrome/browser/mac/keychain_reauthorize.cc create mode 100644 chrome/browser/mac/keychain_reauthorize.mm (limited to 'chrome/browser/mac') diff --git a/chrome/browser/mac/keychain_reauthorize.cc b/chrome/browser/mac/keychain_reauthorize.cc deleted file mode 100644 index b7617ac..0000000 --- a/chrome/browser/mac/keychain_reauthorize.cc +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/mac/keychain_reauthorize.h" - -#include - -#include -#include -#include - -#include "base/basictypes.h" -#include "base/mac/foundation_util.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/memory/scoped_ptr.h" -#include "base/stringprintf.h" -#include "base/sys_string_conversions.h" -#include "chrome/browser/mac/security_wrappers.h" - -namespace chrome { -namespace browser { -namespace mac { - -namespace { - -// Returns the set of requirement strings that ought to be reauthorized. -std::vector RequirementMatches(); - -// Reauthorizes an ACL by examining all of the applications it names, and upon -// finding any whose requirement matches any element of requirement_matches, -// replaces them with this_application. At most one instance of -// this_application will be added to the ACL. Subsequent applications whose -// requirement matches any element of requirement_matches will be removed from -// the ACL. Only the ACL is changed, nothing is written to disk. Returns true -// if any reauthorization is performed and thus acl is modified, and false -// otherwise. -bool ReauthorizeACL( - SecACLRef acl, - const std::vector& requirement_matches, - SecTrustedApplicationRef this_application); - -// Reauthorizes a list of ACLs by calling ReauthorizeACL for each ACL in the -// list. Only the ACL list is changed, nothing is written to disk. Returns -// true if ReauthorizeTrue returns true for any ACL in acl_list, indicating -// that at least one ACL in acl_list was modified and thus at least one child -// child of acl_list was reauthorized. -bool ReauthorizeACLList( - CFArrayRef acl_list, - const std::vector& requirement_matches, - SecTrustedApplicationRef this_application); - -// Reauthorizes a SecKeychainItemRef by calling ReauthorizeACLList to perform -// reauthorization on all ACLs that it contains. Nothing is written to disk. -// If any reauthorization was performed, returns a CrSKeychainItemAndAccess -// object containing the item and its access information. Otherwise, returns -// NULL. -CrSKeychainItemAndAccess* KCItemToKCItemAndReauthorizedAccess( - SecKeychainItemRef item, - const std::vector& requirement_matches, - SecTrustedApplicationRef this_application); - -// Reauthorizes multiple Keychain items by calling -// KCItemToKCItemAndReauthorizedAccess for each item returned by a Keychain -// search. Nothing is written to disk. Reauthorized items are returned. -std::vector KCSearchToKCItemsAndReauthorizedAccesses( - SecKeychainSearchRef search, - const std::vector& requirement_matches, - SecTrustedApplicationRef this_application); - -// Given a SecKeychainAttributeList, strips out any zero-length attributes and -// returns a vector containing the remaining attributes. -std::vector KCAttributesWithoutZeroLength( - SecKeychainAttributeList* old_attribute_list); - -// Given a CrSKeychainItemAndAccess that has had its access field -// reauthorized, places the reauthorized form into the Keychain by deleting -// the old item and replacing it with a new one whose access policy matches -// the reauthorized form. The new item is written to disk and becomes part of -// the Keychain, replacing what had been there previously. -void WriteKCItemAndReauthorizedAccess( - const CrSKeychainItemAndAccess& item_and_reauthorized_access); - -// Given a vector of CrSKeychainItemAndAccess objects, places the reauthorized -// forms of all of them into the Keychain by calling -// WriteKCItemAndReauthorizedAccess for each. The new items are written to -// disk and become part of the Keychain, replacing what had been there -// previously. -void WriteKCItemsAndReauthorizedAccesses( - const std::vector& - items_and_reauthorized_accesses); - -} // namespace - -void KeychainReauthorize() { - ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(FALSE); - - // Apple's documentation (Keychain Services Reference, Constants/Mac OS X - // Keychain Services API Constants/Keychain Item Class Constants) says to - // use CSSM_DL_DB_RECORD_ALL_KEYS, but that doesn't work. - // CSSM_DL_DB_RECORD_ANY (as used by SecurityTool's keychain-dump) does - // work. - base::mac::ScopedCFTypeRef search( - CrSKeychainSearchCreateFromAttributes(NULL, - CSSM_DL_DB_RECORD_ANY, - NULL)); - - std::vector requirement_matches = - RequirementMatches(); - - base::mac::ScopedCFTypeRef this_application( - CrSTrustedApplicationCreateFromPath(NULL)); - - std::vector items_and_reauthorized_accesses = - KCSearchToKCItemsAndReauthorizedAccesses(search, - requirement_matches, - this_application); - - WriteKCItemsAndReauthorizedAccesses(items_and_reauthorized_accesses); -} - -namespace { - -std::vector RequirementMatches() { - // See the designated requirement for a signed released build: - // codesign -d -r- "Google Chrome.app" - // - // Export the certificates from a signed released build: - // codesign -v --extract-certificates=/tmp/cert. "Google Chrome.app" - // (The extracted leaf certificate is at /tmp/cert.0; intermediates and root - // are at successive numbers.) - // - // Show some information about the exported certificates: - // openssl x509 -inform DER -in /tmp/cert.0 -noout -text -fingerprint - // (The "SHA1 Fingerprint" value printed by -fingerprint should match the - // hash used in a codesign designated requirement after allowing for obvious - // formatting differences.) - - const char* const kIdentifierMatches[] = { -#if defined(GOOGLE_CHROME_BUILD) - "com.google.Chrome", - "com.google.Chrome.canary", -#else - "org.chromium.Chromium", -#endif - }; - - const char* const kLeafCertificateHashMatches[] = { - // Only official released builds of Google Chrome have ever been signed - // (with a certificate that anyone knows about or cares about). -#if defined(GOOGLE_CHROME_BUILD) - // This is the new certificate that has not yet been used to sign Chrome, - // but will be. Once used, the reauthorization code will become obsolete - // until it's needed for some other purpose in the future. - // Subject: UID=EQHXZ8M8AV, CN=Developer ID Application: Google Inc., - // OU=EQHXZ8M8AV, O=Google Inc., C=US - // Issuer: CN=Developer ID Certification Authority, - // OU=Apple Certification Authority, O=Apple Inc., C=US - // Validity: 2012-04-26 14:10:10 UTC to 2017-04-27 14:10:10 UTC - // "85cee8254216185620ddc8851c7a9fc4dfe120ef", - - // This certificate was used on 2011-12-20 and 2011-12-21, but the "since - // 2010-07-19" one below was restored afterwards as an interim fix to the - // Keychain authorization problem. See http://crbug.com/108238 and - // http://crbug.com/62605. - // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, - // OU=Digital ID Class 3 - Java Object Signing, CN=Google Inc - // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, - // OU=Terms of use at https://www.verisign.com/rpa (c)10, - // CN=VeriSign Class 3 Code Signing 2010 CA - // Validity: 2011-11-14 00:00:00 UTC to 2014-11-13 23:59:59 UTC - "06c92bec3bbf32068cb9208563d004169448ee21", - - // This certificate has been used since 2010-07-19, except for the brief - // period when the certificate above was used. - // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, - // OU=Digital ID Class 3 - Java Object Signing, CN=Google Inc - // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, - // OU=Terms of use at https://www.verisign.com/rpa (c)09, - // CN=VeriSign Class 3 Code Signing 2009-2 CA - // Validity: 2010-02-22 00:00:00 UTC to 2012-02-22 23:59:59 UTC - "9481882581d8178db8b1649c0eaa4f9eb11288f0", - - // This certificate was used for all public Chrome releases prior to - // 2010-07-19. - // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, - // OU=Digital ID Class 3 - Netscape Object Signing, CN=Google Inc - // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, - // OU=Terms of use at https://www.verisign.com/rpa (c)04, - // CN=VeriSign Class 3 Code Signing 2004 CA - // Validity: 2007-06-19 00:00:00 UTC to 2010-06-18 23:59:59 UTC - "fe5008fe0da7a2033816752d6eafe95214f5a7e1", -#endif - }; - - std::vector requirement_matches; - requirement_matches.reserve(arraysize(kIdentifierMatches) * - ARRAYSIZE_UNSAFE(kLeafCertificateHashMatches)); - - for (size_t identifier_index = 0; - identifier_index < arraysize(kIdentifierMatches); - ++identifier_index) { - for (size_t leaf_certificate_hash_index = 0; - leaf_certificate_hash_index < - ARRAYSIZE_UNSAFE(kLeafCertificateHashMatches); - ++leaf_certificate_hash_index) { - requirement_matches.push_back(base::StringPrintf( - "identifier \"%s\" and certificate leaf = H\"%s\"", - kIdentifierMatches[identifier_index], - kLeafCertificateHashMatches[leaf_certificate_hash_index])); - } - } - - return requirement_matches; -} - -std::vector KCSearchToKCItemsAndReauthorizedAccesses( - SecKeychainSearchRef search, - const std::vector& requirement_matches, - SecTrustedApplicationRef this_application) { - std::vector items_and_accesses; - - base::mac::ScopedCFTypeRef item; - while (item.reset(CrSKeychainSearchCopyNext(search)), item) { - scoped_ptr item_and_access( - KCItemToKCItemAndReauthorizedAccess(item, - requirement_matches, - this_application)); - - if (item_and_access.get()) { - items_and_accesses.push_back(*item_and_access); - } - } - - return items_and_accesses; -} - -CrSKeychainItemAndAccess* KCItemToKCItemAndReauthorizedAccess( - SecKeychainItemRef item, - const std::vector& requirement_matches, - SecTrustedApplicationRef this_application) { - if (!CrSKeychainItemTestAccess(item)) { - return NULL; - } - - base::mac::ScopedCFTypeRef access( - CrSKeychainItemCopyAccess(item)); - base::mac::ScopedCFTypeRef acl_list( - CrSAccessCopyACLList(access)); - if (!acl_list) { - return NULL; - } - - bool acl_list_modified = ReauthorizeACLList(acl_list, - requirement_matches, - this_application); - if (!acl_list_modified) { - return NULL; - } - - return new CrSKeychainItemAndAccess(item, access); -} - -bool ReauthorizeACLList( - CFArrayRef acl_list, - const std::vector& requirement_matches, - SecTrustedApplicationRef this_application) { - bool acl_list_modified = false; - - CFIndex acl_count = CFArrayGetCount(acl_list); - for (CFIndex acl_index = 0; acl_index < acl_count; ++acl_index) { - SecACLRef acl = base::mac::CFCast( - CFArrayGetValueAtIndex(acl_list, acl_index)); - if (!acl) { - continue; - } - - if (ReauthorizeACL(acl, requirement_matches, this_application)) { - acl_list_modified = true; - } - } - - return acl_list_modified; -} - -bool ReauthorizeACL( - SecACLRef acl, - const std::vector& requirement_matches, - SecTrustedApplicationRef this_application) { - scoped_ptr acl_simple_contents( - CrSACLCopySimpleContents(acl)); - if (!acl_simple_contents.get() || - !acl_simple_contents->application_list) { - return false; - } - - CFMutableArrayRef application_list_mutable = NULL; - bool added_this_application = false; - - CFIndex application_count = - CFArrayGetCount(acl_simple_contents->application_list); - for (CFIndex application_index = 0; - application_index < application_count; - ++application_index) { - SecTrustedApplicationRef application = - base::mac::CFCast( - CFArrayGetValueAtIndex(acl_simple_contents->application_list, - application_index)); - base::mac::ScopedCFTypeRef requirement( - CrSTrustedApplicationCopyRequirement(application)); - base::mac::ScopedCFTypeRef requirement_string_cf( - CrSRequirementCopyString(requirement, kSecCSDefaultFlags)); - if (!requirement_string_cf) { - continue; - } - - std::string requirement_string = - base::SysCFStringRefToUTF8(requirement_string_cf); - if (std::find(requirement_matches.begin(), - requirement_matches.end(), - requirement_string) != requirement_matches.end()) { - if (!application_list_mutable) { - application_list_mutable = - CFArrayCreateMutableCopy(NULL, - application_count, - acl_simple_contents->application_list); - acl_simple_contents->application_list.reset( - application_list_mutable); - } - - if (!added_this_application) { - CFArraySetValueAtIndex(application_list_mutable, - application_index, - this_application); - added_this_application = true; - } else { - // Even though it's more bookkeeping to walk a list in the forward - // direction when there are removals, it's done here anyway to - // keep this_application at the position of the first match. - CFArrayRemoveValueAtIndex(application_list_mutable, - application_index); - --application_index; - --application_count; - } - } - } - - if (!application_list_mutable) { - return false; - } - - if (!CrSACLSetSimpleContents(acl, *acl_simple_contents.get())) { - return false; - } - - return true; -} - -void WriteKCItemsAndReauthorizedAccesses( - const std::vector& - items_and_reauthorized_accesses) { - for (std::vector::const_iterator iterator = - items_and_reauthorized_accesses.begin(); - iterator != items_and_reauthorized_accesses.end(); - ++iterator) { - WriteKCItemAndReauthorizedAccess(*iterator); - } -} - -void WriteKCItemAndReauthorizedAccess( - const CrSKeychainItemAndAccess& item_and_reauthorized_access) { - SecKeychainItemRef old_item = item_and_reauthorized_access.item(); - base::mac::ScopedCFTypeRef keychain( - CrSKeychainItemCopyKeychain(old_item)); - - ScopedCrSKeychainItemAttributesAndData old_attributes_and_data( - CrSKeychainItemCopyAttributesAndData(keychain, old_item)); - if (!old_attributes_and_data.get()) { - return; - } - - // SecKeychainItemCreateFromContent fails if any attribute is zero-length, - // but old_attributes_and_data can contain zero-length attributes. Create - // a new attribute list devoid of zero-length attributes. - // - // This is awkward: only the logic to build the - // std::vector is in KCAttributesWithoutZeroLength - // because the storage used for the new attribute list (the vector) needs to - // persist through the lifetime of this function. - // KCAttributesWithoutZeroLength doesn't return a - // CrSKeychainItemAttributesAndData (which could be held here in a - // ScopedCrSKeychainItemAttributesAndData) because it's more convenient to - // build the attribute list using std::vector and point the data at the copy - // in old_attributes_and_data, thus making nothing in new_attributes a - // strongly-held reference. - std::vector new_attributes = - KCAttributesWithoutZeroLength(old_attributes_and_data.attribute_list()); - SecKeychainAttributeList new_attribute_list; - new_attribute_list.count = new_attributes.size(); - new_attribute_list.attr = - new_attribute_list.count ? &new_attributes[0] : NULL; - CrSKeychainItemAttributesAndData new_attributes_and_data = - *old_attributes_and_data.get(); - new_attributes_and_data.attribute_list = &new_attribute_list; - - // Delete the item last, to give everything else above a chance to bail - // out early, and to ensure that the old item is still present while it - // may still be used by the above code. - if (!CrSKeychainItemDelete(old_item)) { - return; - } - - base::mac::ScopedCFTypeRef new_item( - CrSKeychainItemCreateFromContent(new_attributes_and_data, - keychain, - item_and_reauthorized_access.access())); -} - -std::vector KCAttributesWithoutZeroLength( - SecKeychainAttributeList* old_attribute_list) { - UInt32 old_attribute_count = old_attribute_list->count; - std::vector new_attributes; - new_attributes.reserve(old_attribute_count); - for (UInt32 old_attribute_index = 0; - old_attribute_index < old_attribute_count; - ++old_attribute_index) { - SecKeychainAttribute* attribute = - &old_attribute_list->attr[old_attribute_index]; - if (attribute->length) { - new_attributes.push_back(*attribute); - } - } - - return new_attributes; -} - -} // namespace - -} // namespace mac -} // namespace browser -} // namespace chrome diff --git a/chrome/browser/mac/keychain_reauthorize.h b/chrome/browser/mac/keychain_reauthorize.h index 823f47a..8afa695 100644 --- a/chrome/browser/mac/keychain_reauthorize.h +++ b/chrome/browser/mac/keychain_reauthorize.h @@ -6,6 +6,12 @@ #define CHROME_BROWSER_MAC_KEYCHAIN_REAUTHORIZE_H_ #pragma once +#ifdef __OBJC__ +@class NSString; +#else +class NSString; +#endif + namespace chrome { namespace browser { namespace mac { @@ -24,6 +30,25 @@ namespace mac { // decrypt those items), but any application can remove a Keychain item. void KeychainReauthorize(); +// Calls KeychainReauthorize, but only if it's determined that it's necessary. +// pref_key is looked up in the system's standard user defaults (preferences) +// and if its integer value is less than max_tries, KeychainReauthorize is +// attempted. Before the attempt, the preference is incremented, allowing a +// finite number of incomplete attempts at performing the KeychainReauthorize +// operation. When the step completes successfully, the preference is set to +// max_tries to prevent further attempts, and the preference name with the +// word "Success" appended is also stored with a boolean value of YES, +// disambiguating between the cases where the step completed successfully and +// the step completed unsuccessfully while reaching the maximum number of +// tries. +// +// The system's standard user defaults for the application are used +// (~/Library/Preferences/com.google.Chrome.plist, +// com.google.Chrome.canary.plist, etc.) instead of Chrome preferences because +// Keychain access is tied more closely to the bundle identifier and signed +// product than it is to any specific profile (--user-data-dir). +void KeychainReauthorizeIfNeeded(NSString* pref_key, int max_tries); + } // namespace mac } // namespace browser } // namespace chrome diff --git a/chrome/browser/mac/keychain_reauthorize.mm b/chrome/browser/mac/keychain_reauthorize.mm new file mode 100644 index 0000000..5b94889 --- /dev/null +++ b/chrome/browser/mac/keychain_reauthorize.mm @@ -0,0 +1,469 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/mac/keychain_reauthorize.h" + +#import +#include + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/memory/scoped_ptr.h" +#include "base/metrics/histogram.h" +#include "base/stringprintf.h" +#include "base/sys_string_conversions.h" +#include "chrome/browser/mac/security_wrappers.h" + +namespace chrome { +namespace browser { +namespace mac { + +namespace { + +// Returns the set of requirement strings that ought to be reauthorized. +std::vector RequirementMatches(); + +// Reauthorizes an ACL by examining all of the applications it names, and upon +// finding any whose requirement matches any element of requirement_matches, +// replaces them with this_application. At most one instance of +// this_application will be added to the ACL. Subsequent applications whose +// requirement matches any element of requirement_matches will be removed from +// the ACL. Only the ACL is changed, nothing is written to disk. Returns true +// if any reauthorization is performed and thus acl is modified, and false +// otherwise. +bool ReauthorizeACL( + SecACLRef acl, + const std::vector& requirement_matches, + SecTrustedApplicationRef this_application); + +// Reauthorizes a list of ACLs by calling ReauthorizeACL for each ACL in the +// list. Only the ACL list is changed, nothing is written to disk. Returns +// true if ReauthorizeTrue returns true for any ACL in acl_list, indicating +// that at least one ACL in acl_list was modified and thus at least one child +// child of acl_list was reauthorized. +bool ReauthorizeACLList( + CFArrayRef acl_list, + const std::vector& requirement_matches, + SecTrustedApplicationRef this_application); + +// Reauthorizes a SecKeychainItemRef by calling ReauthorizeACLList to perform +// reauthorization on all ACLs that it contains. Nothing is written to disk. +// If any reauthorization was performed, returns a CrSKeychainItemAndAccess +// object containing the item and its access information. Otherwise, returns +// NULL. +CrSKeychainItemAndAccess* KCItemToKCItemAndReauthorizedAccess( + SecKeychainItemRef item, + const std::vector& requirement_matches, + SecTrustedApplicationRef this_application); + +// Reauthorizes multiple Keychain items by calling +// KCItemToKCItemAndReauthorizedAccess for each item returned by a Keychain +// search. Nothing is written to disk. Reauthorized items are returned. +std::vector KCSearchToKCItemsAndReauthorizedAccesses( + SecKeychainSearchRef search, + const std::vector& requirement_matches, + SecTrustedApplicationRef this_application); + +// Given a SecKeychainAttributeList, strips out any zero-length attributes and +// returns a vector containing the remaining attributes. +std::vector KCAttributesWithoutZeroLength( + SecKeychainAttributeList* old_attribute_list); + +// Given a CrSKeychainItemAndAccess that has had its access field +// reauthorized, places the reauthorized form into the Keychain by deleting +// the old item and replacing it with a new one whose access policy matches +// the reauthorized form. The new item is written to disk and becomes part of +// the Keychain, replacing what had been there previously. +void WriteKCItemAndReauthorizedAccess( + const CrSKeychainItemAndAccess& item_and_reauthorized_access); + +// Given a vector of CrSKeychainItemAndAccess objects, places the reauthorized +// forms of all of them into the Keychain by calling +// WriteKCItemAndReauthorizedAccess for each. The new items are written to +// disk and become part of the Keychain, replacing what had been there +// previously. +void WriteKCItemsAndReauthorizedAccesses( + const std::vector& + items_and_reauthorized_accesses); + +} // namespace + +void KeychainReauthorize() { + ScopedSecKeychainSetUserInteractionAllowed user_interaction_allowed(FALSE); + + // Apple's documentation (Keychain Services Reference, Constants/Mac OS X + // Keychain Services API Constants/Keychain Item Class Constants) says to + // use CSSM_DL_DB_RECORD_ALL_KEYS, but that doesn't work. + // CSSM_DL_DB_RECORD_ANY (as used by SecurityTool's keychain-dump) does + // work. + base::mac::ScopedCFTypeRef search( + CrSKeychainSearchCreateFromAttributes(NULL, + CSSM_DL_DB_RECORD_ANY, + NULL)); + + std::vector requirement_matches = + RequirementMatches(); + + base::mac::ScopedCFTypeRef this_application( + CrSTrustedApplicationCreateFromPath(NULL)); + + std::vector items_and_reauthorized_accesses = + KCSearchToKCItemsAndReauthorizedAccesses(search, + requirement_matches, + this_application); + + WriteKCItemsAndReauthorizedAccesses(items_and_reauthorized_accesses); +} + +void KeychainReauthorizeIfNeeded(NSString* pref_key, int max_tries) { + NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults]; + int pref_value = [user_defaults integerForKey:pref_key]; + + if (pref_value < max_tries) { + if (pref_value > 0) { + // Logs the number of previous tries that didn't complete. + UMA_HISTOGRAM_COUNTS("OSX.KeychainReauthorizeIfNeeded", pref_value); + } + + ++pref_value; + [user_defaults setInteger:pref_value forKey:pref_key]; + [user_defaults synchronize]; + + chrome::browser::mac::KeychainReauthorize(); + + [user_defaults setInteger:max_tries forKey:pref_key]; + NSString* success_pref_key = [pref_key stringByAppendingString:@"Success"]; + [user_defaults setBool:YES forKey:success_pref_key]; + [user_defaults synchronize]; + + // Logs the try number (1, 2) that succeeded. + UMA_HISTOGRAM_COUNTS("OSX.KeychainReauthorizeIfNeededSuccess", pref_value); + } +} + +namespace { + +std::vector RequirementMatches() { + // See the designated requirement for a signed released build: + // codesign -d -r- "Google Chrome.app" + // + // Export the certificates from a signed released build: + // codesign -v --extract-certificates=/tmp/cert. "Google Chrome.app" + // (The extracted leaf certificate is at /tmp/cert.0; intermediates and root + // are at successive numbers.) + // + // Show some information about the exported certificates: + // openssl x509 -inform DER -in /tmp/cert.0 -noout -text -fingerprint + // (The "SHA1 Fingerprint" value printed by -fingerprint should match the + // hash used in a codesign designated requirement after allowing for obvious + // formatting differences.) + + const char* const kIdentifierMatches[] = { +#if defined(GOOGLE_CHROME_BUILD) + "com.google.Chrome", + "com.google.Chrome.canary", +#else + "org.chromium.Chromium", +#endif + }; + + const char* const kLeafCertificateHashMatches[] = { + // Only official released builds of Google Chrome have ever been signed + // (with a certificate that anyone knows about or cares about). +#if defined(GOOGLE_CHROME_BUILD) + // This is the new certificate that has not yet been used to sign Chrome, + // but will be. Once used, the reauthorization code will become obsolete + // until it's needed for some other purpose in the future. + // Subject: UID=EQHXZ8M8AV, CN=Developer ID Application: Google Inc., + // OU=EQHXZ8M8AV, O=Google Inc., C=US + // Issuer: CN=Developer ID Certification Authority, + // OU=Apple Certification Authority, O=Apple Inc., C=US + // Validity: 2012-04-26 14:10:10 UTC to 2017-04-27 14:10:10 UTC + // "85cee8254216185620ddc8851c7a9fc4dfe120ef", + + // This certificate was used on 2011-12-20 and 2011-12-21, but the "since + // 2010-07-19" one below was restored afterwards as an interim fix to the + // Keychain authorization problem. See http://crbug.com/108238 and + // http://crbug.com/62605. + // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, + // OU=Digital ID Class 3 - Java Object Signing, CN=Google Inc + // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, + // OU=Terms of use at https://www.verisign.com/rpa (c)10, + // CN=VeriSign Class 3 Code Signing 2010 CA + // Validity: 2011-11-14 00:00:00 UTC to 2014-11-13 23:59:59 UTC + "06c92bec3bbf32068cb9208563d004169448ee21", + + // This certificate has been used since 2010-07-19, except for the brief + // period when the certificate above was used. + // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, + // OU=Digital ID Class 3 - Java Object Signing, CN=Google Inc + // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, + // OU=Terms of use at https://www.verisign.com/rpa (c)09, + // CN=VeriSign Class 3 Code Signing 2009-2 CA + // Validity: 2010-02-22 00:00:00 UTC to 2012-02-22 23:59:59 UTC + "9481882581d8178db8b1649c0eaa4f9eb11288f0", + + // This certificate was used for all public Chrome releases prior to + // 2010-07-19. + // Subject: C=US, ST=California, L=Mountain View, O=Google Inc, + // OU=Digital ID Class 3 - Netscape Object Signing, CN=Google Inc + // Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, + // OU=Terms of use at https://www.verisign.com/rpa (c)04, + // CN=VeriSign Class 3 Code Signing 2004 CA + // Validity: 2007-06-19 00:00:00 UTC to 2010-06-18 23:59:59 UTC + "fe5008fe0da7a2033816752d6eafe95214f5a7e1", +#endif + }; + + std::vector requirement_matches; + requirement_matches.reserve(arraysize(kIdentifierMatches) * + ARRAYSIZE_UNSAFE(kLeafCertificateHashMatches)); + + for (size_t identifier_index = 0; + identifier_index < arraysize(kIdentifierMatches); + ++identifier_index) { + for (size_t leaf_certificate_hash_index = 0; + leaf_certificate_hash_index < + ARRAYSIZE_UNSAFE(kLeafCertificateHashMatches); + ++leaf_certificate_hash_index) { + requirement_matches.push_back(base::StringPrintf( + "identifier \"%s\" and certificate leaf = H\"%s\"", + kIdentifierMatches[identifier_index], + kLeafCertificateHashMatches[leaf_certificate_hash_index])); + } + } + + return requirement_matches; +} + +std::vector KCSearchToKCItemsAndReauthorizedAccesses( + SecKeychainSearchRef search, + const std::vector& requirement_matches, + SecTrustedApplicationRef this_application) { + std::vector items_and_accesses; + + base::mac::ScopedCFTypeRef item; + while (item.reset(CrSKeychainSearchCopyNext(search)), item) { + scoped_ptr item_and_access( + KCItemToKCItemAndReauthorizedAccess(item, + requirement_matches, + this_application)); + + if (item_and_access.get()) { + items_and_accesses.push_back(*item_and_access); + } + } + + return items_and_accesses; +} + +CrSKeychainItemAndAccess* KCItemToKCItemAndReauthorizedAccess( + SecKeychainItemRef item, + const std::vector& requirement_matches, + SecTrustedApplicationRef this_application) { + if (!CrSKeychainItemTestAccess(item)) { + return NULL; + } + + base::mac::ScopedCFTypeRef access( + CrSKeychainItemCopyAccess(item)); + base::mac::ScopedCFTypeRef acl_list( + CrSAccessCopyACLList(access)); + if (!acl_list) { + return NULL; + } + + bool acl_list_modified = ReauthorizeACLList(acl_list, + requirement_matches, + this_application); + if (!acl_list_modified) { + return NULL; + } + + return new CrSKeychainItemAndAccess(item, access); +} + +bool ReauthorizeACLList( + CFArrayRef acl_list, + const std::vector& requirement_matches, + SecTrustedApplicationRef this_application) { + bool acl_list_modified = false; + + CFIndex acl_count = CFArrayGetCount(acl_list); + for (CFIndex acl_index = 0; acl_index < acl_count; ++acl_index) { + SecACLRef acl = base::mac::CFCast( + CFArrayGetValueAtIndex(acl_list, acl_index)); + if (!acl) { + continue; + } + + if (ReauthorizeACL(acl, requirement_matches, this_application)) { + acl_list_modified = true; + } + } + + return acl_list_modified; +} + +bool ReauthorizeACL( + SecACLRef acl, + const std::vector& requirement_matches, + SecTrustedApplicationRef this_application) { + scoped_ptr acl_simple_contents( + CrSACLCopySimpleContents(acl)); + if (!acl_simple_contents.get() || + !acl_simple_contents->application_list) { + return false; + } + + CFMutableArrayRef application_list_mutable = NULL; + bool added_this_application = false; + + CFIndex application_count = + CFArrayGetCount(acl_simple_contents->application_list); + for (CFIndex application_index = 0; + application_index < application_count; + ++application_index) { + SecTrustedApplicationRef application = + base::mac::CFCast( + CFArrayGetValueAtIndex(acl_simple_contents->application_list, + application_index)); + base::mac::ScopedCFTypeRef requirement( + CrSTrustedApplicationCopyRequirement(application)); + base::mac::ScopedCFTypeRef requirement_string_cf( + CrSRequirementCopyString(requirement, kSecCSDefaultFlags)); + if (!requirement_string_cf) { + continue; + } + + std::string requirement_string = + base::SysCFStringRefToUTF8(requirement_string_cf); + if (std::find(requirement_matches.begin(), + requirement_matches.end(), + requirement_string) != requirement_matches.end()) { + if (!application_list_mutable) { + application_list_mutable = + CFArrayCreateMutableCopy(NULL, + application_count, + acl_simple_contents->application_list); + acl_simple_contents->application_list.reset( + application_list_mutable); + } + + if (!added_this_application) { + CFArraySetValueAtIndex(application_list_mutable, + application_index, + this_application); + added_this_application = true; + } else { + // Even though it's more bookkeeping to walk a list in the forward + // direction when there are removals, it's done here anyway to + // keep this_application at the position of the first match. + CFArrayRemoveValueAtIndex(application_list_mutable, + application_index); + --application_index; + --application_count; + } + } + } + + if (!application_list_mutable) { + return false; + } + + if (!CrSACLSetSimpleContents(acl, *acl_simple_contents.get())) { + return false; + } + + return true; +} + +void WriteKCItemsAndReauthorizedAccesses( + const std::vector& + items_and_reauthorized_accesses) { + for (std::vector::const_iterator iterator = + items_and_reauthorized_accesses.begin(); + iterator != items_and_reauthorized_accesses.end(); + ++iterator) { + WriteKCItemAndReauthorizedAccess(*iterator); + } +} + +void WriteKCItemAndReauthorizedAccess( + const CrSKeychainItemAndAccess& item_and_reauthorized_access) { + SecKeychainItemRef old_item = item_and_reauthorized_access.item(); + base::mac::ScopedCFTypeRef keychain( + CrSKeychainItemCopyKeychain(old_item)); + + ScopedCrSKeychainItemAttributesAndData old_attributes_and_data( + CrSKeychainItemCopyAttributesAndData(keychain, old_item)); + if (!old_attributes_and_data.get()) { + return; + } + + // SecKeychainItemCreateFromContent fails if any attribute is zero-length, + // but old_attributes_and_data can contain zero-length attributes. Create + // a new attribute list devoid of zero-length attributes. + // + // This is awkward: only the logic to build the + // std::vector is in KCAttributesWithoutZeroLength + // because the storage used for the new attribute list (the vector) needs to + // persist through the lifetime of this function. + // KCAttributesWithoutZeroLength doesn't return a + // CrSKeychainItemAttributesAndData (which could be held here in a + // ScopedCrSKeychainItemAttributesAndData) because it's more convenient to + // build the attribute list using std::vector and point the data at the copy + // in old_attributes_and_data, thus making nothing in new_attributes a + // strongly-held reference. + std::vector new_attributes = + KCAttributesWithoutZeroLength(old_attributes_and_data.attribute_list()); + SecKeychainAttributeList new_attribute_list; + new_attribute_list.count = new_attributes.size(); + new_attribute_list.attr = + new_attribute_list.count ? &new_attributes[0] : NULL; + CrSKeychainItemAttributesAndData new_attributes_and_data = + *old_attributes_and_data.get(); + new_attributes_and_data.attribute_list = &new_attribute_list; + + // Delete the item last, to give everything else above a chance to bail + // out early, and to ensure that the old item is still present while it + // may still be used by the above code. + if (!CrSKeychainItemDelete(old_item)) { + return; + } + + base::mac::ScopedCFTypeRef new_item( + CrSKeychainItemCreateFromContent(new_attributes_and_data, + keychain, + item_and_reauthorized_access.access())); +} + +std::vector KCAttributesWithoutZeroLength( + SecKeychainAttributeList* old_attribute_list) { + UInt32 old_attribute_count = old_attribute_list->count; + std::vector new_attributes; + new_attributes.reserve(old_attribute_count); + for (UInt32 old_attribute_index = 0; + old_attribute_index < old_attribute_count; + ++old_attribute_index) { + SecKeychainAttribute* attribute = + &old_attribute_list->attr[old_attribute_index]; + if (attribute->length) { + new_attributes.push_back(*attribute); + } + } + + return new_attributes; +} + +} // namespace + +} // namespace mac +} // namespace browser +} // namespace chrome -- cgit v1.1