in c/src/tls/openssl.c [619:700]
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
if (!preverify_ok || X509_STORE_CTX_get_error_depth(ctx) != 0)
// already failed, or not at peer cert in chain
return preverify_ok;
X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
SSL *ssn = (SSL *) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
if (!ssn) {
ssl_log(NULL, PN_LEVEL_ERROR, "Error: unexpected error - SSL session info not available for peer verify!");
return 0; // fail connection
}
pn_tls_t *ssl = (pn_tls_t *)SSL_get_ex_data(ssn, tls_ex_data_index);
if (!ssl) {
// TODO: replace: ssl_log(transport, PN_LEVEL_ERROR, "Error: unexpected error - SSL context info not available for peer verify!");
return 0; // fail connection
}
if (ssl->verify_mode != PN_TLS_VERIFY_PEER_NAME) return preverify_ok;
if (!ssl->peer_hostname) {
ssl_log(NULL, PN_LEVEL_ERROR, "Error: configuration error: PN_TLS_VERIFY_PEER_NAME configured, but no peer hostname set!");
return 0; // fail connection
}
ssl_log(NULL, PN_LEVEL_TRACE, "Checking identifying name in peer cert against '%s'", ssl->peer_hostname);
bool matched = false;
/* first check any SubjectAltName entries, as per RFC2818 */
GENERAL_NAMES *sans = (GENERAL_NAMES *) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
if (sans) {
int name_ct = sk_GENERAL_NAME_num( sans );
int i;
for (i = 0; !matched && i < name_ct; ++i) {
GENERAL_NAME *name = sk_GENERAL_NAME_value( sans, i );
if (name->type == GEN_DNS) {
ASN1_STRING *asn1 = name->d.dNSName;
if (asn1 && asn1->data && asn1->length) {
unsigned char *str;
int len = ASN1_STRING_to_UTF8( &str, asn1 );
if (len >= 0) {
ssl_log(NULL, PN_LEVEL_TRACE, "SubjectAltName (dns) from peer cert = '%.*s'", len, str );
matched = match_dns_pattern( ssl->peer_hostname, (const char *)str, len );
OPENSSL_free( str );
}
}
}
}
GENERAL_NAMES_free( sans );
}
/* if no general names match, try the CommonName from the subject */
X509_NAME *name = X509_get_subject_name(cert);
int i = -1;
while (!matched && (i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) {
X509_NAME_ENTRY *ne = X509_NAME_get_entry(name, i);
ASN1_STRING *name_asn1 = X509_NAME_ENTRY_get_data(ne);
if (name_asn1) {
unsigned char *str;
int len = ASN1_STRING_to_UTF8( &str, name_asn1);
if (len >= 0) {
ssl_log(NULL, PN_LEVEL_TRACE, "commonName from peer cert = '%.*s'", len, str);
matched = match_dns_pattern( ssl->peer_hostname, (const char *)str, len );
OPENSSL_free(str);
}
}
}
if (!matched) {
ssl_log(NULL, PN_LEVEL_ERROR, "Error: no name matching %s found in peer cert - rejecting handshake.",
ssl->peer_hostname);
preverify_ok = 0;
#ifdef X509_V_ERR_APPLICATION_VERIFICATION
X509_STORE_CTX_set_error( ctx, X509_V_ERR_APPLICATION_VERIFICATION );
#endif
} else {
ssl_log(NULL, PN_LEVEL_TRACE, "Name from peer cert matched - peer is valid.");
}
return preverify_ok;
}