From 5d05ee2dd37f778e6aabdc0a476af4125b0193fa Mon Sep 17 00:00:00 2001 From: venaas Date: Thu, 28 Jun 2007 13:01:17 +0000 Subject: added subjectaltname cert checks, incl regexp uri git-svn-id: https://svn.testnett.uninett.no/radsecproxy/trunk@155 e88ac4ed-0b26-0410-9574-a7f39faa03bf --- radsecproxy.c | 321 ++++++++++++++++++++++++++++++++++++++-------------------- radsecproxy.h | 4 +- util.c | 14 ++- 3 files changed, 223 insertions(+), 116 deletions(-) diff --git a/radsecproxy.c b/radsecproxy.c index 6b84f30..abc59b3 100644 --- a/radsecproxy.c +++ b/radsecproxy.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -43,6 +45,7 @@ #include #include #include +#include #include "debug.h" #include "list.h" #include "radsecproxy.h" @@ -127,16 +130,6 @@ static int verify_cb(int ok, X509_STORE_CTX *ctx) { return ok; } -#ifdef DEBUG -void printauth(char *s, unsigned char *t) { - int i; - printf("%s:", s); - for (i = 0; i < 16; i++) - printf("%02x ", t[i]); - printf("\n"); -} -#endif - int resolvepeer(struct clsrvconf *conf, int ai_flags) { struct addrinfo hints, *addrinfo; @@ -360,14 +353,106 @@ unsigned char *radudpget(int s, struct client **client, struct server **server, return rad; } +int subjectaltnameaddr(X509 *cert, int family, struct in6_addr *addr) { + int loc, i, l, n, r = 0; + char *v; + X509_EXTENSION *ex; + STACK_OF(GENERAL_NAME) *alt; + GENERAL_NAME *gn; + + debug(DBG_DBG, "subjectaltnameaddr"); + + loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); + if (loc < 0) + return r; + + ex = X509_get_ext(cert, loc); + alt = X509V3_EXT_d2i(ex); + if (!alt) + return r; + + n = sk_GENERAL_NAME_num(alt); + for (i = 0; i < n; i++) { + gn = sk_GENERAL_NAME_value(alt, i); + if (gn->type != GEN_IPADD) + continue; + r = -1; + v = (char *)ASN1_STRING_data(gn->d.ia5); + l = ASN1_STRING_length(gn->d.ia5); + if (((family == AF_INET && l == sizeof(struct in_addr)) || (family == AF_INET6 && l == sizeof(struct in6_addr))) + && !memcmp(v, &addr, l)) { + r = 1; + break; + } + } + GENERAL_NAMES_free(alt); + return r; +} + +int subjectaltnameregexp(X509 *cert, int type, char *exact, regex_t *regex) { + int loc, i, l, n, r = 0; + char *s, *v; + X509_EXTENSION *ex; + STACK_OF(GENERAL_NAME) *alt; + GENERAL_NAME *gn; + + debug(DBG_DBG, "subjectaltnameregexp"); + + loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); + if (loc < 0) + return r; + + ex = X509_get_ext(cert, loc); + alt = X509V3_EXT_d2i(ex); + if (!alt) + return r; + + n = sk_GENERAL_NAME_num(alt); + for (i = 0; i < n; i++) { + gn = sk_GENERAL_NAME_value(alt, i); + if (gn->type != type) + continue; + r = -1; + v = (char *)ASN1_STRING_data(gn->d.ia5); + l = ASN1_STRING_length(gn->d.ia5); + if (l <= 0) + continue; +#ifdef DEBUG + printfchars(NULL, gn->type == GEN_DNS ? "dns" : "uri", NULL, v, l); +#endif + if (exact) { + if (memcmp(v, exact, l)) + continue; + } else { + s = stringcopy((char *)v, l); + if (!s) { + debug(DBG_ERR, "malloc failed"); + continue; + } + if (regexec(regex, s, 0, NULL, 0)) { + free(s); + continue; + } + free(s); + } + r = 1; + break; + } + GENERAL_NAMES_free(alt); + return r; +} + int tlsverifycert(SSL *ssl, struct clsrvconf *conf) { - int l, loc; + int l, loc, r; X509 *cert; X509_NAME *nm; X509_NAME_ENTRY *e; - unsigned char *v; + ASN1_STRING *s; + char *v; + uint8_t type = 0; /* 0 for DNS, AF_INET for IPv4, AF_INET6 for IPv6 */ unsigned long error; - + struct in6_addr addr; + if (SSL_get_verify_result(ssl) != X509_V_OK) { debug(DBG_ERR, "tlsverifycert: basic validation failed"); while ((error = ERR_get_error())) @@ -379,34 +464,60 @@ int tlsverifycert(SSL *ssl, struct clsrvconf *conf) { if (!cert) { debug(DBG_ERR, "tlsverifycert: failed to obtain certificate"); return 0; - } - nm = X509_get_subject_name(cert); - loc = -1; - for (;;) { - loc = X509_NAME_get_index_by_NID(nm, NID_commonName, loc); - if (loc == -1) - break; - e = X509_NAME_get_entry(nm, loc); - l = ASN1_STRING_to_UTF8(&v, X509_NAME_ENTRY_get_data(e)); - if (l < 0) - continue; +} + + if (inet_pton(AF_INET, conf->host, &addr)) + type = AF_INET; + else if (inet_pton(AF_INET6, conf->host, &addr)) + type = AF_INET6; + + r = type ? subjectaltnameaddr(cert, type, &addr) : subjectaltnameregexp(cert, GEN_DNS, conf->host, NULL); + if (r) { + if (r < 0) { + X509_free(cert); + debug(DBG_DBG, "tlsverifycert: No subjectaltname matching %s %s", type ? "address" : "host", conf->host); + return 0; + } + debug(DBG_DBG, "tlsverifycert: Found subjectaltname matching %s %s", type ? "address" : "host", conf->host); + } else { + nm = X509_get_subject_name(cert); + loc = -1; + for (;;) { + loc = X509_NAME_get_index_by_NID(nm, NID_commonName, loc); + if (loc == -1) + break; + e = X509_NAME_get_entry(nm, loc); + s = X509_NAME_ENTRY_get_data(e); + v = (char *) ASN1_STRING_data(s); + l = ASN1_STRING_length(s); + if (l < 0) + continue; #ifdef DEBUG - { - int i; - printf("cn: "); - for (i = 0; i < l; i++) - printf("%c", v[i]); - printf("\n"); + printfchars(NULL, "cn", NULL, v, l); +#endif + if (l == strlen(conf->host) && !strncasecmp(conf->host, v, l)) { + r = 1; + debug(DBG_DBG, "tlsverifycert: Found cn matching host %s", conf->host); + break; + } } -#endif - if (l == strlen(conf->host) && !strncasecmp(conf->host, (char *)v, l)) { - debug(DBG_DBG, "tlsverifycert: Found cn matching host %s, All OK", conf->host); - return 1; + if (!r) { + X509_free(cert); + debug(DBG_ERR, "tlsverifycert: cn not matching host %s", conf->host); + return 0; + } + } + if (conf->certuriregex) { + r = subjectaltnameregexp(cert, GEN_URI, NULL, conf->certuriregex); + if (r < 1) { + debug(DBG_DBG, "tlsverifycert: subjectaltname URI not matching regex"); + X509_free(cert); + return 0; } - debug(DBG_ERR, "tlsverifycert: cn not matching host %s", conf->host); + debug(DBG_DBG, "tlsverifycert: subjectaltname URI matching regex"); } X509_free(cert); - return 0; + return 1; } void tlsconnect(struct server *server, struct timeval *when, char *text) { @@ -832,21 +943,10 @@ int msmppencrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, first = 0; } -#if 0 - printf("msppencrypt auth in: "); - for (i = 0; i < 16; i++) - printf("%02x ", auth[i]); - printf("\n"); - - printf("msppencrypt salt in: "); - for (i = 0; i < 2; i++) - printf("%02x ", salt[i]); - printf("\n"); - - printf("msppencrypt in: "); - for (i = 0; i < len; i++) - printf("%02x ", text[i]); - printf("\n"); +#if 0 + printfchars(NULL, "msppencrypt auth in", "%02x ", auth, 16); + printfchars(NULL, "msppencrypt salt in", "%02x ", salt, 2); + printfchars(NULL, "msppencrypt in", "%02x ", text, len); #endif if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) || @@ -859,10 +959,7 @@ int msmppencrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, } #if 0 - printf("msppencrypt hash: "); - for (i = 0; i < 16; i++) - printf("%02x ", hash[i]); - printf("\n"); + printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16); #endif for (i = 0; i < 16; i++) @@ -871,9 +968,7 @@ int msmppencrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, for (offset = 16; offset < len; offset += 16) { #if 0 printf("text + offset - 16 c(%d): ", offset / 16); - for (i = 0; i < 16; i++) - printf("%02x ", (text + offset - 16)[i]); - printf("\n"); + printfchars(NULL, NULL, "%02x ", text + offset - 16, 16); #endif if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) || !EVP_DigestUpdate(&mdctx, shared, sharedlen) || @@ -883,11 +978,8 @@ int msmppencrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, pthread_mutex_unlock(&lock); return 0; } -#if 0 - printf("msppencrypt hash: "); - for (i = 0; i < 16; i++) - printf("%02x ", hash[i]); - printf("\n"); +#if 0 + printfchars(NULL, "msppencrypt hash", "%02x ", hash, 16); #endif for (i = 0; i < 16; i++) @@ -895,10 +987,7 @@ int msmppencrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, } #if 0 - printf("msppencrypt out: "); - for (i = 0; i < len; i++) - printf("%02x ", text[i]); - printf("\n"); + printfchars(NULL, "msppencrypt out", "%02x ", text, len); #endif pthread_mutex_unlock(&lock); @@ -920,21 +1009,10 @@ int msmppdecrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, first = 0; } -#if 0 - printf("msppdecrypt auth in: "); - for (i = 0; i < 16; i++) - printf("%02x ", auth[i]); - printf("\n"); - - printf("msppedecrypt salt in: "); - for (i = 0; i < 2; i++) - printf("%02x ", salt[i]); - printf("\n"); - - printf("msppedecrypt in: "); - for (i = 0; i < len; i++) - printf("%02x ", text[i]); - printf("\n"); +#if 0 + printfchars(NULL, "msppdecrypt auth in", "%02x ", auth, 16); + printfchars(NULL, "msppdecrypt salt in", "%02x ", salt, 2); + printfchars(NULL, "msppdecrypt in", "%02x ", text, len); #endif if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) || @@ -947,10 +1025,7 @@ int msmppdecrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, } #if 0 - printf("msppedecrypt hash: "); - for (i = 0; i < 16; i++) - printf("%02x ", hash[i]); - printf("\n"); + printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16); #endif for (i = 0; i < 16; i++) @@ -959,9 +1034,7 @@ int msmppdecrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, for (offset = 16; offset < len; offset += 16) { #if 0 printf("text + offset - 16 c(%d): ", offset / 16); - for (i = 0; i < 16; i++) - printf("%02x ", (text + offset - 16)[i]); - printf("\n"); + printfchars(NULL, NULL, "%02x ", text + offset - 16, 16); #endif if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) || !EVP_DigestUpdate(&mdctx, shared, sharedlen) || @@ -971,23 +1044,17 @@ int msmppdecrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen, pthread_mutex_unlock(&lock); return 0; } -#if 0 - printf("msppedecrypt hash: "); - for (i = 0; i < 16; i++) - printf("%02x ", hash[i]); - printf("\n"); +#if 0 + printfchars(NULL, "msppdecrypt hash", "%02x ", hash, 16); #endif - for (i = 0; i < 16; i++) - plain[offset + i] = text[offset + i] ^ hash[i]; + for (i = 0; i < 16; i++) + plain[offset + i] = text[offset + i] ^ hash[i]; } memcpy(text, plain, len); #if 0 - printf("msppedecrypt out: "); - for (i = 0; i < len; i++) - printf("%02x ", text[i]); - printf("\n"); + printfchars(NULL, "msppdecrypt out", "%02x ", text, len); #endif pthread_mutex_unlock(&lock); @@ -1039,9 +1106,6 @@ int attrvalidate(unsigned char *attrs, int length) { } int pwdrecrypt(uint8_t *pwd, uint8_t len, char *oldsecret, char *newsecret, uint8_t *oldauth, uint8_t *newauth) { -#ifdef DEBUG - int i; -#endif if (len < 16 || len > 128 || len % 16) { debug(DBG_WARN, "pwdrecrypt: invalid password length"); return 0; @@ -1052,10 +1116,7 @@ int pwdrecrypt(uint8_t *pwd, uint8_t len, char *oldsecret, char *newsecret, uint return 0; } #ifdef DEBUG - printf("pwdrecrypt: password: "); - for (i = 0; i < len; i++) - printf("%02x ", pwd[i]); - printf("\n"); + printfchars(NULL, "pwdrecrypt: password", "%02x ", pwd, len); #endif if (!pwdencrypt(pwd, len, newsecret, strlen(newsecret), newauth)) { debug(DBG_WARN, "pwdrecrypt: cannot encrypt password"); @@ -1210,8 +1271,8 @@ void radsrv(struct request *rq) { return; } -#ifdef DEBUG - printauth("auth", auth); +#ifdef DEBUG + printfchars(NULL, "auth", "%02x ", auth, 16); #endif attr = attrget(attrs, len, RAD_Attr_User_Password); @@ -1380,7 +1441,7 @@ void *clientrd(void *arg) { buf[1] = (char)server->requests[i].origid; memcpy(buf + 4, server->requests[i].origauth, 16); #ifdef DEBUG - printauth("origauth/buf+4", buf + 4); + printfchars(NULL, "origauth/buf+4", "%02x ", buf + 4, 16); #endif if (messageauth) { @@ -2120,8 +2181,34 @@ void getgeneralconfig(FILE *f, char *block, ...) { } } +int addmatchcertattr(struct clsrvconf *conf, char *matchcertattr) { + char *v; + + v = matchcertattr + 20; + if (strncasecmp(matchcertattr, "SubjectAltName:URI:/", 20) || !*v) + return 0; + /* regexp, remove optional trailing / if present */ + if (v[strlen(v) - 1] == '/') + v[strlen(v) - 1] = '\0'; + if (!*v) + return 0; + + conf->certuriregex = malloc(sizeof(regex_t)); + if (!conf->certuriregex) { + debug(DBG_ERR, "malloc failed"); + return 0; + } + if (regcomp(conf->certuriregex, v, REG_ICASE | REG_NOSUB)) { + regfree(conf->certuriregex); + conf->certuriregex = NULL; + debug(DBG_ERR, "failed to compile regular expression %s", v); + return 0; + } + return 1; +} + void confclient_cb(FILE *f, char *block, char *opt, char *val) { - char *type = NULL, *tls = NULL; + char *type = NULL, *tls = NULL, *matchcertattr = NULL; struct clsrvconf *conf; debug(DBG_DBG, "confclient_cb called for %s", block); @@ -2136,6 +2223,7 @@ void confclient_cb(FILE *f, char *block, char *opt, char *val) { "host", CONF_STR, &conf->host, "secret", CONF_STR, &conf->secret, "tls", CONF_STR, &tls, + "matchcertificateattribute", CONF_STR, &matchcertattr, NULL ); @@ -2150,6 +2238,8 @@ void confclient_cb(FILE *f, char *block, char *opt, char *val) { conf->ssl_ctx = tls ? tlsgetctx(tls, NULL) : tlsgetctx("defaultclient", "default"); if (!conf->ssl_ctx) debugx(1, DBG_ERR, "error in block %s, no tls context defined", block); + if (matchcertattr && !addmatchcertattr(conf, matchcertattr)) + debugx(1, DBG_ERR, "error in block %s, invalid MatchCertificateAttributeValue", block); conf->type = 'T'; client_tls_count++; } else @@ -2157,6 +2247,8 @@ void confclient_cb(FILE *f, char *block, char *opt, char *val) { free(type); if (tls) free(tls); + if (matchcertattr) + free(matchcertattr); if (!resolvepeer(conf, 0)) debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host, conf->port); @@ -2169,7 +2261,7 @@ void confclient_cb(FILE *f, char *block, char *opt, char *val) { } void confserver_cb(FILE *f, char *block, char *opt, char *val) { - char *type = NULL, *tls = NULL, *statusserver = NULL; + char *type = NULL, *tls = NULL, *matchcertattr = NULL, *statusserver = NULL; struct clsrvconf *conf; debug(DBG_DBG, "confserver_cb called for %s", block); @@ -2185,6 +2277,7 @@ void confserver_cb(FILE *f, char *block, char *opt, char *val) { "port", CONF_STR, &conf->port, "secret", CONF_STR, &conf->secret, "tls", CONF_STR, &tls, + "matchcertificateattribute", CONF_STR, &matchcertattr, "StatusServer", CONF_STR, &statusserver, NULL ); @@ -2202,6 +2295,8 @@ void confserver_cb(FILE *f, char *block, char *opt, char *val) { conf->ssl_ctx = tls ? tlsgetctx(tls, NULL) : tlsgetctx("defaultserver", "default"); if (!conf->ssl_ctx) debugx(1, DBG_ERR, "error in block %s, no tls context defined", block); + if (matchcertattr && !addmatchcertattr(conf, matchcertattr)) + debugx(1, DBG_ERR, "error in block %s, invalid MatchCertificateAttributeValue", block); if (!conf->port) conf->port = stringcopy(DEFAULT_TLS_PORT, 0); conf->type = 'T'; @@ -2211,6 +2306,8 @@ void confserver_cb(FILE *f, char *block, char *opt, char *val) { free(type); if (tls) free(tls); + if (matchcertattr) + free(matchcertattr); if (!resolvepeer(conf, 0)) debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host, conf->port); diff --git a/radsecproxy.h b/radsecproxy.h index 004a269..2516074 100644 --- a/radsecproxy.h +++ b/radsecproxy.h @@ -78,6 +78,7 @@ struct clsrvconf { char *host; char *port; char *secret; + regex_t *certuriregex; uint8_t statusserver; SSL_CTX *ssl_ctx; struct addrinfo *addrinfo; @@ -130,9 +131,8 @@ struct tls { sizeof(struct sockaddr_in) : \ sizeof(struct sockaddr_in6)) -void errx(char *format, ...); -void err(char *format, ...); char *stringcopy(char *s, int len); char *addr2string(struct sockaddr *addr, socklen_t len); +void printfchars(char *prefixfmt, char *prefix, char *charfmt, char *chars, int len); int bindport(int type, char *port); int connectport(int type, char *host, char *port); diff --git a/util.c b/util.c index 4dabf47..0a811a8 100644 --- a/util.c +++ b/util.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Stig Venaas + * Copyright (C) 2006, 2007 Stig Venaas * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -61,7 +61,17 @@ char *stringcopy(char *s, int len) { r[len] = '\0'; return r; } - + +void printfchars(char *prefixfmt, char *prefix, char *charfmt, char *chars, int len) { + int i; + unsigned char *s = (unsigned char *)chars; + if (prefix) + printf(prefixfmt ? prefixfmt : "%s: ", prefix); + for (i = 0; i < len; i++) + printf(charfmt ? charfmt : "%c", s[i]); + printf("\n"); +} + char *addr2string(struct sockaddr *addr, socklen_t len) { struct sockaddr_in6 *sa6; struct sockaddr_in sa4; -- cgit v1.1