/*
* $Id: buildreq.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
*
* Copyright (C) 1995,1997 Lars Fenneberg
*
* See the file COPYRIGHT for the respective terms and conditions.
* If the file is missing contact me at lf@elemental.net
* and I'll send you a copy.
*
*/
#include <includes.h>
#include <radiusclient.h>
unsigned char rc_get_seqnbr(void);
/*
* Function: rc_get_nas_id
*
* Purpose: fills in NAS-Identifier or NAS-IP-Address in request
*
*/
int rc_get_nas_id(VALUE_PAIR **sendpairs)
{
UINT4 client_id;
char *nasid;
nasid = rc_conf_str("nas_identifier");
if (strlen(nasid)) {
/*
* Fill in NAS-Identifier
*/
if (rc_avpair_add(sendpairs, PW_NAS_IDENTIFIER, nasid, 0,
VENDOR_NONE) == NULL)
return (ERROR_RC);
return (OK_RC);
} else {
/*
* Fill in NAS-IP-Address
*/
if ((client_id = rc_own_ipaddress()) == 0)
return (ERROR_RC);
if (rc_avpair_add(sendpairs, PW_NAS_IP_ADDRESS, &client_id,
0, VENDOR_NONE) == NULL)
return (ERROR_RC);
}
return (OK_RC);
}
/*
* Function: rc_buildreq
*
* Purpose: builds a skeleton RADIUS request using information from the
* config file.
*
*/
void rc_buildreq(SEND_DATA *data, int code, char *server, unsigned short port,
int timeout, int retries)
{
data->server = server;
data->svc_port = port;
data->seq_nbr = rc_get_seqnbr();
data->timeout = timeout;
data->retries = retries;
data->code = code;
}
/*
* Function: rc_guess_seqnbr
*
* Purpose: return a random sequence number
*
*/
static unsigned char rc_guess_seqnbr(void)
{
return (unsigned char)(magic() & UCHAR_MAX);
}
/*
* Function: rc_get_seqnbr
*
* Purpose: generate a sequence number
*
*/
unsigned char rc_get_seqnbr(void)
{
FILE *sf;
int tries = 1;
int seq_nbr, pos;
char *seqfile = rc_conf_str("seqfile");
if ((sf = fopen(seqfile, "a+")) == NULL)
{
error("rc_get_seqnbr: couldn't open sequence file %s: %s", seqfile, strerror(errno));
/* well, so guess a sequence number */
return rc_guess_seqnbr();
}
while (do_lock_exclusive(fileno(sf))!= 0)
{
if (errno != EWOULDBLOCK) {
error("rc_get_seqnbr: flock failure: %s: %s", seqfile, strerror(errno));
fclose(sf);
return rc_guess_seqnbr();
}
tries++;
if (tries <= 10)
rc_mdelay(500);
else
break;
}
if (tries > 10) {
error("rc_get_seqnbr: couldn't get lock after %d tries: %s", tries-1, seqfile);
fclose(sf);
return rc_guess_seqnbr();
}
pos = ftell(sf);
rewind(sf);
if (fscanf(sf, "%d", &seq_nbr) != 1) {
if (pos != ftell(sf)) {
/* file was not empty */
error("rc_get_seqnbr: fscanf failure: %s", seqfile);
}
seq_nbr = rc_guess_seqnbr();
}
rewind(sf);
ftruncate(fileno(sf),0);
fprintf(sf,"%d\n", (seq_nbr+1) & UCHAR_MAX);
fflush(sf); /* fflush because a process may read it between the do_unlock and fclose */
if (do_unlock(fileno(sf)) != 0)
error("rc_get_seqnbr: couldn't release lock on %s: %s", seqfile, strerror(errno));
fclose(sf);
return (unsigned char)seq_nbr;
}
/*
* Function: rc_auth
*
* Purpose: Builds an authentication request for port id client_port
* with the value_pairs send and submits it to a server
*
* Returns: received value_pairs in received, messages from the server in msg
* and 0 on success, negative on failure as return value
*
*/
int rc_auth(UINT4 client_port, VALUE_PAIR *send, VALUE_PAIR **received,
char *msg, REQUEST_INFO *info)
{
SERVER *authserver = rc_conf_srv("authserver");
if (!authserver) {
return (ERROR_RC);
}
return rc_auth_using_server(authserver, client_port, send, received,
msg, info);
}
/*
* Function: rc_auth_using_server
*
* Purpose: Builds an authentication request for port id client_port
* with the value_pairs send and submits it to a server. You
* explicitly supply a server list.
*
* Returns: received value_pairs in received, messages from the server in msg
* and 0 on success, negative on failure as return value
*
*/
int rc_auth_using_server(SERVER *authserver,
UINT4 client_port,
VALUE_PAIR *send,
VALUE_PAIR **received,
char *msg, REQUEST_INFO *info)
{
SEND_DATA data;
int result;
int i;
int timeout = rc_conf_int("radius_timeout");
int retries = rc_conf_int("radius_retries");
data.send_pairs = send;
data.receive_pairs = NULL;
/*
* Fill in NAS-IP-Address or NAS-Identifier
*/
if (rc_get_nas_id(&(data.send_pairs)) == ERROR_RC)
return (ERROR_RC);
/*
* Fill in NAS-Port
*/
if (rc_avpair_add(&(data.send_pairs), PW_NAS_PORT, &client_port, 0, VENDOR_NONE) == NULL)
return (ERROR_RC);
result = ERROR_RC;
for(i=0; (i<authserver->max) && (result != OK_RC) && (result != BADRESP_RC)
; i++)
{
if (data.receive_pairs != NULL) {
rc_avpair_free(data.receive_pairs);
data.receive_pairs = NULL;
}
rc_buildreq(&data, PW_ACCESS_REQUEST, authserver->name[i],
authserver->port[i], timeout, retries);
result = rc_send_server (&data, msg, info);
}
*received = data.receive_pairs;
return result;
}
/*
* Function: rc_auth_proxy
*
* Purpose: Builds an authentication request
* with the value_pairs send and submits it to a server.
* Works for a proxy; does not add IP address, and does
* does not rely on config file.
*
* Returns: received value_pairs in received, messages from the server in msg
* and 0 on success, negative on failure as return value
*
*/
int rc_auth_proxy(VALUE_PAIR *send, VALUE_PAIR **received, char *msg)
{
SEND_DATA data;
int result;
int i;
SERVER *authserver = rc_conf_srv("authserver");
int timeout = rc_conf_int("radius_timeout");
int retries = rc_conf_int("radius_retries");
data.send_pairs = send;
data.receive_pairs = NULL;
result = ERROR_RC;
for(i=0; (i<authserver->max) && (result != OK_RC) && (result != BADRESP_RC)
; i++)
{
if (data.receive_pairs != NULL) {
rc_avpair_free(data.receive_pairs);
data.receive_pairs = NULL;
}
rc_buildreq(&data, PW_ACCESS_REQUEST, authserver->name[i],
authserver->port[i], timeout, retries);
result = rc_send_server (&data, msg, NULL);
}
*received = data.receive_pairs;
return result;
}
/*
* Function: rc_acct_using_server
*
* Purpose: Builds an accounting request for port id client_port
* with the value_pairs send. You explicitly supply server list.
*
* Remarks: NAS-Identifier/NAS-IP-Address, NAS-Port and Acct-Delay-Time get
* filled in by this function, the rest has to be supplied.
*/
int rc_acct_using_server(SERVER *acctserver,
UINT4 client_port,
VALUE_PAIR *send)
{
SEND_DATA data;
VALUE_PAIR *adt_vp;
int result;
time_t start_time, dtime;
char msg[4096];
int i;
int timeout = rc_conf_int("radius_timeout");
int retries = rc_conf_int("radius_retries");
data.send_pairs = send;
data.receive_pairs = NULL;
/*
* Fill in NAS-IP-Address or NAS-Identifier
*/
if (rc_get_nas_id(&(data.send_pairs)) == ERROR_RC)
return (ERROR_RC);
/*
* Fill in NAS-Port
*/
if (rc_avpair_add(&(data.send_pairs), PW_NAS_PORT, &client_port, 0, VENDOR_NONE) == NULL)
return (ERROR_RC);
/*
* Fill in Acct-Delay-Time
*/
dtime = 0;
if ((adt_vp = rc_avpair_add(&(data.send_pairs), PW_ACCT_DELAY_TIME, &dtime, 0, VENDOR_NONE)) == NULL)
return (ERROR_RC);
start_time = time(NULL);
result = ERROR_RC;
for(i=0; (i<acctserver->max) && (result != OK_RC) && (result != BADRESP_RC)
; i++)
{
if (data.receive_pairs != NULL) {
rc_avpair_free(data.receive_pairs);
data.receive_pairs = NULL;
}
rc_buildreq(&data, PW_ACCOUNTING_REQUEST, acctserver->name[i],
acctserver->port[i], timeout, retries);
dtime = time(NULL) - start_time;
rc_avpair_assign(adt_vp, &dtime, 0);
result = rc_send_server (&data, msg, NULL);
}
rc_avpair_free(data.receive_pairs);
return result;
}
/*
* Function: rc_acct
*
* Purpose: Builds an accounting request for port id client_port
* with the value_pairs send
*
* Remarks: NAS-Identifier/NAS-IP-Address, NAS-Port and Acct-Delay-Time get
* filled in by this function, the rest has to be supplied.
*/
int rc_acct(UINT4 client_port, VALUE_PAIR *send)
{
SERVER *acctserver = rc_conf_srv("acctserver");
if (!acctserver) return (ERROR_RC);
return rc_acct_using_server(acctserver, client_port, send);
}
/*
* Function: rc_acct_proxy
*
* Purpose: Builds an accounting request with the value_pairs send
*
*/
int rc_acct_proxy(VALUE_PAIR *send)
{
SEND_DATA data;
int result;
char msg[4096];
int i;
SERVER *acctserver = rc_conf_srv("authserver");
int timeout = rc_conf_int("radius_timeout");
int retries = rc_conf_int("radius_retries");
data.send_pairs = send;
data.receive_pairs = NULL;
result = ERROR_RC;
for(i=0; (i<acctserver->max) && (result != OK_RC) && (result != BADRESP_RC)
; i++)
{
if (data.receive_pairs != NULL) {
rc_avpair_free(data.receive_pairs);
data.receive_pairs = NULL;
}
rc_buildreq(&data, PW_ACCOUNTING_REQUEST, acctserver->name[i],
acctserver->port[i], timeout, retries);
result = rc_send_server (&data, msg, NULL);
}
rc_avpair_free(data.receive_pairs);
return result;
}
/*
* Function: rc_check
*
* Purpose: ask the server hostname on the specified port for a
* status message
*
*/
int rc_check(char *host, unsigned short port, char *msg)
{
SEND_DATA data;
int result;
UINT4 service_type;
int timeout = rc_conf_int("radius_timeout");
int retries = rc_conf_int("radius_retries");
data.send_pairs = data.receive_pairs = NULL;
/*
* Fill in NAS-IP-Address or NAS-Identifier,
* although it isn't neccessary
*/
if (rc_get_nas_id(&(data.send_pairs)) == ERROR_RC)
return (ERROR_RC);
/*
* Fill in Service-Type
*/
service_type = PW_ADMINISTRATIVE;
rc_avpair_add(&(data.send_pairs), PW_SERVICE_TYPE, &service_type, 0, VENDOR_NONE);
rc_buildreq(&data, PW_STATUS_SERVER, host, port, timeout, retries);
result = rc_send_server (&data, msg, NULL);
rc_avpair_free(data.receive_pairs);
return result;
}