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>
