#include "sslCrypto.h"
#include "sslContext.h"
#include "sslMemory.h"
#include "sslDebug.h"
#include <string.h>
#include <stdlib.h>
#include "utilities/simulatecrash_assert.h"
#include <Security/SecTrustPriv.h>
#include <Security/SecPolicy.h>
#include <Security/SecCertificate.h>
#include <AssertMacros.h>
#include "utilities/SecCFRelease.h"
#if TARGET_OS_IPHONE
#include <Security/SecRSAKey.h>
#include <Security/SecECKey.h>
#endif
#include <tls_helpers.h>
OSStatus
sslCreateSecTrust(
SSLContext *ctx,
SecTrustRef *pTrust)
{
OSStatus status = errSecAllocate;
SecTrustRef trust = NULL;
require_noerr(status = tls_helper_create_peer_trust(ctx->hdsk, ctx->protocolSide==kSSLServerSide, &trust), errOut);
if (trust && ctx->trustedCerts) {
require_noerr(status = SecTrustSetAnchorCertificates(trust, ctx->trustedCerts), errOut);
require_noerr(status = SecTrustSetAnchorCertificatesOnly(trust, ctx->trustedCertsOnly), errOut);
}
status = errSecSuccess;
errOut:
if(status != noErr) {
CFReleaseSafe(trust);
*pTrust = NULL;
} else {
*pTrust = trust;
}
return status;
}
#if !TARGET_OS_IPHONE
static
SecCertificateRef
sslGetMatchingCertInArray(
SecCertificateRef certRef,
CFArrayRef certArray)
{
SecCertificateRef matchedCert = NULL;
if (certRef == NULL || certArray == NULL) {
return NULL;
}
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
static OSStatus sslVerifyCertChain(
SSLContext *ctx)
{
OSStatus status;
SecTrustRef trust = NULL;
CFReleaseNull(ctx->peerSecTrust);
sslCreateSecTrust(ctx, &trust);
if(trust==NULL) {
if(ctx->protocolSide == kSSLClientSide) {
status = errSSLXCertChainInvalid;
sslErrorLog("***Error: NULL server cert chain\n");
} else {
if(ctx->clientAuth == kAlwaysAuthenticate) {
sslErrorLog("***Error: NULL client cert chain\n");
status = errSSLXCertChainInvalid;
} else {
status = noErr;
}
}
goto errOut;
}
if (!ctx->enableCertVerify) {
status = errSecSuccess;
goto errOut;
}
SecTrustResultType secTrustResult;
require_noerr(status = SecTrustEvaluate(trust, &secTrustResult), errOut);
switch (secTrustResult) {
case kSecTrustResultUnspecified:
case kSecTrustResultProceed:
status = errSecSuccess;
break;
case kSecTrustResultDeny:
case kSecTrustResultRecoverableTrustFailure:
default:
if(ctx->allowAnyRoot) {
sslErrorLog("***Warning: accepting unverified cert chain\n");
status = errSecSuccess;
}
else {
#if !TARGET_OS_IPHONE
if(ctx->trustedLeafCerts) {
if (sslGetMatchingCertInArray(SecTrustGetCertificateAtIndex(trust, 0),
ctx->trustedLeafCerts)) {
status = errSecSuccess;
goto errOut;
}
}
#endif
status = errSSLXCertChainInvalid;
}
break;
}
errOut:
ctx->peerSecTrust = trust;
return status;
}
int
tls_verify_peer_cert(SSLContext *ctx)
{
int err = 0;
OSStatus st;
st = sslVerifyCertChain(ctx);
tls_handshake_trust_t trust;
switch (st) {
case errSecSuccess:
trust = tls_handshake_trust_ok;
break;
case errSSLUnknownRootCert:
case errSSLNoRootCert:
trust = tls_handshake_trust_unknown_root;
break;
case errSSLCertExpired:
case errSSLCertNotYetValid:
trust = tls_handshake_trust_cert_expired;
break;
case errSSLXCertChainInvalid:
default:
trust = tls_handshake_trust_cert_invalid;
break;
}
tls_handshake_set_peer_trust(ctx->hdsk, trust);
if(ctx->protocolSide == kSSLServerSide) {
if (ctx->breakOnClientAuth) {
err = errSSLClientAuthCompleted;
}
} else if(ctx->peerSecTrust) {
if (ctx->breakOnServerAuth) {
err = errSSLServerAuthCompleted;
}
}
return err;
}
#if 0
OSStatus sslVerifySelectedCipher(SSLContext *ctx)
{
if(ctx->protocolSide == kSSLClientSide) {
return errSecSuccess;
}
#if SSL_PAC_SERVER_ENABLE
if((ctx->masterSecretCallback != NULL) &&
(ctx->sessionTicket.data != NULL)) {
return errSecSuccess;
}
#endif
CFIndex requireAlg;
switch (ctx->selectedCipherSpecParams.keyExchangeMethod) {
case SSL_RSA:
case SSL_RSA_EXPORT:
case SSL_DH_RSA:
case SSL_DH_RSA_EXPORT:
case SSL_DHE_RSA:
case SSL_DHE_RSA_EXPORT:
requireAlg = kSecRSAAlgorithmID;
break;
case SSL_DHE_DSS:
case SSL_DHE_DSS_EXPORT:
case SSL_DH_DSS:
case SSL_DH_DSS_EXPORT:
requireAlg = kSecDSAAlgorithmID;
break;
case SSL_DH_anon:
case SSL_DH_anon_EXPORT:
case TLS_PSK:
requireAlg = kSecNullAlgorithmID;
break;
#if SSL_ECDSA_SERVER
case SSL_ECDHE_ECDSA:
case SSL_ECDHE_RSA:
case SSL_ECDH_ECDSA:
case SSL_ECDH_RSA:
case SSL_ECDH_anon:
requireAlg = kSecECDSAAlgorithmID;
break;
#endif
default:
assert(0);
sslErrorLog("sslVerifySelectedCipher: unknown key exchange method\n");
return errSSLInternal;
}
if(requireAlg == kSecNullAlgorithmID) {
return errSecSuccess;
}
if(ctx->signingPrivKeyRef == NULL) {
sslErrorLog("sslVerifySelectedCipher: no signing key\n");
return errSSLBadConfiguration;
}
CFIndex keyAlg = SecKeyGetAlgorithmId(ctx->signingPrivKeyRef);
if (requireAlg != keyAlg) {
sslErrorLog("sslVerifySelectedCipher: signing key alg mismatch\n");
return errSSLBadConfiguration;
}
return errSecSuccess;
}
#endif