Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 14 Mar 2003 22:34:25 +0900 (JST)
From:      YAMAMOTO Shigeru <shigeru@iij.ad.jp>
To:        freebsd-current@FreeBSD.ORG
Subject:   IPv6 patch for libradius
Message-ID:  <20030314.223425.98350578.shigeru@iij.ad.jp>

next in thread | raw e-mail | index | archive | help
----Next_Part(Fri_Mar_14_22:34:25_2003_835)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit


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	<shigeru@iij.ad.jp>

----Next_Part(Fri_Mar_14_22:34:25_2003_835)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="libradius.diff"

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 */
 };
 

----Next_Part(Fri_Mar_14_22:34:25_2003_835)----

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20030314.223425.98350578.shigeru>