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 { | 
