summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--COPYING5
-rw-r--r--ChangeLog0
-rw-r--r--Makefile.am14
-rw-r--r--README19
-rw-r--r--acinclude.m447
-rw-r--r--autogen.sh3
-rw-r--r--configure.ac6
-rw-r--r--radsecproxy.c784
-rw-r--r--radsecproxy.conf-example86
-rw-r--r--radsecproxy.h18
11 files changed, 557 insertions, 426 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..810b3f9
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Stig Venaas <venaas@uninett.no>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..680d891
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,5 @@
+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
+copyright notice and this permission notice appear in all copies.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ChangeLog
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..119021b
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,14 @@
+bin_PROGRAMS = radsecproxy
+
+radsecproxy_SOURCES = radsecproxy.c \
+ util.c \
+ debug.c \
+ radsecproxy.h \
+ debug.h
+
+radsecproxy_CFLAGS = -g -Wall -pedantic -pthread @SSL_CFLAGS@
+radsecproxy_LDFLAGS = @SSL_LDFLAGS@
+radsecproxy_LDADD = @SSL_LIBS@
+
+sysconfdir = ${prefix}/etc/radsecproxy
+dist_sysconf_DATA = $(srcdir)/radsecproxy.conf-example
diff --git a/README b/README
index dce3195..995055d 100644
--- a/README
+++ b/README
@@ -2,20 +2,31 @@ This is a beta version of a generic RADIUS proxy that can support
various RADIUS clients over UDP or TLS (RadSec).
It should build on most Linux and BSD platforms by simply typing
-"make". To use it you need to create a config file called
+"make". You may also try to use autoconf if you like.
+
+To use it you need to create a config file called
"radsecproxy.conf" which must be in /etc/radsecproxy (unless
you alter it in the header file), the current directory, or
you can specify the location with the "-c" command line
option (see below). See the enclosed example file for further
instructions.
-There are three options that may be specified on the command line.
+There are four options that may be specified on the command line.
"-c configfile" to specify a non-default config file path;
"-d loglevel" to set a loglevel of 1, 2, 3 or 4 where 4 is the most
detailed; and "-f" to run the proxy in the foreground with logging
to stderr. Without "-f" the default is to detach as a daemon and
-log to syslog.
+log to syslog. Finally "-v" just prints version information and
+exits.
+
+Thanks to Stefan Winter and Andreas Solberg for making me do this,
+and the funding from GEANT2. Stefan as well as Kolbjørn Barmen
+and Maja Wolniewicz have helped with early testing of the code.
+All of the above plus Milan Sova have provided good feedback on
+several implementation choices. Finally thanks to Hans Zandbelt
+for providing the autoconf stuff. I may have forgotten someone,
+let me know if you feel left out.
For more information, feedback etc. contact <venaas@uninett.no>.
-Stig Venaas, 2007.05.15
+Stig Venaas, 2007.05.24
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644
index 0000000..f854b0e
--- /dev/null
+++ b/acinclude.m4
@@ -0,0 +1,47 @@
+dnl Based on the one from the Boinc project by Reinhard
+
+AC_DEFUN([AX_CHECK_SSL],
+[AC_MSG_CHECKING(for OpenSSL)
+SSL_DIR=
+found_ssl="no"
+AC_ARG_WITH(ssl,
+ AC_HELP_STRING([--with-ssl],
+ [Use SSL (in specified installation directory)]),
+ [check_ssl_dir="$withval"],
+ [check_ssl_dir=])
+for dir in $check_ssl_dir /usr /usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local ; do
+ ssldir="$dir"
+ if test -f "$dir/include/openssl/ssl.h"; then
+ found_ssl="yes";
+ SSL_DIR="${ssldir}"
+ SSL_CFLAGS="-I$ssldir/include -I$ssldir/include/openssl";
+ break;
+ fi
+ if test -f "$dir/include/ssl.h"; then
+ found_ssl="yes";
+ SSL_DIR="${ssldir}"
+ SSL_CFLAGS="-I$ssldir/include/";
+ break
+ fi
+done
+AC_MSG_RESULT($found_ssl)
+if test x_$found_ssl != x_yes; then
+ AC_MSG_ERROR([
+----------------------------------------------------------------------
+ Cannot find SSL libraries.
+
+ Please install OpenSSL or specify installation directory with
+ --with-ssl=(dir).
+----------------------------------------------------------------------
+])
+else
+ printf "OpenSSL found in $ssldir\n";
+ SSL_LIBS="-lssl -lcrypto";
+ SSL_LDFLAGS="-L$ssldir/lib";
+ AC_DEFINE_UNQUOTED([USE_OPENSSL],[1],
+ ["Define to 1 if you want to use the OpenSSL crypto library"])
+ AC_SUBST(SSL_CFLAGS)
+ AC_SUBST(SSL_LDFLAGS)
+ AC_SUBST(SSL_LIBS)
+fi
+])dnl
diff --git a/autogen.sh b/autogen.sh
new file mode 100644
index 0000000..25bf74f
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+touch NEWS
+autoreconf --force --install
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..c5ea60d
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,6 @@
+AC_INIT(radsecproxy, 0.1, venaas@uninett.no)
+AM_INIT_AUTOMAKE
+AC_PROG_CC
+AM_PROG_CC_C_O
+AX_CHECK_SSL
+AC_OUTPUT(Makefile)
diff --git a/radsecproxy.c b/radsecproxy.c
index 76a4860..779e9f4 100644
--- a/radsecproxy.c
+++ b/radsecproxy.c
@@ -6,12 +6,6 @@
* copyright notice and this permission notice appear in all copies.
*/
-/* TODO:
- * accounting
- * radius keep alives (server status)
- * setsockopt(keepalive...), check if openssl has some keepalive feature
-*/
-
/* For UDP there is one server instance consisting of udpserverrd and udpserverth
* rd is responsible for init and launching wr
* For TLS there is a server instance that launches tlsserverrd for each TLS peer
@@ -56,6 +50,7 @@ static struct options options;
static struct client *clients = NULL;
static struct server *servers = NULL;
static struct realm *realms = NULL;
+static struct tls *tls = NULL;
static int client_udp_count = 0;
static int client_tls_count = 0;
@@ -64,6 +59,7 @@ static int server_udp_count = 0;
static int server_tls_count = 0;
static int server_count = 0;
static int realm_count = 0;
+static int tls_count = 0;
static struct peer *tcp_server_listen;
static struct peer *udp_server_listen;
@@ -71,7 +67,6 @@ static struct replyq udp_server_replyq;
static int udp_server_sock = -1;
static pthread_mutex_t *ssl_locks;
static long *ssl_lock_count;
-static SSL_CTX *ssl_ctx = NULL;
extern int optind;
extern char *optarg;
@@ -138,56 +133,6 @@ static int verify_cb(int ok, X509_STORE_CTX *ctx) {
return ok;
}
-SSL_CTX *ssl_init() {
- SSL_CTX *ctx;
- int i;
- unsigned long error;
-
- if (!options.tlscertificatefile || !options.tlscertificatekeyfile)
- debugx(1, DBG_ERR, "TLSCertificateFile and TLSCertificateKeyFile must be specified for TLS");
-
- if (!options.tlscacertificatefile && !options.tlscacertificatepath)
- debugx(1, DBG_ERR, "CA Certificate file/path need to be configured");
-
- ssl_locks = malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
- ssl_lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
- for (i = 0; i < CRYPTO_num_locks(); i++) {
- ssl_lock_count[i] = 0;
- pthread_mutex_init(&ssl_locks[i], NULL);
- }
- CRYPTO_set_id_callback(ssl_thread_id);
- CRYPTO_set_locking_callback(ssl_locking_callback);
-
- SSL_load_error_strings();
- SSL_library_init();
-
- while (!RAND_status()) {
- time_t t = time(NULL);
- pid_t pid = getpid();
- RAND_seed((unsigned char *)&t, sizeof(time_t));
- RAND_seed((unsigned char *)&pid, sizeof(pid));
- }
-
- ctx = SSL_CTX_new(TLSv1_method());
- if (options.tlscertificatekeypassword) {
- SSL_CTX_set_default_passwd_cb_userdata(ctx, options.tlscertificatekeypassword);
- SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
- }
- if (SSL_CTX_use_certificate_chain_file(ctx, options.tlscertificatefile) &&
- SSL_CTX_use_PrivateKey_file(ctx, options.tlscertificatekeyfile, SSL_FILETYPE_PEM) &&
- SSL_CTX_check_private_key(ctx) &&
- SSL_CTX_load_verify_locations(ctx, options.tlscacertificatefile, options.tlscacertificatepath)) {
- SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
- SSL_CTX_set_verify_depth(ctx, MAX_CERT_DEPTH + 1);
- return ctx;
- }
-
- while ((error = ERR_get_error()))
- debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
- debug(DBG_ERR, "Error initialising SSL/TLS");
- exit(1);
-}
-
#ifdef DEBUG
void printauth(char *s, unsigned char *t) {
int i;
@@ -219,7 +164,8 @@ int resolvepeer(struct peer *peer, int ai_flags) {
int connecttoserver(struct addrinfo *addrinfo) {
int s;
struct addrinfo *res;
-
+
+ s = -1;
for (res = addrinfo; res; res = res->ai_next) {
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s < 0) {
@@ -257,7 +203,7 @@ int bindtoaddr(struct addrinfo *addrinfo) {
/* returns the client with matching address, or NULL */
/* if client argument is not NULL, we only check that one client */
struct client *find_client(char type, struct sockaddr *addr, struct client *client) {
- struct sockaddr_in6 *sa6;
+ struct sockaddr_in6 *sa6 = NULL;
struct in_addr *a4 = NULL;
struct client *c;
int i;
@@ -276,7 +222,7 @@ struct client *find_client(char type, struct sockaddr *addr, struct client *clie
for (res = c->peer.addrinfo; res; res = res->ai_next)
if ((a4 && res->ai_family == AF_INET &&
!memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
- (res->ai_family == AF_INET6 &&
+ (sa6 && res->ai_family == AF_INET6 &&
!memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
return c;
if (client)
@@ -289,7 +235,7 @@ struct client *find_client(char type, struct sockaddr *addr, struct client *clie
/* returns the server with matching address, or NULL */
/* if server argument is not NULL, we only check that one server */
struct server *find_server(char type, struct sockaddr *addr, struct server *server) {
- struct sockaddr_in6 *sa6;
+ struct sockaddr_in6 *sa6 = NULL;
struct in_addr *a4 = NULL;
struct server *s;
int i;
@@ -308,7 +254,7 @@ struct server *find_server(char type, struct sockaddr *addr, struct server *serv
for (res = s->peer.addrinfo; res; res = res->ai_next)
if ((a4 && res->ai_family == AF_INET &&
!memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) ||
- (res->ai_family == AF_INET6 &&
+ (sa6 && res->ai_family == AF_INET6 &&
!memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16)))
return s;
if (server)
@@ -466,7 +412,7 @@ void tlsconnect(struct server *server, struct timeval *when, char *text) {
}
SSL_free(server->peer.ssl);
- server->peer.ssl = SSL_new(ssl_ctx);
+ server->peer.ssl = SSL_new(server->peer.ssl_ctx);
SSL_set_fd(server->peer.ssl, server->sock);
if (SSL_connect(server->peer.ssl) > 0 && tlsverifycert(&server->peer))
break;
@@ -681,7 +627,7 @@ unsigned char *attrget(unsigned char *attrs, int length, uint8_t type) {
return NULL;
}
-void sendrq(struct server *to, struct client *from, struct request *rq) {
+void sendrq(struct server *to, struct request *rq) {
int i;
uint8_t *attr;
@@ -723,9 +669,15 @@ void sendrq(struct server *to, struct client *from, struct request *rq) {
pthread_mutex_unlock(&to->newrq_mutex);
}
-void sendreply(struct client *to, struct server *from, unsigned char *buf, struct sockaddr_storage *tosa) {
+void sendreply(struct client *to, unsigned char *buf, struct sockaddr_storage *tosa) {
struct replyq *replyq = to->replyq;
+ if (!radsign(buf, (unsigned char *)to->peer.secret)) {
+ free(buf);
+ debug(DBG_WARN, "sendreply: failed to sign message");
+ return;
+ }
+
pthread_mutex_lock(&replyq->count_mutex);
if (replyq->count == replyq->size) {
debug(DBG_WARN, "No room in queue, dropping request");
@@ -740,7 +692,7 @@ void sendreply(struct client *to, struct server *from, unsigned char *buf, struc
replyq->count++;
if (replyq->count == 1) {
- debug(DBG_DBG, "signalling client writer");
+ debug(DBG_DBG, "signalling server writer");
pthread_cond_signal(&replyq->count_cond);
}
pthread_mutex_unlock(&replyq->count_mutex);
@@ -994,12 +946,12 @@ int msmppdecrypt(uint8_t *text, uint8_t len, uint8_t *shared, uint8_t sharedlen,
return 1;
}
-struct server *id2server(char *id, uint8_t len) {
+struct realm *id2realm(char *id, uint8_t len) {
int i;
for (i = 0; i < realm_count; i++)
if (!regexec(&realms[i].regex, id, 0, NULL, 0)) {
- debug(DBG_DBG, "found matching realm: %s, host %s", realms[i].name, realms[i].server->peer.host);
- return realms[i].server;
+ debug(DBG_DBG, "found matching realm: %s", realms[i].name);
+ return realms + i;
}
return NULL;
}
@@ -1086,13 +1038,54 @@ int msmppe(unsigned char *attrs, int length, uint8_t type, char *attrtxt, struct
return 1;
}
-struct server *radsrv(struct request *rq, unsigned char *buf, struct client *from) {
+void respondstatusserver(struct request *rq) {
+ unsigned char *resp;
+
+ resp = malloc(20);
+ if (!resp) {
+ debug(DBG_ERR, "respondstatusserver: malloc failed");
+ return;
+ }
+ memcpy(resp, rq->buf, 20);
+ resp[0] = RAD_Access_Accept;
+ resp[2] = 0;
+ resp[3] = 20;
+ debug(DBG_DBG, "respondstatusserver: responding to %s", rq->from->peer.host);
+ sendreply(rq->from, resp, rq->from->peer.type == 'U' ? &rq->fromsa : NULL);
+}
+
+void respondreject(struct request *rq, char *message) {
+ unsigned char *resp;
+ int len = 20;
+
+ if (message)
+ len += 2 + strlen(message);
+
+ resp = malloc(len);
+ if (!resp) {
+ debug(DBG_ERR, "respondreject: malloc failed");
+ return;
+ }
+ memcpy(resp, rq->buf, 20);
+ resp[0] = RAD_Access_Reject;
+ *(uint16_t *)(resp + 2) = htons(len);
+ if (message) {
+ resp[20] = RAD_Attr_Reply_Message;
+ resp[21] = len - 20;
+ memcpy(resp + 22, message, len - 22);
+ }
+ sendreply(rq->from, resp, rq->from->peer.type == 'U' ? &rq->fromsa : NULL);
+}
+
+void radsrv(struct request *rq) {
uint8_t code, id, *auth, *attrs, *attr;
uint16_t len;
- struct server *to;
+ struct server *to = NULL;
char username[256];
- unsigned char newauth[16];
+ unsigned char *buf, newauth[16];
+ struct realm *realm = NULL;
+ buf = rq->buf;
code = *(uint8_t *)buf;
id = *(uint8_t *)(buf + 1);
len = RADLEN(buf);
@@ -1100,9 +1093,10 @@ struct server *radsrv(struct request *rq, unsigned char *buf, struct client *fro
debug(DBG_DBG, "radsrv: code %d, id %d, length %d", code, id, len);
- if (code != RAD_Access_Request) {
- debug(DBG_INFO, "radsrv: server currently accepts only access-requests, ignoring");
- return NULL;
+ if (code != RAD_Access_Request && code != RAD_Status_Server) {
+ debug(DBG_INFO, "radsrv: server currently accepts only access-requests and status-server, ignoring");
+ free(buf);
+ return;
}
len -= 20;
@@ -1110,64 +1104,86 @@ struct server *radsrv(struct request *rq, unsigned char *buf, struct client *fro
if (!attrvalidate(attrs, len)) {
debug(DBG_WARN, "radsrv: attribute validation failed, ignoring packet");
- return NULL;
+ free(buf);
+ return;
}
-
- attr = attrget(attrs, len, RAD_Attr_User_Name);
- if (!attr) {
- debug(DBG_WARN, "radsrv: ignoring request, no username attribute");
- return NULL;
+
+ if (code == RAD_Access_Request) {
+ attr = attrget(attrs, len, RAD_Attr_User_Name);
+ if (!attr) {
+ debug(DBG_WARN, "radsrv: ignoring request, no username attribute");
+ free(buf);
+ return;
+ }
+ memcpy(username, ATTRVAL(attr), ATTRVALLEN(attr));
+ username[ATTRVALLEN(attr)] = '\0';
+ debug(DBG_DBG, "Access Request with username: %s", username);
+
+ realm = id2realm(username, strlen(username));
+ if (!realm) {
+ debug(DBG_INFO, "radsrv: ignoring request, don't know where to send it");
+ free(buf);
+ return;
+ }
+ to = realm->server;
+
+ if (to && rqinqueue(to, rq->from, id)) {
+ debug(DBG_INFO, "radsrv: already got request from host %s with id %d, ignoring", rq->from->peer.host, id);
+ free(buf);
+ return;
+ }
}
- memcpy(username, ATTRVAL(attr), ATTRVALLEN(attr));
- username[ATTRVALLEN(attr)] = '\0';
- debug(DBG_DBG, "Access Request with username: %s", username);
- to = id2server(username, strlen(username));
- if (!to) {
- debug(DBG_INFO, "radsrv: ignoring request, don't know where to send it");
- return NULL;
+ attr = attrget(attrs, len, RAD_Attr_Message_Authenticator);
+ if (attr && (ATTRVALLEN(attr) != 16 || !checkmessageauth(buf, ATTRVAL(attr), rq->from->peer.secret))) {
+ debug(DBG_WARN, "radsrv: message authentication failed");
+ free(buf);
+ return;
}
- if (rqinqueue(to, from, id)) {
- debug(DBG_INFO, "radsrv: ignoring request from host %s with id %d, already got one", from->peer.host, id);
- return NULL;
+ if (code == RAD_Status_Server) {
+ respondstatusserver(rq);
+ return;
}
- attr = attrget(attrs, len, RAD_Attr_Message_Authenticator);
- if (attr && (ATTRVALLEN(attr) != 16 || !checkmessageauth(buf, ATTRVAL(attr), from->peer.secret))) {
- debug(DBG_WARN, "radsrv: message authentication failed");
- return NULL;
+ if (!to) {
+ debug(DBG_INFO, "radsrv: sending reject to %s for %s", rq->from->peer.host, username);
+ respondreject(rq, realm->message);
+ return;
+ }
+
+ if (!RAND_bytes(newauth, 16)) {
+ debug(DBG_WARN, "radsrv: failed to generate random auth");
+ free(buf);
+ return;
}
-
- if (!RAND_bytes(newauth, 16)) {
- debug(DBG_WARN, "radsrv: failed to generate random auth");
- return NULL;
- }
#ifdef DEBUG
printauth("auth", auth);
#endif
-
+
attr = attrget(attrs, len, RAD_Attr_User_Password);
if (attr) {
debug(DBG_DBG, "radsrv: found userpwdattr with value length %d", ATTRVALLEN(attr));
- if (!pwdrecrypt(ATTRVAL(attr), ATTRVALLEN(attr), from->peer.secret, to->peer.secret, auth, newauth))
- return NULL;
+ if (!pwdrecrypt(ATTRVAL(attr), ATTRVALLEN(attr), rq->from->peer.secret, to->peer.secret, auth, newauth)) {
+ free(buf);
+ return;
+ }
}
attr = attrget(attrs, len, RAD_Attr_Tunnel_Password);
if (attr) {
debug(DBG_DBG, "radsrv: found tunnelpwdattr with value length %d", ATTRVALLEN(attr));
- if (!pwdrecrypt(ATTRVAL(attr), ATTRVALLEN(attr), from->peer.secret, to->peer.secret, auth, newauth))
- return NULL;
+ if (!pwdrecrypt(ATTRVAL(attr), ATTRVALLEN(attr), rq->from->peer.secret, to->peer.secret, auth, newauth)) {
+ free(buf);
+ return;
+ }
}
- rq->buf = buf;
- rq->from = from;
rq->origid = id;
memcpy(rq->origauth, auth, 16);
memcpy(auth, newauth, 16);
- return to;
+ sendrq(to, rq);
}
void *clientrd(void *arg) {
@@ -1265,7 +1281,7 @@ void *clientrd(void *arg) {
server->requests[i].received = 1;
pthread_mutex_unlock(&server->newrq_mutex);
free(buf);
- debug(DBG_INFO, "clientrd: got status server response");
+ debug(DBG_INFO, "clientrd: got status server response from %s", server->peer.host);
continue;
}
@@ -1329,16 +1345,8 @@ void *clientrd(void *arg) {
server->requests[i].received = 1;
pthread_mutex_unlock(&server->newrq_mutex);
- if (!radsign(buf, (unsigned char *)from->peer.secret)) {
- free(buf);
- debug(DBG_WARN, "clientrd: failed to sign message");
- continue;
- }
-#ifdef DEBUG
- printauth("signedorigauth/buf+4", buf + 4);
-#endif
debug(DBG_DBG, "clientrd: giving packet back to where it came from");
- sendreply(from, server, buf, from->peer.type == 'U' ? &fromsa : NULL);
+ sendreply(from, buf, from->peer.type == 'U' ? &fromsa : NULL);
}
}
@@ -1357,8 +1365,7 @@ void *clientwr(void *arg) {
if (server->statusserver) {
memset(&statsrvrq, 0, sizeof(struct request));
- statsrvrq.buf = statsrvbuf;
- memset(&statsrvbuf, 0, sizeof(statsrvbuf));
+ memset(statsrvbuf, 0, sizeof(statsrvbuf));
statsrvbuf[0] = RAD_Status_Server;
statsrvbuf[3] = 38;
statsrvbuf[20] = RAD_Attr_Message_Authenticator;
@@ -1413,11 +1420,13 @@ void *clientwr(void *arg) {
rq = server->requests + i;
if (rq->received) {
- debug(DBG_DBG, "clientwr: removing received packet from queue");
- if (*rq->buf != RAD_Status_Server)
+ debug(DBG_DBG, "clientwr: packet %d in queue is marked as received", i);
+ if (rq->buf) {
+ debug(DBG_DBG, "clientwr: freeing received packet %d from queue", i);
free(rq->buf);
- /* setting this to NULL means that it can be reused */
- rq->buf = NULL;
+ /* setting this to NULL means that it can be reused */
+ rq->buf = NULL;
+ }
pthread_mutex_unlock(&server->newrq_mutex);
continue;
}
@@ -1434,9 +1443,8 @@ void *clientwr(void *arg) {
? 1 : REQUEST_RETRIES)) {
debug(DBG_DBG, "clientwr: removing expired packet from queue");
if (*rq->buf == RAD_Status_Server)
- debug(DBG_WARN, "clientwr: no status server response, server dead?");
- else
- free(rq->buf);
+ debug(DBG_WARN, "clientwr: no status server response, %s dead?", server->peer.host);
+ free(rq->buf);
/* setting this to NULL means that it can be reused */
rq->buf = NULL;
pthread_mutex_unlock(&server->newrq_mutex);
@@ -1452,7 +1460,6 @@ void *clientwr(void *arg) {
rq->tries++;
clientradput(server, server->requests[i].buf);
gettimeofday(&lastsend, NULL);
- usleep(200000);
}
if (server->statusserver) {
gettimeofday(&now, NULL);
@@ -1461,9 +1468,15 @@ void *clientwr(void *arg) {
debug(DBG_WARN, "clientwr: failed to generate random auth");
continue;
}
+ statsrvrq.buf = malloc(sizeof(statsrvbuf));
+ if (!statsrvrq.buf) {
+ debug(DBG_ERR, "clientwr: malloc failed");
+ continue;
+ }
+ memcpy(statsrvrq.buf, statsrvbuf, sizeof(statsrvbuf));
debug(DBG_DBG, "clientwr: sending status server to %s", server->peer.host);
lastsend.tv_sec = now.tv_sec;
- sendrq(server, NULL, &statsrvrq);
+ sendrq(server, &statsrvrq);
}
}
}
@@ -1496,9 +1509,6 @@ void *udpserverwr(void *arg) {
void *udpserverrd(void *arg) {
struct request rq;
- unsigned char *buf;
- struct server *to;
- struct client *fr;
pthread_t udpserverwrth;
if ((udp_server_sock = bindtoaddr(udp_server_listen->addrinfo)) < 0)
@@ -1511,16 +1521,9 @@ void *udpserverrd(void *arg) {
debugx(1, DBG_ERR, "pthread_create failed");
for (;;) {
- fr = NULL;
memset(&rq, 0, sizeof(struct request));
- buf = radudpget(udp_server_sock, &fr, NULL, &rq.fromsa);
- to = radsrv(&rq, buf, fr);
- if (!to) {
- free(buf);
- debug(DBG_INFO, "udpserverrd: ignoring request, no place to send it");
- continue;
- }
- sendrq(to, fr, &rq);
+ rq.buf = radudpget(udp_server_sock, &rq.from, NULL, &rq.fromsa);
+ radsrv(&rq);
}
}
@@ -1565,9 +1568,7 @@ void *tlsserverwr(void *arg) {
void *tlsserverrd(void *arg) {
struct request rq;
- char unsigned *buf;
unsigned long error;
- struct server *to;
int s;
struct client *client = (struct client *)arg;
pthread_t tlsserverwrth;
@@ -1588,18 +1589,13 @@ void *tlsserverrd(void *arg) {
goto errexit;
}
for (;;) {
- buf = radtlsget(client->peer.ssl);
- if (!buf)
+ memset(&rq, 0, sizeof(struct request));
+ rq.buf = radtlsget(client->peer.ssl);
+ if (!rq.buf)
break;
debug(DBG_DBG, "tlsserverrd: got Radius message from %s", client->peer.host);
- memset(&rq, 0, sizeof(struct request));
- to = radsrv(&rq, buf, client);
- if (!to) {
- free(buf);
- debug(DBG_INFO, "tlsserverrd: ignoring request, no place to send it");
- continue;
- }
- sendrq(to, client, &rq);
+ rq.from = client;
+ radsrv(&rq);
}
debug(DBG_ERR, "tlsserverrd: connection lost");
/* stop writer by setting peer.ssl to NULL and give signal in case waiting for data */
@@ -1657,7 +1653,7 @@ int tlslistener() {
close(snew);
continue;
}
- client->peer.ssl = SSL_new(ssl_ctx);
+ client->peer.ssl = SSL_new(client->peer.ssl_ctx);
SSL_set_fd(client->peer.ssl, snew);
if (pthread_create(&tlsserverth, NULL, tlsserverrd, (void *)client)) {
debug(DBG_ERR, "tlslistener: pthread_create failed");
@@ -1672,23 +1668,139 @@ int tlslistener() {
return 0;
}
-void addrealm(char *value, char *server) {
+void tlsadd(char *value, char *cacertfile, char *cacertpath, char *certfile, char *certkeyfile, char *certkeypwd) {
+ struct tls *new;
+ SSL_CTX *ctx;
int i;
- struct realm *realm;
+ unsigned long error;
- for (i = 0; i < server_count; i++)
- if (!strcasecmp(server, servers[i].peer.host))
+ if (!certfile || !certkeyfile)
+ debugx(1, DBG_ERR, "TLSCertificateFile and TLSCertificateKeyFile must be specified in TLS context %s", value);
+
+ if (!cacertfile && !cacertpath)
+ debugx(1, DBG_ERR, "CA Certificate file or path need to be specified in TLS context %s", value);
+
+ if (!ssl_locks) {
+ ssl_locks = malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
+ ssl_lock_count = OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
+ for (i = 0; i < CRYPTO_num_locks(); i++) {
+ ssl_lock_count[i] = 0;
+ pthread_mutex_init(&ssl_locks[i], NULL);
+ }
+ CRYPTO_set_id_callback(ssl_thread_id);
+ CRYPTO_set_locking_callback(ssl_locking_callback);
+
+ SSL_load_error_strings();
+ SSL_library_init();
+
+ while (!RAND_status()) {
+ time_t t = time(NULL);
+ pid_t pid = getpid();
+ RAND_seed((unsigned char *)&t, sizeof(time_t));
+ RAND_seed((unsigned char *)&pid, sizeof(pid));
+ }
+ }
+ ctx = SSL_CTX_new(TLSv1_method());
+ if (certkeypwd) {
+ SSL_CTX_set_default_passwd_cb_userdata(ctx, certkeypwd);
+ SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
+ }
+ if (!SSL_CTX_use_certificate_chain_file(ctx, certfile) ||
+ !SSL_CTX_use_PrivateKey_file(ctx, certkeyfile, SSL_FILETYPE_PEM) ||
+ !SSL_CTX_check_private_key(ctx) ||
+ !SSL_CTX_load_verify_locations(ctx, cacertfile, cacertpath)) {
+ while ((error = ERR_get_error()))
+ debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
+ debugx(1, DBG_ERR, "Error initialising SSL/TLS in TLS context %s", value);
+ }
+
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
+ SSL_CTX_set_verify_depth(ctx, MAX_CERT_DEPTH + 1);
+
+ tls_count++;
+ tls = realloc(tls, tls_count * sizeof(struct tls));
+ if (!tls)
+ debugx(1, DBG_ERR, "malloc failed");
+ new = tls + tls_count - 1;
+ memset(new, 0, sizeof(struct tls));
+ new->name = stringcopy(value, 0);
+ if (!new->name)
+ debugx(1, DBG_ERR, "malloc failed");
+ new->ctx = ctx;
+ new->count = 0;
+ debug(DBG_DBG, "tlsadd: added TLS context %s", value);
+}
+
+void tlsfree() {
+ int i;
+ for (i = 0; i < tls_count; i++)
+ if (!tls[i].count)
+ SSL_CTX_free(tls[i].ctx);
+ tls_count = 0;
+ free(tls);
+ tls = NULL;
+}
+
+SSL_CTX *tlsgetctx(char *alt1, char *alt2) {
+ int i, c1 = -1, c2 = -1;
+ for (i = 0; i < tls_count; i++) {
+ if (!strcasecmp(tls[i].name, alt1)) {
+ c1 = i;
break;
- if (i == server_count)
- debugx(1, DBG_ERR, "addrealm failed, no server %s", server);
+ }
+ if (c2 == -1 && alt2 && !strcasecmp(tls[i].name, alt2))
+ c2 = i;
+ }
+
+ i = (c1 == -1 ? c2 : c1);
+ if (i == -1)
+ return NULL;
+ tls[i].count++;
+ return tls[i].ctx;
+}
+
+void addrealm(char *value, char *server, char *message) {
+ int i, n;
+ struct realm *realm;
+ char *s, *regex = NULL;
- /* temporary warnings */
- if (*value == '*')
- debugx(1, DBG_ERR, "Regexps are now used for specifying realms, a string\nstarting with '*' is meaningless, you probably want '.*' for matching everything\nEXITING\n");
- if (value[strlen(value) - 1] != '$' && value[strlen(value) - 1] != '*') {
- debug(DBG_ERR, "Regexps are now used for specifying realms, you\nprobably want to rewrite this as e.g. '@example\\.com$' or '\\.com$'\nYou can even do things like '^[a-n].*@example\\.com$' to make about half of the\nusers use this server. Note that the matching is case insensitive.\n");
- sleep(3);
+ if (server) {
+ for (i = 0; i < server_count; i++)
+ if (!strcasecmp(server, servers[i].peer.host))
+ break;
+ if (i == server_count)
+ debugx(1, DBG_ERR, "addrealm failed, no server %s", server);
}
+
+ if (*value == '/') {
+ /* regexp, remove optional trailing / if present */
+ if (value[strlen(value) - 1] == '/')
+ value[strlen(value) - 1] = '\0';
+ } else {
+ /* not a regexp, let us make it one */
+ if (*value == '*' && !value[1])
+ regex = stringcopy(".*", 0);
+ else {
+ for (n = 0, s = value; *s;)
+ if (*s++ == '.')
+ n++;
+ regex = malloc(strlen(value) + n + 3);
+ if (regex) {
+ regex[0] = '@';
+ for (n = 1, s = value; *s; s++) {
+ if (*s == '.')
+ regex[n++] = '\\';
+ regex[n++] = *s;
+ }
+ regex[n++] = '$';
+ regex[n] = '\0';
+ }
+ }
+ if (!regex)
+ debugx(1, DBG_ERR, "malloc failed");
+ debug(DBG_DBG, "addrealm: constructed regexp %s from %s", regex, value);
+ }
+
realm_count++;
realms = realloc(realms, realm_count * sizeof(struct realm));
if (!realms)
@@ -1696,9 +1808,17 @@ void addrealm(char *value, char *server) {
realm = realms + realm_count - 1;
memset(realm, 0, sizeof(struct realm));
realm->name = stringcopy(value, 0);
- realm->server = servers + i;
- if (regcomp(&realm->regex, value, REG_ICASE | REG_NOSUB))
- debugx(1, DBG_ERR, "addrealm: failed to compile regular expression %s", value);
+ if (!realm->name)
+ debugx(1, DBG_ERR, "malloc failed");
+ if (message && strlen(message) > 253)
+ debugx(1, DBG_ERR, "ReplyMessage can be at most 253 bytes");
+ realm->message = message;
+ if (server)
+ realm->server = servers + i;
+ if (regcomp(&realm->regex, regex ? regex : value + 1, REG_ICASE | REG_NOSUB))
+ debugx(1, DBG_ERR, "addrealm: failed to compile regular expression %s", regex ? regex : value + 1);
+ if (regex)
+ free(regex);
debug(DBG_DBG, "addrealm: added realm %s for server %s", value, server);
}
@@ -1740,58 +1860,9 @@ char *parsehostport(char *s, struct peer *peer) {
return p;
}
-/* TODO remove this */
-/* * is default, else longest match ... ";" used for separator */
-char *parserealmlist(char *s, struct server *server) {
-#if 0
- char *p;
- int i, n, l;
- char *realmdata;
- char **realms;
-
- for (p = s, n = 1; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++)
- if (*p == ';')
- n++;
- l = p - s;
- if (!l)
- debugx(1, DBG_ERR, "realm list must be specified");
-
- realmdata = stringcopy(s, l);
- realms = malloc((1+n) * sizeof(char *));
- if (!realms)
- debugx(1, DBG_ERR, "malloc failed");
- realms[0] = realmdata;
- for (n = 1, i = 0; i < l; i++)
- if (realmdata[i] == ';') {
- realmdata[i] = '\0';
- realms[n++] = realmdata + i + 1;
- }
- for (i = 0; i < n; i++)
- addrealm(realms[i], server->peer.host);
- free(realms);
- free(realmdata);
- return p;
-#else
- char *start;
- char *realm;
-
- for (start = s;; s++)
- if (!*s || *s == ';' || *s == ' ' || *s == '\t' || *s == '\n') {
- if (s - start > 0) {
- realm = stringcopy(start, s - start);
- addrealm(realm, server->peer.host);
- free(realm);
- }
- if (*s != ';')
- return s;
- start = s + 1;
- }
-#endif
-}
-
FILE *openconfigfile(const char *filename) {
FILE *f;
- char pathname[100], *base;
+ char pathname[100], *base = NULL;
f = fopen(filename, "r");
if (f) {
@@ -1813,133 +1884,6 @@ FILE *openconfigfile(const char *filename) {
return f;
}
-/* exactly one argument must be non-NULL */
-void getconfig(const char *serverfile, const char *clientfile) {
- FILE *f;
- char line[1024];
- char *p, *field;
- struct client *client;
- struct server *server;
- struct peer *peer;
- int i, count, *ucount, *tcount;
-
- f = openconfigfile(serverfile ? serverfile : clientfile);
- if (serverfile) {
- ucount = &server_udp_count;
- tcount = &server_tls_count;
- } else {
- ucount = &client_udp_count;
- tcount = &client_tls_count;
- }
- while (fgets(line, 1024, f)) {
- for (p = line; *p == ' ' || *p == '\t'; p++);
- switch (*p) {
- case '#':
- case '\n':
- break;
- case 'T':
- (*tcount)++;
- break;
- case 'U':
- (*ucount)++;
- break;
- default:
- debugx(1, DBG_ERR, "type must be U or T, got %c", *p);
- }
- }
-
- if (serverfile) {
- count = server_count = server_udp_count + server_tls_count;
- servers = calloc(count, sizeof(struct server));
- if (!servers)
- debugx(1, DBG_ERR, "malloc failed");
- } else {
- if (client_udp_count) {
- udp_server_replyq.replies = malloc(client_udp_count * MAX_REQUESTS * sizeof(struct reply));
- if (!udp_server_replyq.replies)
- debugx(1, DBG_ERR, "malloc failed");
- udp_server_replyq.size = client_udp_count * MAX_REQUESTS;
- udp_server_replyq.count = 0;
- pthread_mutex_init(&udp_server_replyq.count_mutex, NULL);
- pthread_cond_init(&udp_server_replyq.count_cond, NULL);
- }
-
- count = client_count = client_udp_count + client_tls_count;
- clients = calloc(count, sizeof(struct client));
- if (!clients)
- debugx(1, DBG_ERR, "malloc failed");
- }
-
- rewind(f);
- for (i = 0; i < count && fgets(line, 1024, f);) {
- if (serverfile) {
- server = &servers[i];
- peer = &server->peer;
- } else {
- client = &clients[i];
- peer = &client->peer;
- }
- for (p = line; *p == ' ' || *p == '\t'; p++);
- if (*p == '#' || *p == '\n')
- continue;
- peer->type = *p; /* we already know it must be U or T */
- for (p++; *p == ' ' || *p == '\t'; p++);
- p = parsehostport(p, peer);
- for (; *p == ' ' || *p == '\t'; p++);
- if (serverfile) {
- p = parserealmlist(p, server);
- for (; *p == ' ' || *p == '\t'; p++);
- }
- field = p;
- for (; *p && *p != ' ' && *p != '\t' && *p != '\n'; p++);
- if (field == p) {
- /* no secret set and end of line, line is complete if TLS */
- if (peer->type == 'U')
- debugx(1, DBG_ERR, "secret must be specified for UDP");
- peer->secret = stringcopy(DEFAULT_TLS_SECRET, 0);
- } else {
- peer->secret = stringcopy(field, p - field);
- /* check that rest of line only white space */
- for (; *p == ' ' || *p == '\t'; p++);
- if (*p && *p != '\n')
- debugx(1, DBG_ERR, "max 4 fields per line, found a 5th");
- }
-
- if ((serverfile && !resolvepeer(&server->peer, 0)) ||
- (clientfile && !resolvepeer(&client->peer, 0)))
- debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", peer->host, peer->port);
-
- if (serverfile) {
- pthread_mutex_init(&server->lock, NULL);
- server->sock = -1;
- server->requests = calloc(MAX_REQUESTS, sizeof(struct request));
- if (!server->requests)
- debugx(1, DBG_ERR, "malloc failed");
- server->newrq = 0;
- pthread_mutex_init(&server->newrq_mutex, NULL);
- pthread_cond_init(&server->newrq_cond, NULL);
- } else {
- if (peer->type == 'U')
- client->replyq = &udp_server_replyq;
- else {
- client->replyq = malloc(sizeof(struct replyq));
- if (!client->replyq)
- debugx(1, DBG_ERR, "malloc failed");
- client->replyq->replies = calloc(MAX_REQUESTS, sizeof(struct reply));
- if (!client->replyq->replies)
- debugx(1, DBG_ERR, "malloc failed");
- client->replyq->size = MAX_REQUESTS;
- client->replyq->count = 0;
- pthread_mutex_init(&client->replyq->count_mutex, NULL);
- pthread_cond_init(&client->replyq->count_cond, NULL);
- }
- }
- debug(DBG_DBG, "got type %c, host %s, port %s, secret %s", peer->type, peer->host, peer->port, peer->secret);
- i++;
- }
- fclose(f);
-}
-
struct peer *server_create(char type) {
struct peer *server;
char *conf;
@@ -1963,6 +1907,37 @@ struct peer *server_create(char type) {
return server;
}
+/* returns NULL on error, where to continue parsing if token and ok. E.g. "" will return token with empty string */
+char *strtokenquote(char *s, char **token, char *del, char *quote, char *comment) {
+ char *t = s, *q, *r;
+
+ if (!t || !token || !del)
+ return NULL;
+ while (*t && strchr(del, *t))
+ t++;
+ if (!*t || (comment && strchr(comment, *t))) {
+ *token = NULL;
+ return t + 1; /* needs to be non-NULL, but value doesn't matter */
+ }
+ if (quote && (q = strchr(quote, *t))) {
+ t++;
+ r = t;
+ while (*t && *t != *q)
+ t++;
+ if (!*t || (t[1] && !strchr(del, t[1])))
+ return NULL;
+ *t = '\0';
+ *token = r;
+ return t + 1;
+ }
+ *token = t;
+ t++;
+ while (*t && !strchr(del, *t))
+ t++;
+ *t = '\0';
+ return t + 1;
+}
+
/* Parses config with following syntax:
* One of these:
* option-name value
@@ -1976,17 +1951,24 @@ struct peer *server_create(char type) {
void getgeneralconfig(FILE *f, char *block, ...) {
va_list ap;
char line[1024];
- char *tokens[3], *opt, *val, *word, **str;
- int type, tcount, conftype;
- void (*cbk)(FILE *, char *, char *);
+ /* initialise lots of stuff to avoid stupid compiler warnings */
+ char *tokens[3], *s, *opt = NULL, *val = NULL, *word, *optval, **str = NULL;
+ int type = 0, tcount, conftype = 0;
+ void (*cbk)(FILE *, char *, char *, char *) = NULL;
while (fgets(line, 1024, f)) {
- tokens[0] = strtok(line, " \t\n");
- if (!*tokens || **tokens == '#')
+ s = line;
+ for (tcount = 0; tcount < 3; tcount++) {
+ s = strtokenquote(s, &tokens[tcount], " \t\n", "\"'", tcount ? NULL : "#");
+ if (!s)
+ debugx(1, DBG_ERR, "Syntax error in line starting with: %s", line);
+ if (!tokens[tcount])
+ break;
+ }
+ if (!tcount || **tokens == '#')
continue;
- for (tcount = 1; tcount < 3 && (tokens[tcount] = strtok(NULL, " \t\n")); tcount++);
-
- if (tcount && **tokens == '}') {
+
+ if (**tokens == '}') {
if (block)
return;
debugx(1, DBG_ERR, "configuration error, found } with no matching {");
@@ -2018,6 +2000,9 @@ void getgeneralconfig(FILE *f, char *block, ...) {
debugx(1, DBG_ERR, "configuration error, syntax error in line starting with %s", tokens[0]);
}
+ if (!*val)
+ debugx(1, DBG_ERR, "configuration error, option %s needs a non-empty value", opt);
+
va_start(ap, block);
while ((word = va_arg(ap, char *))) {
type = va_arg(ap, int);
@@ -2028,7 +2013,7 @@ void getgeneralconfig(FILE *f, char *block, ...) {
debugx(1, DBG_ERR, "getgeneralconfig: internal parameter error");
break;
case CONF_CBK:
- cbk = va_arg(ap, void (*)(FILE *, char *, char *));
+ cbk = va_arg(ap, void (*)(FILE *, char *, char *, char *));
break;
default:
debugx(1, DBG_ERR, "getgeneralconfig: internal parameter error");
@@ -2059,7 +2044,12 @@ void getgeneralconfig(FILE *f, char *block, ...) {
*str = stringcopy(val, 0);
break;
case CONF_CBK:
- cbk(f, opt, val);
+ optval = malloc(strlen(opt) + strlen(val) + 2);
+ if (!optval)
+ debugx(1, DBG_ERR, "malloc failed");
+ sprintf(optval, "%s %s", opt, val);
+ cbk(f, optval, opt, val);
+ free(optval);
break;
default:
debugx(1, DBG_ERR, "getgeneralconfig: internal parameter error");
@@ -2067,23 +2057,19 @@ void getgeneralconfig(FILE *f, char *block, ...) {
}
}
-void confclsrv_cb(FILE *f, char *opt, char *val) {
- char *type = NULL, *secret = NULL, *port = NULL, *statusserver = NULL;
- char *block;
+void confclsrv_cb(FILE *f, char *block, char *opt, char *val) {
+ char *type = NULL, *secret = NULL, *port = NULL, *tls = NULL, *statusserver = NULL;
struct client *client = NULL;
struct server *server = NULL;
struct peer *peer;
-
- block = malloc(strlen(opt) + strlen(val) + 2);
- if (!block)
- debugx(1, DBG_ERR, "malloc failed");
- sprintf(block, "%s %s", opt, val);
+
debug(DBG_DBG, "confclsrv_cb called for %s", block);
if (!strcasecmp(opt, "client")) {
getgeneralconfig(f, block,
"type", CONF_STR, &type,
"secret", CONF_STR, &secret,
+ "tls", CONF_STR, &tls,
NULL
);
client_count++;
@@ -2098,6 +2084,7 @@ void confclsrv_cb(FILE *f, char *opt, char *val) {
"type", CONF_STR, &type,
"secret", CONF_STR, &secret,
"port", CONF_STR, &port,
+ "tls", CONF_STR, &tls,
"StatusServer", CONF_STR, &statusserver,
NULL
);
@@ -2130,14 +2117,18 @@ void confclsrv_cb(FILE *f, char *opt, char *val) {
peer->port = stringcopy(DEFAULT_UDP_PORT, 0);
}
} else if (type && !strcasecmp(type, "tls")) {
- peer->type = 'T';
- if (client)
+ if (client) {
+ peer->ssl_ctx = tls ? tlsgetctx(tls, NULL) : tlsgetctx("defaultclient", "default");
client_tls_count++;
- else {
+ } else {
+ peer->ssl_ctx = tls ? tlsgetctx(tls, NULL) : tlsgetctx("defaultserver", "default");
server_tls_count++;
if (!port)
peer->port = stringcopy(DEFAULT_TLS_PORT, 0);
}
+ if (!peer->ssl_ctx)
+ debugx(1, DBG_ERR, "error in block %s, no tls context defined", block);
+ peer->type = 'T';
} else
debugx(1, DBG_ERR, "error in block %s, type must be set to UDP or TLS", block);
free(type);
@@ -2178,30 +2169,43 @@ void confclsrv_cb(FILE *f, char *opt, char *val) {
pthread_mutex_init(&server->newrq_mutex, NULL);
pthread_cond_init(&server->newrq_cond, NULL);
}
-
- free(block);
}
-void confrealm_cb(FILE *f, char *opt, char *val) {
- char *server = NULL;
- char *block;
+void confrealm_cb(FILE *f, char *block, char *opt, char *val) {
+ char *server = NULL, *msg = NULL;
- block = malloc(strlen(opt) + strlen(val) + 2);
- if (!block)
- debugx(1, DBG_ERR, "malloc failed");
- sprintf(block, "%s %s", opt, val);
debug(DBG_DBG, "confrealm_cb called for %s", block);
getgeneralconfig(f, block,
"server", CONF_STR, &server,
+ "ReplyMessage", CONF_STR, &msg,
NULL
);
- if (!server)
- debugx(1, DBG_ERR, "error in block %s, server must be specified", block);
- addrealm(val, server);
+ addrealm(val, server, msg);
free(server);
- free(block);
+}
+
+void conftls_cb(FILE *f, char *block, char *opt, char *val) {
+ char *cacertfile = NULL, *cacertpath = NULL, *certfile = NULL, *certkeyfile = NULL, *certkeypwd = NULL;
+
+ debug(DBG_DBG, "conftls_cb called for %s", block);
+
+ getgeneralconfig(f, block,
+ "CACertificateFile", CONF_STR, &cacertfile,
+ "CACertificatePath", CONF_STR, &cacertpath,
+ "CertificateFile", CONF_STR, &certfile,
+ "CertificateKeyFile", CONF_STR, &certkeyfile,
+ "CertificateKeyPassword", CONF_STR, &certkeypwd,
+ NULL
+ );
+
+ tlsadd(val, cacertfile, cacertpath, certfile, certkeyfile, certkeypwd);
+ free(cacertfile);
+ free(cacertpath);
+ free(certfile);
+ free(certkeyfile);
+ free(certkeypwd);
}
void getmainconfig(const char *configfile) {
@@ -2212,11 +2216,6 @@ void getmainconfig(const char *configfile) {
memset(&options, 0, sizeof(options));
getgeneralconfig(f, NULL,
- "TLSCACertificateFile", CONF_STR, &options.tlscacertificatefile,
- "TLSCACertificatePath", CONF_STR, &options.tlscacertificatepath,
- "TLSCertificateFile", CONF_STR, &options.tlscertificatefile,
- "TLSCertificateKeyFile", CONF_STR, &options.tlscertificatekeyfile,
- "TLSCertificateKeyPassword", CONF_STR, &options.tlscertificatekeypassword,
"ListenUDP", CONF_STR, &options.listenudp,
"ListenTCP", CONF_STR, &options.listentcp,
"LogLevel", CONF_STR, &loglevel,
@@ -2224,10 +2223,12 @@ void getmainconfig(const char *configfile) {
"Client", CONF_CBK, confclsrv_cb,
"Server", CONF_CBK, confclsrv_cb,
"Realm", CONF_CBK, confrealm_cb,
+ "TLS", CONF_CBK, conftls_cb,
NULL
);
fclose(f);
-
+ tlsfree();
+
if (loglevel) {
if (strlen(loglevel) != 1 || *loglevel < '1' || *loglevel > '4')
debugx(1, DBG_ERR, "error in %s, value of option LogLevel is %s, must be 1, 2, 3 or 4", configfile, loglevel);
@@ -2287,6 +2288,7 @@ int main(int argc, char **argv) {
getargs(argc, argv, &foreground, &loglevel, &configfile);
if (loglevel)
debug_set_level(loglevel);
+ debug(DBG_INFO, "radsecproxy revision $Rev$ starting");
getmainconfig(configfile ? configfile : CONFIG_MAIN);
if (loglevel)
options.loglevel = loglevel;
@@ -2300,13 +2302,10 @@ int main(int argc, char **argv) {
debug_set_destination(options.logdestination);
}
- /* TODO remove getconfig completely when all use new config method */
if (!server_count)
- getconfig(CONFIG_SERVERS, NULL);
+ debugx(1, DBG_ERR, "No servers configured, nothing to do, exiting");
if (!client_count)
- getconfig(NULL, CONFIG_CLIENTS);
-
- /* TODO exit if not at least one client and one server configured */
+ debugx(1, DBG_ERR, "No clients configured, nothing to do, exiting");
if (!realm_count)
debugx(1, DBG_ERR, "No realms configured, nothing to do, exiting");
@@ -2319,9 +2318,6 @@ int main(int argc, char **argv) {
debugx(1, DBG_ERR, "pthread_create failed");
}
- if (client_tls_count || server_tls_count)
- ssl_ctx = ssl_init();
-
for (i = 0; i < server_count; i++)
if (pthread_create(&servers[i].clientth, NULL, clientwr, (void *)&servers[i]))
debugx(1, DBG_ERR, "pthread_create failed");
diff --git a/radsecproxy.conf-example b/radsecproxy.conf-example
index 9facde7..029c7e1 100644
--- a/radsecproxy.conf-example
+++ b/radsecproxy.conf-example
@@ -1,17 +1,8 @@
#Master config file, must be in /etc/radsecproxy or proxy's current directory
# All possible config options are listed below
-#
-# You must specify at least one of TLSCACertificateFile or TLSCACertificatePath
-# for TLS to work. We always verify peer certificate (both client and server)
-#TLSCACertificateFile /etc/cacerts/CA.pem
-TLSCACertificatePath /etc/cacerts
-
-# You must specify the below for TLS, we will always present our certificate
-TLSCertificateFile /etc/hostcertkey/host.example.com.pem
-TLSCertificateKeyFile /etc/hostcertkey/host.example.com.key.pem
-# Optionally specify password if key is encrypted (not very secure)
-TLSCertificateKeyPassword follow the white rabbit
+# First you may define any global options, these are:
+#
# You can optionally specify addresses and ports to listen on
# Max one of each, below are just multiple examples
#ListenUDP *:1814
@@ -29,20 +20,55 @@ TLSCertificateKeyPassword follow the white rabbit
#LogDestination x-syslog://
#LogDestination x-syslog://log_local2
+#If we have TLS clients or servers we must define at least one tls block.
+#You can name them whatever you like and then reference them by name when
+#specifying clients or servers later. There are however three special names
+#"default", "defaultclient" and "defaultserver". If no name is defined for
+#a client, the "defaultclient" block will be used if it exists, if not the
+#"default" will be used. For a server, "defaultserver" followed by "default"
+#will be checked.
+#
+#The simplest configuration you can do is:
+tls default {
+ # You must specify at least one of CACertificateFile or CACertificatePath
+ # for TLS to work. We always verify peer certificate (client and server)
+ # CACertificateFile /etc/cacerts/CA.pem
+ CACertificatePath /etc/cacerts
+
+ # You must specify the below for TLS, we always present our certificate
+ CertificateFile /etc/hostcertkey/host.example.com.pem
+ CertificateKeyFile /etc/hostcertkey/host.example.com.key.pem
+ # Optionally specify password if key is encrypted (not very secure)
+ CertificateKeyPassword "follow the white rabbit"
+}
+
+#If you want one cert for all clients and another for all servers, use
+#defaultclient and defaultserver instead of default. If we wanted some
+#particular server to use something else you could specify a block
+#"tls myserver" and then reference that for that server. If you always
+#name the tls block in the client/server config you don't need a default
+
#Now we configure clients, servers and realms. Note that these and
#also the lines above may be in any order, except that a realm
#can only be configured to use a server that is previously configured.
-#Also note that case insensitive regexp is used for realms, matching
-#the entire username string. The matching is done in the order the
-#realms are specified, using the first match found. Some examples are
+#A realm can be a literal domain name, * which matches all, or a
+#regexp. A regexp is specified by the character prefix /
+#For regexp we do case insensitive matching of the entire username string.
+#The matching of realms is done in the order they are specified, using the
+#first match found. Some examples are
#"@example\.com$", "\.com$", ".*" and "^[a-z].*@example\.com$".
#To treat local users separately you might try first specifying "@"
-#and after that ".*".
+#and after that "*".
client 2001:db8::1 {
type tls
secret verysecret
+#we could specify tls here, e.g.
+# tls myclient
+#in order to use tls parameters named myclient. We don't, so we will
+#use "tls defaultclient" if defined, or look for "tls default" as a
+#last resort
}
client 127.0.0.1 {
type udp
@@ -57,7 +83,7 @@ server 127.0.0.1 {
type UDP
secret secret
}
-realm @eduroam\.cc$ {
+realm eduroam.cc {
server 127.0.0.1
}
@@ -65,18 +91,38 @@ server 2001:db8::1 {
type TLS
port 2283
# secret is optional for TLS
+#we could specify tls here, e.g.
+# tls myserver
+#in order to use tls parameters named myserver. We don't, so we will
+#use "tls defaultserver" if defined, or look for "tls default" as a
+#last resort
}
server radius.example.com {
type tls
secret verysecret
+ StatusServer on
+# statusserver is optional, can be on or off. Off is default
}
-realm @example\.com$ {
+# Equivalent to example.com
+realm /@example\.com$ {
server 2001:db8::1
}
-realm \.com$ {
- server 2001:db8::1
+# One can define a realm without servers, the proxy will then reject
+# and requests matching this. Optionally one can specify ReplyMessage
+# attribute to be included in the reject message.
+#
+realm /\.com$ {
}
-realm .* {
+realm /^anonymous$ {
+ replymessage "No Access"
+}
+# The realm below is equivalent to /.*
+realm * {
server radius.example.com
}
+#If you don't have a default server you probably want to
+#reject all unknowns. Optionally you can also include a message
+#realm * {
+ replymessage "User unknown"
+}
diff --git a/radsecproxy.h b/radsecproxy.h
index b4dcfe6..c6b2b19 100644
--- a/radsecproxy.h
+++ b/radsecproxy.h
@@ -8,9 +8,7 @@
#define DEBUG_LEVEL 3
-#define CONFIG_MAIN "/etc/radsecproxy/radsecproxy.conf"
-#define CONFIG_SERVERS "/etc/radsecproxy/servers.conf"
-#define CONFIG_CLIENTS "/etc/radsecproxy/clients.conf"
+#define CONFIG_MAIN "/etc/radsecproxy.conf"
/* MAX_REQUESTS must be 256 due to Radius' 8 bit ID field */
#define MAX_REQUESTS 256
@@ -32,6 +30,7 @@
#define RAD_Attr_User_Name 1
#define RAD_Attr_User_Password 2
+#define RAD_Attr_Reply_Message 18
#define RAD_Attr_Vendor_Specific 26
#define RAD_Attr_Tunnel_Password 69
#define RAD_Attr_Message_Authenticator 80
@@ -43,11 +42,6 @@
#define CONF_CBK 2
struct options {
- char *tlscacertificatefile;
- char *tlscacertificatepath;
- char *tlscertificatefile;
- char *tlscertificatekeyfile;
- char *tlscertificatekeypassword;
char *listenudp;
char *listentcp;
char *logdestination;
@@ -86,6 +80,7 @@ struct peer {
char *port;
char *secret;
SSL *ssl;
+ SSL_CTX *ssl_ctx;
struct addrinfo *addrinfo;
};
@@ -111,10 +106,17 @@ struct server {
struct realm {
char *name;
+ char *message;
regex_t regex;
struct server *server;
};
+struct tls {
+ char *name;
+ SSL_CTX *ctx;
+ int count;
+};
+
#define RADLEN(x) ntohs(((uint16_t *)(x))[1])
#define ATTRTYPE(x) ((x)[0])