diff --git a/ChangeLog b/ChangeLog index 3522d9b..f3013af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ - Rewrite whitelist mode - Autodetect status-server capability of servers - Minimalistic status-server + - Explicit SubjectAltName:DNS and :IP match on certificates Misc: - No longer require docbook2x tools, but include plain manpages diff --git a/radsecproxy.conf.5 b/radsecproxy.conf.5 index 827f50d..32e4d41 100644 --- a/radsecproxy.conf.5 +++ b/radsecproxy.conf.5 @@ -350,10 +350,11 @@ of the configured clients (in the order they are defined), to determine which this might mask clients defined later, which then will never be matched. In the case of TLS/DTLS, the name of the client must match the FQDN or IP -address in the client certificate. Note that this is not required when the -client name is an IP prefix. If overlapping clients are defined (see section -above), they will be searched for matching \fBMatchCertificateAttribute\fR, but -they must reference the same tls block. +address in the client certificate (CN or SubectAltName:DNS or SubjectAltName:IP +respectively). Note that this is not required when the client name is an IP +prefix. If overlapping clients are defined (see section above), they will be +searched for matching \fBMatchCertificateAttribute\fR, but they must reference +the same tls block. The allowed options in a client block are: @@ -412,11 +413,13 @@ For a TLS/DTLS client, disable the default behaviour of matching CN or SubjectAltName against the specified hostname or IP address. .RE -\fBMatchCertificateAttribute ((\fR CN \fB|\fR SubjectAltName:URI \fB) :\fR/\fIregexp\fR/\fB )\fR +\fBmatchCertificateAttribute (\fR CN \fB|\fR SubjectAltName:URI \fB|\fR SubjectAltName:DNS \fB) :\fR/\fIregexp\fR/ +.br +\fBMatchCertificateAttribute \fRSubjectAltName:IP:\fIaddress\fR .RS -Perform additional validation of certificate attributes. Currently only matching -of CN and SubjectAltName type URI is supported. Note that currently this option -can only be specified once in a client block. +Perform additional validation of certificate attributes. Currently matching +of CN and SubjectAltName types URI DNS and IP is supported. Note that currently this +option can only be specified once in a client block. .RE .BI "DuplicateInterval " seconds @@ -608,7 +611,9 @@ block. The details are not repeated here. Please refer to the definitions in the .br .BR "CertificateNameCheck (" on | off ) .br -\fBmatchCertificateAttribute ((\fR CN \fB|\fR SubjectAltName:URI \fB) :\fR/\fIregexp\fR/\fB )\fR +\fBmatchCertificateAttribute (\fR CN \fB|\fR SubjectAltName:URI \fB|\fR SubjectAltName:DNS \fB) :\fR/\fIregexp\fR/ +.br +\fBMatchCertificateAttribute \fRSubjectAltName:IP:\fIaddress\fR .br .BR "AddTTL " 1-255 .br diff --git a/radsecproxy.h b/radsecproxy.h index f660080..8dfd064 100644 --- a/radsecproxy.h +++ b/radsecproxy.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "list.h" #include "radmsg.h" #include "gconfig.h" @@ -150,6 +151,9 @@ struct clsrvconf { char *matchcertattr; regex_t *certcnregex; regex_t *certuriregex; + regex_t *certdnsregex; + in6_addr_t certipmatch; + int certipmatchaf; char *confrewritein; char *confrewriteout; char *confrewriteusername; diff --git a/tlscommon.c b/tlscommon.c index 7362309..4522942 100644 --- a/tlscommon.c +++ b/tlscommon.c @@ -709,6 +709,7 @@ int certnamecheck(X509 *cert, struct list *hostports) { int verifyconfcert(X509 *cert, struct clsrvconf *conf) { char *subject; + char addrbuf[INET6_ADDRSTRLEN]; int ok = 1; subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); @@ -734,6 +735,20 @@ int verifyconfcert(X509 *cert, struct clsrvconf *conf) { ok = 0; } } + if (conf->certdnsregex) { + debug(DBG_DBG, "verifyconfcert: matching subjectaltname DNS regex %s", conf->matchcertattr); + if (subjectaltnameregexp(cert, GEN_DNS, NULL, conf->certdnsregex) < 1) { + debug(DBG_WARN, "verifyconfcert: subjectaltname DNS not matching regex for host %s (%s)", conf->name, subject); + ok = 0; + } + } + if (conf->certipmatchaf) { + debug(DBG_DBG, "verifyconfcert: matching subjectaltname IP %s", inet_ntop(conf->certipmatchaf, &conf->certipmatch, addrbuf, INET6_ADDRSTRLEN)); + if (subjectaltnameaddr(cert, conf->certipmatchaf, &conf->certipmatch) < 1) { + debug(DBG_WARN, "verifyconfcert: subjectaltname IP not matching regex for host %s (%s)", conf->name, subject); + ok = 0; + } + } free(subject); return ok; } @@ -814,32 +829,48 @@ int addmatchcertattr(struct clsrvconf *conf) { char *v; regex_t **r; + if (!strncasecmp(conf->matchcertattr, "SubjectAltName:IP:", 18)) { + if (inet_pton(AF_INET, conf->matchcertattr+18, &conf->certipmatch)) + conf->certipmatchaf = AF_INET; + else if (inet_pton(AF_INET6, conf->matchcertattr+18, &conf->certipmatch)) + conf->certipmatchaf = AF_INET6; + else + return 0; + return 1; + } + + /* the other cases below use a common regex match */ if (!strncasecmp(conf->matchcertattr, "CN:/", 4)) { - r = &conf->certcnregex; - v = conf->matchcertattr + 4; + r = &conf->certcnregex; + v = conf->matchcertattr + 4; } else if (!strncasecmp(conf->matchcertattr, "SubjectAltName:URI:/", 20)) { - r = &conf->certuriregex; - v = conf->matchcertattr + 20; - } else - return 0; + r = &conf->certuriregex; + v = conf->matchcertattr + 20; + } else if (!strncasecmp(conf->matchcertattr, "SubjectAltName:DNS:/", 20)) { + r = &conf->certdnsregex; + v = conf->matchcertattr + 20; + } + else + return 0; + if (!*v) - return 0; + return 0; /* regexp, remove optional trailing / if present */ if (v[strlen(v) - 1] == '/') - v[strlen(v) - 1] = '\0'; + v[strlen(v) - 1] = '\0'; if (!*v) - return 0; + return 0; *r = malloc(sizeof(regex_t)); if (!*r) { - debug(DBG_ERR, "malloc failed"); - return 0; + debug(DBG_ERR, "malloc failed"); + return 0; } if (regcomp(*r, v, REG_EXTENDED | REG_ICASE | REG_NOSUB)) { - free(*r); - *r = NULL; - debug(DBG_ERR, "failed to compile regular expression %s", v); - return 0; + free(*r); + *r = NULL; + debug(DBG_ERR, "failed to compile regular expression %s", v); + return 0; } return 1; }