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
30 #include <Security/SecCertificate.h>
31 #include <Security/SecPolicy.h>
32 #include <Security/SecTrust.h>
33 #include <Security/SecKey.h>
34 #include <Security/SecIdentity.h>
35 #include <Security/SecItem.h>
36 #include <TargetConditionals.h>
37 #if TARGET_OS_EMBEDDED
38 #include <Security/SecTrustPriv.h>
39 #include <Security/SecPolicyPriv.h>
40 #include <Security/SecCertificatePriv.h>
42 #include <Security/SecBase.h>
43 #include <Security/SecIdentityPriv.h>
44 #include <Security/SecIdentitySearch.h>
45 #include <Security/SecKeychain.h>
46 #include <Security/SecKeychainItem.h>
47 #include <Security/SecKeychainItemPriv.h>
48 #include <Security/SecCertificateOIDs.h>
49 #include <Security/SecKeyPriv.h>
50 #include <Security/oidsalg.h>
51 #include <Security/cssmapi.h>
52 #include <Security/SecPolicySearch.h>
54 #include <CoreFoundation/CoreFoundation.h>
55 #if !TARGET_OS_EMBEDDED
56 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
65 #include "crypto_cssm.h"
68 static OSStatus
EvaluateCert(SecCertificateRef evalCertArray
[], CFIndex evalCertArrayNumValues
, CFTypeRef policyRef
, SecKeyRef
*publicKeyRef
);
70 #if !TARGET_OS_EMBEDDED
74 crypto_cssm_x509cert_get_SecPolicyRef (CFStringRef hostname
)
76 SecPolicyRef policyRef
= NULL
;
77 CFDictionaryRef properties
= NULL
;
78 const void *key
[] = { kSecPolicyName
};
79 const void *value
[] = { hostname
};
82 properties
= CFDictionaryCreate(NULL
, key
, value
, 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
83 if (properties
== NULL
) {
85 "unable to create dictionary for policy properties.\n");
88 policyRef
= SecPolicyCreateWithProperties(kSecPolicyAppleIPsec
, properties
);
90 CFRelease(properties
);
95 crypto_cssm_x509cert_CreateSecCertificateRef (vchar_t
*cert
)
97 SecCertificateRef certRef
= NULL
;
99 CFDataRef cert_data
= CFDataCreateWithBytesNoCopy(NULL
, (uint8_t*)cert
->v
, cert
->l
, kCFAllocatorNull
);
101 certRef
= SecCertificateCreateWithData(NULL
, cert_data
);
102 CFRelease(cert_data
);
105 if (certRef
== NULL
) {
107 "unable to get a certifcate reference.\n");
112 /* HACK!!! - temporary until this prototype gets moved */
113 extern CFDataRef
SecCertificateCopySubjectSequence( SecCertificateRef certificate
);
116 crypto_cssm_CopySubjectSequence(SecCertificateRef certRef
)
118 CFDataRef subject
= NULL
;
120 subject
= SecCertificateCopySubjectSequence(certRef
);
127 crypto_cssm_check_x509cert_dates (SecCertificateRef certificateRef
)
129 cert_status_t certStatus
= CERT_STATUS_OK
;
130 #if TARGET_OS_EMBEDDED
131 CFAbsoluteTime timeNow
= 0;
132 CFAbsoluteTime notvalidbeforedate
= 0;
133 CFAbsoluteTime notvalidafterdate
= 0;
134 CFDateRef nowcfdatedata
= NULL
;
135 CFDateRef notvalidbeforedatedata
= NULL
;
136 CFDateRef notvalidafterdatedata
= NULL
;
137 CFArrayRef certProparray
= NULL
;
138 CFDictionaryRef propDict
= NULL
;
139 const void *datevalue
= NULL
;
140 const void *labelvalue
= NULL
;
141 CFGregorianDate gregoriandate
;
145 if ((certProparray
= SecCertificateCopyProperties(certificateRef
))){
146 if ((count
= CFArrayGetCount( certProparray
))){
147 for( i
= 0; i
< count
; i
++) {
148 if ((propDict
= CFArrayGetValueAtIndex(certProparray
, i
))) {
149 if ( CFDictionaryGetValueIfPresent(propDict
, kSecPropertyKeyValue
, (const void**)&datevalue
)){
150 /* get kSecPropertyKeyLabel */
151 if ( (datevalue
) && (CFDictionaryGetValueIfPresent(propDict
, kSecPropertyKeyLabel
, (const void**)&labelvalue
))){
152 if ( (labelvalue
) && (CFStringCompare( (CFStringRef
)labelvalue
, CFSTR("Not Valid Before"), 0) == kCFCompareEqualTo
)){
153 if ( (notvalidbeforedate
= CFDateGetAbsoluteTime(datevalue
))) {
154 if (notvalidbeforedatedata
) {
155 CFRelease(notvalidbeforedatedata
);
157 notvalidbeforedatedata
= CFDateCreate(NULL
, notvalidbeforedate
);
159 }else if ((labelvalue
) && (CFStringCompare( (CFStringRef
)labelvalue
, CFSTR("Not Valid After"), 0 ) == kCFCompareEqualTo
)){
160 if ( (notvalidafterdate
= CFDateGetAbsoluteTime(datevalue
))) {
161 if (notvalidafterdatedata
) {
162 CFRelease(notvalidafterdatedata
);
164 notvalidafterdatedata
= CFDateCreate(NULL
, notvalidafterdate
);
174 if ( (timeNow
= CFAbsoluteTimeGetCurrent()) && (nowcfdatedata
= CFDateCreate( NULL
, timeNow
))){
175 if ( notvalidbeforedatedata
){
176 gregoriandate
= CFAbsoluteTimeGetGregorianDate(notvalidbeforedate
, NULL
);
177 plog(ASL_LEVEL_DEBUG
,
178 "Certificate not valid before yr %d, mon %d, days %d, hours %d, min %d\n", (int)gregoriandate
.year
, gregoriandate
.month
, gregoriandate
.day
, gregoriandate
.hour
, gregoriandate
.minute
);
179 gregoriandate
= CFAbsoluteTimeGetGregorianDate(notvalidafterdate
, NULL
);
180 plog(ASL_LEVEL_DEBUG
,
181 "Certificate not valid after yr %d, mon %d, days %d, hours %d, min %d\n", (int)gregoriandate
.year
, gregoriandate
.month
, gregoriandate
.day
, gregoriandate
.hour
, gregoriandate
.minute
);
182 if ( CFDateCompare( nowcfdatedata
, notvalidbeforedatedata
, NULL
) == kCFCompareLessThan
){
184 "current time before valid time\n");
185 certStatus
= CERT_STATUS_PREMATURE
;
186 } else if (notvalidafterdatedata
&& (CFDateCompare( nowcfdatedata
, notvalidafterdatedata
, NULL
) == kCFCompareGreaterThan
)){
188 "current time after valid time\n");
189 certStatus
= CERT_STATUS_EXPIRED
;
191 plog(ASL_LEVEL_INFO
, "Certificate expiration date is OK\n");
192 certStatus
= CERT_STATUS_OK
;
197 if (notvalidbeforedatedata
)
198 CFRelease(notvalidbeforedatedata
);
199 if (notvalidafterdatedata
)
200 CFRelease(notvalidafterdatedata
);
202 CFRelease(certProparray
);
204 CFRelease(nowcfdatedata
);
210 * Verify cert using security framework
212 int crypto_cssm_check_x509cert (cert_t
*hostcert
, cert_t
*certchain
, CFStringRef hostname
, SecKeyRef
*publicKeyRef
)
215 cert_status_t certStatus
= 0;
217 CFIndex certArrayRefNumValues
= 0;
220 SecCertificateRef
*certArrayRef
= NULL
;
221 SecPolicyRef policyRef
= crypto_cssm_x509cert_get_SecPolicyRef(hostname
);
223 if (!hostcert
|| !certchain
) {
227 // find the total number of certs
228 for (p
= certchain
; p
; p
= p
->chain
, n
++);
230 plog(ASL_LEVEL_DEBUG
,
231 "%s: checking chain of %d certificates.\n", __FUNCTION__
, (int)n
);
234 certArraySiz
= n
* sizeof(CFTypeRef
);
235 certArrayRef
= CFAllocatorAllocate(NULL
, certArraySiz
, 0);
239 bzero(certArrayRef
, certArraySiz
);
240 if ((certArrayRef
[certArrayRefNumValues
] = crypto_cssm_x509cert_CreateSecCertificateRef(&hostcert
->cert
))) {
241 /* don't overwrite any pending status */
242 if (!hostcert
->status
) {
243 hostcert
->status
= crypto_cssm_check_x509cert_dates(certArrayRef
[certArrayRefNumValues
]);
244 if (hostcert
->status
) {
246 "host certificate failed date verification: %d.\n", hostcert
->status
);
247 certStatus
= hostcert
->status
;
250 certArrayRefNumValues
++;
252 for (p
= certchain
; p
&& certArrayRefNumValues
< n
; p
= p
->chain
) {
254 if ((certArrayRef
[certArrayRefNumValues
] = crypto_cssm_x509cert_CreateSecCertificateRef(&p
->cert
))) {
255 /* don't overwrite any pending status */
257 p
->status
= crypto_cssm_check_x509cert_dates(certArrayRef
[certArrayRefNumValues
]);
260 "other certificate in chain failed date verification: %d.\n", p
->status
);
262 certStatus
= p
->status
;
266 certArrayRefNumValues
++;
272 status
= EvaluateCert(certArrayRef
, certArrayRefNumValues
, policyRef
, publicKeyRef
);
274 while (certArrayRefNumValues
) {
275 CFRelease(certArrayRef
[--certArrayRefNumValues
]);
277 CFAllocatorDeallocate(NULL
, certArrayRef
);
280 CFRelease(policyRef
);
282 if (status
!= noErr
&& status
!= -1) {
284 "error %d %s.\n", (int)status
, GetSecurityErrorString(status
));
286 } else if (certStatus
== CERT_STATUS_PREMATURE
|| certStatus
== CERT_STATUS_EXPIRED
) {
294 int crypto_cssm_verify_x509sign(SecKeyRef publicKeyRef
, vchar_t
*hash
, vchar_t
*signature
, Boolean useSHA1
)
296 return SecKeyRawVerify(publicKeyRef
, useSHA1
? kSecPaddingPKCS1SHA1
: kSecPaddingPKCS1
, (uint8_t*)hash
->v
, hash
->l
, (uint8_t*)signature
->v
, signature
->l
);
300 * Encrypt a hash via CSSM using the private key in the keychain
303 vchar_t
* crypto_cssm_getsign(CFDataRef persistentCertRef
, vchar_t
* hash
)
306 OSStatus status
= -1;
307 SecIdentityRef identityRef
= NULL
;
308 SecKeyRef privateKeyRef
= NULL
;
312 CFDictionaryRef persistFind
= NULL
;
313 const void *keys_persist
[] = { kSecReturnRef
, kSecValuePersistentRef
, kSecClass
};
314 const void *values_persist
[] = { kCFBooleanTrue
, persistentCertRef
, kSecClassIdentity
};
316 #define SIG_BUF_SIZE 1024
318 /* find identity by persistent ref */
319 persistFind
= CFDictionaryCreate(NULL
, keys_persist
, values_persist
,
320 (sizeof(keys_persist
) / sizeof(*keys_persist
)), NULL
, NULL
);
321 if (persistFind
== NULL
)
324 status
= SecItemCopyMatching(persistFind
, (CFTypeRef
*)&identityRef
);
328 status
= SecIdentityCopyPrivateKey(identityRef
, &privateKeyRef
);
332 // alloc buffer for result
333 sig
= vmalloc(SIG_BUF_SIZE
);
337 status
= SecKeyRawSign(privateKeyRef
, kSecPaddingPKCS1
, (uint8_t*)hash
->v
,
338 hash
->l
, (uint8_t*)sig
->v
, &sig
->l
);
343 CFRelease(identityRef
);
345 CFRelease(privateKeyRef
);
348 CFRelease(persistFind
);
350 if (status
!= noErr
) {
357 if (status
!= noErr
&& status
!= -1) {
359 "error %d %s.\n", (int)status
, GetSecurityErrorString(status
));
368 * Retrieve a cert from the keychain
370 vchar_t
* crypto_cssm_get_x509cert(CFDataRef persistentCertRef
,
371 cert_status_t
*certStatus
)
374 OSStatus status
= -1;
375 vchar_t
*cert
= NULL
;
376 SecCertificateRef certificateRef
= NULL
;
377 CFDictionaryRef persistFind
= NULL
;
379 CFDataRef certData
= NULL
;
380 SecIdentityRef identityRef
= NULL
;
381 const void *keys_persist
[] = { kSecReturnRef
, kSecValuePersistentRef
, kSecClass
};
382 const void *values_persist
[] = { kCFBooleanTrue
, persistentCertRef
, kSecClassIdentity
};
384 /* find identity by persistent ref */
385 persistFind
= CFDictionaryCreate(NULL
, keys_persist
, values_persist
,
386 (sizeof(keys_persist
) / sizeof(*keys_persist
)), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
387 if (persistFind
== NULL
)
390 status
= SecItemCopyMatching(persistFind
, (CFTypeRef
*)&identityRef
);
394 status
= SecIdentityCopyCertificate(identityRef
, &certificateRef
);
398 certData
= SecCertificateCopyData(certificateRef
);
399 if (certData
== NULL
)
402 dataLen
= CFDataGetLength(certData
);
406 cert
= vmalloc(dataLen
);
410 CFDataGetBytes(certData
, CFRangeMake(0, dataLen
), (uint8_t*)cert
->v
);
412 // verify expiry or missing fields
414 *certStatus
= crypto_cssm_check_x509cert_dates(certificateRef
);
419 CFRelease(identityRef
);
421 CFRelease(certificateRef
);
423 CFRelease(persistFind
);
427 if (status
!= noErr
&& status
!= -1) {
429 "error %d %s.\n", (int)status
, GetSecurityErrorString(status
));
437 * Evaluate the trust of a cert using the policy provided
439 static OSStatus
EvaluateCert(SecCertificateRef evalCertArray
[], CFIndex evalCertArrayNumValues
, CFTypeRef policyRef
, SecKeyRef
*publicKeyRef
)
442 SecTrustRef trustRef
= 0;
443 SecTrustResultType evalResult
;
445 CFArrayRef errorStrings
;
447 CFArrayRef cfCertRef
= CFArrayCreate((CFAllocatorRef
) NULL
, (void*)evalCertArray
, evalCertArrayNumValues
,
448 &kCFTypeArrayCallBacks
);
452 "unable to create CFArray.\n");
456 status
= SecTrustCreateWithCertificates(cfCertRef
, policyRef
, &trustRef
);
460 status
= SecTrustEvaluate(trustRef
, &evalResult
);
464 if (evalResult
!= kSecTrustResultProceed
&& evalResult
!= kSecTrustResultUnspecified
) {
465 plog(ASL_LEVEL_ERR
, "Error evaluating certificate.\n");
467 switch (evalResult
) {
468 case kSecTrustResultInvalid
:
469 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultInvalid.\n");
471 case kSecTrustResultProceed
:
472 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultProceed.\n");
474 case kSecTrustResultDeny
:
475 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultDeny.\n");
477 case kSecTrustResultUnspecified
:
478 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultUnspecified.\n");
480 case kSecTrustResultRecoverableTrustFailure
:
481 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultRecoverableTrustFailure.\n");
483 case kSecTrustResultFatalTrustFailure
:
484 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultFatalTrustFailure.\n");
486 case kSecTrustResultOtherError
:
487 plog(ASL_LEVEL_DEBUG
, "eval result = kSecTrustResultOtherError.\n");
490 plog(ASL_LEVEL_DEBUG
, "eval result unknown: value = %d.\n", (int)evalResult
);
494 errorStrings
= SecTrustCopyProperties(trustRef
);
497 CFDictionaryRef dict
;
500 CFIndex count
, maxcount
= CFArrayGetCount(errorStrings
);
502 plog(ASL_LEVEL_ERR
, "---------------Returned error strings: ---------------.\n");
503 for (count
= 0; count
< maxcount
; count
++) {
504 dict
= CFArrayGetValueAtIndex(errorStrings
, count
);
505 if (dict
&& (CFGetTypeID(dict
) == CFDictionaryGetTypeID())) {
506 val
= CFDictionaryGetValue(dict
, kSecPropertyKeyType
);
507 if (val
&& (CFGetTypeID(val
) == CFStringGetTypeID())) {
508 str
= CFStringGetCStringPtr(val
, kCFStringEncodingMacRoman
);
510 plog(ASL_LEVEL_ERR
, "type = %s.\n", str
);
512 val
= CFDictionaryGetValue(dict
, kSecPropertyKeyValue
);
513 if (val
&& (CFGetTypeID(val
) == CFStringGetTypeID())) {
514 str
= CFStringGetCStringPtr(val
, kCFStringEncodingMacRoman
);
516 plog(ASL_LEVEL_ERR
, "value = %s.\n", str
);
520 plog(ASL_LEVEL_ERR
, "-----------------------------------------------------.\n");
521 CFRelease(errorStrings
);
528 /* get and return the public key */
529 *publicKeyRef
= SecTrustCopyPublicKey(trustRef
);
533 CFRelease(cfCertRef
);
537 if (status
!= noErr
&& status
!= -1) {
539 "error %d %s.\n", (int)status
, GetSecurityErrorString(status
));
546 * Return string representation of Security-related OSStatus.
549 GetSecurityErrorString(OSStatus err
)
556 case errSecNotAvailable
:
557 return "errSecNotAvailable";
559 #if !TARGET_OS_EMBEDDED
569 return "errSecReadOnly";
570 case errSecAuthFailed
:
571 return "errSecAuthFailed";
572 case errSecNoSuchKeychain
:
573 return "errSecNoSuchKeychain";
574 case errSecInvalidKeychain
:
575 return "errSecInvalidKeychain";
576 case errSecDuplicateKeychain
:
577 return "errSecDuplicateKeychain";
578 case errSecDuplicateCallback
:
579 return "errSecDuplicateCallback";
580 case errSecInvalidCallback
:
581 return "errSecInvalidCallback";
582 case errSecBufferTooSmall
:
583 return "errSecBufferTooSmall";
584 case errSecDataTooLarge
:
585 return "errSecDataTooLarge";
586 case errSecNoSuchAttr
:
587 return "errSecNoSuchAttr";
588 case errSecInvalidItemRef
:
589 return "errSecInvalidItemRef";
590 case errSecInvalidSearchRef
:
591 return "errSecInvalidSearchRef";
592 case errSecNoSuchClass
:
593 return "errSecNoSuchClass";
594 case errSecNoDefaultKeychain
:
595 return "errSecNoDefaultKeychain";
596 case errSecInteractionNotAllowed
:
597 return "errSecInteractionNotAllowed";
598 case errSecReadOnlyAttr
:
599 return "errSecReadOnlyAttr";
600 case errSecWrongSecVersion
:
601 return "errSecWrongSecVersion";
602 case errSecKeySizeNotAllowed
:
603 return "errSecKeySizeNotAllowed";
604 case errSecNoStorageModule
:
605 return "errSecNoStorageModule";
606 case errSecNoCertificateModule
:
607 return "errSecNoCertificateModule";
608 case errSecNoPolicyModule
:
609 return "errSecNoPolicyModule";
610 case errSecInteractionRequired
:
611 return "errSecInteractionRequired";
612 case errSecDataNotAvailable
:
613 return "errSecDataNotAvailable";
614 case errSecDataNotModifiable
:
615 return "errSecDataNotModifiable";
616 case errSecCreateChainFailed
:
617 return "errSecCreateChainFailed";
618 case errSecACLNotSimple
:
619 return "errSecACLNotSimple";
620 case errSecPolicyNotFound
:
621 return "errSecPolicyNotFound";
622 case errSecInvalidTrustSetting
:
623 return "errSecInvalidTrustSetting";
624 case errSecNoAccessForItem
:
625 return "errSecNoAccessForItem";
626 case errSecInvalidOwnerEdit
:
627 return "errSecInvalidOwnerEdit";
629 case errSecDuplicateItem
:
630 return "errSecDuplicateItem";
631 case errSecItemNotFound
:
632 return "errSecItemNotFound";