]> git.saurik.com Git - apple/security.git/blob - Security/libsecurity_ssl/lib/sslCrypto.c
Security-57031.1.35.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 assert(certChain);
191
192 if (arePeerCerts) {
193 /* renegotiate - start with a new SecTrustRef */
194 CFReleaseNull(ctx->peerSecTrust);
195 }
196
197 status = sslCreateSecTrust(ctx, certChain, arePeerCerts, &trust);
198
199 if (!ctx->enableCertVerify) {
200 /* trivial case, this is caller's responsibility */
201 status = errSecSuccess;
202 goto errOut;
203 }
204
205 SecTrustResultType secTrustResult;
206 require_noerr(status = SecTrustEvaluate(trust, &secTrustResult), errOut);
207 switch (secTrustResult) {
208 case kSecTrustResultUnspecified:
209 /* cert chain valid, no special UserTrust assignments */
210 case kSecTrustResultProceed:
211 /* cert chain valid AND user explicitly trusts this */
212 status = errSecSuccess;
213 break;
214 case kSecTrustResultDeny:
215 case kSecTrustResultConfirm:
216 case kSecTrustResultRecoverableTrustFailure:
217 default:
218 if(ctx->allowAnyRoot) {
219 sslErrorLog("***Warning: accepting unverified cert chain\n");
220 status = errSecSuccess;
221 }
222 else {
223 /*
224 * If the caller provided a list of trusted leaf certs, check them here
225 */
226 if(ctx->trustedLeafCerts) {
227 if (sslGetMatchingCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(certChain, 0),
228 ctx->trustedLeafCerts)) {
229 status = errSecSuccess;
230 goto errOut;
231 }
232 }
233 status = errSSLXCertChainInvalid;
234 }
235 /* Do we really need to return things like:
236 errSSLNoRootCert
237 errSSLUnknownRootCert
238 errSSLCertExpired
239 errSSLCertNotYetValid
240 errSSLHostNameMismatch
241 for our client to see what went wrong, or should we just always
242 return
243 errSSLXCertChainInvalid
244 when something is wrong? */
245 break;
246 }
247
248 errOut:
249 if (arePeerCerts)
250 ctx->peerSecTrust = trust;
251 else
252 CFReleaseSafe(trust);
253
254 return status;
255 }
256
257 /* Extract public SecKeyRef from Certificate Chain */
258 static
259 int sslCopyPeerPubKey(const SSLCertificate *certchain,
260 SecKeyRef *pubKey)
261 {
262 int err;
263 check(pubKey);
264 SecTrustRef trust = NULL;
265 const SSLCertificate *cert;
266 CFMutableArrayRef certArray = NULL;
267 CFDataRef certData = NULL;
268 SecCertificateRef cfCert = NULL;
269
270 err = errSSLInternal;
271
272 certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
273 cert = certchain;
274 while(cert) {
275 require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out);
276 require((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out);
277 CFArrayAppendValue(certArray, cfCert);
278 CFReleaseNull(cfCert);
279 CFReleaseNull(certData);
280 cert=cert->next;
281 }
282
283 require_noerr((err=SecTrustCreateWithCertificates(certArray, NULL, &trust)), out);
284 SecKeyRef key = SecTrustCopyPublicKey(trust);
285 require_action(key, out, err=-9808); // errSSLBadCert
286
287 *pubKey = key;
288
289 err = errSecSuccess;
290
291 out:
292 CFReleaseSafe(certData);
293 CFReleaseSafe(cfCert);
294 CFReleaseSafe(trust);
295 CFReleaseSafe(certArray);
296
297 return err;
298 }
299
300 /* Extract the pubkey from a cert chain, and send it to the tls_handshake context */
301 static int tls_set_peer_pubkey(tls_handshake_t hdsk, const SSLCertificate *certchain)
302 {
303 int err;
304 CFIndex algId;
305 SecKeyRef pubkey = NULL;
306 CFDataRef modulus = NULL;
307 CFDataRef exponent = NULL;
308 CFDataRef ecpubdata = NULL;
309
310 #if 0
311 { /* dump certs */
312 int i=0;
313 int j;
314 const SSLCertificate *tmp = certchain;
315 while(tmp) {
316 printf("cert%d[] = {", i);
317 for(j=0; j<tmp->derCert.length; j++) {
318 if((j&0xf)==0)
319 printf("\n");
320 printf("0x%02x, ", tmp->derCert.data[j]);
321 }
322 printf("}\n");
323 tmp=tmp->next;
324 i++;
325 }
326 }
327 #endif
328
329 require_noerr((err=sslCopyPeerPubKey(certchain, &pubkey)), errOut);
330
331 #if TARGET_OS_IPHONE
332 algId = SecKeyGetAlgorithmID(pubkey);
333 #else
334 algId = SecKeyGetAlgorithmId(pubkey);
335 #endif
336
337 err = errSSLCrypto;
338
339 switch(algId) {
340 case kSecRSAAlgorithmID:
341 {
342 require((modulus = SecKeyCopyModulus(pubkey)), errOut);
343 require((exponent = SecKeyCopyExponent(pubkey)), errOut);
344
345 tls_buffer mod;
346 tls_buffer exp;
347
348 mod.data = (uint8_t *)CFDataGetBytePtr(modulus);
349 mod.length = CFDataGetLength(modulus);
350
351 exp.data = (uint8_t *)CFDataGetBytePtr(exponent);
352 exp.length = CFDataGetLength(exponent);
353
354 err = tls_handshake_set_peer_rsa_public_key(hdsk, &mod, &exp);
355 break;
356 }
357 case kSecECDSAAlgorithmID:
358 {
359 tls_named_curve curve = SecECKeyGetNamedCurve(pubkey);
360 require((ecpubdata = SecECKeyCopyPublicBits(pubkey)), errOut);
361
362 tls_buffer pubdata;
363 pubdata.data = (uint8_t *)CFDataGetBytePtr(ecpubdata);
364 pubdata.length = CFDataGetLength(ecpubdata);
365
366 err = tls_handshake_set_peer_ec_public_key(hdsk, curve, &pubdata);
367
368 break;
369 }
370 default:
371 break;
372 }
373
374 errOut:
375 CFReleaseSafe(pubkey);
376 CFReleaseSafe(modulus);
377 CFReleaseSafe(exponent);
378 CFReleaseSafe(ecpubdata);
379
380 return err;
381 }
382
383 /* Convert cert in DER format into an CFArray of SecCertificateRef */
384 CFArrayRef
385 tls_get_peer_certs(const SSLCertificate *certs)
386 {
387 const SSLCertificate *cert;
388
389 CFMutableArrayRef certArray = NULL;
390 CFDataRef certData = NULL;
391 SecCertificateRef cfCert = NULL;
392
393 certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
394 require(certArray, out);
395 cert = certs;
396 while(cert) {
397 require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out);
398 require((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out);
399 CFArrayAppendValue(certArray, cfCert);
400 CFReleaseNull(cfCert);
401 CFReleaseNull(certData);
402 cert=cert->next;
403 }
404
405 return certArray;
406
407 out:
408 CFReleaseNull(cfCert);
409 CFReleaseNull(certData);
410 CFReleaseNull(certArray);
411 return NULL;
412 }
413
414 int
415 tls_verify_peer_cert(SSLContext *ctx)
416 {
417 int err;
418 const SSLCertificate *certs;
419
420 certs = tls_handshake_get_peer_certificates(ctx->hdsk);
421 CFReleaseNull(ctx->peerCert);
422 ctx->peerCert = tls_get_peer_certs(certs);
423
424 err = sslVerifyCertChain(ctx, ctx->peerCert, true);
425 tls_handshake_trust_t trust;
426 switch (err) {
427 case errSecSuccess:
428 trust = tls_handshake_trust_ok;
429 break;
430 case errSSLUnknownRootCert:
431 case errSSLNoRootCert:
432 trust = tls_handshake_trust_unknown_root;
433 break;
434 case errSSLCertExpired:
435 case errSSLCertNotYetValid:
436 trust = tls_handshake_trust_cert_expired;
437 break;
438 case errSSLXCertChainInvalid:
439 default:
440 trust = tls_handshake_trust_cert_invalid;
441 break;
442 }
443
444 tls_handshake_set_peer_trust(ctx->hdsk, trust);
445
446 if(err)
447 goto out;
448
449 /* Set the public key */
450 tls_set_peer_pubkey(ctx->hdsk, certs);
451
452 /* Now that cert verification is done, update context state */
453 /* (this code was formerly in SSLProcessHandshakeMessage, */
454 /* directly after the return from SSLProcessCertificate) */
455 if(ctx->protocolSide == kSSLServerSide) {
456 /*
457 * Schedule return to the caller to verify the client's identity.
458 * Note that an error during processing will cause early
459 * termination of the handshake.
460 */
461 if (ctx->breakOnClientAuth) {
462 err = errSSLClientAuthCompleted;
463 }
464 } else {
465 /*
466 * Schedule return to the caller to verify the server's identity.
467 * Note that an error during processing will cause early
468 * termination of the handshake.
469 */
470 if (ctx->breakOnServerAuth) {
471 err = errSSLServerAuthCompleted;
472 }
473 }
474
475 out:
476
477 return err;
478 }
479
480 /*
481 * After ciphersuite negotiation is complete, verify that we have
482 * the capability of actually performing the selected cipher.
483 * Currently we just verify that we have a cert and private signing
484 * key, if needed, and that the signing key's algorithm matches the
485 * expected key exchange method.
486 *
487 * This is currently called from FindCipherSpec(), after it sets
488 * ctx->selectedCipherSpec to a (supposedly) valid value, and from
489 * sslBuildCipherSpecArray(), in server mode (pre-negotiation) only.
490 */
491
492 #if 0
493 OSStatus sslVerifySelectedCipher(SSLContext *ctx)
494 {
495
496 if(ctx->protocolSide == kSSLClientSide) {
497 return errSecSuccess;
498 }
499 #if SSL_PAC_SERVER_ENABLE
500 if((ctx->masterSecretCallback != NULL) &&
501 (ctx->sessionTicket.data != NULL)) {
502 /* EAP via PAC resumption; we can do it */
503 return errSecSuccess;
504 }
505 #endif /* SSL_PAC_SERVER_ENABLE */
506
507 CFIndex requireAlg;
508 switch (ctx->selectedCipherSpecParams.keyExchangeMethod) {
509 case SSL_RSA:
510 case SSL_RSA_EXPORT:
511 case SSL_DH_RSA:
512 case SSL_DH_RSA_EXPORT:
513 case SSL_DHE_RSA:
514 case SSL_DHE_RSA_EXPORT:
515 requireAlg = kSecRSAAlgorithmID;
516 break;
517 case SSL_DHE_DSS:
518 case SSL_DHE_DSS_EXPORT:
519 case SSL_DH_DSS:
520 case SSL_DH_DSS_EXPORT:
521 requireAlg = kSecDSAAlgorithmID;
522 break;
523 case SSL_DH_anon:
524 case SSL_DH_anon_EXPORT:
525 case TLS_PSK:
526 requireAlg = kSecNullAlgorithmID; /* no signing key */
527 break;
528 /*
529 * When SSL_ECDSA_SERVER is true and we support ECDSA on the server side,
530 * we'll need to add some logic here...
531 */
532 #if SSL_ECDSA_SERVER
533 case SSL_ECDHE_ECDSA:
534 case SSL_ECDHE_RSA:
535 case SSL_ECDH_ECDSA:
536 case SSL_ECDH_RSA:
537 case SSL_ECDH_anon:
538 requireAlg = kSecECDSAAlgorithmID;
539 break;
540 #endif
541
542 default:
543 /* needs update per cipherSpecs.c */
544 assert(0);
545 sslErrorLog("sslVerifySelectedCipher: unknown key exchange method\n");
546 return errSSLInternal;
547 }
548
549 if(requireAlg == kSecNullAlgorithmID) {
550 return errSecSuccess;
551 }
552
553 /* private signing key required */
554 if(ctx->signingPrivKeyRef == NULL) {
555 sslErrorLog("sslVerifySelectedCipher: no signing key\n");
556 return errSSLBadConfiguration;
557 }
558
559 /* Check the alg of our signing key. */
560 CFIndex keyAlg = sslPrivKeyGetAlgorithmID(ctx->signingPrivKeyRef);
561 if (requireAlg != keyAlg) {
562 sslErrorLog("sslVerifySelectedCipher: signing key alg mismatch\n");
563 return errSSLBadConfiguration;
564 }
565
566 return errSecSuccess;
567 }
568
569 #endif