]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_ssl/lib/sslCrypto.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_ssl / lib / sslCrypto.c
1 /*
2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * sslCrypto.c - interface between SSL and crypto libraries
26 */
27
28 #include "sslCrypto.h"
29 #include "sslContext.h"
30 #include "sslMemory.h"
31 #include "sslUtils.h"
32 #include "sslDebug.h"
33
34 #include <string.h>
35 #include <stdlib.h>
36 #include <assert.h>
37
38 #include <Security/SecTrustPriv.h>
39 #include <Security/SecPolicy.h>
40 #include <Security/SecCertificate.h>
41
42 #include <AssertMacros.h>
43 #include "utilities/SecCFRelease.h"
44
45 #if TARGET_OS_IPHONE
46 #include <Security/SecRSAKey.h>
47 #include <Security/SecECKey.h>
48 #endif
49
50 /*
51 * Get algorithm id for a SSLPubKey object.
52 */
53 CFIndex sslPubKeyGetAlgorithmID(SecKeyRef pubKey)
54 {
55 #if TARGET_OS_IPHONE
56 return SecKeyGetAlgorithmID(pubKey);
57 #else
58 return SecKeyGetAlgorithmId(pubKey);
59 #endif
60 }
61
62 /*
63 * Get algorithm id for a SSLPrivKey object.
64 */
65 CFIndex sslPrivKeyGetAlgorithmID(SecKeyRef privKey)
66 {
67 #if TARGET_OS_IPHONE
68 return SecKeyGetAlgorithmID(privKey);
69 #else
70 return SecKeyGetAlgorithmId(privKey);
71 #endif
72 }
73
74 static
75 OSStatus sslCreateCFArrayFromList(const tls_buffer_list_t *list, CFArrayRef *cfArray)
76 {
77 int err;
78 CFMutableArrayRef array = NULL;
79 CFDataRef data = NULL;
80
81 err = errSSLInternal;
82
83 array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
84 require(array, out);
85
86 while(list) {
87 require((data = CFDataCreate(kCFAllocatorDefault, list->buffer.data, list->buffer.length)), out);
88 CFArrayAppendValue(array, data);
89 CFReleaseNull(data);
90 list=list->next;
91 }
92
93 *cfArray = array;
94 return errSecSuccess;
95
96 out:
97 CFReleaseSafe(data);
98 CFReleaseSafe(array);
99 return err;
100 }
101
102 OSStatus
103 sslCreateSecTrust(
104 SSLContext *ctx,
105 CFArrayRef certChain,
106 SecTrustRef *pTrust) /* RETURNED */
107 {
108 OSStatus status = errSecAllocate;
109 CFStringRef peerDomainName = NULL;
110 CFTypeRef policies = NULL;
111 SecTrustRef trust = NULL;
112 const char *peerDomainNameData = NULL;
113 size_t peerDomainNameLen = 0;
114
115 if(ctx->protocolSide==kSSLClientSide) {
116 tls_handshake_get_peer_hostname(ctx->hdsk, &peerDomainNameData, &peerDomainNameLen);
117 }
118
119 if (CFArrayGetCount(certChain) == 0) {
120 status = errSSLBadCert;
121 goto errOut;
122 }
123
124 if (peerDomainNameLen && peerDomainNameData) {
125 CFIndex len = peerDomainNameLen;
126 if (peerDomainNameData[len - 1] == 0) {
127 len--;
128 //secwarning("peerDomainName is zero terminated!");
129 }
130 /* @@@ Double check that this is the correct encoding. */
131 require(peerDomainName = CFStringCreateWithBytes(kCFAllocatorDefault,
132 (const UInt8 *)peerDomainNameData, len,
133 kCFStringEncodingUTF8, false), errOut);
134 }
135
136 /* If we are the client, our peer certificates must satisfy the
137 ssl server policy. */
138 bool use_server_policy = (ctx->protocolSide == kSSLClientSide);
139 require(policies = SecPolicyCreateSSL(use_server_policy, peerDomainName), errOut);
140
141 require_noerr(status = SecTrustCreateWithCertificates(certChain, policies,
142 &trust), errOut);
143
144 /* If we are the client, let's see if we have OCSP responses and SCTs in the TLS handshake */
145 if(ctx->protocolSide == kSSLClientSide) {
146 const tls_buffer_list_t *sct_list = tls_handshake_get_peer_sct_list(ctx->hdsk);
147 const tls_buffer *ocsp_response = tls_handshake_get_peer_ocsp_response(ctx->hdsk);
148
149 if(ocsp_response) {
150 CFDataRef responseData = CFDataCreate(kCFAllocatorDefault, ocsp_response->data, ocsp_response->length);
151 status = SecTrustSetOCSPResponse(trust, responseData);
152 CFReleaseSafe(responseData);
153 require_noerr(status, errOut);
154 }
155
156 if(sct_list) {
157 CFArrayRef sctArray = NULL;
158 require_noerr(status = sslCreateCFArrayFromList(sct_list, &sctArray), errOut);
159 #if TARGET_OS_IPHONE
160 status = SecTrustSetSignedCertificateTimestamps(trust, sctArray);
161 #else
162 status = noErr;
163 #endif
164 CFReleaseSafe(sctArray);
165 require_noerr(status, errOut);
166 }
167 }
168
169 /* If we have trustedAnchors we set them here. */
170 if (ctx->trustedCerts) {
171 require_noerr(status = SecTrustSetAnchorCertificates(trust,
172 ctx->trustedCerts), errOut);
173 require_noerr(status = SecTrustSetAnchorCertificatesOnly(trust,
174 ctx->trustedCertsOnly), errOut);
175 }
176
177 status = errSecSuccess;
178
179 errOut:
180 CFReleaseSafe(peerDomainName);
181 CFReleaseSafe(policies);
182
183 *pTrust = trust;
184
185 return status;
186 }
187
188 /* Return the first certificate reference from the supplied array
189 * whose data matches the given certificate, or NULL if none match.
190 */
191 static
192 SecCertificateRef
193 sslGetMatchingCertInArray(
194 SecCertificateRef certRef,
195 CFArrayRef certArray)
196 {
197 SecCertificateRef matchedCert = NULL;
198
199 if (certRef == NULL || certArray == NULL) {
200 return NULL;
201 }
202
203 CFDataRef certData = SecCertificateCopyData(certRef);
204 if (certData) {
205 CFIndex idx, count = CFArrayGetCount(certArray);
206 for(idx=0; idx<count; idx++) {
207 SecCertificateRef aCert = (SecCertificateRef)CFArrayGetValueAtIndex(certArray, idx);
208 CFDataRef aData = SecCertificateCopyData(aCert);
209 if (aData && CFEqual(aData, certData)) {
210 matchedCert = aCert;
211 }
212 CFReleaseSafe(aData);
213 if (matchedCert)
214 break;
215 }
216 CFReleaseSafe(certData);
217 }
218
219 return matchedCert;
220 }
221
222 /*
223 * Verify a chain of DER-encoded certs.
224 */
225 static OSStatus sslVerifyCertChain(
226 SSLContext *ctx,
227 CFArrayRef certChain)
228 {
229 OSStatus status;
230 SecTrustRef trust = NULL;
231
232 /* renegotiate - start with a new SecTrustRef */
233 CFReleaseNull(ctx->peerSecTrust);
234
235 if(certChain==NULL) {
236 if(ctx->protocolSide == kSSLClientSide) {
237 /* No cert chain is always a trust failure on the server side */
238 status = errSSLXCertChainInvalid;
239 sslErrorLog("***Error: NULL server cert chain\n");
240 } else {
241 /* No cert chain on the client side is ok unless using kAlwaysAuthenticate */
242 if(ctx->clientAuth == kAlwaysAuthenticate) {
243 sslErrorLog("***Error: NULL client cert chain\n");
244 status = errSSLXCertChainInvalid;
245 } else {
246 status = noErr;
247 }
248 }
249 goto errOut;
250 }
251
252 status = sslCreateSecTrust(ctx, certChain, &trust);
253
254 if (!ctx->enableCertVerify) {
255 /* trivial case, this is caller's responsibility */
256 status = errSecSuccess;
257 goto errOut;
258 }
259
260 SecTrustResultType secTrustResult;
261 require_noerr(status = SecTrustEvaluate(trust, &secTrustResult), errOut);
262 switch (secTrustResult) {
263 case kSecTrustResultUnspecified:
264 /* cert chain valid, no special UserTrust assignments */
265 case kSecTrustResultProceed:
266 /* cert chain valid AND user explicitly trusts this */
267 status = errSecSuccess;
268 break;
269 case kSecTrustResultDeny:
270 case kSecTrustResultConfirm:
271 case kSecTrustResultRecoverableTrustFailure:
272 default:
273 if(ctx->allowAnyRoot) {
274 sslErrorLog("***Warning: accepting unverified cert chain\n");
275 status = errSecSuccess;
276 }
277 else {
278 /*
279 * If the caller provided a list of trusted leaf certs, check them here
280 */
281 if(ctx->trustedLeafCerts) {
282 if (sslGetMatchingCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(certChain, 0),
283 ctx->trustedLeafCerts)) {
284 status = errSecSuccess;
285 goto errOut;
286 }
287 }
288 status = errSSLXCertChainInvalid;
289 }
290 /* Do we really need to return things like:
291 errSSLNoRootCert
292 errSSLUnknownRootCert
293 errSSLCertExpired
294 errSSLCertNotYetValid
295 errSSLHostNameMismatch
296 for our client to see what went wrong, or should we just always
297 return
298 errSSLXCertChainInvalid
299 when something is wrong? */
300 break;
301 }
302
303 errOut:
304 ctx->peerSecTrust = trust;
305
306 return status;
307 }
308
309 /* Extract public SecKeyRef from Certificate Chain */
310 static
311 int sslCopyPeerPubKey(const SSLCertificate *certchain,
312 SecKeyRef *pubKey)
313 {
314 int err;
315 check(pubKey);
316 SecTrustRef trust = NULL;
317 const SSLCertificate *cert;
318 CFMutableArrayRef certArray = NULL;
319 CFDataRef certData = NULL;
320 SecCertificateRef cfCert = NULL;
321
322 err = errSSLInternal;
323
324 certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
325 cert = certchain;
326 while(cert) {
327 require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out);
328 require_action((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out, err=errSSLBadCert);
329 CFArrayAppendValue(certArray, cfCert);
330 CFReleaseNull(cfCert);
331 CFReleaseNull(certData);
332 cert=cert->next;
333 }
334
335 require_noerr((err=SecTrustCreateWithCertificates(certArray, NULL, &trust)), out);
336 SecKeyRef key = SecTrustCopyPublicKey(trust);
337 require_action(key, out, err=errSSLBadCert);
338
339 *pubKey = key;
340
341 err = errSecSuccess;
342
343 out:
344 CFReleaseSafe(certData);
345 CFReleaseSafe(cfCert);
346 CFReleaseSafe(trust);
347 CFReleaseSafe(certArray);
348
349 return err;
350 }
351
352 /* Extract the pubkey from a cert chain, and send it to the tls_handshake context */
353 int tls_set_peer_pubkey(SSLContext *ctx)
354 {
355 int err;
356 CFIndex algId;
357 SecKeyRef pubkey = NULL;
358 CFDataRef modulus = NULL;
359 CFDataRef exponent = NULL;
360 CFDataRef ecpubdata = NULL;
361 const SSLCertificate *certchain = NULL;
362
363 certchain = tls_handshake_get_peer_certificates(ctx->hdsk);
364 CFReleaseNull(ctx->peerCert);
365
366 /* If there is no certchain, then we don't need to set the pubkey in coreTLS */
367 /* We should really set it to "NULL" or none, but we need to fix the coreTLS API */
368 /* See: <rdar://problem/19723662> coreTLS: replace tls_handshake_set_peer_rsa_public_key and tls_handshake_set_peer_ec_public_key with a common function */
369 if(!certchain)
370 return 0;
371
372 ctx->peerCert = tls_get_peer_certs(certchain);
373
374 #if 0
375 { /* dump certs */
376 int i=0;
377 int j;
378 const SSLCertificate *tmp = certchain;
379 while(tmp) {
380 printf("cert%d[] = {", i);
381 for(j=0; j<tmp->derCert.length; j++) {
382 if((j&0xf)==0)
383 printf("\n");
384 printf("0x%02x, ", tmp->derCert.data[j]);
385 }
386 printf("}\n");
387 tmp=tmp->next;
388 i++;
389 }
390 }
391 #endif
392
393 require_noerr((err=sslCopyPeerPubKey(certchain, &pubkey)), errOut);
394
395 #if TARGET_OS_IPHONE
396 algId = SecKeyGetAlgorithmID(pubkey);
397 #else
398 algId = SecKeyGetAlgorithmId(pubkey);
399 #endif
400
401 err = errSSLCrypto;
402
403 switch(algId) {
404 case kSecRSAAlgorithmID:
405 {
406 require((modulus = SecKeyCopyModulus(pubkey)), errOut);
407 require((exponent = SecKeyCopyExponent(pubkey)), errOut);
408
409 tls_buffer mod;
410 tls_buffer exp;
411
412 mod.data = (uint8_t *)CFDataGetBytePtr(modulus);
413 mod.length = CFDataGetLength(modulus);
414
415 exp.data = (uint8_t *)CFDataGetBytePtr(exponent);
416 exp.length = CFDataGetLength(exponent);
417
418 err = tls_handshake_set_peer_rsa_public_key(ctx->hdsk, &mod, &exp);
419 break;
420 }
421 case kSecECDSAAlgorithmID:
422 {
423 tls_named_curve curve = SecECKeyGetNamedCurve(pubkey);
424 require((ecpubdata = SecECKeyCopyPublicBits(pubkey)), errOut);
425
426 tls_buffer pubdata;
427 pubdata.data = (uint8_t *)CFDataGetBytePtr(ecpubdata);
428 pubdata.length = CFDataGetLength(ecpubdata);
429
430 err = tls_handshake_set_peer_ec_public_key(ctx->hdsk, curve, &pubdata);
431
432 break;
433 }
434 default:
435 break;
436 }
437
438 errOut:
439 CFReleaseSafe(pubkey);
440 CFReleaseSafe(modulus);
441 CFReleaseSafe(exponent);
442 CFReleaseSafe(ecpubdata);
443
444 return err;
445 }
446
447 /* Convert cert in DER format into an CFArray of SecCertificateRef */
448 CFArrayRef
449 tls_get_peer_certs(const SSLCertificate *certs)
450 {
451 const SSLCertificate *cert;
452
453 CFMutableArrayRef certArray = NULL;
454 CFDataRef certData = NULL;
455 SecCertificateRef cfCert = NULL;
456
457 certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
458 require(certArray, out);
459 cert = certs;
460 while(cert) {
461 require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out);
462 require((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out);
463 CFArrayAppendValue(certArray, cfCert);
464 CFReleaseNull(cfCert);
465 CFReleaseNull(certData);
466 cert=cert->next;
467 }
468
469 return certArray;
470
471 out:
472 CFReleaseNull(cfCert);
473 CFReleaseNull(certData);
474 CFReleaseNull(certArray);
475 return NULL;
476 }
477
478 int
479 tls_verify_peer_cert(SSLContext *ctx)
480 {
481 int err = 0;
482 OSStatus st;
483
484 /* Note: A verification failure here does not cause the function to return an error.
485 This will allow the handshake to continue, coreTLS will eventually returns an error,
486 after sending the appropriate alert messages, based on the trust value set with the
487 call to tls_handshake_set_peer_trust(). In some case a verification failure here is
488 normal, for example if there is no cert (eg: PSK and Anon DH ciphersuites) */
489
490 st = sslVerifyCertChain(ctx, ctx->peerCert);
491 tls_handshake_trust_t trust;
492 switch (st) {
493 case errSecSuccess:
494 trust = tls_handshake_trust_ok;
495 break;
496 case errSSLUnknownRootCert:
497 case errSSLNoRootCert:
498 trust = tls_handshake_trust_unknown_root;
499 break;
500 case errSSLCertExpired:
501 case errSSLCertNotYetValid:
502 trust = tls_handshake_trust_cert_expired;
503 break;
504 case errSSLXCertChainInvalid:
505 default:
506 trust = tls_handshake_trust_cert_invalid;
507 break;
508 }
509
510 tls_handshake_set_peer_trust(ctx->hdsk, trust);
511
512 /* Now that trust has been (possibly) evaluated,
513 we check if we need to break out of the handshake */
514 if(ctx->protocolSide == kSSLServerSide) {
515 /*
516 * Schedule return to the caller to verify the client's identity.
517 * This will return even if there was no client cert sent.
518 */
519 if (ctx->breakOnClientAuth) {
520 err = errSSLClientAuthCompleted;
521 }
522 } else if(ctx->peerCert) {
523 /*
524 * Schedule return to the caller to verify the server's identity.
525 * This will only return if a server cert was sent. In other cases
526 * such as PSK and AnonDH, we don't want to break out of the handshake.
527 */
528 if (ctx->breakOnServerAuth) {
529 err = errSSLServerAuthCompleted;
530 }
531 }
532
533 return err;
534 }
535
536 /*
537 * After ciphersuite negotiation is complete, verify that we have
538 * the capability of actually performing the selected cipher.
539 * Currently we just verify that we have a cert and private signing
540 * key, if needed, and that the signing key's algorithm matches the
541 * expected key exchange method.
542 *
543 * This is currently called from FindCipherSpec(), after it sets
544 * ctx->selectedCipherSpec to a (supposedly) valid value, and from
545 * sslBuildCipherSpecArray(), in server mode (pre-negotiation) only.
546 */
547
548 #if 0
549 OSStatus sslVerifySelectedCipher(SSLContext *ctx)
550 {
551
552 if(ctx->protocolSide == kSSLClientSide) {
553 return errSecSuccess;
554 }
555 #if SSL_PAC_SERVER_ENABLE
556 if((ctx->masterSecretCallback != NULL) &&
557 (ctx->sessionTicket.data != NULL)) {
558 /* EAP via PAC resumption; we can do it */
559 return errSecSuccess;
560 }
561 #endif /* SSL_PAC_SERVER_ENABLE */
562
563 CFIndex requireAlg;
564 switch (ctx->selectedCipherSpecParams.keyExchangeMethod) {
565 case SSL_RSA:
566 case SSL_RSA_EXPORT:
567 case SSL_DH_RSA:
568 case SSL_DH_RSA_EXPORT:
569 case SSL_DHE_RSA:
570 case SSL_DHE_RSA_EXPORT:
571 requireAlg = kSecRSAAlgorithmID;
572 break;
573 case SSL_DHE_DSS:
574 case SSL_DHE_DSS_EXPORT:
575 case SSL_DH_DSS:
576 case SSL_DH_DSS_EXPORT:
577 requireAlg = kSecDSAAlgorithmID;
578 break;
579 case SSL_DH_anon:
580 case SSL_DH_anon_EXPORT:
581 case TLS_PSK:
582 requireAlg = kSecNullAlgorithmID; /* no signing key */
583 break;
584 /*
585 * When SSL_ECDSA_SERVER is true and we support ECDSA on the server side,
586 * we'll need to add some logic here...
587 */
588 #if SSL_ECDSA_SERVER
589 case SSL_ECDHE_ECDSA:
590 case SSL_ECDHE_RSA:
591 case SSL_ECDH_ECDSA:
592 case SSL_ECDH_RSA:
593 case SSL_ECDH_anon:
594 requireAlg = kSecECDSAAlgorithmID;
595 break;
596 #endif
597
598 default:
599 /* needs update per cipherSpecs.c */
600 assert(0);
601 sslErrorLog("sslVerifySelectedCipher: unknown key exchange method\n");
602 return errSSLInternal;
603 }
604
605 if(requireAlg == kSecNullAlgorithmID) {
606 return errSecSuccess;
607 }
608
609 /* private signing key required */
610 if(ctx->signingPrivKeyRef == NULL) {
611 sslErrorLog("sslVerifySelectedCipher: no signing key\n");
612 return errSSLBadConfiguration;
613 }
614
615 /* Check the alg of our signing key. */
616 CFIndex keyAlg = sslPrivKeyGetAlgorithmID(ctx->signingPrivKeyRef);
617 if (requireAlg != keyAlg) {
618 sslErrorLog("sslVerifySelectedCipher: signing key alg mismatch\n");
619 return errSSLBadConfiguration;
620 }
621
622 return errSecSuccess;
623 }
624
625 #endif