3 * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * The contents of this file constitute Original Code as defined in and
8 * are subject to the Apple Public Source License Version 1.1 (the
9 * "License"). You may not use this file except in compliance with the
10 * License. Please obtain a copy of the License at
11 * http://www.apple.com/publicsource and read it before using this file.
13 * This 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 OR NON-INFRINGEMENT. Please see the
18 * License for the specific language governing rights and limitations
21 * @APPLE_LICENSE_HEADER_END@
26 * Racoon module for verifying and signing certificates through Security
32 #include <Security/SecCertificate.h>
33 #include <Security/SecPolicy.h>
34 #include <Security/SecTrust.h>
35 #include <Security/SecKeyPriv.h>
36 #include <Security/SecIdentity.h>
37 #include <Security/SecItem.h>
38 #include <TargetConditionals.h>
39 #include <Security/SecItemPriv.h>
41 #include <Security/SecTrustPriv.h>
42 #include <Security/SecPolicyPriv.h>
43 #include <Security/SecCertificatePriv.h>
45 #include <Security/SecBase.h>
46 #include <Security/SecIdentityPriv.h>
47 #include <Security/SecCertificateOIDs.h>
48 #include <Security/SecKeyPriv.h>
49 #include <Security/oidsalg.h>
50 #include <Security/cssmapi.h>
51 #include <Security/SecPolicySearch.h>
53 #include <CoreFoundation/CoreFoundation.h>
54 #if !TARGET_OS_EMBEDDED
55 #include <Security/SecIdentitySearch.h>
56 #include <Security/SecKeychain.h>
57 #include <Security/SecKeychainItem.h>
58 #include <Security/SecKeychainItemPriv.h>
59 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
68 #include "crypto_cssm.h"
71 static OSStatus
EvaluateCert(SecCertificateRef evalCertArray
[], CFIndex evalCertArrayNumValues
, CFTypeRef policyRef
, SecKeyRef
*publicKeyRef
);
76 crypto_cssm_x509cert_get_SecPolicyRef (CFStringRef hostname
)
78 SecPolicyRef policyRef
= NULL
;
79 CFDictionaryRef properties
= NULL
;
80 const void *key
[] = { kSecPolicyName
};
81 const void *value
[] = { hostname
};
84 properties
= CFDictionaryCreate(NULL
, key
, value
, 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
85 if (properties
== NULL
) {
87 "unable to create dictionary for policy properties.\n");
90 policyRef
= SecPolicyCreateWithProperties(kSecPolicyAppleIPsec
, properties
);
92 CFRelease(properties
);
97 crypto_cssm_x509cert_CreateSecCertificateRef (vchar_t
*cert
)
99 SecCertificateRef certRef
= NULL
;
101 CFDataRef cert_data
= CFDataCreateWithBytesNoCopy(NULL
, (uint8_t*)cert
->v
, cert
->l
, kCFAllocatorNull
);
103 certRef
= SecCertificateCreateWithData(NULL
, cert_data
);
104 CFRelease(cert_data
);
107 if (certRef
== NULL
) {
109 "unable to get a certifcate reference.\n");
114 /* HACK!!! - temporary until this prototype gets moved */
115 extern CFDataRef
SecCertificateCopySubjectSequence( SecCertificateRef certificate
);
118 crypto_cssm_CopySubjectSequence(SecCertificateRef certRef
)
120 CFDataRef subject
= NULL
;
122 subject
= SecCertificateCopySubjectSequence(certRef
);
129 crypto_cssm_check_x509cert_dates (SecCertificateRef certificateRef
)
131 cert_status_t certStatus
= CERT_STATUS_OK
;
133 CFAbsoluteTime timeNow
= 0;
134 CFAbsoluteTime notvalidbeforedate
= 0;
135 CFAbsoluteTime notvalidafterdate
= 0;
136 CFDateRef nowcfdatedata
= NULL
;
137 CFDateRef notvalidbeforedatedata
= NULL
;
138 CFDateRef notvalidafterdatedata
= NULL
;
139 CFArrayRef certProparray
= NULL
;
141 if ((timeNow
= CFAbsoluteTimeGetCurrent())) {
142 if (SecCertificateIsValid(certificateRef
, timeNow
)) {
143 plog(ASL_LEVEL_NOTICE
, "Certificate expiration date is OK\n");
144 certStatus
= CERT_STATUS_OK
;
146 nowcfdatedata
= CFDateCreate( NULL
, timeNow
);
148 if ((notvalidbeforedate
= SecCertificateNotValidBefore(certificateRef
))) {
149 notvalidbeforedatedata
= CFDateCreate(NULL
, notvalidbeforedate
);
152 if ((notvalidafterdate
= SecCertificateNotValidAfter(certificateRef
))) {
153 notvalidafterdatedata
= CFDateCreate(NULL
, notvalidafterdate
);
161 CFCalendarRef calendar
= CFCalendarCreateWithIdentifier(kCFAllocatorDefault
, kCFGregorianCalendar
);
164 if (notvalidbeforedate
) {
165 CFCalendarDecomposeAbsoluteTime(calendar
, notvalidbeforedate
, "yMdHm", &year
, &month
, &day
, &hour
, &minute
);
166 plog(ASL_LEVEL_DEBUG
, "Certificate not valid before yr %d, mon %d, days %d, hours %d, min %d\n", year
, month
, day
, hour
, minute
);
169 if (notvalidafterdate
) {
170 CFCalendarDecomposeAbsoluteTime(calendar
, notvalidafterdate
, "yMdHm", &year
, &month
, &day
, &hour
, &minute
);
171 plog(ASL_LEVEL_DEBUG
, "Certificate not valid after yr %d, mon %d, days %d, hours %d, min %d\n", year
, month
, day
, hour
, minute
);
176 if (nowcfdatedata
!= NULL
) {
177 if (notvalidbeforedatedata
&& CFDateCompare(nowcfdatedata
, notvalidbeforedatedata
, NULL
) == kCFCompareLessThan
){
179 "current time before valid time\n");
180 certStatus
= CERT_STATUS_PREMATURE
;
181 } else if (notvalidafterdatedata
&& (CFDateCompare( nowcfdatedata
, notvalidafterdatedata
, NULL
) == kCFCompareGreaterThan
)){
183 "current time after valid time\n");
184 certStatus
= CERT_STATUS_EXPIRED
;
189 plog(ASL_LEVEL_ERR
, "CFAbsoluteTimeGetCurrent() failed");
190 certStatus
= CERT_STATUS_INVALID
;
193 if (notvalidbeforedatedata
)
194 CFRelease(notvalidbeforedatedata
);
195 if (notvalidafterdatedata
)
196 CFRelease(notvalidafterdatedata
);
198 CFRelease(certProparray
);
200 CFRelease(nowcfdatedata
);
206 * Verify cert using security framework
208 int crypto_cssm_check_x509cert (cert_t
*hostcert
, cert_t
*certchain
, CFStringRef hostname
, SecKeyRef
*publicKeyRef
)
211 cert_status_t certStatus
= 0;
213 CFIndex certArrayRefNumValues
= 0;
216 SecCertificateRef
*certArrayRef
= NULL
;
217 SecPolicyRef policyRef
= crypto_cssm_x509cert_get_SecPolicyRef(hostname
);
219 if (!hostcert
|| !certchain
) {
223 // find the total number of certs
224 for (p
= certchain
; p
; p
= p
->chain
, n
++);
226 plog(ASL_LEVEL_DEBUG
,
227 "%s: checking chain of %d certificates.\n", __FUNCTION__
, (int)n
);
230 certArraySiz
= n
* sizeof(CFTypeRef
);
231 certArrayRef
= CFAllocatorAllocate(NULL
, certArraySiz
, 0);
235 bzero(certArrayRef
, certArraySiz
);
236 if ((certArrayRef
[certArrayRefNumValues
] = crypto_cssm_x509cert_CreateSecCertificateRef(&hostcert
->cert
))) {
237 /* don't overwrite any pending status */
238 if (!hostcert
->status
) {
239 hostcert
->status
= crypto_cssm_check_x509cert_dates(certArrayRef
[certArrayRefNumValues
]);
240 if (hostcert
->status
) {
242 "host certificate failed date verification: %d.\n", hostcert
->status
);
243 certStatus
= hostcert
->status
;
246 certArrayRefNumValues
++;
248 for (p
= certchain
; p
&& certArrayRefNumValues
< n
; p
= p
->chain
) {
250 if ((certArrayRef
[certArrayRefNumValues
] = crypto_cssm_x509cert_CreateSecCertificateRef(&p
->cert
))) {
251 /* don't overwrite any pending status */
253 p
->status
= crypto_cssm_check_x509cert_dates(certArrayRef
[certArrayRefNumValues
]);
256 "other certificate in chain failed date verification: %d.\n", p
->status
);
258 certStatus
= p
->status
;
262 certArrayRefNumValues
++;
268 status
= EvaluateCert(certArrayRef
, certArrayRefNumValues
, policyRef
, publicKeyRef
);
270 while (certArrayRefNumValues
) {
271 CFRelease(certArrayRef
[--certArrayRefNumValues
]);
273 CFAllocatorDeallocate(NULL
, certArrayRef
);
276 CFRelease(policyRef
);
278 if (status
!= noErr
&& status
!= -1) {
280 "check_x509cert error %d %s.\n", (int)status
, GetSecurityErrorString(status
));
282 } else if (certStatus
== CERT_STATUS_PREMATURE
|| certStatus
== CERT_STATUS_EXPIRED
) {
290 int crypto_cssm_verify_x509sign(SecKeyRef publicKeyRef
, vchar_t
*hash
, vchar_t
*signature
, Boolean useSHA1
)
292 return SecKeyRawVerify(publicKeyRef
, useSHA1
? kSecPaddingPKCS1SHA1
: kSecPaddingPKCS1
, (uint8_t*)hash
->v
, hash
->l
, (uint8_t*)signature
->v
, signature
->l
);
296 * Encrypt a hash via CSSM using the private key in the keychain
299 vchar_t
* crypto_cssm_getsign(CFDataRef persistentCertRef
, vchar_t
* hash
)
302 OSStatus status
= -1;
303 SecIdentityRef identityRef
= NULL
;
304 SecKeyRef privateKeyRef
= NULL
;
308 CFDictionaryRef persistFind
= NULL
;
309 const void *keys_persist
[] = { kSecReturnRef
, kSecValuePersistentRef
, kSecClass
,
310 #if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
311 kSecUseSystemKeychain
,
314 const void *values_persist
[] = { kCFBooleanTrue
, persistentCertRef
, kSecClassIdentity
,
315 #if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
320 #define SIG_BUF_SIZE 1024
322 /* find identity by persistent ref */
323 persistFind
= CFDictionaryCreate(NULL
, keys_persist
, values_persist
,
324 (sizeof(keys_persist
) / sizeof(*keys_persist
)), NULL
, NULL
);
325 if (persistFind
== NULL
)
328 status
= SecItemCopyMatching(persistFind
, (CFTypeRef
*)&identityRef
);
332 status
= SecIdentityCopyPrivateKey(identityRef
, &privateKeyRef
);
336 // alloc buffer for result
337 sig
= vmalloc(SIG_BUF_SIZE
);
341 status
= SecKeyRawSign(privateKeyRef
, kSecPaddingPKCS1
, (uint8_t*)hash
->v
,
342 hash
->l
, (uint8_t*)sig
->v
, &sig
->l
);
347 CFRelease(identityRef
);
349 CFRelease(privateKeyRef
);
352 CFRelease(persistFind
);
354 if (status
!= noErr
) {
361 if (status
!= noErr
&& status
!= -1) {
363 "getsign error %d %s.\n", (int)status
, GetSecurityErrorString(status
));
372 * Retrieve a cert from the keychain
374 vchar_t
* crypto_cssm_get_x509cert(CFDataRef persistentCertRef
,
375 cert_status_t
*certStatus
)
378 OSStatus status
= -1;
379 vchar_t
*cert
= NULL
;
380 SecCertificateRef certificateRef
= NULL
;
381 CFDictionaryRef persistFind
= NULL
;
383 CFDataRef certData
= NULL
;
384 SecIdentityRef identityRef
= NULL
;
385 const void *keys_persist
[] = { kSecReturnRef
, kSecValuePersistentRef
, kSecClass
,
386 #if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
387 kSecUseSystemKeychain
,
390 const void *values_persist
[] = { kCFBooleanTrue
, persistentCertRef
, kSecClassIdentity
,
391 #if TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
396 /* find identity by persistent ref */
397 persistFind
= CFDictionaryCreate(NULL
, keys_persist
, values_persist
,
398 (sizeof(keys_persist
) / sizeof(*keys_persist
)), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
399 if (persistFind
== NULL
)
402 status
= SecItemCopyMatching(persistFind
, (CFTypeRef
*)&identityRef
);
406 status
= SecIdentityCopyCertificate(identityRef
, &certificateRef
);
410 certData
= SecCertificateCopyData(certificateRef
);
411 if (certData
== NULL
)
414 dataLen
= CFDataGetLength(certData
);
418 cert
= vmalloc(dataLen
);
422 CFDataGetBytes(certData
, CFRangeMake(0, dataLen
), (uint8_t*)cert
->v
);
424 // verify expiry or missing fields
426 *certStatus
= crypto_cssm_check_x509cert_dates(certificateRef
);
431 CFRelease(identityRef
);
433 CFRelease(certificateRef
);
435 CFRelease(persistFind
);
439 if (status
!= noErr
&& status
!= -1) {
441 "get_x509cert error %d %s.\n", (int)status
, GetSecurityErrorString(status
));
449 * Evaluate the trust of a cert using the policy provided
451 static OSStatus
EvaluateCert(SecCertificateRef evalCertArray
[], CFIndex evalCertArrayNumValues
, CFTypeRef policyRef
, SecKeyRef
*publicKeyRef
)
454 SecTrustRef trustRef
= 0;
455 SecTrustResultType evalResult
;
457 CFArrayRef errorStrings
;
459 CFArrayRef cfCertRef
= CFArrayCreate((CFAllocatorRef
) NULL
, (void*)evalCertArray
, evalCertArrayNumValues
,
460 &kCFTypeArrayCallBacks
);
464 "unable to create CFArray.\n");
468 status
= SecTrustCreateWithCertificates(cfCertRef
, policyRef
, &trustRef
);
472 status
= SecTrustEvaluate(trustRef
, &evalResult
);
476 if (evalResult
!= kSecTrustResultProceed
&& evalResult
!= kSecTrustResultUnspecified
) {
477 plog(ASL_LEVEL_ERR
, "Error evaluating certificate.\n");
479 switch (evalResult
) {
480 case kSecTrustResultInvalid
:
481 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultInvalid.\n");
483 case kSecTrustResultProceed
:
484 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultProceed.\n");
486 case kSecTrustResultDeny
:
487 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultDeny.\n");
489 case kSecTrustResultUnspecified
:
490 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultUnspecified.\n");
492 case kSecTrustResultRecoverableTrustFailure
:
493 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultRecoverableTrustFailure.\n");
495 case kSecTrustResultFatalTrustFailure
:
496 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultFatalTrustFailure.\n");
498 case kSecTrustResultOtherError
:
499 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultOtherError.\n");
502 plog(ASL_LEVEL_DEBUG
, "eval result unknown: value = %d.\n", (int)evalResult
);
506 errorStrings
= SecTrustCopyProperties(trustRef
);
509 CFDictionaryRef dict
;
512 CFIndex count
, maxcount
= CFArrayGetCount(errorStrings
);
514 plog(ASL_LEVEL_ERR
, "---------------Returned error strings: ---------------.\n");
515 for (count
= 0; count
< maxcount
; count
++) {
516 dict
= CFArrayGetValueAtIndex(errorStrings
, count
);
517 if (dict
&& (CFGetTypeID(dict
) == CFDictionaryGetTypeID())) {
518 val
= CFDictionaryGetValue(dict
, kSecPropertyKeyType
);
519 if (val
&& (CFGetTypeID(val
) == CFStringGetTypeID())) {
520 str
= CFStringGetCStringPtr(val
, kCFStringEncodingMacRoman
);
522 plog(ASL_LEVEL_ERR
, "type = %s.\n", str
);
524 val
= CFDictionaryGetValue(dict
, kSecPropertyKeyValue
);
525 if (val
&& (CFGetTypeID(val
) == CFStringGetTypeID())) {
526 str
= CFStringGetCStringPtr(val
, kCFStringEncodingMacRoman
);
528 plog(ASL_LEVEL_ERR
, "value = %s.\n", str
);
532 plog(ASL_LEVEL_ERR
, "-----------------------------------------------------.\n");
533 CFRelease(errorStrings
);
540 /* get and return the public key */
541 *publicKeyRef
= SecTrustCopyPublicKey(trustRef
);
545 CFRelease(cfCertRef
);
549 if (status
!= noErr
&& status
!= -1) {
551 "EvaluateCert error %d %s.\n", (int)status
, GetSecurityErrorString(status
));
558 * Return string representation of Security-related OSStatus.
561 GetSecurityErrorString(OSStatus err
)
568 case errSecNotAvailable
:
569 return "errSecNotAvailable";
571 #if !TARGET_OS_EMBEDDED
583 return "errSecReadOnly";
584 case errSecAuthFailed
:
585 return "errSecAuthFailed";
586 case errSecNoSuchKeychain
:
587 return "errSecNoSuchKeychain";
588 case errSecInvalidKeychain
:
589 return "errSecInvalidKeychain";
590 case errSecDuplicateKeychain
:
591 return "errSecDuplicateKeychain";
592 case errSecDuplicateCallback
:
593 return "errSecDuplicateCallback";
594 case errSecInvalidCallback
:
595 return "errSecInvalidCallback";
596 case errSecBufferTooSmall
:
597 return "errSecBufferTooSmall";
598 case errSecDataTooLarge
:
599 return "errSecDataTooLarge";
600 case errSecNoSuchAttr
:
601 return "errSecNoSuchAttr";
602 case errSecInvalidItemRef
:
603 return "errSecInvalidItemRef";
604 case errSecInvalidSearchRef
:
605 return "errSecInvalidSearchRef";
606 case errSecNoSuchClass
:
607 return "errSecNoSuchClass";
608 case errSecNoDefaultKeychain
:
609 return "errSecNoDefaultKeychain";
610 case errSecInteractionNotAllowed
:
611 return "errSecInteractionNotAllowed";
612 case errSecReadOnlyAttr
:
613 return "errSecReadOnlyAttr";
614 case errSecWrongSecVersion
:
615 return "errSecWrongSecVersion";
616 case errSecKeySizeNotAllowed
:
617 return "errSecKeySizeNotAllowed";
618 case errSecNoStorageModule
:
619 return "errSecNoStorageModule";
620 case errSecNoCertificateModule
:
621 return "errSecNoCertificateModule";
622 case errSecNoPolicyModule
:
623 return "errSecNoPolicyModule";
624 case errSecInteractionRequired
:
625 return "errSecInteractionRequired";
626 case errSecDataNotAvailable
:
627 return "errSecDataNotAvailable";
628 case errSecDataNotModifiable
:
629 return "errSecDataNotModifiable";
630 case errSecCreateChainFailed
:
631 return "errSecCreateChainFailed";
632 case errSecACLNotSimple
:
633 return "errSecACLNotSimple";
634 case errSecPolicyNotFound
:
635 return "errSecPolicyNotFound";
636 case errSecInvalidTrustSetting
:
637 return "errSecInvalidTrustSetting";
638 case errSecNoAccessForItem
:
639 return "errSecNoAccessForItem";
640 case errSecInvalidOwnerEdit
:
641 return "errSecInvalidOwnerEdit";
643 case errSecDuplicateItem
:
644 return "errSecDuplicateItem";
645 case errSecItemNotFound
:
646 return "errSecItemNotFound";