2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * sslCrypto.c - interface between SSL and crypto libraries
28 #include "sslCrypto.h"
29 #include "sslContext.h"
30 #include "sslMemory.h"
38 #include <Security/SecTrust.h>
39 #include <Security/SecPolicy.h>
40 #include <Security/SecCertificate.h>
42 #include <AssertMacros.h>
43 #include "utilities/SecCFRelease.h"
46 #include <Security/SecRSAKey.h>
47 #include <Security/SecECKey.h>
51 * Get algorithm id for a SSLPubKey object.
53 CFIndex
sslPubKeyGetAlgorithmID(SecKeyRef pubKey
)
56 return SecKeyGetAlgorithmID(pubKey
);
58 return SecKeyGetAlgorithmId(pubKey
);
63 * Get algorithm id for a SSLPrivKey object.
65 CFIndex
sslPrivKeyGetAlgorithmID(SecKeyRef privKey
)
68 return SecKeyGetAlgorithmID(privKey
);
70 return SecKeyGetAlgorithmId(privKey
);
80 SecTrustRef
*pTrust
) /* RETURNED */
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;
89 if(ctx
->protocolSide
==kSSLClientSide
) {
90 tls_handshake_get_peer_hostname(ctx
->hdsk
, &peerDomainNameData
, &peerDomainNameLen
);
93 if (CFArrayGetCount(certChain
) == 0) {
94 status
= errSSLBadCert
;
99 if (peerDomainNameLen
&& peerDomainNameData
) {
100 CFIndex len
= peerDomainNameLen
;
101 if (peerDomainNameData
[len
- 1] == 0) {
103 //secwarning("peerDomainName is zero terminated!");
105 /* @@@ Double check that this is the correct encoding. */
106 require(peerDomainName
= CFStringCreateWithBytes(kCFAllocatorDefault
,
107 (const UInt8
*)peerDomainNameData
, len
,
108 kCFStringEncodingUTF8
, false), errOut
);
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
);
116 require_noerr(status
= SecTrustCreateWithCertificates(certChain
, policies
,
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
);
127 status
= errSecSuccess
;
130 CFReleaseSafe(peerDomainName
);
131 CFReleaseSafe(policies
);
138 /* Return the first certificate reference from the supplied array
139 * whose data matches the given certificate, or NULL if none match.
143 sslGetMatchingCertInArray(
144 SecCertificateRef certRef
,
145 CFArrayRef certArray
)
147 SecCertificateRef matchedCert
= NULL
;
149 if (certRef
== NULL
|| certArray
== NULL
) {
153 CFDataRef certData
= SecCertificateCopyData(certRef
);
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
)) {
162 CFReleaseSafe(aData
);
166 CFReleaseSafe(certData
);
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.
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.
182 static OSStatus
sslVerifyCertChain(
184 CFArrayRef certChain
,
188 SecTrustRef trust
= NULL
;
191 /* renegotiate - start with a new SecTrustRef */
192 CFReleaseNull(ctx
->peerSecTrust
);
195 if(certChain
==NULL
) {
196 sslErrorLog("***Error: NULL cert chain\n");
197 status
= errSSLXCertChainInvalid
;
201 status
= sslCreateSecTrust(ctx
, certChain
, arePeerCerts
, &trust
);
203 if (!ctx
->enableCertVerify
) {
204 /* trivial case, this is caller's responsibility */
205 status
= errSecSuccess
;
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
;
218 case kSecTrustResultDeny
:
219 case kSecTrustResultConfirm
:
220 case kSecTrustResultRecoverableTrustFailure
:
222 if(ctx
->allowAnyRoot
) {
223 sslErrorLog("***Warning: accepting unverified cert chain\n");
224 status
= errSecSuccess
;
228 * If the caller provided a list of trusted leaf certs, check them here
230 if(ctx
->trustedLeafCerts
) {
231 if (sslGetMatchingCertInArray((SecCertificateRef
)CFArrayGetValueAtIndex(certChain
, 0),
232 ctx
->trustedLeafCerts
)) {
233 status
= errSecSuccess
;
237 status
= errSSLXCertChainInvalid
;
239 /* Do we really need to return things like:
241 errSSLUnknownRootCert
243 errSSLCertNotYetValid
244 errSSLHostNameMismatch
245 for our client to see what went wrong, or should we just always
247 errSSLXCertChainInvalid
248 when something is wrong? */
254 ctx
->peerSecTrust
= trust
;
256 CFReleaseSafe(trust
);
261 /* Extract public SecKeyRef from Certificate Chain */
263 int sslCopyPeerPubKey(const SSLCertificate
*certchain
,
268 SecTrustRef trust
= NULL
;
269 const SSLCertificate
*cert
;
270 CFMutableArrayRef certArray
= NULL
;
271 CFDataRef certData
= NULL
;
272 SecCertificateRef cfCert
= NULL
;
274 err
= errSSLInternal
;
276 certArray
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
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
);
287 require_noerr((err
=SecTrustCreateWithCertificates(certArray
, NULL
, &trust
)), out
);
288 SecKeyRef key
= SecTrustCopyPublicKey(trust
);
289 require_action(key
, out
, err
=-9808); // errSSLBadCert
296 CFReleaseSafe(certData
);
297 CFReleaseSafe(cfCert
);
298 CFReleaseSafe(trust
);
299 CFReleaseSafe(certArray
);
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
)
309 SecKeyRef pubkey
= NULL
;
310 CFDataRef modulus
= NULL
;
311 CFDataRef exponent
= NULL
;
312 CFDataRef ecpubdata
= NULL
;
318 const SSLCertificate
*tmp
= certchain
;
320 printf("cert%d[] = {", i
);
321 for(j
=0; j
<tmp
->derCert
.length
; j
++) {
324 printf("0x%02x, ", tmp
->derCert
.data
[j
]);
333 require_noerr((err
=sslCopyPeerPubKey(certchain
, &pubkey
)), errOut
);
336 algId
= SecKeyGetAlgorithmID(pubkey
);
338 algId
= SecKeyGetAlgorithmId(pubkey
);
344 case kSecRSAAlgorithmID
:
346 require((modulus
= SecKeyCopyModulus(pubkey
)), errOut
);
347 require((exponent
= SecKeyCopyExponent(pubkey
)), errOut
);
352 mod
.data
= (uint8_t *)CFDataGetBytePtr(modulus
);
353 mod
.length
= CFDataGetLength(modulus
);
355 exp
.data
= (uint8_t *)CFDataGetBytePtr(exponent
);
356 exp
.length
= CFDataGetLength(exponent
);
358 err
= tls_handshake_set_peer_rsa_public_key(hdsk
, &mod
, &exp
);
361 case kSecECDSAAlgorithmID
:
363 tls_named_curve curve
= SecECKeyGetNamedCurve(pubkey
);
364 require((ecpubdata
= SecECKeyCopyPublicBits(pubkey
)), errOut
);
367 pubdata
.data
= (uint8_t *)CFDataGetBytePtr(ecpubdata
);
368 pubdata
.length
= CFDataGetLength(ecpubdata
);
370 err
= tls_handshake_set_peer_ec_public_key(hdsk
, curve
, &pubdata
);
379 CFReleaseSafe(pubkey
);
380 CFReleaseSafe(modulus
);
381 CFReleaseSafe(exponent
);
382 CFReleaseSafe(ecpubdata
);
387 /* Convert cert in DER format into an CFArray of SecCertificateRef */
389 tls_get_peer_certs(const SSLCertificate
*certs
)
391 const SSLCertificate
*cert
;
393 CFMutableArrayRef certArray
= NULL
;
394 CFDataRef certData
= NULL
;
395 SecCertificateRef cfCert
= NULL
;
397 certArray
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
398 require(certArray
, out
);
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
);
412 CFReleaseNull(cfCert
);
413 CFReleaseNull(certData
);
414 CFReleaseNull(certArray
);
419 tls_verify_peer_cert(SSLContext
*ctx
)
422 const SSLCertificate
*certs
;
424 certs
= tls_handshake_get_peer_certificates(ctx
->hdsk
);
425 CFReleaseNull(ctx
->peerCert
);
426 ctx
->peerCert
= tls_get_peer_certs(certs
);
428 err
= sslVerifyCertChain(ctx
, ctx
->peerCert
, true);
429 tls_handshake_trust_t trust
;
432 trust
= tls_handshake_trust_ok
;
434 case errSSLUnknownRootCert
:
435 case errSSLNoRootCert
:
436 trust
= tls_handshake_trust_unknown_root
;
438 case errSSLCertExpired
:
439 case errSSLCertNotYetValid
:
440 trust
= tls_handshake_trust_cert_expired
;
442 case errSSLXCertChainInvalid
:
444 trust
= tls_handshake_trust_cert_invalid
;
448 tls_handshake_set_peer_trust(ctx
->hdsk
, trust
);
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. */
459 require_noerr(err
=tls_set_peer_pubkey(ctx
->hdsk
, certs
), out
);
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
) {
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.
471 if (ctx
->breakOnClientAuth
) {
472 err
= errSSLClientAuthCompleted
;
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.
480 if (ctx
->breakOnServerAuth
) {
481 err
= errSSLServerAuthCompleted
;
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.
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.
503 OSStatus
sslVerifySelectedCipher(SSLContext
*ctx
)
506 if(ctx
->protocolSide
== kSSLClientSide
) {
507 return errSecSuccess
;
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
;
515 #endif /* SSL_PAC_SERVER_ENABLE */
518 switch (ctx
->selectedCipherSpecParams
.keyExchangeMethod
) {
522 case SSL_DH_RSA_EXPORT
:
524 case SSL_DHE_RSA_EXPORT
:
525 requireAlg
= kSecRSAAlgorithmID
;
528 case SSL_DHE_DSS_EXPORT
:
530 case SSL_DH_DSS_EXPORT
:
531 requireAlg
= kSecDSAAlgorithmID
;
534 case SSL_DH_anon_EXPORT
:
536 requireAlg
= kSecNullAlgorithmID
; /* no signing key */
539 * When SSL_ECDSA_SERVER is true and we support ECDSA on the server side,
540 * we'll need to add some logic here...
543 case SSL_ECDHE_ECDSA
:
548 requireAlg
= kSecECDSAAlgorithmID
;
553 /* needs update per cipherSpecs.c */
555 sslErrorLog("sslVerifySelectedCipher: unknown key exchange method\n");
556 return errSSLInternal
;
559 if(requireAlg
== kSecNullAlgorithmID
) {
560 return errSecSuccess
;
563 /* private signing key required */
564 if(ctx
->signingPrivKeyRef
== NULL
) {
565 sslErrorLog("sslVerifySelectedCipher: no signing key\n");
566 return errSSLBadConfiguration
;
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
;
576 return errSecSuccess
;