- Rewrite radius servers traversal algorithm.

- Add functions for working with IPv6 attributes.

Approved by:	ae
This commit is contained in:
Sergey Matveychuk 2012-12-06 19:00:37 +00:00
parent 38ce9496fe
commit bf5a1b6502
7 changed files with 212 additions and 42 deletions

View File

@ -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 \

View File

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

View File

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

View File

@ -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>
@ -378,6 +380,18 @@ put_raw_attr(struct rad_handle *h, int type, const void *value, size_t len)
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;
@ -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)
{
@ -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;
}
@ -986,6 +1100,13 @@ rad_put_addr(struct rad_handle *h, int type, struct in_addr addr)
return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_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)
{
@ -1228,6 +1349,15 @@ rad_put_vendor_addr(struct rad_handle *h, int vendor, int type,
sizeof addr.s_addr));
}
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)

View File

@ -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);

View File

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

View File

@ -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);