in source/windows/secure_channel_tls_handler.c [180:349]
static int s_manually_verify_peer_cert(struct aws_channel_handler *handler) {
AWS_LOGF_DEBUG(
AWS_LS_IO_TLS,
"id=%p: manually verifying certifcate chain because a custom CA is configured.",
(void *)handler);
struct secure_channel_handler *sc_handler = handler->impl;
int result = AWS_OP_ERR;
CERT_CONTEXT *peer_certificate = NULL;
HCERTCHAINENGINE engine = NULL;
CERT_CHAIN_CONTEXT *cert_chain_ctx = NULL;
/* get the peer's certificate so we can validate it.*/
SECURITY_STATUS status =
QueryContextAttributes(&sc_handler->sec_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &peer_certificate);
if (status != SEC_E_OK || !peer_certificate) {
AWS_LOGF_ERROR(
AWS_LS_IO_TLS,
"id=%p: failed to load peer's certificate with SECURITY_STATUS %d",
(void *)handler,
(int)status);
return AWS_OP_ERR;
}
/* this next bit scours the custom trust store to try and load a chain to verify
the leaf certificate against. */
CERT_CHAIN_ENGINE_CONFIG engine_config;
AWS_ZERO_STRUCT(engine_config);
engine_config.cbSize = sizeof(engine_config);
engine_config.hExclusiveRoot = sc_handler->custom_ca_store;
if (!CertCreateCertificateChainEngine(&engine_config, &engine)) {
AWS_LOGF_ERROR(
AWS_LS_IO_TLS,
"id=%p: failed to load a certificate chain engine with SECURITY_STATUS %d. "
"Most likely, the configured CA is corrupted.",
(void *)handler,
(int)status);
goto done;
}
/*
* TODO: Investigate CRL options further on a per-platform basis. Add control APIs if appropriate.
*/
DWORD get_chain_flags = 0;
/* mimic chromium here since we intend for this to be used generally */
const LPCSTR usage_identifiers[] = {
szOID_PKIX_KP_SERVER_AUTH,
szOID_SERVER_GATED_CRYPTO,
szOID_SGC_NETSCAPE,
};
CERT_CHAIN_PARA chain_params;
AWS_ZERO_STRUCT(chain_params);
chain_params.cbSize = sizeof(chain_params);
chain_params.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
chain_params.RequestedUsage.Usage.cUsageIdentifier = AWS_ARRAY_SIZE(usage_identifiers);
chain_params.RequestedUsage.Usage.rgpszUsageIdentifier = (LPSTR *)usage_identifiers;
if (!CertGetCertificateChain(
engine,
peer_certificate,
NULL,
peer_certificate->hCertStore,
&chain_params,
get_chain_flags,
NULL,
&cert_chain_ctx)) {
AWS_LOGF_ERROR(
AWS_LS_IO_TLS,
"id=%p: unable to find certificate in chain with SECURITY_STATUS %d.",
(void *)handler,
(int)status);
goto done;
}
struct aws_byte_buf host = aws_tls_handler_server_name(handler);
if (host.len > MAX_HOST_LENGTH) {
AWS_LOGF_ERROR(AWS_LS_IO_TLS, "id=%p: host name too long (%d).", (void *)handler, (int)host.len);
goto done;
}
wchar_t whost[MAX_HOST_LENGTH + 1];
AWS_ZERO_ARRAY(whost);
int converted = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, (const char *)host.buffer, (int)host.len, whost, AWS_ARRAY_SIZE(whost));
if ((size_t)converted != host.len) {
AWS_LOGF_ERROR(
AWS_LS_IO_TLS,
"id=%p: unable to convert host to wstr, %d -> %d, with last error 0x%x.",
(void *)handler,
(int)host.len,
(int)converted,
(int)GetLastError());
goto done;
}
/* check if the chain was trusted */
LPCSTR policyiod = CERT_CHAIN_POLICY_SSL;
SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslpolicy;
AWS_ZERO_STRUCT(sslpolicy);
sslpolicy.cbSize = sizeof(sslpolicy);
sslpolicy.dwAuthType = AUTHTYPE_SERVER;
sslpolicy.fdwChecks = 0;
sslpolicy.pwszServerName = whost;
CERT_CHAIN_POLICY_PARA policypara;
AWS_ZERO_STRUCT(policypara);
policypara.cbSize = sizeof(policypara);
policypara.dwFlags = 0;
policypara.pvExtraPolicyPara = &sslpolicy;
CERT_CHAIN_POLICY_STATUS policystatus;
AWS_ZERO_STRUCT(policystatus);
policystatus.cbSize = sizeof(policystatus);
if (!CertVerifyCertificateChainPolicy(policyiod, cert_chain_ctx, &policypara, &policystatus)) {
int error = GetLastError();
AWS_LOGF_ERROR(
AWS_LS_IO_TLS, "id=%p: CertVerifyCertificateChainPolicy() failed, error 0x%x", (void *)handler, (int)error);
goto done;
}
if (policystatus.dwError) {
AWS_LOGF_ERROR(
AWS_LS_IO_TLS,
"id=%p: certificate verification failed, error 0x%x",
(void *)handler,
(int)policystatus.dwError);
goto done;
}
/* if the chain was trusted, then we're good to go, if it was not
we bail out. */
CERT_SIMPLE_CHAIN *simple_chain = cert_chain_ctx->rgpChain[0];
DWORD trust_mask = ~(DWORD)CERT_TRUST_IS_NOT_TIME_NESTED;
trust_mask &= simple_chain->TrustStatus.dwErrorStatus;
if (trust_mask != 0) {
AWS_LOGF_ERROR(
AWS_LS_IO_TLS,
"id=%p: peer certificate is un-trusted with SECURITY_STATUS %d.",
(void *)handler,
(int)trust_mask);
goto done;
}
AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "id=%p: peer certificate is trusted.", (void *)handler);
result = AWS_OP_SUCCESS;
done:
if (cert_chain_ctx != NULL) {
CertFreeCertificateChain(cert_chain_ctx);
}
if (engine != NULL) {
CertFreeCertificateChainEngine(engine);
}
if (peer_certificate != NULL) {
CertFreeCertificateContext(peer_certificate);
}
return result;
}