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 */
53CFIndex 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 */
65CFIndex sslPrivKeyGetAlgorithmID(SecKeyRef privKey)
66{
67#if TARGET_OS_IPHONE
68	return SecKeyGetAlgorithmID(privKey);
69#else
70	return SecKeyGetAlgorithmId(privKey);
71#endif
72}
73
74
75OSStatus
76sslCreateSecTrust(
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
129errOut:
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 */
141static
142SecCertificateRef
143sslGetMatchingCertInArray(
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 */
182static 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
248errOut:
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 */
258static
259int 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
291out:
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 */
301static 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
374errOut:
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 */
384CFArrayRef
385tls_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
407out:
408    CFReleaseNull(cfCert);
409    CFReleaseNull(certData);
410    CFReleaseNull(certArray);
411    return NULL;
412}
413
414int
415tls_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
475out:
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
493OSStatus 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
570