RADIUS: Add minimal accounting server support
This can be used to test RADIUS Accounting in hostapd. Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
parent
eac674402f
commit
a1dd890a85
8 changed files with 244 additions and 8 deletions
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* hostapd / Configuration file parser
|
* hostapd / Configuration file parser
|
||||||
* Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
|
* Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
|
||||||
*
|
*
|
||||||
* This software may be distributed under the terms of the BSD license.
|
* This software may be distributed under the terms of the BSD license.
|
||||||
* See README for more details.
|
* See README for more details.
|
||||||
|
@ -2188,6 +2188,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
|
||||||
bss->radius_server_clients = os_strdup(pos);
|
bss->radius_server_clients = os_strdup(pos);
|
||||||
} else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
|
} else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
|
||||||
bss->radius_server_auth_port = atoi(pos);
|
bss->radius_server_auth_port = atoi(pos);
|
||||||
|
} else if (os_strcmp(buf, "radius_server_acct_port") == 0) {
|
||||||
|
bss->radius_server_acct_port = atoi(pos);
|
||||||
} else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
|
} else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
|
||||||
bss->radius_server_ipv6 = atoi(pos);
|
bss->radius_server_ipv6 = atoi(pos);
|
||||||
#endif /* RADIUS_SERVER */
|
#endif /* RADIUS_SERVER */
|
||||||
|
|
|
@ -971,6 +971,11 @@ own_ip_addr=127.0.0.1
|
||||||
# The UDP port number for the RADIUS authentication server
|
# The UDP port number for the RADIUS authentication server
|
||||||
#radius_server_auth_port=1812
|
#radius_server_auth_port=1812
|
||||||
|
|
||||||
|
# The UDP port number for the RADIUS accounting server
|
||||||
|
# Commenting this out or setting this to 0 can be used to disable RADIUS
|
||||||
|
# accounting while still enabling RADIUS authentication.
|
||||||
|
#radius_server_acct_port=1813
|
||||||
|
|
||||||
# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API)
|
# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API)
|
||||||
#radius_server_ipv6=1
|
#radius_server_ipv6=1
|
||||||
|
|
||||||
|
|
|
@ -311,6 +311,7 @@ struct hostapd_bss_config {
|
||||||
|
|
||||||
char *radius_server_clients;
|
char *radius_server_clients;
|
||||||
int radius_server_auth_port;
|
int radius_server_auth_port;
|
||||||
|
int radius_server_acct_port;
|
||||||
int radius_server_ipv6;
|
int radius_server_ipv6;
|
||||||
|
|
||||||
char *test_socket; /* UNIX domain socket path for driver_test */
|
char *test_socket; /* UNIX domain socket path for driver_test */
|
||||||
|
|
|
@ -92,6 +92,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
|
||||||
os_memset(&srv, 0, sizeof(srv));
|
os_memset(&srv, 0, sizeof(srv));
|
||||||
srv.client_file = conf->radius_server_clients;
|
srv.client_file = conf->radius_server_clients;
|
||||||
srv.auth_port = conf->radius_server_auth_port;
|
srv.auth_port = conf->radius_server_auth_port;
|
||||||
|
srv.acct_port = conf->radius_server_acct_port;
|
||||||
srv.conf_ctx = hapd;
|
srv.conf_ctx = hapd;
|
||||||
srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
|
srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
|
||||||
srv.ssl_ctx = hapd->ssl_ctx;
|
srv.ssl_ctx = hapd->ssl_ctx;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* RADIUS message processing
|
* RADIUS message processing
|
||||||
* Copyright (c) 2002-2009, 2011-2012, Jouni Malinen <j@w1.fi>
|
* Copyright (c) 2002-2009, 2011-2014, Jouni Malinen <j@w1.fi>
|
||||||
*
|
*
|
||||||
* This software may be distributed under the terms of the BSD license.
|
* This software may be distributed under the terms of the BSD license.
|
||||||
* See README for more details.
|
* See README for more details.
|
||||||
|
@ -473,6 +473,27 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
|
||||||
|
size_t secret_len, const u8 *req_authenticator)
|
||||||
|
{
|
||||||
|
const u8 *addr[2];
|
||||||
|
size_t len[2];
|
||||||
|
|
||||||
|
msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
|
||||||
|
os_memcpy(msg->hdr->authenticator, req_authenticator, MD5_MAC_LEN);
|
||||||
|
addr[0] = wpabuf_head(msg->buf);
|
||||||
|
len[0] = wpabuf_len(msg->buf);
|
||||||
|
addr[1] = secret;
|
||||||
|
len[1] = secret_len;
|
||||||
|
md5_vector(2, addr, len, msg->hdr->authenticator);
|
||||||
|
|
||||||
|
if (wpabuf_len(msg->buf) > 0xffff) {
|
||||||
|
wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
|
||||||
|
(unsigned long) wpabuf_len(msg->buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
|
int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
|
||||||
size_t secret_len)
|
size_t secret_len)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* RADIUS message processing
|
* RADIUS message processing
|
||||||
* Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
|
* Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen <j@w1.fi>
|
||||||
*
|
*
|
||||||
* This software may be distributed under the terms of the BSD license.
|
* This software may be distributed under the terms of the BSD license.
|
||||||
* See README for more details.
|
* See README for more details.
|
||||||
|
@ -204,6 +204,9 @@ int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
|
||||||
const struct radius_hdr *req_hdr);
|
const struct radius_hdr *req_hdr);
|
||||||
void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
|
void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
|
||||||
size_t secret_len);
|
size_t secret_len);
|
||||||
|
void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
|
||||||
|
size_t secret_len,
|
||||||
|
const u8 *req_authenticator);
|
||||||
int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
|
int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
|
||||||
size_t secret_len);
|
size_t secret_len);
|
||||||
int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
|
int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* RADIUS authentication server
|
* RADIUS authentication server
|
||||||
* Copyright (c) 2005-2009, 2011, Jouni Malinen <j@w1.fi>
|
* Copyright (c) 2005-2009, 2011-2014, Jouni Malinen <j@w1.fi>
|
||||||
*
|
*
|
||||||
* This software may be distributed under the terms of the BSD license.
|
* This software may be distributed under the terms of the BSD license.
|
||||||
* See README for more details.
|
* See README for more details.
|
||||||
|
@ -49,6 +49,13 @@ struct radius_server_counters {
|
||||||
u32 bad_authenticators;
|
u32 bad_authenticators;
|
||||||
u32 packets_dropped;
|
u32 packets_dropped;
|
||||||
u32 unknown_types;
|
u32 unknown_types;
|
||||||
|
|
||||||
|
u32 acct_requests;
|
||||||
|
u32 invalid_acct_requests;
|
||||||
|
u32 acct_responses;
|
||||||
|
u32 malformed_acct_requests;
|
||||||
|
u32 acct_bad_authenticators;
|
||||||
|
u32 unknown_acct_types;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,6 +105,11 @@ struct radius_server_data {
|
||||||
*/
|
*/
|
||||||
int auth_sock;
|
int auth_sock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* acct_sock - Socket for RADIUS accounting messages
|
||||||
|
*/
|
||||||
|
int acct_sock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clients - List of authorized RADIUS clients
|
* clients - List of authorized RADIUS clients
|
||||||
*/
|
*/
|
||||||
|
@ -979,6 +991,140 @@ fail:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void radius_server_receive_acct(int sock, void *eloop_ctx,
|
||||||
|
void *sock_ctx)
|
||||||
|
{
|
||||||
|
struct radius_server_data *data = eloop_ctx;
|
||||||
|
u8 *buf = NULL;
|
||||||
|
union {
|
||||||
|
struct sockaddr_storage ss;
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
#ifdef CONFIG_IPV6
|
||||||
|
struct sockaddr_in6 sin6;
|
||||||
|
#endif /* CONFIG_IPV6 */
|
||||||
|
} from;
|
||||||
|
socklen_t fromlen;
|
||||||
|
int len, res;
|
||||||
|
struct radius_client *client = NULL;
|
||||||
|
struct radius_msg *msg = NULL, *resp = NULL;
|
||||||
|
char abuf[50];
|
||||||
|
int from_port = 0;
|
||||||
|
struct radius_hdr *hdr;
|
||||||
|
struct wpabuf *rbuf;
|
||||||
|
|
||||||
|
buf = os_malloc(RADIUS_MAX_MSG_LEN);
|
||||||
|
if (buf == NULL) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
fromlen = sizeof(from);
|
||||||
|
len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
|
||||||
|
(struct sockaddr *) &from.ss, &fromlen);
|
||||||
|
if (len < 0) {
|
||||||
|
wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_IPV6
|
||||||
|
if (data->ipv6) {
|
||||||
|
if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
|
||||||
|
sizeof(abuf)) == NULL)
|
||||||
|
abuf[0] = '\0';
|
||||||
|
from_port = ntohs(from.sin6.sin6_port);
|
||||||
|
RADIUS_DEBUG("Received %d bytes from %s:%d",
|
||||||
|
len, abuf, from_port);
|
||||||
|
|
||||||
|
client = radius_server_get_client(data,
|
||||||
|
(struct in_addr *)
|
||||||
|
&from.sin6.sin6_addr, 1);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_IPV6 */
|
||||||
|
|
||||||
|
if (!data->ipv6) {
|
||||||
|
os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
|
||||||
|
from_port = ntohs(from.sin.sin_port);
|
||||||
|
RADIUS_DEBUG("Received %d bytes from %s:%d",
|
||||||
|
len, abuf, from_port);
|
||||||
|
|
||||||
|
client = radius_server_get_client(data, &from.sin.sin_addr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
RADIUS_DUMP("Received data", buf, len);
|
||||||
|
|
||||||
|
if (client == NULL) {
|
||||||
|
RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
|
||||||
|
data->counters.invalid_acct_requests++;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = radius_msg_parse(buf, len);
|
||||||
|
if (msg == NULL) {
|
||||||
|
RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
|
||||||
|
data->counters.malformed_acct_requests++;
|
||||||
|
client->counters.malformed_acct_requests++;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_free(buf);
|
||||||
|
buf = NULL;
|
||||||
|
|
||||||
|
if (wpa_debug_level <= MSG_MSGDUMP) {
|
||||||
|
radius_msg_dump(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_REQUEST) {
|
||||||
|
RADIUS_DEBUG("Unexpected RADIUS code %d",
|
||||||
|
radius_msg_get_hdr(msg)->code);
|
||||||
|
data->counters.unknown_acct_types++;
|
||||||
|
client->counters.unknown_acct_types++;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->counters.acct_requests++;
|
||||||
|
client->counters.acct_requests++;
|
||||||
|
|
||||||
|
if (radius_msg_verify_acct_req(msg, (u8 *) client->shared_secret,
|
||||||
|
client->shared_secret_len)) {
|
||||||
|
RADIUS_DEBUG("Invalid Authenticator from %s", abuf);
|
||||||
|
data->counters.acct_bad_authenticators++;
|
||||||
|
client->counters.acct_bad_authenticators++;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Write accounting information to a file or database */
|
||||||
|
|
||||||
|
hdr = radius_msg_get_hdr(msg);
|
||||||
|
|
||||||
|
resp = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, hdr->identifier);
|
||||||
|
if (resp == NULL)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret,
|
||||||
|
client->shared_secret_len,
|
||||||
|
hdr->authenticator);
|
||||||
|
|
||||||
|
RADIUS_DEBUG("Reply to %s:%d", abuf, from_port);
|
||||||
|
if (wpa_debug_level <= MSG_MSGDUMP) {
|
||||||
|
radius_msg_dump(resp);
|
||||||
|
}
|
||||||
|
rbuf = radius_msg_get_buf(resp);
|
||||||
|
data->counters.acct_responses++;
|
||||||
|
client->counters.acct_responses++;
|
||||||
|
res = sendto(data->acct_sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0,
|
||||||
|
(struct sockaddr *) &from.ss, fromlen);
|
||||||
|
if (res < 0) {
|
||||||
|
wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
radius_msg_free(resp);
|
||||||
|
radius_msg_free(msg);
|
||||||
|
os_free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int radius_server_disable_pmtu_discovery(int s)
|
static int radius_server_disable_pmtu_discovery(int s)
|
||||||
{
|
{
|
||||||
int r = -1;
|
int r = -1;
|
||||||
|
@ -1329,6 +1475,29 @@ radius_server_init(struct radius_server_conf *conf)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conf->acct_port) {
|
||||||
|
#ifdef CONFIG_IPV6
|
||||||
|
if (conf->ipv6)
|
||||||
|
data->acct_sock = radius_server_open_socket6(
|
||||||
|
conf->acct_port);
|
||||||
|
else
|
||||||
|
#endif /* CONFIG_IPV6 */
|
||||||
|
data->acct_sock = radius_server_open_socket(conf->acct_port);
|
||||||
|
if (data->acct_sock < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS accounting server");
|
||||||
|
radius_server_deinit(data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (eloop_register_read_sock(data->acct_sock,
|
||||||
|
radius_server_receive_acct,
|
||||||
|
data, NULL)) {
|
||||||
|
radius_server_deinit(data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data->acct_sock = -1;
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1347,6 +1516,11 @@ void radius_server_deinit(struct radius_server_data *data)
|
||||||
close(data->auth_sock);
|
close(data->auth_sock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data->acct_sock >= 0) {
|
||||||
|
eloop_unregister_read_sock(data->acct_sock);
|
||||||
|
close(data->acct_sock);
|
||||||
|
}
|
||||||
|
|
||||||
radius_server_free_clients(data, data->clients);
|
radius_server_free_clients(data, data->clients);
|
||||||
|
|
||||||
os_free(data->pac_opaque_encr_key);
|
os_free(data->pac_opaque_encr_key);
|
||||||
|
@ -1410,7 +1584,13 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
|
||||||
"radiusAuthServTotalMalformedAccessRequests=%u\n"
|
"radiusAuthServTotalMalformedAccessRequests=%u\n"
|
||||||
"radiusAuthServTotalBadAuthenticators=%u\n"
|
"radiusAuthServTotalBadAuthenticators=%u\n"
|
||||||
"radiusAuthServTotalPacketsDropped=%u\n"
|
"radiusAuthServTotalPacketsDropped=%u\n"
|
||||||
"radiusAuthServTotalUnknownTypes=%u\n",
|
"radiusAuthServTotalUnknownTypes=%u\n"
|
||||||
|
"radiusAccServTotalRequests=%u\n"
|
||||||
|
"radiusAccServTotalInvalidRequests=%u\n"
|
||||||
|
"radiusAccServTotalResponses=%u\n"
|
||||||
|
"radiusAccServTotalMalformedRequests=%u\n"
|
||||||
|
"radiusAccServTotalBadAuthenticators=%u\n"
|
||||||
|
"radiusAccServTotalUnknownTypes=%u\n",
|
||||||
data->counters.access_requests,
|
data->counters.access_requests,
|
||||||
data->counters.invalid_requests,
|
data->counters.invalid_requests,
|
||||||
data->counters.dup_access_requests,
|
data->counters.dup_access_requests,
|
||||||
|
@ -1420,7 +1600,13 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
|
||||||
data->counters.malformed_access_requests,
|
data->counters.malformed_access_requests,
|
||||||
data->counters.bad_authenticators,
|
data->counters.bad_authenticators,
|
||||||
data->counters.packets_dropped,
|
data->counters.packets_dropped,
|
||||||
data->counters.unknown_types);
|
data->counters.unknown_types,
|
||||||
|
data->counters.acct_requests,
|
||||||
|
data->counters.invalid_acct_requests,
|
||||||
|
data->counters.acct_responses,
|
||||||
|
data->counters.malformed_acct_requests,
|
||||||
|
data->counters.acct_bad_authenticators,
|
||||||
|
data->counters.unknown_acct_types);
|
||||||
if (ret < 0 || ret >= end - pos) {
|
if (ret < 0 || ret >= end - pos) {
|
||||||
*pos = '\0';
|
*pos = '\0';
|
||||||
return pos - buf;
|
return pos - buf;
|
||||||
|
@ -1455,7 +1641,13 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
|
||||||
"radiusAuthServMalformedAccessRequests=%u\n"
|
"radiusAuthServMalformedAccessRequests=%u\n"
|
||||||
"radiusAuthServBadAuthenticators=%u\n"
|
"radiusAuthServBadAuthenticators=%u\n"
|
||||||
"radiusAuthServPacketsDropped=%u\n"
|
"radiusAuthServPacketsDropped=%u\n"
|
||||||
"radiusAuthServUnknownTypes=%u\n",
|
"radiusAuthServUnknownTypes=%u\n"
|
||||||
|
"radiusAccServTotalRequests=%u\n"
|
||||||
|
"radiusAccServTotalInvalidRequests=%u\n"
|
||||||
|
"radiusAccServTotalResponses=%u\n"
|
||||||
|
"radiusAccServTotalMalformedRequests=%u\n"
|
||||||
|
"radiusAccServTotalBadAuthenticators=%u\n"
|
||||||
|
"radiusAccServTotalUnknownTypes=%u\n",
|
||||||
idx,
|
idx,
|
||||||
abuf, mbuf,
|
abuf, mbuf,
|
||||||
cli->counters.access_requests,
|
cli->counters.access_requests,
|
||||||
|
@ -1466,7 +1658,13 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
|
||||||
cli->counters.malformed_access_requests,
|
cli->counters.malformed_access_requests,
|
||||||
cli->counters.bad_authenticators,
|
cli->counters.bad_authenticators,
|
||||||
cli->counters.packets_dropped,
|
cli->counters.packets_dropped,
|
||||||
cli->counters.unknown_types);
|
cli->counters.unknown_types,
|
||||||
|
cli->counters.acct_requests,
|
||||||
|
cli->counters.invalid_acct_requests,
|
||||||
|
cli->counters.acct_responses,
|
||||||
|
cli->counters.malformed_acct_requests,
|
||||||
|
cli->counters.acct_bad_authenticators,
|
||||||
|
cli->counters.unknown_acct_types);
|
||||||
if (ret < 0 || ret >= end - pos) {
|
if (ret < 0 || ret >= end - pos) {
|
||||||
*pos = '\0';
|
*pos = '\0';
|
||||||
return pos - buf;
|
return pos - buf;
|
||||||
|
|
|
@ -21,6 +21,11 @@ struct radius_server_conf {
|
||||||
*/
|
*/
|
||||||
int auth_port;
|
int auth_port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* acct_port - UDP port to listen to as an accounting server
|
||||||
|
*/
|
||||||
|
int acct_port;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* client_file - RADIUS client configuration file
|
* client_file - RADIUS client configuration file
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue