Initial import of RADIUS client library donated by Juniper Networks, Inc.

This commit is contained in:
John Polstra 1998-11-13 00:53:01 +00:00
parent d551f05381
commit 082bfe6741
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/JUNIPER/; revision=41118
6 changed files with 1425 additions and 0 deletions

41
lib/libradius/Makefile Normal file
View File

@ -0,0 +1,41 @@
# Copyright 1998 Juniper Networks, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
LIB= radius
SRCS= radlib.c
CFLAGS+= -Wall
DPADD+= ${LIBMD}
LDADD+= -lmd
SHLIB_MAJOR= 1
SHLIB_MINOR= 0
MAN3+= libradius.3
MAN5+= radius.conf.5
beforeinstall:
${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m 444 \
${.CURDIR}/radlib.h ${DESTDIR}/usr/include
.include <bsd.lib.mk>

317
lib/libradius/libradius.3 Normal file
View File

@ -0,0 +1,317 @@
.\" Copyright 1998 Juniper Networks, Inc.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd July 29, 1998
.Dt LIBRADIUS 3
.Os FreeBSD
.Sh NAME
.Nm libradius
.Nd RADIUS client library
.Sh SYNOPSIS
.Fd #include <radlib.h>
.Ft int
.Fn rad_add_server "struct rad_handle *h" "const char *host" "int port" "const char *secret" "int timeout" "int max_tries"
.Ft void
.Fn rad_close "struct rad_handle *h"
.Ft int
.Fn rad_config "struct rad_handle *h" "const char *file"
.Ft int
.Fn rad_create_request "struct rad_handle *h" "int code"
.Ft struct in_addr
.Fn rad_cvt_addr "const void *data"
.Ft u_int32_t
.Fn rad_cvt_int "const void *data"
.Ft char *
.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 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
.Fn rad_put_attr "struct rad_handle *h" "int type" "const void *data" "size_t len"
.Ft int
.Fn rad_put_int "struct rad_handle *h" "int type" "u_int32_t value"
.Ft int
.Fn rad_put_string "struct rad_handle *h" "int type" "const char *str"
.Ft int
.Fn rad_send_request "struct rad_handle *h"
.Ft const char *
.Fn rad_strerror "struct rad_handle *h"
.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.
.Sh INITIALIZATION
To use the library, an application must first call
.Fn rad_open
to obtain a
.Va struct rad_handle * ,
which provides the context for subsequent operations.
Calls to
.Fn rad_open
always succeed unless insufficient virtual memory is available. If
the necessary memory cannot be allocated,
.Fn rad_open
returns
.Dv NULL .
.Pp
Before issuing any RADIUS requests, the library must be made aware
of the servers it can contact. The easiest way to configure the
library is to call
.Fn rad_config .
.Fn rad_config
causes the library to read a configuration file whose format is
described in
.Xr radius.conf 5 .
The pathname of the configuration file is passed as the
.Va file
argument to
.Fn rad_config .
This argument may also be given as
.Dv NULL ,
in which case the standard configuration file
.Pa /etc/radius.conf
is used.
.Fn rad_config
returns 0 on success, or -1 if an error occurs.
.Pp
The library can also be configured programmatically by calls to
.Fn rad_add_server .
The
.Va host
parameter specifies the server host, either as a fully qualified
domain name or as a dotted-quad IP address in text form.
The
.Va port
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
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
.Va secret
parameter.
It may be any NUL-terminated string of bytes. The RADIUS protocol
ignores all but the leading 128 bytes of the shared secret.
The timeout for receiving replies from the server is passed to the
.Va timeout
parameter, in units of seconds. The maximum number of repeated
requests to make before giving up is passed into the
.Va max_tries
parameter.
.Fn rad_add_server
returns 0 on success, or -1 if an error occurs.
.Pp
.Fn rad_add_server
may be called multiple times, and it may be used together with
.Fn rad_config .
At most 10 servers may be specified.
When multiple servers are given, they are tried in round-robin
fashion until a valid response is received, or until each server's
.Va max_tries
limit has been reached.
.Sh CREATING A RADIUS REQUEST
A RADIUS request consists of a code specifying the kind of request,
and zero or more attributes which provide additional information. To
begin constructing a new request, call
.Fn rad_create_request .
In addition to the usual
.Va struct rad_handle * ,
this function takes a
.Va code
parameter which specifies the type of the request. Most often this
will be
.Dv RAD_ACCESS_REQUEST .
.Fn rad_create_request
returns 0 on success, or -1 on if an error occurs.
.Pp
After the request has been created with
.Fn rad_create request ,
attributes can be attached to it. This is done through calls to
.Fn rad_put_addr ,
.Fn rad_put_int ,
and
.Fn rad_put_string .
Each accepts a
.Va type
parameter identifying the attribute, and a value which may be
an Internet address, an integer, or a NUL-terminated string,
respectively.
.Pp
The library also provides a function
.Fn rad_put_attr
which can be used to supply a raw, uninterpreted attribute. The
.Va data
argument points to an array of bytes, and the
.Va len
argument specifies its length.
.Pp
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,
retrying the defined servers in round-robin fashion as necessary.
If a valid response is received,
.Fn rad_send_request
returns the RADIUS code which specifies the type of the response.
This will typically be
.Dv RAD_ACCESS_ACCEPT ,
.Dv RAD_ACCESS_REJECT ,
or
.Dv RAD_ACCESS_CHALLENGE .
If no valid response is received,
.Fn rad_send_request
returns -1.
.Pp
Like RADIUS requests, each response may contain zero or more
attributes. After a response has been received successfully by
.Fn rad_send_request ,
its attributes can be extracted one by one using
.Fn rad_get_attr .
Each time
.Fn rad_get_attr
is called, it gets the next attribute from the current response, and
stores a pointer to the data and the length of the data via the
reference parameters
.Va data
and
.Va len ,
respectively. Note that the data resides in the response itself,
and must not be modified.
A successful call to
.Fn rad_get_attr
returns the RADIUS attribute type.
If no more attributes remain in the current response,
.Fn rad_get_attr
returns 0.
If an error such as a malformed attribute is detected, -1 is
returned.
.Pp
The common types of attributes can be decoded using
.Fn rad_cvt_addr ,
.Fn rad_cvt_int ,
and
.Fn rad_cvt_string .
These functions accept a pointer to the attribute data, which should
have been obtained using
.Fn rad_get_attr .
In the case of
.Fn rad_cvt_string ,
the length
.Va len
must also be given. These functions interpret the attribute as an
Internet address, an integer, or a string, respectively, and return
its value.
.Fn rad_cvt_string
returns its value as a NUL-terminated string in dynamically
allocated memory. The application should free the string using
.Xr free 3
when it is no longer needed.
.Pp
If insufficient virtual memory is available,
.Fn rad_cvt_string
returns
.Dv NULL .
.Fn rad_cvt_addr
and
.Fn rad_cvt_int
cannot fail.
.Sh OBTAINING ERROR MESSAGES
Those functions which accept a
.Va struct rad_handle *
argument record an error message if they fail. The error message
can be retrieved by calling
.Fn rad_strerror .
The message text is overwritten on each new error for the given
.Va struct rad_handle * .
Thus the message must be copied if it is to be preserved through
subsequent library calls using the same handle.
.Sh CLEANUP
To free the resources used by the RADIUS library, call
.Fn rad_close .
.Sh RETURN VALUES
The following functions return a non-negative value on success. If
they detect an error, they return -1 and record an error message
which can be retrieved using
.Fn rad_strerror .
.Pp
.Bl -item -offset indent -compact
.It
.Fn rad_add_server
.It
.Fn rad_config
.It
.Fn rad_create_request
.It
.Fn rad_get_attr
.It
.Fn rad_put_addr
.It
.Fn rad_put_attr
.It
.Fn rad_put_int
.It
.Fn rad_put_string
.It
.Fn rad_send_request
.El
.Pp
The following functions return a
.No non- Ns Dv NULL
pointer on success. If they are unable to allocate sufficient
virtual memory, they return
.Dv NULL ,
without recording an error message.
.Pp
.Bl -item -offset indent -compact
.It
.Fn rad_cvt_string
.It
.Fn rad_open
.El
.Sh FILES
.Pa /etc/radius.conf
.Sh SEE ALSO
.Xr radius.conf 5
.Rs
.%A C. Rigney, et al
.%T Remote Authentication Dial In User Service (RADIUS)
.%O RFC 2138
.Re
.Sh AUTHORS
This software was written by
.An John Polstra ,
and donated to the FreeBSD project by Juniper Networks, Inc.

123
lib/libradius/radius.conf.5 Normal file
View File

@ -0,0 +1,123 @@
.\" Copyright 1998 Juniper Networks, Inc.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd July 29, 1998
.Dt RADIUS.CONF 5
.Os FreeBSD
.Sh NAME
.Nm radius.conf
.Nd RADIUS client configuration file
.Sh SYNOPSIS
.Pa /etc/radius.conf
.Sh DESCRIPTION
.Nm
contains the information necessary to configure the RADIUS client
library. It is parsed by
.Xr rad_config 3 .
The file contains one or more lines of text, each describing a
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
.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,
in which case it may contain white space and/or begin with the
.Ql #
character. Within a quoted string, the double quote character can
be represented by
.Ql \e\&" ,
and the backslash can be represented by
.Ql \e\e .
No other escape sequences are supported.
.Pp
The first 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
service in the
.Pa /etc/services
file, or to the standard RADIUS port 1812 if there is no such entry in
.Pa /etc/services .
.Pp
The second 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
any length, but the RADIUS protocol uses only the first 128
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
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
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
round-robin fashion, until a valid response is received or the
maximum number of tries has been reached for all servers.
.Pp
The standard location for this file is
.Pa /etc/radius.conf .
But an alternate pathname may be specified in the call to
.Xr rad_config 3 .
Since the file contains sensitive information in the form of the
shared secrets, it should not be readable except by root.
.Sh FILES
.Pa /etc/radius.conf
.Sh EXAMPLES
.Bd -literal
# A simple entry using all the defaults:
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
# A server specified by its IP address:
192.168.27.81 $X*#..38947ax-+=
.Ed
.Sh SEE ALSO
.Xr libradius 3
.Rs
.%A C. Rigney, et al
.%T Remote Authentication Dial In User Service (RADIUS)
.%O RFC 2138
.Re
.Sh AUTHORS
This documentation was written by
.An John Polstra ,
and donated to the FreeBSD project by Juniper Networks, Inc.

737
lib/libradius/radlib.c Normal file
View File

@ -0,0 +1,737 @@
/*-
* Copyright 1998 Juniper Networks, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <md5.h>
#include <netdb.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "radlib_private.h"
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 int is_valid_response(struct rad_handle *, int,
const struct sockaddr_in *);
static int put_password_attr(struct rad_handle *, int,
const void *, size_t);
static int put_raw_attr(struct rad_handle *, int,
const void *, size_t);
static int split(char *, char *[], int, char *, size_t);
static void
clear_password(struct rad_handle *h)
{
if (h->pass_len != 0) {
memset(h->pass, 0, h->pass_len);
h->pass_len = 0;
h->pass_pos = 0;
}
}
static void
generr(struct rad_handle *h, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vsnprintf(h->errmsg, ERRSIZE, format, ap);
va_end(ap);
}
static void
insert_scrambled_password(struct rad_handle *h, int srv)
{
MD5_CTX ctx;
unsigned char md5[16];
const struct rad_server *srvp;
int padded_len;
int pos;
srvp = &h->servers[srv];
padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf;
memcpy(md5, &h->request[POS_AUTH], LEN_AUTH);
for (pos = 0; pos < padded_len; pos += 16) {
int i;
/* Calculate the new scrambler */
MD5Init(&ctx);
MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
MD5Update(&ctx, md5, 16);
MD5Final(md5, &ctx);
/*
* Mix in the current chunk of the password, and copy
* the result into the right place in the request. Also
* modify the scrambler in place, since we will use this
* in calculating the scrambler for next time.
*/
for (i = 0; i < 16; i++)
h->request[h->pass_pos + pos + i] =
md5[i] ^= h->pass[pos + i];
}
}
/*
* Return true if the current response is valid for a request to the
* specified server.
*/
static int
is_valid_response(struct rad_handle *h, int srv,
const struct sockaddr_in *from)
{
MD5_CTX ctx;
unsigned char md5[16];
const struct rad_server *srvp;
int len;
srvp = &h->servers[srv];
/* Check the source address */
if (from->sin_family != srvp->addr.sin_family ||
from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr ||
from->sin_port != srvp->addr.sin_port)
return 0;
/* Check the message length */
if (h->resp_len < POS_ATTRS)
return 0;
len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1];
if (len > h->resp_len)
return 0;
/* Check the response authenticator */
MD5Init(&ctx);
MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - POS_CODE);
MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH);
MD5Update(&ctx, &h->response[POS_ATTRS], len - POS_ATTRS);
MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
MD5Final(md5, &ctx);
if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0)
return 0;
return 1;
}
static int
put_password_attr(struct rad_handle *h, int type, const void *value, size_t len)
{
int padded_len;
int pad_len;
if (h->pass_pos != 0) {
generr(h, "Multiple User-Password attributes specified");
return -1;
}
if (len > PASSSIZE)
len = PASSSIZE;
padded_len = len == 0 ? 16 : (len+15) & ~0xf;
pad_len = padded_len - len;
/*
* Put in a place-holder attribute containing all zeros, and
* remember where it is so we can fill it in later.
*/
clear_password(h);
put_raw_attr(h, type, h->pass, padded_len);
h->pass_pos = h->req_len - padded_len;
/* Save the cleartext password, padded as necessary */
memcpy(h->pass, value, len);
h->pass_len = len;
memset(h->pass + len, 0, pad_len);
return 0;
}
static int
put_raw_attr(struct rad_handle *h, int type, const void *value, size_t len)
{
if (len > 253) {
generr(h, "Attribute too long");
return -1;
}
if (h->req_len + 2 + len > MSGSIZE) {
generr(h, "Maximum message length exceeded");
return -1;
}
h->request[h->req_len++] = type;
h->request[h->req_len++] = len + 2;
memcpy(&h->request[h->req_len], value, len);
h->req_len += len;
return 0;
}
int
rad_add_server(struct rad_handle *h, const char *host, int port,
const char *secret, int timeout, int tries)
{
struct rad_server *srvp;
if (h->num_servers >= MAXSERVERS) {
generr(h, "Too many RADIUS servers specified");
return -1;
}
srvp = &h->servers[h->num_servers];
memset(&srvp->addr, 0, sizeof srvp->addr);
srvp->addr.sin_len = sizeof srvp->addr;
srvp->addr.sin_family = AF_INET;
if (!inet_aton(host, &srvp->addr.sin_addr)) {
struct hostent *hent;
if ((hent = gethostbyname(host)) == NULL) {
generr(h, "%s: host not found", host);
return -1;
}
memcpy(&srvp->addr.sin_addr, hent->h_addr,
sizeof srvp->addr.sin_addr);
}
if (port != 0)
srvp->addr.sin_port = htons(port);
else {
struct servent *sent;
srvp->addr.sin_port =
(sent = getservbyname("radius", "udp")) != NULL ?
sent->s_port : htons(RADIUS_PORT);
}
if ((srvp->secret = strdup(secret)) == NULL) {
generr(h, "Out of memory");
return -1;
}
srvp->timeout = timeout;
srvp->max_tries = tries;
srvp->num_tries = 0;
h->num_servers++;
return 0;
}
void
rad_close(struct rad_handle *h)
{
int srv;
if (h->fd != -1)
close(h->fd);
for (srv = 0; srv < h->num_servers; srv++) {
memset(h->servers[srv].secret, 0,
strlen(h->servers[srv].secret));
free(h->servers[srv].secret);
}
clear_password(h);
free(h);
}
int
rad_config(struct rad_handle *h, const char *path)
{
FILE *fp;
char buf[MAXCONFLINE];
int linenum;
int retval;
if (path == NULL)
path = PATH_RADIUS_CONF;
if ((fp = fopen(path, "r")) == NULL) {
generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
return -1;
}
retval = 0;
linenum = 0;
while (fgets(buf, sizeof buf, fp) != NULL) {
int len;
char *fields[4];
int nfields;
char msg[ERRSIZE];
char *host;
char *port_str;
char *secret;
char *timeout_str;
char *maxtries_str;
char *end;
unsigned long timeout;
unsigned long maxtries;
int port;
linenum++;
len = strlen(buf);
/* We know len > 0, else fgets would have returned NULL. */
if (buf[len - 1] != '\n') {
if (len == sizeof buf - 1)
generr(h, "%s:%d: line too long", path,
linenum);
else
generr(h, "%s:%d: missing newline", path,
linenum);
retval = -1;
break;
}
buf[len - 1] = '\0';
/* Extract the fields from the line. */
nfields = split(buf, fields, 4, msg, sizeof msg);
if (nfields == -1) {
generr(h, "%s:%d: %s", path, linenum, msg);
retval = -1;
break;
}
if (nfields == 0)
continue;
if (nfields < 2) {
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];
/* Parse and validate the fields. */
host = strtok(host, ":");
port_str = strtok(NULL, ":");
if (port_str != NULL) {
port = strtoul(port_str, &end, 10);
if (*end != '\0') {
generr(h, "%s:%d: invalid port", path,
linenum);
retval = -1;
break;
}
} else
port = 0;
if (timeout_str != NULL) {
timeout = strtoul(timeout_str, &end, 10);
if (*end != '\0') {
generr(h, "%s:%d: invalid timeout", path,
linenum);
retval = -1;
break;
}
} else
timeout = TIMEOUT;
if (maxtries_str != NULL) {
maxtries = strtoul(maxtries_str, &end, 10);
if (*end != '\0') {
generr(h, "%s:%d: invalid maxtries", path,
linenum);
retval = -1;
break;
}
} else
maxtries = MAXTRIES;
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;
break;
}
}
/* Clear out the buffer to wipe a possible copy of a shared secret */
memset(buf, 0, sizeof buf);
fclose(fp);
return retval;
}
int
rad_create_request(struct rad_handle *h, int code)
{
int i;
h->request[POS_CODE] = code;
h->request[POS_IDENT] = ++h->ident;
/* Create a random authenticator */
for (i = 0; i < LEN_AUTH; i += 2) {
long r;
r = random();
h->request[POS_AUTH+i] = r;
h->request[POS_AUTH+i+1] = r >> 8;
}
h->req_len = POS_ATTRS;
clear_password(h);
return 0;
}
struct in_addr
rad_cvt_addr(const void *data)
{
struct in_addr value;
memcpy(&value.s_addr, data, sizeof value.s_addr);
return value;
}
u_int32_t
rad_cvt_int(const void *data)
{
u_int32_t value;
memcpy(&value, data, sizeof value);
return ntohl(value);
}
char *
rad_cvt_string(const void *data, size_t len)
{
char *s;
s = malloc(len + 1);
if (s != NULL) {
memcpy(s, data, len);
s[len] = '\0';
}
return s;
}
/*
* Returns the attribute type. If none are left, returns 0. On failure,
* returns -1.
*/
int
rad_get_attr(struct rad_handle *h, const void **value, size_t *len)
{
int type;
if (h->resp_pos >= h->resp_len)
return 0;
if (h->resp_pos + 2 > h->resp_len) {
generr(h, "Malformed attribute in response");
return -1;
}
type = h->response[h->resp_pos++];
*len = h->response[h->resp_pos++] - 2;
if (h->resp_pos + *len > h->resp_len) {
generr(h, "Malformed attribute in response");
return -1;
}
*value = &h->response[h->resp_pos];
h->resp_pos += *len;
return type;
}
/*
* Create and initialize a rad_handle structure, and return it to the
* caller. Can fail only if the necessary memory cannot be allocated.
* In that case, it returns NULL.
*/
struct rad_handle *
rad_open(void)
{
struct rad_handle *h;
h = (struct rad_handle *)malloc(sizeof(struct rad_handle));
if (h != NULL) {
srandomdev();
h->fd = -1;
h->num_servers = 0;
h->ident = random();
h->errmsg[0] = '\0';
memset(h->pass, 0, sizeof h->pass);
h->pass_len = 0;
h->pass_pos = 0;
}
return h;
}
int
rad_put_addr(struct rad_handle *h, int type, struct in_addr addr)
{
return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_addr);
}
int
rad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
{
return type == RAD_USER_PASSWORD ?
put_password_attr(h, type, value, len) :
put_raw_attr(h, type, value, len);
}
int
rad_put_int(struct rad_handle *h, int type, u_int32_t value)
{
u_int32_t nvalue;
nvalue = htonl(value);
return rad_put_attr(h, type, &nvalue, sizeof nvalue);
}
int
rad_put_string(struct rad_handle *h, int type, const char *str)
{
return rad_put_attr(h, type, str, strlen(str));
}
/*
* Returns the response type code on success, or -1 on failure.
*/
int
rad_send_request(struct rad_handle *h)
{
int total_tries;
int try;
int srv;
int n;
int got_valid_response;
/* 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) {
generr(h, "No User-Password attribute 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 */
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;
}
}
/* Compute a new timeout */
gettimeofday(&tv, NULL);
timersub(&timelimit, &tv, &tv);
if (tv.tv_sec < 0) /* Still poll once more */
timerclear(&tv);
}
if (got_valid_response)
break;
/* Advance to the next server */
if (++srv >= h->num_servers)
srv = 0;
}
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 *
rad_strerror(struct rad_handle *h)
{
return h->errmsg;
}
/*
* Destructively split a string into fields separated by white space.
* `#' at the beginning of a field begins a comment that extends to the
* end of the string. Fields may be quoted with `"'. Inside quoted
* strings, the backslash escapes `\"' and `\\' are honored.
*
* Pointers to up to the first maxfields fields are stored in the fields
* array. Missing fields get NULL pointers.
*
* The return value is the actual number of fields parsed, and is always
* <= maxfields.
*
* On a syntax error, places a message in the msg string, and returns -1.
*/
static int
split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
{
char *p;
int i;
static const char ws[] = " \t";
for (i = 0; i < maxfields; i++)
fields[i] = NULL;
p = str;
i = 0;
while (*p != '\0') {
p += strspn(p, ws);
if (*p == '#' || *p == '\0')
break;
if (i >= maxfields) {
snprintf(msg, msglen, "line has too many fields");
return -1;
}
if (*p == '"') {
char *dst;
dst = ++p;
fields[i] = dst;
while (*p != '"') {
if (*p == '\\') {
p++;
if (*p != '"' && *p != '\\' &&
*p != '\0') {
snprintf(msg, msglen,
"invalid `\\' escape");
return -1;
}
}
if (*p == '\0') {
snprintf(msg, msglen,
"unterminated quoted string");
return -1;
}
*dst++ = *p++;
}
*dst = '\0';
p++;
if (*fields[i] == '\0') {
snprintf(msg, msglen,
"empty quoted string not permitted");
return -1;
}
if (*p != '\0' && strspn(p, ws) == 0) {
snprintf(msg, msglen, "quoted string not"
" followed by white space");
return -1;
}
} else {
fields[i] = p;
p += strcspn(p, ws);
if (*p != '\0')
*p++ = '\0';
}
i++;
}
return i;
}

125
lib/libradius/radlib.h Normal file
View File

@ -0,0 +1,125 @@
/*-
* Copyright 1998 Juniper Networks, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _RADLIB_H_
#define _RADLIB_H_
#include <sys/types.h>
#include <netinet/in.h>
/* Message types */
#define RAD_ACCESS_REQUEST 1
#define RAD_ACCESS_ACCEPT 2
#define RAD_ACCESS_REJECT 3
#define RAD_ACCESS_CHALLENGE 11
/* Attribute types and values */
#define RAD_USER_NAME 1 /* String */
#define RAD_USER_PASSWORD 2 /* String */
#define RAD_CHAP_PASSWORD 3 /* String */
#define RAD_NAS_IP_ADDRESS 4 /* IP address */
#define RAD_NAS_PORT 5 /* Integer */
#define RAD_SERVICE_TYPE 6 /* Integer */
#define RAD_LOGIN 1
#define RAD_FRAMED 2
#define RAD_CALLBACK_LOGIN 3
#define RAD_CALLBACK_FRAMED 4
#define RAD_OUTBOUND 5
#define RAD_ADMINISTRATIVE 6
#define RAD_NAS_PROMPT 7
#define RAD_AUTHENTICATE_ONLY 8
#define RAD_CALLBACK_NAS_PROMPT 9
#define RAD_FRAMED_PROTOCOL 7 /* Integer */
#define RAD_PPP 1
#define RAD_SLIP 2
#define RAD_ARAP 3 /* Appletalk */
#define RAD_GANDALF 4
#define RAD_XYLOGICS 5
#define RAD_FRAMED_IP_ADDRESS 8 /* IP address */
#define RAD_FRAMED_IP_NETMASK 9 /* IP address */
#define RAD_FRAMED_ROUTING 10 /* Integer */
#define RAD_FILTER_ID 11 /* String */
#define RAD_FRAMED_MTU 12 /* Integer */
#define RAD_FRAMED_COMPRESSION 13 /* Integer */
#define RAD_LOGIN_IP_HOST 14 /* IP address */
#define RAD_LOGIN_SERVICE 15 /* Integer */
#define RAD_LOGIN_TCP_PORT 16 /* Integer */
/* unassiged 17 */
#define RAD_REPLY_MESSAGE 18 /* String */
#define RAD_CALLBACK_NUMBER 19 /* String */
#define RAD_CALLBACK_ID 20 /* String */
/* unassiged 21 */
#define RAD_FRAMED_ROUTE 22 /* String */
#define RAD_FRAMED_IPX_NETWORK 23 /* IP address */
#define RAD_STATE 24 /* String */
#define RAD_CLASS 25 /* Integer */
#define RAD_VENDOR_SPECIFIC 26 /* Integer */
#define RAD_SESSION_TIMEOUT 27 /* Integer */
#define RAD_IDLE_TIMEOUT 28 /* Integer */
#define RAD_TERMINATION_ACTION 29 /* Integer */
#define RAD_CALLED_STATION_ID 30 /* String */
#define RAD_CALLING_STATION_ID 31 /* String */
#define RAD_NAS_IDENTIFIER 32 /* Integer */
#define RAD_PROXY_STATE 33 /* Integer */
#define RAD_LOGIN_LAT_SERVICE 34 /* Integer */
#define RAD_LOGIN_LAT_NODE 35 /* Integer */
#define RAD_LOGIN_LAT_GROUP 36 /* Integer */
#define RAD_FRAMED_APPLETALK_LINK 37 /* Integer */
#define RAD_FRAMED_APPLETALK_NETWORK 38 /* Integer */
#define RAD_FRAMED_APPLETALK_ZONE 39 /* Integer */
/* reserved for accounting 40-59 */
#define RAD_CHAP_CHALLENGE 60 /* String */
#define RAD_NAS_PORT_TYPE 61 /* Integer */
#define RAD_PORT_LIMIT 62 /* Integer */
#define RAD_LOGIN_LAT_PORT 63 /* Integer */
struct rad_handle;
__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_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 *);
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,
const void *, size_t);
int rad_put_int(struct rad_handle *, int, u_int32_t);
int rad_put_string(struct rad_handle *, int,
const char *);
int rad_send_request(struct rad_handle *);
const char *rad_strerror(struct rad_handle *);
__END_DECLS
#endif /* _RADLIB_H_ */

View File

@ -0,0 +1,82 @@
/*-
* Copyright 1998 Juniper Networks, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef RADLIB_PRIVATE_H
#define RADLIB_PRIVATE_H
#include <sys/types.h>
#include <netinet/in.h>
#include "radlib.h"
/* Defaults */
#define MAXTRIES 3
#define PATH_RADIUS_CONF "/etc/radius.conf"
#define RADIUS_PORT 1812
#define TIMEOUT 3 /* In seconds */
/* Limits */
#define ERRSIZE 128 /* Maximum error message length */
#define MAXCONFLINE 1024 /* Maximum config file line length */
#define MAXSERVERS 10 /* Maximum number of servers to try */
#define MSGSIZE 4096 /* Maximum RADIUS message */
#define PASSSIZE 128 /* Maximum significant password chars */
/* Positions of fields in RADIUS messages */
#define POS_CODE 0 /* Message code */
#define POS_IDENT 1 /* Identifier */
#define POS_LENGTH 2 /* Message length */
#define POS_AUTH 4 /* Authenticator */
#define LEN_AUTH 16 /* Length of authenticator */
#define POS_ATTRS 20 /* Start of attributes */
struct rad_server {
struct sockaddr_in addr; /* Address of server */
char *secret; /* Shared secret */
int timeout; /* Timeout in seconds */
int max_tries; /* Number of tries before giving up */
int num_tries; /* Number of tries so far */
};
struct rad_handle {
int fd; /* Socket file descriptor */
struct rad_server servers[MAXSERVERS]; /* Servers to contact */
int num_servers; /* Number of valid server entries */
int ident; /* Current identifier value */
char errmsg[ERRSIZE]; /* Most recent error message */
unsigned char request[MSGSIZE]; /* Request to send */
int req_len; /* Length of request */
char pass[PASSSIZE]; /* Cleartext password */
int pass_len; /* Length of cleartext password */
int pass_pos; /* Position of scrambled password */
unsigned char response[MSGSIZE]; /* Response received */
int resp_len; /* Length of response */
int resp_pos; /* Current position scanning attrs */
};
#endif