diff options
-rw-r--r-- | radsecproxy.c | 273 | ||||
-rw-r--r-- | radsecproxy.h | 1 |
2 files changed, 154 insertions, 120 deletions
diff --git a/radsecproxy.c b/radsecproxy.c index b6e634b..3514f95 100644 --- a/radsecproxy.c +++ b/radsecproxy.c @@ -912,36 +912,75 @@ unsigned char *radtlsget(SSL *ssl) { return rad; } -int clientradput(struct server *server, unsigned char *rad) { - int cnt; +int clientradputudp(struct server *server, unsigned char *rad) { size_t len; - unsigned long error; - struct timeval lastconnecttry; + struct sockaddr_storage sa; + struct sockaddr *sap; struct clsrvconf *conf = server->conf; + in_port_t *port = NULL; len = RADLEN(rad); - if (conf->type == 'U') { - if (sendto(server->sock, rad, len, 0, conf->addrinfo->ai_addr, conf->addrinfo->ai_addrlen) >= 0) { - debug(DBG_DBG, "clienradput: sent UDP of length %d to %s port %s", len, conf->host, conf->port); - return 1; - } - debug(DBG_WARN, "clientradput: send failed"); + + if (*rad == RAD_Accounting_Request) { + sap = (struct sockaddr *)&sa; + memcpy(sap, conf->addrinfo->ai_addr, conf->addrinfo->ai_addrlen); + } else + sap = conf->addrinfo->ai_addr; + + switch (sap->sa_family) { + case AF_INET: + port = &((struct sockaddr_in *)sap)->sin_port; + break; + case AF_INET6: + port = &((struct sockaddr_in6 *)sap)->sin6_port; + break; + default: return 0; } + if (*rad == RAD_Accounting_Request) + (*port)++; + + if (sendto(server->sock, rad, len, 0, sap, conf->addrinfo->ai_addrlen) >= 0) { + debug(DBG_DBG, "clienradputudp: sent UDP of length %d to %s port %d", len, conf->host, *port); + return 1; + } + + debug(DBG_WARN, "clientradputudp: send failed"); + return 0; +} + +int clientradputtls(struct server *server, unsigned char *rad) { + int cnt; + size_t len; + unsigned long error; + struct timeval lastconnecttry; + struct clsrvconf *conf = server->conf; + + len = RADLEN(rad); lastconnecttry = server->lastconnecttry; while ((cnt = SSL_write(server->ssl, rad, len)) <= 0) { while ((error = ERR_get_error())) - debug(DBG_ERR, "clientradput: TLS: %s", ERR_error_string(error, NULL)); - tlsconnect(server, &lastconnecttry, "clientradput"); + debug(DBG_ERR, "clientradputtls: TLS: %s", ERR_error_string(error, NULL)); + tlsconnect(server, &lastconnecttry, "clientradputtls"); lastconnecttry = server->lastconnecttry; } server->connectionok = 1; - debug(DBG_DBG, "clientradput: 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->host); return 1; } +int clientradput(struct server *server, unsigned char *rad) { + switch (server->conf->type) { + case 'U': + return clientradputudp(server, rad); + case 'T': + return clientradputtls(server, rad); + } + return 0; +} + int radsign(unsigned char *rad, unsigned char *sec) { static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static unsigned char first = 1; @@ -1375,16 +1414,16 @@ struct realm *id2realm(char *id, uint8_t len) { return NULL; } -int rqinqueue(struct server *to, struct client *from, uint8_t id) { - int i; +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 (i = 0; i < MAX_REQUESTS; i++) - if (to->requests[i].buf && !to->requests[i].received && to->requests[i].origid == id && to->requests[i].from == from) + 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 i < MAX_REQUESTS; + return rq < end; } int attrvalidate(unsigned char *attrs, int length) { @@ -1621,6 +1660,16 @@ int rewriteusername(struct request *rq, char *in) { return 1; } +const char *radmsgtype2string(uint8_t code) { + static const char *rad_msg_names[] = { + "", "Access-Request", "Access-Accept", "Access-Reject", + "Accounting-Request", "Accounting-Response", "", "", + "", "", "", "Access-Challenge", + "Status-Server", "Status-Client" + }; + return code < 14 && *rad_msg_names[code] ? rad_msg_names[code] : "Unknown"; +} + void acclog(unsigned char *attrs, int length, char *host) { unsigned char *attr; char username[256]; @@ -1690,11 +1739,11 @@ void respondreject(struct request *rq, char *message) { sendreply(rq->from, resp, rq->from->conf->type == 'U' ? &rq->fromsa : NULL); } -struct server *realm2server(struct realm *realm) { +struct server *chooseserver(struct list *srvconfs) { struct list_node *entry; struct server *server, *best = NULL, *first = NULL; - for (entry = list_first(realm->srvconfs); entry; entry = list_next(entry)) { + for (entry = list_first(srvconfs); entry; entry = list_next(entry)) { server = ((struct clsrvconf *)entry->data)->servers; if (!first) first = server; @@ -1729,8 +1778,7 @@ void radsrv(struct request *rq) { if (code != RAD_Access_Request && code != RAD_Status_Server && code != RAD_Accounting_Request) { debug(DBG_INFO, "radsrv: server currently accepts only access-requests, accounting-requests and status-server, ignoring"); - freerqdata(rq); - return; + goto exit; } len -= 20; @@ -1738,33 +1786,22 @@ void radsrv(struct request *rq) { if (!attrvalidate(attrs, len)) { debug(DBG_WARN, "radsrv: attribute validation failed, ignoring packet"); - freerqdata(rq); - return; + goto exit; } attr = attrget(attrs, len, RAD_Attr_Message_Authenticator); if (attr && (ATTRVALLEN(attr) != 16 || !checkmessageauth(rq->buf, ATTRVAL(attr), rq->from->conf->secret))) { debug(DBG_WARN, "radsrv: message authentication failed"); - freerqdata(rq); - return; + goto exit; } - if (code != RAD_Access_Request) { - switch (code) { - case RAD_Accounting_Request: - /* when forwarding accounting, also filter attributes, call removeattrs(rq) */ - acclog(attrs, len, rq->from->conf->host); - respondaccounting(rq); - break; - case RAD_Status_Server: - respondstatusserver(rq); - break; - } - freerqdata(rq); - return; + if (code == RAD_Status_Server) { + respondstatusserver(rq); + goto exit; } + + /* below: code == RAD_Access_Request || code == RAD_Accounting_Request */ - /* code == RAD_Access_Request */ if (rq->from->conf->rewrite) { dorewrite(rq->buf, rq->from->conf->rewrite); len = RADLEN(rq->buf) - 20; @@ -1772,9 +1809,12 @@ void radsrv(struct request *rq) { attr = attrget(attrs, len, RAD_Attr_User_Name); if (!attr) { - debug(DBG_WARN, "radsrv: ignoring request, no username attribute"); - freerqdata(rq); - return; + if (code == RAD_Accounting_Request) { + acclog(attrs, len, rq->from->conf->host); + respondaccounting(rq); + } else + debug(DBG_WARN, "radsrv: ignoring access request, no username attribute"); + goto exit; } memcpy(username, ATTRVAL(attr), ATTRVALLEN(attr)); username[ATTRVALLEN(attr)] = '\0'; @@ -1782,8 +1822,7 @@ void radsrv(struct request *rq) { if (rq->from->conf->rewriteattrregex) { if (!rewriteusername(rq, username)) { debug(DBG_WARN, "radsrv: username malloc failed, ignoring request"); - freerqdata(rq); - return; + goto exit; } len = RADLEN(rq->buf) - 20; auth = (uint8_t *)(rq->buf + 4); @@ -1791,37 +1830,34 @@ void radsrv(struct request *rq) { } if (rq->origusername) - debug(DBG_DBG, "Access Request with username: %s (originally %s)", username, rq->origusername); + debug(DBG_DBG, "%s with username: %s (originally %s)", radmsgtype2string(code), username, rq->origusername); else - debug(DBG_DBG, "Access Request with username: %s", username); - + debug(DBG_DBG, "%s with username: %s", radmsgtype2string(code), username); + realm = id2realm(username, strlen(username)); if (!realm) { debug(DBG_INFO, "radsrv: ignoring request, don't know where to send it"); - freerqdata(rq); - return; + goto exit; } - to = realm2server(realm); - if (to && rqinqueue(to, rq->from, id)) { - debug(DBG_INFO, "radsrv: already got request from host %s with id %d, ignoring", rq->from->conf->host, id); - freerqdata(rq); - return; - } - + to = chooseserver(code == RAD_Access_Request ? realm->srvconfs : realm->accsrvconfs); if (!to) { if (realm->message) { debug(DBG_INFO, "radsrv: sending reject to %s for %s", rq->from->conf->host, username); respondreject(rq, realm->message); } - freerqdata(rq); - return; + goto exit; } + if (rqinqueue(to, rq->from, id, code)) { + debug(DBG_INFO, "radsrv: already got %s from host %s with id %d, ignoring", + radmsgtype2string(code), rq->from->conf->host, id); + goto exit; + } + if (!RAND_bytes(newauth, 16)) { debug(DBG_WARN, "radsrv: failed to generate random auth"); - freerqdata(rq); - return; + goto exit; } #ifdef DEBUG @@ -1831,25 +1867,25 @@ void radsrv(struct request *rq) { attr = attrget(attrs, len, RAD_Attr_User_Password); if (attr) { debug(DBG_DBG, "radsrv: found userpwdattr with value length %d", ATTRVALLEN(attr)); - if (!pwdrecrypt(ATTRVAL(attr), ATTRVALLEN(attr), rq->from->conf->secret, to->conf->secret, auth, newauth)) { - freerqdata(rq); - return; - } + if (!pwdrecrypt(ATTRVAL(attr), ATTRVALLEN(attr), rq->from->conf->secret, to->conf->secret, auth, newauth)) + goto exit; } attr = attrget(attrs, len, RAD_Attr_Tunnel_Password); if (attr) { debug(DBG_DBG, "radsrv: found tunnelpwdattr with value length %d", ATTRVALLEN(attr)); - if (!pwdrecrypt(ATTRVAL(attr), ATTRVALLEN(attr), rq->from->conf->secret, to->conf->secret, auth, newauth)) { - freerqdata(rq); - return; - } + if (!pwdrecrypt(ATTRVAL(attr), ATTRVALLEN(attr), rq->from->conf->secret, to->conf->secret, auth, newauth)) + goto exit; } rq->origid = id; memcpy(rq->origauth, auth, 16); memcpy(auth, newauth, 16); sendrq(to, rq); + return; + + exit: + freerqdata(rq); } int replyh(struct server *server, unsigned char *buf) { @@ -1865,20 +1901,12 @@ int replyh(struct server *server, unsigned char *buf) { i = buf[1]; /* i is the id */ - switch (*buf) { - case RAD_Access_Accept: - debug(DBG_DBG, "got Access Accept with id %d", i); - break; - case RAD_Access_Reject: - debug(DBG_DBG, "got Access Reject with id %d", i); - break; - case RAD_Access_Challenge: - debug(DBG_DBG, "got Access Challenge with id %d", i); - break; - default: - debug(DBG_INFO, "replyh: discarding, only accept access accept, access reject and access challenge messages"); + if (*buf != RAD_Access_Accept && *buf != RAD_Access_Reject && *buf != RAD_Access_Challenge + && *buf != RAD_Accounting_Response) { + debug(DBG_INFO, "replyh: discarding message type %s, accepting only access accept, access reject, access challenge and accounting response messages", radmsgtype2string(*buf)); return 0; } + debug(DBG_DBG, "got %s message with id %d", radmsgtype2string(*buf), i); rq = server->requests + i; @@ -1971,24 +1999,16 @@ int replyh(struct server *server, unsigned char *buf) { return 0; } - if (*buf == RAD_Access_Accept || *buf == RAD_Access_Reject) { + if (*buf == RAD_Access_Accept || *buf == RAD_Access_Reject || *buf == RAD_Accounting_Response) { attr = attrget(rq->buf + 20, RADLEN(rq->buf) - 20, RAD_Attr_User_Name); - /* we know the attribute exists */ - memcpy(tmp, ATTRVAL(attr), ATTRVALLEN(attr)); - tmp[ATTRVALLEN(attr)] = '\0'; - switch (*buf) { - case RAD_Access_Accept: - if (rq->origusername) - debug(DBG_INFO, "Access Accept for %s (originally %s) from %s", tmp, rq->origusername, server->conf->host); - else - debug(DBG_INFO, "Access Accept for %s from %s", tmp, server->conf->host); - break; - case RAD_Access_Reject: + if (attr) { + memcpy(tmp, ATTRVAL(attr), ATTRVALLEN(attr)); + tmp[ATTRVALLEN(attr)] = '\0'; if (rq->origusername) - debug(DBG_INFO, "Access Reject for %s (originally %s) from %s", tmp, rq->origusername, server->conf->host); + debug(DBG_INFO, "%s for %s (originally %s) from %s", radmsgtype2string(*buf), tmp, + rq->origusername, server->conf->host); else - debug(DBG_INFO, "Access Reject for %s from %s", tmp, server->conf->host); - break; + debug(DBG_INFO, "%s for %s from %s", radmsgtype2string(*buf), tmp, server->conf->host); } } @@ -2646,13 +2666,40 @@ SSL_CTX *tlsgetctx(char *alt1, char *alt2) { return t->ctx; } -void addrealm(char *value, char **servers, char *message) { +struct list *addsrvconfs(char *value, char **names) { + struct list *conflist; int n; - struct realm *realm; - char *s, *regex = NULL; struct list_node *entry; struct clsrvconf *conf; + if (!names || !*names) + return NULL; + + conflist = list_create(); + if (!conflist) + debugx(1, DBG_ERR, "malloc failed"); + + for (n = 0; names[n]; n++) { + for (entry = list_first(srvconfs); entry; entry = list_next(entry)) { + conf = (struct clsrvconf *)entry->data; + if (!strcasecmp(names[n], conf->name)) + break; + } + if (!entry) + debugx(1, DBG_ERR, "addsrvconfs failed for realm %s, no server named %s", value, names[n]); + if (!list_push(conflist, conf)) + debugx(1, DBG_ERR, "malloc failed"); + debug(DBG_DBG, "addsrvconfs: added server %s for realm %s", conf->name, value); + } + + return conflist; +} + +void addrealm(char *value, char **servers, char **accservers, char *message) { + int n; + struct realm *realm; + char *s, *regex = NULL; + if (*value == '/') { /* regexp, remove optional trailing / if present */ if (value[strlen(value) - 1] == '/') @@ -2698,25 +2745,9 @@ void addrealm(char *value, char **servers, char *message) { debugx(1, DBG_ERR, "addrealm: failed to compile regular expression %s", regex ? regex : value + 1); if (regex) free(regex); - - if (servers && *servers) { - realm->srvconfs = list_create(); - if (!realm->srvconfs) - debugx(1, DBG_ERR, "malloc failed"); - for (n = 0; servers[n]; n++) { - for (entry = list_first(srvconfs); entry; entry = list_next(entry)) { - conf = (struct clsrvconf *)entry->data; - if (!strcasecmp(servers[n], conf->name)) - break; - } - if (!entry) - debugx(1, DBG_ERR, "addrealm failed, no server %s", servers[n]); - if (!list_push(realm->srvconfs, conf)) - debugx(1, DBG_ERR, "malloc failed"); - debug(DBG_DBG, "addrealm: added server %s for realm %s", conf->name, value); - } - } else - realm->srvconfs = NULL; + + realm->srvconfs = addsrvconfs(value, servers); + realm->accsrvconfs = addsrvconfs(value, accservers); if (!list_push(realms, realm)) debugx(1, DBG_ERR, "malloc failed"); @@ -3053,18 +3084,20 @@ void confserver_cb(struct gconffile **cf, char *block, char *opt, char *val) { } void confrealm_cb(struct gconffile **cf, char *block, char *opt, char *val) { - char **servers = NULL, *msg = NULL; + char **servers = NULL, **accservers = NULL, *msg = NULL; debug(DBG_DBG, "confrealm_cb called for %s", block); getgenericconfig(cf, block, "server", CONF_MSTR, &servers, + "accountingServer", CONF_MSTR, &accservers, "ReplyMessage", CONF_STR, &msg, NULL ); - addrealm(val, servers, msg); + addrealm(val, servers, accservers, msg); free(servers); + free(accservers); } void conftls_cb(struct gconffile **cf, char *block, char *opt, char *val) { diff --git a/radsecproxy.h b/radsecproxy.h index 737bc59..f60d3a3 100644 --- a/radsecproxy.h +++ b/radsecproxy.h @@ -120,6 +120,7 @@ struct realm { char *message; regex_t regex; struct list *srvconfs; + struct list *accsrvconfs; }; struct tls { |