Add preliminary RADIUS dynamic authorization server (RFC 5176)
This adds the basic DAS mechanism to enable hostapd to be configured to request dynamic authorization requests (Disconnect-Request and CoA-Request). This commit does not add actual processing of the requests, i.e., this will only receive and authenticate the requests and NAK them regardless of what operation is requested. Signed-hostap: Jouni Malinen <j@w1.fi>
This commit is contained in:
parent
af35e7af7f
commit
b031338cf0
11 changed files with 469 additions and 4 deletions
|
@ -110,6 +110,7 @@ CONFIG_NO_ACCOUNTING=y
|
||||||
else
|
else
|
||||||
OBJS += ../src/radius/radius.o
|
OBJS += ../src/radius/radius.o
|
||||||
OBJS += ../src/radius/radius_client.o
|
OBJS += ../src/radius/radius_client.o
|
||||||
|
OBJS += ../src/radius/radius_das.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef CONFIG_NO_ACCOUNTING
|
ifdef CONFIG_NO_ACCOUNTING
|
||||||
|
|
|
@ -561,6 +561,34 @@ hostapd_parse_radius_attr(const char *value)
|
||||||
|
|
||||||
return attr;
|
return attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_parse_das_client(struct hostapd_bss_config *bss,
|
||||||
|
const char *val)
|
||||||
|
{
|
||||||
|
char *secret;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
secret = os_strchr(val, ' ');
|
||||||
|
if (secret == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
secret++;
|
||||||
|
len = os_strlen(secret);
|
||||||
|
|
||||||
|
if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
os_free(bss->radius_das_shared_secret);
|
||||||
|
bss->radius_das_shared_secret = os_malloc(len);
|
||||||
|
if (bss->radius_das_shared_secret == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
os_memcpy(bss->radius_das_shared_secret, secret, len);
|
||||||
|
bss->radius_das_shared_secret_len = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif /* CONFIG_NO_RADIUS */
|
#endif /* CONFIG_NO_RADIUS */
|
||||||
|
|
||||||
|
|
||||||
|
@ -1657,6 +1685,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
|
||||||
a = a->next;
|
a = a->next;
|
||||||
a->next = attr;
|
a->next = attr;
|
||||||
}
|
}
|
||||||
|
} else if (os_strcmp(buf, "radius_das_port") == 0) {
|
||||||
|
bss->radius_das_port = atoi(pos);
|
||||||
|
} else if (os_strcmp(buf, "radius_das_client") == 0) {
|
||||||
|
if (hostapd_parse_das_client(bss, pos) < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "Line %d: invalid "
|
||||||
|
"DAS client", line);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
#endif /* CONFIG_NO_RADIUS */
|
#endif /* CONFIG_NO_RADIUS */
|
||||||
} else if (os_strcmp(buf, "auth_algs") == 0) {
|
} else if (os_strcmp(buf, "auth_algs") == 0) {
|
||||||
bss->auth_algs = atoi(pos);
|
bss->auth_algs = atoi(pos);
|
||||||
|
|
|
@ -696,6 +696,19 @@ own_ip_addr=127.0.0.1
|
||||||
# Operator-Name = "Operator"
|
# Operator-Name = "Operator"
|
||||||
#radius_acct_req_attr=126:s:Operator
|
#radius_acct_req_attr=126:s:Operator
|
||||||
|
|
||||||
|
# Dynamic Authorization Extensions (RFC 5176)
|
||||||
|
# This mechanism can be used to allow dynamic changes to user session based on
|
||||||
|
# commands from a RADIUS server (or some other disconnect client that has the
|
||||||
|
# needed session information). For example, Disconnect message can be used to
|
||||||
|
# request an associated station to be disconnected.
|
||||||
|
#
|
||||||
|
# This is disabled by default. Set radius_das_port to non-zero UDP port
|
||||||
|
# number to enable.
|
||||||
|
#radius_das_port=3799
|
||||||
|
#
|
||||||
|
# DAS client (the host that can send Disconnect/CoA requests) and shared secret
|
||||||
|
#radius_das_client=192.168.1.123 shared secret here
|
||||||
|
|
||||||
##### RADIUS authentication server configuration ##############################
|
##### RADIUS authentication server configuration ##############################
|
||||||
|
|
||||||
# hostapd can be used as a RADIUS authentication server for other hosts. This
|
# hostapd can be used as a RADIUS authentication server for other hosts. This
|
||||||
|
|
|
@ -432,6 +432,7 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
|
||||||
os_free(conf->radius_server_clients);
|
os_free(conf->radius_server_clients);
|
||||||
os_free(conf->test_socket);
|
os_free(conf->test_socket);
|
||||||
os_free(conf->radius);
|
os_free(conf->radius);
|
||||||
|
os_free(conf->radius_das_shared_secret);
|
||||||
hostapd_config_free_vlan(conf);
|
hostapd_config_free_vlan(conf);
|
||||||
if (conf->ssid.dyn_vlan_keys) {
|
if (conf->ssid.dyn_vlan_keys) {
|
||||||
struct hostapd_ssid *ssid = &conf->ssid;
|
struct hostapd_ssid *ssid = &conf->ssid;
|
||||||
|
|
|
@ -186,6 +186,10 @@ struct hostapd_bss_config {
|
||||||
int radius_request_cui;
|
int radius_request_cui;
|
||||||
struct hostapd_radius_attr *radius_auth_req_attr;
|
struct hostapd_radius_attr *radius_auth_req_attr;
|
||||||
struct hostapd_radius_attr *radius_acct_req_attr;
|
struct hostapd_radius_attr *radius_acct_req_attr;
|
||||||
|
int radius_das_port;
|
||||||
|
struct hostapd_ip_addr radius_das_client_addr;
|
||||||
|
u8 *radius_das_shared_secret;
|
||||||
|
size_t radius_das_shared_secret_len;
|
||||||
|
|
||||||
struct hostapd_ssid ssid;
|
struct hostapd_ssid ssid;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* hostapd / Initialization and configuration
|
* hostapd / Initialization and configuration
|
||||||
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
* Copyright (c) 2002-2012, 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.
|
||||||
|
@ -12,6 +12,7 @@
|
||||||
#include "utils/eloop.h"
|
#include "utils/eloop.h"
|
||||||
#include "common/ieee802_11_defs.h"
|
#include "common/ieee802_11_defs.h"
|
||||||
#include "radius/radius_client.h"
|
#include "radius/radius_client.h"
|
||||||
|
#include "radius/radius_das.h"
|
||||||
#include "drivers/driver.h"
|
#include "drivers/driver.h"
|
||||||
#include "hostapd.h"
|
#include "hostapd.h"
|
||||||
#include "authsrv.h"
|
#include "authsrv.h"
|
||||||
|
@ -241,6 +242,8 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
|
||||||
#ifndef CONFIG_NO_RADIUS
|
#ifndef CONFIG_NO_RADIUS
|
||||||
radius_client_deinit(hapd->radius);
|
radius_client_deinit(hapd->radius);
|
||||||
hapd->radius = NULL;
|
hapd->radius = NULL;
|
||||||
|
radius_das_deinit(hapd->radius_das);
|
||||||
|
hapd->radius_das = NULL;
|
||||||
#endif /* CONFIG_NO_RADIUS */
|
#endif /* CONFIG_NO_RADIUS */
|
||||||
|
|
||||||
hostapd_deinit_wps(hapd);
|
hostapd_deinit_wps(hapd);
|
||||||
|
@ -627,6 +630,22 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
|
||||||
wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
|
wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hapd->conf->radius_das_port) {
|
||||||
|
struct radius_das_conf das_conf;
|
||||||
|
os_memset(&das_conf, 0, sizeof(das_conf));
|
||||||
|
das_conf.port = hapd->conf->radius_das_port;
|
||||||
|
das_conf.shared_secret = hapd->conf->radius_das_shared_secret;
|
||||||
|
das_conf.shared_secret_len =
|
||||||
|
hapd->conf->radius_das_shared_secret_len;
|
||||||
|
das_conf.client_addr = &hapd->conf->radius_das_client_addr;
|
||||||
|
hapd->radius_das = radius_das_init(&das_conf);
|
||||||
|
if (hapd->radius_das == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
|
||||||
|
"failed.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif /* CONFIG_NO_RADIUS */
|
#endif /* CONFIG_NO_RADIUS */
|
||||||
|
|
||||||
if (hostapd_acl_init(hapd)) {
|
if (hostapd_acl_init(hapd)) {
|
||||||
|
|
|
@ -85,6 +85,7 @@ struct hostapd_data {
|
||||||
|
|
||||||
struct radius_client_data *radius;
|
struct radius_client_data *radius;
|
||||||
u32 acct_session_id_hi, acct_session_id_lo;
|
u32 acct_session_id_hi, acct_session_id_lo;
|
||||||
|
struct radius_das_data *radius_das;
|
||||||
|
|
||||||
struct iapp_data *iapp;
|
struct iapp_data *iapp;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* RADIUS message processing
|
* RADIUS message processing
|
||||||
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
* Copyright (c) 2002-2009, 2011-2012, 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.
|
||||||
|
@ -147,6 +147,12 @@ static const char *radius_code_string(u8 code)
|
||||||
case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
|
case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
|
||||||
case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
|
case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
|
||||||
case RADIUS_CODE_RESERVED: return "Reserved";
|
case RADIUS_CODE_RESERVED: return "Reserved";
|
||||||
|
case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request";
|
||||||
|
case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK";
|
||||||
|
case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK";
|
||||||
|
case RADIUS_CODE_COA_REQUEST: return "CoA-Request";
|
||||||
|
case RADIUS_CODE_COA_ACK: return "CoA-ACK";
|
||||||
|
case RADIUS_CODE_COA_NAK: return "CoA-NAK";
|
||||||
default: return "?Unknown?";
|
default: return "?Unknown?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,6 +231,7 @@ static struct radius_attr_type radius_attrs[] =
|
||||||
{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
|
{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
|
||||||
RADIUS_ATTR_TEXT },
|
RADIUS_ATTR_TEXT },
|
||||||
{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
|
{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
|
||||||
|
{ RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }
|
||||||
};
|
};
|
||||||
#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
|
#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
|
||||||
|
|
||||||
|
@ -406,6 +413,45 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
|
||||||
|
size_t secret_len,
|
||||||
|
const struct radius_hdr *req_hdr)
|
||||||
|
{
|
||||||
|
const u8 *addr[2];
|
||||||
|
size_t len[2];
|
||||||
|
u8 auth[MD5_MAC_LEN];
|
||||||
|
struct radius_attr_hdr *attr;
|
||||||
|
|
||||||
|
os_memset(auth, 0, MD5_MAC_LEN);
|
||||||
|
attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
|
||||||
|
auth, MD5_MAC_LEN);
|
||||||
|
if (attr == NULL) {
|
||||||
|
wpa_printf(MSG_WARNING, "Could not add Message-Authenticator");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg->hdr->length = htons(wpabuf_len(msg->buf));
|
||||||
|
os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
|
||||||
|
hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
|
||||||
|
wpabuf_len(msg->buf), (u8 *) (attr + 1));
|
||||||
|
|
||||||
|
/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
|
||||||
|
addr[0] = wpabuf_head_u8(msg->buf);
|
||||||
|
len[0] = wpabuf_len(msg->buf);
|
||||||
|
addr[1] = secret;
|
||||||
|
len[1] = secret_len;
|
||||||
|
if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (wpabuf_len(msg->buf) > 0xffff) {
|
||||||
|
wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
|
||||||
|
(unsigned long) wpabuf_len(msg->buf));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -427,6 +473,88 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
|
||||||
|
size_t secret_len)
|
||||||
|
{
|
||||||
|
const u8 *addr[4];
|
||||||
|
size_t len[4];
|
||||||
|
u8 zero[MD5_MAC_LEN];
|
||||||
|
u8 hash[MD5_MAC_LEN];
|
||||||
|
|
||||||
|
os_memset(zero, 0, sizeof(zero));
|
||||||
|
addr[0] = (u8 *) msg->hdr;
|
||||||
|
len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
|
||||||
|
addr[1] = zero;
|
||||||
|
len[1] = MD5_MAC_LEN;
|
||||||
|
addr[2] = (u8 *) (msg->hdr + 1);
|
||||||
|
len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
|
||||||
|
addr[3] = secret;
|
||||||
|
len[3] = secret_len;
|
||||||
|
md5_vector(4, addr, len, hash);
|
||||||
|
return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
|
||||||
|
size_t secret_len)
|
||||||
|
{
|
||||||
|
const u8 *addr[4];
|
||||||
|
size_t len[4];
|
||||||
|
u8 zero[MD5_MAC_LEN];
|
||||||
|
u8 hash[MD5_MAC_LEN];
|
||||||
|
u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
|
||||||
|
u8 orig_authenticator[16];
|
||||||
|
|
||||||
|
struct radius_attr_hdr *attr = NULL, *tmp;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
os_memset(zero, 0, sizeof(zero));
|
||||||
|
addr[0] = (u8 *) msg->hdr;
|
||||||
|
len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
|
||||||
|
addr[1] = zero;
|
||||||
|
len[1] = MD5_MAC_LEN;
|
||||||
|
addr[2] = (u8 *) (msg->hdr + 1);
|
||||||
|
len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
|
||||||
|
addr[3] = secret;
|
||||||
|
len[3] = secret_len;
|
||||||
|
md5_vector(4, addr, len, hash);
|
||||||
|
if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
for (i = 0; i < msg->attr_used; i++) {
|
||||||
|
tmp = radius_get_attr_hdr(msg, i);
|
||||||
|
if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
|
||||||
|
if (attr != NULL) {
|
||||||
|
wpa_printf(MSG_WARNING, "Multiple "
|
||||||
|
"Message-Authenticator attributes "
|
||||||
|
"in RADIUS message");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
attr = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attr == NULL) {
|
||||||
|
/* Message-Authenticator is MAY; not required */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_memcpy(orig, attr + 1, MD5_MAC_LEN);
|
||||||
|
os_memset(attr + 1, 0, MD5_MAC_LEN);
|
||||||
|
os_memcpy(orig_authenticator, msg->hdr->authenticator,
|
||||||
|
sizeof(orig_authenticator));
|
||||||
|
os_memset(msg->hdr->authenticator, 0,
|
||||||
|
sizeof(msg->hdr->authenticator));
|
||||||
|
hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
|
||||||
|
wpabuf_len(msg->buf), auth);
|
||||||
|
os_memcpy(attr + 1, orig, MD5_MAC_LEN);
|
||||||
|
os_memcpy(msg->hdr->authenticator, orig_authenticator,
|
||||||
|
sizeof(orig_authenticator));
|
||||||
|
|
||||||
|
return os_memcmp(orig, auth, MD5_MAC_LEN) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int radius_msg_add_attr_to_array(struct radius_msg *msg,
|
static int radius_msg_add_attr_to_array(struct radius_msg *msg,
|
||||||
struct radius_attr_hdr *attr)
|
struct radius_attr_hdr *attr)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* RADIUS message processing
|
* RADIUS message processing
|
||||||
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
* Copyright (c) 2002-2009, 2012, 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.
|
||||||
|
@ -31,6 +31,12 @@ enum { RADIUS_CODE_ACCESS_REQUEST = 1,
|
||||||
RADIUS_CODE_ACCESS_CHALLENGE = 11,
|
RADIUS_CODE_ACCESS_CHALLENGE = 11,
|
||||||
RADIUS_CODE_STATUS_SERVER = 12,
|
RADIUS_CODE_STATUS_SERVER = 12,
|
||||||
RADIUS_CODE_STATUS_CLIENT = 13,
|
RADIUS_CODE_STATUS_CLIENT = 13,
|
||||||
|
RADIUS_CODE_DISCONNECT_REQUEST = 40,
|
||||||
|
RADIUS_CODE_DISCONNECT_ACK = 41,
|
||||||
|
RADIUS_CODE_DISCONNECT_NAK = 42,
|
||||||
|
RADIUS_CODE_COA_REQUEST = 43,
|
||||||
|
RADIUS_CODE_COA_ACK = 44,
|
||||||
|
RADIUS_CODE_COA_NAK = 45,
|
||||||
RADIUS_CODE_RESERVED = 255
|
RADIUS_CODE_RESERVED = 255
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,7 +89,8 @@ enum { RADIUS_ATTR_USER_NAME = 1,
|
||||||
RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
|
RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
|
||||||
RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
|
RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
|
||||||
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
|
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
|
||||||
RADIUS_ATTR_NAS_IPV6_ADDRESS = 95
|
RADIUS_ATTR_NAS_IPV6_ADDRESS = 95,
|
||||||
|
RADIUS_ATTR_ERROR_CAUSE = 101
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -192,8 +199,15 @@ int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
|
||||||
size_t secret_len);
|
size_t secret_len);
|
||||||
int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
|
int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
|
||||||
size_t secret_len, const u8 *req_authenticator);
|
size_t secret_len, const u8 *req_authenticator);
|
||||||
|
int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
|
||||||
|
size_t secret_len,
|
||||||
|
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);
|
||||||
|
int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
|
||||||
|
size_t secret_len);
|
||||||
|
int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
|
||||||
|
size_t secret_len);
|
||||||
struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
|
struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
|
||||||
const u8 *data, size_t data_len);
|
const u8 *data, size_t data_len);
|
||||||
struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
|
struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
|
||||||
|
|
222
src/radius/radius_das.c
Normal file
222
src/radius/radius_das.c
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
* RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
|
||||||
|
* Copyright (c) 2012, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "includes.h"
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
#include "utils/common.h"
|
||||||
|
#include "utils/eloop.h"
|
||||||
|
#include "utils/ip_addr.h"
|
||||||
|
#include "radius.h"
|
||||||
|
#include "radius_das.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern int wpa_debug_level;
|
||||||
|
|
||||||
|
|
||||||
|
struct radius_das_data {
|
||||||
|
int sock;
|
||||||
|
u8 *shared_secret;
|
||||||
|
size_t shared_secret_len;
|
||||||
|
struct hostapd_ip_addr client_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||||
|
{
|
||||||
|
struct radius_das_data *das = eloop_ctx;
|
||||||
|
u8 buf[1500];
|
||||||
|
union {
|
||||||
|
struct sockaddr_storage ss;
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
#ifdef CONFIG_IPV6
|
||||||
|
struct sockaddr_in6 sin6;
|
||||||
|
#endif /* CONFIG_IPV6 */
|
||||||
|
} from;
|
||||||
|
char abuf[50];
|
||||||
|
int from_port = 0;
|
||||||
|
socklen_t fromlen;
|
||||||
|
int len;
|
||||||
|
struct radius_msg *msg, *reply = NULL;
|
||||||
|
struct radius_hdr *hdr;
|
||||||
|
struct wpabuf *rbuf;
|
||||||
|
|
||||||
|
fromlen = sizeof(from);
|
||||||
|
len = recvfrom(sock, buf, sizeof(buf), 0,
|
||||||
|
(struct sockaddr *) &from.ss, &fromlen);
|
||||||
|
if (len < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
|
||||||
|
from_port = ntohs(from.sin.sin_port);
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
|
||||||
|
len, abuf, from_port);
|
||||||
|
if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = radius_msg_parse(buf, len);
|
||||||
|
if (msg == NULL) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
|
||||||
|
"from %s:%d failed", abuf, from_port);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wpa_debug_level <= MSG_MSGDUMP)
|
||||||
|
radius_msg_dump(msg);
|
||||||
|
|
||||||
|
if (radius_msg_verify_das_req(msg, das->shared_secret,
|
||||||
|
das->shared_secret_len)) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
|
||||||
|
"from %s:%d - drop", abuf, from_port);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr = radius_msg_get_hdr(msg);
|
||||||
|
|
||||||
|
switch (hdr->code) {
|
||||||
|
case RADIUS_CODE_DISCONNECT_REQUEST:
|
||||||
|
/* TODO */
|
||||||
|
reply = radius_msg_new(RADIUS_CODE_DISCONNECT_NAK,
|
||||||
|
hdr->identifier);
|
||||||
|
if (reply == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 405);
|
||||||
|
break;
|
||||||
|
case RADIUS_CODE_COA_REQUEST:
|
||||||
|
/* TODO */
|
||||||
|
reply = radius_msg_new(RADIUS_CODE_COA_NAK,
|
||||||
|
hdr->identifier);
|
||||||
|
if (reply == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Unsupported Service */
|
||||||
|
radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 405);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
|
||||||
|
"packet from %s:%d",
|
||||||
|
hdr->code, abuf, from_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply) {
|
||||||
|
int res;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
|
||||||
|
|
||||||
|
if (radius_msg_finish_das_resp(reply, das->shared_secret,
|
||||||
|
das->shared_secret_len, hdr) <
|
||||||
|
0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DAS: Failed to add "
|
||||||
|
"Message-Authenticator attribute");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wpa_debug_level <= MSG_MSGDUMP)
|
||||||
|
radius_msg_dump(reply);
|
||||||
|
|
||||||
|
rbuf = radius_msg_get_buf(reply);
|
||||||
|
res = sendto(das->sock, wpabuf_head(rbuf),
|
||||||
|
wpabuf_len(rbuf), 0,
|
||||||
|
(struct sockaddr *) &from.ss, fromlen);
|
||||||
|
if (res < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
|
||||||
|
abuf, from_port, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
radius_msg_free(msg);
|
||||||
|
radius_msg_free(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int radius_das_open_socket(int port)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
|
s = socket(PF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (s < 0) {
|
||||||
|
perror("socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||||
|
perror("bind");
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct radius_das_data *
|
||||||
|
radius_das_init(struct radius_das_conf *conf)
|
||||||
|
{
|
||||||
|
struct radius_das_data *das;
|
||||||
|
|
||||||
|
if (conf->port == 0 || conf->shared_secret == NULL ||
|
||||||
|
conf->client_addr == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
das = os_zalloc(sizeof(*das));
|
||||||
|
if (das == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
os_memcpy(&das->client_addr, conf->client_addr,
|
||||||
|
sizeof(das->client_addr));
|
||||||
|
|
||||||
|
das->shared_secret = os_malloc(conf->shared_secret_len);
|
||||||
|
if (das->shared_secret == NULL) {
|
||||||
|
radius_das_deinit(das);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
os_memcpy(das->shared_secret, conf->shared_secret,
|
||||||
|
conf->shared_secret_len);
|
||||||
|
das->shared_secret_len = conf->shared_secret_len;
|
||||||
|
|
||||||
|
das->sock = radius_das_open_socket(conf->port);
|
||||||
|
if (das->sock < 0) {
|
||||||
|
wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
|
||||||
|
"DAS");
|
||||||
|
radius_das_deinit(das);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
|
||||||
|
{
|
||||||
|
radius_das_deinit(das);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return das;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void radius_das_deinit(struct radius_das_data *das)
|
||||||
|
{
|
||||||
|
if (das == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (das->sock >= 0) {
|
||||||
|
eloop_unregister_read_sock(das->sock);
|
||||||
|
close(das->sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
os_free(das->shared_secret);
|
||||||
|
os_free(das);
|
||||||
|
}
|
26
src/radius/radius_das.h
Normal file
26
src/radius/radius_das.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* RADIUS Dynamic Authorization Server (DAS)
|
||||||
|
* Copyright (c) 2012, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RADIUS_DAS_H
|
||||||
|
#define RADIUS_DAS_H
|
||||||
|
|
||||||
|
struct radius_das_data;
|
||||||
|
|
||||||
|
struct radius_das_conf {
|
||||||
|
int port;
|
||||||
|
const u8 *shared_secret;
|
||||||
|
size_t shared_secret_len;
|
||||||
|
const struct hostapd_ip_addr *client_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct radius_das_data *
|
||||||
|
radius_das_init(struct radius_das_conf *conf);
|
||||||
|
|
||||||
|
void radius_das_deinit(struct radius_das_data *data);
|
||||||
|
|
||||||
|
#endif /* RADIUS_DAS_H */
|
Loading…
Reference in a new issue