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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/mac/keychain_reauthorize.h ('k') | chrome/browser/mac/security_wrappers.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/mac/keychain_reauthorize.cc
===================================================================
--- chrome/browser/mac/keychain_reauthorize.cc (revision 0)
+++ chrome/browser/mac/keychain_reauthorize.cc (revision 0)
@@ -0,0 +1,441 @@
+// 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 <Security/Security.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#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<std::string> 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<std::string>& 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<std::string>& 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<std::string>& 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<CrSKeychainItemAndAccess> KCSearchToKCItemsAndReauthorizedAccesses(
+ SecKeychainSearchRef search,
+ const std::vector<std::string>& requirement_matches,
+ SecTrustedApplicationRef this_application);
+
+// Given a SecKeychainAttributeList, strips out any zero-length attributes and
+// returns a vector containing the remaining attributes.
+std::vector<SecKeychainAttribute> 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<CrSKeychainItemAndAccess>&
+ 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<SecKeychainSearchRef> search(
+ CrSKeychainSearchCreateFromAttributes(NULL,
+ CSSM_DL_DB_RECORD_ANY,
+ NULL));
+
+ std::vector<std::string> requirement_matches =
+ RequirementMatches();
+
+ base::mac::ScopedCFTypeRef<SecTrustedApplicationRef> this_application(
+ CrSTrustedApplicationCreateFromPath(NULL));
+
+ std::vector<CrSKeychainItemAndAccess> items_and_reauthorized_accesses =
+ KCSearchToKCItemsAndReauthorizedAccesses(search,
+ requirement_matches,
+ this_application);
+
+ WriteKCItemsAndReauthorizedAccesses(items_and_reauthorized_accesses);
+}
+
+namespace {
+
+std::vector<std::string> 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<std::string> 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<CrSKeychainItemAndAccess> KCSearchToKCItemsAndReauthorizedAccesses(
+ SecKeychainSearchRef search,
+ const std::vector<std::string>& requirement_matches,
+ SecTrustedApplicationRef this_application) {
+ std::vector<CrSKeychainItemAndAccess> items_and_accesses;
+
+ base::mac::ScopedCFTypeRef<SecKeychainItemRef> item;
+ while (item.reset(CrSKeychainSearchCopyNext(search)), item) {
+ scoped_ptr<CrSKeychainItemAndAccess> 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<std::string>& requirement_matches,
+ SecTrustedApplicationRef this_application) {
+ if (!CrSKeychainItemTestAccess(item)) {
+ return NULL;
+ }
+
+ base::mac::ScopedCFTypeRef<SecAccessRef> access(
+ CrSKeychainItemCopyAccess(item));
+ base::mac::ScopedCFTypeRef<CFArrayRef> 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<std::string>& 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<SecACLRef>(
+ 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<std::string>& requirement_matches,
+ SecTrustedApplicationRef this_application) {
+ scoped_ptr<CrSACLSimpleContents> 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<SecTrustedApplicationRef>(
+ CFArrayGetValueAtIndex(acl_simple_contents->application_list,
+ application_index));
+ base::mac::ScopedCFTypeRef<SecRequirementRef> requirement(
+ CrSTrustedApplicationCopyRequirement(application));
+ base::mac::ScopedCFTypeRef<CFStringRef> 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<CrSKeychainItemAndAccess>&
+ items_and_reauthorized_accesses) {
+ for (std::vector<CrSKeychainItemAndAccess>::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<SecKeychainRef> 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<SecKeychainAttribute> 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<SecKeychainAttribute> 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<SecKeychainItemRef> new_item(
+ CrSKeychainItemCreateFromContent(new_attributes_and_data,
+ keychain,
+ item_and_reauthorized_access.access()));
+}
+
+std::vector<SecKeychainAttribute> KCAttributesWithoutZeroLength(
+ SecKeychainAttributeList* old_attribute_list) {
+ UInt32 old_attribute_count = old_attribute_list->count;
+ std::vector<SecKeychainAttribute> 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
« 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