]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_keychain/lib/TrustAdditions.cpp
Security-58286.51.6.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / TrustAdditions.cpp
CommitLineData
b1ab9ed8 1/*
fa7225c8 2 * Copyright (c) 2002-2009,2011-2015 Apple Inc. All Rights Reserved.
b1ab9ed8
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
fa7225c8 5 *
b1ab9ed8
A
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.
fa7225c8 12 *
b1ab9ed8
A
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.
fa7225c8 20 *
b1ab9ed8
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24//
25// TrustAdditions.cpp
26//
27#include "TrustAdditions.h"
28#include "TrustKeychains.h"
29#include "SecBridge.h"
30#include <security_keychain/SecCFTypes.h>
31#include <security_keychain/Globals.h>
32#include <security_keychain/Certificate.h>
33#include <security_keychain/Item.h>
34#include <security_keychain/KCCursor.h>
35#include <security_keychain/KCUtilities.h>
36
37#include <sys/stat.h>
38#include <sys/file.h>
39#include <sys/unistd.h>
40#include <string>
41#include <AvailabilityMacros.h>
42#include <CoreFoundation/CoreFoundation.h>
43#include <CommonCrypto/CommonDigest.h>
427c49bc 44#include <Security/SecBase.h>
b1ab9ed8 45#include <Security/Security.h>
5c19dc3a 46#include <Security/SecCertificatePriv.h>
b1ab9ed8
A
47#include <Security/cssmtype.h>
48#include <Security/cssmapplePriv.h> // for CSSM_APPLE_TP_OCSP_OPTIONS, CSSM_APPLE_TP_OCSP_OPT_FLAGS
49
50#include "SecTrustPriv.h"
51#include "SecTrustSettings.h"
52#include "SecTrustSettingsPriv.h"
53
54//
55// Macros
56//
57#define BEGIN_SECAPI_INTERNAL_CALL \
58 try {
59#define END_SECAPI_INTERNAL_CALL \
60 } /* status is only set on error */ \
61 catch (const MacOSError &err) { status=err.osStatus(); } \
62 catch (const CommonError &err) { status=SecKeychainErrFromOSStatus(err.osStatus()); } \
427c49bc
A
63 catch (const std::bad_alloc &) { status=errSecAllocate; } \
64 catch (...) { status=errSecInternalComponent; }
b1ab9ed8 65
4d3cab3d
A
66#ifdef NDEBUG
67/* this actually compiles to nothing */
fa7225c8 68#define trustDebug(args...) secinfo("trust", ## args)
4d3cab3d
A
69#else
70#define trustDebug(args...) printf(args)
71#endif
72
b1ab9ed8
A
73//
74// Static constants
75//
76static const char *EV_ROOTS_PLIST_SYSTEM_PATH = "/System/Library/Keychains/EVRoots.plist";
77static const char *SYSTEM_ROOTS_PLIST_SYSTEM_PATH = "/System/Library/Keychains/SystemRootCertificates.keychain";
78static const char *X509ANCHORS_SYSTEM_PATH = "/System/Library/Keychains/X509Anchors";
79
80//
81// Static functions
82//
6b200bc3 83static CFArrayRef CF_RETURNS_RETAINED _allowedRootCertificatesForOidString(CFStringRef oidString);
b1ab9ed8 84static CSSM_DATA_PTR _copyFieldDataForOid(CSSM_OID_PTR oid, CSSM_DATA_PTR cert, CSSM_CL_HANDLE clHandle);
6b200bc3
A
85static CFStringRef CF_RETURNS_RETAINED _decimalStringForOid(CSSM_OID_PTR oid);
86static CFDictionaryRef CF_RETURNS_RETAINED _evCAOidDict();
b1ab9ed8 87static void _freeFieldData(CSSM_DATA_PTR value, CSSM_OID_PTR oid, CSSM_CL_HANDLE clHandle);
6b200bc3 88static CFStringRef CF_RETURNS_RETAINED _oidStringForCertificatePolicies(const CE_CertPolicies *certPolicies);
b1ab9ed8
A
89static SecCertificateRef _rootCertificateWithSubjectOfCertificate(SecCertificateRef certificate);
90static SecCertificateRef _rootCertificateWithSubjectKeyIDOfCertificate(SecCertificateRef certificate);
91
92// utility function to safely release (and clear) the given CFTypeRef variable.
93//
6b200bc3 94static void SafeCFRelease(void * CF_CONSUMED cfTypeRefPtr)
b1ab9ed8
A
95{
96 CFTypeRef *obj = (CFTypeRef *)cfTypeRefPtr;
97 if (obj && *obj) {
98 CFRelease(*obj);
99 *obj = NULL;
100 }
101}
102
103// utility function to create a CFDataRef from the contents of the specified file;
104// caller must release
105//
866f8763 106static CFDataRef CF_RETURNS_RETAINED dataWithContentsOfFile(const char *fileName)
b1ab9ed8
A
107{
108 int rtn;
109 int fd;
110 struct stat sb;
427c49bc 111 size_t fileSize;
b1ab9ed8
A
112 UInt8 *fileData = NULL;
113 CFDataRef outCFData = NULL;
114
115 fd = open(fileName, O_RDONLY, 0);
116 if(fd < 0)
117 return NULL;
118
119 rtn = fstat(fd, &sb);
120 if(rtn)
121 goto errOut;
122
427c49bc 123 fileSize = (size_t)sb.st_size;
b1ab9ed8
A
124 fileData = (UInt8 *) malloc(fileSize);
125 if(fileData == NULL)
126 goto errOut;
127
427c49bc 128 rtn = (int)lseek(fd, 0, SEEK_SET);
b1ab9ed8
A
129 if(rtn < 0)
130 goto errOut;
131
427c49bc 132 rtn = (int)read(fd, fileData, fileSize);
b1ab9ed8
A
133 if(rtn != (int)fileSize) {
134 rtn = EIO;
135 } else {
136 rtn = 0;
137 outCFData = CFDataCreate(NULL, fileData, fileSize);
138 }
139errOut:
140 close(fd);
141 if (fileData) {
142 free(fileData);
143 }
144 return outCFData;
145}
146
147// returns a SecKeychainRef for the system root certificate store; caller must release
148//
149static SecKeychainRef systemRootStore()
150{
151 SecKeychainStatus keychainStatus = 0;
152 SecKeychainRef systemRoots = NULL;
427c49bc 153 OSStatus status = errSecSuccess;
b1ab9ed8
A
154 // note: Sec* APIs are not re-entrant due to the API lock
155 // status = SecKeychainOpen(SYSTEM_ROOTS_PLIST_SYSTEM_PATH, &systemRoots);
156 BEGIN_SECAPI_INTERNAL_CALL
157 systemRoots=globals().storageManager.make(SYSTEM_ROOTS_PLIST_SYSTEM_PATH, false)->handle();
158 END_SECAPI_INTERNAL_CALL
159
427c49bc 160 // SecKeychainOpen will return errSecSuccess even if the file didn't exist on disk.
b1ab9ed8
A
161 // We need to do a further check using SecKeychainGetStatus().
162 if (!status && systemRoots) {
163 // note: Sec* APIs are not re-entrant due to the API lock
164 // status = SecKeychainGetStatus(systemRoots, &keychainStatus);
165 BEGIN_SECAPI_INTERNAL_CALL
166 keychainStatus=(SecKeychainStatus)Keychain::optional(systemRoots)->status();
167 END_SECAPI_INTERNAL_CALL
168 }
169 if (status || !systemRoots) {
170 // SystemRootCertificates.keychain can't be opened; look in X509Anchors instead.
171 SafeCFRelease(&systemRoots);
172 // note: Sec* APIs are not re-entrant due to the API lock
173 // status = SecKeychainOpen(X509ANCHORS_SYSTEM_PATH, &systemRoots);
174 BEGIN_SECAPI_INTERNAL_CALL
175 systemRoots=globals().storageManager.make(X509ANCHORS_SYSTEM_PATH, false)->handle();
176 END_SECAPI_INTERNAL_CALL
427c49bc 177 // SecKeychainOpen will return errSecSuccess even if the file didn't exist on disk.
b1ab9ed8
A
178 // We need to do a further check using SecKeychainGetStatus().
179 if (!status && systemRoots) {
180 // note: Sec* APIs are not re-entrant due to the API lock
181 // status = SecKeychainGetStatus(systemRoots, &keychainStatus);
182 BEGIN_SECAPI_INTERNAL_CALL
183 keychainStatus=(SecKeychainStatus)Keychain::optional(systemRoots)->status();
184 END_SECAPI_INTERNAL_CALL
185 }
186 }
187 if (status || !systemRoots) {
188 // Cannot get root certificates if there is no trusted system root certificate store.
189 SafeCFRelease(&systemRoots);
190 return NULL;
191 }
192 return systemRoots;
193}
194
195// returns a CFDictionaryRef created from the specified XML plist file; caller must release
196//
866f8763 197static CFDictionaryRef CF_RETURNS_RETAINED dictionaryWithContentsOfPlistFile(const char *fileName)
b1ab9ed8
A
198{
199 CFDictionaryRef resultDict = NULL;
200 CFDataRef fileData = dataWithContentsOfFile(fileName);
201 if (fileData) {
202 CFPropertyListRef xmlPlist = CFPropertyListCreateFromXMLData(NULL, fileData, kCFPropertyListImmutable, NULL);
203 if (xmlPlist && CFGetTypeID(xmlPlist) == CFDictionaryGetTypeID()) {
204 resultDict = (CFDictionaryRef)xmlPlist;
205 } else {
206 SafeCFRelease(&xmlPlist);
207 }
208 SafeCFRelease(&fileData);
209 }
210 return resultDict;
211}
212
213// returns the Organization component of the given certificate's subject name,
214// or nil if that component could not be found. Caller must release the string.
215//
216static CFStringRef organizationNameForCertificate(SecCertificateRef certificate)
217{
218 CFStringRef organizationName = nil;
427c49bc 219 OSStatus status = errSecSuccess;
b1ab9ed8
A
220
221#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
222 CSSM_OID_PTR oidPtr = (CSSM_OID_PTR) &CSSMOID_OrganizationName;
223 // note: Sec* APIs are not re-entrant due to the API lock
224 // status = SecCertificateCopySubjectComponent(certificate, oidPtr, &organizationName);
225 BEGIN_SECAPI_INTERNAL_CALL
226 organizationName = Certificate::required(certificate)->distinguishedName(&CSSMOID_X509V1SubjectNameCStruct, oidPtr);
227 END_SECAPI_INTERNAL_CALL
228 if (status) {
229 return (CFStringRef)NULL;
230 }
231#else
232 // SecCertificateCopySubjectComponent() doesn't exist on Tiger, so we have
233 // to go get the CSSMOID_OrganizationName the hard way, ourselves.
234 CSSM_DATA_PTR *fieldValues = NULL;
235 // note: Sec* APIs are not re-entrant due to the API lock
236 // status = SecCertificateCopyFieldValues(certificate, &CSSMOID_X509V1SubjectNameCStruct, &fieldValues);
237 BEGIN_SECAPI_INTERNAL_CALL
238 fieldValues = Certificate::required(certificate)->copyFieldValues(&CSSMOID_X509V1SubjectNameCStruct);
239 END_SECAPI_INTERNAL_CALL
240 if (*fieldValues == NULL) {
241 return (CFStringRef)NULL;
242 }
243 if (status || (*fieldValues)->Length == 0 || (*fieldValues)->Data == NULL) {
244 // note: Sec* APIs are not re-entrant due to the API lock
245 // status = SecCertificateReleaseFieldValues(certificate, &CSSMOID_X509V1SubjectNameCStruct, fieldValues);
246 BEGIN_SECAPI_INTERNAL_CALL
247 Certificate::required(certificate)->releaseFieldValues(&CSSMOID_X509V1SubjectNameCStruct, fieldValues);
248 END_SECAPI_INTERNAL_CALL
249 return (CFStringRef)NULL;
250 }
251
252 CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)(*fieldValues)->Data;
253
254 // Iterate over all the relative distinguished name (RDN) entries...
255 unsigned rdnIndex = 0;
256 bool foundIt = FALSE;
257 for (rdnIndex = 0; rdnIndex < x509Name->numberOfRDNs; rdnIndex++) {
258 CSSM_X509_RDN *rdnPtr = x509Name->RelativeDistinguishedName + rdnIndex;
259
260 // And then iterate over the attribute-value pairs of each RDN, looking for a CSSMOID_OrganizationName.
261 unsigned pairIndex;
262 for (pairIndex = 0; pairIndex < rdnPtr->numberOfPairs; pairIndex++) {
263 CSSM_X509_TYPE_VALUE_PAIR *pair = rdnPtr->AttributeTypeAndValue + pairIndex;
264
265 // If this pair isn't the organization name, move on to check the next one.
266 if (!oidsAreEqual(&pair->type, &CSSMOID_OrganizationName))
267 continue;
268
269 // We've found the organization name. Convert value to a string (eg, "Apple Inc.")
270 // Note: there can be more than one organization name in any given CSSM_X509_RDN.
271 // In practice, it's OK to use the first one. In future, if we have a means for
272 // displaying more than one name, this would be where they should be collected
273 // into an array.
274 switch (pair->valueType) {
275 case BER_TAG_PKIX_UTF8_STRING:
276 case BER_TAG_PKIX_UNIVERSAL_STRING:
277 case BER_TAG_GENERAL_STRING:
278 organizationName = CFStringCreateWithBytes(NULL, pair->value.Data, pair->value.Length, kCFStringEncodingUTF8, FALSE);
279 break;
280 case BER_TAG_PRINTABLE_STRING:
281 case BER_TAG_IA5_STRING:
282 organizationName = CFStringCreateWithBytes(NULL, pair->value.Data, pair->value.Length, kCFStringEncodingASCII, FALSE);
283 break;
284 case BER_TAG_T61_STRING:
285 case BER_TAG_VIDEOTEX_STRING:
286 case BER_TAG_ISO646_STRING:
287 organizationName = CFStringCreateWithBytes(NULL, pair->value.Data, pair->value.Length, kCFStringEncodingUTF8, FALSE);
288 // If the data cannot be represented as a UTF-8 string, fall back to ISO Latin 1
289 if (!organizationName) {
290 organizationName = CFStringCreateWithBytes(NULL, pair->value.Data, pair->value.Length, kCFStringEncodingISOLatin1, FALSE);
291 }
292 break;
293 case BER_TAG_PKIX_BMP_STRING:
294 organizationName = CFStringCreateWithBytes(NULL, pair->value.Data, pair->value.Length, kCFStringEncodingUnicode, FALSE);
295 break;
296 default:
297 break;
298 }
299
300 // If we found the organization name, there's no need to keep looping.
301 if (organizationName) {
302 foundIt = TRUE;
303 break;
304 }
305 }
306 if (foundIt)
307 break;
308 }
309 // note: Sec* APIs are not re-entrant due to the API lock
310 // status = SecCertificateReleaseFieldValues(certificate, &CSSMOID_X509V1SubjectNameCStruct, fieldValues);
311 BEGIN_SECAPI_INTERNAL_CALL
312 Certificate::required(certificate)->releaseFieldValues(&CSSMOID_X509V1SubjectNameCStruct, fieldValues);
313 END_SECAPI_INTERNAL_CALL
314#endif
315 return organizationName;
316}
317
318#if !defined(NDEBUG)
319void showCertSKID(const void *value, void *context);
320#endif
321
322static ModuleNexus<Mutex> gPotentialEVChainWithCertificatesMutex;
323
324// returns a CFArrayRef of SecCertificateRef instances; caller must release the returned array
325//
326CFArrayRef potentialEVChainWithCertificates(CFArrayRef certificates)
327{
328 StLock<Mutex> _(gPotentialEVChainWithCertificatesMutex());
329
330 // Given a partial certificate chain (which may or may not include the root,
331 // and does not have a guaranteed order except the first item is the leaf),
332 // examine intermediate certificates to see if they are cross-certified (i.e.
333 // have the same subject and public key as a trusted root); if so, remove the
334 // intermediate from the returned certificate array.
335
336 CFIndex chainIndex, chainLen = (certificates) ? CFArrayGetCount(certificates) : 0;
fa7225c8 337 secinfo("trusteval", "potentialEVChainWithCertificates: chainLen: %ld", chainLen);
b1ab9ed8
A
338 if (chainLen < 2) {
339 if (certificates) {
340 CFRetain(certificates);
341 }
342 return certificates;
343 }
344
345 CFMutableArrayRef certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
346 for (chainIndex = 0; chainIndex < chainLen; chainIndex++) {
347 SecCertificateRef aCert = (SecCertificateRef) CFArrayGetValueAtIndex(certificates, chainIndex);
348 SecCertificateRef replacementCert = NULL;
fa7225c8 349 secinfo("trusteval", "potentialEVChainWithCertificates: examining chainIndex: %ld", chainIndex);
b1ab9ed8
A
350 if (chainIndex > 0) {
351 // if this is not the leaf, then look for a possible replacement root to end the chain
352 // Try lookup using Subject Key ID first
353 replacementCert = _rootCertificateWithSubjectKeyIDOfCertificate(aCert);
354 if (!replacementCert)
355 {
fa7225c8 356 secinfo("trusteval", " not found using SKID, try by subject");
b1ab9ed8
A
357 replacementCert = _rootCertificateWithSubjectOfCertificate(aCert);
358 }
359 }
360 if (!replacementCert) {
fa7225c8 361 secinfo("trusteval", " No replacement found using SKID or subject; keeping original intermediate");
b1ab9ed8
A
362 CFArrayAppendValue(certArray, aCert);
363 }
364 SafeCFRelease(&replacementCert);
365 }
fa7225c8 366 secinfo("trusteval", "potentialEVChainWithCertificates: exit: new chainLen: %ld", CFArrayGetCount(certArray));
b1ab9ed8
A
367#if !defined(NDEBUG)
368 CFArrayApplyFunction(certArray, CFRangeMake(0, CFArrayGetCount(certArray)), showCertSKID, NULL);
369#endif
370
371 return certArray;
372}
373
374// returns a reference to a root certificate, if one can be found in the
375// system root store whose subject name and public key are identical to
376// that of the provided certificate, otherwise returns nil.
377//
378static SecCertificateRef _rootCertificateWithSubjectOfCertificate(SecCertificateRef certificate)
379{
380 if (!certificate)
381 return NULL;
382
383 StLock<Mutex> _(SecTrustKeychainsGetMutex());
384
385 // get data+length for the provided certificate
386 CSSM_CL_HANDLE clHandle = 0;
387 CSSM_DATA certData = { 0, NULL };
427c49bc 388 OSStatus status = errSecSuccess;
b1ab9ed8
A
389 // note: Sec* APIs are not re-entrant due to the API lock
390 // status = SecCertificateGetCLHandle(certificate, &clHandle);
391 BEGIN_SECAPI_INTERNAL_CALL
392 clHandle = Certificate::required(certificate)->clHandle();
393 END_SECAPI_INTERNAL_CALL
394 if (status)
395 return NULL;
396 // note: Sec* APIs are not re-entrant due to the API lock
397 // status = SecCertificateGetData(certificate, &certData);
398 BEGIN_SECAPI_INTERNAL_CALL
399 certData = Certificate::required(certificate)->data();
400 END_SECAPI_INTERNAL_CALL
401 if (status)
402 return NULL;
403
404 // get system roots keychain reference
405 SecKeychainRef systemRoots = systemRootStore();
406 if (!systemRoots)
407 return NULL;
408
409 // copy (normalized) subject for the provided certificate
410 const CSSM_OID_PTR oidPtr = (const CSSM_OID_PTR) &CSSMOID_X509V1SubjectName;
411 const CSSM_DATA_PTR subjectDataPtr = _copyFieldDataForOid(oidPtr, &certData, clHandle);
412 if (!subjectDataPtr)
413 return NULL;
414
415 // copy public key for the provided certificate
416 SecKeyRef keyRef = NULL;
417 SecCertificateRef resultCert = NULL;
418 // note: Sec* APIs are not re-entrant due to the API lock
419 // status = SecCertificateCopyPublicKey(certificate, &keyRef);
420 BEGIN_SECAPI_INTERNAL_CALL
421 keyRef = Certificate::required(certificate)->publicKey()->handle();
422 END_SECAPI_INTERNAL_CALL
423 if (!status) {
424 const CSSM_KEY *cssmKey = NULL;
425 // note: Sec* APIs are not re-entrant due to the API lock
426 // status = SecKeyGetCSSMKey(keyRef, &cssmKey);
427 BEGIN_SECAPI_INTERNAL_CALL
428 cssmKey = KeyItem::required(keyRef)->key();
429 END_SECAPI_INTERNAL_CALL
430 if (!status) {
431 // get SHA-1 hash of the public key
432 uint8 buf[CC_SHA1_DIGEST_LENGTH];
433 CSSM_DATA digest = { sizeof(buf), buf };
434 if (!cssmKey || !cssmKey->KeyData.Data || !cssmKey->KeyData.Length) {
427c49bc 435 status = errSecParam;
b1ab9ed8 436 } else {
427c49bc 437 CC_SHA1(cssmKey->KeyData.Data, (CC_LONG)cssmKey->KeyData.Length, buf);
b1ab9ed8
A
438 }
439 if (!status) {
440 // set up attribute vector (each attribute consists of {tag, length, pointer})
441 // we want to match on the public key hash and the normalized subject name
442 // as well as ensure that the issuer matches the subject
443 SecKeychainAttribute attrs[] = {
427c49bc
A
444 { kSecPublicKeyHashItemAttr, (UInt32)digest.Length, (void *)digest.Data },
445 { kSecSubjectItemAttr, (UInt32)subjectDataPtr->Length, (void *)subjectDataPtr->Data },
446 { kSecIssuerItemAttr, (UInt32)subjectDataPtr->Length, (void *)subjectDataPtr->Data }
b1ab9ed8
A
447 };
448 const SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
449 SecKeychainSearchRef searchRef = NULL;
450 // note: Sec* APIs are not re-entrant due to the API lock
451 // status = SecKeychainSearchCreateFromAttributes(systemRoots, kSecCertificateItemClass, &attributes, &searchRef);
452 BEGIN_SECAPI_INTERNAL_CALL
453 StorageManager::KeychainList keychains;
454 globals().storageManager.optionalSearchList(systemRoots, keychains);
455 KCCursor cursor(keychains, kSecCertificateItemClass, &attributes);
456 searchRef = cursor->handle();
457 END_SECAPI_INTERNAL_CALL
458 if (!status && searchRef) {
459 SecKeychainItemRef certRef = nil;
460 // note: Sec* APIs are not re-entrant due to the API lock
461 // status = SecKeychainSearchCopyNext(searchRef, &certRef); // only need the first one that matches
462 BEGIN_SECAPI_INTERNAL_CALL
463 Item item;
464 if (!KCCursorImpl::required(searchRef)->next(item)) {
465 status=errSecItemNotFound;
466 } else {
467 certRef=item->handle();
468 }
469 END_SECAPI_INTERNAL_CALL
470 if (!status)
471 resultCert = (SecCertificateRef)certRef; // caller must release
472 SafeCFRelease(&searchRef);
473 }
474 }
475 }
476 }
477 _freeFieldData(subjectDataPtr, oidPtr, clHandle);
478 SafeCFRelease(&keyRef);
479 SafeCFRelease(&systemRoots);
480
481 return resultCert;
482}
483
484
485#if !defined(NDEBUG)
486static void logSKID(const char *msg, const CssmData &subjectKeyID)
487{
488 const unsigned char *px = (const unsigned char *)subjectKeyID.data();
489 char buffer[256]={0,};
490 char bytes[16];
491 if (px && msg)
492 {
493 strcpy(buffer, msg);
494 for (unsigned int ix=0; ix<20; ix++)
495 {
496 sprintf(bytes, "%02X", px[ix]);
497 strcat(buffer, bytes);
498 }
fa7225c8 499 secinfo("trusteval", " SKID: %s",buffer);
b1ab9ed8
A
500 }
501}
502
503void showCertSKID(const void *value, void *context)
504{
505 SecCertificateRef certificate = (SecCertificateRef)value;
427c49bc 506 OSStatus status = errSecSuccess;
b1ab9ed8
A
507 BEGIN_SECAPI_INTERNAL_CALL
508 const CssmData &subjectKeyID = Certificate::required(certificate)->subjectKeyIdentifier();
509 logSKID("subjectKeyID: ", subjectKeyID);
510 END_SECAPI_INTERNAL_CALL
511}
512#endif
513
514// returns a reference to a root certificate, if one can be found in the
515// system root store whose subject key ID are identical to
516// that of the provided certificate, otherwise returns nil.
517//
518static SecCertificateRef _rootCertificateWithSubjectKeyIDOfCertificate(SecCertificateRef certificate)
519{
520 SecCertificateRef resultCert = NULL;
427c49bc 521 OSStatus status = errSecSuccess;
b1ab9ed8
A
522
523 if (!certificate)
524 return NULL;
525
526 StLock<Mutex> _(SecTrustKeychainsGetMutex());
527
528 // get system roots keychain reference
529 SecKeychainRef systemRoots = systemRootStore();
530 if (!systemRoots)
531 return NULL;
532
533 StorageManager::KeychainList keychains;
534 globals().storageManager.optionalSearchList(systemRoots, keychains);
535
536 BEGIN_SECAPI_INTERNAL_CALL
537 const CssmData &subjectKeyID = Certificate::required(certificate)->subjectKeyIdentifier();
538#if !defined(NDEBUG)
539 logSKID("search for SKID: ", subjectKeyID);
540#endif
541 // caller must release
542 resultCert = Certificate::required(certificate)->findBySubjectKeyID(keychains, subjectKeyID)->handle();
543#if !defined(NDEBUG)
544 logSKID(" found SKID: ", subjectKeyID);
545#endif
546 END_SECAPI_INTERNAL_CALL
547
548 SafeCFRelease(&systemRoots);
549
550 return resultCert;
551}
552
553// returns an array of possible root certificates (SecCertificateRef instances)
554// for the given EV OID (a hex string); caller must release the array
555//
427c49bc 556static
6b200bc3 557CFArrayRef CF_RETURNS_RETAINED _possibleRootCertificatesForOidString(CFStringRef oidString)
b1ab9ed8
A
558{
559 StLock<Mutex> _(SecTrustKeychainsGetMutex());
560
561 if (!oidString)
562 return NULL;
563 CFDictionaryRef evOidDict = _evCAOidDict();
564 if (!evOidDict)
565 return NULL;
566 CFArrayRef possibleCertificateHashes = (CFArrayRef) CFDictionaryGetValue(evOidDict, oidString);
567 SecKeychainRef systemRoots = systemRootStore();
568 if (!possibleCertificateHashes || !systemRoots) {
569 SafeCFRelease(&evOidDict);
570 return NULL;
571 }
572
573 CFMutableArrayRef possibleRootCertificates = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
574 CFIndex hashCount = CFArrayGetCount(possibleCertificateHashes);
fa7225c8 575 secinfo("evTrust", "_possibleRootCertificatesForOidString: %d possible hashes", (int)hashCount);
b1ab9ed8 576
427c49bc 577 OSStatus status = errSecSuccess;
b1ab9ed8
A
578 SecKeychainSearchRef searchRef = NULL;
579 // note: Sec* APIs are not re-entrant due to the API lock
580 // status = SecKeychainSearchCreateFromAttributes(systemRoots, kSecCertificateItemClass, NULL, &searchRef);
581 BEGIN_SECAPI_INTERNAL_CALL
582 StorageManager::KeychainList keychains;
583 globals().storageManager.optionalSearchList(systemRoots, keychains);
584 KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
585 searchRef = cursor->handle();
586 END_SECAPI_INTERNAL_CALL
587 if (searchRef) {
588 while (!status) {
589 SecKeychainItemRef certRef = NULL;
590 // note: Sec* APIs are not re-entrant due to the API lock
591 // status = SecKeychainSearchCopyNext(searchRef, &certRef);
592 BEGIN_SECAPI_INTERNAL_CALL
593 Item item;
594 if (!KCCursorImpl::required(searchRef)->next(item)) {
595 certRef=NULL;
596 status=errSecItemNotFound;
597 } else {
598 certRef=item->handle();
599 }
600 END_SECAPI_INTERNAL_CALL
601 if (status || !certRef) {
602 break;
603 }
604
605 CSSM_DATA certData = { 0, NULL };
606 // note: Sec* APIs are not re-entrant due to the API lock
607 // status = SecCertificateGetData((SecCertificateRef) certRef, &certData);
608 BEGIN_SECAPI_INTERNAL_CALL
609 certData = Certificate::required((SecCertificateRef)certRef)->data();
610 END_SECAPI_INTERNAL_CALL
611 if (!status) {
612 uint8 buf[CC_SHA1_DIGEST_LENGTH];
613 CSSM_DATA digest = { sizeof(buf), buf };
614 if (!certData.Data || !certData.Length) {
427c49bc 615 status = errSecParam;
b1ab9ed8 616 } else {
427c49bc 617 CC_SHA1(certData.Data, (CC_LONG)certData.Length, buf);
b1ab9ed8
A
618 }
619 if (!status) {
620 CFDataRef hashData = CFDataCreateWithBytesNoCopy(NULL, digest.Data, digest.Length, kCFAllocatorNull);
621 if (hashData && CFArrayContainsValue(possibleCertificateHashes, CFRangeMake(0, hashCount), hashData)) {
622 CFArrayAppendValue(possibleRootCertificates, certRef);
623 }
624 SafeCFRelease(&hashData);
625 }
626 }
627 SafeCFRelease(&certRef);
628 }
629 }
630 SafeCFRelease(&searchRef);
631 SafeCFRelease(&systemRoots);
632 SafeCFRelease(&evOidDict);
633
634 return possibleRootCertificates;
635}
636
637// returns an array of allowed root certificates (SecCertificateRef instances)
638// for the given EV OID (a hex string); caller must release the array.
639// This differs from _possibleRootCertificatesForOidString in that each possible
640// certificate is further checked for trust settings, so we don't include
641// a certificate which is untrusted (or explicitly distrusted).
642//
643CFArrayRef _allowedRootCertificatesForOidString(CFStringRef oidString)
644{
645 CFMutableArrayRef allowedRootCertificates = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
646 CFArrayRef possibleRootCertificates = _possibleRootCertificatesForOidString(oidString);
647 if (possibleRootCertificates) {
648 CFIndex idx, count = CFArrayGetCount(possibleRootCertificates);
649 for (idx=0; idx<count; idx++) {
650 SecCertificateRef cert = (SecCertificateRef) CFArrayGetValueAtIndex(possibleRootCertificates, idx);
5c19dc3a
A
651 /* Need a unified SecCertificateRef instance to hand to SecTrustSettingsCertHashStrFromCert */
652 SecCertificateRef certRef = SecCertificateCreateFromItemImplInstance(cert);
5c19dc3a 653 CFStringRef hashStr = SecTrustSettingsCertHashStrFromCert(certRef);
b1ab9ed8
A
654 if (hashStr) {
655 bool foundMatch = false;
656 bool foundAny = false;
657 CSSM_RETURN *errors = NULL;
658 uint32 errorCount = 0;
6b200bc3 659 SecTrustSettingsDomain foundDomain = kSecTrustSettingsDomainUser;
b1ab9ed8
A
660 SecTrustSettingsResult result = kSecTrustSettingsResultInvalid;
661 OSStatus status = SecTrustSettingsEvaluateCert(
662 hashStr, /* certHashStr */
663 NULL, /* policyOID (optional) */
664 NULL, /* policyString (optional) */
665 0, /* policyStringLen */
666 0, /* keyUsage */
667 true, /* isRootCert */
668 &foundDomain, /* foundDomain */
669 &errors, /* allowedErrors */
670 &errorCount, /* numAllowedErrors */
671 &result, /* resultType */
672 &foundMatch, /* foundMatchingEntry */
673 &foundAny); /* foundAnyEntry */
674
427c49bc 675 if (status == errSecSuccess) {
fa7225c8 676 secinfo("evTrust", "_allowedRootCertificatesForOidString: cert %lu has result %d from domain %d",
b1ab9ed8
A
677 idx, (int)result, (int)foundDomain);
678 // Root certificates must be trusted by the system (and not have
679 // any explicit trust overrides) to be allowed for EV use.
680 if (foundMatch && foundDomain == kSecTrustSettingsDomainSystem &&
681 result == kSecTrustSettingsResultTrustRoot) {
682 CFArrayAppendValue(allowedRootCertificates, cert);
683 }
684 } else {
fa7225c8 685 secinfo("evTrust", "_allowedRootCertificatesForOidString: cert %lu SecTrustSettingsEvaluateCert error %d",
b1ab9ed8
A
686 idx, (int)status);
687 }
688 if (errors) {
689 free(errors);
690 }
691 CFRelease(hashStr);
692 }
5c19dc3a
A
693 if (certRef) {
694 CFRelease(certRef);
695 }
b1ab9ed8
A
696 }
697 CFRelease(possibleRootCertificates);
698 }
699
700 return allowedRootCertificates;
701}
702
703// return a CSSM_DATA_PTR containing field data; caller must release with _freeFieldData
704//
705static CSSM_DATA_PTR _copyFieldDataForOid(CSSM_OID_PTR oid, CSSM_DATA_PTR cert, CSSM_CL_HANDLE clHandle)
706{
707 uint32 numFields = 0;
708 CSSM_HANDLE results = 0;
709 CSSM_DATA_PTR value = 0;
710 CSSM_RETURN crtn = CSSM_CL_CertGetFirstFieldValue(clHandle, cert, oid, &results, &numFields, &value);
711
712 // we aren't going to look for any further fields, so free the results handle immediately
713 if (results) {
714 CSSM_CL_CertAbortQuery(clHandle, results);
715 }
716
717 return (crtn || !numFields) ? NULL : value;
718}
719
720// Some errors are ignorable errors because they do not indicate a problem
721// with the certificate itself, but rather a problem getting a response from
722// the CA server. The EV Certificate spec does not mandate that the application
723// software vendor *must* get a response from OCSP or CRL, it is a "best
724// attempt" approach which will not fail if the server does not respond.
725//
726// The EV spec (26. EV Certificate Status Checking) says that CAs have to
727// maintain either a CRL or OCSP server. They are not required to maintain
728// an OCSP server until after Dec 31, 2010.
729//
730// As to the responsibility of the application software vendor to perform
731// revocation checking, this is only covered by the following section (37.2.):
732//
733// This [indemnification of Application Software Vendors]
734// shall not apply, however, to any claim, damages, or loss
735// suffered by such Application Software Vendor related to an EV Certificate
736// issued by the CA where such claim, damage, or loss was directly caused by
737// such Application Software Vendor’s software displaying as not trustworthy an
738// EV Certificate that is still valid, or displaying as trustworthy: (1) an EV
739// Certificate that has expired, or (2) an EV Certificate that has been revoked
740// (but only in cases where the revocation status is currently available from the
741// CA online, and the browser software either failed to check such status or
742// ignored an indication of revoked status).
743//
744// The last section describes what a browser is required to do: it must attempt
745// to check revocation status (as indicated by the OCSP or CRL server info in
746// the certificate), and it cannot ignore an indication of revoked status
747// (i.e. a positive thumbs-down response from the server, which would be a
748// different error than the ones being skipped.) However, given that we meet
749// those requirements, if the revocation server is down or will not give us a
750// response for whatever reason, that is not our problem.
751
752bool isRevocationServerMetaError(CSSM_RETURN statusCode)
753{
754 switch (statusCode) {
755 case CSSMERR_APPLETP_CRL_NOT_FOUND: // 13. CRL not found
756 case CSSMERR_APPLETP_CRL_SERVER_DOWN: // 14. CRL server down
757 case CSSMERR_APPLETP_OCSP_UNAVAILABLE: // 33. OCSP service unavailable
758 case CSSMERR_APPLETP_NETWORK_FAILURE: // 36. General network failure
759 case CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ: // 41. OCSP responder status: malformed request
760 case CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR: // 42. OCSP responder status: internal error
761 case CSSMERR_APPLETP_OCSP_RESP_TRY_LATER: // 43. OCSP responder status: try later
762 case CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED: // 44. OCSP responder status: signature required
763 case CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED: // 45. OCSP responder status: unauthorized
764 return true;
765 default:
766 return false;
767 }
768}
769
770// returns true if the given status code is related to performing an OCSP revocation check
771//
772bool isOCSPStatusCode(CSSM_RETURN statusCode)
773{
774 switch (statusCode)
775 {
776 case CSSMERR_APPLETP_OCSP_BAD_RESPONSE: // 31. Unparseable OCSP response
777 case CSSMERR_APPLETP_OCSP_BAD_REQUEST: // 32. Unparseable OCSP request
778 case CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ: // 41. OCSP responder status: malformed request
779 case CSSMERR_APPLETP_OCSP_UNAVAILABLE: // 33. OCSP service unavailable
780 case CSSMERR_APPLETP_OCSP_STATUS_UNRECOGNIZED: // 34. OCSP status: cert unrecognized
781 case CSSMERR_APPLETP_OCSP_NOT_TRUSTED: // 37. OCSP response not verifiable to anchor or root
782 case CSSMERR_APPLETP_OCSP_INVALID_ANCHOR_CERT: // 38. OCSP response verified to untrusted root
783 case CSSMERR_APPLETP_OCSP_SIG_ERROR: // 39. OCSP response signature error
784 case CSSMERR_APPLETP_OCSP_NO_SIGNER: // 40. No signer for OCSP response found
785 case CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR: // 42. OCSP responder status: internal error
786 case CSSMERR_APPLETP_OCSP_RESP_TRY_LATER: // 43. OCSP responder status: try later
787 case CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED: // 44. OCSP responder status: signature required
788 case CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED: // 45. OCSP responder status: unauthorized
789 case CSSMERR_APPLETP_OCSP_NONCE_MISMATCH: // 46. OCSP response nonce did not match request
790 return true;
791 default:
792 return false;
793 }
794}
795
796// returns true if the given status code is related to performing a CRL revocation check
797//
798bool isCRLStatusCode(CSSM_RETURN statusCode)
799{
800 switch (statusCode)
801 {
802 case CSSMERR_APPLETP_CRL_EXPIRED: // 11. CRL expired
803 case CSSMERR_APPLETP_CRL_NOT_VALID_YET: // 12. CRL not yet valid
804 case CSSMERR_APPLETP_CRL_NOT_FOUND: // 13. CRL not found
805 case CSSMERR_APPLETP_CRL_SERVER_DOWN: // 14. CRL server down
806 case CSSMERR_APPLETP_CRL_BAD_URI: // 15. Illegal CRL distribution point URI
807 case CSSMERR_APPLETP_CRL_NOT_TRUSTED: // 18. CRL not verifiable to anchor or root
808 case CSSMERR_APPLETP_CRL_INVALID_ANCHOR_CERT: // 19. CRL verified to untrusted root
809 case CSSMERR_APPLETP_CRL_POLICY_FAIL: // 20. CRL failed policy verification
810 return true;
811 default:
812 return false;
813 }
814}
815
816// returns true if the given status code is related to performing a revocation check
817//
818bool isRevocationStatusCode(CSSM_RETURN statusCode)
819{
820 if (statusCode == CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK || // 35. Revocation check not successful for each cert
821 statusCode == CSSMERR_APPLETP_NETWORK_FAILURE || // 36. General network error
822 isOCSPStatusCode(statusCode) == true || // OCSP error
823 isCRLStatusCode(statusCode) == true) // CRL error
824 return true;
825 else
826 return false;
827}
828
b1ab9ed8
A
829// returns a CFArrayRef of allowed root certificates for the provided leaf certificate
830// if it passes initial EV evaluation criteria and should be subject to OCSP revocation
831// checking; otherwise, NULL is returned. (Caller must release the result if not NULL.)
832//
833CFArrayRef allowedEVRootsForLeafCertificate(CFArrayRef certificates)
834{
835 // Given a partial certificate chain (which may or may not include the root,
836 // and does not have a guaranteed order except the first item is the leaf),
837 // determine whether the leaf claims to have a supported EV policy OID.
838 //
839 // Unless this function returns NULL, a full SSL trust evaluation with OCSP revocation
840 // checking must be performed successfully for the certificate to be considered valid.
841 // This function is intended to be called before the chain has been evaluated,
842 // in order to obtain the list of allowed roots for the evaluation. Once the "regular"
843 // TP evaluation has taken place, chainMeetsExtendedValidationCriteria() should be
844 // called to complete extended validation checking.
845
846 CFIndex count = (certificates) ? CFArrayGetCount(certificates) : 0;
847 if (count < 1)
848 return NULL;
849
850 CSSM_CL_HANDLE clHandle = 0;
851 CSSM_DATA certData = { 0, NULL };
852 SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(certificates, 0);
427c49bc 853 OSStatus status = errSecSuccess;
b1ab9ed8
A
854 // note: Sec* APIs are not re-entrant due to the API lock
855 // status = SecCertificateGetCLHandle(certRef, &clHandle);
856 BEGIN_SECAPI_INTERNAL_CALL
857 clHandle = Certificate::required(certRef)->clHandle();
858 END_SECAPI_INTERNAL_CALL
859 if (status)
860 return NULL;
861 // note: Sec* APIs are not re-entrant due to the API lock
862 // status = SecCertificateGetData(certRef, &certData);
863 BEGIN_SECAPI_INTERNAL_CALL
864 certData = Certificate::required(certRef)->data();
865 END_SECAPI_INTERNAL_CALL
866 if (status)
867 return NULL;
868
869 // Does the leaf certificate contain a Certificate Policies extension?
870 const CSSM_OID_PTR oidPtr = (CSSM_OID_PTR) &CSSMOID_CertificatePolicies;
871 CSSM_DATA_PTR extensionDataPtr = _copyFieldDataForOid(oidPtr, &certData, clHandle);
872 if (!extensionDataPtr)
873 return NULL;
874
875 // Does the extension contain one of the magic EV CA OIDs we know about?
876 CSSM_X509_EXTENSION *cssmExtension = (CSSM_X509_EXTENSION *)extensionDataPtr->Data;
877 CE_CertPolicies *certPolicies = (CE_CertPolicies *)cssmExtension->value.parsedValue;
878 CFStringRef oidString = _oidStringForCertificatePolicies(certPolicies);
879 _freeFieldData(extensionDataPtr, oidPtr, clHandle);
880
881 // Fetch the allowed root CA certificates for this OID, if any
882 CFArrayRef allowedRoots = (oidString) ? _allowedRootCertificatesForOidString(oidString) : NULL;
883 CFIndex rootCount = (allowedRoots) ? CFArrayGetCount(allowedRoots) : 0;
fa7225c8 884 secinfo("evTrust", "allowedEVRootsForLeafCertificate: found %d allowed roots", (int)rootCount);
b1ab9ed8
A
885 SafeCFRelease(&oidString);
886 if (!allowedRoots || !rootCount) {
887 SafeCFRelease(&allowedRoots);
888 return NULL;
889 }
890
891 // The leaf certificate needs extended validation (with revocation checking).
892 // Return the array of allowed roots for this leaf certificate.
893 return allowedRoots;
894}
895
4d3cab3d
A
896// returns true if the provided certificate contains a wildcard in either
897// its common name or subject alternative name.
898//
899static
900bool hasWildcardDNSName(SecCertificateRef certRef)
901{
902 OSStatus status = errSecSuccess;
903 CFArrayRef dnsNames = NULL;
904
905 BEGIN_SECAPI_INTERNAL_CALL
906 Required(&dnsNames) = Certificate::required(certRef)->copyDNSNames();
907 END_SECAPI_INTERNAL_CALL
908 if (status || !dnsNames)
909 return false;
910
911 bool hasWildcard = false;
912 const CFStringRef wildcard = CFSTR("*");
913 CFIndex index, count = CFArrayGetCount(dnsNames);
914 for (index = 0; index < count; index ++) {
915 CFStringRef name = (CFStringRef) CFArrayGetValueAtIndex(dnsNames, index);
916 if (name) {
917 CFRange foundRange = CFStringFind(name, wildcard, 0);
918 if (foundRange.length != 0 && foundRange.location != kCFNotFound) {
919 hasWildcard = true;
920 break;
921 }
922 }
923 }
924 CFRelease(dnsNames);
925 return hasWildcard;
926}
927
b1ab9ed8
A
928// returns a CFDictionaryRef of extended validation results for the given chain,
929// or NULL if the certificate chain did not meet all EV criteria. (Caller must
930// release the result if not NULL.)
931//
427c49bc 932static
b1ab9ed8
A
933CFDictionaryRef extendedValidationResults(CFArrayRef certChain, SecTrustResultType trustResult, OSStatus tpResult)
934{
935 // This function is intended to be called after the "regular" TP evaluation
936 // has taken place (i.e. trustResult and tpResult are available), and there
937 // is a full certificate chain to examine.
938
939 CFIndex chainIndex, chainLen = (certChain) ? CFArrayGetCount(certChain) : 0;
940 if (chainLen < 2) {
941 return NULL; // invalid chain length
942 }
943
944 if (trustResult != kSecTrustResultUnspecified) {
945
946 // "Recoverable" means the certificate failed to meet all policy requirements, but is intrinsically OK.
947 // One of the failures we might encounter is if the OCSP responder tells us to go away. Since this is a
948 // real-world case, we'll check for OCSP and CRL meta-errors specifically.
949 bool recovered = false;
950 if (trustResult == kSecTrustResultRecoverableTrustFailure) {
951 recovered = isRevocationServerMetaError((CSSM_RETURN)tpResult);
952 }
953 if (!recovered) {
954 return NULL;
955 }
956 }
957
958 //
959 // What we know at this point:
960 //
4d3cab3d
A
961 // 1. From a previous call to allowedEVRootsForLeafCertificate
962 // (or we wouldn't be getting called by extendedTrustResults):
b1ab9ed8
A
963 // - a leaf certificate exists
964 // - that certificate contains a Certificate Policies extension
965 // - that extension contains an OID from one of the trusted EV CAs we know about
966 // - we have found at least one allowed EV root for that OID
967 //
968 // 2. From the TP evaluation:
969 // - the leaf certificate verifies back to a trusted EV root (with no trust settings overrides)
970 // - SSL trust evaluation with OCSP revocation checking enabled returned no (fatal) errors
971 //
4d3cab3d
A
972 // We need to verify the following additional requirements for the leaf (as of EV 1.1, 6(a)(2)):
973 // - cannot specify a wildcard in commonName or subjectAltName
974 // (note: this is a change since EV 1.0 (9.2.1), which stated that "Wildcard FQDNs are permitted.")
975 //
b1ab9ed8
A
976 // Finally, we need to check the following requirements (EV 1.1 specification, Appendix B):
977 // - the trusted root, if created after 10/31/2006, must have:
978 // - critical basicConstraints extension with CA bit set
979 // - critical keyUsage extension with keyCertSign and cRLSign bits set
980 // - intermediate certs, if present, must have:
981 // - certificatePolicies extension, containing either a known EV CA OID, or anyPolicy
982 // - non-critical cRLDistributionPoint extension
983 // - critical basicConstraints extension with CA bit set
984 // - critical keyUsage extension with keyCertSign and cRLSign bits set
4d3cab3d
A
985 //
986
987 // check leaf certificate for wildcard names
988 if (hasWildcardDNSName((SecCertificateRef) CFArrayGetValueAtIndex(certChain, 0))) {
fa7225c8 989 trustDebug("has wildcard name (does not meet EV criteria)\n");
4d3cab3d
A
990 return NULL;
991 }
b1ab9ed8
A
992
993 // check intermediate CA certificates for required extensions per Appendix B of EV 1.1 specification.
994 bool hasRequiredExtensions = true;
995 CSSM_CL_HANDLE clHandle = 0;
996 CSSM_DATA certData = { 0, NULL };
997 CSSM_OID_PTR oidPtr = (CSSM_OID_PTR) &CSSMOID_CertificatePolicies;
998 for (chainIndex = 1; hasRequiredExtensions && chainLen > 2 && chainIndex < chainLen - 1; chainIndex++) {
999 SecCertificateRef intermediateCert = (SecCertificateRef) CFArrayGetValueAtIndex(certChain, chainIndex);
427c49bc 1000 OSStatus status = errSecSuccess;
b1ab9ed8
A
1001 // note: Sec* APIs are not re-entrant due to the API lock
1002 // status = SecCertificateGetCLHandle(intermediateCert, &clHandle);
1003 BEGIN_SECAPI_INTERNAL_CALL
1004 clHandle = Certificate::required(intermediateCert)->clHandle();
1005 END_SECAPI_INTERNAL_CALL
1006 if (status)
1007 return NULL;
1008 // note: Sec* APIs are not re-entrant due to the API lock
1009 // status = SecCertificateGetData(intermediateCert, &certData);
1010 BEGIN_SECAPI_INTERNAL_CALL
1011 certData = Certificate::required(intermediateCert)->data();
1012 END_SECAPI_INTERNAL_CALL
1013 if (status)
1014 return NULL;
1015
1016 CSSM_DATA_PTR extensionDataPtr = _copyFieldDataForOid(oidPtr, &certData, clHandle);
1017 if (!extensionDataPtr)
1018 return NULL;
1019
1020 CSSM_X509_EXTENSION *cssmExtension = (CSSM_X509_EXTENSION *)extensionDataPtr->Data;
1021 CE_CertPolicies *certPolicies = (CE_CertPolicies *)cssmExtension->value.parsedValue;
1022 CFStringRef oidString = _oidStringForCertificatePolicies(certPolicies);
1023 hasRequiredExtensions = (oidString != NULL);
1024 SafeCFRelease(&oidString);
1025 _freeFieldData(extensionDataPtr, oidPtr, clHandle);
1026
1027 // FIX: add checks for the following (not essential to this implementation):
1028 // - non-critical cRLDistributionPoint extension
1029 // - critical basicConstraints extension with CA bit set
1030 // - critical keyUsage extension with keyCertSign and cRLSign bits set
1031 // Tracked by <rdar://problem/6119322>
1032 }
1033
1034 if (hasRequiredExtensions) {
1035 SecCertificateRef leafCert = (SecCertificateRef) CFArrayGetValueAtIndex(certChain, 0);
1036 CFStringRef organizationName = organizationNameForCertificate(leafCert);
1037 if (organizationName != NULL) {
1038 CFMutableDictionaryRef resultDict = CFDictionaryCreateMutable(NULL, 0,
1039 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1040 CFDictionaryAddValue(resultDict, kSecEVOrganizationName, organizationName);
fa7225c8 1041 trustDebug("[EV] extended validation succeeded\n");
b1ab9ed8
A
1042 SafeCFRelease(&organizationName);
1043 return resultDict;
1044 }
1045 }
1046
1047 return NULL;
1048}
1049
1050// returns a CFDictionaryRef containing extended trust results.
1051// Caller must release this dictionary.
1052//
1053// If the isEVCandidate argument is true, extended validation checking is performed
1054// and the kSecEVOrganizationName key will be set in the dictionary if EV criteria is met.
1055// In all cases, kSecTrustEvaluationDate and kSecTrustExpirationDate will be set.
1056//
1057CFDictionaryRef extendedTrustResults(CFArrayRef certChain, SecTrustResultType trustResult, OSStatus tpResult, bool isEVCandidate)
1058{
1059 CFMutableDictionaryRef resultDict = NULL;
1060 if (isEVCandidate) {
1061 resultDict = (CFMutableDictionaryRef) extendedValidationResults(certChain, trustResult, tpResult);
1062 }
1063 if (!resultDict) {
1064 resultDict = CFDictionaryCreateMutable(NULL, 0,
1065 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1066 if (!resultDict) {
1067 return NULL;
1068 }
1069 }
1070 CFAbsoluteTime at = CFAbsoluteTimeGetCurrent();
1071 CFDateRef trustEvaluationDate = CFDateCreate(kCFAllocatorDefault, at);
1072 // by default, permit caching of trust evaluation results for up to 2 hours
1073 // FIXME: need to modify this based on cert expiration and OCSP/CRL validity
1074 CFDateRef trustExpirationDate = CFDateCreate(kCFAllocatorDefault, at + (60*60*2));
1075 CFDictionaryAddValue(resultDict, kSecTrustEvaluationDate, trustEvaluationDate);
1076 SafeCFRelease(&trustEvaluationDate);
1077 CFDictionaryAddValue(resultDict, kSecTrustExpirationDate, trustExpirationDate);
1078 SafeCFRelease(&trustExpirationDate);
1079
1080 return resultDict;
1081}
1082
1083// returns a CFDictionaryRef containing mappings from supported EV CA OIDs to SHA-1 hash values;
1084// caller must release
1085//
1086static CFDictionaryRef _evCAOidDict()
1087{
1088 static CFDictionaryRef s_evCAOidDict = NULL;
1089 if (s_evCAOidDict) {
1090 CFRetain(s_evCAOidDict);
fa7225c8 1091 secinfo("evTrust", "_evCAOidDict: returning static instance (rc=%d)", (int)CFGetRetainCount(s_evCAOidDict));
b1ab9ed8
A
1092 return s_evCAOidDict;
1093 }
fa7225c8 1094 secinfo("evTrust", "_evCAOidDict: initializing static instance");
b1ab9ed8
A
1095
1096 s_evCAOidDict = dictionaryWithContentsOfPlistFile(EV_ROOTS_PLIST_SYSTEM_PATH);
1097 if (!s_evCAOidDict)
1098 return NULL;
1099
1100#if !defined MAC_OS_X_VERSION_10_6 || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
1101 // Work around rdar://6302788 by hard coding a hash that was missed when addressing <rdar://problem/6238289&6238296>
1102 // This is being addressed in SnowLeopard by rdar://6305989
1103 CFStringRef oidString = CFSTR("2.16.840.1.114028.10.1.2");
1104 CFMutableArrayRef hashes = (CFMutableArrayRef) CFDictionaryGetValue(s_evCAOidDict, oidString);
1105 if (hashes) {
1106 uint8 hashBytes[] = {0xB3, 0x1E, 0xB1, 0xB7, 0x40, 0xE3, 0x6C, 0x84, 0x02, 0xDA, 0xDC, 0x37, 0xD4, 0x4D, 0xF5, 0xD4, 0x67, 0x49, 0x52, 0xF9};
1107 CFDataRef hashData = CFDataCreate(NULL, hashBytes, sizeof(hashBytes));
1108 CFIndex hashCount = CFArrayGetCount(hashes);
1109 if (hashData && CFArrayContainsValue(hashes, CFRangeMake(0, hashCount), hashData)) {
fa7225c8 1110 secinfo("evTrust", "_evCAOidDict: added hardcoded hash value");
b1ab9ed8
A
1111 CFArrayAppendValue(hashes, hashData);
1112 }
1113 SafeCFRelease(&hashData);
1114 }
1115#endif
1116 CFRetain(s_evCAOidDict);
fa7225c8 1117 secinfo("evTrust", "_evCAOidDict: returning static instance (rc=%d)", (int)CFGetRetainCount(s_evCAOidDict));
b1ab9ed8
A
1118 return s_evCAOidDict;
1119}
1120
1121// returns a CFStringRef containing a decimal representation of the given OID.
1122// Caller must release.
1123
1124static CFStringRef _decimalStringForOid(CSSM_OID_PTR oid)
1125{
1126 CFMutableStringRef str = CFStringCreateMutable(NULL, 0);
1127 if (!str || oid->Length > 32)
1128 return str;
1129
1130 // The first two levels are encoded into one byte, since the root level
1131 // has only 3 nodes (40*x + y). However if x = joint-iso-itu-t(2) then
1132 // y may be > 39, so we have to add special-case handling for this.
1133 unsigned long value = 0;
1134 unsigned int x = oid->Data[0] / 40;
1135 unsigned int y = oid->Data[0] % 40;
1136 if (x > 2) {
1137 // Handle special case for large y if x = 2
1138 y += (x - 2) * 40;
1139 x = 2;
1140 }
1141
1142 CFStringAppendFormat(str, NULL, CFSTR("%d.%d"), x, y);
1143
1144 for (x = 1; x < oid->Length; x++) {
1145 value = (value << 7) | (oid->Data[x] & 0x7F);
1146 if(!(oid->Data[x] & 0x80)) {
1147 CFStringAppendFormat(str, NULL, CFSTR(".%ld"), value);
1148 value = 0;
1149 }
1150 }
1151
1152#if !defined(NDEBUG)
1153 CFIndex nameLen = CFStringGetLength(str);
1154 CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8);
1155 char *nameBuf = (char *)malloc(bufLen);
1156 if (!CFStringGetCString(str, nameBuf, bufLen-1, kCFStringEncodingUTF8))
1157 nameBuf[0]=0;
fa7225c8 1158 secinfo("evTrust", "_decimalStringForOid: \"%s\"", nameBuf);
b1ab9ed8
A
1159 free(nameBuf);
1160#endif
1161
1162 return str;
1163}
1164
1165static void _freeFieldData(CSSM_DATA_PTR value, CSSM_OID_PTR oid, CSSM_CL_HANDLE clHandle)
1166{
1167 if (value && value->Data) {
1168 CSSM_CL_FreeFieldValue(clHandle, oid, value);
1169 }
1170 return;
1171}
1172
1173static ModuleNexus<Mutex> gOidStringForCertificatePoliciesMutex;
1174
6b200bc3 1175static CFStringRef CF_RETURNS_RETAINED _oidStringForCertificatePolicies(const CE_CertPolicies *certPolicies)
b1ab9ed8
A
1176{
1177 StLock<Mutex> _(gOidStringForCertificatePoliciesMutex());
1178
1179 // returns the first EV OID (as a string) found in the given Certificate Policies extension,
1180 // or NULL if the extension does not contain any known EV OIDs. (Note that the "any policy" OID
1181 // is a special case and will be returned if present, although its presence is only meaningful
1182 // in an intermediate CA.)
1183
1184 if (!certPolicies) {
fa7225c8 1185 secinfo("evTrust", "oidStringForCertificatePolicies: missing certPolicies!");
b1ab9ed8
A
1186 return NULL;
1187 }
1188
1189 CFDictionaryRef evOidDict = _evCAOidDict();
1190 if (!evOidDict) {
fa7225c8 1191 secinfo("evTrust", "oidStringForCertificatePolicies: nil OID dictionary!");
b1ab9ed8
A
1192 return NULL;
1193 }
1194
1195 CFStringRef foundOidStr = NULL;
1196 uint32 policyIndex, maxIndex = 10; // sanity check; EV certs normally have EV OID as first policy
1197 for (policyIndex = 0; policyIndex < certPolicies->numPolicies && policyIndex < maxIndex; policyIndex++) {
1198 CE_PolicyInformation *certPolicyInfo = &certPolicies->policies[policyIndex];
1199 CSSM_OID_PTR oid = &certPolicyInfo->certPolicyId;
1200 CFStringRef oidStr = _decimalStringForOid(oid);
1201 if (!oidStr)
1202 continue;
1203 if (!CFStringCompare(oidStr, CFSTR("2.5.29.32.0"), 0) || // is it the "any" OID, or
1204 CFDictionaryGetValue(evOidDict, oidStr) != NULL) { // a known EV CA OID?
1205 foundOidStr = CFStringCreateCopy(NULL, oidStr);
1206 }
1207 SafeCFRelease(&oidStr);
1208 if (foundOidStr)
1209 break;
1210 }
1211 SafeCFRelease(&evOidDict);
1212
1213 return foundOidStr;
1214}
1215