Hi all, I make a patch to support IPv6 for libradius.
This is a quick hack. Unfortunatly, I can't test it over IPv6 environment, only IPv4 environment. Because, I don't have a RADIUS which supports IPv6 access. This patch extents radius.conf(5) format. - specify 10 or more RADISU (original: up to 10) - specify IPv6 address (original: IPv4 only) ex.) auth [2003:3:14::1]:1645 "I can't see you" 5 4 auth [2003:3:14::2] $X*#..38947ax-+= auth localhost "test teset" Please test it if you take interest in this patch. Thanks, ------- YAMAMOTO Shigeru <[EMAIL PROTECTED]>
Index: radlib.c =================================================================== RCS file: /share/cvsup/FreeBSD/current/usr/src/lib/libradius/radlib.c,v retrieving revision 1.10 diff -u -r1.10 radlib.c --- radlib.c 12 Jun 2002 00:21:07 -0000 1.10 +++ radlib.c 10 Mar 2003 03:14:40 -0000 @@ -48,10 +48,10 @@ static void clear_password(struct rad_handle *); static void generr(struct rad_handle *, const char *, ...) __printflike(2, 3); -static void insert_scrambled_password(struct rad_handle *, int); -static void insert_request_authenticator(struct rad_handle *, int); -static int is_valid_response(struct rad_handle *, int, - const struct sockaddr_in *); +static void insert_scrambled_password(struct rad_handle *, struct rad_server*); +static void insert_request_authenticator(struct rad_handle *, struct rad_server*); +static int is_valid_response(struct rad_handle *, struct rad_server*, + const struct sockaddr *); static int put_password_attr(struct rad_handle *, int, const void *, size_t); static int put_raw_attr(struct rad_handle *, int, @@ -79,15 +79,13 @@ } static void -insert_scrambled_password(struct rad_handle *h, int srv) +insert_scrambled_password(struct rad_handle* h, struct rad_server* server) { MD5_CTX ctx; unsigned char md5[16]; - const struct rad_server *srvp; int padded_len; int pos; - srvp = &h->servers[srv]; padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf; memcpy(md5, &h->request[POS_AUTH], LEN_AUTH); @@ -96,7 +94,7 @@ /* Calculate the new scrambler */ MD5Init(&ctx); - MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); + MD5Update(&ctx, server->secret, strlen(server->secret)); MD5Update(&ctx, md5, 16); MD5Final(md5, &ctx); @@ -113,19 +111,16 @@ } static void -insert_request_authenticator(struct rad_handle *h, int srv) +insert_request_authenticator(struct rad_handle* h, struct rad_server* server) { MD5_CTX ctx; - const struct rad_server *srvp; - - srvp = &h->servers[srv]; /* Create the request authenticator */ MD5Init(&ctx); MD5Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE); MD5Update(&ctx, memset(&h->request[POS_AUTH], 0, LEN_AUTH), LEN_AUTH); MD5Update(&ctx, &h->request[POS_ATTRS], h->req_len - POS_ATTRS); - MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); + MD5Update(&ctx, server->secret, strlen(server->secret)); MD5Final(&h->request[POS_AUTH], &ctx); } @@ -134,40 +129,48 @@ * specified server. */ static int -is_valid_response(struct rad_handle *h, int srv, - const struct sockaddr_in *from) +is_valid_response(struct rad_handle* h, struct rad_server* server, const struct sockaddr* from) { - MD5_CTX ctx; - unsigned char md5[16]; - const struct rad_server *srvp; - int len; - - srvp = &h->servers[srv]; + int valid = 0; /* Check the source address */ - if (from->sin_family != srvp->addr.sin_family || - from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr || - from->sin_port != srvp->addr.sin_port) - return 0; - - /* Check the message length */ - if (h->resp_len < POS_ATTRS) - return 0; - len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1]; - if (len > h->resp_len) - return 0; - - /* Check the response authenticator */ - MD5Init(&ctx); - MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - POS_CODE); - MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH); - MD5Update(&ctx, &h->response[POS_ATTRS], len - POS_ATTRS); - MD5Update(&ctx, srvp->secret, strlen(srvp->secret)); - MD5Final(md5, &ctx); - if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0) - return 0; + if (from->sa_family == server->addr.ss_family && memcmp(from, &(server->addr), from->sa_len) == 0) { + /* Check the message length */ + if (h->resp_len < POS_ATTRS) { + valid = 0; + } + else { + int len; + + len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1]; + if (len > h->resp_len) { + valid = 0; + } + else { + MD5_CTX ctx; + unsigned char md5[16]; + + /* Check the response authenticator */ + MD5Init(&ctx); + MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - POS_CODE); + MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH); + MD5Update(&ctx, &h->response[POS_ATTRS], len - POS_ATTRS); + MD5Update(&ctx, server->secret, strlen(server->secret)); + MD5Final(md5, &ctx); + if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0) { + valid = 0; + } + else { + valid = 1; + } + } + } + } + else { + valid = 0; + } - return 1; + return(valid); } static int @@ -222,65 +225,83 @@ rad_add_server(struct rad_handle *h, const char *host, int port, const char *secret, int timeout, int tries) { - struct rad_server *srvp; + int error = 0; + struct addrinfo hints; + struct addrinfo* res0; + char sbuf[NI_MAXSERV]; - if (h->num_servers >= MAXSERVERS) { - generr(h, "Too many RADIUS servers specified"); - return -1; + if (port != 0) { + snprintf(sbuf, sizeof(sbuf), "%d", port); + } + else if (h->type == RADIUS_AUTH) { + snprintf(sbuf, sizeof(sbuf), "radius"); + } + else { + snprintf(sbuf, sizeof(sbuf), "radacct"); } - srvp = &h->servers[h->num_servers]; - - memset(&srvp->addr, 0, sizeof srvp->addr); - srvp->addr.sin_len = sizeof srvp->addr; - srvp->addr.sin_family = AF_INET; - if (!inet_aton(host, &srvp->addr.sin_addr)) { - struct hostent *hent; - if ((hent = gethostbyname(host)) == NULL) { - generr(h, "%s: host not found", host); - return -1; - } - memcpy(&srvp->addr.sin_addr, hent->h_addr, - sizeof srvp->addr.sin_addr); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + error = getaddrinfo(host, sbuf, &hints, &res0); + if (error) { + generr(h, "%s:%d: %s", host, port, gai_strerror(error)); } - if (port != 0) - srvp->addr.sin_port = htons(port); else { - struct servent *sent; + struct addrinfo* res; - if (h->type == RADIUS_AUTH) - srvp->addr.sin_port = - (sent = getservbyname("radius", "udp")) != NULL ? - sent->s_port : htons(RADIUS_PORT); - else - srvp->addr.sin_port = - (sent = getservbyname("radacct", "udp")) != NULL ? - sent->s_port : htons(RADACCT_PORT); - } - if ((srvp->secret = strdup(secret)) == NULL) { - generr(h, "Out of memory"); - return -1; + for (res = res0; res != (struct addrinfo*)0; res = res->ai_next) { + struct rad_server* server; + + server = (struct rad_server*)calloc(1, sizeof(*server)); + if (!server) { + error = errno; + generr(h, "%s", strerror(error)); + } + else { + server->secret = strdup(secret); + error = errno; + if (server->secret == NULL) { + generr(h, "%s", strerror(error)); + } + else { + memcpy(&(server->addr), res->ai_addr, res->ai_addrlen); + server->timeout = timeout; + server->max_tries = tries; + server->num_tries = 0; + + TAILQ_INSERT_TAIL(&(h->servers), server, link); + error = 0; + } + } + } + + freeaddrinfo(res0); } - srvp->timeout = timeout; - srvp->max_tries = tries; - srvp->num_tries = 0; - h->num_servers++; - return 0; + + return(error); } void -rad_close(struct rad_handle *h) +rad_close(struct rad_handle* h) { - int srv; - - if (h->fd != -1) + if (h->fd >= 0) { close(h->fd); - for (srv = 0; srv < h->num_servers; srv++) { - memset(h->servers[srv].secret, 0, - strlen(h->servers[srv].secret)); - free(h->servers[srv].secret); + h->fd = -1; + } + + while (!TAILQ_EMPTY(&(h->servers))) { + struct rad_server* server; + + server = TAILQ_FIRST(&(h->servers)); + TAILQ_REMOVE(&(h->servers), server, link); + free(server->secret); + free(server); } + h->server = (struct rad_server*)0; + clear_password(h); + free(h); } @@ -379,14 +400,27 @@ continue; /* Parse and validate the fields. */ - res = host; - host = strsep(&res, ":"); - port_str = strsep(&res, ":"); + res = strchr(fields[1], '['); + if (res != NULL) { + /* IPv6: + * [aaaa:bbbb:cccc:dddd:eeee:ffff:gggg:hhhh] + * [aaaa:bbbb:cccc:dddd:eeee:ffff:gggg:hhhh]:port + */ + res ++; + host = strsep(&res, "]"); + strsep(&res, ":"); /* dummy */ + port_str = strsep(&res, ":"); + } + else { + res = host; + host = strsep(&res, ":"); + port_str = strsep(&res, ":"); + } if (port_str != NULL) { port = strtoul(port_str, &end, 10); if (*end != '\0') { generr(h, "%s:%d: invalid port", path, - linenum); + linenum); retval = -1; break; } @@ -427,6 +461,43 @@ return retval; } +static +int +rad_open_socket(struct rad_handle* h) { + int error = 0; + + if (h->server) { + /* Make sure we have a socket to use */ + if (h->fd >= 0) { + close(h->fd); + h->fd = -1; + } + + if ((h->fd = socket(h->server->addr.ss_family, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + error = errno; + generr(h, "Cannot create socket: %s", strerror(errno)); + } + else { + struct sockaddr_storage ss; + + memset(&ss, 0, sizeof ss); + ss.ss_len = h->server->addr.ss_len; + ss.ss_family = h->server->addr.ss_family; + if (bind(h->fd, (const struct sockaddr*)&ss, ss.ss_len) == -1) { + error = errno; + generr(h, "bind: %s", strerror(errno)); + close(h->fd); + h->fd = -1; + } + } + } + else { + error = EADDRNOTAVAIL; + } + + return(error); +} + /* * rad_init_send_request() must have previously been called. * Returns: @@ -442,7 +513,7 @@ int n; if (selected) { - struct sockaddr_in from; + struct sockaddr_storage from; int fromlen; fromlen = sizeof from; @@ -452,7 +523,7 @@ generr(h, "recvfrom: %s", strerror(errno)); return -1; } - if (is_valid_response(h, h->srv, &from)) { + if (is_valid_response(h, h->server, (const struct sockaddr*)&from)) { h->resp_len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1]; h->resp_pos = POS_ATTRS; @@ -470,22 +541,54 @@ * tries left. There is guaranteed to be one, or we * would have exited this loop by now. */ - while (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries) - if (++h->srv >= h->num_servers) - h->srv = 0; + if (!TAILQ_EMPTY(&(h->servers))) { + struct rad_server* server; + struct rad_server* candidate = (struct rad_server*)0; + + server = h->server; + do { + if (server->num_tries < server->max_tries) { + /* found a candidate */ + candidate = server; + break; + } + + if (server == TAILQ_LAST(&(h->servers), list_head_rad_server)) { + server = TAILQ_FIRST(&(h->servers)); + } + else { + server = TAILQ_NEXT(server, link); + } + } while (server != h->server); + + if (candidate) { + h->server = candidate; + if (rad_open_socket(h) != 0) { + return(-1); + } + } + else { + generr(h, "no server selected"); + return -1; + } + } + else { + generr(h, "no servers"); + return -1; + } if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) /* Insert the request authenticator into the request */ - insert_request_authenticator(h, h->srv); + insert_request_authenticator(h, h->server); else /* Insert the scrambled password into the request */ if (h->pass_pos != 0) - insert_scrambled_password(h, h->srv); + insert_scrambled_password(h, h->server); /* Send the request */ n = sendto(h->fd, h->request, h->req_len, 0, - (const struct sockaddr *)&h->servers[h->srv].addr, - sizeof h->servers[h->srv].addr); + (const struct sockaddr *)&h->server->addr, + h->server->addr.ss_len); if (n != h->req_len) { if (n == -1) generr(h, "sendto: %s", strerror(errno)); @@ -495,8 +598,8 @@ } h->try++; - h->servers[h->srv].num_tries++; - tv->tv_sec = h->servers[h->srv].timeout; + h->server->num_tries++; + tv->tv_sec = h->server->timeout; tv->tv_usec = 0; *fd = h->fd; @@ -585,30 +688,6 @@ int rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv) { - int srv; - - /* Make sure we have a socket to use */ - if (h->fd == -1) { - struct sockaddr_in sin; - - if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { - generr(h, "Cannot create socket: %s", strerror(errno)); - return -1; - } - memset(&sin, 0, sizeof sin); - sin.sin_len = sizeof sin; - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = INADDR_ANY; - sin.sin_port = htons(0); - if (bind(h->fd, (const struct sockaddr *)&sin, - sizeof sin) == -1) { - generr(h, "bind: %s", strerror(errno)); - close(h->fd); - h->fd = -1; - return -1; - } - } - if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) { /* Make sure no password given */ if (h->pass_pos || h->chap_pass) { @@ -636,16 +715,26 @@ * counter for each server. */ h->total_tries = 0; - for (srv = 0; srv < h->num_servers; srv++) { - h->total_tries += h->servers[srv].max_tries; - h->servers[srv].num_tries = 0; + h->server = (struct rad_server*)0; + if (!TAILQ_EMPTY(&(h->servers))) { + struct rad_server* server; + + TAILQ_FOREACH(server, &(h->servers), link) { + h->total_tries += server->max_tries; + server->num_tries = 0; + } + + h->server = TAILQ_FIRST(&(h->servers)); + if (rad_open_socket(h) != 0) { + return -1; + } } if (h->total_tries == 0) { generr(h, "No RADIUS servers specified"); return -1; } - h->try = h->srv = 0; + h->try = 0; return rad_continue_send_request(h, 0, fd, tv); } @@ -660,11 +749,12 @@ { struct rad_handle *h; - h = (struct rad_handle *)malloc(sizeof(struct rad_handle)); + h = (struct rad_handle *)calloc(1, sizeof(struct rad_handle)); if (h != NULL) { srandomdev(); h->fd = -1; - h->num_servers = 0; + TAILQ_INIT(&(h->servers)); + h->server = (struct rad_server*)0; h->ident = random(); h->errmsg[0] = '\0'; memset(h->pass, 0, sizeof h->pass); @@ -943,5 +1033,5 @@ const char * rad_server_secret(struct rad_handle *h) { - return (h->servers[h->srv].secret); + return (h->server->secret); } Index: radlib_private.h =================================================================== RCS file: /share/cvsup/FreeBSD/current/usr/src/lib/libradius/radlib_private.h,v retrieving revision 1.5 diff -u -r1.5 radlib_private.h --- radlib_private.h 7 May 2002 10:47:18 -0000 1.5 +++ radlib_private.h 10 Mar 2003 03:14:40 -0000 @@ -31,6 +31,7 @@ #include <sys/types.h> #include <netinet/in.h> +#include <sys/queue.h> #include "radlib.h" #include "radlib_vs.h" @@ -62,17 +63,20 @@ #define POS_ATTRS 20 /* Start of attributes */ struct rad_server { - struct sockaddr_in addr; /* Address of server */ + struct sockaddr_storage addr; /* Address of server */ char *secret; /* Shared secret */ int timeout; /* Timeout in seconds */ int max_tries; /* Number of tries before giving up */ int num_tries; /* Number of tries so far */ + + TAILQ_ENTRY(rad_server) link; }; +TAILQ_HEAD(list_head_rad_server, rad_server); struct rad_handle { - int fd; /* Socket file descriptor */ - struct rad_server servers[MAXSERVERS]; /* Servers to contact */ - int num_servers; /* Number of valid server entries */ + int fd; /* Socket file descriptor */ + struct list_head_rad_server servers; /* Servers to contact */ + struct rad_server* server; /* Server we did last */ int ident; /* Current identifier value */ char errmsg[ERRSIZE]; /* Most recent error message */ unsigned char request[MSGSIZE]; /* Request to send */ @@ -86,7 +90,6 @@ int resp_pos; /* Current position scanning attrs */ int total_tries; /* How many requests we'll send */ int try; /* How many requests we've sent */ - int srv; /* Server number we did last */ int type; /* Handle type */ };