]> git.saurik.com Git - apple/security.git/blob - Security/libsecurity_ssl/lib/sslCrypto.c
Security-57031.30.12.tar.gz
[apple/security.git] / Security / 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/SecTrust.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
75 OSStatus
76 sslCreateSecTrust(
77 SSLContext *ctx,
78 CFArrayRef certChain,
79 bool arePeerCerts,
80 SecTrustRef *pTrust) /* RETURNED */
81 {
82 OSStatus status = errSecAllocate;
83 CFStringRef peerDomainName = NULL;
84 CFTypeRef policies = NULL;
85 SecTrustRef trust = NULL;
86 const char *peerDomainNameData = NULL;
87 size_t peerDomainNameLen = 0;
88
89 if(ctx->protocolSide==kSSLClientSide) {
90 tls_handshake_get_peer_hostname(ctx->hdsk, &peerDomainNameData, &peerDomainNameLen);
91 }
92
93 if (CFArrayGetCount(certChain) == 0) {
94 status = errSSLBadCert;
95 goto errOut;
96 }
97
98 if (arePeerCerts) {
99 if (peerDomainNameLen && peerDomainNameData) {
100 CFIndex len = peerDomainNameLen;
101 if (peerDomainNameData[len - 1] == 0) {
102 len--;
103 //secwarning("peerDomainName is zero terminated!");
104 }
105 /* @@@ Double check that this is the correct encoding. */
106 require(peerDomainName = CFStringCreateWithBytes(kCFAllocatorDefault,
107 (const UInt8 *)peerDomainNameData, len,
108 kCFStringEncodingUTF8, false), errOut);
109 }
110 }
111 /* If we are the client, our peer certificates must satisfy the
112 ssl server policy. */
113 bool server = ctx->protocolSide == kSSLClientSide;
114 require(policies = SecPolicyCreateSSL(server, peerDomainName), errOut);
115
116 require_noerr(status = SecTrustCreateWithCertificates(certChain, policies,
117 &trust), errOut);
118
119 /* If we have trustedAnchors we set them here. */
120 if (ctx->trustedCerts) {
121 require_noerr(status = SecTrustSetAnchorCertificates(trust,
122 ctx->trustedCerts), errOut);
123 require_noerr(status = SecTrustSetAnchorCertificatesOnly(trust,
124 ctx->trustedCertsOnly), errOut);
125 }
126
127 status = errSecSuccess;
128
129 errOut:
130 CFReleaseSafe(peerDomainName);
131 CFReleaseSafe(policies);
132
133 *pTrust = trust;
134
135 return status;
136 }
137
138 /* Return the first certificate reference from the supplied array
139 * whose data matches the given certificate, or NULL if none match.
140 */
141 static
142 SecCertificateRef
143 sslGetMatchingCertInArray(
144 SecCertificateRef certRef,
145 CFArrayRef certArray)
146 {
147 SecCertificateRef matchedCert = NULL;
148
149 if (certRef == NULL || certArray == NULL) {
150 return NULL;
151 }
152
153 CFDataRef certData = SecCertificateCopyData(certRef);
154 if (certData) {
155 CFIndex idx, count = CFArrayGetCount(certArray);
156 for(idx=0; idx<count; idx++) {
157 SecCertificateRef aCert = (SecCertificateRef)CFArrayGetValueAtIndex(certArray, idx);
158 CFDataRef aData = SecCertificateCopyData(aCert);
159 if (aData && CFEqual(aData, certData)) {
160 matchedCert = aCert;
161 }
162 CFReleaseSafe(aData);
163 if (matchedCert)
164 break;
165 }
166 CFReleaseSafe(certData);
167 }
168
169 return matchedCert;
170 }
171
172 /*
173 * Verify a chain of DER-encoded certs.
174 * Last cert in a chain is the leaf; this must also be present
175 * in ctx->trustedCerts.
176 *
177 * If arePeerCerts is true, host name verification is enabled and we
178 * save the resulting SecTrustRef in ctx->peerSecTrust. Otherwise
179 * we're just validating our own certs; no host name checking and
180 * peerSecTrust is transient.
181 */
182 static OSStatus sslVerifyCertChain(
183 SSLContext *ctx,
184 CFArrayRef certChain,
185 bool arePeerCerts)
186 {
187 OSStatus status;
188 SecTrustRef trust = NULL;
189
190 if (arePeerCerts) {
191 /* renegotiate - start with a new SecTrustRef */
192 CFReleaseNull(ctx->peerSecTrust);
193 }
194
195 if(certChain==NULL) {
196 sslErrorLog("***Error: NULL cert chain\n");
197 status = errSSLXCertChainInvalid;
198 goto errOut;
199 }
200
201 status = sslCreateSecTrust(ctx, certChain, arePeerCerts, &trust);
202
203 if (!ctx->enableCertVerify) {
204 /* trivial case, this is caller's responsibility */
205 status = errSecSuccess;
206 goto errOut;
207 }
208
209 SecTrustResultType secTrustResult;
210 require_noerr(status = SecTrustEvaluate(trust, &secTrustResult), errOut);
211 switch (secTrustResult) {
212 case kSecTrustResultUnspecified:
213 /* cert chain valid, no special UserTrust assignments */
214 case kSecTrustResultProceed:
215 /* cert chain valid AND user explicitly trusts this */
216 status = errSecSuccess;
217 break;
218 case kSecTrustResultDeny:
219 case kSecTrustResultConfirm:
220 case kSecTrustResultRecoverableTrustFailure:
221 default:
222 if(ctx->allowAnyRoot) {
223 sslErrorLog("***Warning: accepting unverified cert chain\n");
224 status = errSecSuccess;
225 }
226 else {
227 /*
228 * If the caller provided a list of trusted leaf certs, check them here
229 */
230 if(ctx->trustedLeafCerts) {
231 if (sslGetMatchingCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(certChain, 0),
232 ctx->trustedLeafCerts)) {
233 status = errSecSuccess;
234 goto errOut;
235 }
236 }
237 status = errSSLXCertChainInvalid;
238 }
239 /* Do we really need to return things like:
240 errSSLNoRootCert
241 errSSLUnknownRootCert
242 errSSLCertExpired
243 errSSLCertNotYetValid
244 errSSLHostNameMismatch
245 for our client to see what went wrong, or should we just always
246 return
247 errSSLXCertChainInvalid
248 when something is wrong? */
249 break;
250 }
251
252 errOut:
253 if (arePeerCerts)
254 ctx->peerSecTrust = trust;
255 else
256 CFReleaseSafe(trust);
257
258 return status;
259 }
260
261 /* Extract public SecKeyRef from Certificate Chain */
262 static
263 int sslCopyPeerPubKey(const SSLCertificate *certchain,
264 SecKeyRef *pubKey)
265 {
266 int err;
267 check(pubKey);
268 SecTrustRef trust = NULL;
269 const SSLCertificate *cert;
270 CFMutableArrayRef certArray = NULL;
271 CFDataRef certData = NULL;
272 SecCertificateRef cfCert = NULL;
273
274 err = errSSLInternal;
275
276 certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
277 cert = certchain;
278 while(cert) {
279 require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out);
280 require((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out);
281 CFArrayAppendValue(certArray, cfCert);
282 CFReleaseNull(cfCert);
283 CFReleaseNull(certData);
284 cert=cert->next;
285 }
286
287 require_noerr((err=SecTrustCreateWithCertificates(certArray, NULL, &trust)), out);
288 SecKeyRef key = SecTrustCopyPublicKey(trust);
289 require_action(key, out, err=-9808); // errSSLBadCert
290
291 *pubKey = key;
292
293 err = errSecSuccess;
294
295 out:
296 CFReleaseSafe(certData);
297 CFReleaseSafe(cfCert);
298 CFReleaseSafe(trust);
299 CFReleaseSafe(certArray);
300
301 return err;
302 }
303
304 /* Extract the pubkey from a cert chain, and send it to the tls_handshake context */
305 static int tls_set_peer_pubkey(tls_handshake_t hdsk, const SSLCertificate *certchain)
306 {
307 int err;
308 CFIndex algId;
309 SecKeyRef pubkey = NULL;
310 CFDataRef modulus = NULL;
311 CFDataRef exponent = NULL;
312 CFDataRef ecpubdata = NULL;
313
314 #if 0
315 { /* dump certs */
316 int i=0;
317 int j;
318 const SSLCertificate *tmp = certchain;
319 while(tmp) {
320 printf("cert%d[] = {", i);
321 for(j=0; j<tmp->derCert.length; j++) {
322 if((j&0xf)==0)
323 printf("\n");
324 printf("0x%02x, ", tmp->derCert.data[j]);
325 }
326 printf("}\n");
327 tmp=tmp->next;
328 i++;
329 }
330 }
331 #endif
332
333 require_noerr((err=sslCopyPeerPubKey(certchain, &pubkey)), errOut);
334
335 #if TARGET_OS_IPHONE
336 algId = SecKeyGetAlgorithmID(pubkey);
337 #else
338 algId = SecKeyGetAlgorithmId(pubkey);
339 #endif
340
341 err = errSSLCrypto;
342
343 switch(algId) {
344 case kSecRSAAlgorithmID:
345 {
346 require((modulus = SecKeyCopyModulus(pubkey)), errOut);
347 require((exponent = SecKeyCopyExponent(pubkey)), errOut);
348
349 tls_buffer mod;
350 tls_buffer exp;
351
352 mod.data = (uint8_t *)CFDataGetBytePtr(modulus);
353 mod.length = CFDataGetLength(modulus);
354
355 exp.data = (uint8_t *)CFDataGetBytePtr(exponent);
356 exp.length = CFDataGetLength(exponent);
357
358 err = tls_handshake_set_peer_rsa_public_key(hdsk, &mod, &exp);
359 break;
360 }
361 case kSecECDSAAlgorithmID:
362 {
363 tls_named_curve curve = SecECKeyGetNamedCurve(pubkey);
364 require((ecpubdata = SecECKeyCopyPublicBits(pubkey)), errOut);
365
366 tls_buffer pubdata;
367 pubdata.data = (uint8_t *)CFDataGetBytePtr(ecpubdata);
368 pubdata.length = CFDataGetLength(ecpubdata);
369
370 err = tls_handshake_set_peer_ec_public_key(hdsk, curve, &pubdata);
371
372 break;
373 }
374 default:
375 break;
376 }
377
378 errOut:
379 CFReleaseSafe(pubkey);
380 CFReleaseSafe(modulus);
381 CFReleaseSafe(exponent);
382 CFReleaseSafe(ecpubdata);
383
384 return err;
385 }
386
387 /* Convert cert in DER format into an CFArray of SecCertificateRef */
388 CFArrayRef
389 tls_get_peer_certs(const SSLCertificate *certs)
390 {
391 const SSLCertificate *cert;
392
393 CFMutableArrayRef certArray = NULL;
394 CFDataRef certData = NULL;
395 SecCertificateRef cfCert = NULL;
396
397 certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
398 require(certArray, out);
399 cert = certs;
400 while(cert) {
401 require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out);
402 require((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out);
403 CFArrayAppendValue(certArray, cfCert);
404 CFReleaseNull(cfCert);
405 CFReleaseNull(certData);
406 cert=cert->next;
407 }
408
409 return certArray;
410
411 out:
412 CFReleaseNull(cfCert);
413 CFReleaseNull(certData);
414 CFReleaseNull(certArray);
415 return NULL;
416 }
417
418 int
419 tls_verify_peer_cert(SSLContext *ctx)
420 {
421 int err;
422 const SSLCertificate *certs;
423
424 certs = tls_handshake_get_peer_certificates(ctx->hdsk);
425 CFReleaseNull(ctx->peerCert);
426 ctx->peerCert = tls_get_peer_certs(certs);
427
428 err = sslVerifyCertChain(ctx, ctx->peerCert, true);
429 tls_handshake_trust_t trust;
430 switch (err) {
431 case errSecSuccess:
432 trust = tls_handshake_trust_ok;
433 break;
434 case errSSLUnknownRootCert:
435 case errSSLNoRootCert:
436 trust = tls_handshake_trust_unknown_root;
437 break;
438 case errSSLCertExpired:
439 case errSSLCertNotYetValid:
440 trust = tls_handshake_trust_cert_expired;
441 break;
442 case errSSLXCertChainInvalid:
443 default:
444 trust = tls_handshake_trust_cert_invalid;
445 break;
446 }
447
448 tls_handshake_set_peer_trust(ctx->hdsk, trust);
449
450 if(err)
451 goto out;
452
453 /* Set the public key, only if we have certs.
454 We don't return an handshake error if there is no cert,
455 The fact that there is no cert should be reflected in the
456 trust results above, or will be handle when the application
457 does its own trust evaluation. */
458 if(certs) {
459 require_noerr(err=tls_set_peer_pubkey(ctx->hdsk, certs), out);
460 }
461
462 /* Now that cert verification is done, update context state */
463 /* (this code was formerly in SSLProcessHandshakeMessage, */
464 /* directly after the return from SSLProcessCertificate) */
465 if(ctx->protocolSide == kSSLServerSide) {
466 /*
467 * Schedule return to the caller to verify the client's identity.
468 * Note that an error during processing will cause early
469 * termination of the handshake.
470 */
471 if (ctx->breakOnClientAuth) {
472 err = errSSLClientAuthCompleted;
473 }
474 } else {
475 /*
476 * Schedule return to the caller to verify the server's identity.
477 * Note that an error during processing will cause early
478 * termination of the handshake.
479 */
480 if (ctx->breakOnServerAuth) {
481 err = errSSLServerAuthCompleted;
482 }
483 }
484
485 out:
486
487 return err;
488 }
489
490 /*
491 * After ciphersuite negotiation is complete, verify that we have
492 * the capability of actually performing the selected cipher.
493 * Currently we just verify that we have a cert and private signing
494 * key, if needed, and that the signing key's algorithm matches the
495 * expected key exchange method.
496 *
497 * This is currently called from FindCipherSpec(), after it sets
498 * ctx->selectedCipherSpec to a (supposedly) valid value, and from
499 * sslBuildCipherSpecArray(), in server mode (pre-negotiation) only.
500 */
501
502 #if 0
503 OSStatus sslVerifySelectedCipher(SSLContext *ctx)
504 {
505
506 if(ctx->protocolSide == kSSLClientSide) {
507 return errSecSuccess;
508 }
509 #if SSL_PAC_SERVER_ENABLE
510 if((ctx->masterSecretCallback != NULL) &&
511 (ctx->sessionTicket.data != NULL)) {
512 /* EAP via PAC resumption; we can do it */
513 return errSecSuccess;
514 }
515 #endif /* SSL_PAC_SERVER_ENABLE */
516
517 CFIndex requireAlg;
518 switch (ctx->selectedCipherSpecParams.keyExchangeMethod) {
519 case SSL_RSA:
520 case SSL_RSA_EXPORT:
521 case SSL_DH_RSA:
522 case SSL_DH_RSA_EXPORT:
523 case SSL_DHE_RSA:
524 case SSL_DHE_RSA_EXPORT:
525 requireAlg = kSecRSAAlgorithmID;
526 break;
527 case SSL_DHE_DSS:
528 case SSL_DHE_DSS_EXPORT:
529 case SSL_DH_DSS:
530 case SSL_DH_DSS_EXPORT:
531 requireAlg = kSecDSAAlgorithmID;
532 break;
533 case SSL_DH_anon:
534 case SSL_DH_anon_EXPORT:
535 case TLS_PSK:
536 requireAlg = kSecNullAlgorithmID; /* no signing key */
537 break;
538 /*
539 * When SSL_ECDSA_SERVER is true and we support ECDSA on the server side,
540 * we'll need to add some logic here...
541 */
542 #if SSL_ECDSA_SERVER
543 case SSL_ECDHE_ECDSA:
544 case SSL_ECDHE_RSA:
545 case SSL_ECDH_ECDSA:
546 case SSL_ECDH_RSA:
547 case SSL_ECDH_anon:
548 requireAlg = kSecECDSAAlgorithmID;
549 break;
550 #endif
551
552 default:
553 /* needs update per cipherSpecs.c */
554 assert(0);
555 sslErrorLog("sslVerifySelectedCipher: unknown key exchange method\n");
556 return errSSLInternal;
557 }
558
559 if(requireAlg == kSecNullAlgorithmID) {
560 return errSecSuccess;
561 }
562
563 /* private signing key required */
564 if(ctx->signingPrivKeyRef == NULL) {
565 sslErrorLog("sslVerifySelectedCipher: no signing key\n");
566 return errSSLBadConfiguration;
567 }
568
569 /* Check the alg of our signing key. */
570 CFIndex keyAlg = sslPrivKeyGetAlgorithmID(ctx->signingPrivKeyRef);
571 if (requireAlg != keyAlg) {
572 sslErrorLog("sslVerifySelectedCipher: signing key alg mismatch\n");
573 return errSSLBadConfiguration;
574 }
575
576 return errSecSuccess;
577 }
578
579 #endif