#include "sslCrypto.h"
#include "sslContext.h"
#include "sslMemory.h"
-#include "sslUtils.h"
#include "sslDebug.h"
#include <string.h>
#include <Security/SecECKey.h>
#endif
-/*
- * Get algorithm id for a SSLPubKey object.
- */
-CFIndex sslPubKeyGetAlgorithmID(SecKeyRef pubKey)
-{
-#if TARGET_OS_IPHONE
- return SecKeyGetAlgorithmID(pubKey);
-#else
- return SecKeyGetAlgorithmId(pubKey);
-#endif
-}
-
-/*
- * Get algorithm id for a SSLPrivKey object.
- */
-CFIndex sslPrivKeyGetAlgorithmID(SecKeyRef privKey)
-{
-#if TARGET_OS_IPHONE
- return SecKeyGetAlgorithmID(privKey);
-#else
- return SecKeyGetAlgorithmId(privKey);
-#endif
-}
+#include <tls_helpers.h>
-static
-OSStatus sslCreateCFArrayFromList(const tls_buffer_list_t *list, CFArrayRef *cfArray)
-{
- int err;
- CFMutableArrayRef array = NULL;
- CFDataRef data = NULL;
-
- err = errSSLInternal;
-
- array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- require(array, out);
-
- while(list) {
- require((data = CFDataCreate(kCFAllocatorDefault, list->buffer.data, list->buffer.length)), out);
- CFArrayAppendValue(array, data);
- CFReleaseNull(data);
- list=list->next;
- }
-
- *cfArray = array;
- return errSecSuccess;
-
-out:
- CFReleaseSafe(data);
- CFReleaseSafe(array);
- return err;
-}
OSStatus
sslCreateSecTrust(
SSLContext *ctx,
- CFArrayRef certChain,
SecTrustRef *pTrust) /* RETURNED */
{
OSStatus status = errSecAllocate;
- CFStringRef peerDomainName = NULL;
- CFTypeRef policies = NULL;
SecTrustRef trust = NULL;
- const char *peerDomainNameData = NULL;
- size_t peerDomainNameLen = 0;
-
- if(ctx->protocolSide==kSSLClientSide) {
- tls_handshake_get_peer_hostname(ctx->hdsk, &peerDomainNameData, &peerDomainNameLen);
- }
- if (CFArrayGetCount(certChain) == 0) {
- status = errSSLBadCert;
- goto errOut;
- }
-
- if (peerDomainNameLen && peerDomainNameData) {
- CFIndex len = peerDomainNameLen;
- if (peerDomainNameData[len - 1] == 0) {
- len--;
- //secwarning("peerDomainName is zero terminated!");
- }
- /* @@@ Double check that this is the correct encoding. */
- require(peerDomainName = CFStringCreateWithBytes(kCFAllocatorDefault,
- (const UInt8 *)peerDomainNameData, len,
- kCFStringEncodingUTF8, false), errOut);
- }
-
- /* If we are the client, our peer certificates must satisfy the
- ssl server policy. */
- bool use_server_policy = (ctx->protocolSide == kSSLClientSide);
- require(policies = SecPolicyCreateSSL(use_server_policy, peerDomainName), errOut);
-
- require_noerr(status = SecTrustCreateWithCertificates(certChain, policies,
- &trust), errOut);
-
- /* If we are the client, let's see if we have OCSP responses and SCTs in the TLS handshake */
- if(ctx->protocolSide == kSSLClientSide) {
- const tls_buffer_list_t *sct_list = tls_handshake_get_peer_sct_list(ctx->hdsk);
- const tls_buffer *ocsp_response = tls_handshake_get_peer_ocsp_response(ctx->hdsk);
-
- if(ocsp_response) {
- CFDataRef responseData = CFDataCreate(kCFAllocatorDefault, ocsp_response->data, ocsp_response->length);
- status = SecTrustSetOCSPResponse(trust, responseData);
- CFReleaseSafe(responseData);
- require_noerr(status, errOut);
- }
-
- if(sct_list) {
- CFArrayRef sctArray = NULL;
- require_noerr(status = sslCreateCFArrayFromList(sct_list, &sctArray), errOut);
-#if TARGET_OS_IPHONE
- status = SecTrustSetSignedCertificateTimestamps(trust, sctArray);
-#else
- status = noErr;
-#endif
- CFReleaseSafe(sctArray);
- require_noerr(status, errOut);
- }
- }
+ require_noerr(status = tls_helper_create_peer_trust(ctx->hdsk, ctx->protocolSide==kSSLServerSide, &trust), errOut);
/* If we have trustedAnchors we set them here. */
- if (ctx->trustedCerts) {
- require_noerr(status = SecTrustSetAnchorCertificates(trust,
- ctx->trustedCerts), errOut);
- require_noerr(status = SecTrustSetAnchorCertificatesOnly(trust,
- ctx->trustedCertsOnly), errOut);
+ if (trust && ctx->trustedCerts) {
+ require_noerr(status = SecTrustSetAnchorCertificates(trust, ctx->trustedCerts), errOut);
+ require_noerr(status = SecTrustSetAnchorCertificatesOnly(trust, ctx->trustedCertsOnly), errOut);
}
status = errSecSuccess;
errOut:
- CFReleaseSafe(peerDomainName);
- CFReleaseSafe(policies);
-
- *pTrust = trust;
+ if(status != noErr) {
+ CFReleaseSafe(trust);
+ *pTrust = NULL;
+ } else {
+ *pTrust = trust;
+ }
return status;
}
+#if !TARGET_OS_IPHONE
/* Return the first certificate reference from the supplied array
* whose data matches the given certificate, or NULL if none match.
*/
return NULL;
}
- CFDataRef certData = SecCertificateCopyData(certRef);
- if (certData) {
- CFIndex idx, count = CFArrayGetCount(certArray);
- for(idx=0; idx<count; idx++) {
- SecCertificateRef aCert = (SecCertificateRef)CFArrayGetValueAtIndex(certArray, idx);
- CFDataRef aData = SecCertificateCopyData(aCert);
- if (aData && CFEqual(aData, certData)) {
- matchedCert = aCert;
- }
- CFReleaseSafe(aData);
- if (matchedCert)
- break;
- }
- CFReleaseSafe(certData);
- }
+ CFIndex idx, count = CFArrayGetCount(certArray);
+ for (idx = 0; idx < count; idx++) {
+ SecCertificateRef otherCert = (SecCertificateRef) CFArrayGetValueAtIndex(certArray, idx);
+ if (CFEqual(certRef, otherCert)) {
+ matchedCert = otherCert;
+ break;
+ }
+ }
return matchedCert;
}
+#endif
/*
* Verify a chain of DER-encoded certs.
*/
static OSStatus sslVerifyCertChain(
- SSLContext *ctx,
- CFArrayRef certChain)
+ SSLContext *ctx)
{
OSStatus status;
SecTrustRef trust = NULL;
/* renegotiate - start with a new SecTrustRef */
CFReleaseNull(ctx->peerSecTrust);
- if(certChain==NULL) {
+ /* on failure, we always return trust==NULL, so we don't check the returned status here */
+ sslCreateSecTrust(ctx, &trust);
+
+ if(trust==NULL) {
if(ctx->protocolSide == kSSLClientSide) {
/* No cert chain is always a trust failure on the server side */
status = errSSLXCertChainInvalid;
goto errOut;
}
- status = sslCreateSecTrust(ctx, certChain, &trust);
if (!ctx->enableCertVerify) {
/* trivial case, this is caller's responsibility */
}
SecTrustResultType secTrustResult;
- require_noerr(status = SecTrustEvaluate(trust, &secTrustResult), errOut);
+ require_noerr(status = SecTrustEvaluate(trust, &secTrustResult), errOut);
+
switch (secTrustResult) {
case kSecTrustResultUnspecified:
/* cert chain valid, no special UserTrust assignments */
status = errSecSuccess;
break;
case kSecTrustResultDeny:
- case kSecTrustResultConfirm:
case kSecTrustResultRecoverableTrustFailure:
default:
if(ctx->allowAnyRoot) {
status = errSecSuccess;
}
else {
+#if !TARGET_OS_IPHONE
/*
* If the caller provided a list of trusted leaf certs, check them here
*/
- if(ctx->trustedLeafCerts) {
- if (sslGetMatchingCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(certChain, 0),
- ctx->trustedLeafCerts)) {
- status = errSecSuccess;
- goto errOut;
- }
- }
+ if(ctx->trustedLeafCerts) {
+ if (sslGetMatchingCertInArray(SecTrustGetCertificateAtIndex(trust, 0),
+ ctx->trustedLeafCerts)) {
+ status = errSecSuccess;
+ goto errOut;
+ }
+ }
+#endif
status = errSSLXCertChainInvalid;
}
/* Do we really need to return things like:
return status;
}
-/* Extract public SecKeyRef from Certificate Chain */
-static
-int sslCopyPeerPubKey(const SSLCertificate *certchain,
- SecKeyRef *pubKey)
-{
- int err;
- check(pubKey);
- SecTrustRef trust = NULL;
- const SSLCertificate *cert;
- CFMutableArrayRef certArray = NULL;
- CFDataRef certData = NULL;
- SecCertificateRef cfCert = NULL;
-
- err = errSSLInternal;
-
- certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- cert = certchain;
- while(cert) {
- require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out);
- require_action((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out, err=errSSLBadCert);
- CFArrayAppendValue(certArray, cfCert);
- CFReleaseNull(cfCert);
- CFReleaseNull(certData);
- cert=cert->next;
- }
-
- require_noerr((err=SecTrustCreateWithCertificates(certArray, NULL, &trust)), out);
- SecKeyRef key = SecTrustCopyPublicKey(trust);
- require_action(key, out, err=errSSLBadCert);
-
- *pubKey = key;
-
- err = errSecSuccess;
-
-out:
- CFReleaseSafe(certData);
- CFReleaseSafe(cfCert);
- CFReleaseSafe(trust);
- CFReleaseSafe(certArray);
-
- return err;
-}
-
-/* Extract the pubkey from a cert chain, and send it to the tls_handshake context */
-int tls_set_peer_pubkey(SSLContext *ctx)
-{
- int err;
- CFIndex algId;
- SecKeyRef pubkey = NULL;
- CFDataRef modulus = NULL;
- CFDataRef exponent = NULL;
- CFDataRef ecpubdata = NULL;
- const SSLCertificate *certchain = NULL;
-
- certchain = tls_handshake_get_peer_certificates(ctx->hdsk);
- CFReleaseNull(ctx->peerCert);
-
- /* If there is no certchain, then we don't need to set the pubkey in coreTLS */
- /* We should really set it to "NULL" or none, but we need to fix the coreTLS API */
- /* See: <rdar://problem/19723662> coreTLS: replace tls_handshake_set_peer_rsa_public_key and tls_handshake_set_peer_ec_public_key with a common function */
- if(!certchain)
- return 0;
-
- ctx->peerCert = tls_get_peer_certs(certchain);
-
-#if 0
- { /* dump certs */
- int i=0;
- int j;
- const SSLCertificate *tmp = certchain;
- while(tmp) {
- printf("cert%d[] = {", i);
- for(j=0; j<tmp->derCert.length; j++) {
- if((j&0xf)==0)
- printf("\n");
- printf("0x%02x, ", tmp->derCert.data[j]);
- }
- printf("}\n");
- tmp=tmp->next;
- i++;
- }
- }
-#endif
-
- require_noerr((err=sslCopyPeerPubKey(certchain, &pubkey)), errOut);
-
-#if TARGET_OS_IPHONE
- algId = SecKeyGetAlgorithmID(pubkey);
-#else
- algId = SecKeyGetAlgorithmId(pubkey);
-#endif
-
- err = errSSLCrypto;
-
- switch(algId) {
- case kSecRSAAlgorithmID:
- {
- require((modulus = SecKeyCopyModulus(pubkey)), errOut);
- require((exponent = SecKeyCopyExponent(pubkey)), errOut);
-
- tls_buffer mod;
- tls_buffer exp;
-
- mod.data = (uint8_t *)CFDataGetBytePtr(modulus);
- mod.length = CFDataGetLength(modulus);
-
- exp.data = (uint8_t *)CFDataGetBytePtr(exponent);
- exp.length = CFDataGetLength(exponent);
-
- err = tls_handshake_set_peer_rsa_public_key(ctx->hdsk, &mod, &exp);
- break;
- }
- case kSecECDSAAlgorithmID:
- {
- tls_named_curve curve = SecECKeyGetNamedCurve(pubkey);
- require((ecpubdata = SecECKeyCopyPublicBits(pubkey)), errOut);
-
- tls_buffer pubdata;
- pubdata.data = (uint8_t *)CFDataGetBytePtr(ecpubdata);
- pubdata.length = CFDataGetLength(ecpubdata);
-
- err = tls_handshake_set_peer_ec_public_key(ctx->hdsk, curve, &pubdata);
-
- break;
- }
- default:
- break;
- }
-
-errOut:
- CFReleaseSafe(pubkey);
- CFReleaseSafe(modulus);
- CFReleaseSafe(exponent);
- CFReleaseSafe(ecpubdata);
-
- return err;
-}
-
-/* Convert cert in DER format into an CFArray of SecCertificateRef */
-CFArrayRef
-tls_get_peer_certs(const SSLCertificate *certs)
-{
- const SSLCertificate *cert;
-
- CFMutableArrayRef certArray = NULL;
- CFDataRef certData = NULL;
- SecCertificateRef cfCert = NULL;
-
- certArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
- require(certArray, out);
- cert = certs;
- while(cert) {
- require((certData = CFDataCreate(kCFAllocatorDefault, cert->derCert.data, cert->derCert.length)), out);
- require((cfCert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)), out);
- CFArrayAppendValue(certArray, cfCert);
- CFReleaseNull(cfCert);
- CFReleaseNull(certData);
- cert=cert->next;
- }
-
- return certArray;
-
-out:
- CFReleaseNull(cfCert);
- CFReleaseNull(certData);
- CFReleaseNull(certArray);
- return NULL;
-}
-
int
tls_verify_peer_cert(SSLContext *ctx)
{
call to tls_handshake_set_peer_trust(). In some case a verification failure here is
normal, for example if there is no cert (eg: PSK and Anon DH ciphersuites) */
- st = sslVerifyCertChain(ctx, ctx->peerCert);
+ st = sslVerifyCertChain(ctx);
tls_handshake_trust_t trust;
switch (st) {
case errSecSuccess:
if (ctx->breakOnClientAuth) {
err = errSSLClientAuthCompleted;
}
- } else if(ctx->peerCert) {
+ } else if(ctx->peerSecTrust) {
/*
* Schedule return to the caller to verify the server's identity.
* This will only return if a server cert was sent. In other cases
}
/* Check the alg of our signing key. */
- CFIndex keyAlg = sslPrivKeyGetAlgorithmID(ctx->signingPrivKeyRef);
+ CFIndex keyAlg = SecKeyGetAlgorithmId(ctx->signingPrivKeyRef);
if (requireAlg != keyAlg) {
sslErrorLog("sslVerifySelectedCipher: signing key alg mismatch\n");
return errSSLBadConfiguration;