aboutsummaryrefslogtreecommitdiff
path: root/lib/libradius
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libradius')
-rw-r--r--lib/libradius/Makefile1
-rw-r--r--lib/libradius/libradius.320
-rw-r--r--lib/libradius/radius.conf.516
-rw-r--r--lib/libradius/radlib.c204
-rw-r--r--lib/libradius/radlib.h5
-rw-r--r--lib/libradius/radlib_private.h9
-rw-r--r--lib/libradius/radlib_vs.h1
7 files changed, 213 insertions, 43 deletions
diff --git a/lib/libradius/Makefile b/lib/libradius/Makefile
index d71d8f2022ed..cb68eaa32b1a 100644
--- a/lib/libradius/Makefile
+++ b/lib/libradius/Makefile
@@ -35,6 +35,7 @@ MAN= libradius.3 radius.conf.5
MLINKS+=libradius.3 rad_acct_open.3 \
libradius.3 rad_add_server.3 \
+ libradius.3 rad_add_server_ex.3 \
libradius.3 rad_auth_open.3 \
libradius.3 rad_bind_to.3 \
libradius.3 rad_close.3 \
diff --git a/lib/libradius/libradius.3 b/lib/libradius/libradius.3
index ae0ee2406032..f3ff70058d44 100644
--- a/lib/libradius/libradius.3
+++ b/lib/libradius/libradius.3
@@ -37,6 +37,8 @@
.Fn rad_acct_open "void"
.Ft int
.Fn rad_add_server "struct rad_handle *h" "const char *host" "int port" "const char *secret" "int timeout" "int max_tries"
+.Ft int
+.Fn rad_add_server_ex "struct rad_handle *h" "const char *host" "int port" "const char *secret" "int timeout" "int max_tries" "int dead_time" "struct in_addr *bindto"
.Ft "struct rad_handle *"
.Fn rad_auth_open "void"
.Ft void
@@ -153,7 +155,12 @@ is used.
returns 0 on success, or \-1 if an error occurs.
.Pp
The library can also be configured programmatically by calls to
-.Fn rad_add_server .
+.Fn rad_add_server
+or
+.Fn rad_add_server_ex .
+.Fn rad_add_server
+is a backward compatible function, implemented via
+.Fn rad_add_server_ex .
The
.Fa host
parameter specifies the server host, either as a fully qualified
@@ -188,11 +195,20 @@ The maximum number of repeated
requests to make before giving up is passed into the
.Fa max_tries
parameter.
+Time interval in seconds when the server will not be requested
+if it is marked as dead (did not answer on the last try) set with
+.Fa dead_time
+parameter.
+.Fa bindto
+parameter is an IP address on the multihomed host that is used as
+a source address for all requests.
.Fn rad_add_server
returns 0 on success, or \-1 if an error occurs.
.Pp
.Fn rad_add_server
-may be called multiple times, and it may be used together with
+or
+.Fn rad_add_server_ex
+may be called multiple times, and they may be used together with
.Fn rad_config .
At most 10 servers may be specified.
When multiple servers are given, they are tried in round-robin
diff --git a/lib/libradius/radius.conf.5 b/lib/libradius/radius.conf.5
index 6ac84e07cc3f..6f89c3ae2df3 100644
--- a/lib/libradius/radius.conf.5
+++ b/lib/libradius/radius.conf.5
@@ -44,7 +44,7 @@ Leading
white space is ignored, as are empty lines and lines containing
only comments.
.Pp
-A RADIUS server is described by three to five fields on a line:
+A RADIUS server is described by three to seven fields on a line:
.Pp
.Bl -item -offset indent -compact
.It
@@ -57,6 +57,10 @@ Shared secret
Timeout
.It
Retries
+.It
+Dead time
+.It
+Bind address
.El
.Pp
The fields are separated by white space.
@@ -139,6 +143,13 @@ If omitted, it defaults to 3 attempts.
Note,
this is the total number of attempts and not the number of retries.
.Pp
+The sixth field contains a decimal integer specifying a time interval
+in seconds when the server will not requested if it was inaccessible
+on the last try. 0 means ask always.
+.Pp
+The seventh field contains an IP address on multihomed host. All
+requests will be binded to this IP.
+.Pp
Up to 10 RADIUS servers may be specified for each service type.
The servers are tried in
round-robin fashion, until a valid response is received or the
@@ -161,6 +172,9 @@ acct radius1.domain.com OurLittleSecret
# timeout and maximum tries:
auth auth.domain.com:1645 "I can't see you" 5 4
+# As above but set dead time and bind address
+auth auth.domain.com:1645 "I can't see you" 5 4 60 192.168.1.8
+
# A server specified by its IP address:
auth 192.168.27.81 $X*#..38947ax-+=
.Ed
diff --git a/lib/libradius/radlib.c b/lib/libradius/radlib.c
index 46a9b58c9e2f..55c6f5cb04a1 100644
--- a/lib/libradius/radlib.c
+++ b/lib/libradius/radlib.c
@@ -43,6 +43,8 @@ __FBSDID("$FreeBSD$");
#include <md5.h>
#endif
+#define MAX_FIELDS 7
+
/* We need the MPPE_KEY_LEN define */
#include <netgraph/ng_mppc.h>
@@ -379,6 +381,18 @@ int
rad_add_server(struct rad_handle *h, const char *host, int port,
const char *secret, int timeout, int tries)
{
+ struct in_addr bindto;
+ bindto.s_addr = INADDR_ANY;
+
+ return rad_add_server_ex(h, host, port, secret, timeout, tries,
+ DEAD_TIME, &bindto);
+}
+
+int
+rad_add_server_ex(struct rad_handle *h, const char *host, int port,
+ const char *secret, int timeout, int tries, int dead_time,
+ struct in_addr *bindto)
+{
struct rad_server *srvp;
if (h->num_servers >= MAXSERVERS) {
@@ -421,6 +435,10 @@ rad_add_server(struct rad_handle *h, const char *host, int port,
srvp->timeout = timeout;
srvp->max_tries = tries;
srvp->num_tries = 0;
+ srvp->is_dead = 0;
+ srvp->dead_time = dead_time;
+ srvp->next_probe = 0;
+ srvp->bindto = bindto->s_addr;
h->num_servers++;
return 0;
}
@@ -441,6 +459,13 @@ rad_close(struct rad_handle *h)
free(h);
}
+void
+rad_bind_to(struct rad_handle *h, in_addr_t addr)
+{
+
+ h->bindto = addr;
+}
+
int
rad_config(struct rad_handle *h, const char *path)
{
@@ -459,7 +484,7 @@ rad_config(struct rad_handle *h, const char *path)
linenum = 0;
while (fgets(buf, sizeof buf, fp) != NULL) {
int len;
- char *fields[5];
+ char *fields[MAX_FIELDS];
int nfields;
char msg[ERRSIZE];
char *type;
@@ -468,11 +493,15 @@ rad_config(struct rad_handle *h, const char *path)
char *secret;
char *timeout_str;
char *maxtries_str;
+ char *dead_time_str;
+ char *bindto_str;
char *end;
char *wanttype;
unsigned long timeout;
unsigned long maxtries;
+ unsigned long dead_time;
int port;
+ struct in_addr bindto;
int i;
linenum++;
@@ -491,7 +520,7 @@ rad_config(struct rad_handle *h, const char *path)
buf[len - 1] = '\0';
/* Extract the fields from the line. */
- nfields = split(buf, fields, 5, msg, sizeof msg);
+ nfields = split(buf, fields, MAX_FIELDS, msg, sizeof msg);
if (nfields == -1) {
generr(h, "%s:%d: %s", path, linenum, msg);
retval = -1;
@@ -507,7 +536,7 @@ rad_config(struct rad_handle *h, const char *path)
*/
if (strcmp(fields[0], "auth") != 0 &&
strcmp(fields[0], "acct") != 0) {
- if (nfields >= 5) {
+ if (nfields >= MAX_FIELDS) {
generr(h, "%s:%d: invalid service type", path,
linenum);
retval = -1;
@@ -529,6 +558,8 @@ rad_config(struct rad_handle *h, const char *path)
secret = fields[2];
timeout_str = fields[3];
maxtries_str = fields[4];
+ dead_time_str = fields[5];
+ bindto_str = fields[6];
/* Ignore the line if it is for the wrong service type. */
wanttype = h->type == RADIUS_AUTH ? "auth" : "acct";
@@ -570,8 +601,30 @@ rad_config(struct rad_handle *h, const char *path)
} else
maxtries = MAXTRIES;
- if (rad_add_server(h, host, port, secret, timeout, maxtries) ==
- -1) {
+ if (dead_time_str != NULL) {
+ dead_time = strtoul(dead_time_str, &end, 10);
+ if (*end != '\0') {
+ generr(h, "%s:%d: invalid dead_time", path,
+ linenum);
+ retval = -1;
+ break;
+ }
+ } else
+ dead_time = DEAD_TIME;
+
+ if (bindto_str != NULL) {
+ bindto.s_addr = inet_addr(bindto_str);
+ if (bindto.s_addr == INADDR_NONE) {
+ generr(h, "%s:%d: invalid bindto", path,
+ linenum);
+ retval = -1;
+ break;
+ }
+ } else
+ bindto.s_addr = INADDR_ANY;
+
+ if (rad_add_server_ex(h, host, port, secret, timeout, maxtries,
+ dead_time, &bindto) == -1) {
strcpy(msg, h->errmsg);
generr(h, "%s:%d: %s", path, linenum, msg);
retval = -1;
@@ -596,7 +649,9 @@ int
rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
struct timeval *tv)
{
- int n;
+ int n, cur_srv;
+ time_t now;
+ struct sockaddr_in sin;
if (h->type == RADIUS_SERVER) {
generr(h, "denied function call");
@@ -621,19 +676,61 @@ rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
}
}
- if (h->try == h->total_tries) {
- generr(h, "No valid RADIUS responses received");
- return -1;
- }
-
/*
* Scan round-robin to the next server that has some
* 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;
+ cur_srv = h->srv;
+ now = time(NULL);
+ if (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries) {
+ /* Set next probe time for this server */
+ if (h->servers[h->srv].dead_time) {
+ h->servers[h->srv].is_dead = 1;
+ h->servers[h->srv].next_probe = now +
+ h->servers[h->srv].dead_time;
+ }
+ do {
+ h->srv++;
+ if (h->srv >= h->num_servers)
+ h->srv = 0;
+ if (h->servers[h->srv].is_dead == 0)
+ break;
+ if (h->servers[h->srv].dead_time &&
+ h->servers[h->srv].next_probe <= now) {
+ h->servers[h->srv].is_dead = 0;
+ h->servers[h->srv].num_tries = 0;
+ break;
+ }
+ } while (h->srv != cur_srv);
+
+ if (h->srv == cur_srv) {
+ generr(h, "No valid RADIUS responses received");
+ return (-1);
+ }
+ }
+
+ /* Rebind */
+ if (h->bindto != h->servers[h->srv].bindto) {
+ h->bindto = h->servers[h->srv].bindto;
+ close(h->fd);
+ 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 = h->bindto;
+ sin.sin_port = 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->out[POS_CODE] == RAD_ACCESS_REQUEST) {
/* Insert the scrambled password into the request */
@@ -641,9 +738,11 @@ rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
insert_scrambled_password(h, h->srv);
}
insert_message_authenticator(h, 0);
+
if (h->out[POS_CODE] != RAD_ACCESS_REQUEST) {
/* Insert the request authenticator into the request */
- insert_request_authenticator(h, h->srv);
+ memset(&h->out[POS_AUTH], 0, LEN_AUTH);
+ insert_request_authenticator(h, 0);
}
/* Send the request */
@@ -654,7 +753,6 @@ rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
tv->tv_sec = 1; /* Do not wait full timeout if send failed. */
else
tv->tv_sec = h->servers[h->srv].timeout;
- h->try++;
h->servers[h->srv].num_tries++;
tv->tv_usec = 0;
*fd = h->fd;
@@ -740,6 +838,10 @@ rad_create_request(struct rad_handle *h, int code)
generr(h, "denied function call");
return (-1);
}
+ if (h->num_servers == 0) {
+ generr(h, "No RADIUS servers specified");
+ return (-1);
+ }
h->out[POS_CODE] = code;
h->out[POS_IDENT] = ++h->ident;
if (code == RAD_ACCESS_REQUEST) {
@@ -756,16 +858,9 @@ rad_create_request(struct rad_handle *h, int code)
clear_password(h);
h->authentic_pos = 0;
h->out_created = 1;
- h->bindto = INADDR_ANY;
return 0;
}
-void
-rad_bind_to(struct rad_handle *h, in_addr_t addr)
-{
- h->bindto = addr;
-}
-
int
rad_create_response(struct rad_handle *h, int code)
{
@@ -793,6 +888,15 @@ rad_cvt_addr(const void *data)
return value;
}
+struct in6_addr
+rad_cvt_addr6(const void *data)
+{
+ struct in6_addr value;
+
+ memcpy(&value.s6_addr, data, sizeof value.s6_addr);
+ return value;
+}
+
u_int32_t
rad_cvt_int(const void *data)
{
@@ -848,6 +952,8 @@ int
rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
{
int srv;
+ time_t now;
+ struct sockaddr_in sin;
if (h->type == RADIUS_SERVER) {
generr(h, "denied function call");
@@ -855,8 +961,6 @@ rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
}
/* 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;
@@ -902,21 +1006,30 @@ rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
h->out[POS_LENGTH] = h->out_len >> 8;
h->out[POS_LENGTH+1] = h->out_len;
- /*
- * Count the total number of tries we will make, and zero the
- * 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->srv = 0;
+ now = time(NULL);
+ for (srv = 0; srv < h->num_servers; srv++)
h->servers[srv].num_tries = 0;
- }
- if (h->total_tries == 0) {
- generr(h, "No RADIUS servers specified");
- return -1;
+ /* Find a first good server. */
+ for (srv = 0; srv < h->num_servers; srv++) {
+ if (h->servers[srv].is_dead == 0)
+ break;
+ if (h->servers[srv].dead_time &&
+ h->servers[srv].next_probe <= now) {
+ h->servers[srv].is_dead = 0;
+ break;
+ }
+ h->srv++;
}
- h->try = h->srv = 0;
+ /* If all servers was dead on the last probe, try from beginning */
+ if (h->srv == h->num_servers) {
+ for (srv = 0; srv < h->num_servers; srv++) {
+ h->servers[srv].is_dead = 0;
+ h->servers[srv].next_probe = 0;
+ }
+ h->srv = 0;
+ }
return rad_continue_send_request(h, 0, fd, tv);
}
@@ -946,6 +1059,7 @@ rad_auth_open(void)
h->type = RADIUS_AUTH;
h->out_created = 0;
h->eap_msg = 0;
+ h->bindto = INADDR_ANY;
}
return h;
}
@@ -987,6 +1101,13 @@ rad_put_addr(struct rad_handle *h, int type, struct in_addr addr)
}
int
+rad_put_addr6(struct rad_handle *h, int type, struct in6_addr addr)
+{
+
+ return rad_put_attr(h, type, &addr.s6_addr, sizeof addr.s6_addr);
+}
+
+int
rad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
{
int result;
@@ -1229,6 +1350,15 @@ rad_put_vendor_addr(struct rad_handle *h, int vendor, int type,
}
int
+rad_put_vendor_addr6(struct rad_handle *h, int vendor, int type,
+ struct in6_addr addr)
+{
+
+ return (rad_put_vendor_attr(h, vendor, type, &addr.s6_addr,
+ sizeof addr.s6_addr));
+}
+
+int
rad_put_vendor_attr(struct rad_handle *h, int vendor, int type,
const void *value, size_t len)
{
diff --git a/lib/libradius/radlib.h b/lib/libradius/radlib.h
index f9e451b465d9..a47931878922 100644
--- a/lib/libradius/radlib.h
+++ b/lib/libradius/radlib.h
@@ -194,6 +194,9 @@ __BEGIN_DECLS
struct rad_handle *rad_acct_open(void);
int rad_add_server(struct rad_handle *,
const char *, int, const char *, int, int);
+int rad_add_server_ex(struct rad_handle *,
+ const char *, int, const char *, int, int,
+ int, struct in_addr *);
struct rad_handle *rad_auth_open(void);
void rad_bind_to(struct rad_handle *, in_addr_t);
void rad_close(struct rad_handle *);
@@ -203,6 +206,7 @@ int rad_continue_send_request(struct rad_handle *, int,
int rad_create_request(struct rad_handle *, int);
int rad_create_response(struct rad_handle *, int);
struct in_addr rad_cvt_addr(const void *);
+struct in6_addr rad_cvt_addr6(const void *);
u_int32_t rad_cvt_int(const void *);
char *rad_cvt_string(const void *, size_t);
int rad_get_attr(struct rad_handle *, const void **,
@@ -211,6 +215,7 @@ int rad_init_send_request(struct rad_handle *, int *,
struct timeval *);
struct rad_handle *rad_open(void); /* Deprecated, == rad_auth_open */
int rad_put_addr(struct rad_handle *, int, struct in_addr);
+int rad_put_addr6(struct rad_handle *, int, struct in6_addr);
int rad_put_attr(struct rad_handle *, int,
const void *, size_t);
int rad_put_int(struct rad_handle *, int, u_int32_t);
diff --git a/lib/libradius/radlib_private.h b/lib/libradius/radlib_private.h
index a76e594aa613..bfbbbd17ca89 100644
--- a/lib/libradius/radlib_private.h
+++ b/lib/libradius/radlib_private.h
@@ -46,6 +46,7 @@
#define RADIUS_PORT 1812
#define RADACCT_PORT 1813
#define TIMEOUT 3 /* In seconds */
+#define DEAD_TIME 0
/* Limits */
#define ERRSIZE 128 /* Maximum error message length */
@@ -68,6 +69,10 @@ struct rad_server {
int timeout; /* Timeout in seconds */
int max_tries; /* Number of tries before giving up */
int num_tries; /* Number of tries so far */
+ int is_dead; /* The server did not answer last time */
+ time_t dead_time; /* Don't try this server for the time period if it is dead */
+ time_t next_probe; /* Time of a next probe after failure */
+ in_addr_t bindto; /* Bind to address */
};
struct rad_handle {
@@ -88,11 +93,9 @@ struct rad_handle {
unsigned char in[MSGSIZE]; /* Response received */
int in_len; /* Length of response */
int in_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 */
- in_addr_t bindto; /* Bind to address */
+ in_addr_t bindto; /* Current bind address */
};
struct vendor_attribute {
diff --git a/lib/libradius/radlib_vs.h b/lib/libradius/radlib_vs.h
index 8b3a75e79d6a..0acfb0e10e00 100644
--- a/lib/libradius/radlib_vs.h
+++ b/lib/libradius/radlib_vs.h
@@ -73,6 +73,7 @@ struct rad_handle;
__BEGIN_DECLS
int rad_get_vendor_attr(u_int32_t *, const void **, size_t *);
int rad_put_vendor_addr(struct rad_handle *, int, int, struct in_addr);
+int rad_put_vendor_addr6(struct rad_handle *, int, int, struct in6_addr);
int rad_put_vendor_attr(struct rad_handle *, int, int, const void *,
size_t);
int rad_put_vendor_int(struct rad_handle *, int, int, u_int32_t);