Allow an alternate to rad_send_request() for programs that

don't wish to wait for the RADIUS server to respond.
Reviewed by: jdp
This commit is contained in:
Brian Somers 1999-02-05 11:23:44 +00:00
parent d7f9b906ed
commit 48caee2acd
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=43662
4 changed files with 229 additions and 142 deletions

View File

@ -39,6 +39,8 @@
.Ft int
.Fn rad_config "struct rad_handle *h" "const char *file"
.Ft int
.Fn rad_continue_send_request "struct rad_handle *h" "int selected" "int *fd" "struct timeval *tv"
.Ft int
.Fn rad_create_request "struct rad_handle *h" "int code"
.Ft struct in_addr
.Fn rad_cvt_addr "const void *data"
@ -48,6 +50,8 @@
.Fn rad_cvt_string "const void *data" "size_t len"
.Ft int
.Fn rad_get_attr "struct rad_handle *h" "const void **data" "size_t *len"
.Ft int
.Fn rad_init_send_request "struct rad_handle *h" "int *fd" "struct timeval *tv"
.Ft struct rad_handle *
.Fn rad_open "void"
.Ft int
@ -179,10 +183,16 @@ The
.Fn rad_put_X
functions return 0 on success, or -1 if an error occurs.
.Sh SENDING THE REQUEST AND RECEIVING THE RESPONSE
After the RADIUS request has been constructed, it is sent by means
of
.Fn rad_send_request .
This function sends the request and waits for a valid reply,
After the RADIUS request has been constructed, it is sent either by means of
.Fn rad_send_request
or by a combination of calls to
.Fn rad_init_send_request
and
.Fn rad_continue_send_request .
.Pp
The
.Fn rad_send_request
function sends the request and waits for a valid reply,
retrying the defined servers in round-robin fashion as necessary.
If a valid response is received,
.Fn rad_send_request
@ -196,9 +206,43 @@ If no valid response is received,
.Fn rad_send_request
returns -1.
.Pp
As an alternative, if you do not wish to block waiting for a response,
.Fn rad_init_send_request
and
.Fn rad_continue_send_request
may be used instead. If a reply is received from the RADIUS server or a
timeout occurs, these functions return a value as described for
.Fn rad_send_request .
Otherwise, a value of zero is returned and the values pointed to by
.Ar fd
and
.Ar tv
are set to the descriptor and timeout that should be passed to
.Xr select 2 .
.Pp
.Fn rad_init_send_request
must be called first, followed by repeated calls to
.Fn rad_continue_send_request
as long as a return value of zero is given.
Between each call, the application should call
.Xr select 2 ,
passing
.Ar *fd
as a read descriptor and timing out after the interval specified by
.Ar tv .
When select returns,
.Fn rad_continue_send_request
should be called with
.Ar selected
set to a non-zero value if
.Xr select 2
indicated that the descriptor is readable.
.Pp
Like RADIUS requests, each response may contain zero or more
attributes. After a response has been received successfully by
.Fn rad_send_request ,
.Fn rad_send_request
or
.Fn rad_continue_send_request ,
its attributes can be extracted one by one using
.Fn rad_get_attr .
Each time
@ -286,6 +330,10 @@ which can be retrieved using
.It
.Fn rad_put_string
.It
.Fn rad_init_send_request
.It
.Fn rad_continue_send_request
.It
.Fn rad_send_request
.El
.Pp

View File

@ -362,8 +362,6 @@ rad_config(struct rad_handle *h, const char *path)
if (rad_add_server(h, host, port, secret, timeout, maxtries) ==
-1) {
char msg[ERRSIZE];
strcpy(msg, h->errmsg);
generr(h, "%s:%d: %s", path, linenum, msg);
retval = -1;
@ -376,6 +374,78 @@ rad_config(struct rad_handle *h, const char *path)
return retval;
}
/*
* rad_init_send_request() must have previously been called.
* Returns:
* 0 The application should select on *fd with a timeout of tv before
* calling rad_continue_send_request again.
* < 0 Failure
* > 0 Success
*/
int
rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
struct timeval *tv)
{
int n;
if (selected) {
struct sockaddr_in from;
int fromlen;
fromlen = sizeof from;
h->resp_len = recvfrom(h->fd, h->response,
MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
if (h->resp_len == -1) {
generr(h, "recvfrom: %s", strerror(errno));
return -1;
}
if (is_valid_response(h, h->srv, &from)) {
h->resp_len = h->response[POS_LENGTH] << 8 |
h->response[POS_LENGTH+1];
h->resp_pos = POS_ATTRS;
return h->response[POS_CODE];
}
}
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;
/* Insert the scrambled password into the request */
if (h->pass_pos != 0)
insert_scrambled_password(h, h->srv);
/* 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);
if (n != h->req_len) {
if (n == -1)
generr(h, "sendto: %s", strerror(errno));
else
generr(h, "sendto: short write");
return -1;
}
h->try++;
h->servers[h->srv].num_tries++;
tv->tv_sec = h->servers[h->srv].timeout;
tv->tv_usec = 0;
*fd = h->fd;
return 0;
}
int
rad_create_request(struct rad_handle *h, int code)
{
@ -452,6 +522,69 @@ rad_get_attr(struct rad_handle *h, const void **value, size_t *len)
return type;
}
/*
* Returns -1 on error, 0 to indicate no event and >0 for success
*/
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;
}
}
/* Make sure the user gave us a password */
if (h->pass_pos == 0 && !h->chap_pass) {
generr(h, "No User or Chap Password attributes given");
return -1;
}
if (h->pass_pos != 0 && h->chap_pass) {
generr(h, "Both User and Chap Password attributes given");
return -1;
}
/* Fill in the length field in the message */
h->request[POS_LENGTH] = h->req_len >> 8;
h->request[POS_LENGTH+1] = h->req_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->servers[srv].num_tries = 0;
}
if (h->total_tries == 0) {
generr(h, "No RADIUS servers specified");
return -1;
}
h->try = h->srv = 0;
return rad_continue_send_request(h, 0, fd, tv);
}
/*
* Create and initialize a rad_handle structure, and return it to the
* caller. Can fail only if the necessary memory cannot be allocated.
@ -520,151 +653,49 @@ rad_put_string(struct rad_handle *h, int type, const char *str)
int
rad_send_request(struct rad_handle *h)
{
int total_tries;
int try;
int srv;
struct timeval timelimit;
struct timeval tv;
int fd;
int n;
int got_valid_response;
/* Make sure we have a socket to use */
if (h->fd == -1) {
struct sockaddr_in sin;
n = rad_init_send_request(h, &fd, &tv);
if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
generr(h, "Cannot create socket: %s", strerror(errno));
if (n != 0)
return n;
gettimeofday(&timelimit, NULL);
timeradd(&tv, &timelimit, &timelimit);
for ( ; ; ) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
n = select(fd + 1, &readfds, NULL, NULL, &tv);
if (n == -1) {
generr(h, "select: %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;
}
}
/* Make sure the user gave us a password */
if (h->pass_pos == 0 && !h->chap_pass) {
generr(h, "No User or Chap Password attributes given");
return -1;
}
if (h->pass_pos != 0 && h->chap_pass) {
generr(h, "Both User and Chap Password attributes given");
return -1;
}
/* Fill in the length field in the message */
h->request[POS_LENGTH] = h->req_len >> 8;
h->request[POS_LENGTH+1] = h->req_len;
/*
* Count the total number of tries we will make, and zero the
* counter for each server.
*/
total_tries = 0;
for (srv = 0; srv < h->num_servers; srv++) {
total_tries += h->servers[srv].max_tries;
h->servers[srv].num_tries = 0;
}
if (total_tries == 0) {
generr(h, "No RADIUS servers specified");
return -1;
}
srv = 0;
got_valid_response = 0;
for (try = 0; try < total_tries; try++) {
struct timeval timelimit;
struct timeval tv;
/*
* 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[srv].num_tries >=
h->servers[srv].max_tries)
if (++srv >= h->num_servers)
srv = 0;
/* Insert the scrambled password into the request */
if (h->pass_pos != 0)
insert_scrambled_password(h, srv);
/* Send the request */
n = sendto(h->fd, h->request, h->req_len, 0,
(const struct sockaddr *)&h->servers[srv].addr,
sizeof h->servers[srv].addr);
if (n != h->req_len) {
if (n == -1)
generr(h, "sendto: %s", strerror(errno));
else
generr(h, "sendto: short write");
return -1;
}
h->servers[srv].num_tries++;
/* Wait for a valid response */
gettimeofday(&timelimit, NULL);
timelimit.tv_sec += h->servers[srv].timeout;
tv.tv_sec = h->servers[srv].timeout;
tv.tv_usec = 0;
for ( ; ; ) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(h->fd, &readfds);
n = select(h->fd + 1, &readfds, NULL, NULL, &tv);
if (n == -1) {
generr(h, "select: %s", strerror(errno));
return -1;
}
if (n == 0) /* Timed out */
break;
if (FD_ISSET(h->fd, &readfds)) {
struct sockaddr_in from;
int fromlen;
fromlen = sizeof from;
h->resp_len = recvfrom(h->fd, h->response,
MSGSIZE, MSG_WAITALL,
(struct sockaddr *)&from, &fromlen);
if (h->resp_len == -1) {
generr(h, "recvfrom: %s",
strerror(errno));
return -1;
}
if (is_valid_response(h, srv, &from)) {
got_valid_response = 1;
break;
}
}
if (!FD_ISSET(fd, &readfds)) {
/* Compute a new timeout */
gettimeofday(&tv, NULL);
timersub(&timelimit, &tv, &tv);
if (tv.tv_sec < 0) /* Still poll once more */
timerclear(&tv);
if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
/* Continue the select */
continue;
}
if (got_valid_response)
break;
/* Advance to the next server */
if (++srv >= h->num_servers)
srv = 0;
n = rad_continue_send_request(h, n, &fd, &tv);
if (n != 0)
return n;
gettimeofday(&timelimit, NULL);
timeradd(&tv, &timelimit, &timelimit);
}
if (!got_valid_response) {
generr(h, "No valid RADIUS responses received");
return -1;
}
h->resp_len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1];
h->resp_pos = POS_ATTRS;
return h->response[POS_CODE];
}
const char *

View File

@ -99,18 +99,23 @@
#define RAD_LOGIN_LAT_PORT 63 /* Integer */
struct rad_handle;
struct timeval;
__BEGIN_DECLS
int rad_add_server(struct rad_handle *,
const char *, int, const char *, int, int);
void rad_close(struct rad_handle *);
int rad_config(struct rad_handle *, const char *);
int rad_continue_send_request(struct rad_handle *, int,
int *, struct timeval *);
int rad_create_request(struct rad_handle *, int);
struct in_addr rad_cvt_addr(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 **,
size_t *);
int rad_init_send_request(struct rad_handle *, int *,
struct timeval *);
struct rad_handle *rad_open(void);
int rad_put_addr(struct rad_handle *, int, struct in_addr);
int rad_put_attr(struct rad_handle *, int,

View File

@ -74,10 +74,13 @@ struct rad_handle {
char pass[PASSSIZE]; /* Cleartext password */
int pass_len; /* Length of cleartext password */
int pass_pos; /* Position of scrambled password */
unsigned chap_pass : 1; /* Have we got a CHAP_PASSWORD ? */
char chap_pass; /* Have we got a CHAP_PASSWORD ? */
unsigned char response[MSGSIZE]; /* Response received */
int resp_len; /* Length of response */
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 */
};
#endif