new debug version with timestamp off by default
[radsecproxy.git] / radsecproxy.c
index 6e0c2c6..80f6a47 100644 (file)
@@ -6,6 +6,11 @@
  * 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
+ * Multiple outgoing connections if not enough IDs? (multiple servers per conf?)
+ * Also useful for TCP accounting which is not yet supported?
  * We are not removing client requests from dynamic servers, see removeclientrqs()
+ * Reserve ID 0 for statusserver requests?
  * Need to remove UDP clients when no activity for a while...
  * Remove expired stuff from clients request list?
  */
@@ -572,7 +579,6 @@ struct client *addclient(struct clsrvconf *conf, uint8_t lock) {
     }
     
     memset(new, 0, sizeof(struct client));
-    pthread_mutex_init(&new->lock, NULL);
     new->conf = conf;
     if (conf->pdef->addclient)
        conf->pdef->addclient(new);
@@ -585,20 +591,19 @@ struct client *addclient(struct clsrvconf *conf, uint8_t lock) {
 }
 
 void removeclient(struct client *client) {
+    struct clsrvconf *conf;
+    
     if (!client)
        return;
-
-    pthread_mutex_lock(client->conf->lock);
-    if (client->conf->clients) {
-       pthread_mutex_lock(&client->lock);
+    conf = client->conf;
+    pthread_mutex_lock(conf->lock);
+    if (conf->clients) {
        removequeue(client->replyq);
-       list_removedata(client->conf->clients, client);
-       pthread_mutex_unlock(&client->lock);
-       pthread_mutex_destroy(&client->lock);
+       list_removedata(conf->clients, client);
        free(client->addr);
        free(client);
     }
-    pthread_mutex_unlock(client->conf->lock);
+    pthread_mutex_unlock(conf->lock);
 }
 
 void removeclientrqs(struct client *client) {
@@ -1345,14 +1350,14 @@ int pwdrecrypt(uint8_t *pwd, uint8_t len, char *oldsecret, char *newsecret, uint
     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;
     }
@@ -1365,7 +1370,7 @@ int msmppe(unsigned char *attrs, int length, uint8_t type, char *attrtxt, struct
     
     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;
@@ -1627,39 +1632,13 @@ void acclog(struct radmsg *msg, char *host) {
     }
 }
 
-void respondaccounting(struct request *rq) {
-    struct radmsg *msg;
-
-    msg = radmsg_init(RAD_Accounting_Response, rq->msg->id, rq->msg->auth);
-    if (msg) {
-       radmsg_free(rq->msg);
-       rq->msg = msg;
-       debug(DBG_DBG, "respondaccounting: responding to %s", rq->from->conf->host);
-       sendreply(rq);
-    } else     
-       debug(DBG_ERR, "respondaccounting: malloc failed");
-}
-
-void respondstatusserver(struct request *rq) {
-    struct radmsg *msg;
-
-    msg = radmsg_init(RAD_Access_Accept, rq->msg->id, rq->msg->auth);
-    if (msg) {
-       radmsg_free(rq->msg);
-       rq->msg = msg;
-       debug(DBG_DBG, "respondstatusserver: responding to %s", rq->from->conf->host);
-       sendreply(rq);
-    } else
-       debug(DBG_ERR, "respondstatusserver: malloc failed");
-}
-
-void respondreject(struct request *rq, char *message) {
+void respond(struct request *rq, uint8_t code, char *message) {
     struct radmsg *msg;
     struct tlv *attr;
 
-    msg = radmsg_init(RAD_Access_Reject, rq->msg->id, rq->msg->auth);
+    msg = radmsg_init(code, rq->msg->id, rq->msg->auth);
     if (!msg) {
-       debug(DBG_ERR, "respondreject: malloc failed");
+       debug(DBG_ERR, "respond: malloc failed");
        return;
     }
     if (message && *message) {
@@ -1667,14 +1646,15 @@ void respondreject(struct request *rq, char *message) {
        if (!attr || !radmsg_add(msg, attr)) {
            freetlv(attr);
            radmsg_free(msg);
-           debug(DBG_ERR, "respondreject: malloc failed");
+           debug(DBG_ERR, "respond: malloc failed");
            return;
        }
     }
 
     radmsg_free(rq->msg);
     rq->msg = msg;
-    debug(DBG_DBG, "respondreject: responding to %s", rq->from->conf->host);
+    debug(DBG_DBG, "respond: sending %s to %s", radmsgtype2string(msg->code), rq->from->conf->host);
+    rq->refcount++;
     sendreply(rq);
 }
 
@@ -1740,45 +1720,39 @@ struct request *newrequest() {
     return rq;
 }
 
-int addclientrq(struct request *rq, uint8_t id) {
+int addclientrq(struct request *rq) {
     struct request *r;
     struct timeval now;
-
-    pthread_mutex_lock(&rq->from->lock);
-    gettimeofday(&now, NULL);
-    r = rq->from->rqs[id];
+    
+    r = rq->from->rqs[rq->rqid];
     if (r) {
-       if (now.tv_sec - r->created.tv_sec < r->from->conf->dupinterval) {
-#if 0
-           later           
-           if (r->replybuf) {
-               debug(DBG_INFO, "radsrv: already sent reply to request with id %d from %s, resending", id, r->from->conf->host);
-               r->refcount++;
-               sendreply(r);
-           } else
-#endif         
-               debug(DBG_INFO, "radsrv: already got request with id %d from %s, ignoring", id, r->from->conf->host);           
-           pthread_mutex_unlock(&rq->from->lock);
-           return 0;
+       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[id] = rq;
-    pthread_mutex_unlock(&rq->from->lock);
+    rq->from->rqs[rq->rqid] = rq;
     return 1;
 }
 
 void rmclientrq(struct request *rq, uint8_t id) {
     struct request *r;
 
-    pthread_mutex_lock(&rq->from->lock);
     r = rq->from->rqs[id];
     if (r) {
        freerq(r);
        rq->from->rqs[id] = NULL;
     }
-    pthread_mutex_unlock(&rq->from->lock);
 }
 
 /* returns 0 if validation/authentication fails, else 1 */
@@ -1786,7 +1760,6 @@ 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;
@@ -1802,17 +1775,20 @@ int radsrv(struct request *rq) {
     }
     
     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");      
        goto exit;
     }
     
-    if (!addclientrq(rq, msg->id))
+    if (!addclientrq(rq))
        goto exit;
 
     if (msg->code == RAD_Status_Server) {
-       respondstatusserver(rq);
+       respond(rq, RAD_Access_Accept, NULL);
        goto exit;
     }
 
@@ -1825,7 +1801,7 @@ int radsrv(struct request *rq) {
     if (!attr) {
        if (msg->code == RAD_Accounting_Request) {
            acclog(msg, from->conf->host);
-           respondaccounting(rq);
+           respond(rq, RAD_Accounting_Response, NULL);
        } else
            debug(DBG_WARN, "radsrv: ignoring access request, no username attribute");
        goto exit;
@@ -1850,10 +1826,10 @@ int radsrv(struct request *rq) {
     if (!to) {
        if (realm->message && msg->code == RAD_Access_Request) {
            debug(DBG_INFO, "radsrv: sending reject to %s for %s", from->conf->host, userascii);
-           respondreject(rq, realm->message);
+           respond(rq, RAD_Access_Reject, realm->message);
        } else if (realm->accresp && msg->code == RAD_Accounting_Request) {
            acclog(msg, from->conf->host);
-           respondaccounting(rq);
+           respond(rq, RAD_Accounting_Response, NULL);
        }
        goto exit;
     }
@@ -1864,11 +1840,11 @@ int radsrv(struct request *rq) {
        goto exit;
     }
 
-    if (msg->code != RAD_Accounting_Request) {
-       if (!RAND_bytes(newauth, 16)) {
-           debug(DBG_WARN, "radsrv: failed to generate random auth");
-           goto rmclrqexit;
-       }
+    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;
     }
     
 #ifdef DEBUG
@@ -1878,21 +1854,17 @@ int radsrv(struct request *rq) {
     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, from->conf->secret, to->conf->secret, msg->auth, newauth))
+       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, from->conf->secret, to->conf->secret, msg->auth, newauth))
+       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 rmclrqexit;
     
@@ -1903,7 +1875,7 @@ int radsrv(struct request *rq) {
  rmclrqexit:
     rmclientrq(rq, msg->id);
  exit:
-    free(rq);
+    freerq(rq);
     free(userascii);
     return 1;
 }
@@ -1913,7 +1885,7 @@ void replyh(struct server *server, unsigned char *buf) {
     struct rqout *rqout;
     int sublen;
     unsigned char *subattrs;
-    uint8_t *username, *stationid;
+    uint8_t *username, *stationid, *replymsg;
     struct radmsg *msg = NULL;
     struct tlv *attr;
     struct list_node *node;
@@ -1988,18 +1960,31 @@ void replyh(struct server *server, unsigned char *buf) {
        username = radattr2ascii(radmsg_gettype(rqout->rq->msg, RAD_Attr_User_Name));
        if (username) {
            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)rqout->rq->origid;
-    memcpy(msg->auth, rqout->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);
@@ -2060,7 +2045,7 @@ struct request *createstatsrvrq() {
 /* code for removing state not finished */
 void *clientwr(void *arg) {
     struct server *server = (struct server *)arg;
-    struct rqout *rqout;
+    struct rqout *rqout = NULL;
     pthread_t clientrdth;
     int i, secs, dynconffail = 0;
     uint8_t rnd;
@@ -3541,6 +3526,7 @@ int main(int argc, char **argv) {
     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);