]> git.saurik.com Git - apple/ipsec.git/blob - ipsec-tools/racoon/crypto_cssm.c
e5b35d6bdebb914f89e17296223461a7453d05f7
[apple/ipsec.git] / ipsec-tools / racoon / crypto_cssm.c
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
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
36 #include <TargetConditionals.h>
37 #if TARGET_OS_EMBEDDED
38 #include <Security/SecItem.h>
39 #else
40 #include <Security/SecBase.h>
41 #include <Security/SecIdentityPriv.h>
42 #include <Security/SecIdentitySearch.h>
43 #include <Security/SecKeychain.h>
44 #include <Security/SecKeychainItem.h>
45 #include <Security/SecKeychainItemPriv.h>
46
47 #include <Security/SecKeyPriv.h>
48 #include <Security/oidsalg.h>
49 #include <Security/cssmapi.h>
50 #include <Security/SecPolicySearch.h>
51 #endif
52
53 #include <CoreFoundation/CoreFoundation.h>
54 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
55 #include "plog.h"
56 #include "debug.h"
57 #include "misc.h"
58
59
60 #include "crypto_cssm.h"
61
62
63 static OSStatus EvaluateCert(SecCertificateRef cert, CFTypeRef policyRef);
64 static const char *GetSecurityErrorString(OSStatus err);
65 #if !TARGET_OS_EMBEDDED
66 static OSStatus FindPolicy(const CSSM_OID *policyOID, SecPolicyRef *policyRef);
67 static OSStatus CopySystemKeychain(SecKeychainRef *keychainRef);
68 #endif
69
70 /*
71 * Verify cert using security framework
72 */
73 int crypto_cssm_check_x509cert(vchar_t *cert, CFStringRef hostname)
74 {
75 OSStatus status;
76 SecCertificateRef certRef = NULL;
77 SecPolicyRef policyRef = NULL;
78
79 #if !TARGET_OS_EMBEDDED
80 CSSM_DATA certData;
81 CSSM_OID ourPolicyOID = CSSMOID_APPLE_TP_IP_SEC;
82
83 // create cert ref
84 certData.Length = cert->l;
85 certData.Data = (uint8 *)cert->v;
86 status = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
87 &certRef);
88 if (status != noErr)
89 goto end;
90
91 // get our policy object
92 status = FindPolicy(&ourPolicyOID, &policyRef);
93 if (status != noErr)
94 goto end;
95 // no options used at present - verification of subjectAltName fields, etc.
96 // are done elsewhere in racoon in oakley_check_certid()
97
98 #else
99 CFDataRef cert_data = CFDataCreateWithBytesNoCopy(NULL, cert->v, cert->l, kCFAllocatorNull);
100 if (cert_data) {
101 certRef = SecCertificateCreateWithData(NULL, cert_data);
102 CFRelease(cert_data);
103 }
104
105 if (certRef == NULL) {
106 plog(LLV_ERROR, LOCATION, NULL,
107 "unable to create a certRef.\n");
108 status = -1;
109 goto end;
110 }
111
112 if (hostname) {
113 policyRef = SecPolicyCreateSSL(FALSE, hostname);
114 if (policyRef == NULL) {
115 plog(LLV_ERROR, LOCATION, NULL,
116 "unable to create a SSL policyRef.\n");
117 status = -1;
118 goto end;
119 }
120 } else
121 {
122 policyRef = SecPolicyCreateBasicX509();
123 if (policyRef == NULL) {
124 plog(LLV_ERROR, LOCATION, NULL,
125 "unable to create a Basic X509 policyRef.\n");
126 status = -1;
127 goto end;
128 }
129 }
130
131 #endif
132
133 // evaluate cert
134 status = EvaluateCert(certRef, policyRef);
135
136 end:
137
138 if (certRef)
139 CFRelease(certRef);
140 if (policyRef)
141 CFRelease(policyRef);
142
143 if (status != noErr && status != -1) {
144 plog(LLV_ERROR, LOCATION, NULL,
145 "error %d %s.\n", status, GetSecurityErrorString(status));
146 status = -1;
147 }
148 return status;
149
150 }
151
152 /*
153 * Encrypt a hash via CSSM using the private key in the keychain
154 * from an identity.
155 */
156 vchar_t* crypto_cssm_getsign(CFDataRef persistentCertRef, vchar_t* hash)
157 {
158
159 OSStatus status;
160 SecIdentityRef identityRef = NULL;
161 SecKeyRef privateKeyRef = NULL;
162 vchar_t *sig = NULL;
163
164 #if !TARGET_OS_EMBEDDED
165 u_int32_t bytesEncrypted = 0;
166 SecCertificateRef certificateRef = NULL;
167 SecIdentitySearchRef idSearchRef = NULL;
168 SecKeychainRef keychainRef = NULL;
169 const CSSM_KEY *cssmKey = NULL;
170 CSSM_CSP_HANDLE cspHandle = nil;
171 CSSM_CC_HANDLE cssmContextHandle = nil;
172 const CSSM_ACCESS_CREDENTIALS *credentials = NULL;
173 //CSSM_SIZE bytesEncrypted = 0; //%%%%HWR fix this - need new headers on Leopard
174 CSSM_DATA clearData;
175 CSSM_DATA cipherData;
176 CSSM_DATA remData;
177 CSSM_CONTEXT_ATTRIBUTE newAttr;
178
179 remData.Length = 0;
180 remData.Data = 0;
181
182 if (persistentCertRef) {
183 // get cert from keychain
184 status = SecKeychainItemCopyFromPersistentReference(persistentCertRef, (SecKeychainItemRef*)&certificateRef);
185 if (status != noErr)
186 goto end;
187
188 // get keychain ref where cert is contained
189 status = SecKeychainItemCopyKeychain((SecKeychainItemRef)certificateRef, &keychainRef);
190 if (status != noErr)
191 goto end;
192
193 // get identity from the certificate
194 status = SecIdentityCreateWithCertificate(keychainRef, certificateRef, &identityRef);
195 if (status != noErr)
196 goto end;
197
198 } else {
199
200 // copy system keychain
201 status = CopySystemKeychain(&keychainRef);
202 if (status != noErr)
203 goto end;
204
205 // serach for first identity in system keychain
206 status = SecIdentitySearchCreate(keychainRef, CSSM_KEYUSE_SIGN, &idSearchRef);
207 if (status != noErr)
208 goto end;
209
210 status = SecIdentitySearchCopyNext(idSearchRef, &identityRef);
211 if (status != noErr)
212 goto end;
213
214 // get certificate from identity
215 status = SecIdentityCopyCertificate(identityRef, &certificateRef);
216 if (status != noErr)
217 goto end;
218 }
219
220 // get private key from identity
221 status = SecIdentityCopyPrivateKey(identityRef, &privateKeyRef);
222 if (status != noErr)
223 goto end;
224
225 // get CSSM_KEY pointer from key ref
226 status = SecKeyGetCSSMKey(privateKeyRef, &cssmKey);
227 if (status != noErr)
228 goto end;
229
230 // get CSSM CSP handle
231 status = SecKeychainGetCSPHandle(keychainRef, &cspHandle);
232 if (status != noErr)
233 goto end;
234
235 // create CSSM credentials to unlock private key for encryption - no UI to be used
236 status = SecKeyGetCredentials(privateKeyRef, CSSM_ACL_AUTHORIZATION_ENCRYPT,
237 kSecCredentialTypeNoUI, &credentials);
238 if (status != noErr)
239 goto end;
240
241 // create asymmetric context for encryption
242 status = CSSM_CSP_CreateAsymmetricContext(cspHandle, CSSM_ALGID_RSA, credentials, cssmKey,
243 CSSM_PADDING_PKCS1, &cssmContextHandle);
244 if (status != noErr)
245 goto end;
246
247 // add mode attribute to use private key for encryption
248 newAttr.AttributeType = CSSM_ATTRIBUTE_MODE;
249 newAttr.AttributeLength = sizeof(uint32);
250 newAttr.Attribute.Data = (CSSM_DATA_PTR)CSSM_ALGMODE_PRIVATE_KEY;
251 status = CSSM_UpdateContextAttributes(cssmContextHandle, 1, &newAttr);
252 if(status != noErr)
253 goto end;
254
255 // and finally - encrypt data
256 clearData.Length = hash->l;
257 clearData.Data = (uint8 *)hash->v;
258 cipherData.Length = 0;
259 cipherData.Data = NULL;
260 status = CSSM_EncryptData(cssmContextHandle, &clearData, 1, &cipherData, 1, &bytesEncrypted,
261 &remData);
262 if (status != noErr)
263 goto end;
264
265 if (remData.Length != 0) { // something didn't go right - should be zero
266 status = -1;
267 plog(LLV_ERROR, LOCATION, NULL,
268 "unencrypted data remaining after encrypting hash.\n");
269 goto end;
270 }
271
272 // alloc buffer for result
273 sig = vmalloc(0);
274 if (sig == NULL)
275 goto end;
276
277 sig->l = cipherData.Length;
278 sig->v = (caddr_t)cipherData.Data;
279
280 #else
281
282 CFDictionaryRef persistFind = NULL;
283 const void *keys_persist[] = { kSecReturnRef, kSecValuePersistentRef };
284 const void *values_persist[] = { kCFBooleanTrue, persistentCertRef };
285
286 #define SIG_BUF_SIZE 1024
287
288 /* find identity by persistent ref */
289 persistFind = CFDictionaryCreate(NULL, keys_persist, values_persist,
290 (sizeof(keys_persist) / sizeof(*keys_persist)), NULL, NULL);
291 if (persistFind == NULL)
292 goto end;
293
294 status = SecItemCopyMatching(persistFind, (CFTypeRef *)&identityRef);
295 if (status != noErr)
296 goto end;
297
298 status = SecIdentityCopyPrivateKey(identityRef, &privateKeyRef);
299 if (status != noErr)
300 goto end;
301
302 // alloc buffer for result
303 sig = vmalloc(SIG_BUF_SIZE);
304 if (sig == NULL)
305 goto end;
306
307 status = SecKeyRawSign(privateKeyRef, kSecPaddingPKCS1, hash->v,
308 hash->l, sig->v, &sig->l);
309
310 #endif
311
312
313 end:
314 if (identityRef)
315 CFRelease(identityRef);
316 if (privateKeyRef)
317 CFRelease(privateKeyRef);
318
319 #if !TARGET_OS_EMBEDDED
320 if (certificateRef)
321 CFRelease(certificateRef);
322 if (keychainRef)
323 CFRelease(keychainRef);
324 if (idSearchRef)
325 CFRelease(idSearchRef);
326 if (cssmContextHandle)
327 CSSM_DeleteContext(cssmContextHandle);
328 #else
329 if (persistFind)
330 CFRelease(persistFind);
331 #endif
332
333 if (status != noErr) {
334 if (sig) {
335 vfree(sig);
336 sig = NULL;
337 }
338 }
339
340 if (status != noErr && status != -1) {
341 plog(LLV_ERROR, LOCATION, NULL,
342 "error %d %s.\n", status, GetSecurityErrorString(status));
343 status = -1;
344 }
345 return sig;
346
347 }
348
349
350 /*
351 * Retrieve a cert from the keychain
352 */
353 vchar_t* crypto_cssm_get_x509cert(CFDataRef persistentCertRef)
354 {
355
356 OSStatus status;
357 vchar_t *cert = NULL;
358 SecIdentityRef identityRef = NULL;
359 SecCertificateRef certificateRef = NULL;
360
361 #if !TARGET_OS_EMBEDDED
362 CSSM_DATA cssmData;
363 SecIdentitySearchRef idSearchRef = NULL;
364 SecKeychainRef keychainRef = NULL;
365
366 // get cert ref
367 if (persistentCertRef) {
368 status = SecKeychainItemCopyFromPersistentReference(persistentCertRef, (SecKeychainItemRef*)&certificateRef);
369 if (status != noErr)
370 goto end;
371 } else {
372 // copy system keychain
373 status = CopySystemKeychain(&keychainRef);
374 if (status != noErr)
375 goto end;
376
377 // find first identity in system keychain
378 status = SecIdentitySearchCreate(keychainRef, CSSM_KEYUSE_SIGN, &idSearchRef);
379 if (status != noErr)
380 goto end;
381
382 status = SecIdentitySearchCopyNext(idSearchRef, &identityRef);
383 if (status != noErr)
384 goto end;
385
386 // get certificate from identity
387 status = SecIdentityCopyCertificate(identityRef, &certificateRef);
388 if (status != noErr)
389 goto end;
390
391 }
392
393 // get certificate data
394 cssmData.Length = 0;
395 cssmData.Data = NULL;
396 status = SecCertificateGetData(certificateRef, &cssmData);
397 if (status != noErr)
398 goto end;
399
400 if (cssmData.Length == 0)
401 goto end;
402
403 cert = vmalloc(cssmData.Length);
404 if (cert == NULL)
405 goto end;
406
407 // cssmData struct just points to the data
408 // data must be copied to be returned
409 memcpy(cert->v, cssmData.Data, cssmData.Length);
410
411 #else
412
413 CFDictionaryRef persistFind = NULL;
414 const void *keys_persist[] = { kSecReturnRef, kSecValuePersistentRef };
415 const void *values_persist[] = { kCFBooleanTrue, persistentCertRef };
416 size_t dataLen;
417 CFDataRef certData = NULL;
418
419 /* find identity by persistent ref */
420 persistFind = CFDictionaryCreate(NULL, keys_persist, values_persist,
421 (sizeof(keys_persist) / sizeof(*keys_persist)), NULL, NULL);
422 if (persistFind == NULL)
423 goto end;
424
425 status = SecItemCopyMatching(persistFind, (CFTypeRef *)&identityRef);
426 if (status != noErr)
427 goto end;
428
429 status = SecIdentityCopyCertificate(identityRef, &certificateRef);
430 if (status != noErr)
431 goto end;
432
433 certData = SecCertificateCopyData(certificateRef);
434 if (certData == NULL)
435 goto end;
436
437 dataLen = CFDataGetLength(certData);
438 if (dataLen == 0)
439 goto end;
440
441 cert = vmalloc(dataLen);
442 if (cert == NULL)
443 goto end;
444
445 CFDataGetBytes(certData, CFRangeMake(0, dataLen), cert->v);
446
447 #endif
448
449 end:
450 if (certificateRef)
451 CFRelease(certificateRef);
452 if (identityRef)
453 CFRelease(identityRef);
454 #if !TARGET_OS_EMBEDDED
455 if (idSearchRef)
456 CFRelease(idSearchRef);
457 if (keychainRef)
458 CFRelease(keychainRef);
459 #else
460 if (persistFind)
461 CFRelease(persistFind);
462 if (certData)
463 CFRelease(certData);
464 #endif
465
466 if (status != noErr && status != -1) {
467 plog(LLV_ERROR, LOCATION, NULL,
468 "error %d %s.\n", status, GetSecurityErrorString(status));
469 status = -1;
470 }
471 return cert;
472
473 }
474
475 #if !TARGET_OS_EMBEDDED
476 /*
477 * Find a policy ref by OID
478 */
479 static OSStatus FindPolicy(const CSSM_OID *policyOID, SecPolicyRef *policyRef)
480 {
481
482 OSStatus status;
483 SecPolicySearchRef searchRef = nil;
484
485 status = SecPolicySearchCreate(CSSM_CERT_X_509v3, policyOID, NULL, &searchRef);
486 if (status != noErr)
487 goto end;
488
489 status = SecPolicySearchCopyNext(searchRef, policyRef);
490
491 end:
492 if (searchRef)
493 CFRelease(searchRef);
494
495 if (status != noErr) {
496 plog(LLV_ERROR, LOCATION, NULL,
497 "error %d %s.\n", status, GetSecurityErrorString(status));
498 status = -1;
499 }
500 return status;
501 }
502 #endif
503
504 /*
505 * Evaluate the trust of a cert using the policy provided
506 */
507 static OSStatus EvaluateCert(SecCertificateRef cert, CFTypeRef policyRef)
508 {
509 OSStatus status;
510 SecTrustRef trustRef = 0;
511 SecTrustResultType evalResult;
512
513 SecCertificateRef evalCertArray[1] = { cert };
514
515 CFArrayRef cfCertRef = CFArrayCreate((CFAllocatorRef) NULL, (void*)evalCertArray, 1,
516 &kCFTypeArrayCallBacks);
517
518 if (!cfCertRef) {
519 plog(LLV_ERROR, LOCATION, NULL,
520 "unable to create CFArray.\n");
521 return -1;
522 }
523
524 status = SecTrustCreateWithCertificates(cfCertRef, policyRef, &trustRef);
525 if (status != noErr)
526 goto end;
527
528 status = SecTrustEvaluate(trustRef, &evalResult);
529 if (status != noErr)
530 goto end;
531
532 if (evalResult != kSecTrustResultProceed && evalResult != kSecTrustResultUnspecified) {
533 plog(LLV_ERROR, LOCATION, NULL,
534 "error evaluating certificate.\n");
535 status = -1;
536 }
537
538
539 end:
540 if (cfCertRef)
541 CFRelease(cfCertRef);
542 if (trustRef)
543 CFRelease(trustRef);
544
545 if (status != noErr && status != -1) {
546 plog(LLV_ERROR, LOCATION, NULL,
547 "error %d %s.\n", status, GetSecurityErrorString(status));
548 status = -1;
549 }
550 return status;
551 }
552
553 #if !TARGET_OS_EMBEDDED
554 /*
555 * Copy the system keychain
556 */
557 static OSStatus CopySystemKeychain(SecKeychainRef *keychainRef)
558 {
559
560 OSStatus status;
561
562 status = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem);
563 if (status != noErr)
564 goto end;
565
566 status = SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, keychainRef);
567
568 end:
569
570 if (status != noErr) {
571 plog(LLV_ERROR, LOCATION, NULL,
572 "error %d %s.\n", status, GetSecurityErrorString(status));
573 status = -1;
574 }
575 return status;
576
577 }
578 #endif
579
580 /*
581 * Return string representation of Security-related OSStatus.
582 */
583 const char *
584 GetSecurityErrorString(OSStatus err)
585 {
586 switch(err) {
587 case noErr:
588 return "noErr";
589 case memFullErr:
590 return "memFullErr";
591 case paramErr:
592 return "paramErr";
593 case unimpErr:
594 return "unimpErr";
595
596 /* SecBase.h: */
597 case errSecNotAvailable:
598 return "errSecNotAvailable";
599 #if !TARGET_OS_EMBEDDED
600 case errSecReadOnly:
601 return "errSecReadOnly";
602 case errSecAuthFailed:
603 return "errSecAuthFailed";
604 case errSecNoSuchKeychain:
605 return "errSecNoSuchKeychain";
606 case errSecInvalidKeychain:
607 return "errSecInvalidKeychain";
608 case errSecDuplicateKeychain:
609 return "errSecDuplicateKeychain";
610 case errSecDuplicateCallback:
611 return "errSecDuplicateCallback";
612 case errSecInvalidCallback:
613 return "errSecInvalidCallback";
614 case errSecBufferTooSmall:
615 return "errSecBufferTooSmall";
616 case errSecDataTooLarge:
617 return "errSecDataTooLarge";
618 case errSecNoSuchAttr:
619 return "errSecNoSuchAttr";
620 case errSecInvalidItemRef:
621 return "errSecInvalidItemRef";
622 case errSecInvalidSearchRef:
623 return "errSecInvalidSearchRef";
624 case errSecNoSuchClass:
625 return "errSecNoSuchClass";
626 case errSecNoDefaultKeychain:
627 return "errSecNoDefaultKeychain";
628 case errSecInteractionNotAllowed:
629 return "errSecInteractionNotAllowed";
630 case errSecReadOnlyAttr:
631 return "errSecReadOnlyAttr";
632 case errSecWrongSecVersion:
633 return "errSecWrongSecVersion";
634 case errSecKeySizeNotAllowed:
635 return "errSecKeySizeNotAllowed";
636 case errSecNoStorageModule:
637 return "errSecNoStorageModule";
638 case errSecNoCertificateModule:
639 return "errSecNoCertificateModule";
640 case errSecNoPolicyModule:
641 return "errSecNoPolicyModule";
642 case errSecInteractionRequired:
643 return "errSecInteractionRequired";
644 case errSecDataNotAvailable:
645 return "errSecDataNotAvailable";
646 case errSecDataNotModifiable:
647 return "errSecDataNotModifiable";
648 case errSecCreateChainFailed:
649 return "errSecCreateChainFailed";
650 case errSecACLNotSimple:
651 return "errSecACLNotSimple";
652 case errSecPolicyNotFound:
653 return "errSecPolicyNotFound";
654 case errSecInvalidTrustSetting:
655 return "errSecInvalidTrustSetting";
656 case errSecNoAccessForItem:
657 return "errSecNoAccessForItem";
658 case errSecInvalidOwnerEdit:
659 return "errSecInvalidOwnerEdit";
660 #endif
661 case errSecDuplicateItem:
662 return "errSecDuplicateItem";
663 case errSecItemNotFound:
664 return "errSecItemNotFound";
665 default:
666 return "<unknown>";
667 }
668 }
669