aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hostapd/Makefile1
-rw-r--r--hostapd/config_file.c36
-rw-r--r--hostapd/hostapd.conf13
-rw-r--r--src/ap/ap_config.c1
-rw-r--r--src/ap/ap_config.h4
-rw-r--r--src/ap/hostapd.c21
-rw-r--r--src/ap/hostapd.h1
-rw-r--r--src/radius/radius.c130
-rw-r--r--src/radius/radius.h18
-rw-r--r--src/radius/radius_das.c222
-rw-r--r--src/radius/radius_das.h26
11 files changed, 469 insertions, 4 deletions
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 05a363b..9e42d03 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -110,6 +110,7 @@ CONFIG_NO_ACCOUNTING=y
else
OBJS += ../src/radius/radius.o
OBJS += ../src/radius/radius_client.o
+OBJS += ../src/radius/radius_das.o
endif
ifdef CONFIG_NO_ACCOUNTING
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 5c8824c..eab8ad4 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -561,6 +561,34 @@ hostapd_parse_radius_attr(const char *value)
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 */
@@ -1657,6 +1685,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
a = a->next;
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 */
} else if (os_strcmp(buf, "auth_algs") == 0) {
bss->auth_algs = atoi(pos);
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 8890cd2..611ce95 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -696,6 +696,19 @@ own_ip_addr=127.0.0.1
# Operator-Name = "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 ##############################
# hostapd can be used as a RADIUS authentication server for other hosts. This
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 392b4fd..e916f12 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -432,6 +432,7 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->radius_server_clients);
os_free(conf->test_socket);
os_free(conf->radius);
+ os_free(conf->radius_das_shared_secret);
hostapd_config_free_vlan(conf);
if (conf->ssid.dyn_vlan_keys) {
struct hostapd_ssid *ssid = &conf->ssid;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 5473be1..78c9068 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -186,6 +186,10 @@ struct hostapd_bss_config {
int radius_request_cui;
struct hostapd_radius_attr *radius_auth_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;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 0f94188..9d6fd7b 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1,6 +1,6 @@
/*
* 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.
* See README for more details.
@@ -12,6 +12,7 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "radius/radius_client.h"
+#include "radius/radius_das.h"
#include "drivers/driver.h"
#include "hostapd.h"
#include "authsrv.h"
@@ -241,6 +242,8 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
#ifndef CONFIG_NO_RADIUS
radius_client_deinit(hapd->radius);
hapd->radius = NULL;
+ radius_das_deinit(hapd->radius_das);
+ hapd->radius_das = NULL;
#endif /* CONFIG_NO_RADIUS */
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.");
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 */
if (hostapd_acl_init(hapd)) {
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 63cf494..4e45d59 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -85,6 +85,7 @@ struct hostapd_data {
struct radius_client_data *radius;
u32 acct_session_id_hi, acct_session_id_lo;
+ struct radius_das_data *radius_das;
struct iapp_data *iapp;
diff --git a/src/radius/radius.c b/src/radius/radius.c
index 0dd6b12..ed0a9de 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -1,6 +1,6 @@
/*
* 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.
* 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_CLIENT: return "Status-Client";
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?";
}
}
@@ -225,6 +231,7 @@ static struct radius_attr_type radius_attrs[] =
{ RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
RADIUS_ATTR_TEXT },
{ 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]))
@@ -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,
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,
struct radius_attr_hdr *attr)
{
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 44123bd..8cc6113 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -1,6 +1,6 @@
/*
* 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.
* See README for more details.
@@ -31,6 +31,12 @@ enum { RADIUS_CODE_ACCESS_REQUEST = 1,
RADIUS_CODE_ACCESS_CHALLENGE = 11,
RADIUS_CODE_STATUS_SERVER = 12,
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
};
@@ -83,7 +89,8 @@ enum { RADIUS_ATTR_USER_NAME = 1,
RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
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);
int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
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,
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,
const u8 *data, size_t data_len);
struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
new file mode 100644
index 0000000..ae3df89
--- /dev/null
+++ b/src/radius/radius_das.c
@@ -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);
+}
diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
new file mode 100644
index 0000000..4e21c6d
--- /dev/null
+++ b/src/radius/radius_das.h
@@ -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 */