Add AAA server domain name suffix matching constraint

The new domain_suffix_match (and domain_suffix_match2 for Phase 2
EAP-TLS) can now be used to specify an additional constraint for the
server certificate domain name. If set, one of the dNSName values (or if
no dNSName is present, one of the commonName values) in the certificate
must have a suffix match with the specified value. Suffix match is done
based on full domain name labels, i.e., "example.com" matches
"test.example.com" but not "test-example.com".

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2013-10-06 18:02:16 -07:00 committed by Jouni Malinen
parent be7963b3c2
commit 01f809c7db
6 changed files with 154 additions and 5 deletions

View file

@ -79,7 +79,7 @@ struct tls_connection {
ENGINE *engine; /* functional reference to the engine */
EVP_PKEY *private_key; /* the private key if using engine */
#endif /* OPENSSL_NO_ENGINE */
char *subject_match, *altsubject_match;
char *subject_match, *altsubject_match, *suffix_match;
int read_alerts, write_alerts, failed;
tls_session_ticket_cb session_ticket_cb;
@ -1023,6 +1023,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
tls_engine_deinit(conn);
os_free(conn->subject_match);
os_free(conn->altsubject_match);
os_free(conn->suffix_match);
os_free(conn->session_ticket);
os_free(conn);
}
@ -1113,6 +1114,97 @@ static int tls_match_altsubject(X509 *cert, const char *match)
}
static int domain_suffix_match(const u8 *val, size_t len, const char *match)
{
size_t i, match_len;
/* Check for embedded nuls that could mess up suffix matching */
for (i = 0; i < len; i++) {
if (val[i] == '\0') {
wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject");
return 0;
}
}
match_len = os_strlen(match);
if (match_len > len)
return 0;
if (os_strncasecmp((const char *) val + len - match_len, match,
match_len) != 0)
return 0; /* no match */
if (match_len == len)
return 1; /* exact match */
if (val[len - match_len - 1] == '.')
return 1; /* full label match completes suffix match */
wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
return 0;
}
static int tls_match_suffix(X509 *cert, const char *match)
{
GENERAL_NAME *gen;
void *ext;
int i;
int dns_name = 0;
X509_NAME *name;
wpa_printf(MSG_DEBUG, "TLS: Match domain against suffix %s", match);
ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
gen = sk_GENERAL_NAME_value(ext, i);
if (gen->type != GEN_DNS)
continue;
dns_name++;
wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
gen->d.dNSName->data,
gen->d.dNSName->length);
if (domain_suffix_match(gen->d.dNSName->data,
gen->d.dNSName->length, match) == 1) {
wpa_printf(MSG_DEBUG, "TLS: Suffix match in dNSName found");
return 1;
}
}
if (dns_name) {
wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
return 0;
}
name = X509_get_subject_name(cert);
i = -1;
for (;;) {
X509_NAME_ENTRY *e;
ASN1_STRING *cn;
i = X509_NAME_get_index_by_NID(name, NID_commonName, i);
if (i == -1)
break;
e = X509_NAME_get_entry(name, i);
if (e == NULL)
continue;
cn = X509_NAME_ENTRY_get_data(e);
if (cn == NULL)
continue;
wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
cn->data, cn->length);
if (domain_suffix_match(cn->data, cn->length, match) == 1) {
wpa_printf(MSG_DEBUG, "TLS: Suffix match in commonName found");
return 1;
}
}
wpa_printf(MSG_DEBUG, "TLS: No CommonName suffix match found");
return 0;
}
static enum tls_fail_reason openssl_tls_fail_reason(int err)
{
switch (err) {
@ -1241,7 +1333,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
SSL *ssl;
struct tls_connection *conn;
struct tls_context *context;
char *match, *altmatch;
char *match, *altmatch, *suffix_match;
const char *err_str;
err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
@ -1263,6 +1355,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
context = conn->context;
match = conn->subject_match;
altmatch = conn->altsubject_match;
suffix_match = conn->suffix_match;
if (!preverify_ok && !conn->ca_cert_verify)
preverify_ok = 1;
@ -1331,6 +1424,14 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"AltSubject mismatch",
TLS_FAIL_ALTSUBJECT_MISMATCH);
} else if (depth == 0 && suffix_match &&
!tls_match_suffix(err_cert, suffix_match)) {
wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
suffix_match);
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"Domain suffix mismatch",
TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
} else
openssl_tls_cert_event(conn, err_cert, depth, buf);
@ -1606,7 +1707,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl)
static int tls_connection_set_subject_match(struct tls_connection *conn,
const char *subject_match,
const char *altsubject_match)
const char *altsubject_match,
const char *suffix_match)
{
os_free(conn->subject_match);
conn->subject_match = NULL;
@ -1624,6 +1726,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn,
return -1;
}
os_free(conn->suffix_match);
conn->suffix_match = NULL;
if (suffix_match) {
conn->suffix_match = os_strdup(suffix_match);
if (conn->suffix_match == NULL)
return -1;
}
return 0;
}
@ -2981,7 +3091,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
}
if (tls_connection_set_subject_match(conn,
params->subject_match,
params->altsubject_match))
params->altsubject_match,
params->suffix_match))
return -1;
if (params->engine && params->ca_cert_id) {