diff --git a/raddb/eap.conf b/raddb/eap.conf index 073dd41..3e07c38 100644 --- a/raddb/eap.conf +++ b/raddb/eap.conf @@ -259,6 +259,16 @@ # # 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/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c index 28049cb..40d6002 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c @@ -122,6 +122,8 @@ static CONF_PARSER module_config[] = { offsetof(EAP_TLS_CONF, cipher_list), NULL, NULL}, { "check_cert_issuer", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, check_cert_issuer), NULL, NULL}, + { "check_cert_san", PW_TYPE_STRING_PTR, + offsetof(EAP_TLS_CONF, check_cert_san), NULL, NULL}, { "make_cert_command", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, make_cert_command), NULL, NULL}, @@ -465,6 +467,7 @@ static 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]; EAP_HANDLER *handler = NULL; X509 *client_cert; @@ -662,6 +665,48 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) } /* + * Check the SAN against the xlat'ed value. + */ + if (my_ok && conf->check_cert_san) { + if (!radius_xlat(san_str, sizeof(san_str), conf->check_cert_san, handler->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 * previous checks passed. diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h index d60b1b6..e8b70ba 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h @@ -60,6 +60,7 @@ typedef struct eap_tls_conf { char *check_cert_cn; char *cipher_list; char *check_cert_issuer; + char *check_cert_san; int session_cache_enable; int session_timeout;