X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=tls.c;h=90c3dc916a9de4d085f6e51a159ea62eb14906a9;hb=refs%2Fheads%2Fmaint-1.6;hp=a9f91b397b68e104bd3fcd55cfa9a991326a8a95;hpb=5b259b9efe0902fb95228cf76e7d49eb2fb3c601;p=radsecproxy.git diff --git a/tls.c b/tls.c index a9f91b3..90c3dc9 100644 --- a/tls.c +++ b/tls.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Stig Venaas + * Copyright (C) 2006-2009 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 @@ -26,18 +26,74 @@ #include #include #include +#include "radsecproxy.h" +#include "hostport.h" + +#ifdef RADPROT_TLS #include "debug.h" -#include "list.h" #include "util.h" -#include "radsecproxy.h" -#include "tls.h" + +static void setprotoopts(struct commonprotoopts *opts); +static char **getlistenerargs(); +void *tlslistener(void *arg); +int tlsconnect(struct server *server, struct timeval *when, int timeout, char *text); +void *tlsclientrd(void *arg); +int clientradputtls(struct server *server, unsigned char *rad); +void tlssetsrcres(); + +static const struct protodefs protodefs = { + "tls", + "radsec", /* secretdefault */ + SOCK_STREAM, /* socktype */ + "2083", /* portdefault */ + 0, /* retrycountdefault */ + 0, /* retrycountmax */ + REQUEST_RETRY_INTERVAL * REQUEST_RETRY_COUNT, /* retryintervaldefault */ + 60, /* retryintervalmax */ + DUPLICATE_INTERVAL, /* duplicateintervaldefault */ + setprotoopts, /* setprotoopts */ + getlistenerargs, /* getlistenerargs */ + tlslistener, /* listener */ + tlsconnect, /* connecter */ + tlsclientrd, /* clientconnreader */ + clientradputtls, /* clientradput */ + NULL, /* addclient */ + NULL, /* addserverextra */ + tlssetsrcres, /* setsrcres */ + NULL /* initextra */ +}; + +static struct addrinfo *srcres = NULL; +static uint8_t handle; +static struct commonprotoopts *protoopts = NULL; + +const struct protodefs *tlsinit(uint8_t h) { + handle = h; + return &protodefs; +} + +static void setprotoopts(struct commonprotoopts *opts) { + protoopts = opts; +} + +static char **getlistenerargs() { + return protoopts ? protoopts->listenargs : NULL; +} + +void tlssetsrcres() { + if (!srcres) + srcres = + resolvepassiveaddrinfo(protoopts ? protoopts->sourcearg : NULL, + AF_UNSPEC, NULL, protodefs.socktype); +} int tlsconnect(struct server *server, struct timeval *when, int timeout, char *text) { struct timeval now; time_t elapsed; X509 *cert; + SSL_CTX *ctx = NULL; unsigned long error; - + debug(DBG_DBG, "tlsconnect: called from %s", text); pthread_mutex_lock(&server->lock); if (when && memcmp(&server->lastconnecttry, when, sizeof(struct timeval))) { @@ -72,16 +128,21 @@ int tlsconnect(struct server *server, struct timeval *when, int timeout, char *t sleep(60); } else server->lastconnecttry.tv_sec = now.tv_sec; /* no sleep at startup */ - debug(DBG_WARN, "tlsconnect: trying to open TLS connection to %s port %s", server->conf->host, server->conf->port); + if (server->sock >= 0) close(server->sock); - if ((server->sock = connecttcp(server->conf->addrinfo, getsrcprotores(RAD_TLS))) < 0) { - debug(DBG_ERR, "tlsconnect: connecttcp failed"); + if ((server->sock = connecttcphostlist(server->conf->hostports, srcres)) < 0) continue; - } - + SSL_free(server->ssl); - server->ssl = SSL_new(server->conf->ssl_ctx); + server->ssl = NULL; + ctx = tlsgetctx(handle, server->conf->tlsconf); + if (!ctx) + continue; + server->ssl = SSL_new(ctx); + if (!server->ssl) + continue; + SSL_set_fd(server->ssl, server->sock); if (SSL_connect(server->ssl) <= 0) { while ((error = ERR_get_error())) @@ -97,7 +158,8 @@ int tlsconnect(struct server *server, struct timeval *when, int timeout, char *t } X509_free(cert); } - debug(DBG_WARN, "tlsconnect: TLS connection to %s port %s up", server->conf->host, server->conf->port); + debug(DBG_WARN, "tlsconnect: TLS connection to %s up", server->conf->name); + server->connectionok = 1; gettimeofday(&server->lastconnecttry, NULL); pthread_mutex_unlock(&server->lock); return 1; @@ -107,24 +169,25 @@ int tlsconnect(struct server *server, struct timeval *when, int timeout, char *t /* returns 0 on timeout, -1 on error and num if ok */ int sslreadtimeout(SSL *ssl, unsigned char *buf, int num, int timeout) { int s, ndesc, cnt, len; - fd_set readfds, writefds; + fd_set readfds; struct timeval timer; - + s = SSL_get_fd(ssl); if (s < 0) return -1; /* make socket non-blocking? */ for (len = 0; len < num; len += cnt) { - FD_ZERO(&readfds); - FD_SET(s, &readfds); - writefds = readfds; - if (timeout) { - timer.tv_sec = timeout; - timer.tv_usec = 0; + if (SSL_pending(ssl) == 0) { + FD_ZERO(&readfds); + FD_SET(s, &readfds); + if (timeout) { + timer.tv_sec = timeout; + timer.tv_usec = 0; + } + ndesc = select(s + 1, &readfds, NULL, NULL, timeout ? &timer : NULL); + if (ndesc < 1) + return ndesc; } - ndesc = select(s + 1, &readfds, &writefds, NULL, timeout ? &timer : NULL); - if (ndesc < 1) - return ndesc; cnt = SSL_read(ssl, buf + len, num - len); if (cnt <= 0) @@ -163,21 +226,21 @@ unsigned char *radtlsget(SSL *ssl, int timeout) { continue; } memcpy(rad, buf, 4); - + cnt = sslreadtimeout(ssl, rad + 4, len - 4, timeout); if (cnt < 1) { debug(DBG_DBG, cnt ? "radtlsget: connection lost" : "radtlsget: timeout"); free(rad); return NULL; } - + if (len >= 20) break; - + free(rad); debug(DBG_WARN, "radtlsget: packet smaller than minimum radius size"); } - + debug(DBG_DBG, "radtlsget: got %d bytes", len); return rad; } @@ -186,22 +249,18 @@ int clientradputtls(struct server *server, unsigned char *rad) { int cnt; size_t len; unsigned long error; - struct timeval lastconnecttry; struct clsrvconf *conf = server->conf; - + + if (!server->connectionok) + return 0; len = RADLEN(rad); - lastconnecttry = server->lastconnecttry; - while ((cnt = SSL_write(server->ssl, rad, len)) <= 0) { + if ((cnt = SSL_write(server->ssl, rad, len)) <= 0) { while ((error = ERR_get_error())) debug(DBG_ERR, "clientradputtls: TLS: %s", ERR_error_string(error, NULL)); - if (server->dynamiclookuparg) - return 0; - tlsconnect(server, &lastconnecttry, 0, "clientradputtls"); - lastconnecttry = server->lastconnecttry; + return 0; } - server->connectionok = 1; - debug(DBG_DBG, "clientradputtls: Sent %d bytes, Radius packet of length %d to TLS peer %s", cnt, len, conf->host); + debug(DBG_DBG, "clientradputtls: Sent %d bytes, Radius packet of length %d to TLS peer %s", cnt, len, conf->name); return 1; } @@ -209,7 +268,7 @@ void *tlsclientrd(void *arg) { struct server *server = (struct server *)arg; unsigned char *buf; struct timeval now, lastconnecttry; - + for (;;) { /* yes, lastconnecttry is really necessary */ lastconnecttry = server->lastconnecttry; @@ -231,8 +290,17 @@ void *tlsclientrd(void *arg) { } } } + debug(DBG_INFO, "tlsclientrd: exiting for %s", server->conf->name); ERR_remove_state(0); + SSL_shutdown(server->ssl); + shutdown(server->sock, SHUT_RDWR); + close(server->sock); + + /* Wake up clientwr(). */ server->clientrdgone = 1; + pthread_mutex_lock(&server->newrq_mutex); + pthread_cond_signal(&server->newrq_cond); + pthread_mutex_unlock(&server->newrq_mutex); return NULL; } @@ -240,15 +308,15 @@ void *tlsserverwr(void *arg) { int cnt; unsigned long error; struct client *client = (struct client *)arg; - struct queue *replyq; - struct reply *reply; - - debug(DBG_DBG, "tlsserverwr: starting for %s", client->conf->host); + struct gqueue *replyq; + struct request *reply; + + debug(DBG_DBG, "tlsserverwr: starting for %s", addr2string(client->addr)); replyq = client->replyq; for (;;) { pthread_mutex_lock(&replyq->mutex); while (!list_first(replyq->entries)) { - if (client->ssl) { + if (client->ssl) { debug(DBG_DBG, "tlsserverwr: waiting for signal"); pthread_cond_wait(&replyq->cond, &replyq->mutex); debug(DBG_DBG, "tlsserverwr: got signal"); @@ -261,17 +329,16 @@ void *tlsserverwr(void *arg) { pthread_exit(NULL); } } - reply = (struct reply *)list_shift(replyq->entries); + reply = (struct request *)list_shift(replyq->entries); pthread_mutex_unlock(&replyq->mutex); - cnt = SSL_write(client->ssl, reply->buf, RADLEN(reply->buf)); + cnt = SSL_write(client->ssl, reply->replybuf, RADLEN(reply->replybuf)); if (cnt > 0) - debug(DBG_DBG, "tlsserverwr: sent %d bytes, Radius packet of length %d", - cnt, RADLEN(reply->buf)); + debug(DBG_DBG, "tlsserverwr: sent %d bytes, Radius packet of length %d to %s", + cnt, RADLEN(reply->replybuf), addr2string(client->addr)); else while ((error = ERR_get_error())) debug(DBG_ERR, "tlsserverwr: SSL: %s", ERR_error_string(error, NULL)); - free(reply->buf); - free(reply); + freerq(reply); } } @@ -279,21 +346,21 @@ void tlsserverrd(struct client *client) { struct request *rq; uint8_t *buf; pthread_t tlsserverwrth; - - debug(DBG_DBG, "tlsserverrd: starting for %s", client->conf->host); - - if (pthread_create(&tlsserverwrth, NULL, tlsserverwr, (void *)client)) { + + debug(DBG_DBG, "tlsserverrd: starting for %s", addr2string(client->addr)); + + if (pthread_create(&tlsserverwrth, &pthread_attr, tlsserverwr, (void *)client)) { debug(DBG_ERR, "tlsserverrd: pthread_create failed"); return; } for (;;) { - buf = radtlsget(client->ssl, 0); + buf = radtlsget(client->ssl, IDLE_TIMEOUT * 3); if (!buf) { - debug(DBG_ERR, "tlsserverrd: connection from %s lost", client->conf->host); + debug(DBG_ERR, "tlsserverrd: connection from %s lost", addr2string(client->addr)); break; } - debug(DBG_DBG, "tlsserverrd: got Radius message from %s", client->conf->host); + debug(DBG_DBG, "tlsserverrd: got Radius message from %s", addr2string(client->addr)); rq = newrequest(); if (!rq) { free(buf); @@ -302,11 +369,11 @@ void tlsserverrd(struct client *client) { rq->buf = buf; rq->from = client; if (!radsrv(rq)) { - debug(DBG_ERR, "tlsserverrd: message authentication/validation failed, closing connection from %s", client->conf->host); + debug(DBG_ERR, "tlsserverrd: message authentication/validation failed, closing connection from %s", addr2string(client->addr)); break; } } - + /* stop writer by setting ssl to NULL and give signal in case waiting for data */ client->ssl = NULL; pthread_mutex_lock(&client->replyq->mutex); @@ -314,31 +381,37 @@ void tlsserverrd(struct client *client) { pthread_mutex_unlock(&client->replyq->mutex); debug(DBG_DBG, "tlsserverrd: waiting for writer to end"); pthread_join(tlsserverwrth, NULL); - removeclientrqs(client); - debug(DBG_DBG, "tlsserverrd: reader for %s exiting", client->conf->host); + debug(DBG_DBG, "tlsserverrd: reader for %s exiting", addr2string(client->addr)); } void *tlsservernew(void *arg) { int s; struct sockaddr_storage from; - size_t fromlen = sizeof(from); + socklen_t fromlen = sizeof(from); struct clsrvconf *conf; struct list_node *cur = NULL; SSL *ssl = NULL; X509 *cert = NULL; + SSL_CTX *ctx = NULL; unsigned long error; struct client *client; + struct tls *accepted_tls = NULL; s = *(int *)arg; if (getpeername(s, (struct sockaddr *)&from, &fromlen)) { debug(DBG_DBG, "tlsservernew: getpeername failed, exiting"); goto exit; } - debug(DBG_WARN, "tlsservernew: incoming TLS connection from %s", addr2string((struct sockaddr *)&from, fromlen)); + debug(DBG_WARN, "tlsservernew: incoming TLS connection from %s", addr2string((struct sockaddr *)&from)); - conf = find_clconf(RAD_TLS, (struct sockaddr *)&from, &cur); + conf = find_clconf(handle, (struct sockaddr *)&from, &cur); if (conf) { - ssl = SSL_new(conf->ssl_ctx); + ctx = tlsgetctx(handle, conf->tlsconf); + if (!ctx) + goto exit; + ssl = SSL_new(ctx); + if (!ssl) + goto exit; SSL_set_fd(ssl, s); if (SSL_accept(ssl) <= 0) { @@ -350,27 +423,29 @@ void *tlsservernew(void *arg) { cert = verifytlscert(ssl); if (!cert) goto exit; + accepted_tls = conf->tlsconf; } - + while (conf) { - if (verifyconfcert(cert, conf)) { - X509_free(cert); - client = addclient(conf, 1); - if (client) { - client->ssl = ssl; - tlsserverrd(client); - removeclient(client); - } else - debug(DBG_WARN, "tlsservernew: failed to create new client instance"); - goto exit; - } - conf = find_clconf(RAD_TLS, (struct sockaddr *)&from, &cur); + if (accepted_tls == conf->tlsconf && verifyconfcert(cert, conf)) { + X509_free(cert); + client = addclient(conf, 1); + if (client) { + client->ssl = ssl; + client->addr = addr_copy((struct sockaddr *)&from); + tlsserverrd(client); + removeclient(client); + } else + debug(DBG_WARN, "tlsservernew: failed to create new client instance"); + goto exit; + } + conf = find_clconf(handle, (struct sockaddr *)&from, &cur); } debug(DBG_WARN, "tlsservernew: ignoring request, no matching TLS client"); if (cert) X509_free(cert); - exit: +exit: if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); @@ -385,7 +460,7 @@ void *tlslistener(void *arg) { pthread_t tlsserverth; int s, *sp = (int *)arg; struct sockaddr_storage from; - size_t fromlen = sizeof(from); + socklen_t fromlen = sizeof(from); listen(*sp, 0); @@ -395,7 +470,7 @@ void *tlslistener(void *arg) { debug(DBG_WARN, "accept failed"); continue; } - if (pthread_create(&tlsserverth, NULL, tlsservernew, (void *)&s)) { + if (pthread_create(&tlsserverth, &pthread_attr, tlsservernew, (void *)&s)) { debug(DBG_ERR, "tlslistener: pthread_create failed"); shutdown(s, SHUT_RDWR); close(s); @@ -406,3 +481,12 @@ void *tlslistener(void *arg) { free(sp); return NULL; } +#else +const struct protodefs *tlsinit(uint8_t h) { + return NULL; +} +#endif + +/* Local Variables: */ +/* c-file-style: "stroustrup" */ +/* End: */