Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

radius async support #59

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions include/freeradius-client.h
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ typedef struct value_pair
#define OK_RC 0
#define TIMEOUT_RC 1
#define REJECT_RC 2
#define READBLOCK_RC 3

typedef struct send_data /* Used to pass information to sendserver() function */
{
Expand All @@ -424,6 +425,25 @@ typedef struct send_data /* Used to pass information to sendserver() function */
VALUE_PAIR *receive_pairs; //!< Where to place received a/v pairs.
} SEND_DATA;

typedef struct send_context /* Used to pass information to cc_aaa_receive_async() function */
{
int idx; //!< index to the destination that was last tried
rc_handle *rh; //!< rh a handle to parsed configuration.
char *msg; //!< will contain the concatenation of any %PW_REPLY_MESSAGE received.
unsigned type; //!< request type (accounting / authentification)
SEND_DATA *data; //!< used to pass information to sendserver() function
int again; //!< first or second pass through all destinations;
int sockfd; //!< socket to open connection
VALUE_PAIR *adt_vp; //!< internal rc_aaa parameter
struct addrinfo *auth_addr;//!< internal rc_aaa parameter
SERVER *aaaserver;//!< server descriptions
int skip_count;//!< internal rc_aaa parameter
double start_time;//!< internal rc_aaa parameter
int request_type;//!< acct or auth
unsigned char vector[AUTH_VECTOR_LEN];//!< internal sendserver() param
char secret[MAX_SECRET_LENGTH + 1];//!< radius secret
} SEND_CONTEXT;

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
Expand Down Expand Up @@ -466,13 +486,24 @@ VALUE_PAIR *rc_avpair_readin(rc_handle const *, FILE *);
void rc_buildreq(rc_handle const *, SEND_DATA *, int, char *, unsigned short, char *, int, int);
unsigned char rc_get_id();
int rc_auth(rc_handle *, uint32_t, VALUE_PAIR *, VALUE_PAIR **, char *);

int rc_auth_async(rc_handle *, uint32_t, VALUE_PAIR *, VALUE_PAIR **, char *, SEND_CONTEXT**);
int rc_auth_resume(SEND_CONTEXT **, VALUE_PAIR **);

int rc_auth_proxy(rc_handle *, VALUE_PAIR *, VALUE_PAIR **, char *);
int rc_acct(rc_handle *, uint32_t, VALUE_PAIR *);

int rc_acct_async(rc_handle *, uint32_t, VALUE_PAIR *, SEND_CONTEXT **);
int rc_acct_resume(SEND_CONTEXT **);

int rc_acct_proxy(rc_handle *, VALUE_PAIR *);
int rc_check(rc_handle *, char *, char *, unsigned short, char *);

int rc_aaa(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **received,
char *msg, int add_nas_port, int request_type);
int rc_aaa_async (rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **received,
char *msg, int add_nas_port, int request_type, SEND_CONTEXT **ctx);
int rc_aaa_receive_async(SEND_CONTEXT **ctx, VALUE_PAIR **received, int request_type);

/* clientid.c */

Expand Down
297 changes: 297 additions & 0 deletions lib/buildreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,239 @@ int rc_aaa(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **r
return result;
}

/** Builds an authentication/accounting request for port id client_port with the value_pairs send and submits it to a server;
*
* @param rh a handle to parsed configuration.
* @param client_port the client port number to use (may be zero to use any available).
* @param send a #VALUE_PAIR array of values (e.g., %PW_USER_NAME).
* @param received an allocated array of received values.
* @param msg must be an array of %PW_MAX_MSG_SIZE or %NULL; will contain the concatenation of any
* %PW_REPLY_MESSAGE received.
* @param add_nas_port if non-zero it will include %PW_NAS_PORT in sent pairs.
* @param request_type one of standard RADIUS codes (e.g., %PW_ACCESS_REQUEST).
* @param ctx the context which shall be passed to the asynchronous receive function;
* @return OK_RC on send success and populated @ctx and socket in @ctx->sockfd;
* resume shall be called
* ERROR_RC on failure
* if upper layer application detects timeout on sockfd it shall call this function
* again with the same @ctx
*/

int rc_aaa_async (rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **received,
char *msg, int add_nas_port, int request_type, SEND_CONTEXT **ctx)
{
SEND_DATA data;
VALUE_PAIR *adt_vp = NULL;
int result;
int i, skip_count;
int resume_send = 0;
SERVER *aaaserver;
int radius_deadtime = rc_conf_int(rh, "radius_deadtime");
double start_time = 0;
double now = 0;
time_t dtime;
unsigned type;

if (*ctx != NULL) {
/* if here it means another destination is tried */
aaaserver = (*ctx)->aaaserver;
data = *((*ctx)->data);
skip_count = (*ctx)->skip_count;

/* resume from the next destination */
(*ctx)->idx++;

if (!(*ctx)->again) {
if (radius_deadtime > 0)
aaaserver->deadtime_ends[(*ctx)->idx] =
(*ctx)->start_time + radius_deadtime;
}
} else {
if (request_type != PW_ACCOUNTING_REQUEST) {
aaaserver = rc_conf_srv(rh, "authserver");
type = AUTH;
} else {
aaaserver = rc_conf_srv(rh, "acctserver");
type = ACCT;
}
if (aaaserver == NULL)
return ERROR_RC;

data.send_pairs = send;
data.receive_pairs = NULL;

if (add_nas_port != 0) {
/*
* Fill in NAS-Port
*/
if (rc_avpair_add(rh, &(data.send_pairs), PW_NAS_PORT,
&client_port, 0, 0) == NULL)
return ERROR_RC;
}

if (request_type == PW_ACCOUNTING_REQUEST) {
/*
* Fill in Acct-Delay-Time
*/
dtime = 0;
now = rc_getctime();
adt_vp = rc_avpair_get(data.send_pairs, PW_ACCT_DELAY_TIME, 0);
if (adt_vp == NULL) {
adt_vp = rc_avpair_add(rh, &(data.send_pairs),
PW_ACCT_DELAY_TIME, &dtime, 0, 0);
if (adt_vp == NULL)
return ERROR_RC;
start_time = now;
} else {
start_time = now - adt_vp->lvalue;
}
}

if ((*ctx = malloc(sizeof(SEND_CONTEXT) + sizeof(SEND_DATA))) == NULL) {
rc_log(LOG_ERR, "rc_aaa_async: out of memory\n");
return -1;
}
memset(*ctx, '\0', sizeof(SEND_CONTEXT) + sizeof(SEND_DATA));

(*ctx)->rh = rh;
(*ctx)->data = (SEND_DATA *)(*ctx + 1);
(*ctx)->msg = msg;
(*ctx)->idx = 0;

skip_count = 0;
}

if ((*ctx)->again != 1) {
result = ERROR_RC;
for (i = (*ctx)->idx; (i < aaaserver->max) && (result != OK_RC);
i++, now = rc_getctime()) {
if (aaaserver->deadtime_ends[i] != -1 &&
aaaserver->deadtime_ends[i] > start_time) {
skip_count++;
continue;
}

if (data.receive_pairs != NULL) {
rc_avpair_free(data.receive_pairs);
data.receive_pairs = NULL;
}

rc_buildreq(rh, &data, request_type, aaaserver->name[i],
aaaserver->port[i], aaaserver->secret[i], 0, 0);
(*(*ctx)->data) = data;

if (request_type == PW_ACCOUNTING_REQUEST) {
dtime = now - start_time;
rc_avpair_assign(adt_vp, &dtime, 0);
}

result = rc_send_server_async (rh, &data, msg, type, ctx);
if (result == OK_RC) {
(*ctx)->idx = i;
(*ctx)->skip_count = skip_count;
}
}

if (result == OK_RC) {
(*ctx)->type = type;
(*ctx)->start_time = start_time;
(*ctx)->adt_vp = adt_vp;
(*ctx)->aaaserver = aaaserver;

return result;
}

if (skip_count == 0) {
goto out_err;
}

(*ctx)->again = 1;
(*ctx)->idx = 0;
}

result = ERROR_RC;
for (i = (*ctx)->idx; (i < aaaserver->max) && (result != OK_RC); i++) {
if (aaaserver->deadtime_ends[i] != -1 ||
aaaserver->deadtime_ends[i] <= start_time) {
continue;
}

if (data.receive_pairs != NULL) {
rc_avpair_free(data.receive_pairs);
data.receive_pairs = NULL;
}

rc_buildreq(rh, &data, request_type, aaaserver->name[i],
aaaserver->port[i], aaaserver->secret[i], 0, 0);
(*(*ctx)->data) = data;

if (request_type == PW_ACCOUNTING_REQUEST) {
dtime = now - start_time;
rc_avpair_assign(adt_vp, &dtime, 0);
}

result = rc_send_server_async (rh, &data, msg, type, ctx);
if (result == OK_RC)
(*ctx)->idx = i;

if (result != OK_RC)
aaaserver->deadtime_ends[i] = -1;

}

if (result == OK_RC) {
(*ctx)->type = type;
(*ctx)->start_time = start_time;
(*ctx)->adt_vp = adt_vp;
(*ctx)->aaaserver = aaaserver;

return result;
}

out_err:
/* got through all entries; none OK; free ctx and exit*/
free(*ctx);
*ctx = NULL;

return result;
}

/* Receives the reply from the server
* @param ctx the context that was set by rc_aaa_async function
* @param received an allocated array of received values.
* @return NULL @ctx and OK_RC(0) on success
* BLOCK_RC(3) and not NULL @ctx on EWOULDBLOCK/EAGAIN
* NULL @ctx on any other failure return code
*/
/*rc_receive async name*/
int rc_aaa_receive_async(SEND_CONTEXT **ctx, VALUE_PAIR **received, int request_type)
{
int i;
int result;

if (*ctx == NULL) {
rc_log(LOG_ERR, "rc_aaa_async: context is null");
return ERROR_RC;
}

result = rc_receive_async(ctx);

if (result != READBLOCK_RC) {
i = (*ctx)->idx;
(*ctx)->aaaserver->deadtime_ends[i] = -1;
if (request_type != PW_ACCOUNTING_REQUEST) {
*received = (*ctx)->data->receive_pairs;
} else {
rc_avpair_free((*ctx)->data->receive_pairs);
}
} else {
free(*ctx);
*ctx = NULL;
}

return result;
}

/** Builds an authentication request for port id client_port with the value_pairs send and submits it to a server
*
* @param rh a handle to parsed configuration.
Expand All @@ -196,6 +429,39 @@ int rc_auth(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **
return rc_aaa(rh, client_port, send, received, msg, 1, PW_ACCESS_REQUEST);
}

/* Builds an authentication request for port id client_port with the value_pairs send and submits it to a server
* @param rh a handle to parsed configuration.
* @param client_port the client port number to use (may be zero to use any available).
* @param send a #VALUE_PAIR array of values (e.g., %PW_USER_NAME).
* @param received an allocated array of received values.
* @param msg must be an array of %PW_MAX_MSG_SIZE or %NULL; will contain the concatenation of any
* %PW_REPLY_MESSAGE received.
* @param ctx the context which shall be passed to the asynchronous receive function.
* @return received value_pairs in @received, messages from the server in msg (if non-NULL),
* context for resume function in @ctx, sockfd in @ctx->sockfd and %OK_RC (0) on success
* negative on failure as return value.
* on failure an error code is called; function shall not be called again
* if upper layer application detects timeout on socket it shall call this function
* again with same context
*/
int rc_auth_async(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **received,
char *msg, SEND_CONTEXT **ctx)
{

return rc_aaa_async(rh, client_port, send, received, msg, 1, PW_ACCESS_REQUEST, ctx);
}

/* Asynchronously receives the authentification reply from the server
* @param ctx the context that was set by rc_auth_async function
* @param received an allocated array of received values.
* @return received value_pairs in @received OK_RC(0) on success;
* BLOCK_RC and not null @ctx on EWOULDBLOCK/EAGAIN
* any other rc means failure or rejection
*/
int rc_auth_resume(SEND_CONTEXT **ctx, VALUE_PAIR ** received) {
return rc_aaa_receive_async(ctx, received, PW_ACCESS_REQUEST);
}

/** Builds an authentication request for proxying
*
* Builds an authentication request with the value_pairs send and submits it to a server.
Expand Down Expand Up @@ -229,6 +495,37 @@ int rc_acct(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send)
return rc_aaa(rh, client_port, send, NULL, NULL, 1, PW_ACCOUNTING_REQUEST);
}

/** Builds an accounting request for port id client_port with the value_pairs at send
*
* @note NAS-IP-Address, NAS-Port and Acct-Delay-Time get filled in by this function, the rest has to be supplied.
*
* @param rh a handle to parsed configuration.
* @param client_port the client port number to use (may be zero to use any available).
* @param send a #VALUE_PAIR array of values (e.g., %PW_USER_NAME).
* @param ctx the context which shall be passed to the asynchronous receive function;
*@return received value_pairs in @received, messages from the server in msg (if non-NULL),
* context for resume function in @ctx, sockfd in @ctx->sockfd and %OK_RC (0) on success
* negative on failure as return value.
* on failure an error code is called; function shall not be called again
* if upper layer application detects timeout on socket it shall call this function
* again with same context

*/
int rc_acct_async(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, SEND_CONTEXT **ctx) {
return rc_aaa_async(rh, client_port, send, NULL, NULL, 1, PW_ACCOUNTING_REQUEST, ctx);
}

/* Asynchronously receives the accounting reply from the server
* @param ctx the context that was set by rc_acct_resume function
* @return NULL @ctx and OK_RC(0) on success;
* BLOCK_RC and not NULL @ctx on EWOULDBLOCK/EAGAIN
* any other rc means failure
*
*/
int rc_acct_resume(SEND_CONTEXT **ctx) {
return rc_aaa_receive_async(ctx, NULL, PW_ACCOUNTING_REQUEST);
}

/** Builds an accounting request with the value_pairs at send
*
* @param rh a handle to parsed configuration.
Expand Down
Loading