/* * Verify a chain of DER-encoded certs. */ static OSStatus sslVerifyCertChain( SSLContext *ctx) { OSStatus status; SecTrustRef trust = NULL; /* renegotiate - start with a new SecTrustRef */ CFReleaseNull(ctx->peerSecTrust); /* 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; sslErrorLog("***Error: NULL server cert chain\n"); } else { /* No cert chain on the client side is ok unless using kAlwaysAuthenticate */ if(ctx->clientAuth == kAlwaysAuthenticate) { sslErrorLog("***Error: NULL client cert chain\n"); status = errSSLXCertChainInvalid; } else { status = noErr; } } goto errOut; } if (!ctx->enableCertVerify) { /* trivial case, this is caller's responsibility */ status = errSecSuccess; goto errOut; } SecTrustResultType secTrustResult; require_noerr(status = SecTrustEvaluate(trust, &secTrustResult), errOut); switch (secTrustResult) { case kSecTrustResultUnspecified: /* cert chain valid, no special UserTrust assignments */ case kSecTrustResultProceed: /* cert chain valid AND user explicitly trusts this */ status = errSecSuccess; break; case kSecTrustResultDeny: case kSecTrustResultConfirm: case kSecTrustResultRecoverableTrustFailure: default: if(ctx->allowAnyRoot) { sslErrorLog("***Warning: accepting unverified cert chain\n"); 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(SecTrustGetCertificateAtIndex(trust, 0), ctx->trustedLeafCerts)) { status = errSecSuccess; goto errOut; } } #endif status = errSSLXCertChainInvalid; } /* Do we really need to return things like: errSSLNoRootCert errSSLUnknownRootCert errSSLCertExpired errSSLCertNotYetValid errSSLHostNameMismatch for our client to see what went wrong, or should we just always return errSSLXCertChainInvalid when something is wrong? */ break; } errOut: ctx->peerSecTrust = trust; return status; }
/* * Verify a chain of DER-encoded certs. * Last cert in a chain is the leaf; this must also be present * in ctx->trustedCerts. * * If arePeerCerts is true, host name verification is enabled and we * save the resulting SecTrustRef in ctx->peerSecTrust. Otherwise * we're just validating our own certs; no host name checking and * peerSecTrust is transient. */ extern OSStatus sslVerifyCertChain( SSLContext *ctx, CFArrayRef certChain, bool arePeerCerts) { OSStatus status; SecTrustRef trust = NULL; assert(certChain); if (arePeerCerts) { /* renegotiate - start with a new SecTrustRef */ CFReleaseNull(ctx->peerSecTrust); } status = sslCreateSecTrust(ctx, certChain, arePeerCerts, &trust); if (!ctx->enableCertVerify) { /* trivial case, this is caller's responsibility */ status = noErr; goto errOut; } SecTrustResultType secTrustResult; require_noerr(status = SecTrustEvaluate(trust, &secTrustResult), errOut); switch (secTrustResult) { case kSecTrustResultUnspecified: /* cert chain valid, no special UserTrust assignments */ case kSecTrustResultProceed: /* cert chain valid AND user explicitly trusts this */ status = noErr; break; case kSecTrustResultDeny: case kSecTrustResultConfirm: case kSecTrustResultRecoverableTrustFailure: default: if(ctx->allowAnyRoot) { sslErrorLog("***Warning: accepting unverified cert chain\n"); status = noErr; } else { /* * 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 = noErr; goto errOut; } } status = errSSLXCertChainInvalid; } /* Do we really need to return things like: errSSLNoRootCert errSSLUnknownRootCert errSSLCertExpired errSSLCertNotYetValid errSSLHostNameMismatch for our client to see what went wrong, or should we just always return errSSLXCertChainInvalid when something is wrong? */ break; } errOut: if (arePeerCerts) ctx->peerSecTrust = trust; else CFReleaseSafe(trust); return status; }