diff --git a/raddb/modules/eap b/raddb/modules/eap index 26885e4..1b38ea8 100644 --- a/raddb/modules/eap +++ b/raddb/modules/eap @@ -252,7 +252,17 @@ # can be done via any mechanism you choose. # # check_cert_cn = %{User-Name} - # + + # + # If check_cert_san is set, the value will + # be xlat'ed and checked against each of the + # elements within the subjectAltName attribute. + # If subjectAltName is missing, or no elements + # match the value, the certificate verification + # will fail. + # + # check_cert_san = %{User-Name} + # Set this option to specify the allowed # TLS cipher suites. The format is listed # in "man 1 ciphers". diff --git a/raddb/sites-available/tls b/raddb/sites-available/tls index 8cba28f..a3a3e51 100644 --- a/raddb/sites-available/tls +++ b/raddb/sites-available/tls @@ -122,7 +122,17 @@ listen { # can be done via any mechanism you choose. # # check_cert_cn = %{User-Name} - # + + # + # If check_cert_san is set, the value will + # be xlat'ed and checked against each of the + # elements within the subjectAltName attribute. + # If subjectAltName is missing, or no elements + # match the value, the certificate verification + # will fail. + # + # check_cert_san = %{User-Name} + # Set this option to specify the allowed # TLS cipher suites. The format is listed # in "man 1 ciphers". diff --git a/src/include/tls.h b/src/include/tls.h index 333698a..f777938 100644 --- a/src/include/tls.h +++ b/src/include/tls.h @@ -355,6 +355,7 @@ struct fr_tls_server_conf_t { char *check_cert_cn; char *cipher_list; char *check_cert_issuer; + char *check_cert_san; int session_cache_enable; int session_timeout; diff --git a/src/main/tls.c b/src/main/tls.c index 10caec4..19fe10e 100644 --- a/src/main/tls.c +++ b/src/main/tls.c @@ -779,6 +779,8 @@ static CONF_PARSER tls_server_config[] = { offsetof(fr_tls_server_conf_t, cipher_list), NULL, NULL}, { "check_cert_issuer", PW_TYPE_STRING_PTR, offsetof(fr_tls_server_conf_t, check_cert_issuer), NULL, NULL}, + { "check_cert_san", PW_TYPE_STRING_PTR, + offsetof(fr_tls_server_conf_t, check_cert_san), NULL, NULL}, { "make_cert_command", PW_TYPE_STRING_PTR, offsetof(fr_tls_server_conf_t, make_cert_command), NULL, NULL}, { "require_client_cert", PW_TYPE_BOOLEAN, @@ -1179,6 +1181,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx) char issuer[1024]; /* Used for the issuer name */ char common_name[1024]; char cn_str[1024]; + char san_str[1024]; char buf[64]; X509 *client_cert; SSL *ssl; @@ -1391,6 +1394,49 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx) my_ok = 0; } + /* + * Check the subjectAltName against the xlat'ed value. + */ + if (my_ok && conf->check_cert_san) { + if (!radius_xlat(san_str, sizeof(san_str), conf->check_cert_san, request, NULL)) { + radlog(L_ERR, "rlm_eap_tls (%s): xlat failed.", + conf->check_cert_san); + /* if this fails, fail the verification */ + my_ok = 0; + } else { + STACK_OF(GENERAL_NAME) *names; + BIO *bio; + if ((names = X509_get_ext_d2i(client_cert, + NID_subject_alt_name, NULL, NULL)) && + (bio = BIO_new(BIO_s_mem()))) { + GENERAL_NAME *name; + char bio_str[1024]; + int found = 0, i, n; + + for (i = 0; i < sk_GENERAL_NAME_num(names); i++) { + name = sk_GENERAL_NAME_value(names, i); + ASN1_STRING_print_ex(bio, name->d.ia5, ASN1_STRFLGS_ESC_CTRL|ASN1_STRFLGS_UTF8_CONVERT); + n = BIO_pending(bio); + if (n > 0 && n < sizeof(bio_str)) { + n = BIO_read(bio, bio_str, n); + bio_str[n] = 0; + RDEBUG2("checking certificate subjectAltName (%s) with xlat'ed value (%s)", bio_str, san_str); + if (!strcmp(san_str, bio_str)) { + found = 1; + } + } + } + BIO_free(bio); + + if (!found) { + radlog(L_AUTH, "rlm_eap_tls: Certificate subjectAltName does not match specified value (%s)!", san_str); + my_ok = 0; + } + } + } + } + + /* * If the conf tells us to, check the CN in the * cert against xlat'ed value, but only if the