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
;
193 /* renegotiate - start with a new SecTrustRef */
194 CFReleaseNull(ctx
->peerSecTrust
);
197 status
= sslCreateSecTrust(ctx
, certChain
, arePeerCerts
, &trust
);
199 if (!ctx
->enableCertVerify
) {
200 /* trivial case, this is caller's responsibility */
201 status
= errSecSuccess
;
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
;
214 case kSecTrustResultDeny
:
215 case kSecTrustResultConfirm
:
216 case kSecTrustResultRecoverableTrustFailure
:
218 if(ctx
->allowAnyRoot
) {
219 sslErrorLog("***Warning: accepting unverified cert chain\n");
220 status
= errSecSuccess
;
224 * If the caller provided a list of trusted leaf certs, check them here
226 if(ctx
->trustedLeafCerts
) {
227 if (sslGetMatchingCertInArray((SecCertificateRef
)CFArrayGetValueAtIndex(certChain
, 0),
228 ctx
->trustedLeafCerts
)) {
229 status
= errSecSuccess
;
233 status
= errSSLXCertChainInvalid
;
235 /* Do we really need to return things like:
237 errSSLUnknownRootCert
239 errSSLCertNotYetValid
240 errSSLHostNameMismatch
241 for our client to see what went wrong, or should we just always
243 errSSLXCertChainInvalid
244 when something is wrong? */
250 ctx
->peerSecTrust
= trust
;
252 CFReleaseSafe(trust
);
257 /* Extract public SecKeyRef from Certificate Chain */
259 int sslCopyPeerPubKey(const SSLCertificate
*certchain
,
264 SecTrustRef trust
= NULL
;
265 const SSLCertificate
*cert
;
266 CFMutableArrayRef certArray
= NULL
;
267 CFDataRef certData
= NULL
;
268 SecCertificateRef cfCert
= NULL
;
270 err
= errSSLInternal
;
272 certArray
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
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
);
283 require_noerr((err
=SecTrustCreateWithCertificates(certArray
, NULL
, &trust
)), out
);
284 SecKeyRef key
= SecTrustCopyPublicKey(trust
);
285 require_action(key
, out
, err
=-9808); // errSSLBadCert
292 CFReleaseSafe(certData
);
293 CFReleaseSafe(cfCert
);
294 CFReleaseSafe(trust
);
295 CFReleaseSafe(certArray
);
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
)
305 SecKeyRef pubkey
= NULL
;
306 CFDataRef modulus
= NULL
;
307 CFDataRef exponent
= NULL
;
308 CFDataRef ecpubdata
= NULL
;
314 const SSLCertificate
*tmp
= certchain
;
316 printf("cert%d[] = {", i
);
317 for(j
=0; j
<tmp
->derCert
.length
; j
++) {
320 printf("0x%02x, ", tmp
->derCert
.data
[j
]);
329 require_noerr((err
=sslCopyPeerPubKey(certchain
, &pubkey
)), errOut
);
332 algId
= SecKeyGetAlgorithmID(pubkey
);
334 algId
= SecKeyGetAlgorithmId(pubkey
);
340 case kSecRSAAlgorithmID
:
342 require((modulus
= SecKeyCopyModulus(pubkey
)), errOut
);
343 require((exponent
= SecKeyCopyExponent(pubkey
)), errOut
);
348 mod
.data
= (uint8_t *)CFDataGetBytePtr(modulus
);
349 mod
.length
= CFDataGetLength(modulus
);
351 exp
.data
= (uint8_t *)CFDataGetBytePtr(exponent
);
352 exp
.length
= CFDataGetLength(exponent
);
354 err
= tls_handshake_set_peer_rsa_public_key(hdsk
, &mod
, &exp
);
357 case kSecECDSAAlgorithmID
:
359 tls_named_curve curve
= SecECKeyGetNamedCurve(pubkey
);
360 require((ecpubdata
= SecECKeyCopyPublicBits(pubkey
)), errOut
);
363 pubdata
.data
= (uint8_t *)CFDataGetBytePtr(ecpubdata
);
364 pubdata
.length
= CFDataGetLength(ecpubdata
);
366 err
= tls_handshake_set_peer_ec_public_key(hdsk
, curve
, &pubdata
);
375 CFReleaseSafe(pubkey
);
376 CFReleaseSafe(modulus
);
377 CFReleaseSafe(exponent
);
378 CFReleaseSafe(ecpubdata
);
383 /* Convert cert in DER format into an CFArray of SecCertificateRef */
385 tls_get_peer_certs(const SSLCertificate
*certs
)
387 const SSLCertificate
*cert
;
389 CFMutableArrayRef certArray
= NULL
;
390 CFDataRef certData
= NULL
;
391 SecCertificateRef cfCert
= NULL
;
393 certArray
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
394 require(certArray
, out
);
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
);
408 CFReleaseNull(cfCert
);
409 CFReleaseNull(certData
);
410 CFReleaseNull(certArray
);
415 tls_verify_peer_cert(SSLContext
*ctx
)
418 const SSLCertificate
*certs
;
420 certs
= tls_handshake_get_peer_certificates(ctx
->hdsk
);
421 CFReleaseNull(ctx
->peerCert
);
422 ctx
->peerCert
= tls_get_peer_certs(certs
);
424 err
= sslVerifyCertChain(ctx
, ctx
->peerCert
, true);
425 tls_handshake_trust_t trust
;
428 trust
= tls_handshake_trust_ok
;
430 case errSSLUnknownRootCert
:
431 case errSSLNoRootCert
:
432 trust
= tls_handshake_trust_unknown_root
;
434 case errSSLCertExpired
:
435 case errSSLCertNotYetValid
:
436 trust
= tls_handshake_trust_cert_expired
;
438 case errSSLXCertChainInvalid
:
440 trust
= tls_handshake_trust_cert_invalid
;
444 tls_handshake_set_peer_trust(ctx
->hdsk
, trust
);
449 /* Set the public key */
450 tls_set_peer_pubkey(ctx
->hdsk
, certs
);
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
) {
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.
461 if (ctx
->breakOnClientAuth
) {
462 err
= errSSLClientAuthCompleted
;
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.
470 if (ctx
->breakOnServerAuth
) {
471 err
= errSSLServerAuthCompleted
;
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.
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.
493 OSStatus
sslVerifySelectedCipher(SSLContext
*ctx
)
496 if(ctx
->protocolSide
== kSSLClientSide
) {
497 return errSecSuccess
;
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
;
505 #endif /* SSL_PAC_SERVER_ENABLE */
508 switch (ctx
->selectedCipherSpecParams
.keyExchangeMethod
) {
512 case SSL_DH_RSA_EXPORT
:
514 case SSL_DHE_RSA_EXPORT
:
515 requireAlg
= kSecRSAAlgorithmID
;
518 case SSL_DHE_DSS_EXPORT
:
520 case SSL_DH_DSS_EXPORT
:
521 requireAlg
= kSecDSAAlgorithmID
;
524 case SSL_DH_anon_EXPORT
:
526 requireAlg
= kSecNullAlgorithmID
; /* no signing key */
529 * When SSL_ECDSA_SERVER is true and we support ECDSA on the server side,
530 * we'll need to add some logic here...
533 case SSL_ECDHE_ECDSA
:
538 requireAlg
= kSecECDSAAlgorithmID
;
543 /* needs update per cipherSpecs.c */
545 sslErrorLog("sslVerifySelectedCipher: unknown key exchange method\n");
546 return errSSLInternal
;
549 if(requireAlg
== kSecNullAlgorithmID
) {
550 return errSecSuccess
;
553 /* private signing key required */
554 if(ctx
->signingPrivKeyRef
== NULL
) {
555 sslErrorLog("sslVerifySelectedCipher: no signing key\n");
556 return errSSLBadConfiguration
;
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
;
566 return errSecSuccess
;