/*
* radius.c
*
* Written by Michael Bretterklieber <michael@bretterklieber.com>
* Written by Brendan Bank <brendan@gnarst.net>
*/
#include "radius.h"
#include "pptp.h"
#include "pppoe.h"
#include "chap.h"
#include "ngfunc.h"
#include <sys/types.h>
#include <radlib.h>
#include <radlib_vs.h>
#include <md5.h>
/* Global variables */
static int RadiusSetCommand(int ac, char *av[], void *arg);
static int RadiusAddServer (short request_type);
static int RadiusInit(short request_type);
static void RadiusClose(void);
static const char * RadiusMPPEPolicyname(int policy);
static const char * RadiusMPPETypesname(int types);
/* compatibility functions until libradius has these builtin */
static int rad_demangle2(struct rad_handle *, const void *, size_t, u_char *);
static int rad_demangle_mppe_key2(struct rad_handle *, const void *, size_t, u_char *, size_t *);
/* Set menu options */
enum {
SET_SERVER,
SET_ME,
SET_TIMEOUT,
SET_RETRIES,
SET_CONFIG,
SET_UPDATE
};
/*
* GLOBAL VARIABLES
*/
const struct cmdtab RadiusSetCmds[] = {
{ "server <name> <secret> [auth port] [acct port]", "Set radius server parameters" ,
RadiusSetCommand, NULL, (void *) SET_SERVER },
{ "me <ip>", "Set NAS IP address" ,
RadiusSetCommand, NULL, (void *) SET_ME },
{ "timeout <seconds>", "Set timeout in seconds",
RadiusSetCommand, NULL, (void *) SET_TIMEOUT },
{ "retries <# retries>", "set number of retries",
RadiusSetCommand, NULL, (void *) SET_RETRIES },
{ "config <path to radius.conf>", "set path to config file for libradius",
RadiusSetCommand, NULL, (void *) SET_CONFIG },
{ "acct-update <seconds>", "set update interval",
RadiusSetCommand, NULL, (void *) SET_UPDATE },
{ NULL },
};
/* Set menu options */
static int
RadiusSetCommand(int ac, char *av[], void *arg)
{
static char function[] = "RadiusSetCommand";
RadConf const conf = &bund->radius.conf;
RadServe_Conf server;
RadServe_Conf t_server;
int val, count;
/* Log(LG_RADIUS, ("[%s] %s: started", lnk->name, function)); */
if (ac == 0)
return(-1);
switch ((int) arg) {
case SET_SERVER:
if (ac > 4 || ac < 2) {
return(-1);
}
count = 0;
for ( t_server = conf->server ; t_server ;
t_server = t_server->next) {
count++;
}
if (count > RADIUS_MAX_SERVERS) {
Log(LG_RADIUS, ("[%s] %s: cannot configure more than %d servers",
lnk->name, function, RADIUS_MAX_SERVERS));
return (-1);
}
server = Malloc(MB_RADIUS, sizeof(*server));
server->auth_port = 1812;
server->acct_port = 1813;
server->next = NULL;
if (strlen(av[0]) > 255) {
Log(LG_ERR, ("Hostname too long!. > 255 char."));
return(-1);
}
if (strlen(av[1]) > 127) {
Log(LG_ERR, ("Shared Secret too long! > 127 char."));
return(-1);
}
if (ac > 2 && atoi(av[2]) < 65535 && atoi(av[2]) > 1) {
server->auth_port = atoi (av[2]);
} else if ( ac > 2 ) {
Log(LG_ERR, ("Auth Port number too high > 65535"));
return(-1);
}
if (ac > 3 && atoi(av[3]) < 65535 && atoi(av[3]) > 1) {
server->acct_port = atoi (av[3]);
} else if ( ac > 3 ) {
Log(LG_ERR, ("Acct Port number too high > 65535"));
return(-1);
}
server->hostname = Malloc(MB_RADIUS, strlen(av[0]) + 1);
server->sharedsecret = Malloc(MB_RADIUS, strlen(av[1]) + 1);
sprintf(server->hostname, "%s" , av[0]);
sprintf(server->sharedsecret, "%s" , av[1]);
if (conf->server != NULL)
server->next = conf->server;
conf->server = server;
break;
case SET_ME:
val = inet_aton(*av, &(conf->radius_me));
if (val == 0)
Log(LG_ERR, ("Bad NAS address."));
break;
case SET_TIMEOUT:
val = atoi(*av);
if (val <= 0)
Log(LG_ERR, ("Timeout must be positive."));
else
conf->radius_timeout = val;
break;
case SET_UPDATE:
val = atoi(*av);
if (val <= 0)
Log(LG_ERR, ("Update interval must be positive."));
else
conf->acct_update = val;
break;
case SET_RETRIES:
val = atoi(*av);
if (val <= 0)
Log(LG_ERR, ("Retries must be positive."));
else
conf->radius_retries = val;
break;
case SET_CONFIG:
if (strlen(av[0]) > PATH_MAX)
Log(LG_ERR, (" PATH_MAX exceeded for config file."));
else
strcpy(conf->file, av[0]);
break;
default:
assert(0);
}
return 0;
}
int
RadiusInit(short request_type)
{
struct radius *rad = &bund->radius;
RadiusClose();
if (request_type == RAD_ACCESS_REQUEST) {
rad->radh = rad_open();
if (rad->radh == NULL) {
Log(LG_RADIUS, ("[%s] RADIUS: rad_open failed", lnk->name));
return (RAD_NACK);
}
/* RAD_ACCOUNTING_REQUEST */
} else {
rad->radh = rad_acct_open();
if (rad->radh == NULL) {
Log(LG_RADIUS, ("[%s] RADIUS: rad_acct_open failed", lnk->name));
return (RAD_NACK);
}
}
if (strlen(bund->radius.conf.file)) {
Log(LG_RADIUS, ("[%s] RADIUS: using %s", lnk->name, bund->radius.conf.file));
if (rad_config(rad->radh, bund->radius.conf.file) != 0) {
Log(LG_RADIUS, ("[%s] RADIUS: rad_config: %s", lnk->name, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
}
if (RadiusAddServer(request_type) == RAD_NACK) {
RadiusClose();
return (RAD_NACK);
}
return RAD_ACK;
}
void
RadiusClose(void)
{
struct radius *rad = &bund->radius;
if (rad->radh != NULL) rad_close(rad->radh);
rad->radh = NULL;
}
static int GetLinkID(void) {
int port, i;
port =- 1;
for (i = 0; i < gNumLinks; i++) {
if (gLinks[i] && gLinks[i]==lnk) {
port = i;
}
}
return port;
};
void
RadiusDown(void)
{
Log(LG_RADIUS, ("[%s] RADIUS: Down Event", lnk->name));
RadiusDestroy();
}
void
RadiusDestroy(void)
{
struct radius *rad = &bund->radius;
struct radius_acl *acls, *acls1;
RadiusClose();
free(rad->msdomain);
rad->msdomain = NULL;
free(rad->mschap_error);
rad->mschap_error = NULL;
free(rad->mschapv2resp);
rad->mschapv2resp = NULL;
free(rad->reply_message);
rad->reply_message = NULL;
acls = rad->acl_rule;
while (acls != NULL) {
acls1 = acls->next;
free(acls);
acls = acls1;
};
acls = rad->acl_pipe;
while (acls != NULL) {
acls1 = acls->next;
free(acls);
acls = acls1;
};
acls = rad->acl_queue;
while (acls != NULL) {
acls1 = acls->next;
free(acls);
acls = acls1;
};
memset(rad, 0, sizeof(struct radius) - sizeof(struct radiusconf));
}
int RadiusStart(short request_type)
{
static char function[] = "RadiusStart";
struct radius *rad = &bund->radius;
char host[MAXHOSTNAMELEN];
struct in_addr *peer_ip;
char *peeripname;
u_char *peer_mac;
char peermacname[18];
if (RadiusInit(request_type) == RAD_NACK)
return RAD_NACK;
if (gethostname(host, sizeof (host)) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: gethostname() failed", lnk->name, function));
return (RAD_NACK);
}
if (rad_create_request(rad->radh, request_type) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: rad_create_request: %s", lnk->name, rad_strerror(rad->radh)));
return (RAD_NACK);
}
if (rad_put_string(rad->radh, RAD_NAS_IDENTIFIER, host) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_string(host) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
if (rad->conf.radius_me.s_addr != 0) {
if (rad_put_addr(rad->radh, RAD_NAS_IP_ADDRESS, rad->conf.radius_me) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_addr(RAD_NAS_IP_ADDRESS) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
}
if (rad_put_int(rad->radh, RAD_NAS_PORT, GetLinkID()) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_int(RAD_NAS_PORT) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
if (rad_put_int(rad->radh, RAD_NAS_PORT_TYPE, RAD_VIRTUAL) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_int(RAD_NAS_PORT_TYPE) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
if (rad_put_int(rad->radh, RAD_SERVICE_TYPE, RAD_FRAMED) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_int(RAD_SERVICE_TYPE) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
if (rad_put_int(rad->radh, RAD_FRAMED_PROTOCOL, RAD_PPP) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_int(RAD_FRAMED_PROTOCOL) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
peer_ip = PptpGetPeerIp();
if (peer_ip != NULL && peer_ip->s_addr != 0) {
peeripname = inet_ntoa(*peer_ip);
if (peeripname != NULL) {
if (rad_put_string(rad->radh, RAD_CALLING_STATION_ID, peeripname) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_string(RAD_CALLING_STATION_ID) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
}
}
peer_mac = PppoeGetPeerAddr();
if ((peer_mac != NULL) &&
((peer_mac[0] != 0) || (peer_mac[1] != 0) || (peer_mac[2] != 0) ||
(peer_mac[3] !=0 ) || (peer_mac[4] != 0) || (peer_mac[5] != 0))
) {
snprintf(peermacname, sizeof(peermacname), "%02x%02x%02x%02x%02x%02x",
peer_mac[0], peer_mac[1], peer_mac[2], peer_mac[3], peer_mac[4], peer_mac[5]);
if (rad_put_string(rad->radh, RAD_CALLING_STATION_ID, peermacname) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_string(RAD_CALLING_STATION_ID) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
}
return RAD_ACK;
}
int
RadiusPutAuth(const char *name, const char *password, int passlen,
const char *challenge, int challenge_size, u_char chapid, int auth_type)
{
static char function[] = "RadiusPutAuth";
struct radius *rad = &bund->radius;
struct rad_chapvalue rad_chapval;
struct rad_mschapvalue rad_mschapval;
struct rad_mschapv2value rad_mschapv2val;
struct mschapvalue *mschapval;
struct mschapv2value *mschapv2val;
if (name == NULL || password == NULL) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: name or password NULL", lnk->name, function));
return (RAD_NACK);
}
/* Remember authname */
strncpy(rad->authname, name, AUTH_MAX_AUTHNAME);
if (rad_put_string(rad->radh, RAD_USER_NAME, name) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_string(username) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
/* Remember Auth-Type */
rad->auth_type = auth_type;
switch (auth_type) {
case CHAP_ALG_MSOFT:
Log(LG_RADIUS, ("[%s] RADIUS: %s: RADIUS_CHAP (MSOFTv1) peer name: %s", lnk->name, function, name));
if (passlen != 49) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: RADIUS_CHAP (MSOFTv1) unrecognised key length %d/%d",
lnk->name, function, passlen, 49));
RadiusClose();
return RAD_NACK;
}
if (rad_put_vendor_attr(rad->radh, RAD_VENDOR_MICROSOFT, RAD_MICROSOFT_MS_CHAP_CHALLENGE,
challenge, challenge_size) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_vendor_attr(RAD_MICROSOFT_MS_CHAP_CHALLENGE) failed %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
mschapval = (struct mschapvalue *)password;
rad_mschapval.ident = chapid;
rad_mschapval.flags = 0x01;
memcpy(rad_mschapval.lm_response, mschapval->lmHash, 24);
memcpy(rad_mschapval.nt_response, mschapval->ntHash, 24);
if (rad_put_vendor_attr(rad->radh, RAD_VENDOR_MICROSOFT, RAD_MICROSOFT_MS_CHAP_RESPONSE,
&rad_mschapval, sizeof rad_mschapval) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_vendor_attr(RAD_MICROSOFT_MS_CHAP_RESPONSE) failed %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
break;
case CHAP_ALG_MSOFTv2:
Log(LG_RADIUS, ("[%s] RADIUS: %s: RADIUS_CHAP (MSOFTv2) peer name: %s", lnk->name, function, name));
if (rad_put_vendor_attr(rad->radh, RAD_VENDOR_MICROSOFT, RAD_MICROSOFT_MS_CHAP_CHALLENGE, challenge,
challenge_size) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_vendor_attr(RAD_MICROSOFT_MS_CHAP_CHALLENGE) failed %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
if (passlen != sizeof(*mschapv2val)) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: RADIUS_CHAP (MSOFTv2) unrecognised key length %d/%d", lnk->name,
function, passlen, sizeof(*mschapv2val)));
RadiusClose();
return RAD_NACK;
}
mschapv2val = (struct mschapv2value *)password;
rad_mschapv2val.ident = chapid;
rad_mschapv2val.flags = mschapv2val->flags;
memcpy(rad_mschapv2val.response, mschapv2val->ntHash, sizeof rad_mschapv2val.response);
memset(rad_mschapv2val.reserved, '\0', sizeof rad_mschapv2val.reserved);
memcpy(rad_mschapv2val.pchallenge, mschapv2val->peerChal, sizeof rad_mschapv2val.pchallenge);
if (rad_put_vendor_attr(rad->radh, RAD_VENDOR_MICROSOFT, RAD_MICROSOFT_MS_CHAP2_RESPONSE,
&rad_mschapv2val, sizeof rad_mschapv2val) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_vendor_attr(RAD_MICROSOFT_MS_CHAP2_RESPONSE) failed %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
break;
case CHAP_ALG_MD5:
/* Radius wants the CHAP Ident in the first byte of the CHAP-Password */
rad_chapval.ident = chapid;
memcpy(rad_chapval.response, password, passlen);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RADIUS_CHAP (MD5) peer name: %s", lnk->name, function, name));
if (rad_put_attr(rad->radh, RAD_CHAP_PASSWORD, &rad_chapval, passlen + 1) == -1 ||
rad_put_attr(rad->radh, RAD_CHAP_CHALLENGE, challenge, challenge_size) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_string(password) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
break;
case RADIUS_PAP:
Log(LG_RADIUS, ("[%s] RADIUS: %s: RADIUS_PAP DEBUG: peer name: %s", lnk->name, function, name));
if (rad_put_string(rad->radh, RAD_USER_PASSWORD, password) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_string(password) failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
break;
default:
Log(LG_RADIUS, ("[%s] RADIUS: %s: RADIUS auth type unkown", lnk->name, function));
RadiusClose();
return (RAD_NACK);
break;
}
return RAD_ACK;
}
/* XXX mbretter: b0rked */
int
RadiusPutChangePassword(const char *mschapvalue, int mschapvaluelen, u_char chapid, int chap_type)
{
static char function[] = "RadiusPutChangePassword";
struct radius *rad = &bund->radius;
struct mschapv2value_cpw *mschapv2val_cpw;
struct rad_mschapv2value_cpw rad_mschapv2val_cpw;
struct rad_mschap_new_nt_pw new_nt_pw;
int chunk;
switch (chap_type) {
case CHAP_ALG_MSOFT:
Log(LG_RADIUS, ("[%s] RADIUS: %s: Password changing using MS-CHAPv1 not implemented",
lnk->name, function));
return (RAD_NACK);
break;
case CHAP_ALG_MSOFTv2:
mschapv2val_cpw = (struct mschapv2value_cpw *)mschapvalue;
if (mschapvaluelen != sizeof(*mschapv2val_cpw)) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: RADIUS_CHAP (MSOFTv2) unrecognised key length %d/%d",
lnk->name, function,
mschapvaluelen, sizeof(*mschapv2val_cpw)));
return RAD_NACK;
}
rad_mschapv2val_cpw.code = 7;
rad_mschapv2val_cpw.ident = chapid;
memcpy(rad_mschapv2val_cpw.encryptedHash, mschapv2val_cpw->encryptedHash,
sizeof rad_mschapv2val_cpw.encryptedHash);
memcpy(rad_mschapv2val_cpw.pchallenge, mschapv2val_cpw->peerChal,
sizeof rad_mschapv2val_cpw.pchallenge);
memset(rad_mschapv2val_cpw.reserved, '\0',
sizeof rad_mschapv2val_cpw.reserved);
memcpy(rad_mschapv2val_cpw.nt_response, mschapv2val_cpw->ntResponse,
sizeof rad_mschapv2val_cpw.nt_response);
memcpy(rad_mschapv2val_cpw.flags, mschapv2val_cpw->flags,
sizeof rad_mschapv2val_cpw.flags);
if (rad_put_vendor_attr(rad->radh, RAD_VENDOR_MICROSOFT, RAD_MICROSOFT_MS_CHAP2_PW,
&rad_mschapv2val_cpw, sizeof rad_mschapv2val_cpw) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_vendor_attr(RAD_MICROSOFT_MS_CHAP2_PW) failed %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
new_nt_pw.ident = chapid;
for (chunk = 0; chunk < 4; chunk++) {
new_nt_pw.chunk = chunk;
printf("Chunk:%d\n",chunk);
memcpy(new_nt_pw.data, &mschapv2val_cpw->encryptedPass[(sizeof new_nt_pw.data) * chunk],
sizeof new_nt_pw.data);
if (rad_put_vendor_attr(rad->radh, RAD_VENDOR_MICROSOFT, RAD_MICROSOFT_MS_CHAP_NT_ENC_PW,
&new_nt_pw, sizeof new_nt_pw) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_vendor_attr(RAD_MICROSOFT_MS_CHAP_NT_ENC_PW) failed %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return (RAD_NACK);
}
}
break;
default:
Log(LG_RADIUS, ("[%s] RADIUS: %s: RADIUS auth type unkown", lnk->name, function));
RadiusClose();
return (RAD_NACK);
break;
}
return RAD_ACK;
}
int RadiusSendRequest(void)
{
static char function[] = "RadiusSendRequest";
struct radius *rad = &bund->radius;
struct timeval timelimit;
struct timeval tv;
int fd, n;
#ifdef DEBUG
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_init_send_request ...", lnk->name, function));
#endif
n = rad_init_send_request(rad->radh, &fd, &tv);
if (n != 0) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_init_send_request failed: %d", lnk->name,
function, n));
return RAD_NACK;
}
gettimeofday(&timelimit, NULL);
timeradd(&tv, &timelimit, &timelimit);
for ( ; ; ) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
#ifdef DEBUG
Log(LG_RADIUS, ("[%s] RADIUS: %s: selecting ...", lnk->name, function));
#endif
n = select(fd + 1, &readfds, NULL, NULL, &tv);
if (n == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: select failed: %s", lnk->name, function, strerror(errno)));
return RAD_NACK;
}
if (!FD_ISSET(fd, &readfds)) {
/* Compute a new timeout */
gettimeofday(&tv, NULL);
timersub(&timelimit, &tv, &tv);
if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
/* Continue the select */
continue;
}
#ifdef DEBUG
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_continue_send_request ...", lnk->name, function));
#endif
n = rad_continue_send_request(rad->radh, n, &fd, &tv);
if (n != 0)
break;
gettimeofday(&timelimit, NULL);
timeradd(&tv, &timelimit, &timelimit);
}
switch (n) {
case RAD_ACCESS_ACCEPT:
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_ACCESS_ACCEPT for user %s", lnk->name,
function, rad->authname));
rad->valid = 1;
break;
case RAD_ACCESS_REJECT:
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_ACCESS_REJECT for user %s", lnk->name,
function, rad->authname));
rad->valid = 0;
break;
case RAD_ACCOUNTING_RESPONSE:
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_ACCOUNTING_RESPONSE for user %s", lnk->name,
function, rad->authname));
return RAD_ACK;
case -1:
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_send_request failed %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return(RAD_NACK);
break;
default:
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_send_request: unexpected return value %s", lnk->name,
function, rad_strerror(rad->radh)));
RadiusClose();
return(RAD_NACK);
}
return RAD_ACK;
}
int
RadiusPAPAuthenticate(const char *name, const char *password)
{
struct radius *rad = &bund->radius;
RadiusDestroy();
if (RadiusStart(RAD_ACCESS_REQUEST) == RAD_NACK)
return RAD_NACK;
if (RadiusPutAuth(name, password, 0, NULL, NULL, 0, RADIUS_PAP) == RAD_NACK)
return RAD_NACK;
if (RadiusSendRequest() == RAD_NACK)
return RAD_NACK;
if (RadiusGetParams() == RAD_NACK)
return RAD_NACK;
if (rad->valid) {
lnk->radius.authentic = 1;
return RAD_ACK;
} else {
return RAD_NACK;
}
}
int
RadiusCHAPAuthenticate(const char *name, const char *password, int passlen,
const char *challenge, int challenge_size, u_char chapid, int chap_type)
{
struct radius *rad = &bund->radius;
RadiusDestroy();
if (RadiusStart(RAD_ACCESS_REQUEST) == RAD_NACK)
return RAD_NACK;
if (RadiusPutAuth(name, password, passlen, challenge, challenge_size, chapid, chap_type) == RAD_NACK)
return RAD_NACK;
if (RadiusSendRequest() == RAD_NACK)
return RAD_NACK;
if (RadiusGetParams() == RAD_NACK)
return RAD_NACK;
if (rad->valid) {
lnk->radius.authentic = 1;
return RAD_ACK;
} else {
return RAD_NACK;
}
}
/* XXX mbretter: b0rked */
int
RadiusMSCHAPChangePassword(const char *mschapvalue, int mschapvaluelen, const char *challenge,
int challenge_size, u_char chapid, int chap_type)
{
static char function[] = "RadiusMSCHAPChangePassword";
struct radius *rad = &bund->radius;
struct mschapv2value_cpw *mschapv2val_cpw;
struct mschapv2value *mschapv2val;
if (chap_type != CHAP_ALG_MSOFTv2) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: must use MS-CHAPv2 for changing passwords", lnk->name, function));
return RAD_NACK;
}
if (RadiusStart(RAD_ACCESS_REQUEST) == RAD_NACK)
return RAD_NACK;
mschapv2val_cpw = (struct mschapv2value_cpw *)mschapvalue;
mschapv2val = (struct mschapv2value *)mschapv2val_cpw->encryptedHash;
if (RadiusPutChangePassword(mschapvalue, mschapvaluelen, chapid, chap_type) == RAD_NACK)
return RAD_NACK;
if (RadiusPutAuth(rad->authname, (const char *)mschapv2val, sizeof *mschapv2val, challenge,
challenge_size, chapid, chap_type) == RAD_NACK)
return RAD_NACK;
if (RadiusSendRequest() == RAD_NACK)
return RAD_NACK;
if (RadiusGetParams() == RAD_NACK)
return RAD_NACK;
if (rad->valid) {
lnk->radius.authentic = 1;
return RAD_ACK;
} else {
return RAD_NACK;
}
}
int
RadiusGetParams()
{
char function[] = "RadiusGetParams";
struct radius *rad = &bund->radius;
int res,i,j;
size_t len;
const void *data;
u_int32_t vendor;
char *route;
char *acl1,*acl2;
struct radius_acl **acls, *acls1;
struct ifaceroute r;
struct in_range range;
short got_mppe_keys = FALSE;
while ((res = rad_get_attr(rad->radh, &data, &len)) > 0) {
switch (res) {
case RAD_FRAMED_IP_ADDRESS:
rad->ip = rad_cvt_addr(data);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_FRAMED_IP_ADDRESS: %s ",
lnk->name, function, inet_ntoa(rad->ip)));
break;
case RAD_FRAMED_IP_NETMASK:
rad->mask = rad_cvt_addr(data);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_FRAMED_IP_NETMASK: %s ",
lnk->name, function, inet_ntoa(rad->mask)));
break;
case RAD_FRAMED_ROUTE:
route = rad_cvt_string(data,len);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_FRAMED_ROUTE: %s ",
lnk->name, function, route));
if (!ParseAddr(route, &range)) {
Log(LG_ERR, ("route: bad route \"%s\"", route));
free(route);
break;
}
free(route);
r.netmask.s_addr = range.width ?
htonl(~0 << (32 - range.width)) : 0;
r.dest.s_addr = (range.ipaddr.s_addr & r.netmask.s_addr);
j = 0;
for (i=0;i<rad->n_routes;i++) {
if ((r.dest.s_addr==rad->routes[i].dest.s_addr)&&(r.netmask.s_addr==rad->routes[i].netmask.s_addr))
j=1;
};
if (j==0) {
rad->routes[rad->n_routes++] = r;
};
break;
case RAD_SESSION_TIMEOUT:
rad->session_timeout = rad_cvt_int(data);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_SESSION_TIMEOUT: %lu ",
lnk->name, function, rad->session_timeout));
break;
case RAD_IDLE_TIMEOUT:
rad->idle_timeout = rad_cvt_int(data);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_IDLE_TIMEOUT: %lu ",
lnk->name, function, rad->idle_timeout));
break;
case RAD_ACCT_INTERIM_INTERVAL:
rad->interim_interval = rad_cvt_int(data);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_ACCT_INTERIM_INTERVAL: %lu ",
lnk->name, function, rad->interim_interval));
break;
case RAD_FRAMED_MTU:
rad->mtu = rad_cvt_int(data);
if (rad->mtu < IFACE_MIN_MTU || rad->mtu > IFACE_MAX_MTU) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_FRAMED_MTU: invalid MTU: %lu ",
lnk->name, function, rad->mtu));
rad->mtu = 0;
break;
}
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_FRAMED_MTU: %lu ",
lnk->name, function, rad->mtu));
break;
case RAD_FRAMED_COMPRESSION:
rad->vj = rad_cvt_int(data) == 1 ? 1 : 0;
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_FRAMED_COMPRESSION: %d ",
lnk->name, function, rad->vj));
break;
case RAD_FRAMED_PROTOCOL:
rad->protocol = rad_cvt_int(data);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_FRAMED_PROTOCOL: %lu ",
lnk->name, function, rad->protocol));
break;
case RAD_SERVICE_TYPE:
rad->service_type = rad_cvt_int(data);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_FRAMED_PROTOCOL: %lu ",
lnk->name, function, rad->service_type));
break;
case RAD_CLASS:
rad->class = rad_cvt_int(data);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_CLASS: %lu ",
lnk->name, function, rad->class));
break;
case RAD_REPLY_MESSAGE:
free(rad->reply_message);
rad->reply_message = rad_cvt_string(data, len);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_REPLY_MESSAGE: %s ",
lnk->name, function, rad->reply_message));
break;
case RAD_VENDOR_SPECIFIC:
if ((res = rad_get_vendor_attr(&vendor, &data, &len)) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_get_vendor_attr failed: %s ",
lnk->name, function, rad_strerror(rad->radh)));
return RAD_NACK;
}
switch (vendor) {
case RAD_VENDOR_MICROSOFT:
switch (res) {
case RAD_MICROSOFT_MS_CHAP_ERROR:
/* there is a nullbyte on the first pos, don't know why */
if (((const char *)data)[0] == '\0') {
data+=sizeof(const char);
len--;
}
free(rad->mschap_error);
rad->mschap_error = rad_cvt_string(data, len);
Log(LG_RADIUS, ("[%s] RADIUS: %s: MS-CHAP-Error: %s",
lnk->name, function, rad->mschap_error));
rad->valid = 0;
return RAD_NACK;
break;
/* this was taken from userland ppp */
case RAD_MICROSOFT_MS_CHAP2_SUCCESS:
free(rad->mschapv2resp);
if (len == 0)
rad->mschapv2resp = NULL;
else {
if (len < 3 || ((const char *)data)[1] != '=') {
/*
* Only point at the String field if we don't think the
* peer has misformatted the response.
*/
data+=sizeof(const char);
len--;
} else
Log(LG_RADIUS, ("[%s] RADIUS: %s: Warning: The MS-CHAP2-Success attribute is mis-formatted. Compensating",
lnk->name, function));
if ((rad->mschapv2resp = rad_cvt_string((const char *)data, len)) == NULL) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_cvt_string failed: %s",
lnk->name, function, rad_strerror(rad->radh)));
return RAD_NACK;
}
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_MICROSOFT_MS_CHAP2_SUCCESS: %s",
lnk->name, function, rad->mschapv2resp));
}
break;
case RAD_MICROSOFT_MS_CHAP_DOMAIN:
free(rad->msdomain);
rad->msdomain = rad_cvt_string(data, len);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_MICROSOFT_MS_CHAP_DOMAIN: %s",
lnk->name, function, rad->msdomain));
break;
/* MPPE Keys MS-CHAPv2 */
case RAD_MICROSOFT_MS_MPPE_RECV_KEY:
got_mppe_keys = TRUE;
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_MICROSOFT_MS_MPPE_RECV_KEY",
lnk->name, function));
rad_demangle_mppe_key2(rad->radh, data, len, rad->mppe.recvkey, &rad->mppe.recvkeylen);
break;
case RAD_MICROSOFT_MS_MPPE_SEND_KEY:
got_mppe_keys = TRUE;
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_MICROSOFT_MS_MPPE_SEND_KEY",
lnk->name, function));
rad_demangle_mppe_key2(rad->radh, data, len, rad->mppe.sendkey, &rad->mppe.sendkeylen);
break;
/* MPPE Keys MS-CHAPv1 */
case RAD_MICROSOFT_MS_CHAP_MPPE_KEYS:
got_mppe_keys = TRUE;
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_MICROSOFT_MS_CHAP_MPPE_KEYS",
lnk->name, function));
if (len != 32) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: Server returned garbage %d of expected %d Bytes",
lnk->name, function, len, 32));
return RAD_NACK;
}
if (rad_demangle2(rad->radh, data, len, rad->mppe.lm_key) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_demangle failed: %s",
lnk->name, function, rad_strerror(rad->radh)));
return RAD_NACK;
}
break;
case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY:
rad->mppe.policy = rad_cvt_int(data);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY: %d (%s)",
lnk->name, function, rad->mppe.policy, RadiusMPPEPolicyname(rad->mppe.policy)));
break;
case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES:
rad->mppe.types = rad_cvt_int(data);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES: %d (%s)",
lnk->name, function, rad->mppe.types, RadiusMPPETypesname(rad->mppe.types)));
break;
default:
Log(LG_RADIUS, ("[%s] RADIUS: %s: Dropping MICROSOFT vendor specific attribute: %d ",
lnk->name, function, res));
break;
}
case RAD_VENDOR_MPD:
if (res == RAD_MPD_RULE) {
acl2 = rad_cvt_string(data, len);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_MPD_RULE: %s",
lnk->name, function, acl2));
acls = &(rad->acl_rule);
} else if (res == RAD_MPD_PIPE) {
acl2 = rad_cvt_string(data, len);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_MPD_PIPE: %s",
lnk->name, function, acl2));
acls = &(rad->acl_pipe);
} else if (res == RAD_MPD_QUEUE) {
acl2 = rad_cvt_string(data, len);
Log(LG_RADIUS, ("[%s] RADIUS: %s: RAD_MPD_QUEUE: %s",
lnk->name, function, acl2));
acls = &(rad->acl_queue);
} else {
Log(LG_RADIUS, ("[%s] RADIUS: %s: Dropping MPD vendor specific attribute: %d ", lnk->name, function, res));
break;
};
acl1 = strsep(&acl2,"=");
i = atol(acl1);
if (i <= 0) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: wrong acl number: %i",
lnk->name, function,i));
free(acl1);
break;
};
if ((acl2 == NULL) && (acl2[0] == 0)) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: wrong acl",
lnk->name, function));
free(acl1);
break;
};
acls1 = malloc(sizeof(struct radius_acl));
acls1->number = i;
strncpy(acls1->rule,acl2,ACL_LEN);
while ((*acls != NULL)&&((*acls)->number < i)) {
acls = &((*acls)->next);
};
if (*acls == NULL) {
acls1->next = NULL;
} else if ((*acls)->number == i) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: duplicate acl",
lnk->name, function));
free(acl1);
break;
} else {
acls1->next = *acls;
};
*acls = acls1;
free(acl1);
break;
default:
Log(LG_RADIUS, ("[%s] RADIUS: %s: Dropping vendor %d attribute: %d ", lnk->name, function, vendor, res));
break;
}
break;
default:
Log(LG_RADIUS, ("[%s] RADIUS: %s: Dropping attribute: %d ", lnk->name, function, res));
break;
}
}
/* sanity check, this happens when FreeRADIUS has no msoft-dictionary loaded */
if (rad->auth_type == CHAP_ALG_MSOFTv2 && rad->mschapv2resp == NULL) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: PANIC no MS-CHAPv2 response received",
lnk->name, function));
return RAD_NACK;
}
/* MPPE allowed or required, but no MPPE keys returned */
/* print warning, because MPPE doesen't work */
if (!got_mppe_keys && rad->mppe.policy != MPPE_POLICY_NONE) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: WARNING no MPPE-Keys received, MPPE will not work",
lnk->name, function));
}
/* If no MPPE-Infos are returned by the RADIUS server, then allow all */
/* MSoft IAS sends no Infos if all MPPE-Types are enabled and if encryption is optional */
if (rad->mppe.policy == MPPE_POLICY_NONE &&
rad->mppe.types == MPPE_TYPE_0BIT &&
got_mppe_keys) {
rad->mppe.policy = MPPE_POLICY_ALLOWED;
rad->mppe.types = MPPE_TYPE_40BIT | MPPE_TYPE_128BIT | MPPE_TYPE_56BIT;
Log(LG_RADIUS, ("[%s] RADIUS: %s: MPPE-Keys, but no MPPE-Infos received => allowing MPPE with all types",
lnk->name, function));
}
return RAD_ACK;
}
void
RadiusAcctUpdate(void *a)
{
char function[] = "RadiusAcctUpdate";
Log(LG_RADIUS, ("[%s] RADIUS: %s: Sending Accounting Update",
lnk->name, function));
TimerStop(&lnk->radius.radUpdate);
RadiusAccount(RAD_UPDATE);
TimerStart(&lnk->radius.radUpdate);
}
int
RadiusAccount(short acct_type)
{
char function[] = "RadiusAccount";
struct radius *rad = &bund->radius;
int authentic;
/* if Radius-Auth wasn't used, then copy in authname */
if (!strlen(rad->authname))
strncpy(rad->authname, bund->peer_authname, AUTH_MAX_AUTHNAME);
Log(LG_RADIUS, ("[%s] RADIUS: %s for: %s", lnk->name, function, rad->authname));
if (lnk->radius.authentic) {
authentic = RAD_AUTH_RADIUS;
} else {
authentic = RAD_AUTH_LOCAL;
}
if (RadiusStart(RAD_ACCOUNTING_REQUEST) == RAD_NACK)
return RAD_NACK;
/* Grab some accounting data and initialize structure */
if (acct_type == RAD_START) {
/* Generate a session ID */
snprintf(lnk->radius.session_id, RAD_ACCT_MAX_SESSIONID, "%ld-%s",
time(NULL) % 10000000, lnk->name);
/* The first accounting request generates the multi Session ID */
/* wich is the same for all links */
if (strlen(rad->multi_session_id) == 0) {
snprintf(rad->multi_session_id, RAD_ACCT_MAX_SESSIONID, "%ld-%s",
time(NULL) % 10000000, bund->name);
}
}
if (rad_put_string(rad->radh, RAD_USER_NAME, lnk->peer_authname) != 0 ||
rad_put_addr(rad->radh, RAD_FRAMED_IP_ADDRESS, bund->ipcp.peer_addr)) { /*!= 0 ||
rad_put_addr(rad->radh, RAD_FRAMED_IP_NETMASK, ac->mask) != 0) {*/
Log(LG_RADIUS, ("[%s] RADIUS: %s: put (USER_NAME, FRAMED_IP_ADDRESS): %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return RAD_NACK;
}
if (rad_put_int(rad->radh, RAD_ACCT_STATUS_TYPE, acct_type) != 0 ||
rad_put_string(rad->radh, RAD_ACCT_SESSION_ID, lnk->radius.session_id) != 0 ||
rad_put_string(rad->radh, RAD_ACCT_MULTI_SESSION_ID, rad->multi_session_id) != 0) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: put (STATUS_TYPE, SESSION_ID, MULTI_SESSION_ID): %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return RAD_NACK;
}
if (rad_put_int(rad->radh, RAD_ACCT_LINK_COUNT, bund->n_links) != 0) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_int(RAD_ACCT_LINK_COUNT) failed: %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return RAD_NACK;
}
if (rad_put_int(rad->radh, RAD_ACCT_AUTHENTIC, authentic) != 0) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_int(RAD_ACCT_AUTHENTIC) failed: %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return RAD_NACK;
}
if (acct_type == RAD_STOP || acct_type == RAD_UPDATE) {
if (acct_type == RAD_STOP) {
int termCause = RAD_TERM_PORT_ERROR;
if (lnk->downReason != NULL) {
if (!strcmp(lnk->downReason, "")) {
termCause = RAD_TERM_NAS_REQUEST;
} else if (!strncmp(lnk->downReason, STR_PEER_DISC, strlen(STR_PEER_DISC))) {
termCause = RAD_TERM_USER_REQUEST;
} else if (!strncmp(lnk->downReason, STR_QUIT, strlen(STR_QUIT))) {
termCause = RAD_TERM_ADMIN_REBOOT;
} else if (!strncmp(lnk->downReason, STR_PORT_SHUTDOWN, strlen(STR_PORT_SHUTDOWN))) {
termCause = RAD_TERM_NAS_REBOOT;
} else if (!strncmp(lnk->downReason, STR_IDLE_TIMEOUT, strlen(STR_IDLE_TIMEOUT))) {
termCause = RAD_TERM_IDLE_TIMEOUT;
} else if (!strncmp(lnk->downReason, STR_SESSION_TIMEOUT, strlen(STR_SESSION_TIMEOUT))) {
termCause = RAD_TERM_SESSION_TIMEOUT;
} else if (!strncmp(lnk->downReason, STR_DROPPED, strlen(STR_DROPPED))) {
termCause = RAD_TERM_LOST_CARRIER;
} else if (!strncmp(lnk->downReason, STR_ECHO_TIMEOUT, strlen(STR_ECHO_TIMEOUT))) {
termCause = RAD_TERM_LOST_SERVICE;
} else if (!strncmp(lnk->downReason, STR_PROTO_ERR, strlen(STR_PROTO_ERR))) {
termCause = RAD_TERM_SERVICE_UNAVAILABLE;
} else if (!strncmp(lnk->downReason, STR_LOGIN_FAIL, strlen(STR_LOGIN_FAIL))) {
termCause = RAD_TERM_USER_ERROR;
};
Log(LG_RADIUS, ("[%s] RADIUS: Termination cause: %s, RADIUS: %d",
lnk->name, lnk->downReason, termCause));
}
if (rad_put_int(rad->radh, RAD_ACCT_TERMINATE_CAUSE, termCause) != 0) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_int(RAD_ACCT_TERMINATE_CAUSE) failed: %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return RAD_NACK;
}
}
if (rad_put_int(rad->radh, RAD_ACCT_SESSION_TIME, time(NULL) - lnk->bm.last_open) != 0) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: rad_put_int(RAD_ACCT_SESSION_TIME) failed: %s",
lnk->name, function, rad_strerror(rad->radh)));
RadiusClose();
return RAD_NACK;
}
LinkUpdateStats();
if (rad_put_int(rad->radh, RAD_ACCT_INPUT_OCTETS, lnk->stats.recvOctets % MAX_U_INT32) != 0 ||
rad_put_int(rad->radh, RAD_ACCT_INPUT_PACKETS, lnk->stats.recvFrames) != 0 ||
rad_put_int(rad->radh, RAD_ACCT_OUTPUT_OCTETS, lnk->stats.xmitOctets % MAX_U_INT32) != 0 ||
rad_put_int(rad->radh, RAD_ACCT_OUTPUT_PACKETS, lnk->stats.xmitFrames) != 0 ||
rad_put_int(rad->radh, RAD_ACCT_INPUT_GIGAWORDS, lnk->stats.recvOctets / MAX_U_INT32) != 0 ||
rad_put_int(rad->radh, RAD_ACCT_OUTPUT_GIGAWORDS, lnk->stats.xmitOctets / MAX_U_INT32) != 0) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: put stats: %s", lnk->name, function,
rad_strerror(rad->radh)));
RadiusClose();
return RAD_NACK;
}
}
Log(LG_RADIUS, ("[%s] RADIUS: %s: Sending accounting data (Type: %d)",
lnk->name, function, acct_type));
if (RadiusSendRequest() == RAD_NACK)
return RAD_NACK;
return RAD_ACK;
}
int
RadiusAddServer (short request_type)
{
RadConf const c = &bund->radius.conf;
RadServe_Conf s;
char function[] = "RadiusAddServer";
int i;
struct radius *rad = &bund->radius;
if (c->server == NULL)
return (RAD_ACK);
s = c->server;
i = 1;
while (s) {
Log(LG_RADIUS, ("[%s] RADIUS: %s Adding %s", lnk->name, function, s->hostname));
if (request_type == RAD_ACCESS_REQUEST) {
if (rad_add_server (rad->radh, s->hostname,
s->auth_port,
s->sharedsecret,
c->radius_timeout,
c->radius_retries) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s error: %s", lnk->name, function, rad_strerror(rad->radh)));
return (RAD_NACK);
}
} else {
if (rad_add_server (rad->radh, s->hostname,
s->acct_port,
s->sharedsecret,
c->radius_timeout,
c->radius_retries) == -1) {
Log(LG_RADIUS, ("[%s] RADIUS: %s error: %s", lnk->name, function, rad_strerror(rad->radh)));
return (RAD_NACK);
}
}
s = s->next;
}
return (RAD_ACK);
}
void
RadiusSetAuth(AuthData auth)
{
char function[] = "RadiusSetAuth";
strncpy(auth->authname, bund->radius.authname, AUTH_MAX_AUTHNAME);
if (Enabled(&bund->ipcp.conf.options, IPCP_CONF_RADIUSIP)) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: Trying to use IP-address from radius-server",
lnk->name, function));
if (strcmp(inet_ntoa(bund->radius.ip), "255.255.255.255") == 0
|| bund->radius.ip.s_addr == INADDR_ANY) {
/* the peer can choose an address */
Log(LG_RADIUS, ("[%s] RADIUS: %s: server says that the peer can choose an address",
lnk->name, function));
auth->range.ipaddr.s_addr = 0;
auth->range.width = 0;
auth->range_valid = 1;
} else if (strcmp(inet_ntoa(bund->radius.ip), "255.255.255.254") == 0) {
/* we should choose the ip */
Log(LG_RADIUS, ("[%s] RADIUS: %s: server says that we should choose an address",
lnk->name, function));
auth->range_valid = 0;
} else {
/* or use IP from Radius-server */
Log(LG_RADIUS, ("[%s] RADIUS: %s: using this IP: %s",
lnk->name, function, inet_ntoa(bund->radius.ip)));
memcpy(&auth->range.ipaddr, &bund->radius.ip, sizeof(struct in_addr));
auth->range_valid = 1;
auth->range.width = 32;
}
}
}
int
RadStat(int ac, char *av[], void *arg)
{
RadConf const conf = &bund->radius.conf;
RadServe_Conf server;
struct radius *rad = &bund->radius;
int i;
printf("\tTimeout : %d\n", conf->radius_timeout);
printf("\tRetries : %d\n", conf->radius_retries);
printf("\tConfig-file : %s\n", conf->file);
printf("\tMe (NAS-IP) : %s\n", inet_ntoa(conf->radius_me));
printf("\tAcct-Interval: %d\n", conf->acct_update);
if (conf->server != NULL) {
server = conf->server;
i = 1;
while (server) {
printf("\t--------------- Radius Server %d ---------------\n", i);
printf("\thostname : %s\n", server->hostname);
printf("\tsecret : *********\n");
printf("\tauth port : %d\n", server->auth_port);
printf("\tacct port : %d\n", server->acct_port);
i++;
server = server->next;
}
}
printf("\t--------------- Radius Data ---------------\n");
printf("\tAuthenticated : %s\n", rad->valid ? "yes" : "no");
printf("\tAuthname : %s\n", rad->authname);
printf("\tReply-message : %s\n", rad->reply_message == NULL ? "" : rad->reply_message);
printf("\tIP : %s\n", inet_ntoa(rad->ip));
printf("\tMASK : %s\n", inet_ntoa(rad->mask));
printf("\tMTU : %lu\n", rad->mtu);
printf("\tSession-timeout : %lu\n", rad->session_timeout);
printf("\tIdle-timeout : %lu\n", rad->idle_timeout);
printf("\tVJ : %d\n", rad->vj);
printf("\tClass : %lu\n", rad->class);
printf("\tProtocol : %lu\n", rad->protocol);
printf("\tService-type : %lu\n", rad->service_type);
printf("\tFilter-Id : %s\n", rad->filterid == NULL ? "" : rad->filterid);
printf("\tAcct-SID : %s\n", lnk->radius.session_id);
printf("\tAcct-MSID : %s\n", rad->multi_session_id);
printf("\t--------------- Radius MSoft related Data ---------------\n");
printf("\tMPPE Types : %s\n", RadiusMPPETypesname(rad->mppe.types));
printf("\tMPPE Policy : %s\n", RadiusMPPEPolicyname(rad->mppe.policy));
printf("\tMS-Domain : %s\n", rad->msdomain == NULL ? "" : rad->msdomain);
printf("\tMS-CHAP-Error : %s\n", rad->mschap_error == NULL ? "" : rad->mschap_error);
printf("\tMS-CHAPv2-Response : %s\n", rad->mschapv2resp == NULL ? "" : rad->mschapv2resp);
return (0);
}
static const char *
RadiusMPPEPolicyname(int policy)
{
switch(policy) {
case MPPE_POLICY_ALLOWED:
return "Allowed";
case MPPE_POLICY_REQUIRED:
return "Required";
case MPPE_POLICY_NONE:
return "Not available";
default:
return "Unknown Policy";
}
}
static const char *
RadiusMPPETypesname(int types) {
static char res[30];
memset(res, 0, sizeof res);
if (types == 0) {
sprintf(res, "no encryption required");
return res;
}
if (types & MPPE_TYPE_40BIT) sprintf (res, "40 ");
if (types & MPPE_TYPE_56BIT) sprintf (&res[strlen(res)], "56 ");
if (types & MPPE_TYPE_128BIT) sprintf (&res[strlen(res)], "128 ");
if (strlen(res) == 0) {
sprintf (res, "unknown types");
} else {
sprintf (&res[strlen(res)], "bit");
}
return res;
}
/* compatibility functions until libradius has these builtin */
static int
rad_demangle2(struct rad_handle *h, const void *mangled, size_t mlen, u_char *demangled)
{
char function[] = "rad_demangle";
char R[AUTH_LEN];
const char *S;
int i, Ppos;
MD5_CTX Context;
u_char b[16], *C;
if ((mlen % 16 != 0) || (mlen > 128)) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: Cannot interpret mangled data of length %ld",
lnk->name, function, (u_long)mlen));
return -1;
}
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) != AUTH_LEN) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: Cannot obtain the RADIUS request authenticator",
lnk->name, function));
return -1;
}
MD5Init(&Context);
MD5Update(&Context, S, strlen(S));
MD5Update(&Context, R, AUTH_LEN);
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 0;
}
static int
rad_demangle_mppe_key2(struct rad_handle *h, const void *mangled, size_t mlen, u_char *demangled, size_t *len)
{
char function[] = "rad_demangle_mppe_key";
char R[AUTH_LEN]; /* variable names as per rfc2548 */
const char *S;
u_char b[16];
const u_char *A, *C;
MD5_CTX Context;
int Slen, i, Clen, Ppos;
u_char *P;
if (mlen % 16 != SALT_LEN) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: Cannot interpret mangled data of length %ld",
lnk->name, function, (u_long)mlen));
return -1;
}
/* We need the RADIUS Request-Authenticator */
if (rad_request_authenticator(h, R, sizeof R) != AUTH_LEN) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: Cannot obtain the RADIUS request authenticator",
lnk->name, function));
return -1;
}
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, AUTH_LEN);
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) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: Mangled data seems to be garbage %d %d",
lnk->name, function, *len, mlen-1));
return -1;
}
if (*len > MPPE_KEY_LEN) {
Log(LG_RADIUS, ("[%s] RADIUS: %s: Key to long (%d) for me max. %d",
lnk->name, function, *len, MPPE_KEY_LEN));
return -1;
}
memcpy(demangled, P + 1, *len);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1