- Rewrite radius servers traversal algorithm.
- Add functions for working with IPv6 attributes. Approved by: ae
This commit is contained in:
parent
38ce9496fe
commit
bf5a1b6502
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user