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:
parent
84b7ab73bf
commit
54fb95ffd0
@ -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
|
||||
|
@ -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 *
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user