Add a note about the change of default place to look for radsecproxy.conf.
[libradsec.git] / radsecproxy.c
index 4f79293..5e9b5ca 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no>
+ * Copyright (C) 2010,2011,2012 NORDUnet A/S
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
 #include <regex.h>
 #include <libgen.h>
 #include <pthread.h>
+#include <errno.h>
+#include <assert.h>
 #include <openssl/ssl.h>
 #include <openssl/rand.h>
 #include <openssl/err.h>
 #include <openssl/md5.h>
 #include "debug.h"
-#include "list.h"
 #include "hash.h"
 #include "util.h"
 #include "hostport.h"
@@ -76,6 +78,9 @@
 #include "tcp.h"
 #include "tls.h"
 #include "dtls.h"
+#if defined(WANT_FTICKS)
+#include "fticks.h"
+#endif
 
 static struct options options;
 static struct list *clconfs, *srvconfs;
@@ -370,24 +375,24 @@ int addserver(struct clsrvconf *conf) {
            goto errexit;
        }
        if (pthread_mutex_init(conf->servers->requests[i].lock, NULL)) {
-           debug(DBG_ERR, "mutex init failed");
+           debugerrno(errno, 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");
+       debugerrno(errno, DBG_ERR, "mutex init failed");
        goto errexit;
     }
     conf->servers->newrq = 0;
     if (pthread_mutex_init(&conf->servers->newrq_mutex, NULL)) {
-       debug(DBG_ERR, "mutex init failed");
+       debugerrno(errno, DBG_ERR, "mutex init failed");
        pthread_mutex_destroy(&conf->servers->lock);
        goto errexit;
     }
     if (pthread_cond_init(&conf->servers->newrq_cond, NULL)) {
-       debug(DBG_ERR, "mutex init failed");
+       debugerrno(errno, DBG_ERR, "mutex init failed");
        pthread_mutex_destroy(&conf->servers->newrq_mutex);
        pthread_mutex_destroy(&conf->servers->lock);
        goto errexit;
@@ -559,43 +564,7 @@ void sendreply(struct request *rq) {
     pthread_mutex_unlock(&to->replyq->mutex);
 }
 
-int pwdencrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
-    static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-    static unsigned char first = 1;
-    static EVP_MD_CTX mdctx;
-    unsigned char hash[EVP_MAX_MD_SIZE], *input;
-    unsigned int md_len;
-    uint8_t i, offset = 0, out[128];
-
-    pthread_mutex_lock(&lock);
-    if (first) {
-       EVP_MD_CTX_init(&mdctx);
-       first = 0;
-    }
-
-    input = auth;
-    for (;;) {
-       if (!EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL) ||
-           !EVP_DigestUpdate(&mdctx, (uint8_t *)shared, sharedlen) ||
-           !EVP_DigestUpdate(&mdctx, input, 16) ||
-           !EVP_DigestFinal_ex(&mdctx, hash, &md_len) ||
-           md_len != 16) {
-           pthread_mutex_unlock(&lock);
-           return 0;
-       }
-       for (i = 0; i < 16; i++)
-           out[offset + i] = hash[i] ^ in[offset + i];
-       input = out + offset - 16;
-       offset += 16;
-       if (offset == len)
-           break;
-    }
-    memcpy(in, out, len);
-    pthread_mutex_unlock(&lock);
-    return 1;
-}
-
-int pwddecrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
+int pwdcrypt(char encrypt_flag, uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_t *auth) {
     static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
     static unsigned char first = 1;
     static EVP_MD_CTX mdctx;
@@ -621,7 +590,10 @@ int pwddecrypt(uint8_t *in, uint8_t len, char *shared, uint8_t sharedlen, uint8_
        }
        for (i = 0; i < 16; i++)
            out[offset + i] = hash[i] ^ in[offset + i];
-       input = in + offset;
+       if (encrypt_flag)
+           input = out + offset;
+       else
+           input = in + offset;
        offset += 16;
        if (offset == len)
            break;
@@ -883,14 +855,14 @@ int pwdrecrypt(uint8_t *pwd, uint8_t len, char *oldsecret, char *newsecret, uint
        return 0;
     }
 
-    if (!pwddecrypt(pwd, len, oldsecret, strlen(oldsecret), oldauth)) {
+    if (!pwdcrypt(0, pwd, len, oldsecret, strlen(oldsecret), oldauth)) {
        debug(DBG_WARN, "pwdrecrypt: cannot decrypt password");
        return 0;
     }
 #ifdef DEBUG
     printfchars(NULL, "pwdrecrypt: password", "%02x ", pwd, len);
 #endif
-    if (!pwdencrypt(pwd, len, newsecret, strlen(newsecret), newauth)) {
+    if (!pwdcrypt(1, pwd, len, newsecret, strlen(newsecret), newauth)) {
        debug(DBG_WARN, "pwdrecrypt: cannot encrypt password");
        return 0;
     }
@@ -1335,6 +1307,8 @@ struct clsrvconf *choosesrvconf(struct list *srvconfs) {
        server = (struct clsrvconf *)entry->data;
        if (!server->servers)
            return server;
+        if (server->servers->dynfailing)
+            continue;
        if (!first)
            first = server;
        if (!server->servers->connectionok && !server->servers->dynstartup)
@@ -1373,11 +1347,15 @@ struct server *findserver(struct realm **realm, struct tlv *username, uint8_t ac
            pthread_mutex_unlock(&(*realm)->mutex);
            freerealm(*realm);
            *realm = subrealm;
+            debug(DBG_DBG, "added realm: %s", (*realm)->name);
            srvconf = choosesrvconf(acc ? (*realm)->accsrvconfs : (*realm)->srvconfs);
+            debug(DBG_DBG, "found conf for new realm: %s", srvconf->name);
        }
     }
-    if (srvconf)
+    if (srvconf) {
+        debug(DBG_DBG, "found matching conf: %s", srvconf->name);
        server = srvconf->servers;
+    }
 
 exit:
     free(id);
@@ -1666,26 +1644,42 @@ void replyh(struct server *server, unsigned char *buf) {
            replymsg = radattr2ascii(radmsg_gettype(msg, RAD_Attr_Reply_Message));
            if (stationid) {
                if (replymsg) {
-                   debug(DBG_WARN, "%s for user %s stationid %s from %s (%s) to %s (%s)",
-                         radmsgtype2string(msg->code), username, stationid, server->conf->name, replymsg, from->conf->name, addr2string(from->addr));
+                   debug(DBG_NOTICE,
+                         "%s for user %s stationid %s from %s (%s) to %s (%s)",
+                         radmsgtype2string(msg->code), username, stationid,
+                         server->conf->name, replymsg, from->conf->name,
+                         addr2string(from->addr));
                    free(replymsg);
                } else
-                   debug(DBG_WARN, "%s for user %s stationid %s from %s to %s (%s)",
-                         radmsgtype2string(msg->code), username, stationid, server->conf->name, from->conf->name, addr2string(from->addr));
+                   debug(DBG_NOTICE,
+                         "%s for user %s stationid %s from %s to %s (%s)",
+                         radmsgtype2string(msg->code), username, stationid,
+                         server->conf->name, from->conf->name,
+                         addr2string(from->addr));
                free(stationid);
            } else {
                if (replymsg) {
-                   debug(DBG_WARN, "%s for user %s from %s (%s) to %s (%s)",
-                         radmsgtype2string(msg->code), username, server->conf->name, replymsg, from->conf->name, addr2string(from->addr));
+                   debug(DBG_NOTICE, "%s for user %s from %s (%s) to %s (%s)",
+                         radmsgtype2string(msg->code), username,
+                         server->conf->name, replymsg, from->conf->name,
+                         addr2string(from->addr));
                    free(replymsg);
                } else
-                   debug(DBG_WARN, "%s for user %s from %s to %s (%s)",
-                         radmsgtype2string(msg->code), username, server->conf->name, from->conf->name, addr2string(from->addr));
+                   debug(DBG_NOTICE, "%s for user %s from %s to %s (%s)",
+                         radmsgtype2string(msg->code), username,
+                         server->conf->name, from->conf->name,
+                         addr2string(from->addr));
            }
            free(username);
        }
     }
 
+#if defined(WANT_FTICKS)
+    if (msg->code == RAD_Access_Accept || msg->code == RAD_Access_Reject)
+       if (options.fticks_reporting && from->conf->fticks_viscountry != NULL)
+           fticks_log(&options, from, msg, rqout);
+#endif
+
     msg->id = (char)rqout->rq->rqid;
     memcpy(msg->auth, rqout->rq->rqauth, 16);
 
@@ -1764,18 +1758,25 @@ void *clientwr(void *arg) {
 
     conf = server->conf;
 
+#define ZZZ 900
+
     if (server->dynamiclookuparg && !dynamicconfig(server)) {
        dynconffail = 1;
        server->dynstartup = 0;
-       sleep(900);
+       server->dynfailing = 1;
+       debug(DBG_WARN, "%s: dynamicconfig(%s) failed, sleeping %ds",
+              __func__, server->conf->name, ZZZ);
+       sleep(ZZZ);
        goto errexit;
     }
 
+    /* FIXME: Is resolving not always done by compileserverconfig(),
+     * either as part of static configuration setup or by
+     * dynamicconfig() above?  */
     if (!resolvehostports(conf->hostports, conf->pdef->socktype)) {
-       debug(DBG_WARN, "clientwr: resolve failed");
-       server->dynstartup = 0;
-       sleep(900);
-       goto errexit;
+        debug(DBG_WARN, "%s: resolve failed, sleeping %ds", __func__, ZZZ);
+        sleep(ZZZ);
+        goto errexit;
     }
 
     memset(&timeout, 0, sizeof(struct timespec));
@@ -1788,14 +1789,17 @@ void *clientwr(void *arg) {
     if (conf->pdef->connecter) {
        if (!conf->pdef->connecter(server, NULL, server->dynamiclookuparg ? 5 : 0, "clientwr")) {
            if (server->dynamiclookuparg) {
-               server->dynstartup = 0;
-               sleep(900);
+                server->dynstartup = 0;
+               server->dynfailing = 1;
+                debug(DBG_WARN, "%s: connect failed, sleeping %ds",
+                      __func__, ZZZ);
+               sleep(ZZZ);
            }
            goto errexit;
        }
        server->connectionok = 1;
        if (pthread_create(&clientrdth, NULL, conf->pdef->clientconnreader, (void *)server)) {
-           debug(DBG_ERR, "clientwr: pthread_create failed");
+           debugerrno(errno, DBG_ERR, "clientwr: pthread_create failed");
            goto errexit;
        }
     } else
@@ -1928,7 +1932,7 @@ void createlistener(uint8_t type, char *arg) {
     for (res = hp->addrinfo; res; res = res->ai_next) {
         s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
         if (s < 0) {
-            debug(DBG_WARN, "createlistener: socket failed");
+            debugerrno(errno, DBG_WARN, "createlistener: socket failed");
             continue;
         }
        setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
@@ -1940,7 +1944,7 @@ void createlistener(uint8_t type, char *arg) {
            setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
 #endif
        if (bind(s, res->ai_addr, res->ai_addrlen)) {
-           debug(DBG_WARN, "createlistener: bind failed");
+           debugerrno(errno, DBG_WARN, "createlistener: bind failed");
            close(s);
            s = -1;
            continue;
@@ -1951,7 +1955,7 @@ void createlistener(uint8_t type, char *arg) {
             debugx(1, DBG_ERR, "malloc failed");
        *sp = s;
        if (pthread_create(&th, NULL, protodefs[type]->listener, (void *)sp))
-            debugx(1, DBG_ERR, "pthread_create failed");
+            debugerrnox(errno, DBG_ERR, "pthread_create failed");
        pthread_detach(th);
     }
     if (!sp)
@@ -2100,7 +2104,7 @@ struct realm *addrealm(struct list *realmlist, char *value, char **servers, char
     memset(realm, 0, sizeof(struct realm));
 
     if (pthread_mutex_init(&realm->mutex, NULL)) {
-       debug(DBG_ERR, "mutex init failed");
+       debugerrno(errno, DBG_ERR, "mutex init failed");
        free(realm);
        realm = NULL;
        goto exit;
@@ -2186,12 +2190,19 @@ struct list *createsubrealmservers(struct realm *realm, struct list *srvconfs) {
                debug(DBG_ERR, "malloc failed");
                continue;
            }
+            debug(DBG_DBG, "%s: copying config %s", __func__, conf->name);
            *srvconf = *conf;
+            /* Shallow copy -- sharing all the pointers.  addserver()
+             * will take care of servers (which btw has to be NUL) but
+             * the rest of them are shared with the config found in
+             * the srvconfs list.  */
            if (addserver(srvconf)) {
                srvconf->servers->dynamiclookuparg = stringcopy(realm->name, 0);
                srvconf->servers->dynstartup = 1;
+                debug(DBG_DBG, "%s: new client writer for %s",
+                      __func__, srvconf->servers->conf->name);
                if (pthread_create(&clientth, NULL, clientwr, (void *)(srvconf->servers))) {
-                   debug(DBG_ERR, "pthread_create failed");
+                   debugerrno(errno, DBG_ERR, "pthread_create failed");
                    freeserver(srvconf->servers, 1);
                    srvconf->servers = NULL;
                } else
@@ -2253,12 +2264,12 @@ int dynamicconfig(struct server *server) {
     debug(DBG_DBG, "dynamicconfig: need dynamic server config for %s", server->dynamiclookuparg);
 
     if (pipe(fd) > 0) {
-       debug(DBG_ERR, "dynamicconfig: pipe error");
+       debugerrno(errno, DBG_ERR, "dynamicconfig: pipe error");
        goto errexit;
     }
     pid = fork();
     if (pid < 0) {
-       debug(DBG_ERR, "dynamicconfig: fork error");
+       debugerrno(errno, DBG_ERR, "dynamicconfig: fork error");
        close(fd[0]);
        close(fd[1]);
        goto errexit;
@@ -2276,20 +2287,19 @@ int dynamicconfig(struct server *server) {
 
     close(fd[1]);
     pushgconffile(&cf, fdopen(fd[0], "r"), conf->dynamiclookupcommand);
-    ok = getgenericconfig(&cf, NULL,
-                         "Server", CONF_CBK, confserver_cb, (void *)conf,
-                         NULL
-       );
+    ok = getgenericconfig(&cf, NULL, "Server", CONF_CBK, confserver_cb,
+                         (void *) conf, NULL);
     freegconf(&cf);
 
     if (waitpid(pid, &status, 0) < 0) {
-       debug(DBG_ERR, "dynamicconfig: wait error");
+       debugerrno(errno, DBG_ERR, "dynamicconfig: wait error");
        goto errexit;
     }
 
     if (status) {
-       debug(DBG_INFO, "dynamicconfig: command exited with status %d", WEXITSTATUS(status));
-       goto errexit;
+        debug(DBG_INFO, "dynamicconfig: command exited with status %d",
+              WEXITSTATUS(status));
+        goto errexit;
     }
 
     if (ok)
@@ -2327,8 +2337,14 @@ int vattrname2val(char *attrname, uint32_t *vendor, uint32_t *type) {
     return *type < 256;
 }
 
-/* should accept both names and numeric values, only numeric right now */
-struct tlv *extractattr(char *nameval) {
+/** Extract attributes from string NAMEVAL, create a struct tlv and
+ * return the tlv.  If VENDOR_FLAG, NAMEVAL is on the form
+ * "<vendor>:<name>:<val>" and otherwise it's "<name>:<val>".  Return
+ * NULL if fields are missing or if conversion fails.
+ *
+ * FIXME: Should accept both names and numeric values, only numeric
+ * right now */
+struct tlv *extractattr(char *nameval, char vendor_flag) {
     int len, name = 0;
     int vendor = 0;        /* Vendor 0 is reserved, see RFC 1700.  */
     char *s, *s2;
@@ -2339,9 +2355,10 @@ struct tlv *extractattr(char *nameval) {
        return NULL;
     name = atoi(nameval);
 
-    /* Two ':' means that we have vendor:name:val.  */
-    s2 = strchr(s + 1, ':');
-    if (s2) {
+    if (vendor_flag) {
+       s2 = strchr(s + 1, ':');
+       if (!s2)
+           return NULL;
        vendor = name;
        name = atoi(s + 1);
        s = s2;
@@ -2364,7 +2381,7 @@ struct tlv *extractattr(char *nameval) {
     a->t = name;
     a->l = len;
 
-    if (vendor)
+    if (vendor_flag)
        a = makevendortlv(vendor, a);
 
     return a;
@@ -2432,10 +2449,12 @@ struct modattr *extractmodattr(char *nameval) {
 struct rewrite *getrewrite(char *alt1, char *alt2) {
     struct rewrite *r;
 
-    if ((r = hash_read(rewriteconfs,  alt1, strlen(alt1))))
-       return r;
-    if ((r = hash_read(rewriteconfs,  alt2, strlen(alt2))))
-       return r;
+    if (alt1)
+       if ((r = hash_read(rewriteconfs,  alt1, strlen(alt1))))
+           return r;
+    if (alt2)
+       if ((r = hash_read(rewriteconfs,  alt2, strlen(alt2))))
+           return r;
     return NULL;
 }
 
@@ -2480,7 +2499,7 @@ void addrewrite(char *value, char **rmattrs, char **rmvattrs, char **addattrs, c
        if (!adda)
            debugx(1, DBG_ERR, "malloc failed");
        for (i = 0; addattrs[i]; i++) {
-           a = extractattr(addattrs[i]);
+           a = extractattr(addattrs[i], 0);
            if (!a)
                debugx(1, DBG_ERR, "addrewrite: adding invalid attribute %s", addattrs[i]);
            if (!list_push(adda, a))
@@ -2495,7 +2514,7 @@ void addrewrite(char *value, char **rmattrs, char **rmvattrs, char **addattrs, c
        if (!adda)
            debugx(1, DBG_ERR, "malloc failed");
        for (i = 0; addvattrs[i]; i++) {
-           a = extractattr(addvattrs[i]);
+           a = extractattr(addvattrs[i], 1);
            if (!a)
                debugx(1, DBG_ERR, "addrewrite: adding invalid vendor attribute %s", addvattrs[i]);
            if (!list_push(adda, a))
@@ -2544,6 +2563,9 @@ int setttlattr(struct options *opts, char *defaultattr) {
 }
 
 void freeclsrvconf(struct clsrvconf *conf) {
+    assert(conf);
+    assert(conf->name);
+    debug(DBG_DBG, "%s: freeing %p (%s)", __func__, conf, conf->name);
     free(conf->name);
     if (conf->hostsrc)
        freegconfmstr(conf->hostsrc);
@@ -2646,7 +2668,10 @@ int mergesrvconf(struct clsrvconf *dst, struct clsrvconf *src) {
        !mergeconfstring(&dst->matchcertattr, &src->matchcertattr) ||
        !mergeconfstring(&dst->confrewritein, &src->confrewritein) ||
        !mergeconfstring(&dst->confrewriteout, &src->confrewriteout) ||
-       !mergeconfstring(&dst->dynamiclookupcommand, &src->dynamiclookupcommand))
+       !mergeconfstring(&dst->confrewriteusername, &src->confrewriteusername) ||
+       !mergeconfstring(&dst->dynamiclookupcommand, &src->dynamiclookupcommand) ||
+       !mergeconfstring(&dst->fticks_viscountry, &src->fticks_viscountry) ||
+       !mergeconfstring(&dst->fticks_visinst, &src->fticks_visinst))
        return 0;
     if (src->pdef)
        dst->pdef = src->pdef;
@@ -2672,22 +2697,27 @@ int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
     memset(conf, 0, sizeof(struct clsrvconf));
     conf->certnamecheck = 1;
 
-    if (!getgenericconfig(cf, block,
-                         "type", CONF_STR, &conftype,
-                         "host", CONF_MSTR, &conf->hostsrc,
-                         "secret", CONF_STR, &conf->secret,
+    if (!getgenericconfig(
+           cf, block,
+           "type", CONF_STR, &conftype,
+           "host", CONF_MSTR, &conf->hostsrc,
+           "secret", CONF_STR, &conf->secret,
 #if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
-                         "tls", CONF_STR, &conf->tls,
-                         "matchcertificateattribute", CONF_STR, &conf->matchcertattr,
-                         "CertificateNameCheck", CONF_BLN, &conf->certnamecheck,
+           "tls", CONF_STR, &conf->tls,
+           "matchcertificateattribute", CONF_STR, &conf->matchcertattr,
+           "CertificateNameCheck", CONF_BLN, &conf->certnamecheck,
 #endif
-                         "DuplicateInterval", CONF_LINT, &dupinterval,
-                         "addTTL", CONF_LINT, &addttl,
-                         "rewrite", CONF_STR, &rewriteinalias,
-                         "rewriteIn", CONF_STR, &conf->confrewritein,
-                         "rewriteOut", CONF_STR, &conf->confrewriteout,
-                         "rewriteattribute", CONF_STR, &conf->confrewriteusername,
-                         NULL
+           "DuplicateInterval", CONF_LINT, &dupinterval,
+           "addTTL", CONF_LINT, &addttl,
+           "rewrite", CONF_STR, &rewriteinalias,
+           "rewriteIn", CONF_STR, &conf->confrewritein,
+           "rewriteOut", CONF_STR, &conf->confrewriteout,
+           "rewriteattribute", CONF_STR, &conf->confrewriteusername,
+#if defined(WANT_FTICKS)
+           "fticksVISCOUNTRY", CONF_STR, &conf->fticks_viscountry,
+           "fticksVISINST", CONF_STR, &conf->fticks_visinst,
+#endif
+           NULL
            ))
        debugx(1, DBG_ERR, "configuration error");
 
@@ -2712,7 +2742,9 @@ int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
 
 #if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
     if (conf->type == RAD_TLS || conf->type == RAD_DTLS) {
-       conf->tlsconf = conf->tls ? tlsgettls(conf->tls, NULL) : tlsgettls("defaultclient", "default");
+       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))
@@ -2737,7 +2769,9 @@ int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
        conf->confrewritein = rewriteinalias;
     else
        free(rewriteinalias);
-    conf->rewritein = conf->confrewritein ? getrewrite(conf->confrewritein, NULL) : getrewrite("defaultclient", "default");
+    conf->rewritein = conf->confrewritein
+        ? getrewrite(conf->confrewritein, NULL)
+        : getrewrite("defaultClient", "default");
     if (conf->confrewriteout)
        conf->rewriteout = getrewrite(conf->confrewriteout, NULL);
 
@@ -2749,7 +2783,7 @@ int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
 
     if (!addhostport(&conf->hostports, conf->hostsrc, conf->pdef->portdefault, 1) ||
        !resolvehostports(conf->hostports, conf->pdef->socktype))
-       debugx(1, DBG_ERR, "resolve failed, exiting");
+       debugx(1, DBG_ERR, "%s: resolve failed, exiting", __func__);
 
     if (!conf->secret) {
        if (!conf->pdef->secretdefault)
@@ -2772,7 +2806,9 @@ int confclient_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
 int compileserverconfig(struct clsrvconf *conf, const char *block) {
 #if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
     if (conf->type == RAD_TLS || conf->type == RAD_DTLS) {
-       conf->tlsconf = conf->tls ? tlsgettls(conf->tls, NULL) : tlsgettls("defaultserver", "default");
+       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;
@@ -2797,17 +2833,19 @@ int compileserverconfig(struct clsrvconf *conf, const char *block) {
     if (conf->retrycount == 255)
        conf->retrycount = conf->pdef->retrycountdefault;
 
-    conf->rewritein = conf->confrewritein ? getrewrite(conf->confrewritein, NULL) : getrewrite("defaultserver", "default");
+    conf->rewritein = conf->confrewritein
+        ? getrewrite(conf->confrewritein, NULL)
+        : getrewrite("defaultServer", "default");
     if (conf->confrewriteout)
        conf->rewriteout = getrewrite(conf->confrewriteout, NULL);
 
     if (!addhostport(&conf->hostports, conf->hostsrc, conf->portsrc, 0)) {
-       debug(DBG_ERR, "error in block %s, failed to parse %s", block, conf->hostsrc);
+       debug(DBG_ERR, "error in block %s, failed to parse %s", block, *conf->hostsrc);
        return 0;
     }
 
     if (!conf->dynamiclookupcommand && !resolvehostports(conf->hostports, conf->pdef->socktype)) {
-       debug(DBG_ERR, "resolve failed, exiting");
+       debug(DBG_ERR, "%s: resolve failed", __func__);
        return 0;
     }
     return 1;
@@ -2931,7 +2969,8 @@ int confserver_cb(struct gconffile **cf, void *arg, char *block, char *opt, char
 
     if (resconf || !conf->dynamiclookupcommand) {
        if (!compileserverconfig(conf, block))
-           goto errexit;
+            return 0; /* Don't goto errexit and free resconf -- it's
+                       * not ours to free.  */
     }
 
     if (!conf->secret) {
@@ -3019,6 +3058,11 @@ void getmainconfig(const char *configfile) {
     struct gconffile *cfs;
     char **listenargs[RAD_PROTOCOUNT];
     char *sourcearg[RAD_PROTOCOUNT];
+#if defined(WANT_FTICKS)
+    uint8_t *fticks_reporting_str = NULL;
+    uint8_t *fticks_mac_str = NULL;
+    uint8_t *fticks_key_str = NULL;
+#endif
     int i;
 
     cfs = openconfigfile(configfile);
@@ -3042,42 +3086,50 @@ void getmainconfig(const char *configfile) {
     if (!rewriteconfs)
        debugx(1, DBG_ERR, "malloc failed");
 
-    if (!getgenericconfig(&cfs, NULL,
+    if (!getgenericconfig(
+           &cfs, NULL,
 #ifdef RADPROT_UDP
-                         "ListenUDP", CONF_MSTR, &listenargs[RAD_UDP],
-                         "SourceUDP", CONF_STR, &sourcearg[RAD_UDP],
+           "ListenUDP", CONF_MSTR, &listenargs[RAD_UDP],
+           "SourceUDP", CONF_STR, &sourcearg[RAD_UDP],
 #endif
 #ifdef RADPROT_TCP
-                         "ListenTCP", CONF_MSTR, &listenargs[RAD_TCP],
-                         "SourceTCP", CONF_STR, &sourcearg[RAD_TCP],
+           "ListenTCP", CONF_MSTR, &listenargs[RAD_TCP],
+           "SourceTCP", CONF_STR, &sourcearg[RAD_TCP],
 #endif
 #ifdef RADPROT_TLS
-                         "ListenTLS", CONF_MSTR, &listenargs[RAD_TLS],
-                         "SourceTLS", CONF_STR, &sourcearg[RAD_TLS],
+           "ListenTLS", CONF_MSTR, &listenargs[RAD_TLS],
+           "SourceTLS", CONF_STR, &sourcearg[RAD_TLS],
 #endif
 #ifdef RADPROT_DTLS
-                         "ListenDTLS", CONF_MSTR, &listenargs[RAD_DTLS],
-                         "SourceDTLS", CONF_STR, &sourcearg[RAD_DTLS],
+           "ListenDTLS", CONF_MSTR, &listenargs[RAD_DTLS],
+           "SourceDTLS", CONF_STR, &sourcearg[RAD_DTLS],
 #endif
-                         "TTLAttribute", CONF_STR, &options.ttlattr,
-                         "addTTL", CONF_LINT, &addttl,
-                         "LogLevel", CONF_LINT, &loglevel,
-                         "LogDestination", CONF_STR, &options.logdestination,
-                         "LoopPrevention", CONF_BLN, &options.loopprevention,
-                         "Client", CONF_CBK, confclient_cb, NULL,
-                         "Server", CONF_CBK, confserver_cb, NULL,
-                         "Realm", CONF_CBK, confrealm_cb, NULL,
+            "PidFile", CONF_STR, &options.pidfile,
+           "TTLAttribute", CONF_STR, &options.ttlattr,
+           "addTTL", CONF_LINT, &addttl,
+           "LogLevel", CONF_LINT, &loglevel,
+           "LogDestination", CONF_STR, &options.logdestination,
+           "LoopPrevention", CONF_BLN, &options.loopprevention,
+           "Client", CONF_CBK, confclient_cb, NULL,
+           "Server", CONF_CBK, confserver_cb, NULL,
+           "Realm", CONF_CBK, confrealm_cb, NULL,
 #if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
-                         "TLS", CONF_CBK, conftls_cb, NULL,
+           "TLS", CONF_CBK, conftls_cb, NULL,
 #endif
-                         "Rewrite", CONF_CBK, confrewrite_cb, NULL,
-                         NULL
+           "Rewrite", CONF_CBK, confrewrite_cb, NULL,
+#if defined(WANT_FTICKS)
+           "FTicksReporting", CONF_STR, &fticks_reporting_str,
+           "FTicksMAC", CONF_STR, &fticks_mac_str,
+           "FTicksKey", CONF_STR, &fticks_key_str,
+           "FTicksSyslogFacility", CONF_STR, &options.ftickssyslogfacility,
+#endif
+           NULL
            ))
        debugx(1, DBG_ERR, "configuration error");
 
     if (loglevel != LONG_MIN) {
-       if (loglevel < 1 || loglevel > 4)
-           debugx(1, DBG_ERR, "error in %s, value of option LogLevel is %d, must be 1, 2, 3 or 4", configfile, loglevel);
+       if (loglevel < 1 || loglevel > 5)
+           debugx(1, DBG_ERR, "error in %s, value of option LogLevel is %d, must be 1, 2, 3, 4 or 5", configfile, loglevel);
        options.loglevel = (uint8_t)loglevel;
     }
     if (addttl != LONG_MIN) {
@@ -3088,6 +3140,11 @@ void getmainconfig(const char *configfile) {
     if (!setttlattr(&options, DEFAULT_TTL_ATTR))
        debugx(1, DBG_ERR, "Failed to set TTLAttribute, exiting");
 
+#if defined(WANT_FTICKS)
+    fticks_configure(&options, &fticks_reporting_str, &fticks_mac_str,
+                    &fticks_key_str);
+#endif
+
     for (i = 0; i < RAD_PROTOCOUNT; i++)
        if (listenargs[i] || sourcearg[i])
            setprotoopts(i, listenargs[i], sourcearg[i]);
@@ -3102,8 +3159,8 @@ void getargs(int argc, char **argv, uint8_t *foreground, uint8_t *pretend, uint8
            *configfile = optarg;
            break;
        case 'd':
-           if (strlen(optarg) != 1 || *optarg < '1' || *optarg > '4')
-               debugx(1, DBG_ERR, "Debug level must be 1, 2, 3 or 4, not %s", optarg);
+           if (strlen(optarg) != 1 || *optarg < '1' || *optarg > '5')
+               debugx(1, DBG_ERR, "Debug level must be 1, 2, 3, 4 or 5, not %s", optarg);
            *loglevel = *optarg - '0';
            break;
        case 'f':
@@ -3116,7 +3173,7 @@ void getargs(int argc, char **argv, uint8_t *foreground, uint8_t *pretend, uint8
            *pretend = 1;
            break;
        case 'v':
-           debug(DBG_ERR, "radsecproxy revision $Rev$");
+           debug(DBG_ERR, "radsecproxy revision %s", PACKAGE_VERSION);
            debug(DBG_ERR, "This binary was built with support for the following transports:");
 #ifdef RADPROT_UDP
            debug(DBG_ERR, "  UDP");
@@ -3186,14 +3243,14 @@ void *sighandler(void *arg) {
 }
 
 int createpidfile(const char *pidfile) {
-    int r;
+    int r = 0;
     FILE *f = fopen(pidfile, "w");
     if (f)
-       r = fprintf(f, "%d\n", getpid());
+       r = fprintf(f, "%ld\n", (long) getpid());
     return f && !fclose(f) && r >= 0;
 }
 
-int main(int argc, char **argv) {
+int radsecproxy_main(int argc, char **argv) {
     pthread_t sigth;
     sigset_t sigset;
     struct list_node *entry;
@@ -3219,8 +3276,18 @@ int main(int argc, char **argv) {
        options.loglevel = loglevel;
     else if (options.loglevel)
        debug_set_level(options.loglevel);
-    if (!foreground)
-       debug_set_destination(options.logdestination ? options.logdestination : "x-syslog:///");
+    if (!foreground) {
+       debug_set_destination(options.logdestination
+                              ? options.logdestination
+                              : "x-syslog:///", LOG_TYPE_DEBUG);
+#if defined(WANT_FTICKS)
+       if (options.ftickssyslogfacility) {
+            debug_set_destination(options.ftickssyslogfacility,
+                                  LOG_TYPE_FTICKS);
+            free(options.ftickssyslogfacility);
+       }
+#endif
+    }
     free(options.logdestination);
 
     if (!list_first(clconfs))
@@ -3235,7 +3302,9 @@ int main(int argc, char **argv) {
        debugx(1, DBG_ERR, "daemon() failed: %s", strerror(errno));
 
     debug_timestamp_on();
-    debug(DBG_INFO, "radsecproxy revision $Rev$ starting");
+    debug(DBG_INFO, "radsecproxy revision %s starting", PACKAGE_VERSION);
+    if (!pidfile)
+        pidfile = options.pidfile;
     if (pidfile && !createpidfile(pidfile))
        debugx(1, DBG_ERR, "failed to create pidfile %s: %s", pidfile, strerror(errno));