- Added rad_demangle() for demangling user-passwords (needed for

MS-CHAPv1 MPPE-keys).
- Added rad_demangle_mppe_key() for demangling mppe-keys (needed
  for MPPE-keys).
- Added some typecasts for avoiding compiler warnings.
- Fix: better handle wrong usage of the lib (if the programmer
  has not called rad_create_request() but rad_put_*(), then a
  weird error message was returned).
- Added a new function for putting the Message-Authenticator.
- Verify the Message-Authenticator, if it was found inside a
  response packet and silently drop the packet, if the validation
  failed.
- Implicitly put the Message-Authenticator, if the EAP-Message
  attribute was added.
- Added some missing defines.

Submitted by:	Michael Bretterklieber
PR:		46555
This commit is contained in:
Ruslan Ermilov 2004-04-27 15:00:29 +00:00
parent d5ac36eda7
commit b4b831ef39
7 changed files with 421 additions and 33 deletions

View File

@ -901,7 +901,7 @@ _prebuild_libs+= lib/libcom_err lib/libcrypt lib/libexpat \
lib/libsbuf lib/libtacplus lib/libutil lib/libypclnt \
lib/libz lib/msun
lib/libopie__L lib/libradius__L lib/libtacplus__L: lib/libmd__L
lib/libopie__L lib/libtacplus__L: lib/libmd__L
lib/libypclnt__L: lib/librpcsvc__L
_generic_libs+= lib
@ -909,6 +909,7 @@ _generic_libs+= lib
.if !defined(NOCRYPT)
.if !defined(NO_OPENSSL)
_prebuild_libs+= secure/lib/libcrypto secure/lib/libssl
lib/libradius__L: secure/lib/libssl__L
.if !defined(NO_OPENSSH)
_prebuild_libs+= secure/lib/libssh
secure/lib/libssh__L: secure/lib/libcrypto__L lib/libz__L
@ -917,6 +918,10 @@ secure/lib/libssh__L: secure/lib/libcrypto__L lib/libz__L
_generic_libs+= secure/lib
.endif
.if defined(NOCRYPT) || defined(NO_OPENSSL)
lib/libradius__L: lib/libmd__L
.endif
_generic_libs+= usr.bin/lex/lib
.if ${MACHINE_ARCH} == "i386"

View File

@ -22,15 +22,22 @@
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
# $FreeBSD$
LIB= radius
SRCS= radlib.c
INCS= radlib.h radlib_vs.h
CFLAGS+= -Wall
DPADD= ${LIBMD}
LDADD= -lmd
SHLIB_MAJOR= 1
MAN= libradius.3 radius.conf.5
.if defined(NOCRYPT) || defined(NO_OPENSSL)
DPADD= ${LIBMD}
LDADD= -lmd
.else
DPADD= ${LIBSSL}
LDADD= -lssl
CFLAGS+= -DWITH_SSL
.endif
.include <bsd.lib.mk>

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 12, 2002
.Dd April 27, 2004
.Dt LIBRADIUS 3
.Os
.Sh NAME
@ -67,6 +67,8 @@
.Ft int
.Fn rad_put_string "struct rad_handle *h" "int type" "const char *str"
.Ft int
.Fn rad_put_message_authentic "struct rad_handle *h"
.Ft int
.Fn rad_put_vendor_addr "struct rad_handle *h" "int vendor" "int type" "struct in_addr addr"
.Ft int
.Fn rad_put_vendor_attr "struct rad_handle *h" "int vendor" "int type" "const void *data" "size_t len"
@ -80,6 +82,10 @@
.Fn rad_send_request "struct rad_handle *h"
.Ft "const char *"
.Fn rad_server_secret "struct rad_handle *h"
.Ft u_char *
.Fn rad_demangle "struct rad_handle *h" "const void *mangled" "size_t mlen"
.Ft u_char *
.Fn rad_demangle_mppe_key "struct rad_handle *h" "const void *mangled" "size_t mlen" "size_t *len"
.Ft "const char *"
.Fn rad_strerror "struct rad_handle *h"
.Sh DESCRIPTION
@ -87,7 +93,7 @@ The
.Nm
library implements the client side of the Remote Authentication Dial
In User Service (RADIUS).
RADIUS, defined in RFCs 2138 and 2139,
RADIUS, defined in RFCs 2865 and 2866,
allows clients to perform authentication and accounting by means of
network requests to remote servers.
.Ss Initialization
@ -232,6 +238,22 @@ argument points to an array of bytes, and the
.Fa len
argument specifies its length.
.Pp
It is possible adding the Message-Authenticator to the request.
This is an HMAC-MD5 hash of the entire Access-Request packet (see RFC 3579).
This attribute must be present in any packet that includes an EAP-Message
attribute.
It can be added by using the
.Fn rad_put_message_authentic
function.
The
.Nm
library
calculates the HMAC-MD5 hash implicitly before sending the request.
If the Message-Authenticator was found inside the response packet,
then the packet is silently dropped, if the validation failed.
In order to get this feature, the library should be compiled with
OpenSSL support.
.Pp
The
.Fn rad_put_X
functions return 0 on success, or \-1 if an error occurs.
@ -395,6 +417,26 @@ The
.Fn rad_server_secret
returns the secret shared with the current RADIUS server according to the
supplied rad_handle.
.Pp
The
.Fn rad_demangle
function demangles attributes containing passwords and MS-CHAPv1 MPPE-Keys.
The return value is
.Dv NULL
on failure, or the plaintext attribute.
This value should be freed using
.Xr free 3
when it is no longer needed.
.Pp
The
.Fn rad_demangle_mppe_key
function demangles the send- and recv-keys when using MPPE (see RFC 2548).
The return value is
.Dv NULL
on failure, or the plaintext attribute.
This value should be freed using
.Xr free 3
when it is no longer needed.
.Ss Obtaining Error Messages
Those functions which accept a
.Vt "struct rad_handle *"
@ -434,6 +476,8 @@ which can be retrieved using
.It
.Fn rad_put_string
.It
.Fn rad_put_message_authentic
.It
.Fn rad_init_send_request
.It
.Fn rad_continue_send_request
@ -457,6 +501,20 @@ without recording an error message.
.It
.Fn rad_cvt_string
.El
.Pp
The following functions return a
.No non- Ns Dv NULL
pointer on success.
If they fail, they return
.Dv NULL ,
with recording an error message.
.Pp
.Bl -item -offset indent -compact
.It
.Fn rad_demangle
.It
.Fn rad_demangle_mppe_key
.El
.Sh FILES
.Bl -tag -width indent
.It Pa /etc/radius.conf
@ -466,12 +524,22 @@ without recording an error message.
.Rs
.%A "C. Rigney, et al"
.%T "Remote Authentication Dial In User Service (RADIUS)"
.%O "RFC 2138"
.%O "RFC 2865"
.Re
.Rs
.%A "C. Rigney"
.%T "RADIUS Accounting"
.%O "RFC 2139"
.%O "RFC 2866"
.Re
.Rs
.%A G. Zorn
.%T "Microsoft Vendor-specific RADIUS attributes"
.%O RFC 2548
.Re
.Rs
.%A C. Rigney, et al
.%T "RADIUS extensions"
.%O RFC 2869
.Re
.Sh AUTHORS
.An -nosplit
@ -483,3 +551,5 @@ project by Juniper Networks, Inc.
.An Oleg Semyonov
subsequently added the ability to perform RADIUS
accounting.
Later additions and changes by
.An Michael Bretterklieber .

View File

@ -32,9 +32,21 @@ __FBSDID("$FreeBSD$");
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef WITH_SSL
#include <openssl/hmac.h>
#include <openssl/md5.h>
#define MD5Init MD5_Init
#define MD5Update MD5_Update
#define MD5Final MD5_Final
#else
#define MD5_DIGEST_LENGTH 16
#include <md5.h>
#endif
/* We need the MPPE_KEY_LEN define */
#include <netgraph/ng_mppc.h>
#include <errno.h>
#include <md5.h>
#include <netdb.h>
#include <stdarg.h>
#include <stddef.h>
@ -50,6 +62,7 @@ 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 void insert_message_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,
@ -82,7 +95,7 @@ static void
insert_scrambled_password(struct rad_handle *h, int srv)
{
MD5_CTX ctx;
unsigned char md5[16];
unsigned char md5[MD5_DIGEST_LENGTH];
const struct rad_server *srvp;
int padded_len;
int pos;
@ -129,6 +142,31 @@ insert_request_authenticator(struct rad_handle *h, int srv)
MD5Final(&h->request[POS_AUTH], &ctx);
}
static void
insert_message_authenticator(struct rad_handle *h, int srv)
{
#ifdef WITH_SSL
u_char md[EVP_MAX_MD_SIZE];
u_int md_len;
const struct rad_server *srvp;
HMAC_CTX ctx;
srvp = &h->servers[srv];
if (h->authentic_pos != 0) {
HMAC_CTX_init(&ctx);
HMAC_Init(&ctx, srvp->secret, strlen(srvp->secret), EVP_md5());
HMAC_Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE);
HMAC_Update(&ctx, &h->request[POS_AUTH], LEN_AUTH);
HMAC_Update(&ctx, &h->request[POS_ATTRS],
h->req_len - POS_ATTRS);
HMAC_Final(&ctx, md, &md_len);
HMAC_CTX_cleanup(&ctx);
HMAC_cleanup(&ctx);
memcpy(&h->request[h->authentic_pos + 2], md, md_len);
}
#endif
}
/*
* Return true if the current response is valid for a request to the
* specified server.
@ -138,9 +176,14 @@ is_valid_response(struct rad_handle *h, int srv,
const struct sockaddr_in *from)
{
MD5_CTX ctx;
unsigned char md5[16];
unsigned char md5[MD5_DIGEST_LENGTH];
const struct rad_server *srvp;
int len;
#ifdef WITH_SSL
HMAC_CTX hctx;
u_char resp[MSGSIZE], md[EVP_MAX_MD_SIZE];
int pos, md_len;
#endif
srvp = &h->servers[srv];
@ -167,6 +210,44 @@ is_valid_response(struct rad_handle *h, int srv,
if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0)
return 0;
#ifdef WITH_SSL
/*
* For non accounting responses check the message authenticator,
* if any.
*/
if (h->response[POS_CODE] != RAD_ACCOUNTING_RESPONSE) {
memcpy(resp, h->response, MSGSIZE);
pos = POS_ATTRS;
/* Search and verify the Message-Authenticator */
while (pos < len - 2) {
if (h->response[pos] == RAD_MESSAGE_AUTHENTIC) {
/* zero fill the Message-Authenticator */
memset(&resp[pos + 2], 0, MD5_DIGEST_LENGTH);
HMAC_CTX_init(&hctx);
HMAC_Init(&hctx, srvp->secret,
strlen(srvp->secret), EVP_md5());
HMAC_Update(&hctx, &h->response[POS_CODE],
POS_AUTH - POS_CODE);
HMAC_Update(&hctx, &h->request[POS_AUTH],
LEN_AUTH);
HMAC_Update(&hctx, &resp[POS_ATTRS],
h->resp_len - POS_ATTRS);
HMAC_Final(&hctx, md, &md_len);
HMAC_CTX_cleanup(&hctx);
HMAC_cleanup(&hctx);
if (memcmp(md, &h->response[pos + 2],
MD5_DIGEST_LENGTH) != 0)
return 0;
break;
}
pos += h->response[pos + 1];
}
}
#endif
return 1;
}
@ -244,7 +325,7 @@ rad_add_server(struct rad_handle *h, const char *host, int port,
sizeof srvp->addr.sin_addr);
}
if (port != 0)
srvp->addr.sin_port = htons(port);
srvp->addr.sin_port = htons((u_short)port);
else {
struct servent *sent;
@ -482,6 +563,8 @@ rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
if (h->pass_pos != 0)
insert_scrambled_password(h, h->srv);
insert_message_authenticator(h, h->srv);
/* Send the request */
n = sendto(h->fd, h->request, h->req_len, 0,
(const struct sockaddr *)&h->servers[h->srv].addr,
@ -514,11 +597,12 @@ rad_create_request(struct rad_handle *h, int code)
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->request[POS_AUTH+i] = (u_char)r;
h->request[POS_AUTH+i+1] = (u_char)(r >> 8);
}
h->req_len = POS_ATTRS;
clear_password(h);
h->request_created = 1;
return 0;
}
@ -570,7 +654,7 @@ rad_get_attr(struct rad_handle *h, const void **value, size_t *len)
}
type = h->response[h->resp_pos++];
*len = h->response[h->resp_pos++] - 2;
if (h->resp_pos + *len > h->resp_len) {
if (h->resp_pos + (int)*len > h->resp_len) {
generr(h, "Malformed attribute in response");
return -1;
}
@ -612,18 +696,23 @@ rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
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");
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;
if (h->eap_msg == 0) {
/* 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;
}
}
}
@ -671,7 +760,10 @@ rad_auth_open(void)
h->pass_len = 0;
h->pass_pos = 0;
h->chap_pass = 0;
h->authentic_pos = 0;
h->type = RADIUS_AUTH;
h->request_created = 0;
h->eap_msg = 0;
}
return h;
}
@ -704,12 +796,41 @@ rad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
{
int result;
if (type == RAD_USER_PASSWORD)
if (!h->request_created) {
generr(h, "Please call rad_create_request()"
" before putting attributes");
return -1;
}
if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
if (type == RAD_EAP_MESSAGE) {
generr(h, "EAP-Message attribute is not valid"
" in accounting requests");
return -1;
}
}
/*
* When proxying EAP Messages, the Message Authenticator
* MUST be present; see RFC 3579.
*/
if (type == RAD_EAP_MESSAGE) {
if (rad_put_message_authentic(h) == -1)
return -1;
}
if (type == RAD_USER_PASSWORD) {
result = put_password_attr(h, type, value, len);
else {
} else if (type == RAD_MESSAGE_AUTHENTIC) {
result = rad_put_message_authentic(h);
} else {
result = put_raw_attr(h, type, value, len);
if (result == 0 && type == RAD_CHAP_PASSWORD)
h->chap_pass = 1;
if (result == 0) {
if (type == RAD_CHAP_PASSWORD)
h->chap_pass = 1;
else if (type == RAD_EAP_MESSAGE)
h->eap_msg = 1;
}
}
return result;
@ -730,6 +851,32 @@ rad_put_string(struct rad_handle *h, int type, const char *str)
return rad_put_attr(h, type, str, strlen(str));
}
int
rad_put_message_authentic(struct rad_handle *h)
{
#ifdef WITH_SSL
u_char md_zero[MD5_DIGEST_LENGTH];
if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
generr(h, "Message-Authenticator is not valid"
" in accounting requests");
return -1;
}
if (h->authentic_pos == 0) {
h->authentic_pos = h->req_len;
memset(md_zero, 0, sizeof(md_zero));
return (put_raw_attr(h, RAD_MESSAGE_AUTHENTIC, md_zero,
sizeof(md_zero)));
}
return 0;
#else
generr(h, "Message Authenticator not supported,"
" please recompile libradius with SSL support");
return -1;
#endif
}
/*
* Returns the response type code on success, or -1 on failure.
*/
@ -893,6 +1040,12 @@ rad_put_vendor_attr(struct rad_handle *h, int vendor, int type,
struct vendor_attribute *attr;
int res;
if (!h->request_created) {
generr(h, "Please call rad_create_request()"
" before putting attributes");
return -1;
}
if ((attr = malloc(len + 6)) == NULL) {
generr(h, "malloc failure (%d bytes)", len + 6);
return -1;
@ -940,6 +1093,138 @@ rad_request_authenticator(struct rad_handle *h, char *buf, size_t len)
return (LEN_AUTH);
}
u_char *
rad_demangle(struct rad_handle *h, const void *mangled, size_t mlen)
{
char R[LEN_AUTH];
const char *S;
int i, Ppos;
MD5_CTX Context;
u_char b[MD5_DIGEST_LENGTH], *C, *demangled;
if ((mlen % 16 != 0) || mlen > 128) {
generr(h, "Cannot interpret mangled data of length %lu",
(u_long)mlen);
return NULL;
}
C = (u_char *)mangled;
/* We need the shared secret as Salt */
S = rad_server_secret(h);
/* We need the request authenticator */
if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) {
generr(h, "Cannot obtain the RADIUS request authenticator");
return NULL;
}
demangled = malloc(mlen);
if (!demangled)
return NULL;
MD5Init(&Context);
MD5Update(&Context, S, strlen(S));
MD5Update(&Context, R, LEN_AUTH);
MD5Final(b, &Context);
Ppos = 0;
while (mlen) {
mlen -= 16;
for (i = 0; i < 16; i++)
demangled[Ppos++] = C[i] ^ b[i];
if (mlen) {
MD5Init(&Context);
MD5Update(&Context, S, strlen(S));
MD5Update(&Context, C, 16);
MD5Final(b, &Context);
}
C += 16;
}
return demangled;
}
u_char *
rad_demangle_mppe_key(struct rad_handle *h, const void *mangled,
size_t mlen, size_t *len)
{
char R[LEN_AUTH]; /* variable names as per rfc2548 */
const char *S;
u_char b[MD5_DIGEST_LENGTH], *demangled;
const u_char *A, *C;
MD5_CTX Context;
int Slen, i, Clen, Ppos;
u_char *P;
if (mlen % 16 != SALT_LEN) {
generr(h, "Cannot interpret mangled data of length %lu",
(u_long)mlen);
return NULL;
}
/* We need the RADIUS Request-Authenticator */
if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) {
generr(h, "Cannot obtain the RADIUS request authenticator");
return NULL;
}
A = (const u_char *)mangled; /* Salt comes first */
C = (const u_char *)mangled + SALT_LEN; /* Then the ciphertext */
Clen = mlen - SALT_LEN;
S = rad_server_secret(h); /* We need the RADIUS secret */
Slen = strlen(S);
P = alloca(Clen); /* We derive our plaintext */
MD5Init(&Context);
MD5Update(&Context, S, Slen);
MD5Update(&Context, R, LEN_AUTH);
MD5Update(&Context, A, SALT_LEN);
MD5Final(b, &Context);
Ppos = 0;
while (Clen) {
Clen -= 16;
for (i = 0; i < 16; i++)
P[Ppos++] = C[i] ^ b[i];
if (Clen) {
MD5Init(&Context);
MD5Update(&Context, S, Slen);
MD5Update(&Context, C, 16);
MD5Final(b, &Context);
}
C += 16;
}
/*
* The resulting plain text consists of a one-byte length, the text and
* maybe some padding.
*/
*len = *P;
if (*len > mlen - 1) {
generr(h, "Mangled data seems to be garbage %d %d",
*len, mlen-1);
return NULL;
}
if (*len > MPPE_KEY_LEN * 2) {
generr(h, "Key to long (%d) for me max. %d",
*len, MPPE_KEY_LEN * 2);
return NULL;
}
demangled = malloc(*len);
if (!demangled)
return NULL;
memcpy(demangled, P + 1, *len);
return demangled;
}
const char *
rad_server_secret(struct rad_handle *h)
{

View File

@ -32,6 +32,9 @@
#include <sys/types.h>
#include <netinet/in.h>
/* Limits */
#define RAD_MAX_ATTR_LEN 253
/* Message types */
#define RAD_ACCESS_REQUEST 1
#define RAD_ACCESS_ACCEPT 2
@ -98,6 +101,9 @@
#define RAD_FRAMED_APPLETALK_NETWORK 38 /* Integer */
#define RAD_FRAMED_APPLETALK_ZONE 39 /* Integer */
/* reserved for accounting 40-59 */
#define RAD_ACCT_INPUT_GIGAWORDS 52
#define RAD_ACCT_OUTPUT_GIGAWORDS 53
#define RAD_CHAP_CHALLENGE 60 /* String */
#define RAD_NAS_PORT_TYPE 61 /* Integer */
#define RAD_ASYNC 0
@ -123,6 +129,9 @@
#define RAD_PORT_LIMIT 62 /* Integer */
#define RAD_LOGIN_LAT_PORT 63 /* Integer */
#define RAD_CONNECT_INFO 77 /* String */
#define RAD_EAP_MESSAGE 79 /* Octets */
#define RAD_MESSAGE_AUTHENTIC 80 /* Octets */
#define RAD_ACCT_INTERIM_INTERVAL 85 /* Integer */
#define RAD_NAS_IPV6_ADDRESS 95 /* IPv6 address */
#define RAD_FRAMED_INTERFACE_ID 96 /* 8 octets */
#define RAD_FRAMED_IPV6_PREFIX 97 /* Octets */
@ -134,6 +143,7 @@
#define RAD_ACCT_STATUS_TYPE 40 /* Integer */
#define RAD_START 1
#define RAD_STOP 2
#define RAD_UPDATE 3
#define RAD_ACCOUNTING_ON 7
#define RAD_ACCOUNTING_OFF 8
#define RAD_ACCT_DELAY_TIME 41 /* Integer */
@ -196,11 +206,15 @@ int rad_put_attr(struct rad_handle *, int,
int rad_put_int(struct rad_handle *, int, u_int32_t);
int rad_put_string(struct rad_handle *, int,
const char *);
int rad_put_message_authentic(struct rad_handle *);
ssize_t rad_request_authenticator(struct rad_handle *, char *,
size_t);
int rad_send_request(struct rad_handle *);
const char *rad_server_secret(struct rad_handle *);
const char *rad_strerror(struct rad_handle *);
u_char *rad_demangle(struct rad_handle *, const void *,
size_t);
__END_DECLS
#endif /* _RADLIB_H_ */

View File

@ -76,11 +76,14 @@ struct rad_handle {
int ident; /* Current identifier value */
char errmsg[ERRSIZE]; /* Most recent error message */
unsigned char request[MSGSIZE]; /* Request to send */
char request_created; /* rad_create_request() called? */
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 */
char chap_pass; /* Have we got a CHAP_PASSWORD ? */
int authentic_pos; /* Position of message authenticator */
char eap_msg; /* Are we an EAP Proxy? */
unsigned char response[MSGSIZE]; /* Response received */
int resp_len; /* Length of response */
int resp_pos; /* Current position scanning attrs */

View File

@ -66,15 +66,19 @@
#define RAD_MICROSOFT_MS_SECONDARY_NBNS_SERVER 31
#define RAD_MICROSOFT_MS_ARAP_CHALLENGE 33
#define SALT_LEN 2
struct rad_handle;
__BEGIN_DECLS
int rad_get_vendor_attr(u_int32_t *, const void **, size_t *);
int rad_put_vendor_addr(struct rad_handle *, int, int, struct in_addr);
int rad_put_vendor_attr(struct rad_handle *, int, int, const void *,
int rad_get_vendor_attr(u_int32_t *, const void **, size_t *);
int rad_put_vendor_addr(struct rad_handle *, int, int, struct in_addr);
int rad_put_vendor_attr(struct rad_handle *, int, int, const void *,
size_t);
int rad_put_vendor_int(struct rad_handle *, int, int, u_int32_t);
int rad_put_vendor_string(struct rad_handle *, int, int, const char *);
int rad_put_vendor_int(struct rad_handle *, int, int, u_int32_t);
int rad_put_vendor_string(struct rad_handle *, int, int, const char *);
u_char *rad_demangle_mppe_key(struct rad_handle *, const void *, size_t,
size_t *);
__END_DECLS
#endif /* _RADLIB_VS_H_ */