* copyright notice and this permission notice appear in all copies.
*/
+/* Code contributions from:
+ *
+ * Arne Schwabe <schwabe at uni-paderborn.de>
+ */
+
/* 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
*/
/* Bugs:
- * TCP accounting not yet supported
+ * May segfault when dtls connections go down? More testing needed
+ * Need to remove UDP clients when no activity for a while...
+ * Remove expired stuff from clients request list?
* We are not removing client requests from dynamic servers, see removeclientrqs()
+ * Multiple outgoing connections if not enough IDs? (multiple servers per conf?)
+ * Useful for TCP accounting? Now we require separate server config for alt port
*/
#include <signal.h>
int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val);
void freerealm(struct realm *realm);
void freeclsrvconf(struct clsrvconf *conf);
-void freerqdata(struct request *rq);
+void freerq(struct request *rq);
+void freerqoutdata(struct rqout *rqout);
+void rmclientrq(struct request *rq, uint8_t id);
static const struct protodefs protodefs[] = {
{ "udp", /* UDP, assuming RAD_UDP defined as 0 */
10, /* retrycountmax */
REQUEST_RETRY_INTERVAL, /* retryintervaldefault */
60, /* retryintervalmax */
+ DUPLICATE_INTERVAL, /* duplicateintervaldefault */
udpserverrd, /* listener */
&options.sourceudp, /* srcaddrport */
NULL, /* connecter */
0, /* retrycountmax */
REQUEST_RETRY_INTERVAL * REQUEST_RETRY_COUNT, /* retryintervaldefault */
60, /* retryintervalmax */
+ DUPLICATE_INTERVAL, /* duplicateintervaldefault */
tlslistener, /* listener */
&options.sourcetls, /* srcaddrport */
tlsconnect, /* connecter */
0, /* retrycountmax */
REQUEST_RETRY_INTERVAL * REQUEST_RETRY_COUNT, /* retryintervaldefault */
60, /* retryintervalmax */
+ DUPLICATE_INTERVAL, /* duplicateintervaldefault */
tcplistener, /* listener */
&options.sourcetcp, /* srcaddrport */
tcpconnect, /* connecter */
10, /* retrycountmax */
REQUEST_RETRY_INTERVAL, /* retryintervaldefault */
60, /* retryintervalmax */
+ DUPLICATE_INTERVAL, /* duplicateintervaldefault */
udpdtlsserverrd, /* listener */
&options.sourcedtls, /* srcaddrport */
dtlsconnect, /* connecter */
debug(DBG_WARN, "resolvepeer: can't resolve (null) port (null)");
return 0;
}
- for (res = addrinfo; res; res = res->ai_next) {
- switch (res->ai_family) {
- case AF_INET:
- ((struct sockaddr_in *)res->ai_addr)->sin_port = 0;
- break;
- case AF_INET6:
- ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = 0;
- break;
- }
- }
+ for (res = addrinfo; res; res = res->ai_next)
+ port_set(res->ai_addr, 0);
} else {
if (slash)
hints.ai_flags |= AI_NUMERICHOST;
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;
-
- s = -1;
- for (res = addrinfo; res; res = res->ai_next) {
- s = bindtoaddr(src, res->ai_family, 1, 1);
- if (s < 0) {
- debug(DBG_WARN, "connecttoserver: socket failed");
- continue;
- }
- if (connect(s, res->ai_addr, res->ai_addrlen) == 0)
- break;
- debug(DBG_WARN, "connecttoserver: connect failed");
- close(s);
- s = -1;
- }
- return s;
-}
-
/* returns 1 if the len first bits are equal, else 0 */
int prefixmatch(void *a1, void *a2, uint8_t len) {
static uint8_t mask[] = { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
return;
pthread_mutex_lock(&q->mutex);
for (entry = list_first(q->entries); entry; entry = list_next(entry))
- free(((struct reply *)entry)->buf);
+ freerq((struct request *)entry);
list_destroy(q->entries);
pthread_cond_destroy(&q->cond);
pthread_mutex_unlock(&q->mutex);
removequeue(q);
}
-struct client *addclient(struct clsrvconf *conf) {
+struct client *addclient(struct clsrvconf *conf, uint8_t lock) {
struct client *new = malloc(sizeof(struct client));
if (!new) {
debug(DBG_ERR, "malloc failed");
return NULL;
}
+
+ if (lock)
+ pthread_mutex_lock(conf->lock);
if (!conf->clients) {
conf->clients = list_create();
if (!conf->clients) {
+ if (lock)
+ pthread_mutex_unlock(conf->lock);
debug(DBG_ERR, "malloc failed");
return NULL;
}
else
new->replyq = newqueue();
list_push(conf->clients, new);
+ if (lock)
+ pthread_mutex_unlock(conf->lock);
return new;
}
-void removeclient(struct client *client) {
- if (!client || !client->conf->clients)
- return;
- removequeue(client->replyq);
- list_removedata(client->conf->clients, client);
- free(client);
-}
-
void removeclientrqs(struct client *client) {
- struct list_node *entry;
- struct server *server;
struct request *rq;
+ struct rqout *rqout;
int i;
-
- for (entry = list_first(srvconfs); entry; entry = list_next(entry)) {
- server = ((struct clsrvconf *)entry->data)->servers;
- if (!server)
+
+ for (i = 0; i < MAX_REQUESTS; i++) {
+ rq = client->rqs[i];
+ if (!rq)
continue;
- pthread_mutex_lock(&server->newrq_mutex);
- for (i = 0; i < MAX_REQUESTS; i++) {
- rq = server->requests + i;
- if (rq->from == client)
- rq->from = NULL;
- }
- pthread_mutex_unlock(&server->newrq_mutex);
+ rqout = rq->to->requests + rq->newid;
+ pthread_mutex_lock(rqout->lock);
+ if (rqout->rq == rq) /* still pointing to our request */
+ freerqoutdata(rqout);
+ pthread_mutex_unlock(rqout->lock);
+ freerq(rq);
}
}
+void removeclient(struct client *client) {
+ struct clsrvconf *conf;
+
+ if (!client)
+ return;
+ conf = client->conf;
+ pthread_mutex_lock(conf->lock);
+ if (conf->clients) {
+ removeclientrqs(client);
+ removequeue(client->replyq);
+ list_removedata(conf->clients, client);
+ free(client->addr);
+ free(client);
+ }
+ pthread_mutex_unlock(conf->lock);
+}
+
void freeserver(struct server *server, uint8_t destroymutex) {
- struct request *rq, *end;
+ struct rqout *rqout, *end;
if (!server)
return;
if (server->requests) {
- rq = server->requests;
- for (end = rq + MAX_REQUESTS; rq < end; rq++)
- freerqdata(rq);
+ rqout = server->requests;
+ for (end = rqout + MAX_REQUESTS; rqout < end; rqout++) {
+ freerqoutdata(rqout);
+ pthread_mutex_destroy(rqout->lock);
+ free(rqout->lock);
+ }
free(server->requests);
}
if (server->rbios)
int addserver(struct clsrvconf *conf) {
struct clsrvconf *res;
uint8_t type;
+ int i;
if (conf->servers) {
debug(DBG_ERR, "addserver: currently works with just one server per conf");
if (conf->pdef->addserverextra)
conf->pdef->addserverextra(conf);
- conf->servers->requests = calloc(MAX_REQUESTS, sizeof(struct request));
+ conf->servers->requests = calloc(MAX_REQUESTS, sizeof(struct rqout));
if (!conf->servers->requests) {
debug(DBG_ERR, "malloc failed");
goto errexit;
}
+ for (i = 0; i < MAX_REQUESTS; i++) {
+ conf->servers->requests[i].lock = malloc(sizeof(pthread_mutex_t));
+ if (!conf->servers->requests[i].lock) {
+ debug(DBG_ERR, "malloc failed");
+ goto errexit;
+ }
+ if (pthread_mutex_init(conf->servers->requests[i].lock, NULL)) {
+ debug(DBG_ERR, "mutex init failed");
+ free(conf->servers->requests[i].lock);
+ conf->servers->requests[i].lock = NULL;
+ goto errexit;
+ }
+ }
if (pthread_mutex_init(&conf->servers->lock, NULL)) {
debug(DBG_ERR, "mutex init failed");
goto errexit;
return 1;
}
-int radsign(unsigned char *rad, unsigned char *sec) {
- static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
- static unsigned char first = 1;
- static EVP_MD_CTX mdctx;
- unsigned int md_len;
- int result;
-
- pthread_mutex_lock(&lock);
- if (first) {
- EVP_MD_CTX_init(&mdctx);
- first = 0;
- }
-
- result = (EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) &&
- EVP_DigestUpdate(&mdctx, rad, RADLEN(rad)) &&
- EVP_DigestUpdate(&mdctx, sec, strlen((char *)sec)) &&
- EVP_DigestFinal_ex(&mdctx, rad + 4, &md_len) &&
- md_len == 16);
- pthread_mutex_unlock(&lock);
- return result;
-}
-
unsigned char *attrget(unsigned char *attrs, int length, uint8_t type) {
while (length > 1) {
if (ATTRTYPE(attrs) == type)
return NULL;
}
-void freerqdata(struct request *rq) {
+void freerq(struct request *rq) {
+ if (!rq)
+ return;
+ debug(DBG_DBG, "freerq: called with refcount %d", rq->refcount);
+ if (--rq->refcount)
+ return;
if (rq->origusername)
free(rq->origusername);
- if (rq->msg)
- radmsg_free(rq->msg);
if (rq->buf)
free(rq->buf);
+ if (rq->replybuf)
+ free(rq->replybuf);
+ if (rq->msg)
+ radmsg_free(rq->msg);
+ free(rq);
}
-void sendrq(struct server *to, struct request *rq) {
- int i;
+void freerqoutdata(struct rqout *rqout) {
+ if (!rqout)
+ return;
+ if (rqout->rq) {
+ freerq(rqout->rq);
+ rqout->rq = NULL;
+ }
+ rqout->tries = 0;
+ memset(&rqout->expiry, 0, sizeof(struct timeval));
+}
+void sendrq(struct request *rq) {
+ int i, start;
+ struct server *to = rq->to;
+
+ start = to->conf->statusserver ? 1 : 0;
pthread_mutex_lock(&to->newrq_mutex);
- /* might simplify if only try nextid, might be ok */
- for (i = to->nextid; i < MAX_REQUESTS; i++)
- if (!to->requests[i].buf)
- break;
- if (i == MAX_REQUESTS) {
- for (i = 0; i < to->nextid; i++)
- if (!to->requests[i].buf)
- break;
- if (i == to->nextid) {
- debug(DBG_WARN, "sendrq: no room in queue, dropping request");
- freerqdata(rq);
- goto exit;
+ if (start && rq->msg->code == RAD_Status_Server) {
+ pthread_mutex_lock(to->requests[0].lock);
+ if (to->requests[0].rq) {
+ pthread_mutex_unlock(to->requests[0].lock);
+ debug(DBG_WARN, "sendrq: status server already in queue, dropping request");
+ goto errexit;
+ }
+ i = 0;
+ } else {
+ if (!to->nextid)
+ to->nextid = start;
+ /* might simplify if only try nextid, might be ok */
+ for (i = to->nextid; i < MAX_REQUESTS; i++) {
+ if (!to->requests[i].rq) {
+ pthread_mutex_lock(to->requests[i].lock);
+ if (!to->requests[i].rq)
+ break;
+ pthread_mutex_unlock(to->requests[i].lock);
+ }
+ }
+ if (i == MAX_REQUESTS) {
+ for (i = start; i < to->nextid; i++) {
+ if (!to->requests[i].rq) {
+ pthread_mutex_lock(to->requests[i].lock);
+ if (!to->requests[i].rq)
+ break;
+ pthread_mutex_unlock(to->requests[i].lock);
+ }
+ }
+ if (i == to->nextid) {
+ debug(DBG_WARN, "sendrq: no room in queue, dropping request");
+ goto errexit;
+ }
}
}
-
+ rq->newid = (uint8_t)i;
rq->msg->id = (uint8_t)i;
rq->buf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
if (!rq->buf) {
- freerqdata(rq);
- goto exit;
+ pthread_mutex_unlock(to->requests[i].lock);
+ debug(DBG_ERR, "sendrq: radmsg2buf failed");
+ goto errexit;
}
- if (*rq->buf == RAD_Accounting_Request) {
- if (!radsign(rq->buf, (unsigned char *)to->conf->secret)) {
- debug(DBG_WARN, "sendrq: failed to sign Accounting-Request message");
- freerqdata(rq);
- goto exit;
- }
- }
-
debug(DBG_DBG, "sendrq: inserting packet with id %d in queue for %s", i, to->conf->host);
- to->requests[i] = *rq;
- to->nextid = i + 1;
+ to->requests[i].rq = rq;
+ pthread_mutex_unlock(to->requests[i].lock);
+ if (i >= start) /* i is not reserved for statusserver */
+ to->nextid = i + 1;
if (!to->newrq) {
to->newrq = 1;
pthread_cond_signal(&to->newrq_cond);
}
- exit:
+ pthread_mutex_unlock(&to->newrq_mutex);
+ return;
+
+ errexit:
+ if (rq->from)
+ rmclientrq(rq, rq->msg->id);
+ freerq(rq);
pthread_mutex_unlock(&to->newrq_mutex);
}
-void sendreply(struct client *to, unsigned char *buf, struct sockaddr_storage *tosa, int toudpsock) {
- struct reply *reply;
+void sendreply(struct request *rq) {
uint8_t first;
-
- if (!radsign(buf, (unsigned char *)to->conf->secret)) {
- free(buf);
- debug(DBG_WARN, "sendreply: failed to sign message");
+ struct client *to = rq->from;
+
+ if (!rq->replybuf)
+ rq->replybuf = radmsg2buf(rq->msg, (uint8_t *)to->conf->secret);
+ radmsg_free(rq->msg);
+ rq->msg = NULL;
+ if (!rq->replybuf) {
+ freerq(rq);
+ debug(DBG_ERR, "sendreply: radmsg2buf failed");
return;
}
- reply = malloc(sizeof(struct reply));
- if (!reply) {
- free(buf);
- debug(DBG_ERR, "sendreply: malloc failed");
- return;
- }
- memset(reply, 0, sizeof(struct reply));
- reply->buf = buf;
- if (tosa)
- reply->tosa = *tosa;
- reply->toudpsock = toudpsock;
-
pthread_mutex_lock(&to->replyq->mutex);
-
first = list_first(to->replyq->entries) == NULL;
- if (!list_push(to->replyq->entries, reply)) {
+ if (!list_push(to->replyq->entries, rq)) {
pthread_mutex_unlock(&to->replyq->mutex);
- free(reply);
- free(buf);
+ freerq(rq);
debug(DBG_ERR, "sendreply: malloc failed");
return;
}
}
}
-int rqinqueue(struct server *to, struct client *from, uint8_t id, uint8_t code) {
- struct request *rq = to->requests, *end;
-
- pthread_mutex_lock(&to->newrq_mutex);
- for (end = rq + MAX_REQUESTS; rq < end; rq++)
- if (rq->buf && !rq->received && rq->origid == id && rq->from == from && *rq->buf == code)
- break;
- pthread_mutex_unlock(&to->newrq_mutex);
-
- return rq < end;
-}
-
int attrvalidate(unsigned char *attrs, int length) {
while (length > 1) {
if (ATTRLEN(attrs) < 2) {
return 1;
}
-int msmpprecrypt(uint8_t *msmpp, uint8_t len, char *oldsecret, char *newsecret, unsigned char *oldauth, char *newauth) {
+int msmpprecrypt(uint8_t *msmpp, uint8_t len, char *oldsecret, char *newsecret, uint8_t *oldauth, uint8_t *newauth) {
if (len < 18)
return 0;
- if (!msmppdecrypt(msmpp + 2, len - 2, (unsigned char *)oldsecret, strlen(oldsecret), oldauth, msmpp)) {
+ if (!msmppdecrypt(msmpp + 2, len - 2, (uint8_t *)oldsecret, strlen(oldsecret), oldauth, msmpp)) {
debug(DBG_WARN, "msmpprecrypt: failed to decrypt msppe key");
return 0;
}
- if (!msmppencrypt(msmpp + 2, len - 2, (unsigned char *)newsecret, strlen(newsecret), (unsigned char *)newauth, msmpp)) {
+ if (!msmppencrypt(msmpp + 2, len - 2, (uint8_t *)newsecret, strlen(newsecret), newauth, msmpp)) {
debug(DBG_WARN, "msmpprecrypt: failed to encrypt msppe key");
return 0;
}
for (attr = attrs; (attr = attrget(attr, length - (attr - attrs), type)); attr += ATTRLEN(attr)) {
debug(DBG_DBG, "msmppe: Got %s", attrtxt);
- if (!msmpprecrypt(ATTRVAL(attr), ATTRVALLEN(attr), oldsecret, newsecret, rq->buf + 4, rq->origauth))
+ if (!msmpprecrypt(ATTRVAL(attr), ATTRVALLEN(attr), oldsecret, newsecret, rq->buf + 4, rq->rqauth))
return 0;
}
return 1;
}
}
-void respondaccounting(struct request *rq) {
- unsigned char *resp;
-
- resp = malloc(20);
- if (!resp) {
- debug(DBG_ERR, "respondaccounting: malloc failed");
- return;
- }
- memcpy(resp, rq->buf, 20);
- resp[0] = RAD_Accounting_Response;
- resp[2] = 0;
- resp[3] = 20;
- debug(DBG_DBG, "respondaccounting: responding to %s", rq->from->conf->host);
- sendreply(rq->from, resp, &rq->fromsa, rq->fromudpsock);
-}
-
-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->conf->host);
- sendreply(rq->from, resp, &rq->fromsa, rq->fromudpsock);
-}
-
-void respondreject(struct request *rq, char *message) {
- unsigned char *resp;
- int len = 20;
+void respond(struct request *rq, uint8_t code, char *message) {
+ struct radmsg *msg;
+ struct tlv *attr;
- if (message && *message)
- len += 2 + strlen(message);
-
- resp = malloc(len);
- if (!resp) {
- debug(DBG_ERR, "respondreject: malloc failed");
+ msg = radmsg_init(code, rq->msg->id, rq->msg->auth);
+ if (!msg) {
+ debug(DBG_ERR, "respond: malloc failed");
return;
}
- memcpy(resp, rq->buf, 20);
- resp[0] = RAD_Access_Reject;
- *(uint16_t *)(resp + 2) = htons(len);
if (message && *message) {
- resp[20] = RAD_Attr_Reply_Message;
- resp[21] = len - 20;
- memcpy(resp + 22, message, len - 22);
+ attr = maketlv(RAD_Attr_Reply_Message, strlen(message), message);
+ if (!attr || !radmsg_add(msg, attr)) {
+ freetlv(attr);
+ radmsg_free(msg);
+ debug(DBG_ERR, "respond: malloc failed");
+ return;
+ }
}
- sendreply(rq->from, resp, &rq->fromsa, rq->fromudpsock);
+
+ radmsg_free(rq->msg);
+ rq->msg = msg;
+ debug(DBG_DBG, "respond: sending %s to %s", radmsgtype2string(msg->code), rq->from->conf->host);
+ rq->refcount++;
+ sendreply(rq);
}
struct clsrvconf *choosesrvconf(struct list *srvconfs) {
return srvconf->servers;
}
+
+struct request *newrequest() {
+ struct request *rq;
+
+ rq = malloc(sizeof(struct request));
+ if (!rq) {
+ debug(DBG_ERR, "newrequest: malloc failed");
+ return NULL;
+ }
+ memset(rq, 0, sizeof(struct request));
+ rq->refcount = 1;
+ gettimeofday(&rq->created, NULL);
+ return rq;
+}
+
+int addclientrq(struct request *rq) {
+ struct request *r;
+ struct timeval now;
+
+ r = rq->from->rqs[rq->rqid];
+ if (r) {
+ if (rq->udpport == r->udpport && !memcmp(rq->rqauth, r->rqauth, 16)) {
+ gettimeofday(&now, NULL);
+ if (now.tv_sec - r->created.tv_sec < r->from->conf->dupinterval) {
+ if (r->replybuf) {
+ debug(DBG_INFO, "addclientrq: already sent reply to request with id %d from %s, resending", rq->rqid, addr2string(r->from->addr));
+ r->refcount++;
+ sendreply(r);
+ } else
+ debug(DBG_INFO, "addclientrq: already got request with id %d from %s, ignoring", rq->rqid, addr2string(r->from->addr));
+ return 0;
+ }
+ }
+ freerq(r);
+ }
+ rq->refcount++;
+ rq->from->rqs[rq->rqid] = rq;
+ return 1;
+}
+
+void rmclientrq(struct request *rq, uint8_t id) {
+ struct request *r;
+
+ r = rq->from->rqs[id];
+ if (r) {
+ freerq(r);
+ rq->from->rqs[id] = NULL;
+ }
+}
+
/* returns 0 if validation/authentication fails, else 1 */
int radsrv(struct request *rq) {
struct radmsg *msg = NULL;
struct tlv *attr;
uint8_t *userascii = NULL;
- unsigned char newauth[16];
struct realm *realm = NULL;
struct server *to = NULL;
+ struct client *from = rq->from;
+
+ msg = buf2radmsg(rq->buf, (uint8_t *)from->conf->secret, NULL);
+ free(rq->buf);
+ rq->buf = NULL;
- msg = buf2radmsg(rq->buf, (uint8_t *)rq->from->conf->secret, NULL);
if (!msg) {
debug(DBG_WARN, "radsrv: message validation failed, ignoring packet");
- freerqdata(rq);
+ freerq(rq);
return 0;
}
- debug(DBG_DBG, "radsrv: code %d, id %d", msg->code, msg->id);
+ rq->msg = msg;
+ rq->rqid = msg->id;
+ memcpy(rq->rqauth, msg->auth, 16);
+
+ debug(DBG_DBG, "radsrv: code %d, id %d", msg->code, msg->id);
if (msg->code != RAD_Access_Request && msg->code != RAD_Status_Server && msg->code != RAD_Accounting_Request) {
- debug(DBG_INFO, "radsrv: server currently accepts only access-requests, accounting-requests and status-server, ignoring");
+ debug(DBG_INFO, "radsrv: server currently accepts only access-requests, accounting-requests and status-server, ignoring");
goto exit;
}
+
+ if (!addclientrq(rq))
+ goto exit;
if (msg->code == RAD_Status_Server) {
- respondstatusserver(rq);
+ respond(rq, RAD_Access_Accept, NULL);
goto exit;
}
/* below: code == RAD_Access_Request || code == RAD_Accounting_Request */
- if (rq->from->conf->rewritein && !dorewrite(msg, rq->from->conf->rewritein))
- goto exit;
+ if (from->conf->rewritein && !dorewrite(msg, from->conf->rewritein))
+ goto rmclrqexit;
attr = radmsg_gettype(msg, RAD_Attr_User_Name);
if (!attr) {
if (msg->code == RAD_Accounting_Request) {
- acclog(msg, rq->from->conf->host);
- respondaccounting(rq);
+ acclog(msg, from->conf->host);
+ respond(rq, RAD_Accounting_Response, NULL);
} else
debug(DBG_WARN, "radsrv: ignoring access request, no username attribute");
goto exit;
}
- if (rq->from->conf->rewriteusername && !rewriteusername(rq, attr)) {
+ if (from->conf->rewriteusername && !rewriteusername(rq, attr)) {
debug(DBG_WARN, "radsrv: username malloc failed, ignoring request");
- goto exit;
+ goto rmclrqexit;
}
userascii = radattr2ascii(attr);
if (!userascii)
- goto exit;
+ goto rmclrqexit;
debug(DBG_DBG, "%s with username: %s", radmsgtype2string(msg->code), userascii);
to = findserver(&realm, attr, msg->code == RAD_Accounting_Request);
debug(DBG_INFO, "radsrv: ignoring request, don't know where to send it");
goto exit;
}
+
if (!to) {
if (realm->message && msg->code == RAD_Access_Request) {
- debug(DBG_INFO, "radsrv: sending reject to %s for %s", rq->from->conf->host, userascii);
- respondreject(rq, realm->message);
+ debug(DBG_INFO, "radsrv: sending reject to %s for %s", from->conf->host, userascii);
+ respond(rq, RAD_Access_Reject, realm->message);
} else if (realm->accresp && msg->code == RAD_Accounting_Request) {
- acclog(msg, rq->from->conf->host);
- respondaccounting(rq);
+ acclog(msg, from->conf->host);
+ respond(rq, RAD_Accounting_Response, NULL);
}
goto exit;
}
- free(rq->buf);
- rq->buf = NULL;
-
- if (options.loopprevention && !strcmp(rq->from->conf->name, to->conf->name)) {
+ if (options.loopprevention && !strcmp(from->conf->name, to->conf->name)) {
debug(DBG_INFO, "radsrv: Loop prevented, not forwarding request from client %s to server %s, discarding",
- rq->from->conf->name, to->conf->name);
+ from->conf->name, to->conf->name);
goto exit;
}
- if (rqinqueue(to, rq->from, msg->id, msg->code)) {
- debug(DBG_INFO, "radsrv: already got %s from host %s with id %d, ignoring",
- radmsgtype2string(msg->code), rq->from->conf->host, msg->id);
- goto exit;
+ if (msg->code == RAD_Accounting_Request)
+ memset(msg->auth, 0, 16);
+ else if (!RAND_bytes(msg->auth, 16)) {
+ debug(DBG_WARN, "radsrv: failed to generate random auth");
+ goto rmclrqexit;
}
- if (msg->code != RAD_Accounting_Request) {
- if (!RAND_bytes(newauth, 16)) {
- debug(DBG_WARN, "radsrv: failed to generate random auth");
- goto exit;
- }
- }
-
#ifdef DEBUG
printfchars(NULL, "auth", "%02x ", auth, 16);
#endif
attr = radmsg_gettype(msg, RAD_Attr_User_Password);
if (attr) {
debug(DBG_DBG, "radsrv: found userpwdattr with value length %d", attr->l);
- if (!pwdrecrypt(attr->v, attr->l, rq->from->conf->secret, to->conf->secret, msg->auth, newauth))
- goto exit;
+ if (!pwdrecrypt(attr->v, attr->l, from->conf->secret, to->conf->secret, rq->rqauth, msg->auth))
+ goto rmclrqexit;
}
attr = radmsg_gettype(msg, RAD_Attr_Tunnel_Password);
if (attr) {
debug(DBG_DBG, "radsrv: found tunnelpwdattr with value length %d", attr->l);
- if (!pwdrecrypt(attr->v, attr->l, rq->from->conf->secret, to->conf->secret, msg->auth, newauth))
- goto exit;
+ if (!pwdrecrypt(attr->v, attr->l, from->conf->secret, to->conf->secret, rq->rqauth, msg->auth))
+ goto rmclrqexit;
}
- rq->origid = msg->id;
- memcpy(rq->origauth, msg->auth, 16);
- memcpy(msg->auth, newauth, 16);
-
if (to->conf->rewriteout && !dorewrite(msg, to->conf->rewriteout))
- goto exit;
+ goto rmclrqexit;
free(userascii);
- rq->msg = msg;
- sendrq(to, rq);
+ rq->to = to;
+ sendrq(rq);
return 1;
+ rmclrqexit:
+ rmclientrq(rq, msg->id);
exit:
+ freerq(rq);
free(userascii);
- radmsg_free(msg);
- freerqdata(rq);
return 1;
}
void replyh(struct server *server, unsigned char *buf) {
struct client *from;
- struct request *rq;
+ struct rqout *rqout;
int sublen;
unsigned char *subattrs;
- struct sockaddr_storage fromsa;
- uint8_t *username, *stationid;
+ uint8_t *username, *stationid, *replymsg;
struct radmsg *msg = NULL;
struct tlv *attr;
struct list_node *node;
server->connectionok = 1;
server->lostrqs = 0;
- rq = server->requests + buf[1];
- msg = buf2radmsg(buf, (uint8_t *)server->conf->secret, rq->msg->auth);
+ rqout = server->requests + buf[1];
+ pthread_mutex_lock(rqout->lock);
+ if (!rqout->tries) {
+ free(buf);
+ buf = NULL;
+ debug(DBG_INFO, "replyh: no outstanding request with this id, ignoring reply");
+ goto errunlock;
+ }
+
+ msg = buf2radmsg(buf, (uint8_t *)server->conf->secret, rqout->rq->msg->auth);
free(buf);
buf = NULL;
if (!msg) {
debug(DBG_WARN, "replyh: message validation failed, ignoring packet");
- return;
+ goto errunlock;
}
if (msg->code != RAD_Access_Accept && msg->code != RAD_Access_Reject && msg->code != RAD_Access_Challenge
&& msg->code != RAD_Accounting_Response) {
debug(DBG_INFO, "replyh: discarding message type %s, accepting only access accept, access reject, access challenge and accounting response messages", radmsgtype2string(msg->code));
- radmsg_free(msg);
- return;
- }
- debug(DBG_DBG, "got %s message with id %d", radmsgtype2string(msg->code), msg->id);
-
- pthread_mutex_lock(&server->newrq_mutex);
- if (!rq->buf || !rq->tries) {
- debug(DBG_INFO, "replyh: no matching request sent with this id, ignoring reply");
goto errunlock;
}
+ debug(DBG_DBG, "got %s message with id %d", radmsgtype2string(msg->code), msg->id);
- if (rq->received) {
- debug(DBG_INFO, "replyh: already received, ignoring reply");
- goto errunlock;
- }
-
gettimeofday(&server->lastrcv, NULL);
- if (rq->msg->code == RAD_Status_Server) {
- rq->received = 1;
+ if (rqout->rq->msg->code == RAD_Status_Server) {
+ freerqoutdata(rqout);
debug(DBG_DBG, "replyh: got status server response from %s", server->conf->host);
goto errunlock;
}
gettimeofday(&server->lastreply, NULL);
-
- from = rq->from;
- if (!from) {
- debug(DBG_INFO, "replyh: client gone, ignoring reply");
- goto errunlock;
- }
+ from = rqout->rq->from;
- if (server->conf->rewritein && !dorewrite(msg, rq->from->conf->rewritein)) {
+ if (server->conf->rewritein && !dorewrite(msg, from->conf->rewritein)) {
debug(DBG_WARN, "replyh: rewritein failed");
goto errunlock;
}
subattrs = attr->v + 4;
if (!attrvalidate(subattrs, sublen) ||
!msmppe(subattrs, sublen, RAD_VS_ATTR_MS_MPPE_Send_Key, "MS MPPE Send Key",
- rq, server->conf->secret, from->conf->secret) ||
+ rqout->rq, server->conf->secret, from->conf->secret) ||
!msmppe(subattrs, sublen, RAD_VS_ATTR_MS_MPPE_Recv_Key, "MS MPPE Recv Key",
- rq, server->conf->secret, from->conf->secret))
+ rqout->rq, server->conf->secret, from->conf->secret))
break;
}
if (node) {
}
if (msg->code == RAD_Access_Accept || msg->code == RAD_Access_Reject || msg->code == RAD_Accounting_Response) {
- username = radattr2ascii(radmsg_gettype(rq->msg, RAD_Attr_User_Name));
+ username = radattr2ascii(radmsg_gettype(rqout->rq->msg, RAD_Attr_User_Name));
if (username) {
- stationid = radattr2ascii(radmsg_gettype(rq->msg, RAD_Attr_Calling_Station_Id));
+ stationid = radattr2ascii(radmsg_gettype(rqout->rq->msg, RAD_Attr_Calling_Station_Id));
+ replymsg = radattr2ascii(radmsg_gettype(msg, RAD_Attr_Reply_Message));
if (stationid) {
- debug(DBG_INFO, "%s for user %s stationid %s from %s",
- radmsgtype2string(msg->code), username, stationid, server->conf->host);
+ if (replymsg) {
+ debug(DBG_INFO, "%s for user %s stationid %s from %s (%s)",
+ radmsgtype2string(msg->code), username, stationid, server->conf->host, replymsg);
+ free(replymsg);
+ } else
+ debug(DBG_INFO, "%s for user %s stationid %s from %s",
+ radmsgtype2string(msg->code), username, stationid, server->conf->host);
free(stationid);
- } else
- debug(DBG_INFO, "%s for user %s from %s", radmsgtype2string(msg->code), username, server->conf->host);
+ } else {
+ if (replymsg) {
+ debug(DBG_INFO, "%s for user %s from %s (%s)",
+ radmsgtype2string(msg->code), username, server->conf->host, replymsg);
+ free(replymsg);
+ } else
+ debug(DBG_INFO, "%s for user %s from %s",
+ radmsgtype2string(msg->code), username, server->conf->host);
+ }
free(username);
}
}
- msg->id = (char)rq->origid;
- memcpy(msg->auth, rq->origauth, 16);
+ msg->id = (char)rqout->rq->rqid;
+ memcpy(msg->auth, rqout->rq->rqauth, 16);
#ifdef DEBUG
printfchars(NULL, "origauth/buf+4", "%02x ", buf + 4, 16);
#endif
- if (rq->origusername && (attr = radmsg_gettype(msg, RAD_Attr_User_Name))) {
- if (!resizeattr(attr, strlen(rq->origusername))) {
+ if (rqout->rq->origusername && (attr = radmsg_gettype(msg, RAD_Attr_User_Name))) {
+ if (!resizeattr(attr, strlen(rqout->rq->origusername))) {
debug(DBG_WARN, "replyh: malloc failed, ignoring reply");
goto errunlock;
}
- memcpy(attr->v, rq->origusername, strlen(rq->origusername));
+ memcpy(attr->v, rqout->rq->origusername, strlen(rqout->rq->origusername));
}
if (from->conf->rewriteout && !dorewrite(msg, from->conf->rewriteout)) {
goto errunlock;
}
- buf = radmsg2buf(msg, (uint8_t *)from->conf->secret);
- radmsg_free(msg);
- msg = NULL;
- if (!buf) {
- debug(DBG_ERR, "replyh: malloc failed");
- goto errunlock;
- }
-
- fromsa = rq->fromsa; /* only needed for UDP */
- /* once we set received = 1, rq may be reused */
- rq->received = 1;
-
debug(DBG_INFO, "replyh: passing reply to client %s", from->conf->name);
- sendreply(from, buf, &fromsa, rq->fromudpsock);
- pthread_mutex_unlock(&server->newrq_mutex);
+ radmsg_free(rqout->rq->msg);
+ rqout->rq->msg = msg;
+ rqout->rq->refcount++;
+ sendreply(rqout->rq);
+ freerqoutdata(rqout);
+ pthread_mutex_unlock(rqout->lock);
return;
errunlock:
radmsg_free(msg);
- pthread_mutex_unlock(&server->newrq_mutex);
+ pthread_mutex_unlock(rqout->lock);
return;
}
-struct radmsg *createstatsrvmsg() {
- struct radmsg *msg;
+struct request *createstatsrvrq() {
+ struct request *rq;
struct tlv *attr;
- msg = radmsg_init(RAD_Status_Server, 0, NULL);
- if (!msg)
+ rq = newrequest();
+ if (!rq)
return NULL;
-
+ rq->msg = radmsg_init(RAD_Status_Server, 0, NULL);
+ if (!rq->msg)
+ goto exit;
attr = maketlv(RAD_Attr_Message_Authenticator, 16, NULL);
- if (!attr) {
- radmsg_free(msg);
- return NULL;
- }
- if (!radmsg_add(msg, attr)) {
+ if (!attr)
+ goto exit;
+ if (!radmsg_add(rq->msg, attr)) {
freetlv(attr);
- radmsg_free(msg);
- return NULL;
+ goto exit;
}
- return msg;
+ return rq;
+
+ exit:
+ freerq(rq);
+ return NULL;
}
/* code for removing state not finished */
void *clientwr(void *arg) {
struct server *server = (struct server *)arg;
- struct request *rq;
+ struct rqout *rqout = NULL;
pthread_t clientrdth;
- int i, secs, dynconffail = 0;
+ int i, dynconffail = 0;
+ time_t secs;
uint8_t rnd;
struct timeval now, laststatsrv;
struct timespec timeout;
- struct request statsrvrq;
+ struct request *statsrvrq;
struct clsrvconf *conf;
conf = server->conf;
rnd /= 32;
if (conf->statusserver) {
secs = server->lastrcv.tv_sec > laststatsrv.tv_sec ? server->lastrcv.tv_sec : laststatsrv.tv_sec;
+ if (now.tv_sec - secs > STATUS_SERVER_PERIOD)
+ secs = now.tv_sec;
if (!timeout.tv_sec || timeout.tv_sec > secs + STATUS_SERVER_PERIOD + rnd)
timeout.tv_sec = secs + STATUS_SERVER_PERIOD + rnd;
} else {
pthread_join(clientrdth, NULL);
goto errexit;
}
- pthread_mutex_lock(&server->newrq_mutex);
- while (i < MAX_REQUESTS && !server->requests[i].buf)
- i++;
- if (i == MAX_REQUESTS) {
- pthread_mutex_unlock(&server->newrq_mutex);
- break;
- }
- rq = server->requests + i;
-
- if (rq->received) {
- 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);
- freerqdata(rq);
- /* setting this to NULL means that it can be reused */
- rq->buf = NULL;
+
+ for (; i < MAX_REQUESTS; i++) {
+ rqout = server->requests + i;
+ if (rqout->rq) {
+ pthread_mutex_lock(rqout->lock);
+ if (rqout->rq)
+ break;
+ pthread_mutex_unlock(rqout->lock);
}
- pthread_mutex_unlock(&server->newrq_mutex);
- continue;
- }
-
+ }
+
+ if (i == MAX_REQUESTS)
+ break;
+
gettimeofday(&now, NULL);
- if (now.tv_sec < rq->expiry.tv_sec) {
- if (!timeout.tv_sec || rq->expiry.tv_sec < timeout.tv_sec)
- timeout.tv_sec = rq->expiry.tv_sec;
- pthread_mutex_unlock(&server->newrq_mutex);
+ if (now.tv_sec < rqout->expiry.tv_sec) {
+ if (!timeout.tv_sec || rqout->expiry.tv_sec < timeout.tv_sec)
+ timeout.tv_sec = rqout->expiry.tv_sec;
+ pthread_mutex_unlock(rqout->lock);
continue;
}
- if (rq->tries == (*rq->buf == RAD_Status_Server ? 1 : conf->retrycount + 1)) {
+ if (rqout->tries == (*rqout->rq->buf == RAD_Status_Server ? 1 : conf->retrycount + 1)) {
debug(DBG_DBG, "clientwr: removing expired packet from queue");
if (conf->statusserver) {
- if (*rq->buf == RAD_Status_Server) {
+ if (*rqout->rq->buf == RAD_Status_Server) {
debug(DBG_WARN, "clientwr: no status server response, %s dead?", conf->host);
if (server->lostrqs < 255)
server->lostrqs++;
if (server->lostrqs < 255)
server->lostrqs++;
}
- freerqdata(rq);
- /* setting this to NULL means that it can be reused */
- rq->buf = NULL;
- pthread_mutex_unlock(&server->newrq_mutex);
+ freerqoutdata(rqout);
+ pthread_mutex_unlock(rqout->lock);
continue;
}
- pthread_mutex_unlock(&server->newrq_mutex);
- rq->expiry.tv_sec = now.tv_sec + conf->retryinterval;
- if (!timeout.tv_sec || rq->expiry.tv_sec < timeout.tv_sec)
- timeout.tv_sec = rq->expiry.tv_sec;
- rq->tries++;
- conf->pdef->clientradput(server, server->requests[i].buf);
+ rqout->expiry.tv_sec = now.tv_sec + conf->retryinterval;
+ if (!timeout.tv_sec || rqout->expiry.tv_sec < timeout.tv_sec)
+ timeout.tv_sec = rqout->expiry.tv_sec;
+ rqout->tries++;
+ conf->pdef->clientradput(server, rqout->rq->buf);
+ pthread_mutex_unlock(rqout->lock);
}
- if (conf->statusserver) {
+ if (conf->statusserver && server->connectionok) {
secs = server->lastrcv.tv_sec > laststatsrv.tv_sec ? server->lastrcv.tv_sec : laststatsrv.tv_sec;
gettimeofday(&now, NULL);
if (now.tv_sec - secs > STATUS_SERVER_PERIOD) {
laststatsrv = now;
- memset(&statsrvrq, 0, sizeof(struct request));
- statsrvrq.msg = createstatsrvmsg();
- if (statsrvrq.msg) {
+ statsrvrq = createstatsrvrq();
+ if (statsrvrq) {
+ statsrvrq->to = server;
debug(DBG_DBG, "clientwr: sending status server to %s", conf->host);
- sendrq(server, &statsrvrq);
+ sendrq(statsrvrq);
}
}
}
return ctx;
}
-SSL_CTX *tlsgetctx(uint8_t type, char *alt1, char *alt2) {
+struct tls *tlsgettls(char *alt1, char *alt2) {
struct tls *t;
t = hash_read(tlsconfs, alt1, strlen(alt1));
- if (!t) {
+ if (!t)
t = hash_read(tlsconfs, alt2, strlen(alt2));
- if (!t)
- return NULL;
- }
+ return t;
+}
+SSL_CTX *tlsgetctx(uint8_t type, struct tls *t) {
+ struct timeval now;
+
+ if (!t)
+ return NULL;
+ gettimeofday(&now, NULL);
+
switch (type) {
case RAD_TLS:
- if (!t->tlsctx)
+ if (t->tlsexpiry && t->tlsctx) {
+ if (t->tlsexpiry < now.tv_sec) {
+ t->tlsexpiry = now.tv_sec + t->cacheexpiry;
+ SSL_CTX_free(t->tlsctx);
+ return t->tlsctx = tlscreatectx(RAD_TLS, t);
+ }
+ }
+ if (!t->tlsctx) {
t->tlsctx = tlscreatectx(RAD_TLS, t);
+ if (t->cacheexpiry)
+ t->tlsexpiry = now.tv_sec + t->cacheexpiry;
+ }
return t->tlsctx;
case RAD_DTLS:
- if (!t->dtlsctx)
+ if (t->dtlsexpiry && t->dtlsctx) {
+ if (t->dtlsexpiry < now.tv_sec) {
+ t->dtlsexpiry = now.tv_sec + t->cacheexpiry;
+ SSL_CTX_free(t->dtlsctx);
+ return t->dtlsctx = tlscreatectx(RAD_DTLS, t);
+ }
+ }
+ if (!t->dtlsctx) {
t->dtlsctx = tlscreatectx(RAD_DTLS, t);
+ if (t->cacheexpiry)
+ t->dtlsexpiry = now.tv_sec + t->cacheexpiry;
+ }
return t->dtlsctx;
}
return NULL;
free(conf->rewriteout);
if (conf->addrinfo)
freeaddrinfo(conf->addrinfo);
+ if (conf->lock) {
+ pthread_mutex_destroy(conf->lock);
+ free(conf->lock);
+ }
/* not touching ssl_ctx, clients and servers */
free(conf);
}
int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val) {
struct clsrvconf *conf;
char *conftype = NULL, *rewriteinalias = NULL;
+ long int dupinterval = LONG_MIN;
debug(DBG_DBG, "confclient_cb called for %s", block);
conf = malloc(sizeof(struct clsrvconf));
- if (!conf || !list_push(clconfs, conf))
+ if (!conf)
debugx(1, DBG_ERR, "malloc failed");
memset(conf, 0, sizeof(struct clsrvconf));
conf->certnamecheck = 1;
"tls", CONF_STR, &conf->tls,
"matchcertificateattribute", CONF_STR, &conf->matchcertattr,
"CertificateNameCheck", CONF_BLN, &conf->certnamecheck,
+ "DuplicateInterval", CONF_LINT, &dupinterval,
"rewrite", CONF_STR, &rewriteinalias,
"rewriteIn", CONF_STR, &conf->confrewritein,
"rewriteOut", CONF_STR, &conf->confrewriteout,
free(conftype);
if (conf->type == RAD_TLS || conf->type == RAD_DTLS) {
- conf->ssl_ctx = conf->tls ? tlsgetctx(conf->type, conf->tls, NULL) : tlsgetctx(conf->type, "defaultclient", "default");
- if (!conf->ssl_ctx)
+ conf->tlsconf = conf->tls ? tlsgettls(conf->tls, NULL) : tlsgettls("defaultclient", "default");
+ if (!conf->tlsconf)
debugx(1, DBG_ERR, "error in block %s, no tls context defined", block);
if (conf->matchcertattr && !addmatchcertattr(conf))
debugx(1, DBG_ERR, "error in block %s, invalid MatchCertificateAttributeValue", block);
}
-
+
+ if (dupinterval != LONG_MIN) {
+ if (dupinterval < 0 || dupinterval > 255)
+ debugx(1, DBG_ERR, "error in block %s, value of option DuplicateInterval is %d, must be 0-255", block, dupinterval);
+ conf->dupinterval = (uint8_t)dupinterval;
+ } else
+ conf->dupinterval = conf->pdef->duplicateintervaldefault;
+
if (!conf->confrewritein)
conf->confrewritein = rewriteinalias;
else
if (!conf->secret)
debugx(1, DBG_ERR, "malloc failed");
}
+
+ conf->lock = malloc(sizeof(pthread_mutex_t));
+ if (!conf->lock)
+ debugx(1, DBG_ERR, "malloc failed");
+
+ pthread_mutex_init(conf->lock, NULL);
+ if (!list_push(clconfs, conf))
+ debugx(1, DBG_ERR, "malloc failed");
return 1;
}
int compileserverconfig(struct clsrvconf *conf, const char *block) {
if (conf->type == RAD_TLS || conf->type == RAD_DTLS) {
- conf->ssl_ctx = conf->tls ? tlsgetctx(conf->type, conf->tls, NULL) : tlsgetctx(conf->type, "defaultserver", "default");
- if (!conf->ssl_ctx) {
+ conf->tlsconf = conf->tls ? tlsgettls(conf->tls, NULL) : tlsgettls("defaultserver", "default");
+ if (!conf->tlsconf) {
debug(DBG_ERR, "error in block %s, no tls context defined", block);
return 0;
}
int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val) {
struct tls *conf;
+ long int expiry = LONG_MIN;
debug(DBG_DBG, "conftls_cb called for %s", block);
"CertificateFile", CONF_STR, &conf->certfile,
"CertificateKeyFile", CONF_STR, &conf->certkeyfile,
"CertificateKeyPassword", CONF_STR, &conf->certkeypwd,
+ "CacheExpiry", CONF_LINT, &expiry,
"CRLCheck", CONF_BLN, &conf->crlcheck,
NULL
)) {
debug(DBG_ERR, "conftls_cb: CA Certificate file or path need to be specified in block %s", val);
goto errexit;
}
+ if (expiry != LONG_MIN) {
+ if (expiry < 0) {
+ debug(DBG_ERR, "error in block %s, value of option CacheExpiry is %ld, may not be negative", val, expiry);
+ goto errexit;
+ }
+ conf->cacheexpiry = expiry;
+ }
conf->name = stringcopy(val, 0);
if (!conf->name) {
if (!foreground && (daemon(0, 0) < 0))
debugx(1, DBG_ERR, "daemon() failed: %s", strerror(errno));
+ debug_timestamp_on();
debug(DBG_INFO, "radsecproxy revision $Rev$ starting");
sigemptyset(&sigset);