#include <openssl/x509v3.h>
#include "debug.h"
#include "list.h"
+#include "hash.h"
#include "util.h"
#include "gconfig.h"
#include "radsecproxy.h"
+#include "udp.h"
#include "tcp.h"
#include "tls.h"
#include "dtls.h"
static struct options options;
static struct list *clconfs, *srvconfs;
-struct list *realms, *tlsconfs, *rewriteconfs;
+struct list *realms;
+struct hash *tlsconfs, *rewriteconfs;
static struct addrinfo *srcprotores[4] = { NULL, NULL, NULL, NULL };
-static struct queue *udp_server_replyq = NULL;
-static int udp_client4_sock = -1;
-static int udp_client6_sock = -1;
-static int dtls_client4_sock = -1;
-static int dtls_client6_sock = -1;
-static pthread_mutex_t tlsconfs_lock;
static pthread_mutex_t *ssl_locks = NULL;
static long *ssl_lock_count;
extern int optind;
void freerealm(struct realm *realm);
void freeclsrvconf(struct clsrvconf *conf);
void freerqdata(struct request *rq);
-void *udpserverrd(void *arg);
-void *udpclientrd(void *arg);
-int clientradputudp(struct server *server, unsigned char *rad);
-X509 *verifytlscert(SSL *ssl);
-int radsrv(struct request *rq);
static const struct protodefs protodefs[] = {
{ "udp", /* UDP, assuming RAD_UDP defined as 0 */
udpserverrd, /* listener */
&options.sourceudp, /* srcaddrport */
NULL, /* connecter */
- udpclientrd, /* clientreader */
- clientradputudp /* clientradput */
+ NULL, /* clientconnreader */
+ clientradputudp, /* clientradput */
+ addclientudp, /* addclient */
+ addserverextraudp, /* addserverextra */
+ initextraudp /* initextra */
},
{ "tls", /* TLS, assuming RAD_TLS defined as 1 */
"mysecret", /* secretdefault */
tlslistener, /* listener */
&options.sourcetls, /* srcaddrport */
tlsconnect, /* connecter */
- tlsclientrd, /* clientreader */
- clientradputtls /* clientradput */
+ tlsclientrd, /* clientconnreader */
+ clientradputtls, /* clientradput */
+ NULL, /* addclient */
+ NULL, /* addserverextra */
+ NULL /* initextra */
},
{ "tcp", /* TCP, assuming RAD_TCP defined as 2 */
NULL, /* secretdefault */
tcplistener, /* listener */
&options.sourcetcp, /* srcaddrport */
tcpconnect, /* connecter */
- tcpclientrd, /* clientreader */
- clientradputtcp /* clientradput */
+ tcpclientrd, /* clientconnreader */
+ clientradputtcp, /* clientradput */
+ NULL, /* addclient */
+ NULL, /* addserverextra */
+ NULL /* initextra */
},
{ "dtls", /* DTLS, assuming RAD_DTLS defined as 3 */
"mysecret", /* secretdefault */
udpdtlsserverrd, /* listener */
&options.sourcedtls, /* srcaddrport */
dtlsconnect, /* connecter */
- dtlsclientrd, /* clientreader */
- clientradputdtls /* clientradput */
+ dtlsclientrd, /* clientconnreader */
+ clientradputdtls, /* clientradput */
+ addclientdtls, /* addclient */
+ addserverextradtls, /* addserverextra */
+ initextradtls /* initextra */
},
{ NULL
}
return 1;
}
-int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only) {
- int s, on = 1;
- struct addrinfo *res;
-
- for (res = addrinfo; res; res = res->ai_next) {
- if (family != AF_UNSPEC && family != res->ai_family)
- continue;
- s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
- if (s < 0) {
- debug(DBG_WARN, "bindtoaddr: socket failed");
- continue;
- }
- if (reuse)
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-#ifdef IPV6_V6ONLY
- if (v6only)
- setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
-#endif
-
- if (!bind(s, res->ai_addr, res->ai_addrlen))
- return s;
- debug(DBG_WARN, "bindtoaddr: bind failed");
- close(s);
- }
- return -1;
-}
-
char *parsehostport(char *s, struct clsrvconf *conf, char *default_port) {
char *p, *field;
int ipv6 = 0;
free(res);
}
+int bindtoaddr(struct addrinfo *addrinfo, int family, int reuse, int v6only) {
+ int s, on = 1;
+ struct addrinfo *res;
+
+ for (res = addrinfo; res; res = res->ai_next) {
+ if (family != AF_UNSPEC && family != res->ai_family)
+ continue;
+ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s < 0) {
+ debug(DBG_WARN, "bindtoaddr: socket failed");
+ continue;
+ }
+ if (reuse)
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+#ifdef IPV6_V6ONLY
+ if (v6only)
+ setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+#endif
+ if (!bind(s, res->ai_addr, res->ai_addrlen))
+ return s;
+ debug(DBG_WARN, "bindtoaddr: bind failed");
+ close(s);
+ }
+ return -1;
+}
+
int connecttcp(struct addrinfo *addrinfo, struct addrinfo *src) {
int s;
struct addrinfo *res;
}
/* returns next config of given type, or NULL */
-struct clsrvconf *find_conf_type(uint8_t type, struct list *confs, struct list_node **cur) {
+struct clsrvconf *find_clconf_type(uint8_t type, struct list_node **cur) {
struct list_node *entry;
struct clsrvconf *conf;
- for (entry = (cur && *cur ? list_next(*cur) : list_first(confs)); entry; entry = list_next(entry)) {
+ for (entry = (cur && *cur ? list_next(*cur) : list_first(clconfs)); entry; entry = list_next(entry)) {
conf = (struct clsrvconf *)entry->data;
if (conf->type == type) {
if (cur)
memset(new, 0, sizeof(struct client));
new->conf = conf;
- new->replyq = conf->type == RAD_UDP ? udp_server_replyq : newqueue();
- if (conf->type == RAD_DTLS)
- new->rbios = newqueue();
+ if (conf->pdef->addclient)
+ conf->pdef->addclient(new);
+ else
+ new->replyq = newqueue();
list_push(conf->clients, new);
return new;
}
freeclsrvres(res);
}
- switch (type) {
- case RAD_UDP:
- switch (conf->addrinfo->ai_family) {
- case AF_INET:
- if (udp_client4_sock < 0) {
- udp_client4_sock = bindtoaddr(srcprotores[RAD_UDP], AF_INET, 0, 1);
- if (udp_client4_sock < 0)
- debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->host);
- }
- conf->servers->sock = udp_client4_sock;
- break;
- case AF_INET6:
- if (udp_client6_sock < 0) {
- udp_client6_sock = bindtoaddr(srcprotores[RAD_UDP], AF_INET6, 0, 1);
- if (udp_client6_sock < 0)
- debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->host);
- }
- conf->servers->sock = udp_client6_sock;
- break;
- default:
- debugx(1, DBG_ERR, "addserver: unsupported address family");
- }
- break;
- case RAD_DTLS:
- switch (conf->addrinfo->ai_family) {
- case AF_INET:
- if (dtls_client4_sock < 0) {
- dtls_client4_sock = bindtoaddr(srcprotores[RAD_DTLS], AF_INET, 0, 1);
- if (dtls_client4_sock < 0)
- debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->host);
- }
- conf->servers->sock = dtls_client4_sock;
- break;
- case AF_INET6:
- if (dtls_client6_sock < 0) {
- dtls_client6_sock = bindtoaddr(srcprotores[RAD_DTLS], AF_INET6, 0, 1);
- if (dtls_client6_sock < 0)
- debugx(1, DBG_ERR, "addserver: failed to create client socket for server %s", conf->host);
- }
- conf->servers->sock = dtls_client6_sock;
- break;
- default:
- debugx(1, DBG_ERR, "addserver: unsupported address family");
- }
- break;
- default:
- conf->servers->sock = -1;
- }
+ conf->servers->sock = -1;
+ if (conf->pdef->addserverextra)
+ conf->pdef->addserverextra(conf);
conf->servers->requests = calloc(MAX_REQUESTS, sizeof(struct request));
if (!conf->servers->requests) {
return 0;
}
-/* exactly one of client and server must be non-NULL */
-/* return who we received from in *client or *server */
-/* return from in sa if not NULL */
-unsigned char *radudpget(int s, struct client **client, struct server **server, struct sockaddr_storage *sa) {
- int cnt, len;
- unsigned char buf[4], *rad = NULL;
- struct sockaddr_storage from;
- socklen_t fromlen = sizeof(from);
- struct clsrvconf *p;
- struct list_node *node;
- fd_set readfds;
-
- for (;;) {
- if (rad) {
- free(rad);
- rad = NULL;
- }
- FD_ZERO(&readfds);
- FD_SET(s, &readfds);
- if (select(s + 1, &readfds, NULL, NULL, NULL) < 1)
- continue;
- cnt = recvfrom(s, buf, 4, MSG_PEEK | MSG_TRUNC, (struct sockaddr *)&from, &fromlen);
- if (cnt == -1) {
- debug(DBG_WARN, "radudpget: recv failed");
- continue;
- }
- if (cnt < 20) {
- debug(DBG_WARN, "radudpget: length too small");
- recv(s, buf, 4, 0);
- continue;
- }
-
- p = find_conf(RAD_UDP, (struct sockaddr *)&from, client ? clconfs : srvconfs, NULL);
- if (!p) {
- debug(DBG_WARN, "radudpget: got packet from wrong or unknown UDP peer %s, ignoring", addr2string((struct sockaddr *)&from, fromlen));
- recv(s, buf, 4, 0);
- continue;
- }
-
- len = RADLEN(buf);
- if (len < 20) {
- debug(DBG_WARN, "radudpget: length too small");
- recv(s, buf, 4, 0);
- continue;
- }
-
- rad = malloc(len);
- if (!rad) {
- debug(DBG_ERR, "radudpget: malloc failed");
- recv(s, buf, 4, 0);
- continue;
- }
-
- cnt = recv(s, rad, len, MSG_TRUNC);
- debug(DBG_DBG, "radudpget: got %d bytes from %s", cnt, addr2string((struct sockaddr *)&from, fromlen));
-
- if (cnt < len) {
- debug(DBG_WARN, "radudpget: packet smaller than length field in radius header");
- continue;
- }
- if (cnt > len)
- debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
-
- if (client) {
- node = list_first(p->clients);
- *client = node ? (struct client *)node->data : addclient(p);
- if (!*client)
- continue;
- } else if (server)
- *server = p->servers;
- break;
- }
- if (sa)
- *sa = from;
- return rad;
-}
-
int subjectaltnameaddr(X509 *cert, int family, struct in6_addr *addr) {
int loc, i, l, n, r = 0;
char *v;
return 1;
}
-int clientradputudp(struct server *server, unsigned char *rad) {
- size_t len;
- struct sockaddr_storage sa;
- struct sockaddr *sap;
- struct clsrvconf *conf = server->conf;
- in_port_t *port = NULL;
-
- len = RADLEN(rad);
-
- if (*rad == RAD_Accounting_Request) {
- sap = (struct sockaddr *)&sa;
- memcpy(sap, conf->addrinfo->ai_addr, conf->addrinfo->ai_addrlen);
- } else
- sap = conf->addrinfo->ai_addr;
-
- switch (sap->sa_family) {
- case AF_INET:
- port = &((struct sockaddr_in *)sap)->sin_port;
- break;
- case AF_INET6:
- port = &((struct sockaddr_in6 *)sap)->sin6_port;
- break;
- default:
- return 0;
- }
-
- if (*rad == RAD_Accounting_Request)
- *port = htons(ntohs(*port) + 1);
-
- if (sendto(server->sock, rad, len, 0, sap, conf->addrinfo->ai_addrlen) >= 0) {
- debug(DBG_DBG, "clienradputudp: sent UDP of length %d to %s port %d", len, conf->host, ntohs(*port));
- return 1;
- }
-
- debug(DBG_WARN, "clientradputudp: send failed");
- return 0;
-}
-
int radsign(unsigned char *rad, unsigned char *sec) {
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static unsigned char first = 1;
return 1;
}
-void *udpclientrd(void *arg) {
- struct server *server;
- unsigned char *buf;
- int *s = (int *)arg;
-
- for (;;) {
- server = NULL;
- buf = radudpget(*s, NULL, &server, NULL);
- if (!replyh(server, buf))
- free(buf);
- }
-}
-
/* code for removing state not finished */
void *clientwr(void *arg) {
struct server *server = (struct server *)arg;
if (!conf->pdef->connecter(server, NULL, server->dynamiclookuparg ? 6 : 0, "clientwr"))
goto errexit;
server->connectionok = 1;
- if (pthread_create(&clientrdth, NULL, conf->pdef->clientreader, (void *)server)) {
+ if (pthread_create(&clientrdth, NULL, conf->pdef->clientconnreader, (void *)server)) {
debug(DBG_ERR, "clientwr: pthread_create failed");
goto errexit;
}
return NULL;
}
-void *udpserverwr(void *arg) {
- struct queue *replyq = udp_server_replyq;
- struct reply *reply;
-
- for (;;) {
- pthread_mutex_lock(&replyq->mutex);
- while (!(reply = (struct reply *)list_shift(replyq->entries))) {
- debug(DBG_DBG, "udp server writer, waiting for signal");
- pthread_cond_wait(&replyq->cond, &replyq->mutex);
- debug(DBG_DBG, "udp server writer, got signal");
- }
- pthread_mutex_unlock(&replyq->mutex);
-
- if (sendto(reply->toudpsock, reply->buf, RADLEN(reply->buf), 0,
- (struct sockaddr *)&reply->tosa, SOCKADDR_SIZE(reply->tosa)) < 0)
- debug(DBG_WARN, "sendudp: send failed");
- free(reply->buf);
- free(reply);
- }
-}
-
-void *udpserverrd(void *arg) {
- struct request rq;
- int *sp = (int *)arg;
-
- for (;;) {
- memset(&rq, 0, sizeof(struct request));
- rq.buf = radudpget(*sp, &rq.from, NULL, &rq.fromsa);
- rq.fromudpsock = *sp;
- radsrv(&rq);
- }
- free(sp);
-}
-
void createlistener(uint8_t type, char *arg) {
pthread_t th;
struct clsrvconf *listenres;
}
SSL_CTX *tlsgetctx(uint8_t type, char *alt1, char *alt2) {
- struct list_node *entry;
- struct tls *t, *t1 = NULL, *t2 = NULL;
- SSL_CTX *ctx = NULL;
-
- pthread_mutex_lock(&tlsconfs_lock);
-
- for (entry = list_first(tlsconfs); entry; entry = list_next(entry)) {
- t = (struct tls *)entry->data;
- if (!strcasecmp(t->name, alt1)) {
- t1 = t;
- break;
- }
- if (!t2 && alt2 && !strcasecmp(t->name, alt2))
- t2 = t;
- }
+ struct tls *t;
- t = (t1 ? t1 : t2);
- if (!t)
- goto exit;
+ t = hash_read(tlsconfs, alt1, strlen(alt1));
+ if (!t) {
+ t = hash_read(tlsconfs, alt2, strlen(alt2));
+ if (!t)
+ return NULL;
+ }
switch (type) {
case RAD_TLS:
if (!t->tlsctx)
t->tlsctx = tlscreatectx(RAD_TLS, t);
- ctx = t->tlsctx;
- break;
+ return t->tlsctx;
case RAD_DTLS:
if (!t->dtlsctx)
t->dtlsctx = tlscreatectx(RAD_DTLS, t);
- ctx = t->dtlsctx;
- break;
+ return t->dtlsctx;
}
-
- exit:
- pthread_mutex_unlock(&tlsconfs_lock);
- return ctx;
+ return NULL;
}
struct list *addsrvconfs(char *value, char **names) {
}
struct rewrite *getrewrite(char *alt1, char *alt2) {
- struct list_node *entry;
- struct rewriteconf *r, *r1 = NULL, *r2 = NULL;
-
- for (entry = list_first(rewriteconfs); entry; entry = list_next(entry)) {
- r = (struct rewriteconf *)entry->data;
- if (!strcasecmp(r->name, alt1)) {
- r1 = r;
- break;
- }
- if (!r2 && alt2 && !strcasecmp(r->name, alt2))
- r2 = r;
- }
+ struct rewrite *r;
- r = (r1 ? r1 : r2);
- if (!r)
- return NULL;
- return r->rewrite;
+ if ((r = hash_read(rewriteconfs, alt1, strlen(alt1))))
+ return r;
+ if ((r = hash_read(rewriteconfs, alt2, strlen(alt2))))
+ return r;
+ return NULL;
}
void addrewrite(char *value, char **attrs, char **vattrs) {
- struct rewriteconf *new;
struct rewrite *rewrite = NULL;
int i, n;
uint8_t *a = NULL;
rewrite->removevendorattrs = va;
}
- new = malloc(sizeof(struct rewriteconf));
- if (!new || !list_push(rewriteconfs, new))
- debugx(1, DBG_ERR, "malloc failed");
-
- memset(new, 0, sizeof(struct rewriteconf));
- new->name = stringcopy(value, 0);
- if (!new->name)
+ if (!hash_insert(rewriteconfs, value, strlen(value), rewrite))
debugx(1, DBG_ERR, "malloc failed");
-
- new->rewrite = rewrite;
debug(DBG_DBG, "addrewrite: added rewrite block %s", value);
}
debug(DBG_ERR, "conftls_cb: malloc failed");
goto errexit;
}
-
- pthread_mutex_lock(&tlsconfs_lock);
- if (!list_push(tlsconfs, conf)) {
+
+ if (!hash_insert(tlsconfs, val, strlen(val), conf)) {
debug(DBG_ERR, "conftls_cb: malloc failed");
- pthread_mutex_unlock(&tlsconfs_lock);
goto errexit;
}
- pthread_mutex_unlock(&tlsconfs_lock);
debug(DBG_DBG, "conftls_cb: added TLS block %s", val);
return 1;
if (!realms)
debugx(1, DBG_ERR, "malloc failed");
- tlsconfs = list_create();
+ tlsconfs = hash_create();
if (!tlsconfs)
debugx(1, DBG_ERR, "malloc failed");
- rewriteconfs = list_create();
+ rewriteconfs = hash_create();
if (!rewriteconfs)
debugx(1, DBG_ERR, "malloc failed");
}
int main(int argc, char **argv) {
- pthread_t sigth, udpclient4rdth, udpclient6rdth, udpserverwrth, dtlsclient4rdth, dtlsclient6rdth;
+ pthread_t sigth;
sigset_t sigset;
struct list_node *entry;
uint8_t foreground = 0, pretend = 0, loglevel = 0;
char *configfile = NULL;
struct clsrvconf *srvconf;
+ int i;
debug_init("radsecproxy");
debug_set_level(DEBUG_LEVEL);
- pthread_mutex_init(&tlsconfs_lock, NULL);
getargs(argc, argv, &foreground, &pretend, &loglevel, &configfile);
if (loglevel)
freeaddrinfo(srcprotores[RAD_UDP]);
srcprotores[RAD_UDP] = NULL;
}
+
+ for (i = 0; protodefs[i].name; i++)
+ if (protodefs[i].initextra)
+ protodefs[i].initextra();
- if (udp_client4_sock >= 0)
- if (pthread_create(&udpclient4rdth, NULL, protodefs[RAD_UDP].clientreader, (void *)&udp_client4_sock))
- debugx(1, DBG_ERR, "pthread_create failed");
- if (udp_client6_sock >= 0)
- if (pthread_create(&udpclient6rdth, NULL, protodefs[RAD_UDP].clientreader, (void *)&udp_client6_sock))
- debugx(1, DBG_ERR, "pthread_create failed");
-
- if (dtls_client4_sock >= 0)
- if (pthread_create(&dtlsclient4rdth, NULL, udpdtlsclientrd, (void *)&dtls_client4_sock))
- debugx(1, DBG_ERR, "pthread_create failed");
- if (dtls_client6_sock >= 0)
- if (pthread_create(&dtlsclient6rdth, NULL, udpdtlsclientrd, (void *)&dtls_client6_sock))
- debugx(1, DBG_ERR, "pthread_create failed");
-
- if (find_conf_type(RAD_TCP, clconfs, NULL))
+ if (find_clconf_type(RAD_TCP, NULL))
createlisteners(RAD_TCP, options.listentcp);
- if (find_conf_type(RAD_TLS, clconfs, NULL))
+ if (find_clconf_type(RAD_TLS, NULL))
createlisteners(RAD_TLS, options.listentls);
- if (find_conf_type(RAD_DTLS, clconfs, NULL))
+ if (find_clconf_type(RAD_DTLS, NULL))
createlisteners(RAD_DTLS, options.listendtls);
- if (find_conf_type(RAD_UDP, clconfs, NULL)) {
- udp_server_replyq = newqueue();
- if (pthread_create(&udpserverwrth, NULL, udpserverwr, NULL))
- debugx(1, DBG_ERR, "pthread_create failed");
+ if (find_clconf_type(RAD_UDP, NULL)) {
createlisteners(RAD_UDP, options.listenudp);
if (options.listenaccudp)
createlisteners(RAD_UDP, options.listenaccudp);