]>
Commit | Line | Data |
---|---|---|
52b7d2ce A |
1 | |
2 | /* | |
3 | * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved. | |
4 | * | |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
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. | |
12 | * | |
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 | |
19 | * under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | ||
25 | /* | |
26 | * Racoon module for verifying and signing certificates through Security | |
27 | * Framework and CSSM | |
28 | */ | |
29 | ||
52b7d2ce A |
30 | #include <Security/SecCertificate.h> |
31 | #include <Security/SecPolicy.h> | |
d1e348cf A |
32 | #include <Security/SecTrust.h> |
33 | #include <Security/SecKey.h> | |
52b7d2ce | 34 | #include <Security/SecIdentity.h> |
d1e348cf A |
35 | |
36 | #include <TargetConditionals.h> | |
37 | #if TARGET_OS_EMBEDDED | |
38 | #include <Security/SecItem.h> | |
fce29cd9 A |
39 | #include <Security/SecTrustPriv.h> |
40 | #include <Security/SecPolicyPriv.h> | |
41 | #include <Security/SecCertificatePriv.h> | |
d1e348cf A |
42 | #else |
43 | #include <Security/SecBase.h> | |
52b7d2ce A |
44 | #include <Security/SecIdentityPriv.h> |
45 | #include <Security/SecIdentitySearch.h> | |
46 | #include <Security/SecKeychain.h> | |
47 | #include <Security/SecKeychainItem.h> | |
48 | #include <Security/SecKeychainItemPriv.h> | |
d1e348cf | 49 | |
52b7d2ce | 50 | #include <Security/SecKeyPriv.h> |
52b7d2ce A |
51 | #include <Security/oidsalg.h> |
52 | #include <Security/cssmapi.h> | |
53 | #include <Security/SecPolicySearch.h> | |
d1e348cf A |
54 | #endif |
55 | ||
52b7d2ce A |
56 | #include <CoreFoundation/CoreFoundation.h> |
57 | #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> | |
58 | #include "plog.h" | |
59 | #include "debug.h" | |
60 | #include "misc.h" | |
fce29cd9 | 61 | #include "oakley.h" |
52b7d2ce | 62 | |
52b7d2ce | 63 | |
d1e348cf | 64 | #include "crypto_cssm.h" |
52b7d2ce | 65 | |
e8d9021d A |
66 | #if TARGET_OS_EMBEDDED |
67 | static OSStatus EvaluateCert(SecCertificateRef evalCertArray[], CFIndex evalCertArrayNumValues, CFTypeRef policyRef, SecKeyRef *publicKeyRef); | |
68 | #else | |
69 | static OSStatus EvaluateCert(SecCertificateRef evalCertArray[], CFIndex evalCertArrayNumValues, CFTypeRef policyRef); | |
70 | #endif | |
52b7d2ce | 71 | |
d1e348cf A |
72 | #if !TARGET_OS_EMBEDDED |
73 | static OSStatus FindPolicy(const CSSM_OID *policyOID, SecPolicyRef *policyRef); | |
74 | static OSStatus CopySystemKeychain(SecKeychainRef *keychainRef); | |
75 | #endif | |
52b7d2ce | 76 | |
e8d9021d A |
77 | static SecPolicyRef |
78 | crypto_cssm_x509cert_get_SecPolicyRef (CFStringRef hostname) | |
52b7d2ce A |
79 | { |
80 | OSStatus status; | |
d1e348cf | 81 | SecPolicyRef policyRef = NULL; |
e8d9021d A |
82 | #if !TARGET_OS_EMBEDDED |
83 | CSSM_OID ourPolicyOID = CSSMOID_APPLE_TP_IP_SEC; | |
d1e348cf | 84 | |
e8d9021d A |
85 | // get our policy object |
86 | status = FindPolicy(&ourPolicyOID, &policyRef); | |
87 | if (status != noErr && status != -1) { | |
88 | plog(LLV_ERROR, LOCATION, NULL, | |
89 | "error %d %s.\n", status, GetSecurityErrorString(status)); | |
90 | } | |
91 | #else | |
92 | if (hostname) { | |
93 | policyRef = SecPolicyCreateIPSec(FALSE, hostname); | |
94 | if (policyRef == NULL) { | |
95 | plog(LLV_ERROR, LOCATION, NULL, | |
96 | "unable to create a SSL policyRef.\n"); | |
97 | } | |
98 | } | |
99 | #endif | |
100 | return policyRef; | |
101 | } | |
102 | ||
103 | SecCertificateRef | |
104 | crypto_cssm_x509cert_get_SecCertificateRef (vchar_t *cert) | |
105 | { | |
106 | OSStatus status; | |
107 | SecCertificateRef certRef = NULL; | |
d1e348cf | 108 | #if !TARGET_OS_EMBEDDED |
52b7d2ce | 109 | CSSM_DATA certData; |
52b7d2ce A |
110 | |
111 | // create cert ref | |
112 | certData.Length = cert->l; | |
113 | certData.Data = (uint8 *)cert->v; | |
114 | status = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, | |
e8d9021d A |
115 | &certRef); |
116 | if (status != noErr && status != -1) { | |
117 | plog(LLV_ERROR, LOCATION, NULL, | |
118 | "error %d %s.\n", status, GetSecurityErrorString(status)); | |
119 | } | |
d1e348cf A |
120 | #else |
121 | CFDataRef cert_data = CFDataCreateWithBytesNoCopy(NULL, cert->v, cert->l, kCFAllocatorNull); | |
122 | if (cert_data) { | |
123 | certRef = SecCertificateCreateWithData(NULL, cert_data); | |
124 | CFRelease(cert_data); | |
125 | } | |
e8d9021d | 126 | #endif |
d1e348cf A |
127 | if (certRef == NULL) { |
128 | plog(LLV_ERROR, LOCATION, NULL, | |
e8d9021d | 129 | "unable to create a certRef.\n"); |
d1e348cf | 130 | } |
e8d9021d A |
131 | return certRef; |
132 | } | |
d1e348cf | 133 | |
e8d9021d A |
134 | static cert_status_t |
135 | crypto_cssm_check_x509cert_dates (SecCertificateRef certificateRef) | |
136 | { | |
137 | cert_status_t certStatus = CERT_STATUS_OK; | |
138 | #if TARGET_OS_EMBEDDED | |
139 | CFAbsoluteTime timeNow = 0; | |
140 | CFAbsoluteTime notvalidbeforedate = 0; | |
141 | CFAbsoluteTime notvalidafterdate = 0; | |
142 | CFDateRef nowcfdatedata = NULL; | |
143 | CFDateRef notvalidbeforedatedata = NULL; | |
144 | CFDateRef notvalidafterdatedata = NULL; | |
145 | CFArrayRef certProparray = NULL; | |
146 | CFDictionaryRef propDict = NULL; | |
147 | const void *datevalue = NULL; | |
148 | const void *labelvalue = NULL; | |
149 | CFGregorianDate gregoriandate; | |
150 | CFIndex count; | |
151 | CFIndex i; | |
152 | ||
153 | if ((certProparray = SecCertificateCopyProperties(certificateRef))){ | |
154 | if ((count = CFArrayGetCount( certProparray ))){ | |
155 | for( i = 0; i < count; i++) { | |
156 | if ((propDict = CFArrayGetValueAtIndex(certProparray, i))) { | |
157 | if ( CFDictionaryGetValueIfPresent(propDict, kSecPropertyKeyValue, (const void**)&datevalue)){ | |
158 | /* get kSecPropertyKeyLabel */ | |
159 | if ( (datevalue) && (CFDictionaryGetValueIfPresent(propDict, kSecPropertyKeyLabel, (const void**)&labelvalue))){ | |
160 | if ( (labelvalue) && (CFStringCompare( (CFStringRef)labelvalue, CFSTR("Not Valid Before"), 0) == kCFCompareEqualTo)){ | |
161 | if ( notvalidbeforedate = CFDateGetAbsoluteTime(datevalue)) { | |
162 | if (notvalidbeforedatedata) { | |
163 | CFRelease(notvalidbeforedatedata); | |
164 | } | |
165 | notvalidbeforedatedata = CFDateCreate(NULL, notvalidbeforedate); | |
166 | } | |
167 | }else if ((labelvalue) && (CFStringCompare( (CFStringRef)labelvalue, CFSTR("Not Valid After"), 0 ) == kCFCompareEqualTo)){ | |
168 | if ( notvalidafterdate = CFDateGetAbsoluteTime(datevalue)) { | |
169 | if (notvalidafterdatedata) { | |
170 | CFRelease(notvalidafterdatedata); | |
171 | } | |
172 | notvalidafterdatedata = CFDateCreate(NULL, notvalidafterdate); | |
173 | } | |
174 | } | |
175 | } | |
176 | } | |
177 | } | |
178 | } | |
d1e348cf | 179 | } |
d1e348cf | 180 | } |
e8d9021d A |
181 | |
182 | if ( (timeNow = CFAbsoluteTimeGetCurrent()) && (nowcfdatedata = CFDateCreate( NULL, timeNow))){ | |
183 | if ( notvalidbeforedatedata ){ | |
184 | gregoriandate = CFAbsoluteTimeGetGregorianDate(notvalidbeforedate, NULL); | |
185 | plog(LLV_DEBUG, LOCATION, NULL, | |
186 | "cert not valid before yr %d, mon %d, days %d, hours %d, min %d\n", gregoriandate.year, gregoriandate.month, gregoriandate.day, gregoriandate.hour, gregoriandate.minute); | |
187 | gregoriandate = CFAbsoluteTimeGetGregorianDate(notvalidafterdate, NULL); | |
188 | plog(LLV_DEBUG, LOCATION, NULL, | |
189 | "cert not valid after yr %d, mon %d, days %d, hours %d, min %d\n", gregoriandate.year, gregoriandate.month, gregoriandate.day, gregoriandate.hour, gregoriandate.minute); | |
190 | if ( CFDateCompare( nowcfdatedata, notvalidbeforedatedata, NULL ) == kCFCompareLessThan){ | |
191 | plog(LLV_ERROR, LOCATION, NULL, | |
192 | "current time before valid time\n"); | |
193 | certStatus = CERT_STATUS_PREMATURE; | |
194 | } else if (notvalidafterdatedata && (CFDateCompare( nowcfdatedata, notvalidafterdatedata, NULL ) == kCFCompareGreaterThan)){ | |
195 | plog(LLV_ERROR, LOCATION, NULL, | |
196 | "current time after valid time\n"); | |
197 | certStatus = CERT_STATUS_EXPIRED; | |
198 | }else { | |
199 | plog(LLV_INFO, LOCATION, NULL, "certificate expiration date OK\n"); | |
200 | certStatus = CERT_STATUS_OK; | |
201 | } | |
202 | } | |
203 | } | |
204 | ||
205 | if (notvalidbeforedatedata) | |
206 | CFRelease(notvalidbeforedatedata); | |
207 | if (notvalidafterdatedata) | |
208 | CFRelease(notvalidafterdatedata); | |
209 | if (certProparray) | |
210 | CFRelease(certProparray); | |
211 | if (nowcfdatedata) | |
212 | CFRelease(nowcfdatedata); | |
213 | #endif | |
214 | return certStatus; | |
215 | } | |
216 | ||
217 | /* | |
218 | * Verify cert using security framework | |
219 | */ | |
220 | #if TARGET_OS_EMBEDDED | |
221 | int crypto_cssm_check_x509cert (cert_t *hostcert, cert_t *certchain, CFStringRef hostname, SecKeyRef *publicKeyRef) | |
222 | #else | |
223 | int crypto_cssm_check_x509cert (cert_t *hostcert, cert_t *certchain, CFStringRef hostname) | |
d1e348cf | 224 | #endif |
e8d9021d A |
225 | { |
226 | cert_t *p; | |
227 | cert_status_t certStatus = 0; | |
228 | OSStatus status; | |
229 | CFIndex certArrayRefNumValues = 0; | |
230 | CFIndex n = 0; | |
231 | int certArraySiz; | |
232 | SecCertificateRef *certArrayRef = NULL; | |
233 | SecPolicyRef policyRef = crypto_cssm_x509cert_get_SecPolicyRef(hostname); | |
234 | ||
235 | if (!hostcert || !certchain) { | |
236 | return -1; | |
237 | } | |
238 | ||
239 | // find the total number of certs | |
240 | for (p = certchain; p; p = p->chain, n++); | |
241 | if (n> 1) { | |
242 | plog(LLV_DEBUG2, LOCATION, NULL, | |
243 | "%s: checking chain of %d certificates.\n", __FUNCTION__, n); | |
244 | } | |
245 | ||
246 | certArraySiz = n * sizeof(CFTypeRef); | |
247 | certArrayRef = CFAllocatorAllocate(NULL, certArraySiz, 0); | |
248 | if (!certArrayRef) { | |
249 | return -1; | |
250 | } | |
251 | bzero(certArrayRef, certArraySiz); | |
252 | if ((certArrayRef[certArrayRefNumValues] = crypto_cssm_x509cert_get_SecCertificateRef(&hostcert->cert))) { | |
253 | /* don't overwrite any pending status */ | |
254 | if (!hostcert->status) { | |
255 | hostcert->status = crypto_cssm_check_x509cert_dates(certArrayRef[certArrayRefNumValues]); | |
256 | if (hostcert->status) { | |
257 | plog(LLV_ERROR, LOCATION, NULL, | |
258 | "host certificate failed date verification: %d.\n", hostcert->status); | |
259 | certStatus = hostcert->status; | |
260 | } | |
261 | } | |
262 | certArrayRefNumValues++; | |
263 | } | |
264 | for (p = certchain; p && certArrayRefNumValues < n; p = p->chain) { | |
265 | if (p != hostcert) { | |
266 | if ((certArrayRef[certArrayRefNumValues] = crypto_cssm_x509cert_get_SecCertificateRef(&p->cert))) { | |
267 | /* don't overwrite any pending status */ | |
268 | if (!p->status) { | |
269 | p->status = crypto_cssm_check_x509cert_dates(certArrayRef[certArrayRefNumValues]); | |
270 | if (p->status) { | |
271 | plog(LLV_ERROR, LOCATION, NULL, | |
272 | "other certificate in chain failed date verification: %d.\n", p->status); | |
273 | if (!certStatus) { | |
274 | certStatus = p->status; | |
275 | } | |
276 | } | |
277 | } | |
278 | certArrayRefNumValues++; | |
279 | } | |
280 | } | |
281 | } | |
52b7d2ce A |
282 | |
283 | // evaluate cert | |
e8d9021d A |
284 | #if TARGET_OS_EMBEDDED |
285 | status = EvaluateCert(certArrayRef, certArrayRefNumValues, policyRef, publicKeyRef); | |
286 | #else | |
287 | status = EvaluateCert(certArrayRef, certArrayRefNumValues, policyRef); | |
288 | #endif | |
289 | ||
290 | while (certArrayRefNumValues) { | |
291 | CFRelease(certArrayRef[--certArrayRefNumValues]); | |
292 | } | |
293 | CFAllocatorDeallocate(NULL, certArrayRef); | |
52b7d2ce | 294 | |
52b7d2ce A |
295 | if (policyRef) |
296 | CFRelease(policyRef); | |
297 | ||
298 | if (status != noErr && status != -1) { | |
299 | plog(LLV_ERROR, LOCATION, NULL, | |
e8d9021d | 300 | "error %d %s.\n", status, GetSecurityErrorString(status)); |
52b7d2ce | 301 | status = -1; |
e8d9021d | 302 | } else if (certStatus == CERT_STATUS_PREMATURE || certStatus == CERT_STATUS_EXPIRED) { |
fce29cd9 A |
303 | status = -1; |
304 | } | |
52b7d2ce | 305 | return status; |
e8d9021d A |
306 | |
307 | } | |
52b7d2ce | 308 | |
e8d9021d A |
309 | #if TARGET_OS_EMBEDDED |
310 | int crypto_cssm_verify_x509sign(SecKeyRef publicKeyRef, vchar_t *hash, vchar_t *signature) | |
311 | { | |
312 | return SecKeyRawVerify(publicKeyRef, kSecPaddingPKCS1, hash->v, hash->l, signature->v, signature->l); | |
52b7d2ce | 313 | } |
e8d9021d | 314 | #endif |
52b7d2ce A |
315 | |
316 | /* | |
317 | * Encrypt a hash via CSSM using the private key in the keychain | |
318 | * from an identity. | |
319 | */ | |
320 | vchar_t* crypto_cssm_getsign(CFDataRef persistentCertRef, vchar_t* hash) | |
321 | { | |
322 | ||
e8d9021d | 323 | OSStatus status = -1; |
52b7d2ce | 324 | SecIdentityRef identityRef = NULL; |
d1e348cf A |
325 | SecKeyRef privateKeyRef = NULL; |
326 | vchar_t *sig = NULL; | |
327 | ||
328 | #if !TARGET_OS_EMBEDDED | |
e8d9021d | 329 | CSSM_SIZE bytesEncrypted = 0; |
d1e348cf | 330 | SecCertificateRef certificateRef = NULL; |
52b7d2ce A |
331 | SecIdentitySearchRef idSearchRef = NULL; |
332 | SecKeychainRef keychainRef = NULL; | |
52b7d2ce A |
333 | const CSSM_KEY *cssmKey = NULL; |
334 | CSSM_CSP_HANDLE cspHandle = nil; | |
335 | CSSM_CC_HANDLE cssmContextHandle = nil; | |
336 | const CSSM_ACCESS_CREDENTIALS *credentials = NULL; | |
52b7d2ce A |
337 | CSSM_DATA clearData; |
338 | CSSM_DATA cipherData; | |
339 | CSSM_DATA remData; | |
340 | CSSM_CONTEXT_ATTRIBUTE newAttr; | |
52b7d2ce A |
341 | |
342 | remData.Length = 0; | |
343 | remData.Data = 0; | |
344 | ||
d1e348cf | 345 | if (persistentCertRef) { |
52b7d2ce A |
346 | // get cert from keychain |
347 | status = SecKeychainItemCopyFromPersistentReference(persistentCertRef, (SecKeychainItemRef*)&certificateRef); | |
348 | if (status != noErr) | |
349 | goto end; | |
350 | ||
351 | // get keychain ref where cert is contained | |
352 | status = SecKeychainItemCopyKeychain((SecKeychainItemRef)certificateRef, &keychainRef); | |
353 | if (status != noErr) | |
354 | goto end; | |
355 | ||
356 | // get identity from the certificate | |
357 | status = SecIdentityCreateWithCertificate(keychainRef, certificateRef, &identityRef); | |
358 | if (status != noErr) | |
359 | goto end; | |
360 | ||
361 | } else { | |
d1e348cf | 362 | |
52b7d2ce A |
363 | // copy system keychain |
364 | status = CopySystemKeychain(&keychainRef); | |
365 | if (status != noErr) | |
366 | goto end; | |
367 | ||
368 | // serach for first identity in system keychain | |
369 | status = SecIdentitySearchCreate(keychainRef, CSSM_KEYUSE_SIGN, &idSearchRef); | |
370 | if (status != noErr) | |
371 | goto end; | |
372 | ||
373 | status = SecIdentitySearchCopyNext(idSearchRef, &identityRef); | |
374 | if (status != noErr) | |
375 | goto end; | |
376 | ||
377 | // get certificate from identity | |
378 | status = SecIdentityCopyCertificate(identityRef, &certificateRef); | |
379 | if (status != noErr) | |
380 | goto end; | |
381 | } | |
382 | ||
52b7d2ce A |
383 | // get private key from identity |
384 | status = SecIdentityCopyPrivateKey(identityRef, &privateKeyRef); | |
385 | if (status != noErr) | |
386 | goto end; | |
387 | ||
388 | // get CSSM_KEY pointer from key ref | |
389 | status = SecKeyGetCSSMKey(privateKeyRef, &cssmKey); | |
390 | if (status != noErr) | |
391 | goto end; | |
392 | ||
393 | // get CSSM CSP handle | |
394 | status = SecKeychainGetCSPHandle(keychainRef, &cspHandle); | |
395 | if (status != noErr) | |
396 | goto end; | |
397 | ||
398 | // create CSSM credentials to unlock private key for encryption - no UI to be used | |
399 | status = SecKeyGetCredentials(privateKeyRef, CSSM_ACL_AUTHORIZATION_ENCRYPT, | |
400 | kSecCredentialTypeNoUI, &credentials); | |
401 | if (status != noErr) | |
402 | goto end; | |
403 | ||
404 | // create asymmetric context for encryption | |
405 | status = CSSM_CSP_CreateAsymmetricContext(cspHandle, CSSM_ALGID_RSA, credentials, cssmKey, | |
406 | CSSM_PADDING_PKCS1, &cssmContextHandle); | |
407 | if (status != noErr) | |
408 | goto end; | |
409 | ||
410 | // add mode attribute to use private key for encryption | |
411 | newAttr.AttributeType = CSSM_ATTRIBUTE_MODE; | |
412 | newAttr.AttributeLength = sizeof(uint32); | |
413 | newAttr.Attribute.Data = (CSSM_DATA_PTR)CSSM_ALGMODE_PRIVATE_KEY; | |
414 | status = CSSM_UpdateContextAttributes(cssmContextHandle, 1, &newAttr); | |
415 | if(status != noErr) | |
416 | goto end; | |
417 | ||
418 | // and finally - encrypt data | |
419 | clearData.Length = hash->l; | |
420 | clearData.Data = (uint8 *)hash->v; | |
421 | cipherData.Length = 0; | |
422 | cipherData.Data = NULL; | |
423 | status = CSSM_EncryptData(cssmContextHandle, &clearData, 1, &cipherData, 1, &bytesEncrypted, | |
424 | &remData); | |
425 | if (status != noErr) | |
426 | goto end; | |
427 | ||
428 | if (remData.Length != 0) { // something didn't go right - should be zero | |
429 | status = -1; | |
430 | plog(LLV_ERROR, LOCATION, NULL, | |
431 | "unencrypted data remaining after encrypting hash.\n"); | |
432 | goto end; | |
433 | } | |
434 | ||
435 | // alloc buffer for result | |
436 | sig = vmalloc(0); | |
437 | if (sig == NULL) | |
438 | goto end; | |
439 | ||
440 | sig->l = cipherData.Length; | |
441 | sig->v = (caddr_t)cipherData.Data; | |
d1e348cf A |
442 | |
443 | #else | |
444 | ||
445 | CFDictionaryRef persistFind = NULL; | |
446 | const void *keys_persist[] = { kSecReturnRef, kSecValuePersistentRef }; | |
447 | const void *values_persist[] = { kCFBooleanTrue, persistentCertRef }; | |
448 | ||
449 | #define SIG_BUF_SIZE 1024 | |
450 | ||
451 | /* find identity by persistent ref */ | |
452 | persistFind = CFDictionaryCreate(NULL, keys_persist, values_persist, | |
453 | (sizeof(keys_persist) / sizeof(*keys_persist)), NULL, NULL); | |
454 | if (persistFind == NULL) | |
455 | goto end; | |
456 | ||
457 | status = SecItemCopyMatching(persistFind, (CFTypeRef *)&identityRef); | |
458 | if (status != noErr) | |
459 | goto end; | |
460 | ||
461 | status = SecIdentityCopyPrivateKey(identityRef, &privateKeyRef); | |
462 | if (status != noErr) | |
463 | goto end; | |
464 | ||
465 | // alloc buffer for result | |
466 | sig = vmalloc(SIG_BUF_SIZE); | |
467 | if (sig == NULL) | |
468 | goto end; | |
469 | ||
470 | status = SecKeyRawSign(privateKeyRef, kSecPaddingPKCS1, hash->v, | |
471 | hash->l, sig->v, &sig->l); | |
472 | ||
473 | #endif | |
474 | ||
52b7d2ce A |
475 | |
476 | end: | |
52b7d2ce A |
477 | if (identityRef) |
478 | CFRelease(identityRef); | |
479 | if (privateKeyRef) | |
480 | CFRelease(privateKeyRef); | |
d1e348cf A |
481 | |
482 | #if !TARGET_OS_EMBEDDED | |
483 | if (certificateRef) | |
484 | CFRelease(certificateRef); | |
485 | if (keychainRef) | |
486 | CFRelease(keychainRef); | |
52b7d2ce A |
487 | if (idSearchRef) |
488 | CFRelease(idSearchRef); | |
489 | if (cssmContextHandle) | |
490 | CSSM_DeleteContext(cssmContextHandle); | |
d1e348cf A |
491 | #else |
492 | if (persistFind) | |
493 | CFRelease(persistFind); | |
494 | #endif | |
495 | ||
52b7d2ce A |
496 | if (status != noErr) { |
497 | if (sig) { | |
498 | vfree(sig); | |
499 | sig = NULL; | |
500 | } | |
501 | } | |
502 | ||
503 | if (status != noErr && status != -1) { | |
504 | plog(LLV_ERROR, LOCATION, NULL, | |
505 | "error %d %s.\n", status, GetSecurityErrorString(status)); | |
506 | status = -1; | |
507 | } | |
508 | return sig; | |
509 | ||
510 | } | |
511 | ||
512 | ||
513 | /* | |
514 | * Retrieve a cert from the keychain | |
515 | */ | |
fce29cd9 A |
516 | vchar_t* crypto_cssm_get_x509cert(CFDataRef persistentCertRef, |
517 | cert_status_t *certStatus) | |
52b7d2ce A |
518 | { |
519 | ||
e8d9021d | 520 | OSStatus status = -1; |
52b7d2ce A |
521 | vchar_t *cert = NULL; |
522 | SecIdentityRef identityRef = NULL; | |
52b7d2ce A |
523 | SecCertificateRef certificateRef = NULL; |
524 | ||
d1e348cf A |
525 | #if !TARGET_OS_EMBEDDED |
526 | CSSM_DATA cssmData; | |
527 | SecIdentitySearchRef idSearchRef = NULL; | |
528 | SecKeychainRef keychainRef = NULL; | |
52b7d2ce A |
529 | |
530 | // get cert ref | |
531 | if (persistentCertRef) { | |
532 | status = SecKeychainItemCopyFromPersistentReference(persistentCertRef, (SecKeychainItemRef*)&certificateRef); | |
533 | if (status != noErr) | |
534 | goto end; | |
535 | } else { | |
536 | // copy system keychain | |
537 | status = CopySystemKeychain(&keychainRef); | |
538 | if (status != noErr) | |
539 | goto end; | |
540 | ||
541 | // find first identity in system keychain | |
542 | status = SecIdentitySearchCreate(keychainRef, CSSM_KEYUSE_SIGN, &idSearchRef); | |
543 | if (status != noErr) | |
544 | goto end; | |
545 | ||
546 | status = SecIdentitySearchCopyNext(idSearchRef, &identityRef); | |
547 | if (status != noErr) | |
548 | goto end; | |
549 | ||
550 | // get certificate from identity | |
551 | status = SecIdentityCopyCertificate(identityRef, &certificateRef); | |
552 | if (status != noErr) | |
553 | goto end; | |
554 | ||
555 | } | |
d1e348cf | 556 | |
52b7d2ce A |
557 | // get certificate data |
558 | cssmData.Length = 0; | |
559 | cssmData.Data = NULL; | |
560 | status = SecCertificateGetData(certificateRef, &cssmData); | |
561 | if (status != noErr) | |
562 | goto end; | |
563 | ||
564 | if (cssmData.Length == 0) | |
565 | goto end; | |
566 | ||
567 | cert = vmalloc(cssmData.Length); | |
568 | if (cert == NULL) | |
569 | goto end; | |
570 | ||
571 | // cssmData struct just points to the data | |
572 | // data must be copied to be returned | |
573 | memcpy(cert->v, cssmData.Data, cssmData.Length); | |
d1e348cf | 574 | |
fce29cd9 A |
575 | // verify expiry or missing fields |
576 | if (certStatus) { | |
577 | *certStatus = CERT_STATUS_OK; | |
578 | } | |
d1e348cf A |
579 | #else |
580 | ||
581 | CFDictionaryRef persistFind = NULL; | |
582 | const void *keys_persist[] = { kSecReturnRef, kSecValuePersistentRef }; | |
583 | const void *values_persist[] = { kCFBooleanTrue, persistentCertRef }; | |
584 | size_t dataLen; | |
585 | CFDataRef certData = NULL; | |
586 | ||
587 | /* find identity by persistent ref */ | |
588 | persistFind = CFDictionaryCreate(NULL, keys_persist, values_persist, | |
e8d9021d | 589 | (sizeof(keys_persist) / sizeof(*keys_persist)), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
d1e348cf A |
590 | if (persistFind == NULL) |
591 | goto end; | |
592 | ||
593 | status = SecItemCopyMatching(persistFind, (CFTypeRef *)&identityRef); | |
594 | if (status != noErr) | |
595 | goto end; | |
596 | ||
597 | status = SecIdentityCopyCertificate(identityRef, &certificateRef); | |
598 | if (status != noErr) | |
599 | goto end; | |
600 | ||
601 | certData = SecCertificateCopyData(certificateRef); | |
602 | if (certData == NULL) | |
603 | goto end; | |
604 | ||
605 | dataLen = CFDataGetLength(certData); | |
606 | if (dataLen == 0) | |
607 | goto end; | |
608 | ||
609 | cert = vmalloc(dataLen); | |
610 | if (cert == NULL) | |
611 | goto end; | |
612 | ||
613 | CFDataGetBytes(certData, CFRangeMake(0, dataLen), cert->v); | |
614 | ||
fce29cd9 A |
615 | // verify expiry or missing fields |
616 | if (certStatus) { | |
e8d9021d | 617 | *certStatus = crypto_cssm_check_x509cert_dates(certificateRef); |
fce29cd9 A |
618 | } |
619 | ||
d1e348cf | 620 | #endif |
52b7d2ce A |
621 | |
622 | end: | |
623 | if (certificateRef) | |
624 | CFRelease(certificateRef); | |
625 | if (identityRef) | |
626 | CFRelease(identityRef); | |
d1e348cf | 627 | #if !TARGET_OS_EMBEDDED |
52b7d2ce A |
628 | if (idSearchRef) |
629 | CFRelease(idSearchRef); | |
630 | if (keychainRef) | |
631 | CFRelease(keychainRef); | |
d1e348cf A |
632 | #else |
633 | if (persistFind) | |
634 | CFRelease(persistFind); | |
635 | if (certData) | |
636 | CFRelease(certData); | |
637 | #endif | |
638 | ||
52b7d2ce A |
639 | if (status != noErr && status != -1) { |
640 | plog(LLV_ERROR, LOCATION, NULL, | |
641 | "error %d %s.\n", status, GetSecurityErrorString(status)); | |
642 | status = -1; | |
643 | } | |
644 | return cert; | |
645 | ||
646 | } | |
647 | ||
d1e348cf | 648 | #if !TARGET_OS_EMBEDDED |
52b7d2ce A |
649 | /* |
650 | * Find a policy ref by OID | |
651 | */ | |
652 | static OSStatus FindPolicy(const CSSM_OID *policyOID, SecPolicyRef *policyRef) | |
653 | { | |
654 | ||
655 | OSStatus status; | |
656 | SecPolicySearchRef searchRef = nil; | |
657 | ||
658 | status = SecPolicySearchCreate(CSSM_CERT_X_509v3, policyOID, NULL, &searchRef); | |
659 | if (status != noErr) | |
660 | goto end; | |
661 | ||
662 | status = SecPolicySearchCopyNext(searchRef, policyRef); | |
663 | ||
664 | end: | |
665 | if (searchRef) | |
666 | CFRelease(searchRef); | |
667 | ||
668 | if (status != noErr) { | |
669 | plog(LLV_ERROR, LOCATION, NULL, | |
670 | "error %d %s.\n", status, GetSecurityErrorString(status)); | |
671 | status = -1; | |
672 | } | |
673 | return status; | |
674 | } | |
d1e348cf | 675 | #endif |
52b7d2ce A |
676 | |
677 | /* | |
678 | * Evaluate the trust of a cert using the policy provided | |
679 | */ | |
e8d9021d A |
680 | #if TARGET_OS_EMBEDDED |
681 | static OSStatus EvaluateCert(SecCertificateRef evalCertArray[], CFIndex evalCertArrayNumValues, CFTypeRef policyRef, SecKeyRef *publicKeyRef) | |
682 | #else | |
683 | static OSStatus EvaluateCert(SecCertificateRef evalCertArray[], CFIndex evalCertArrayNumValues, CFTypeRef policyRef) | |
684 | ||
685 | #endif | |
52b7d2ce A |
686 | { |
687 | OSStatus status; | |
688 | SecTrustRef trustRef = 0; | |
689 | SecTrustResultType evalResult; | |
fce29cd9 A |
690 | |
691 | #if TARGET_OS_EMBEDDED | |
692 | CFArrayRef errorStrings; | |
693 | #else | |
694 | CSSM_TP_APPLE_EVIDENCE_INFO *statusChain; | |
695 | CFArrayRef certChain; | |
696 | #endif | |
52b7d2ce | 697 | |
e8d9021d | 698 | CFArrayRef cfCertRef = CFArrayCreate((CFAllocatorRef) NULL, (void*)evalCertArray, evalCertArrayNumValues, |
d1e348cf | 699 | &kCFTypeArrayCallBacks); |
52b7d2ce A |
700 | |
701 | if (!cfCertRef) { | |
702 | plog(LLV_ERROR, LOCATION, NULL, | |
703 | "unable to create CFArray.\n"); | |
704 | return -1; | |
705 | } | |
706 | ||
707 | status = SecTrustCreateWithCertificates(cfCertRef, policyRef, &trustRef); | |
708 | if (status != noErr) | |
709 | goto end; | |
fce29cd9 | 710 | |
52b7d2ce A |
711 | status = SecTrustEvaluate(trustRef, &evalResult); |
712 | if (status != noErr) | |
713 | goto end; | |
714 | ||
715 | if (evalResult != kSecTrustResultProceed && evalResult != kSecTrustResultUnspecified) { | |
fce29cd9 A |
716 | plog(LLV_ERROR, LOCATION, NULL, "Error evaluating certificate.\n"); |
717 | ||
718 | switch (evalResult) { | |
719 | case kSecTrustResultInvalid: | |
720 | plog(LLV_DEBUG, LOCATION, NULL, "eval result = kSecTrustResultInvalid.\n"); | |
721 | break; | |
722 | case kSecTrustResultProceed: | |
723 | plog(LLV_DEBUG, LOCATION, NULL, "eval result = kSecTrustResultProceed.\n"); | |
724 | break; | |
725 | case kSecTrustResultConfirm: | |
726 | plog(LLV_DEBUG, LOCATION, NULL, "eval result = kSecTrustResultConfirm.\n"); | |
727 | break; | |
728 | case kSecTrustResultDeny: | |
729 | plog(LLV_DEBUG, LOCATION, NULL, "eval result = kSecTrustResultDeny.\n"); | |
730 | break; | |
731 | case kSecTrustResultUnspecified: | |
732 | plog(LLV_DEBUG, LOCATION, NULL, "eval result = kSecTrustResultUnspecified.\n"); | |
733 | break; | |
734 | case kSecTrustResultRecoverableTrustFailure: | |
735 | plog(LLV_DEBUG, LOCATION, NULL, "eval result = kSecTrustResultRecoverableTrustFailure.\n"); | |
736 | break; | |
737 | case kSecTrustResultFatalTrustFailure: | |
738 | plog(LLV_DEBUG, LOCATION, NULL, "eval result = kSecTrustResultFatalTrustFailure.\n"); | |
739 | break; | |
740 | case kSecTrustResultOtherError: | |
741 | plog(LLV_DEBUG, LOCATION, NULL, "eval result = kSecTrustResultOtherError.\n"); | |
742 | break; | |
743 | default: | |
744 | plog(LLV_DEBUG, LOCATION, NULL, "eval result unknown: value = %d.\n", (int)evalResult); | |
745 | break; | |
746 | } | |
747 | ||
748 | ||
749 | #if TARGET_OS_EMBEDDED | |
750 | errorStrings = SecTrustCopyProperties(trustRef); | |
751 | if (errorStrings) { | |
752 | ||
753 | CFDictionaryRef dict; | |
754 | CFStringRef val; | |
755 | const char *str; | |
756 | CFIndex count, maxcount = CFArrayGetCount(errorStrings); | |
757 | ||
758 | plog(LLV_ERROR, LOCATION, NULL, "---------------Returned error strings: ---------------.\n"); | |
759 | for (count = 0; count < maxcount; count++) { | |
760 | dict = CFArrayGetValueAtIndex(errorStrings, count); | |
761 | if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) { | |
762 | val = CFDictionaryGetValue(dict, kSecPropertyKeyType); | |
763 | if (val && (CFGetTypeID(val) == CFStringGetTypeID())) { | |
764 | str = CFStringGetCStringPtr(val, kCFStringEncodingMacRoman); | |
765 | if (str) | |
766 | plog(LLV_ERROR, LOCATION, NULL, "type = %s.\n", str); | |
767 | } | |
768 | val = CFDictionaryGetValue(dict, kSecPropertyKeyValue); | |
769 | if (val && (CFGetTypeID(val) == CFStringGetTypeID())) { | |
770 | str = CFStringGetCStringPtr(val, kCFStringEncodingMacRoman); | |
771 | if (str) | |
772 | plog(LLV_ERROR, LOCATION, NULL, "value = %s.\n", str); | |
773 | } | |
774 | } | |
775 | } | |
776 | plog(LLV_ERROR, LOCATION, NULL, "-----------------------------------------------------.\n"); | |
777 | CFRelease(errorStrings); | |
778 | } | |
779 | ||
780 | #else | |
781 | SecTrustGetResult(trustRef, &evalResult, &certChain, &statusChain); | |
782 | plog(LLV_ERROR, LOCATION, NULL, "Cert status bits = 0x%x.\n", statusChain->StatusBits); | |
783 | plog(LLV_ERROR, LOCATION, NULL, "Cert status NumStatusCodes = 0x%x.\n", statusChain->NumStatusCodes); | |
784 | { | |
785 | int i; | |
786 | for (i = 0; i < statusChain->NumStatusCodes; i++) | |
787 | plog(LLV_ERROR, LOCATION, NULL, "Cert status code i = 0x%x %d.\n", *(statusChain->StatusCodes + i), *(statusChain->StatusCodes + i)); | |
788 | } | |
789 | plog(LLV_ERROR, LOCATION, NULL, "Cert status Index = %d.\n", statusChain->Index); | |
790 | CFRelease(certChain); | |
791 | #endif | |
792 | ||
52b7d2ce | 793 | status = -1; |
e8d9021d | 794 | goto end; |
52b7d2ce A |
795 | } |
796 | ||
797 | ||
e8d9021d A |
798 | #if TARGET_OS_EMBEDDED |
799 | /* get and return the public key */ | |
800 | *publicKeyRef = SecTrustCopyPublicKey(trustRef); | |
801 | #endif | |
802 | ||
52b7d2ce A |
803 | end: |
804 | if (cfCertRef) | |
805 | CFRelease(cfCertRef); | |
806 | if (trustRef) | |
807 | CFRelease(trustRef); | |
808 | ||
809 | if (status != noErr && status != -1) { | |
810 | plog(LLV_ERROR, LOCATION, NULL, | |
811 | "error %d %s.\n", status, GetSecurityErrorString(status)); | |
812 | status = -1; | |
813 | } | |
814 | return status; | |
815 | } | |
816 | ||
d1e348cf | 817 | #if !TARGET_OS_EMBEDDED |
52b7d2ce A |
818 | /* |
819 | * Copy the system keychain | |
820 | */ | |
821 | static OSStatus CopySystemKeychain(SecKeychainRef *keychainRef) | |
822 | { | |
823 | ||
824 | OSStatus status; | |
825 | ||
826 | status = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); | |
827 | if (status != noErr) | |
828 | goto end; | |
829 | ||
830 | status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, keychainRef); | |
831 | ||
832 | end: | |
833 | ||
834 | if (status != noErr) { | |
835 | plog(LLV_ERROR, LOCATION, NULL, | |
836 | "error %d %s.\n", status, GetSecurityErrorString(status)); | |
837 | status = -1; | |
838 | } | |
839 | return status; | |
840 | ||
841 | } | |
d1e348cf | 842 | #endif |
52b7d2ce A |
843 | |
844 | /* | |
845 | * Return string representation of Security-related OSStatus. | |
846 | */ | |
847 | const char * | |
848 | GetSecurityErrorString(OSStatus err) | |
849 | { | |
850 | switch(err) { | |
851 | case noErr: | |
852 | return "noErr"; | |
853 | case memFullErr: | |
854 | return "memFullErr"; | |
855 | case paramErr: | |
856 | return "paramErr"; | |
857 | case unimpErr: | |
858 | return "unimpErr"; | |
859 | ||
860 | /* SecBase.h: */ | |
861 | case errSecNotAvailable: | |
862 | return "errSecNotAvailable"; | |
d1e348cf | 863 | #if !TARGET_OS_EMBEDDED |
52b7d2ce A |
864 | case errSecReadOnly: |
865 | return "errSecReadOnly"; | |
866 | case errSecAuthFailed: | |
867 | return "errSecAuthFailed"; | |
868 | case errSecNoSuchKeychain: | |
869 | return "errSecNoSuchKeychain"; | |
870 | case errSecInvalidKeychain: | |
871 | return "errSecInvalidKeychain"; | |
872 | case errSecDuplicateKeychain: | |
873 | return "errSecDuplicateKeychain"; | |
874 | case errSecDuplicateCallback: | |
875 | return "errSecDuplicateCallback"; | |
876 | case errSecInvalidCallback: | |
877 | return "errSecInvalidCallback"; | |
52b7d2ce A |
878 | case errSecBufferTooSmall: |
879 | return "errSecBufferTooSmall"; | |
880 | case errSecDataTooLarge: | |
881 | return "errSecDataTooLarge"; | |
882 | case errSecNoSuchAttr: | |
883 | return "errSecNoSuchAttr"; | |
884 | case errSecInvalidItemRef: | |
885 | return "errSecInvalidItemRef"; | |
886 | case errSecInvalidSearchRef: | |
887 | return "errSecInvalidSearchRef"; | |
888 | case errSecNoSuchClass: | |
889 | return "errSecNoSuchClass"; | |
890 | case errSecNoDefaultKeychain: | |
891 | return "errSecNoDefaultKeychain"; | |
892 | case errSecInteractionNotAllowed: | |
893 | return "errSecInteractionNotAllowed"; | |
894 | case errSecReadOnlyAttr: | |
895 | return "errSecReadOnlyAttr"; | |
896 | case errSecWrongSecVersion: | |
897 | return "errSecWrongSecVersion"; | |
898 | case errSecKeySizeNotAllowed: | |
899 | return "errSecKeySizeNotAllowed"; | |
900 | case errSecNoStorageModule: | |
901 | return "errSecNoStorageModule"; | |
902 | case errSecNoCertificateModule: | |
903 | return "errSecNoCertificateModule"; | |
904 | case errSecNoPolicyModule: | |
905 | return "errSecNoPolicyModule"; | |
906 | case errSecInteractionRequired: | |
907 | return "errSecInteractionRequired"; | |
908 | case errSecDataNotAvailable: | |
909 | return "errSecDataNotAvailable"; | |
910 | case errSecDataNotModifiable: | |
911 | return "errSecDataNotModifiable"; | |
912 | case errSecCreateChainFailed: | |
913 | return "errSecCreateChainFailed"; | |
914 | case errSecACLNotSimple: | |
915 | return "errSecACLNotSimple"; | |
916 | case errSecPolicyNotFound: | |
917 | return "errSecPolicyNotFound"; | |
918 | case errSecInvalidTrustSetting: | |
919 | return "errSecInvalidTrustSetting"; | |
920 | case errSecNoAccessForItem: | |
921 | return "errSecNoAccessForItem"; | |
922 | case errSecInvalidOwnerEdit: | |
923 | return "errSecInvalidOwnerEdit"; | |
d1e348cf A |
924 | #endif |
925 | case errSecDuplicateItem: | |
926 | return "errSecDuplicateItem"; | |
927 | case errSecItemNotFound: | |
928 | return "errSecItemNotFound"; | |
52b7d2ce A |
929 | default: |
930 | return "<unknown>"; | |
931 | } | |
932 | } | |
933 |