#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/syslog.h>
#include <netinet/in.h>
#include <net/if_dl.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netdb.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <radius.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <event.h>
#include "radius_req.h"
#include "npppd_local.h"
#include "npppd_radius.h"
#include "net_utils.h"
#ifdef NPPPD_RADIUS_DEBUG
#define NPPPD_RADIUS_DBG(x) ppp_log x
#define NPPPD_RADIUS_ASSERT(x) ASSERT(x)
#else
#define NPPPD_RADIUS_DBG(x)
#define NPPPD_RADIUS_ASSERT(x)
#endif
static int l2tp_put_tunnel_attributes(RADIUS_PACKET *, void *);
static int pptp_put_tunnel_attributes(RADIUS_PACKET *, void *);
static int radius_acct_request(npppd *, npppd_ppp *, int );
static void radius_acct_on_cb(void *, RADIUS_PACKET *, int, RADIUS_REQUEST_CTX);
static void npppd_ppp_radius_acct_reqcb(void *, RADIUS_PACKET *, int, RADIUS_REQUEST_CTX);
void
ppp_process_radius_attrs(npppd_ppp *_this, RADIUS_PACKET *pkt)
{
struct in_addr ip4;
int got_pri, got_sec;
char buf0[40], buf1[40];
if (radius_get_ipv4_attr(pkt, RADIUS_TYPE_FRAMED_IP_ADDRESS, &ip4)
== 0)
_this->realm_framed_ip_address = ip4;
_this->realm_framed_ip_netmask.s_addr = 0xffffffffL;
#ifndef NPPPD_COMPAT_4_2
if (radius_get_ipv4_attr(pkt, RADIUS_TYPE_FRAMED_IP_NETMASK, &ip4)
== 0)
_this->realm_framed_ip_netmask = ip4;
#endif
if (!ppp_ipcp(_this)->dns_configured) {
got_pri = got_sec = 0;
if (radius_get_vs_ipv4_attr(pkt, RADIUS_VENDOR_MICROSOFT,
RADIUS_VTYPE_MS_PRIMARY_DNS_SERVER, &ip4) == 0) {
got_pri = 1;
_this->ipcp.dns_pri = ip4;
}
if (radius_get_vs_ipv4_attr(pkt, RADIUS_VENDOR_MICROSOFT,
RADIUS_VTYPE_MS_SECONDARY_DNS_SERVER, &ip4) == 0) {
got_sec = 1;
_this->ipcp.dns_sec = ip4;
}
if (got_pri || got_sec)
ppp_log(_this, LOG_INFO, "DNS server address%s "
"(%s%s%s) %s configured by RADIUS server",
((got_pri + got_sec) > 1)? "es" : "",
(got_pri)? inet_ntop(AF_INET, &_this->ipcp.dns_pri,
buf0, sizeof(buf0)) : "",
(got_pri != 0 && got_sec != 0)? "," : "",
(got_sec)? inet_ntop(AF_INET, &_this->ipcp.dns_sec,
buf1, sizeof(buf1)) : "",
((got_pri + got_sec) > 1)? "are" : "is");
}
if (!ppp_ipcp(_this)->nbns_configured) {
got_pri = got_sec = 0;
if (radius_get_vs_ipv4_attr(pkt, RADIUS_VENDOR_MICROSOFT,
RADIUS_VTYPE_MS_PRIMARY_NBNS_SERVER, &ip4) == 0) {
got_pri = 1;
_this->ipcp.nbns_pri = ip4;
}
if (radius_get_vs_ipv4_attr(pkt, RADIUS_VENDOR_MICROSOFT,
RADIUS_VTYPE_MS_SECONDARY_NBNS_SERVER, &ip4) == 0) {
got_sec = 1;
_this->ipcp.nbns_sec = ip4;
}
if (got_pri || got_sec)
ppp_log(_this, LOG_INFO, "NBNS server address%s "
"(%s%s%s) %s configured by RADIUS server",
((got_pri + got_sec) > 1)? "es" : "",
(got_pri)? inet_ntop(AF_INET, &_this->ipcp.nbns_pri,
buf0, sizeof(buf0)) : "",
(got_pri != 0 && got_sec != 0)? "," : "",
(got_sec)? inet_ntop(AF_INET, &_this->ipcp.nbns_sec,
buf1, sizeof(buf1)) : "",
((got_pri + got_sec) > 1)? "are" : "is");
}
}
void
npppd_ppp_radius_acct_start(npppd *pppd, npppd_ppp *ppp)
{
NPPPD_RADIUS_DBG((ppp, LOG_INFO, "%s()", __func__));
if (ppp->realm == NULL || !npppd_ppp_is_realm_radius(pppd, ppp))
return;
radius_acct_request(pppd, ppp, 0);
}
void
npppd_ppp_radius_acct_stop(npppd *pppd, npppd_ppp *ppp)
{
NPPPD_RADIUS_DBG((ppp, LOG_INFO, "%s()", __func__));
if (ppp->realm == NULL || !npppd_ppp_is_realm_radius(pppd, ppp))
return;
radius_acct_request(pppd, ppp, 1);
}
static void
npppd_ppp_radius_acct_reqcb(void *context, RADIUS_PACKET *pkt, int flags,
RADIUS_REQUEST_CTX ctx)
{
u_int ppp_id;
ppp_id = (uintptr_t)context;
if ((flags & RADIUS_REQUEST_TIMEOUT) != 0) {
log_printf(LOG_WARNING, "ppp id=%u radius accounting request "
"failed: no response from the server.", ppp_id);
}
else if ((flags & RADIUS_REQUEST_ERROR) != 0)
log_printf(LOG_WARNING, "ppp id=%u radius accounting request "
"failed: %m", ppp_id);
else if ((flags & RADIUS_REQUEST_CHECK_AUTHENTICATOR_NO_CHECK) == 0 &&
(flags & RADIUS_REQUEST_CHECK_AUTHENTICATOR_OK) == 0)
log_printf(LOG_WARNING, "ppp id=%d radius accounting request "
"failed: the server responses with bad authenticator",
ppp_id);
else {
#ifdef NPPPD_RADIUS_DEBUG
log_printf(LOG_DEBUG, "ppp id=%u radius accounting request "
"succeeded.", ppp_id);
#endif
return;
}
if (radius_request_can_failover(ctx)) {
if (radius_request_failover(ctx) == 0) {
struct sockaddr *sa;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
sa = radius_get_server_address(ctx);
if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf),
sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV)
!= 0) {
strlcpy(hbuf, "unknown", sizeof(hbuf));
strlcpy(sbuf, "", sizeof(sbuf));
}
log_printf(LOG_DEBUG, "ppp id=%u "
"fail over to %s:%s for radius accounting request",
ppp_id, hbuf, sbuf);
} else {
log_printf(LOG_WARNING, "ppp id=%u "
"failed to fail over for radius accounting request",
ppp_id);
}
}
}
#define ATTR_INT32(_a,_v) \
do { \
if (radius_put_uint32_attr(radpkt, (_a), (_v)) != 0) \
goto fail; \
} while (0 )
#define ATTR_STR(_a,_v) \
do { \
if (radius_put_string_attr(radpkt, (_a), (_v)) != 0) \
goto fail; \
} while (0 )
static int
radius_acct_request(npppd *pppd, npppd_ppp *ppp, int stop)
{
RADIUS_PACKET *radpkt;
RADIUS_REQUEST_CTX radctx;
radius_req_setting *rad_setting;
char buf[128];
if (ppp->username[0] == '\0')
return 0;
radpkt = NULL;
radctx = NULL;
rad_setting = npppd_auth_radius_get_radius_acct_setting(ppp->realm);
if (!radius_req_setting_has_server(rad_setting))
return 0;
if ((radpkt = radius_new_request_packet(RADIUS_CODE_ACCOUNTING_REQUEST))
== NULL)
goto fail;
if (radius_prepare(rad_setting, (void *)(uintptr_t)ppp->id, &radctx,
npppd_ppp_radius_acct_reqcb) != 0)
goto fail;
if (radius_prepare_nas_address(rad_setting, radpkt) != 0)
goto fail;
ATTR_INT32(RADIUS_TYPE_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_VIRTUAL);
ATTR_INT32(RADIUS_TYPE_NAS_PORT, ppp->id);
ATTR_STR(RADIUS_TYPE_NAS_IDENTIFIER, pppd->conf.nas_id);
if (ppp->calling_number[0] != '\0')
ATTR_STR(RADIUS_TYPE_CALLING_STATION_ID, ppp->calling_number);
switch (ppp->tunnel_type) {
case NPPPD_TUNNEL_L2TP:
ATTR_INT32(RADIUS_TYPE_TUNNEL_TYPE, RADIUS_TUNNEL_TYPE_L2TP);
if (l2tp_put_tunnel_attributes(radpkt, ppp->phy_context) != 0)
goto fail;
break;
case NPPPD_TUNNEL_PPTP:
ATTR_INT32(RADIUS_TYPE_TUNNEL_TYPE, RADIUS_TUNNEL_TYPE_PPTP);
if (pptp_put_tunnel_attributes(radpkt, ppp->phy_context) != 0)
goto fail;
break;
}
ATTR_STR(RADIUS_TYPE_USER_NAME, ppp->username);
ATTR_INT32(RADIUS_TYPE_SERVICE_TYPE, RADIUS_SERVICE_TYPE_FRAMED);
ATTR_INT32(RADIUS_TYPE_FRAMED_PROTOCOL, RADIUS_FRAMED_PROTOCOL_PPP);
if (ppp->acct_framed_ip_address.s_addr != INADDR_ANY)
ATTR_INT32(RADIUS_TYPE_FRAMED_IP_ADDRESS,
ntohl(ppp->acct_framed_ip_address.s_addr));
ATTR_INT32(RADIUS_TYPE_ACCT_STATUS_TYPE, (stop)
? RADIUS_ACCT_STATUS_TYPE_STOP : RADIUS_ACCT_STATUS_TYPE_START);
ATTR_INT32(RADIUS_TYPE_ACCT_DELAY_TIME, 0);
if (stop) {
ATTR_INT32(RADIUS_TYPE_ACCT_INPUT_OCTETS,
(uint32_t)(ppp->ibytes & 0xFFFFFFFFU));
ATTR_INT32(RADIUS_TYPE_ACCT_OUTPUT_OCTETS,
(uint32_t)(ppp->obytes & 0xFFFFFFFFU));
}
snprintf(buf, sizeof(buf), "%08X%08X", pppd->boot_id, ppp->id);
ATTR_STR(RADIUS_TYPE_ACCT_SESSION_ID, buf);
ATTR_INT32(RADIUS_TYPE_ACCT_AUTHENTIC, RADIUS_ACCT_AUTHENTIC_RADIUS);
if (stop) {
ATTR_INT32(RADIUS_TYPE_ACCT_SESSION_TIME,
ppp->end_monotime - ppp->start_monotime);
ATTR_INT32(RADIUS_TYPE_ACCT_INPUT_PACKETS, ppp->ipackets);
ATTR_INT32(RADIUS_TYPE_ACCT_OUTPUT_PACKETS, ppp->opackets);
if (ppp->terminate_cause != 0)
ATTR_INT32(RADIUS_TYPE_ACCT_TERMINATE_CAUSE,
ppp->terminate_cause);
ATTR_INT32(RADIUS_TYPE_ACCT_INPUT_GIGAWORDS, ppp->ibytes >> 32);
ATTR_INT32(RADIUS_TYPE_ACCT_OUTPUT_GIGAWORDS,
ppp->obytes >> 32);
}
radius_request(radctx, radpkt);
return 0;
fail:
ppp_log(ppp, LOG_WARNING, "radius accounting request failed: %m");
if (radctx != NULL)
radius_cancel_request(radctx);
if (radpkt != NULL)
radius_delete_packet(radpkt);
return -1;
}
void
radius_acct_on(npppd *pppd, radius_req_setting *rad_setting)
{
RADIUS_REQUEST_CTX radctx = NULL;
RADIUS_PACKET *radpkt = NULL;
if (!radius_req_setting_has_server(rad_setting))
return;
if ((radpkt = radius_new_request_packet(RADIUS_CODE_ACCOUNTING_REQUEST))
== NULL)
goto fail;
if (radius_prepare(rad_setting, NULL, &radctx, radius_acct_on_cb) != 0)
goto fail;
if (radius_prepare_nas_address(rad_setting, radpkt) != 0)
goto fail;
ATTR_INT32(RADIUS_TYPE_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_VIRTUAL);
ATTR_INT32(RADIUS_TYPE_ACCT_STATUS_TYPE, RADIUS_ACCT_STATUS_TYPE_ACCT_ON);
ATTR_STR(RADIUS_TYPE_NAS_IDENTIFIER, pppd->conf.nas_id);
radius_request(radctx, radpkt);
return;
fail:
if (radctx != NULL)
radius_cancel_request(radctx);
if (radpkt != NULL)
radius_delete_packet(radpkt);
}
static void
radius_acct_on_cb(void *context, RADIUS_PACKET *pkt, int flags,
RADIUS_REQUEST_CTX ctx)
{
if ((flags & (RADIUS_REQUEST_TIMEOUT | RADIUS_REQUEST_ERROR)) != 0)
radius_request_failover(ctx);
}
#ifdef USE_NPPPD_PPTP
#include "pptp.h"
#endif
static int
pptp_put_tunnel_attributes(RADIUS_PACKET *radpkt, void *call0)
{
#ifdef USE_NPPPD_PPTP
pptp_call *call = call0;
pptp_ctrl *ctrl;
char hbuf[NI_MAXHOST], buf[128];
ctrl = call->ctrl;
switch (ctrl->peer.ss_family) {
case AF_INET:
ATTR_INT32(RADIUS_TYPE_TUNNEL_MEDIUM_TYPE,
RADIUS_TUNNEL_MEDIUM_TYPE_IPV4);
break;
case AF_INET6:
ATTR_INT32(RADIUS_TYPE_TUNNEL_MEDIUM_TYPE,
RADIUS_TUNNEL_MEDIUM_TYPE_IPV6);
break;
default:
return -1;
}
if (getnameinfo((struct sockaddr *)&ctrl->peer, ctrl->peer.ss_len, hbuf,
sizeof(hbuf), NULL, 0, NI_NUMERICHOST))
return 1;
ATTR_STR(RADIUS_TYPE_TUNNEL_CLIENT_ENDPOINT, hbuf);
if (getnameinfo((struct sockaddr *)&ctrl->our, ctrl->our.ss_len, hbuf,
sizeof(hbuf), NULL, 0, NI_NUMERICHOST))
return 1;
ATTR_STR(RADIUS_TYPE_TUNNEL_SERVER_ENDPOINT, hbuf);
snprintf(buf, sizeof(buf), "PPTP-CALL-%d", call->id);
ATTR_STR(RADIUS_TYPE_TUNNEL_ASSIGNMENT_ID, buf);
snprintf(buf, sizeof(buf), "PPTP-CTRL-%d", ctrl->id);
ATTR_STR(RADIUS_TYPE_ACCT_TUNNEL_CONNECTION, buf);
return 0;
fail:
#endif
return 1;
}
#ifdef USE_NPPPD_L2TP
#include "l2tp.h"
#endif
static int
l2tp_put_tunnel_attributes(RADIUS_PACKET *radpkt, void *call0)
{
#ifdef USE_NPPPD_L2TP
l2tp_call *call = call0;
l2tp_ctrl *ctrl;
char hbuf[NI_MAXHOST], buf[128];
ctrl = call->ctrl;
switch (ctrl->peer.ss_family) {
case AF_INET:
ATTR_INT32(RADIUS_TYPE_TUNNEL_MEDIUM_TYPE,
RADIUS_TUNNEL_MEDIUM_TYPE_IPV4);
break;
case AF_INET6:
ATTR_INT32(RADIUS_TYPE_TUNNEL_MEDIUM_TYPE,
RADIUS_TUNNEL_MEDIUM_TYPE_IPV6);
break;
default:
return -1;
}
if (getnameinfo((struct sockaddr *)&ctrl->peer, ctrl->peer.ss_len, hbuf,
sizeof(hbuf), NULL, 0, NI_NUMERICHOST))
return 1;
ATTR_STR(RADIUS_TYPE_TUNNEL_CLIENT_ENDPOINT, hbuf);
if (getnameinfo((struct sockaddr *)&ctrl->sock, ctrl->sock.ss_len, hbuf,
sizeof(hbuf), NULL, 0, NI_NUMERICHOST))
return 1;
ATTR_STR(RADIUS_TYPE_TUNNEL_SERVER_ENDPOINT, hbuf);
snprintf(buf, sizeof(buf), "L2TP-CALL-%d", call->id);
ATTR_STR(RADIUS_TYPE_TUNNEL_ASSIGNMENT_ID, buf);
snprintf(buf, sizeof(buf), "L2TP-CTRL-%d", ctrl->id);
ATTR_STR(RADIUS_TYPE_ACCT_TUNNEL_CONNECTION, buf);
return 0;
fail:
#endif
return 1;
}
int
ppp_set_radius_attrs_for_authreq(npppd_ppp *_this,
radius_req_setting *rad_setting, RADIUS_PACKET *radpkt)
{
if (radius_prepare_nas_address(rad_setting, radpkt) != 0)
goto fail;
if (radius_put_uint32_attr(radpkt, RADIUS_TYPE_SERVICE_TYPE,
RADIUS_SERVICE_TYPE_FRAMED) != 0)
goto fail;
if (radius_put_uint32_attr(radpkt, RADIUS_TYPE_FRAMED_PROTOCOL,
RADIUS_FRAMED_PROTOCOL_PPP) != 0)
goto fail;
if (_this->calling_number[0] != '\0') {
if (radius_put_string_attr(radpkt,
RADIUS_TYPE_CALLING_STATION_ID, _this->calling_number) != 0)
return 1;
}
if (radius_put_uint32_attr(radpkt, RADIUS_TYPE_NAS_PORT, _this->id)
!= 0)
goto fail;
return 0;
fail:
return 1;
}
static int npppd_radius_dae_listen_start(struct npppd_radius_dae_listen *);
static void npppd_radius_dae_on_event(int, short, void *);
static void npppd_radius_dae_listen_stop(struct npppd_radius_dae_listen *);
void
npppd_radius_dae_init(npppd *_this)
{
struct npppd_radius_dae_listens listens;
struct npppd_radius_dae_listen *listen, *listent;
struct radlistenconf *listenconf;
TAILQ_INIT(&listens);
TAILQ_FOREACH(listenconf, &_this->conf.raddaelistenconfs, entry) {
TAILQ_FOREACH_SAFE(listen, &_this->raddae_listens, entry,
listent) {
if ((listen->addr.sin4.sin_family == AF_INET &&
listenconf->addr.sin4.sin_family == AF_INET &&
memcmp(&listen->addr.sin4, &listenconf->addr.sin4,
sizeof(struct sockaddr_in)) == 0) ||
(listen->addr.sin6.sin6_family == AF_INET6 &&
listenconf->addr.sin6.sin6_family == AF_INET6 &&
memcmp(&listen->addr.sin6, &listenconf->addr.sin6,
sizeof(struct sockaddr_in6)) == 0))
break;
}
if (listen != NULL)
TAILQ_REMOVE(&_this->raddae_listens, listen, entry);
else {
if ((listen = calloc(1, sizeof(*listen))) == NULL) {
log_printf(LOG_ERR, "%s: calloc failed: %m",
__func__);
goto fail;
}
listen->pppd = _this;
listen->sock = -1;
if (listenconf->addr.sin4.sin_family == AF_INET)
listen->addr.sin4 = listenconf->addr.sin4;
else
listen->addr.sin6 = listenconf->addr.sin6;
}
TAILQ_INSERT_TAIL(&listens, listen, entry);
}
TAILQ_FOREACH(listen, &listens, entry) {
if (listen->sock == -1)
npppd_radius_dae_listen_start(listen);
}
TAILQ_FOREACH_SAFE(listen, &_this->raddae_listens, entry, listent) {
TAILQ_REMOVE(&_this->raddae_listens, listen, entry);
npppd_radius_dae_listen_stop(listen);
free(listen);
}
fail:
TAILQ_CONCAT(&_this->raddae_listens, &listens, entry);
return;
}
void
npppd_radius_dae_fini(npppd *_this)
{
struct npppd_radius_dae_listen *listen, *listent;
TAILQ_FOREACH_SAFE(listen, &_this->raddae_listens, entry, listent) {
TAILQ_REMOVE(&_this->raddae_listens, listen, entry);
npppd_radius_dae_listen_stop(listen);
free(listen);
}
}
int
npppd_radius_dae_listen_start(struct npppd_radius_dae_listen *listen)
{
char buf[80];
int sock = -1, on = 1;
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
log_printf(LOG_ERR, "%s: socket(): %m", __func__);
goto on_error;
}
on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
log_printf(LOG_WARNING, "%s: setsockopt(,,SO_REUSEADDR): %m",
__func__);
goto on_error;
}
if (bind(sock, (struct sockaddr *)&listen->addr,
listen->addr.sin4.sin_len) == -1) {
log_printf(LOG_ERR, "%s: bind(): %m", __func__);
goto on_error;
}
listen->sock = sock;
event_set(&listen->evsock, listen->sock, EV_READ | EV_PERSIST,
npppd_radius_dae_on_event, listen);
event_add(&listen->evsock, NULL);
log_printf(LOG_INFO, "radius Listening %s/udp (DAE)",
addrport_tostring((struct sockaddr *)&listen->addr,
listen->addr.sin4.sin_len, buf, sizeof(buf)));
return (0);
on_error:
if (sock >= 0)
close(sock);
return (-1);
}
void
npppd_radius_dae_on_event(int fd, short ev, void *ctx)
{
char buf[80], attr[256], username[256];
char *endp;
const char *reason, *nakcause = NULL;
struct npppd_radius_dae_listen *listen = ctx;
struct radclientconf *client;
npppd *_this = listen->pppd;
RADIUS_PACKET *req = NULL, *res = NULL;
struct sockaddr_storage ss;
socklen_t sslen;
unsigned long long ppp_id;
int code, n = 0;
uint32_t cause = 0;
struct in_addr ina;
slist *users;
npppd_ppp *ppp;
reason = "disconnect requested";
sslen = sizeof(ss);
req = radius_recvfrom(listen->sock, 0, (struct sockaddr *)&ss, &sslen);
if (req == NULL) {
log_printf(LOG_WARNING, "%s: receiving a RADIUS message "
"failed: %m", __func__);
return;
}
TAILQ_FOREACH(client, &_this->conf.raddaeclientconfs, entry) {
if (ss.ss_family == AF_INET &&
((struct sockaddr_in *)&ss)->sin_addr.s_addr ==
client->addr.sin4.sin_addr.s_addr)
break;
else if (ss.ss_family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&((struct sockaddr_in6 *)&ss)->sin6_addr,
&client->addr.sin6.sin6_addr))
break;
}
if (client == NULL) {
log_printf(LOG_WARNING, "radius received a RADIUS message from "
"%s: unknown client", addrport_tostring(
(struct sockaddr *)&ss, ss.ss_len, buf, sizeof(buf)));
goto out;
}
if (radius_check_accounting_request_authenticator(req,
client->secret) != 0) {
log_printf(LOG_WARNING, "radius received an invalid RADIUS "
"message from %s: bad response authenticator",
addrport_tostring(
(struct sockaddr *)&ss, ss.ss_len, buf, sizeof(buf)));
goto out;
}
if ((code = radius_get_code(req)) != RADIUS_CODE_DISCONNECT_REQUEST) {
if (code == RADIUS_CODE_COA_REQUEST) {
code = RADIUS_CODE_COA_NAK;
cause = RADIUS_ERROR_CAUSE_ADMINISTRATIVELY_PROHIBITED;
nakcause = "Coa-Request is not supported";
goto send;
}
log_printf(LOG_WARNING, "radius received an invalid RADIUS "
"message from %s: unknown code %d",
addrport_tostring((struct sockaddr *)&ss, ss.ss_len, buf,
sizeof(buf)), code);
goto out;
}
log_printf(LOG_INFO, "radius received Disconnect-Request from %s",
addrport_tostring((struct sockaddr *)&ss, ss.ss_len, buf,
sizeof(buf)));
if (radius_get_string_attr(req, RADIUS_TYPE_NAS_IDENTIFIER, attr,
sizeof(attr)) == 0 && strcmp(attr, _this->conf.nas_id) != 0) {
cause = RADIUS_ERROR_CAUSE_NAS_IDENTIFICATION_MISMATCH;
nakcause = "NAS Identifier is not matched";
goto search_done;
}
memset(username, 0, sizeof(username));
radius_get_string_attr(req, RADIUS_TYPE_USER_NAME, username,
sizeof(username));
snprintf(buf, sizeof(buf), "%08X", _this->boot_id);
if (radius_get_string_attr(req, RADIUS_TYPE_ACCT_SESSION_ID, attr,
sizeof(attr)) == 0) {
ppp = NULL;
if (strlen(attr) != 16 || strncmp(buf, attr, 8) != 0) {
cause = RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE;
nakcause = "Session-Id is wrong";
goto search_done;
}
ppp_id = strtoull(attr + 8, &endp, 16);
if (*endp != '\0' || errno == ERANGE || ppp_id == ULLONG_MAX) {
cause = RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE;
nakcause = "Session-Id is invalid";
goto search_done;
}
if ((ppp = npppd_get_ppp_by_id(_this, ppp_id)) == NULL)
goto search_done;
if (username[0] != '\0' &&
strcmp(username, ppp->username) != 0) {
cause = RADIUS_ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE;
nakcause = "User-Name is not matched";
goto search_done;
}
ppp_stop(ppp, reason);
n++;
} else if (username[0] != '\0') {
users = npppd_get_ppp_by_user(_this, username);
if (users == NULL)
goto search_done;
memset(&ina, 0, sizeof(ina));
radius_get_uint32_attr(req, RADIUS_TYPE_FRAMED_IP_ADDRESS,
&ina.s_addr);
slist_itr_first(users);
while ((ppp = slist_itr_next(users)) != NULL) {
if (ntohl(ina.s_addr) != 0 &&
ina.s_addr != ppp->ppp_framed_ip_address.s_addr)
continue;
ppp_stop(ppp, reason);
n++;
}
} else if (radius_get_uint32_attr(req, RADIUS_TYPE_FRAMED_IP_ADDRESS,
&ina.s_addr) == 0) {
ppp = npppd_get_ppp_by_ip(_this, ina);
if (ppp != NULL) {
ppp_stop(ppp, reason);
n++;
}
}
search_done:
if (n > 0)
code = RADIUS_CODE_DISCONNECT_ACK;
else {
if (nakcause == NULL)
nakcause = "session not found";
if (cause == 0)
cause = RADIUS_ERROR_CAUSE_SESSION_NOT_FOUND;
code = RADIUS_CODE_DISCONNECT_NAK;
}
send:
res = radius_new_response_packet(code, req);
if (res == NULL) {
log_printf(LOG_WARNING, "%s: radius_new_response_packet: %m",
__func__);
goto out;
}
if (cause != 0)
radius_put_uint32_attr(res, RADIUS_TYPE_ERROR_CAUSE, cause);
radius_set_response_authenticator(res, client->secret);
if (radius_sendto(listen->sock, res, 0, (struct sockaddr *)&ss, sslen)
== -1)
log_printf(LOG_WARNING, "%s: sendto(): %m", __func__);
log_printf(LOG_INFO, "radius send %s to %s%s%s",
(code == RADIUS_CODE_DISCONNECT_ACK)? "Disconnect-ACK" :
(code == RADIUS_CODE_DISCONNECT_NAK)? "Disconnect-NAK" : "CoA-NAK",
addrport_tostring((struct sockaddr *)&ss, ss.ss_len, buf,
sizeof(buf)), (nakcause)? ": " : "", (nakcause)? nakcause : "");
out:
radius_delete_packet(req);
if (res != NULL)
radius_delete_packet(res);
}
void
npppd_radius_dae_listen_stop(struct npppd_radius_dae_listen *listen)
{
char buf[80];
if (listen->sock >= 0) {
log_printf(LOG_INFO, "radius Shutdown %s/udp (DAE)",
addrport_tostring((struct sockaddr *)&listen->addr,
listen->addr.sin4.sin_len, buf, sizeof(buf)));
event_del(&listen->evsock);
close(listen->sock);
listen->sock = -1;
}
}