/ OSX / libsecurity_keychain / lib / IdentityCursor.cpp
IdentityCursor.cpp
  1  /*
  2   * Copyright (c) 2002-2008,2011-2012 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   * IdentityCursor.cpp -- Working with IdentityCursor
 24   */
 25  
 26  #include <security_keychain/IdentityCursor.h>
 27  #include <security_keychain/Identity.h>
 28  #include <security_keychain/Trust.h>
 29  #include <security_keychain/Item.h>
 30  #include <security_keychain/Certificate.h>
 31  #include <security_keychain/KeyItem.h>
 32  #include <security_keychain/Globals.h>
 33  #include <security_cdsa_utilities/Schema.h>
 34  #include <security_cdsa_utilities/KeySchema.h>
 35  #include <Security/oidsalg.h>
 36  #include <Security/SecKeychainItemPriv.h>
 37  #include <security_utilities/simpleprefs.h>
 38  #include <sys/param.h>
 39  
 40  using namespace KeychainCore;
 41  
 42  IdentityCursorPolicyAndID::IdentityCursorPolicyAndID(const StorageManager::KeychainList &searchList, CSSM_KEYUSE keyUsage, CFStringRef idString, SecPolicyRef policy, bool returnOnlyValidIdentities) :
 43  	IdentityCursor(searchList, keyUsage),
 44  	mPolicy(policy),
 45  	mIDString(idString),
 46  	mReturnOnlyValidIdentities(returnOnlyValidIdentities),
 47  	mPreferredIdentityChecked(false),
 48  	mPreferredIdentity(nil)
 49  {
 50  	if (mPolicy) {
 51  		CFRetain(mPolicy);
 52  	}
 53  	if (mIDString) {
 54  		CFRetain(mIDString);
 55  	}
 56  }
 57  
 58  IdentityCursorPolicyAndID::~IdentityCursorPolicyAndID() _NOEXCEPT
 59  {
 60  	if (mPolicy) {
 61  		CFRelease(mPolicy);
 62  	}
 63  	if (mIDString) {
 64  		CFRelease(mIDString);
 65  	}
 66  }
 67  
 68  void
 69  IdentityCursorPolicyAndID::findPreferredIdentity()
 70  {
 71  	char idUTF8[MAXPATHLEN];
 72  	if (!mIDString || !CFStringGetCString(mIDString, idUTF8, sizeof(idUTF8)-1, kCFStringEncodingUTF8))
 73  		idUTF8[0] = (char)'\0';
 74  	uint32_t iprfValue = 'iprf'; // value is specified in host byte order, since kSecTypeItemAttr has type uint32 in the db schema
 75  	SecKeychainAttribute sAttrs[] = {
 76  		{ kSecTypeItemAttr, sizeof(uint32_t), &iprfValue },
 77  		{ kSecServiceItemAttr, (UInt32)strlen(idUTF8), (char *)idUTF8 }
 78  	};
 79  	SecKeychainAttributeList sAttrList = { sizeof(sAttrs) / sizeof(sAttrs[0]), sAttrs };
 80  
 81  //	StorageManager::KeychainList keychains;
 82  //	globals().storageManager.optionalSearchList((CFTypeRef)nil, keychains);
 83  
 84  	Item item;
 85  	KCCursor cursor(mSearchList /*keychains*/, kSecGenericPasswordItemClass, &sAttrList);
 86  	if (!cursor->next(item))
 87  		return;
 88  
 89  	// get persistent certificate reference
 90  	SecKeychainAttribute itemAttrs[] = { { kSecGenericItemAttr, 0, NULL } };
 91  	SecKeychainAttributeList itemAttrList = { sizeof(itemAttrs) / sizeof(itemAttrs[0]), itemAttrs };
 92  	item->getContent(NULL, &itemAttrList, NULL, NULL);
 93  
 94  	// find certificate, given persistent reference data
 95  	CFDataRef pItemRef = CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)itemAttrs[0].data, itemAttrs[0].length, kCFAllocatorNull);
 96  	SecKeychainItemRef certItemRef = nil;
 97  	OSStatus status = SecKeychainItemCopyFromPersistentReference(pItemRef, &certItemRef);
 98  	if (pItemRef)
 99  		CFRelease(pItemRef);
100  	item->freeContent(&itemAttrList, NULL);
101  	if (status || !certItemRef)
102  		return;
103  
104  	// create identity reference, given certificate
105  	Item certItem = ItemImpl::required(SecKeychainItemRef(certItemRef));
106  	SecPointer<Certificate> certificate(static_cast<Certificate *>(certItem.get()));
107  	SecPointer<Identity> identity(new Identity(mSearchList /*keychains*/, certificate));
108  
109  	mPreferredIdentity = identity;
110  
111  	if (certItemRef)
112  		CFRelease(certItemRef);
113  }
114  
115  bool
116  IdentityCursorPolicyAndID::next(SecPointer<Identity> &identity)
117  {
118  	SecPointer<Identity> currIdentity;
119  	Boolean identityOK = true;
120  
121  	if (!mPreferredIdentityChecked)
122  	{
123          try
124          {
125              findPreferredIdentity();
126          }
127          catch(...) {}
128  		mPreferredIdentityChecked = true;
129  		if (mPreferredIdentity)
130  		{
131  			identity = mPreferredIdentity;
132  			return true;
133  		}
134  	}
135  
136  	for (;;)
137  	{
138  		bool result = IdentityCursor::next(currIdentity);   // base class finds the next identity by keyUsage
139  		if ( result )
140  		{
141  			if (mPreferredIdentity && (currIdentity == mPreferredIdentity))
142  			{
143  				identityOK = false;	// we already returned this one, move on to the next
144  				continue;
145  			}
146  
147  			// If there was no policy specified, we're done.
148  			if ( !mPolicy )
149  			{
150  				identityOK = true; // return this identity
151  				break;
152  			}
153  
154  			// To reduce the number of (potentially expensive) trust evaluations performed, we need
155  			// to do some pre-processing to filter out certs that don't match the search criteria.
156  			// Rather than try to duplicate the TP's policy logic here, we'll just call the TP with
157  			// a single-element certificate array, no anchors, and no keychains to search.
158  
159  			SecPointer<Certificate> certificate = currIdentity->certificate();
160  			CFRef<SecCertificateRef> certRef(certificate->handle());
161  			CFRef<CFMutableArrayRef> anchorsArray(CFArrayCreateMutable(NULL, 1, NULL));
162  			CFRef<CFMutableArrayRef> certArray(CFArrayCreateMutable(NULL, 1, NULL));
163  			if ( !certArray || !anchorsArray )
164  			{
165  				identityOK = false; // skip this and move on to the next one
166  				continue;
167  			}
168  			CFArrayAppendValue(certArray, certRef);
169  
170  			SecPointer<Trust> trustLite = new Trust(certArray, mPolicy);
171  			StorageManager::KeychainList emptyList;
172  			// Set the anchors and keychain search list to be empty
173  			trustLite->anchors(anchorsArray);
174  			trustLite->searchLibs(emptyList);
175  			trustLite->evaluate();
176  			SecTrustResultType trustResult = trustLite->result();
177  
178  			if (trustResult == kSecTrustResultRecoverableTrustFailure ||
179  				trustResult == kSecTrustResultFatalTrustFailure)
180  			{
181  				CFArrayRef certChain = NULL;
182  				CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL, *evInfo = NULL;
183  				trustLite->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain));
184  				if (statusChain)
185  					evInfo = &statusChain[0];
186  				if (!evInfo || evInfo->NumStatusCodes > 0) // per-cert codes means we can't use this cert for this policy
187  					trustResult = kSecTrustResultInvalid; // handled below
188  				if (certChain)
189  					CFRelease(certChain);
190  			}
191  			if (trustResult == kSecTrustResultInvalid)
192  			{
193  				identityOK = false; // move on to the next one
194  				continue;
195  			}
196  
197  			// If trust evaluation isn't requested, we're done.
198  			if ( !mReturnOnlyValidIdentities )
199  			{
200  				identityOK = true; // return this identity
201  				break;
202  			}
203  
204  			// Perform a full trust evaluation on the certificate with the specified policy.
205  			SecPointer<Trust> trust = new Trust(certArray, mPolicy);
206  			trust->evaluate();
207  			trustResult = trust->result();
208  
209  			if (trustResult == kSecTrustResultInvalid ||
210  				trustResult == kSecTrustResultRecoverableTrustFailure ||
211  				trustResult == kSecTrustResultFatalTrustFailure)
212  			{
213  				identityOK = false; // move on to the next one
214  				continue;
215  			}
216  
217  			identityOK = true; // this one was OK; return it.
218  			break;
219  		}
220  		else
221  		{
222  			identityOK = false; // no more left.
223  			break;
224  		}
225  	}   // for(;;)
226  
227  	if ( identityOK )
228  	{
229  		identity = currIdentity; // caller will release the identity
230  		return true;
231  	}
232  	else
233  	{
234  		return false;
235  	}
236  }
237  
238  
239  IdentityCursor::IdentityCursor(const StorageManager::KeychainList &searchList, CSSM_KEYUSE keyUsage) :
240  	mSearchList(searchList),
241  	mKeyCursor(mSearchList, (SecItemClass) CSSM_DL_DB_RECORD_PRIVATE_KEY, NULL),
242  	mMutex(Mutex::recursive)
243  {
244  	StLock<Mutex>_(mMutex);
245  
246  	// If keyUsage is CSSM_KEYUSE_ANY then we need a key that can do everything
247  	if (keyUsage & CSSM_KEYUSE_ANY)
248  		keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT
249  						   | CSSM_KEYUSE_DERIVE | CSSM_KEYUSE_SIGN
250  						   | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_SIGN_RECOVER
251  						   | CSSM_KEYUSE_VERIFY_RECOVER | CSSM_KEYUSE_WRAP
252  						   | CSSM_KEYUSE_UNWRAP;
253  
254  	if (keyUsage & CSSM_KEYUSE_ENCRYPT)
255  		mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Encrypt, true);
256  	if (keyUsage & CSSM_KEYUSE_DECRYPT)
257  		mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Decrypt, true);
258  	if (keyUsage & CSSM_KEYUSE_DERIVE)
259  		mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Derive, true);
260  	if (keyUsage & CSSM_KEYUSE_SIGN)
261  		mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Sign, true);
262  	if (keyUsage & CSSM_KEYUSE_VERIFY)
263  		mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Verify, true);
264  	if (keyUsage & CSSM_KEYUSE_SIGN_RECOVER)
265  		mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::SignRecover, true);
266  	if (keyUsage & CSSM_KEYUSE_VERIFY_RECOVER)
267  		mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::VerifyRecover, true);
268  	if (keyUsage & CSSM_KEYUSE_WRAP)
269  		mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Wrap, true);
270  	if (keyUsage & CSSM_KEYUSE_UNWRAP)
271  		mKeyCursor->add(CSSM_DB_EQUAL, KeySchema::Unwrap, true);
272  }
273  
274  IdentityCursor::~IdentityCursor() _NOEXCEPT
275  {
276  }
277  
278  CFDataRef CF_RETURNS_RETAINED
279  IdentityCursor::pubKeyHashForSystemIdentity(CFStringRef domain)
280  {
281  	StLock<Mutex>_(mMutex);
282  
283      CFDataRef entryValue = nil;
284  	unique_ptr<Dictionary> identDict;
285  	Dictionary* d = Dictionary::CreateDictionary("com.apple.security.systemidentities", Dictionary::US_System);
286  	if (d)
287  	{
288  		identDict.reset(d);
289          entryValue = identDict->getDataValue(domain);
290          if (entryValue == nil) {
291              /* try for default entry if we're not already looking for default */
292              if(!CFEqual(domain, kSecIdentityDomainDefault)) {
293                  entryValue = identDict->getDataValue(kSecIdentityDomainDefault);
294              }
295          }
296      }
297  
298      if (entryValue) {
299          CFRetain(entryValue);
300      }
301      return entryValue;
302  }
303  
304  bool
305  IdentityCursor::next(SecPointer<Identity> &identity)
306  {
307  	StLock<Mutex>_(mMutex);
308  
309  	for (;;)
310  	{
311  		if (!mCertificateCursor)
312  		{
313  			Item key;
314  			if (!mKeyCursor->next(key))
315  				return false;
316  
317  			mCurrentKey = static_cast<KeyItem *>(key.get());
318  
319  			CssmClient::DbUniqueRecord uniqueId = mCurrentKey->dbUniqueRecord();
320  			CssmClient::DbAttributes dbAttributes(uniqueId->database(), 1);
321  			dbAttributes.add(KeySchema::Label);
322  			uniqueId->get(&dbAttributes, NULL);
323  			const CssmData &keyHash = dbAttributes[0];
324  
325  			mCertificateCursor = KCCursor(mSearchList, (SecItemClass) CSSM_DL_DB_RECORD_X509_CERTIFICATE, NULL);
326  			mCertificateCursor->add(CSSM_DB_EQUAL, Schema::kX509CertificatePublicKeyHash, keyHash);
327  
328              // if we have entries for the system identities, exclude their public key hashes in the search
329              CFDataRef systemDefaultCertPubKeyHash = pubKeyHashForSystemIdentity(kSecIdentityDomainDefault);
330              if (systemDefaultCertPubKeyHash) {
331                  CssmData pkHash((void *)CFDataGetBytePtr(systemDefaultCertPubKeyHash), CFDataGetLength(systemDefaultCertPubKeyHash));
332                  mCertificateCursor->add(CSSM_DB_NOT_EQUAL, Schema::kX509CertificatePublicKeyHash, pkHash);
333                  CFRelease(systemDefaultCertPubKeyHash);
334              }
335              CFDataRef kerbKDCCertPubKeyHash = pubKeyHashForSystemIdentity(kSecIdentityDomainKerberosKDC);
336              if (kerbKDCCertPubKeyHash) {
337                  CssmData pkHash((void *)CFDataGetBytePtr(kerbKDCCertPubKeyHash), CFDataGetLength(kerbKDCCertPubKeyHash));
338                  mCertificateCursor->add(CSSM_DB_NOT_EQUAL, Schema::kX509CertificatePublicKeyHash, pkHash);
339                  CFRelease(kerbKDCCertPubKeyHash);
340              }
341  		}
342  
343  		Item cert;
344  		if (mCertificateCursor->next(cert))
345  		{
346  			SecPointer<Certificate> certificate(static_cast<Certificate *>(cert.get()));
347  			identity = new Identity(mCurrentKey, certificate);
348  			return true;
349  		}
350  		else
351  			mCertificateCursor = KCCursor();
352  	}
353  }