diff options
-rw-r--r-- | list.c | 28 | ||||
-rw-r--r-- | list.h | 3 | ||||
-rw-r--r-- | radsecproxy.c | 179 | ||||
-rw-r--r-- | radsecproxy.h | 4 |
4 files changed, 164 insertions, 50 deletions
@@ -52,12 +52,40 @@ void *list_shift(struct list *list) { node = list->first; list->first = node->next; + if (!list->first) + list->last = NULL; data = node->data; free(node); return data; } +/* removes first entry with matching data pointer */ +void list_removedata(struct list *list, void *data) { + struct list_node *node, *t; + + if (!list->first) + return; + + node = list->first; + if (node->data == data) { + list->first = node->next; + if (!list->first) + list->last = NULL; + free(node); + return; + } + for (; node->next; node = node->next) + if (node->next->data == data) { + t = node->next; + node->next = node->next->next; + if (!node->next) /* we removed the last one */ + list->last = node; + free(t); + return; + } +} + /* returns first node */ struct list_node *list_first(struct list *list) { return list->first; @@ -19,6 +19,9 @@ int list_push(struct list *list, void *data); /* removes first entry from list and returns data */ void *list_shift(struct list *list); +/* removes first entry with matching data pointer */ +void list_removedata(struct list *list, void *data); + /* returns first node */ struct list_node *list_first(struct list *list); diff --git a/radsecproxy.c b/radsecproxy.c index 9df3786..7c27d82 100644 --- a/radsecproxy.c +++ b/radsecproxy.c @@ -132,16 +132,60 @@ static int verify_cb(int ok, X509_STORE_CTX *ctx) { int resolvepeer(struct clsrvconf *conf, int ai_flags) { struct addrinfo hints, *addrinfo; - + char *slash, *s; + int plen; + + slash = conf->host ? strchr(conf->host, '/') : NULL; + if (slash) { + s = slash + 1; + if (!*s) { + debug(DBG_WARN, "resolvepeer: prefix length must be specified after the / in %s", conf->host); + return 0; + } + for (; *s; s++) + if (*s < '0' || *s > '9') { + debug(DBG_WARN, "resolvepeer: %s in %s is not a valid prefix length", slash + 1, conf->host); + return 0; + } + plen = atoi(slash + 1); + if (plen < 0 || plen > 128) { + debug(DBG_WARN, "resolvepeer: %s in %s is not a valid prefix length", slash + 1, conf->host); + return 0; + } + *slash = '\0'; + } memset(&hints, 0, sizeof(hints)); hints.ai_socktype = (conf->type == 'T' ? SOCK_STREAM : SOCK_DGRAM); hints.ai_family = AF_UNSPEC; hints.ai_flags = ai_flags; + if (slash) + hints.ai_flags |= AI_NUMERICHOST; if (getaddrinfo(conf->host, conf->port, &hints, &addrinfo)) { debug(DBG_WARN, "resolvepeer: can't resolve %s port %s", conf->host, conf->port); return 0; } + if (slash) { + *slash = '/'; + switch (addrinfo->ai_family) { + case AF_INET: + if (plen > 32) { + debug(DBG_WARN, "resolvepeer: prefix length must be <= 32 in %s", conf->host); + freeaddrinfo(addrinfo); + return 0; + } + break; + case AF_INET6: + break; + default: + debug(DBG_WARN, "resolvepeer: prefix must be IPv4 or IPv6 in %s", conf->host); + freeaddrinfo(addrinfo); + return 0; + } + conf->prefixlen = plen; + } else + conf->prefixlen = 255; + if (conf->addrinfo) freeaddrinfo(conf->addrinfo); conf->addrinfo = addrinfo; @@ -187,7 +231,19 @@ int bindtoaddr(struct addrinfo *addrinfo) { return -1; } -/* returns the peer with matching address, or NULL */ +/* 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 }; + int r, l = len / 8; + if (l && memcmp(a1, a2, l)) + return 0; + r = len % 8; + if (!r) + return 1; + return (((uint8_t *)a1)[l] & mask[r]) == (((uint8_t *)a2)[l] & mask[r]); +} + +/* returns the config with matching address, or NULL */ /* if conf argument is not NULL, we only check that one */ struct clsrvconf *find_conf(char type, struct sockaddr *addr, struct list *confs, struct clsrvconf *conf) { struct sockaddr_in6 *sa6 = NULL; @@ -197,35 +253,55 @@ struct clsrvconf *find_conf(char type, struct sockaddr *addr, struct list *confs if (addr->sa_family == AF_INET6) { sa6 = (struct sockaddr_in6 *)addr; - if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) + if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) { a4 = (struct in_addr *)&sa6->sin6_addr.s6_addr[12]; + sa6 = NULL; + } } else a4 = &((struct sockaddr_in *)addr)->sin_addr; if (conf) { - if (conf->type == type) - if (!conf->host) /* for now this means match everything */ - return conf; - for (res = conf->addrinfo; res; res = res->ai_next) - if ((a4 && res->ai_family == AF_INET && - !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) || - (sa6 && res->ai_family == AF_INET6 && - !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16))) + if (conf->type == type) { + if (conf->prefixlen == 255) { + for (res = conf->addrinfo; res; res = res->ai_next) + if ((a4 && res->ai_family == AF_INET && + !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) || + (sa6 && res->ai_family == AF_INET6 && + !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16))) + return conf; + } else { + res = conf->addrinfo; + if (res && + ((a4 && res->ai_family == AF_INET && + prefixmatch(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, conf->prefixlen)) || + (sa6 && res->ai_family == AF_INET6 && + prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, conf->prefixlen)))) return conf; + } + } return NULL; } for (entry = list_first(confs); entry; entry = list_next(entry)) { conf = (struct clsrvconf *)entry->data; - if (conf->type == type) - if (!conf->host) /* for now this means match everything */ - return conf; - for (res = conf->addrinfo; res; res = res->ai_next) - if ((a4 && res->ai_family == AF_INET && - !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) || - (sa6 && res->ai_family == AF_INET6 && - !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16))) + if (conf->type == type) { + if (conf->prefixlen == 255) { + for (res = conf->addrinfo; res; res = res->ai_next) + if ((a4 && res->ai_family == AF_INET && + !memcmp(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4)) || + (sa6 && res->ai_family == AF_INET6 && + !memcmp(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 16))) + return conf; + } else { + res = conf->addrinfo; + if (res && + ((a4 && res->ai_family == AF_INET && + prefixmatch(a4, &((struct sockaddr_in *)res->ai_addr)->sin_addr, conf->prefixlen)) || + (sa6 && res->ai_family == AF_INET6 && + prefixmatch(&sa6->sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, conf->prefixlen)))) return conf; + } + } } return NULL; } @@ -244,19 +320,35 @@ struct replyq *newreplyq() { return replyq; } -void addclient(struct clsrvconf *conf) { - if (conf->clients) { - debug(DBG_ERR, "currently works with just one client per conf"); - return; +struct client *addclient(struct clsrvconf *conf) { + struct client *new = malloc(sizeof(struct client)); + + if (!new) { + debug(DBG_ERR, "malloc failed"); + return NULL; } - conf->clients = malloc(sizeof(struct client)); if (!conf->clients) { - debug(DBG_ERR, "malloc failed"); - return; + conf->clients = list_create(); + if (!conf->clients) { + debug(DBG_ERR, "malloc failed"); + return NULL; + } } - memset(conf->clients, 0, sizeof(struct client)); - conf->clients->conf = conf; - conf->clients->replyq = conf->type == 'T' ? newreplyq() : udp_server_replyq; + + memset(new, 0, sizeof(struct client)); + new->conf = conf; + new->replyq = conf->type == 'T' ? newreplyq() : udp_server_replyq; + + list_push(conf->clients, new); + return new; +} + +void removeclient(struct client *client) { + if (!client || !client->conf->clients) + return; + + list_removedata(client->conf->clients, client); + free(client); } void addserver(struct clsrvconf *conf) { @@ -340,12 +432,11 @@ unsigned char *radudpget(int s, struct client **client, struct server **server, if (client && !*client) { if (!p->clients) - addclient(p); - if (!p->clients) { + *client = addclient(p); + if (!*client) { free(rad); continue; } - *client = p->clients; } else if (server && !*server) *server = p->servers; @@ -1725,7 +1816,7 @@ void *tlsserverrd(void *arg) { shutdown(s, SHUT_RDWR); close(s); debug(DBG_DBG, "tlsserverrd thread for %s exiting", client->conf->host); - client->ssl = NULL; + removeclient(client); pthread_exit(NULL); } @@ -1760,13 +1851,10 @@ int tlslistener() { continue; } - if (!conf->clients) - addclient(conf); - client = conf->clients; + client = addclient(conf); - if (!client || client->ssl) { - if (client) - debug(DBG_WARN, "Ignoring incoming TLS connection, already have one from this client"); + if (!client) { + debug(DBG_WARN, "Failed to create new client instance"); shutdown(snew, SHUT_RDWR); close(snew); continue; @@ -1776,9 +1864,9 @@ int tlslistener() { if (pthread_create(&tlsserverth, NULL, tlsserverrd, (void *)client)) { debug(DBG_ERR, "tlslistener: pthread_create failed"); SSL_free(client->ssl); + removeclient(client); shutdown(snew, SHUT_RDWR); close(snew); - client->ssl = NULL; continue; } pthread_detach(tlsserverth); @@ -2235,11 +2323,6 @@ void confclient_cb(FILE *f, char *block, char *opt, char *val) { if (!conf->host) conf->host = stringcopy(val, 0); - if (!strcmp(conf->host, "*")) { - free(conf->host); - conf->host = NULL; - } - if (type && !strcasecmp(type, "udp")) { conf->type = 'U'; client_udp_count++; @@ -2259,10 +2342,8 @@ void confclient_cb(FILE *f, char *block, char *opt, char *val) { if (matchcertattr) free(matchcertattr); - if (conf->host) { - if (!resolvepeer(conf, 0)) - debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host, conf->port); - } + if (!resolvepeer(conf, 0)) + debugx(1, DBG_ERR, "failed to resolve host %s port %s, exiting", conf->host, conf->port); if (!conf->secret) { if (conf->type == 'U') diff --git a/radsecproxy.h b/radsecproxy.h index 2516074..5342363 100644 --- a/radsecproxy.h +++ b/radsecproxy.h @@ -82,7 +82,8 @@ struct clsrvconf { uint8_t statusserver; SSL_CTX *ssl_ctx; struct addrinfo *addrinfo; - struct client *clients; + uint8_t prefixlen; + struct list *clients; struct server *servers; }; @@ -90,6 +91,7 @@ struct client { struct clsrvconf *conf; SSL *ssl; struct replyq *replyq; + struct client *next; }; struct server { |