summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--radsecproxy.c321
-rw-r--r--radsecproxy.h4
-rw-r--r--util.c14
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 <string.h>
#include <unistd.h>
#include <sys/time.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
#include <regex.h>
#include <libgen.h>
#include <pthread.h>
@@ -43,6 +45,7 @@
#include <openssl/err.h>
#include <openssl/md5.h>
#include <openssl/hmac.h>
+#include <openssl/x509v3.h>
#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 <venaas@uninett.no>
+ * Copyright (C) 2006, 2007 Stig Venaas <venaas@uninett.no>
*
* 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;