/ OSX / libsecurity_keychain / lib / defaultcreds.cpp
defaultcreds.cpp
  1  /*
  2   * Copyright (c) 2004,2011-2012,2014 Apple Inc. All Rights Reserved.
  3   * 
  4   * @APPLE_LICENSE_HEADER_START@
  5   * 
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   * 
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   * 
 21   * @APPLE_LICENSE_HEADER_END@
 22   */
 23  
 24  //
 25  // defaultcreds - default computations for keychain open credentials
 26  //
 27  #include "Keychains.h"
 28  #include "defaultcreds.h"
 29  #include "StorageManager.h"
 30  #include "Globals.h"
 31  #include "KCCursor.h"
 32  #include <security_cdsa_utilities/Schema.h>
 33  #include <algorithm>
 34  
 35  
 36  namespace Security {
 37  namespace KeychainCore {
 38  
 39  using namespace CssmClient;
 40  
 41  
 42  DefaultCredentials::DefaultCredentials(KeychainImpl* kcImpl, Allocator &alloc)
 43  	: TrackingAllocator(alloc), AutoCredentials(static_cast<TrackingAllocator&>(*this)),
 44  	  mMade(false), mKeychainImpl (kcImpl)
 45  {
 46  }
 47  
 48  
 49  void DefaultCredentials::clear()
 50  {
 51  	TrackingAllocator::reset();
 52  	mNeededItems.clear();
 53  	mMade = false;
 54  }
 55  
 56  
 57  //
 58  // The main driver.
 59  // This scans a database for referral records and forms corresponding
 60  // credentials to trigger unlocks.
 61  // Returns true if any valid unlock credentials were found; false otherwise.
 62  // Only throws if the database is messed up.
 63  //
 64  bool DefaultCredentials::operator () (Db database)
 65  {
 66  	if (!mMade) {
 67  		try {
 68  			// before we do anything else, see if we have a relation in the database of the appropriate type
 69  			KeychainSchema keychainSchema = mKeychainImpl->keychainSchema();
 70  			if (keychainSchema->hasRecordType(UnlockReferralRecord::recordType))
 71  			{
 72  				clear();			
 73  				Table<UnlockReferralRecord> referrals(database);
 74  				for (Table<UnlockReferralRecord>::iterator it = referrals.begin(); it != referrals.end(); it++) {
 75  					switch ((*it)->type()) {
 76  					case CSSM_APPLE_UNLOCK_TYPE_KEY_DIRECT:
 77  					case CSSM_APPLE_UNLOCK_TYPE_WRAPPED_PRIVATE:
 78  						keyReferral(**it);
 79  						break;
 80  					case CSSM_APPLE_UNLOCK_TYPE_KEYBAG:
 81  						keybagReferral(**it);
 82  						break;
 83  					default:
 84  						secinfo("kcreferral", "referral type %lu (to %s) not supported",
 85  							(unsigned long)(*it)->type(), (*it)->dbName().c_str());
 86  						break;
 87  					}
 88  				}
 89  			}
 90  			secinfo("kcreferral", "%lu samples generated", (unsigned long)size());
 91  		} catch (...) {
 92  			secinfo("kcreferral", "exception setting default credentials for %s; using standard value", database->name());
 93  		}
 94  		mMade = true;
 95  	}
 96  	
 97  	return size() > 0;	// got credentials?
 98  }
 99  
100  
101  //
102  // Process a single referral record. This will handle all known types
103  // of referrals, other than keybag (see keybagReferral).
104  //
105  void DefaultCredentials::keyReferral(const UnlockReferralRecord &ref)
106  {
107  	secinfo("kcreferral", "processing type %ld referral to %s",
108  		(long)ref.type(), ref.dbName().c_str());
109  	DLDbIdentifier identifier(ref.dbName().c_str(), ref.dbGuid(), ref.dbSSID(), ref.dbSSType());
110  
111  	// first, try the keychain indicated
112  	try {
113  		KeychainList list;
114  		list.push_back(globals().storageManager.keychain(identifier));
115  		if (unlockKey(ref, list))	// try just this database...
116  			return;						// ... bingo!
117  	} catch (...) { }
118  	
119  	// try the entire search list (just in case)
120  	try {
121  		secinfo("kcreferral", "no joy with %s; trying the entire keychain list for guid %s",
122  			ref.dbName().c_str(), ref.dbGuid().toString().c_str());
123  		unlockKey(ref, fallbackSearchList(identifier));
124  		return;
125  	} catch (...) { }
126  	secinfo("kcreferral", "no luck at all; we'll skip this record");
127  }
128  
129  
130  bool DefaultCredentials::unlockKey(const UnlockReferralRecord &ref, const KeychainList &list)
131  {
132  	bool foundSome = false;
133  	try {
134  		// form the query
135  		SecKeychainAttribute attributes[1] = {
136  			{ kSecKeyLabel, (UInt32)ref.keyLabel().length(), ref.keyLabel().data() }
137  		};
138  		SecKeychainAttributeList search = { 1, attributes };
139  		CSSM_DB_RECORDTYPE recordType =
140  			(ref.type() == CSSM_APPLE_UNLOCK_TYPE_KEY_DIRECT) ?
141  				CSSM_DL_DB_RECORD_SYMMETRIC_KEY : CSSM_DL_DB_RECORD_PRIVATE_KEY;
142  		KCCursor cursor(list, (SecItemClass) recordType, &search);
143  		
144  		Item keyItem;
145  		while (cursor->next(keyItem)) {
146  			secinfo("kcreferral", "located source key in %s", keyItem->keychain()->name());
147  			
148  			// get a reference to the key in the provider keychain
149  			CssmClient::Key key = dynamic_cast<KeyItem &>(*keyItem).key();
150  			const CssmKey &masterKey = key;
151  			
152  			// get the CSP handle FOR THE UNLOCKING KEY'S KEYCHAIN
153  			CSSM_CSP_HANDLE cspHandle = key->csp()->handle();
154  
155  			// (a)symmetric-key form: KCLOCK, (A)SYMMETRIC_KEY, cspHandle, masterKey
156  			// Note that the last list element ("ref") is doing an implicit cast to a
157  			// CssmData, which passes the data portion of the UnlockReferralRecord
158  			append(TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
159  				new(allocator) ListElement((recordType==CSSM_DL_DB_RECORD_SYMMETRIC_KEY)?
160  					CSSM_WORDID_SYMMETRIC_KEY:CSSM_WORDID_ASYMMETRIC_KEY),
161  				new(allocator) ListElement(allocator, CssmData::wrap(cspHandle)),
162  				new(allocator) ListElement(allocator, CssmData::wrap(masterKey)),
163  				new(allocator) ListElement(allocator, ref.get())		
164  				));
165  
166  			// let's make sure everything we need stays around
167  			mNeededItems.insert(keyItem);
168  			foundSome = true;
169  		}
170  	} catch (...) {
171  		// (ignore it)
172  	}
173  	return foundSome;
174  }
175  
176  void
177  DefaultCredentials::keybagReferral(const UnlockReferralRecord &ref)
178  {
179  	secinfo("kcreferral", "processing type %ld referral", (long)ref.type());
180  
181  	try {
182  		// assemble and add CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK item
183  		append(TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
184  			new(allocator) ListElement(CSSM_WORDID_KEYBAG_KEY),
185  			new(allocator) ListElement(allocator, CssmData::wrap(ref.dbGuid())),
186  			new(allocator) ListElement(allocator, ref.get())
187  			));
188  	} catch (...) {
189  	}
190  }
191  
192  //
193  // Take the official keychain search list, and return those keychains whose
194  // module Guid matches the one given. Essentially, this focuses the search list
195  // to a particular type of keychain.
196  //
197  struct NotGuid {
198  	NotGuid(const Guid &g) : guid(g) { }
199  	const Guid &guid;
200  	bool operator () (Keychain kc) { return kc->database()->dl()->guid() != guid; }
201  };
202  
203  DefaultCredentials::KeychainList DefaultCredentials::fallbackSearchList(const DLDbIdentifier &ident)
204  {
205  	KeychainList list;
206  	globals().storageManager.getSearchList(list);
207  	list.erase(remove_if(list.begin(), list.end(), NotGuid(ident.ssuid().guid())), list.end());
208  	return list;
209  }
210  
211  
212  }	// namespace KeychainCore
213  }	// namespace Security