Add support for RADIUS accounting. Note, this changes the format

of the /etc/radius.conf file.   But the code contains hacks for
backward compatibility, so old files will continue to work.

I updated the man pages and made a couple of minor changes, but
everything else was submitted by Oleg.

PR:		misc/14284
Submitted by:	Oleg Semyonov <os@altavista.net>
This commit is contained in:
John Polstra 1999-10-31 04:47:59 +00:00
parent 8308463eba
commit 0981dfef84
5 changed files with 259 additions and 53 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd July 29, 1998
.Dd October 30, 1999
.Dt LIBRADIUS 3
.Os FreeBSD
.Sh NAME
@ -32,8 +32,12 @@
.Nd RADIUS client library
.Sh SYNOPSIS
.Fd #include <radlib.h>
.Ft struct rad_handle *
.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 struct rad_handle *
.Fn rad_auth_open "void"
.Ft void
.Fn rad_close "struct rad_handle *h"
.Ft int
@ -52,8 +56,6 @@
.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
.Fn rad_put_addr "struct rad_handle *h" "int type" "struct in_addr addr"
.Ft int
@ -69,23 +71,31 @@
.Sh DESCRIPTION
The
.Nm
library implements the client side of the Remote Authentication
Dial In User Service (RADIUS). RADIUS, defined in RFC 2138, allows
clients to perform authentication by means of network requests to
remote authentication servers.
library implements the client side of the Remote Authentication Dial
In User Service (RADIUS). RADIUS, defined in RFCs 2138 and 2139,
allows clients to perform authentication and accounting by means of
network requests to remote servers.
.Sh INITIALIZATION
To use the library, an application must first call
.Fn rad_open
.Fn rad_auth_open
or
.Fn rad_acct_open
to obtain a
.Va struct rad_handle * ,
which provides the context for subsequent operations.
The former function is used for RADIUS authentication and the
latter is used for RADIUS accounting.
Calls to
.Fn rad_open
.Fn rad_auth_open
and
.Fn rad_acct_open
always succeed unless insufficient virtual memory is available. If
the necessary memory cannot be allocated,
.Fn rad_open
returns
the necessary memory cannot be allocated, the functions return
.Dv NULL .
For compatibility with earlier versions of this library,
.Fn rad_open
is provided as a synonym for
.Fn rad_auth_open .
.Pp
Before issuing any RADIUS requests, the library must be made aware
of the servers it can contact. The easiest way to configure the
@ -119,9 +129,12 @@ parameter specifies the UDP port to contact on the server. If
.Va port
is given as 0, the library looks up the
.Ql radius/udp
or
.Ql radacct/udp
service in the network services database, and uses the port found
there. If no entry is found, the library uses port 1812, the standard
RADIUS port. The shared secret for the server host is passed to the
there. If no entry is found, the library uses the standard RADIUS
ports, 1812 for authentication and 1813 for accounting.
The shared secret for the server host is passed to the
.Va secret
parameter.
It may be any NUL-terminated string of bytes. The RADIUS protocol
@ -346,9 +359,11 @@ without recording an error message.
.Pp
.Bl -item -offset indent -compact
.It
.Fn rad_cvt_string
.Fn rad_acct_open
.It
.Fn rad_open
.Fn rad_auth_open
.It
.Fn rad_cvt_string
.El
.Sh FILES
.Pa /etc/radius.conf
@ -359,7 +374,14 @@ without recording an error message.
.%T Remote Authentication Dial In User Service (RADIUS)
.%O RFC 2138
.Re
.Rs
.%A C. Rigney
.%T RADIUS Accounting
.%O RFC 2139
.Re
.Sh AUTHORS
This software was written by
This software was originally written by
.An John Polstra ,
and donated to the FreeBSD project by Juniper Networks, Inc.
Oleg Semyonov subsequently added the ability to perform RADIUS
accounting.

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd July 29, 1998
.Dd October 30, 1999
.Dt RADIUS.CONF 5
.Os FreeBSD
.Sh NAME
@ -42,8 +42,22 @@ single RADIUS server which will be used by the library. Leading
white space is ignored, as are empty lines and lines containing
only comments.
.Pp
A RADIUS server is described by two to four fields on a line. The
fields are separated by white space. The
A RADIUS server is described by three to five fields on a line:
.Pp
.Bl -item -offset indent -compact
.It
Service type
.It
Server host
.It
Shared secret
.It
Timeout
.It
Retries
.El
.Pp
The fields are separated by white space. The
.Ql #
character at the beginning of a field begins a comment, which extends
to the end of the line. A field may be enclosed in double quotes,
@ -56,19 +70,43 @@ and the backslash can be represented by
.Ql \e\e .
No other escape sequences are supported.
.Pp
The first field specifies
.Pp
The first field gives the service type, either
.Ql auth
for RADIUS authentication or
.Ql acct
for RADIUS accounting. If a single server provides both services, two
lines are required in the file. Earlier versions of this file did
not include a service type. For backward compatibility, if the first
field is not
.Ql auth
or
.Ql acct
the library behaves as if
.Ql auth
were specified, and interprets the fields in the line as if they
were fields two through five.
.Pp
The second field specifies
the server host, either as a fully qualified domain name or as a
dotted-quad IP address. The host may optionally be followed by a
.Ql \&:
and a numeric port number, without intervening white space. If the
port specification is omitted, it defaults to the
.Ql radius
or
.Ql radacct
service in the
.Pa /etc/services
file, or to the standard RADIUS port 1812 if there is no such entry in
.Pa /etc/services .
file for service types
.Ql auth
and
.Ql acct ,
respectively.
If no such entry is present, the standard ports 1812 and 1813 are
used.
.Pp
The second field contains the shared secret, which should be known
The third field contains the shared secret, which should be known
only to the client and server hosts. It is an arbitrary string of
characters, though it must be enclosed in double quotes if it
contains white space. The shared secret may be
@ -77,16 +115,17 @@ characters. N.B., some popular RADIUS servers have bugs which
prevent them from working properly with secrets longer than 16
characters.
.Pp
The third field contains a decimal integer specifying the timeout in
The fourth field contains a decimal integer specifying the timeout in
seconds for receiving a valid reply from the server. If this field
is omitted, it defaults to 3 seconds.
.Pp
The fourth field contains a decimal integer specifying the maximum
The fifth field contains a decimal integer specifying the maximum
number of attempts that will be made to authenticate with the server
before giving up. If omitted, it defaults to 3 attempts. Note,
this is the total number of attempts and not the number of retries.
.Pp
Up to 10 RADIUS servers may be specified. The servers are tried in
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
maximum number of tries has been reached for all servers.
.Pp
@ -101,14 +140,14 @@ shared secrets, it should not be readable except by root.
.Sh EXAMPLES
.Bd -literal
# A simple entry using all the defaults:
radius1.domain.com OurLittleSecret
acct radius1.domain.com OurLittleSecret
# A server still using the obsolete RADIUS port, with increased
# timeout and maximum tries:
auth.domain.com:1645 "I can't see you, but I know you're there" 5 4
auth auth.domain.com:1645 "I can't see you" 5 4
# A server specified by its IP address:
192.168.27.81 $X*#..38947ax-+=
auth 192.168.27.81 $X*#..38947ax-+=
.Ed
.Sh SEE ALSO
.Xr libradius 3
@ -117,6 +156,11 @@ auth.domain.com:1645 "I can't see you, but I know you're there" 5 4
.%T Remote Authentication Dial In User Service (RADIUS)
.%O RFC 2138
.Re
.Rs
.%A C. Rigney
.%T RADIUS Accounting
.%O RFC 2139
.Re
.Sh AUTHORS
This documentation was written by
.An John Polstra ,

View File

@ -48,6 +48,7 @@ static void clear_password(struct rad_handle *);
static void generr(struct rad_handle *, const char *, ...)
__printflike(2, 3);
static void insert_scrambled_password(struct rad_handle *, int);
static void insert_request_authenticator(struct rad_handle *, int);
static int is_valid_response(struct rad_handle *, int,
const struct sockaddr_in *);
static int put_password_attr(struct rad_handle *, int,
@ -110,6 +111,23 @@ insert_scrambled_password(struct rad_handle *h, int srv)
}
}
static void
insert_request_authenticator(struct rad_handle *h, int srv)
{
MD5_CTX ctx;
const struct rad_server *srvp;
srvp = &h->servers[srv];
/* Create the request authenticator */
MD5Init(&ctx);
MD5Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE);
MD5Update(&ctx, memset(&h->request[POS_AUTH], 0, LEN_AUTH), LEN_AUTH);
MD5Update(&ctx, &h->request[POS_ATTRS], h->req_len - POS_ATTRS);
MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
MD5Final(&h->request[POS_AUTH], &ctx);
}
/*
* Return true if the current response is valid for a request to the
* specified server.
@ -229,9 +247,14 @@ rad_add_server(struct rad_handle *h, const char *host, int port,
else {
struct servent *sent;
srvp->addr.sin_port =
(sent = getservbyname("radius", "udp")) != NULL ?
sent->s_port : htons(RADIUS_PORT);
if (h->type == RADIUS_AUTH)
srvp->addr.sin_port =
(sent = getservbyname("radius", "udp")) != NULL ?
sent->s_port : htons(RADIUS_PORT);
else
srvp->addr.sin_port =
(sent = getservbyname("radacct", "udp")) != NULL ?
sent->s_port : htons(RADACCT_PORT);
}
if ((srvp->secret = strdup(secret)) == NULL) {
generr(h, "Out of memory");
@ -278,18 +301,21 @@ rad_config(struct rad_handle *h, const char *path)
linenum = 0;
while (fgets(buf, sizeof buf, fp) != NULL) {
int len;
char *fields[4];
char *fields[5];
int nfields;
char msg[ERRSIZE];
char *type;
char *host;
char *port_str;
char *secret;
char *timeout_str;
char *maxtries_str;
char *end;
char *wanttype;
unsigned long timeout;
unsigned long maxtries;
int port;
int i;
linenum++;
len = strlen(buf);
@ -307,7 +333,7 @@ rad_config(struct rad_handle *h, const char *path)
buf[len - 1] = '\0';
/* Extract the fields from the line. */
nfields = split(buf, fields, 4, msg, sizeof msg);
nfields = split(buf, fields, 5, msg, sizeof msg);
if (nfields == -1) {
generr(h, "%s:%d: %s", path, linenum, msg);
retval = -1;
@ -315,16 +341,41 @@ rad_config(struct rad_handle *h, const char *path)
}
if (nfields == 0)
continue;
if (nfields < 2) {
/*
* The first field should contain "auth" or "acct" for
* authentication or accounting, respectively. But older
* versions of the file didn't have that field. Default
* it to "auth" for backward compatibility.
*/
if (strcmp(fields[0], "auth") != 0 &&
strcmp(fields[0], "acct") != 0) {
if (nfields >= 5) {
generr(h, "%s:%d: invalid service type", path,
linenum);
retval = -1;
break;
}
nfields++;
for (i = nfields; --i > 0; )
fields[i] = fields[i - 1];
fields[0] = "auth";
}
if (nfields < 3) {
generr(h, "%s:%d: missing shared secret", path,
linenum);
retval = -1;
break;
}
host = fields[0];
secret = fields[1];
timeout_str = fields[2];
maxtries_str = fields[3];
type = fields[0];
host = fields[1];
secret = fields[2];
timeout_str = fields[3];
maxtries_str = fields[4];
/* Ignore the line if it is for the wrong service type. */
wanttype = h->type == RADIUS_AUTH ? "auth" : "acct";
if (strcmp(type, wanttype) != 0)
continue;
/* Parse and validate the fields. */
host = strtok(host, ":");
@ -421,9 +472,13 @@ rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
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);
if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST)
/* Insert the request authenticator into the request */
insert_request_authenticator(h, h->srv);
else
/* 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,
@ -552,14 +607,22 @@ rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
}
}
/* 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;
if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
/* Make sure no password given */
if (h->pass_pos || h->chap_pass) {
generr(h, "User or Chap Password in accounting request");
return -1;
}
} else {
/* 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 */
@ -591,7 +654,7 @@ rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
* In that case, it returns NULL.
*/
struct rad_handle *
rad_open(void)
rad_auth_open(void)
{
struct rad_handle *h;
@ -606,10 +669,28 @@ rad_open(void)
h->pass_len = 0;
h->pass_pos = 0;
h->chap_pass = 0;
h->type = RADIUS_AUTH;
}
return h;
}
struct rad_handle *
rad_acct_open(void)
{
struct rad_handle *h;
h = rad_open();
if (h != NULL)
h->type = RADIUS_ACCT;
return h;
}
struct rad_handle *
rad_open(void)
{
return rad_auth_open();
}
int
rad_put_addr(struct rad_handle *h, int type, struct in_addr addr)
{

View File

@ -36,6 +36,8 @@
#define RAD_ACCESS_REQUEST 1
#define RAD_ACCESS_ACCEPT 2
#define RAD_ACCESS_REJECT 3
#define RAD_ACCOUNTING_REQUEST 4
#define RAD_ACCOUNTING_RESPONSE 5
#define RAD_ACCESS_CHALLENGE 11
/* Attribute types and values */
@ -66,6 +68,9 @@
#define RAD_FILTER_ID 11 /* String */
#define RAD_FRAMED_MTU 12 /* Integer */
#define RAD_FRAMED_COMPRESSION 13 /* Integer */
#define RAD_COMP_NONE 0
#define RAD_COMP_VJ 1
#define RAD_COMP_IPXHDR 2
#define RAD_LOGIN_IP_HOST 14 /* IP address */
#define RAD_LOGIN_SERVICE 15 /* Integer */
#define RAD_LOGIN_TCP_PORT 16 /* Integer */
@ -95,15 +100,63 @@
/* reserved for accounting 40-59 */
#define RAD_CHAP_CHALLENGE 60 /* String */
#define RAD_NAS_PORT_TYPE 61 /* Integer */
#define RAD_ASYNC 0
#define RAD_SYNC 1
#define RAD_ISDN_SYNC 2
#define RAD_ISDN_ASYNC_V120 3
#define RAD_ISDN_ASYNC_V110 4
#define RAD_VIRTUAL 5
#define RAD_PORT_LIMIT 62 /* Integer */
#define RAD_LOGIN_LAT_PORT 63 /* Integer */
#define RAD_CONNECT_INFO 77 /* String */
/* Accounting attribute types and values */
#define RAD_ACCT_STATUS_TYPE 40 /* Integer */
#define RAD_START 1
#define RAD_STOP 2
#define RAD_ACCOUNTING_ON 7
#define RAD_ACCOUNTING_OFF 8
#define RAD_ACCT_DELAY_TIME 41 /* Integer */
#define RAD_ACCT_INPUT_OCTETS 42 /* Integer */
#define RAD_ACCT_OUTPUT_OCTETS 43 /* Integer */
#define RAD_ACCT_SESSION_ID 44 /* String */
#define RAD_ACCT_AUTHENTIC 45 /* Integer */
#define RAD_AUTH_RADIUS 1
#define RAD_AUTH_LOCAL 2
#define RAD_AUTH_REMOTE 3
#define RAD_ACCT_SESSION_TIME 46 /* Integer */
#define RAD_ACCT_INPUT_PACKETS 47 /* Integer */
#define RAD_ACCT_OUTPUT_PACKETS 48 /* Integer */
#define RAD_ACCT_TERMINATE_CAUSE 49 /* Integer */
#define RAD_TERM_USER_REQUEST 1
#define RAD_TERM_LOST_CARRIER 2
#define RAD_TERM_LOST_SERVICE 3
#define RAD_TERM_IDLE_TIMEOUT 4
#define RAD_TERM_SESSION_TIMEOUT 5
#define RAD_TERM_ADMIN_RESET 6
#define RAD_TERM_ADMIN_REBOOT 7
#define RAD_TERM_PORT_ERROR 8
#define RAD_TERM_NAS_ERROR 9
#define RAD_TERM_NAS_REQUEST 10
#define RAD_TERM_NAS_REBOOT 11
#define RAD_TERM_PORT_UNNEEDED 12
#define RAD_TERM_PORT_PREEMPTED 13
#define RAD_TERM_PORT_SUSPENDED 14
#define RAD_TERM_SERVICE_UNAVAILABLE 15
#define RAD_TERM_CALLBACK 16
#define RAD_TERM_USER_ERROR 17
#define RAD_TERM_HOST_REQUEST 18
#define RAD_ACCT_MULTI_SESSION_ID 50 /* String */
#define RAD_ACCT_LINK_COUNT 51 /* Integer */
struct rad_handle;
struct timeval;
__BEGIN_DECLS
struct rad_handle *rad_acct_open(void);
int rad_add_server(struct rad_handle *,
const char *, int, const char *, int, int);
struct rad_handle *rad_auth_open(void);
void rad_close(struct rad_handle *);
int rad_config(struct rad_handle *, const char *);
int rad_continue_send_request(struct rad_handle *, int,
@ -116,7 +169,7 @@ 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);
struct rad_handle *rad_open(void); /* Deprecated, == rad_auth_open */
int rad_put_addr(struct rad_handle *, int, struct in_addr);
int rad_put_attr(struct rad_handle *, int,
const void *, size_t);

View File

@ -34,10 +34,15 @@
#include "radlib.h"
/* Handle types */
#define RADIUS_AUTH 0 /* RADIUS authentication, default */
#define RADIUS_ACCT 1 /* RADIUS accounting */
/* Defaults */
#define MAXTRIES 3
#define PATH_RADIUS_CONF "/etc/radius.conf"
#define RADIUS_PORT 1812
#define RADACCT_PORT 1813
#define TIMEOUT 3 /* In seconds */
/* Limits */
@ -81,6 +86,7 @@ struct rad_handle {
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 */
};
#endif