RADIUS: Preliminary support RADIUS/TLS as an alternative to RADIUS/UDP

This adds initial parts for RADIUS/TLS support in the RADIUS client.
This can be used with eapol_test and hostapd. This functionality is not
included by default and CONFIG_RADIUS_TLS=y in .config can be used to
enable it.

This version does not yet include all the needed functionality for TLS
validation and the rules for dropping a TCP connection based on invalid
RADIUS attributes.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2024-02-25 17:15:01 +02:00
parent 87f33c26b9
commit 95a825bc43
11 changed files with 715 additions and 22 deletions

View file

@ -647,6 +647,11 @@ ifdef CHAP
OBJS += src/eap_common/chap.c
endif
ifdef CONFIG_RADIUS_TLS
TLS_FUNCS=y
L_CFLAGS += -DCONFIG_RADIUS_TLS
endif
ifdef TLS_FUNCS
NEED_DES=y
# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)

View file

@ -682,6 +682,11 @@ ifdef CHAP
OBJS += ../src/eap_common/chap.o
endif
ifdef CONFIG_RADIUS_TLS
TLS_FUNCS=y
CFLAGS += -DCONFIG_RADIUS_TLS
endif
ifdef TLS_FUNCS
NEED_DES=y
# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)

View file

@ -121,6 +121,9 @@ CONFIG_PKCS12=y
# Build IPv6 support for RADIUS operations
CONFIG_IPV6=y
# Include support fo RADIUS/TLS into the RADIUS client
#CONFIG_RADIUS_TLS=y
# IEEE Std 802.11r-2008 (Fast BSS Transition)
#CONFIG_IEEE80211R=y

View file

@ -2869,6 +2869,37 @@ static int hostapd_config_fill(struct hostapd_config *conf,
os_free(bss->radius->auth_server->shared_secret);
bss->radius->auth_server->shared_secret = (u8 *) os_strdup(pos);
bss->radius->auth_server->shared_secret_len = len;
} else if (bss->radius->auth_server &&
os_strcmp(buf, "auth_server_type") == 0) {
if (os_strcmp(pos, "UDP") == 0) {
bss->radius->auth_server->tls = false;
#ifdef CONFIG_RADIUS_TLS
} else if (os_strcmp(pos, "TLS") == 0) {
bss->radius->auth_server->tls = true;
#endif /* CONFIG_RADIUS_TLS */
} else {
wpa_printf(MSG_ERROR, "Line %d: unsupported RADIUS type '%s'",
line, pos);
return 1;
}
#ifdef CONFIG_RADIUS_TLS
} else if (bss->radius->auth_server &&
os_strcmp(buf, "auth_server_ca_cert") == 0) {
os_free(bss->radius->auth_server->ca_cert);
bss->radius->auth_server->ca_cert = os_strdup(pos);
} else if (bss->radius->auth_server &&
os_strcmp(buf, "auth_server_client_cert") == 0) {
os_free(bss->radius->auth_server->client_cert);
bss->radius->auth_server->client_cert = os_strdup(pos);
} else if (bss->radius->auth_server &&
os_strcmp(buf, "auth_server_private_key") == 0) {
os_free(bss->radius->auth_server->private_key);
bss->radius->auth_server->private_key = os_strdup(pos);
} else if (bss->radius->auth_server &&
os_strcmp(buf, "auth_server_private_key_passwd") == 0) {
os_free(bss->radius->auth_server->private_key_passwd);
bss->radius->auth_server->private_key_passwd = os_strdup(pos);
#endif /* CONFIG_RADIUS_TLS */
} else if (os_strcmp(buf, "acct_server_addr") == 0) {
if (hostapd_config_read_radius_addr(
&bss->radius->acct_servers,
@ -2903,6 +2934,37 @@ static int hostapd_config_fill(struct hostapd_config *conf,
os_free(bss->radius->acct_server->shared_secret);
bss->radius->acct_server->shared_secret = (u8 *) os_strdup(pos);
bss->radius->acct_server->shared_secret_len = len;
} else if (bss->radius->acct_server &&
os_strcmp(buf, "acct_server_type") == 0) {
if (os_strcmp(pos, "UDP") == 0) {
bss->radius->acct_server->tls = false;
#ifdef CONFIG_RADIUS_TLS
} else if (os_strcmp(pos, "TLS") == 0) {
bss->radius->acct_server->tls = true;
#endif /* CONFIG_RADIUS_TLS */
} else {
wpa_printf(MSG_ERROR, "Line %d: unsupported RADIUS type '%s'",
line, pos);
return 1;
}
#ifdef CONFIG_RADIUS_TLS
} else if (bss->radius->acct_server &&
os_strcmp(buf, "acct_server_ca_cert") == 0) {
os_free(bss->radius->acct_server->ca_cert);
bss->radius->acct_server->ca_cert = os_strdup(pos);
} else if (bss->radius->acct_server &&
os_strcmp(buf, "acct_server_client_cert") == 0) {
os_free(bss->radius->acct_server->client_cert);
bss->radius->acct_server->client_cert = os_strdup(pos);
} else if (bss->radius->acct_server &&
os_strcmp(buf, "acct_server_private_key") == 0) {
os_free(bss->radius->acct_server->private_key);
bss->radius->acct_server->private_key = os_strdup(pos);
} else if (bss->radius->acct_server &&
os_strcmp(buf, "acct_server_private_key_passwd") == 0) {
os_free(bss->radius->acct_server->private_key_passwd);
bss->radius->acct_server->private_key_passwd = os_strdup(pos);
#endif /* CONFIG_RADIUS_TLS */
} else if (os_strcmp(buf, "radius_retry_primary_interval") == 0) {
bss->radius->retry_primary_interval = atoi(pos);
} else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) {

View file

@ -141,6 +141,9 @@ CONFIG_PKCS12=y
# Build IPv6 support for RADIUS operations
CONFIG_IPV6=y
# Include support fo RADIUS/TLS into the RADIUS client
#CONFIG_RADIUS_TLS=y
# IEEE Std 802.11r-2008 (Fast BSS Transition)
#CONFIG_IEEE80211R=y

View file

@ -1581,6 +1581,16 @@ own_ip_addr=127.0.0.1
#acct_server_port=1813
#acct_server_shared_secret=secret2
# RADIUS/TLS instead of RADIUS/UDP
#auth_server_addr=127.0.0.1
#auth_server_port=2083
#auth_server_type=TLS
#auth_server_shared_secret=radsec
#auth_server_ca_cert=<path to trusted CA certificate(s)>
#auth_server_client_cert=<path to client certificate>
#auth_server_private_key=<path to private key>
#auth_server_private_key_passwd=<password for decrypting private key>
# Retry interval for trying to return to the primary RADIUS server (in
# seconds). RADIUS client code will automatically try to use the next server
# when the current server is not replying to requests. If this interval is set,

View file

@ -555,6 +555,10 @@ static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
for (i = 0; i < num_servers; i++) {
os_free(servers[i].shared_secret);
os_free(servers[i].ca_cert);
os_free(servers[i].client_cert);
os_free(servers[i].private_key);
os_free(servers[i].private_key_passwd);
}
os_free(servers);
}

View file

@ -1,18 +1,20 @@
/*
* RADIUS client
* Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
* Copyright (c) 2002-2024, 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 <fcntl.h>
#include <net/if.h>
#include "common.h"
#include "eloop.h"
#include "crypto/tls.h"
#include "radius.h"
#include "radius_client.h"
#include "eloop.h"
/* Defaults for RADIUS retransmit values (exponential backoff) */
@ -173,11 +175,31 @@ struct radius_client_data {
*/
int auth_sock;
/**
* auth_tls - Whether current authentication connection uses TLS
*/
bool auth_tls;
/**
* auth_tls_ready - Whether authentication TLS is ready
*/
bool auth_tls_ready;
/**
* acct_sock - Currently used socket for RADIUS accounting server
*/
int acct_sock;
/**
* acct_tls - Whether current accounting connection uses TLS
*/
bool acct_tls;
/**
* acct_tls_ready - Whether accounting TLS is ready
*/
bool acct_tls_ready;
/**
* auth_handlers - Authentication message handlers
*/
@ -222,6 +244,12 @@ struct radius_client_data {
* interim_error_cb_ctx - interim_error_cb() context data
*/
void *interim_error_cb_ctx;
#ifdef CONFIG_RADIUS_TLS
void *tls_ctx;
struct tls_connection *auth_tls_conn;
struct tls_connection *acct_tls_conn;
#endif /* CONFIG_RADIUS_TLS */
};
@ -354,9 +382,19 @@ static int radius_client_retransmit(struct radius_client_data *radius,
u8 *acct_delay_time;
size_t acct_delay_time_len;
int num_servers;
#ifdef CONFIG_RADIUS_TLS
struct wpabuf *out = NULL;
struct tls_connection *conn = NULL;
bool acct = false;
#endif /* CONFIG_RADIUS_TLS */
if (entry->msg_type == RADIUS_ACCT ||
entry->msg_type == RADIUS_ACCT_INTERIM) {
#ifdef CONFIG_RADIUS_TLS
acct = true;
if (radius->acct_tls)
conn = radius->acct_tls_conn;
#endif /* CONFIG_RADIUS_TLS */
num_servers = conf->num_acct_servers;
if (radius->acct_sock < 0)
radius_client_init_acct(radius);
@ -374,6 +412,10 @@ static int radius_client_retransmit(struct radius_client_data *radius,
conf->acct_server->retransmissions++;
}
} else {
#ifdef CONFIG_RADIUS_TLS
if (radius->auth_tls)
conn = radius->auth_tls_conn;
#endif /* CONFIG_RADIUS_TLS */
num_servers = conf->num_auth_servers;
if (radius->auth_sock < 0)
radius_client_init_auth(radius);
@ -409,6 +451,15 @@ static int radius_client_retransmit(struct radius_client_data *radius,
return 1;
}
#ifdef CONFIG_RADIUS_TLS
if ((acct && radius->acct_tls && !radius->acct_tls_ready) ||
(!acct && radius->auth_tls && !radius->auth_tls_ready)) {
wpa_printf(MSG_DEBUG,
"RADIUS: TLS connection not yet ready for TX");
goto not_ready;
}
#endif /* CONFIG_RADIUS_TLS */
if (entry->msg_type == RADIUS_ACCT &&
radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME,
&acct_delay_time, &acct_delay_time_len,
@ -453,11 +504,37 @@ static int radius_client_retransmit(struct radius_client_data *radius,
os_get_reltime(&entry->last_attempt);
buf = radius_msg_get_buf(entry->msg);
#ifdef CONFIG_RADIUS_TLS
if (conn) {
out = tls_connection_encrypt(radius->tls_ctx, conn, buf);
if (!out) {
wpa_printf(MSG_INFO,
"RADIUS: Failed to encrypt RADIUS message (TLS)");
return -1;
}
wpa_printf(MSG_DEBUG,
"RADIUS: TLS encryption of %zu bytes of plaintext to %zu bytes of ciphertext",
wpabuf_len(buf), wpabuf_len(out));
buf = out;
}
#endif /* CONFIG_RADIUS_TLS */
wpa_printf(MSG_DEBUG, "RADIUS: Send %zu bytes to the server",
wpabuf_len(buf));
if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
if (radius_client_handle_send_error(radius, s, entry->msg_type)
> 0)
> 0) {
#ifdef CONFIG_RADIUS_TLS
wpabuf_free(out);
#endif /* CONFIG_RADIUS_TLS */
return 0;
}
}
#ifdef CONFIG_RADIUS_TLS
wpabuf_free(out);
not_ready:
#endif /* CONFIG_RADIUS_TLS */
entry->next_try = now + entry->next_wait;
entry->next_wait *= 2;
@ -714,6 +791,11 @@ static int radius_client_disable_pmtu_discovery(int s)
static void radius_close_auth_socket(struct radius_client_data *radius)
{
if (radius->auth_sock >= 0) {
#ifdef CONFIG_RADIUS_TLS
if (radius->conf->auth_server->tls)
eloop_unregister_sock(radius->auth_sock,
EVENT_TYPE_WRITE);
#endif /* CONFIG_RADIUS_TLS */
eloop_unregister_read_sock(radius->auth_sock);
close(radius->auth_sock);
radius->auth_sock = -1;
@ -724,6 +806,11 @@ static void radius_close_auth_socket(struct radius_client_data *radius)
static void radius_close_acct_socket(struct radius_client_data *radius)
{
if (radius->acct_sock >= 0) {
#ifdef CONFIG_RADIUS_TLS
if (radius->conf->acct_server->tls)
eloop_unregister_sock(radius->acct_sock,
EVENT_TYPE_WRITE);
#endif /* CONFIG_RADIUS_TLS */
eloop_unregister_read_sock(radius->acct_sock);
close(radius->acct_sock);
radius->acct_sock = -1;
@ -766,8 +853,18 @@ int radius_client_send(struct radius_client_data *radius,
char *name;
int s, res;
struct wpabuf *buf;
#ifdef CONFIG_RADIUS_TLS
struct wpabuf *out = NULL;
struct tls_connection *conn = NULL;
bool acct = false;
#endif /* CONFIG_RADIUS_TLS */
if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
#ifdef CONFIG_RADIUS_TLS
acct = true;
if (radius->acct_tls)
conn = radius->acct_tls_conn;
#endif /* CONFIG_RADIUS_TLS */
if (conf->acct_server && radius->acct_sock < 0)
radius_client_init_acct(radius);
@ -786,6 +883,10 @@ int radius_client_send(struct radius_client_data *radius,
s = radius->acct_sock;
conf->acct_server->requests++;
} else {
#ifdef CONFIG_RADIUS_TLS
if (radius->auth_tls)
conn = radius->auth_tls_conn;
#endif /* CONFIG_RADIUS_TLS */
if (conf->auth_server && radius->auth_sock < 0)
radius_client_init_auth(radius);
@ -811,11 +912,42 @@ int radius_client_send(struct radius_client_data *radius,
if (conf->msg_dumps)
radius_msg_dump(msg);
#ifdef CONFIG_RADIUS_TLS
if ((acct && radius->acct_tls && !radius->acct_tls_ready) ||
(!acct && radius->auth_tls && !radius->auth_tls_ready)) {
wpa_printf(MSG_DEBUG,
"RADIUS: TLS connection not yet ready for TX");
goto skip_send;
}
#endif /* CONFIG_RADIUS_TLS */
buf = radius_msg_get_buf(msg);
#ifdef CONFIG_RADIUS_TLS
if (conn) {
out = tls_connection_encrypt(radius->tls_ctx, conn, buf);
if (!out) {
wpa_printf(MSG_INFO,
"RADIUS: Failed to encrypt RADIUS message (TLS)");
return -1;
}
wpa_printf(MSG_DEBUG,
"RADIUS: TLS encryption of %zu bytes of plaintext to %zu bytes of ciphertext",
wpabuf_len(buf), wpabuf_len(out));
buf = out;
}
#endif /* CONFIG_RADIUS_TLS */
wpa_printf(MSG_DEBUG, "RADIUS: Send %zu bytes to the server",
wpabuf_len(buf));
res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
#ifdef CONFIG_RADIUS_TLS
wpabuf_free(out);
#endif /* CONFIG_RADIUS_TLS */
if (res < 0)
radius_client_handle_send_error(radius, s, msg_type);
#ifdef CONFIG_RADIUS_TLS
skip_send:
#endif /* CONFIG_RADIUS_TLS */
radius_client_list_add(radius, msg, msg_type, shared_secret,
shared_secret_len, addr);
@ -823,6 +955,137 @@ int radius_client_send(struct radius_client_data *radius,
}
#ifdef CONFIG_RADIUS_TLS
static void radius_client_close_tcp(struct radius_client_data *radius,
int sock, RadiusType msg_type)
{
wpa_printf(MSG_DEBUG, "RADIUS: Closing TCP connection (sock %d)",
sock);
if (msg_type == RADIUS_ACCT) {
radius->acct_tls_ready = false;
radius_close_acct_socket(radius);
} else {
radius->auth_tls_ready = false;
radius_close_auth_socket(radius);
}
}
static void
radius_client_process_tls_handshake(struct radius_client_data *radius,
int sock, RadiusType msg_type,
u8 *buf, size_t len)
{
struct wpabuf *in, *out = NULL, *appl;
struct tls_connection *conn;
int res;
bool ready = false;
wpa_printf(MSG_DEBUG,
"RADIUS: Process %zu bytes of received TLS handshake message",
len);
if (msg_type == RADIUS_ACCT)
conn = radius->acct_tls_conn;
else
conn = radius->auth_tls_conn;
in = wpabuf_alloc_copy(buf, len);
if (!in)
return;
appl = NULL;
out = tls_connection_handshake(radius->tls_ctx, conn, in, &appl);
wpabuf_free(in);
if (!out) {
wpa_printf(MSG_DEBUG,
"RADIUS: Could not generate TLS handshake data");
goto fail;
}
if (tls_connection_get_failed(radius->tls_ctx, conn)) {
wpa_printf(MSG_INFO, "RADIUS: TLS handshake failed");
goto fail;
}
if (tls_connection_established(radius->tls_ctx, conn)) {
wpa_printf(MSG_DEBUG,
"RADIUS: TLS connection established (sock=%d)",
sock);
if (msg_type == RADIUS_ACCT)
radius->acct_tls_ready = true;
else
radius->auth_tls_ready = true;
ready = true;
}
wpa_printf(MSG_DEBUG, "RADIUS: Sending %zu bytes of TLS handshake",
wpabuf_len(out));
res = send(sock, wpabuf_head(out), wpabuf_len(out), 0);
if (res < 0) {
wpa_printf(MSG_INFO, "RADIUS: send: %s", strerror(errno));
goto fail;
}
if ((size_t) res != wpabuf_len(out)) {
wpa_printf(MSG_INFO,
"RADIUS: Could not send all data for TLS handshake: only %d bytes sent",
res);
goto fail;
}
wpabuf_free(out);
if (ready) {
struct radius_msg_list *entry, *prev, *tmp;
struct os_reltime now;
/* Send all pending message of matching type since the TLS
* tunnel has now been established. */
os_get_reltime(&now);
entry = radius->msgs;
prev = NULL;
while (entry) {
if (entry->msg_type != msg_type) {
prev = entry;
entry = entry->next;
continue;
}
if (radius_client_retransmit(radius, entry, now.sec)) {
if (prev)
prev->next = entry->next;
else
radius->msgs = entry->next;
tmp = entry;
entry = entry->next;
radius_client_msg_free(tmp);
radius->num_msgs--;
continue;
}
prev = entry;
entry = entry->next;
}
}
return;
fail:
wpabuf_free(out);
tls_connection_deinit(radius->tls_ctx, conn);
if (msg_type == RADIUS_ACCT)
radius->acct_tls_conn = NULL;
else
radius->auth_tls_conn = NULL;
radius_client_close_tcp(radius, sock, msg_type);
}
#endif /* CONFIG_RADIUS_TLS */
static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
struct radius_client_data *radius = eloop_ctx;
@ -840,12 +1103,28 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
struct os_reltime now;
struct hostapd_radius_server *rconf;
int invalid_authenticator = 0;
#ifdef CONFIG_RADIUS_TLS
struct tls_connection *conn = NULL;
bool tls, tls_ready;
#endif /* CONFIG_RADIUS_TLS */
if (msg_type == RADIUS_ACCT) {
#ifdef CONFIG_RADIUS_TLS
if (radius->acct_tls)
conn = radius->acct_tls_conn;
tls = radius->acct_tls;
tls_ready = radius->acct_tls_ready;
#endif /* CONFIG_RADIUS_TLS */
handlers = radius->acct_handlers;
num_handlers = radius->num_acct_handlers;
rconf = conf->acct_server;
} else {
#ifdef CONFIG_RADIUS_TLS
if (radius->auth_tls)
conn = radius->auth_tls_conn;
tls = radius->auth_tls;
tls_ready = radius->auth_tls_ready;
#endif /* CONFIG_RADIUS_TLS */
handlers = radius->auth_handlers;
num_handlers = radius->num_auth_handlers;
rconf = conf->auth_server;
@ -861,6 +1140,52 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
wpa_printf(MSG_INFO, "recvmsg[RADIUS]: %s", strerror(errno));
return;
}
#ifdef CONFIG_RADIUS_TLS
if (tls && len == 0) {
wpa_printf(MSG_DEBUG, "RADIUS: No TCP data available");
goto close_tcp;
}
if (tls && !tls_ready) {
radius_client_process_tls_handshake(radius, sock, msg_type,
buf, len);
return;
}
if (conn) {
struct wpabuf *out, *in;
in = wpabuf_alloc_copy(buf, len);
if (!in)
return;
wpa_printf(MSG_DEBUG,
"RADIUS: Process %d bytes of encrypted TLS data",
len);
out = tls_connection_decrypt(radius->tls_ctx, conn, in);
wpabuf_free(in);
if (!out) {
wpa_printf(MSG_INFO,
"RADIUS: Failed to decrypt TLS data");
goto close_tcp;
}
if (wpabuf_len(out) == 0) {
wpa_printf(MSG_DEBUG,
"RADIUS: Full message not yet received - continue waiting for additional TLS data");
wpabuf_free(out);
return;
}
if (wpabuf_len(out) > RADIUS_MAX_MSG_LEN) {
wpa_printf(MSG_INFO,
"RADIUS: Too long RADIUS message from TLS: %zu",
wpabuf_len(out));
wpabuf_free(out);
goto close_tcp;
}
os_memcpy(buf, wpabuf_head(out), wpabuf_len(out));
len = wpabuf_len(out);
wpabuf_free(out);
}
#endif /* CONFIG_RADIUS_TLS */
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
@ -976,9 +1301,121 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
fail:
radius_msg_free(msg);
return;
#ifdef CONFIG_RADIUS_TLS
close_tcp:
radius_client_close_tcp(radius, sock, msg_type);
#endif /* CONFIG_RADIUS_TLS */
}
#ifdef CONFIG_RADIUS_TLS
static void radius_client_write_ready(int sock, void *eloop_ctx, void *sock_ctx)
{
struct radius_client_data *radius = eloop_ctx;
RadiusType msg_type = (uintptr_t) sock_ctx;
struct tls_connection *conn = NULL;
struct wpabuf *in, *out = NULL, *appl;
int res = -1;
struct tls_connection_params params;
struct hostapd_radius_server *server;
wpa_printf(MSG_DEBUG, "RADIUS: TCP connection established - start TLS handshake (sock=%d)",
sock);
if (msg_type == RADIUS_ACCT) {
eloop_unregister_sock(sock, EVENT_TYPE_WRITE);
eloop_register_read_sock(sock, radius_client_receive, radius,
(void *) RADIUS_ACCT);
if (radius->acct_tls_conn) {
wpa_printf(MSG_DEBUG,
"RADIUS: Deinit previously used TLS connection");
tls_connection_deinit(radius->tls_ctx,
radius->acct_tls_conn);
radius->acct_tls_conn = NULL;
}
server = radius->conf->acct_server;
} else {
eloop_unregister_sock(sock, EVENT_TYPE_WRITE);
eloop_register_read_sock(sock, radius_client_receive, radius,
(void *) RADIUS_AUTH);
if (radius->auth_tls_conn) {
wpa_printf(MSG_DEBUG,
"RADIUS: Deinit previously used TLS connection");
tls_connection_deinit(radius->tls_ctx,
radius->auth_tls_conn);
radius->auth_tls_conn = NULL;
}
server = radius->conf->auth_server;
}
if (!server)
goto fail;
conn = tls_connection_init(radius->tls_ctx);
if (!conn) {
wpa_printf(MSG_INFO,
"RADIUS: Failed to initiate TLS connection");
goto fail;
}
os_memset(&params, 0, sizeof(params));
params.ca_cert = server->ca_cert;
params.client_cert = server->client_cert;
params.private_key = server->private_key;
params.private_key_passwd = server->private_key_passwd;
params.flags = TLS_CONN_DISABLE_TLSv1_0 | TLS_CONN_DISABLE_TLSv1_1;
if (tls_connection_set_params(radius->tls_ctx, conn, &params)) {
wpa_printf(MSG_INFO,
"RADIUS: Failed to set TLS connection parameters");
goto fail;
}
in = NULL;
appl = NULL;
out = tls_connection_handshake(radius->tls_ctx, conn, in, &appl);
if (!out) {
wpa_printf(MSG_DEBUG,
"RADIUS: Could not generate TLS handshake data");
goto fail;
}
if (tls_connection_get_failed(radius->tls_ctx, conn)) {
wpa_printf(MSG_INFO, "RADIUS: TLS handshake failed");
goto fail;
}
wpa_printf(MSG_DEBUG, "RADIUS: Sending %zu bytes of TLS handshake",
wpabuf_len(out));
res = send(sock, wpabuf_head(out), wpabuf_len(out), 0);
if (res < 0) {
wpa_printf(MSG_INFO, "RADIUS: send: %s", strerror(errno));
goto fail;
}
if ((size_t) res != wpabuf_len(out)) {
wpa_printf(MSG_INFO,
"RADIUS: Could not send all data for TLS handshake: only %d bytes sent",
res);
goto fail;
}
wpabuf_free(out);
if (msg_type == RADIUS_ACCT)
radius->acct_tls_conn = conn;
else
radius->auth_tls_conn = conn;
return;
fail:
wpa_printf(MSG_INFO, "RADIUS: Failed to perform TLS handshake");
tls_connection_deinit(radius->tls_ctx, conn);
wpabuf_free(out);
radius_client_close_tcp(radius, sock, msg_type);
}
#endif /* CONFIG_RADIUS_TLS */
/**
* radius_client_get_id - Get an identifier for a new RADIUS message
* @radius: RADIUS client context from radius_client_init()
@ -1095,6 +1532,17 @@ radius_change_server(struct radius_client_data *radius,
int sel_sock;
struct radius_msg_list *entry;
struct hostapd_radius_servers *conf = radius->conf;
int type = SOCK_DGRAM;
bool tls = nserv->tls;
if (tls) {
#ifdef CONFIG_RADIUS_TLS
type = SOCK_STREAM;
#else /* CONFIG_RADIUS_TLS */
wpa_printf(MSG_ERROR, "RADIUS: TLS not supported");
return -1;
#endif /* CONFIG_RADIUS_TLS */
}
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
@ -1153,7 +1601,7 @@ radius_change_server(struct radius_client_data *radius,
serv.sin_port = htons(nserv->port);
addr = (struct sockaddr *) &serv;
addrlen = sizeof(serv);
sel_sock = socket(PF_INET, SOCK_DGRAM, 0);
sel_sock = socket(PF_INET, type, 0);
if (sel_sock >= 0)
radius_client_disable_pmtu_discovery(sel_sock);
break;
@ -1166,7 +1614,7 @@ radius_change_server(struct radius_client_data *radius,
serv6.sin6_port = htons(nserv->port);
addr = (struct sockaddr *) &serv6;
addrlen = sizeof(serv6);
sel_sock = socket(PF_INET6, SOCK_DGRAM, 0);
sel_sock = socket(PF_INET6, type, 0);
break;
#endif /* CONFIG_IPV6 */
default:
@ -1180,6 +1628,15 @@ radius_change_server(struct radius_client_data *radius,
return -1;
}
#ifdef CONFIG_RADIUS_TLS
if (tls && fcntl(sel_sock, F_SETFL, O_NONBLOCK) != 0) {
wpa_printf(MSG_DEBUG, "RADIUS: fnctl(O_NONBLOCK) failed: %s",
strerror(errno));
close(sel_sock);
return -1;
}
#endif /* CONFIG_RADIUS_TLS */
#ifdef __linux__
if (conf->force_client_dev && conf->force_client_dev[0]) {
if (setsockopt(sel_sock, SOL_SOCKET, SO_BINDTODEVICE,
@ -1233,10 +1690,17 @@ radius_change_server(struct radius_client_data *radius,
}
if (connect(sel_sock, addr, addrlen) < 0) {
wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
if (nserv->tls && errno == EINPROGRESS) {
wpa_printf(MSG_DEBUG,
"RADIUS: TCP connection establishment in progress (sock %d)",
sel_sock);
} else {
wpa_printf(MSG_INFO, "connect[radius]: %s",
strerror(errno));
close(sel_sock);
return -2;
}
}
#ifndef CONFIG_NATIVE_WINDOWS
switch (nserv->addr.af) {
@ -1273,9 +1737,26 @@ radius_change_server(struct radius_client_data *radius,
radius->acct_sock = sel_sock;
}
eloop_register_read_sock(sel_sock, radius_client_receive, radius,
if (!tls)
eloop_register_read_sock(sel_sock, radius_client_receive,
radius,
auth ? (void *) RADIUS_AUTH :
(void *) RADIUS_ACCT);
#ifdef CONFIG_RADIUS_TLS
if (tls)
eloop_register_sock(sel_sock, EVENT_TYPE_WRITE,
radius_client_write_ready, radius,
auth ? (void *) RADIUS_AUTH :
(void *) RADIUS_ACCT);
#endif /* CONFIG_RADIUS_TLS */
if (auth) {
radius->auth_tls = nserv->tls;
radius->auth_tls_ready = false;
} else {
radius->acct_tls = nserv->tls;
radius->acct_tls_ready = false;
}
return 0;
}
@ -1332,6 +1813,15 @@ static int radius_client_init_acct(struct radius_client_data *radius)
}
#ifdef CONFIG_RADIUS_TLS
static void radius_tls_event_cb(void *ctx, enum tls_event ev,
union tls_event_data *data)
{
wpa_printf(MSG_DEBUG, "RADIUS: TLS event %d", ev);
}
#endif /* CONFIG_RADIUS_TLS */
/**
* radius_client_init - Initialize RADIUS client
* @ctx: Callback context to be used in hostapd_logger() calls
@ -1370,6 +1860,22 @@ radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
radius_retry_primary_timer, radius,
NULL);
#ifdef CONFIG_RADIUS_TLS
if ((conf->auth_server && conf->auth_server->tls) ||
(conf->acct_server && conf->acct_server->tls)) {
struct tls_config tls_conf;
os_memset(&tls_conf, 0, sizeof(tls_conf));
tls_conf.event_cb = radius_tls_event_cb;
radius->tls_ctx = tls_init(&tls_conf);
if (!radius->tls_ctx) {
radius_client_deinit(radius);
return NULL;
}
}
#endif /* CONFIG_RADIUS_TLS */
return radius;
}
@ -1391,6 +1897,13 @@ void radius_client_deinit(struct radius_client_data *radius)
radius_client_flush(radius, 0);
os_free(radius->auth_handlers);
os_free(radius->acct_handlers);
#ifdef CONFIG_RADIUS_TLS
if (radius->tls_ctx) {
tls_connection_deinit(radius->tls_ctx, radius->auth_tls_conn);
tls_connection_deinit(radius->tls_ctx, radius->acct_tls_conn);
tls_deinit(radius->tls_ctx);
}
#endif /* CONFIG_RADIUS_TLS */
os_free(radius);
}

View file

@ -1,6 +1,6 @@
/*
* RADIUS client
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@ -35,6 +35,11 @@ struct hostapd_radius_server {
*/
int port;
/**
* tls - Whether to use RADIUS/TLS instead of RADIUS/UDP
*/
bool tls;
/**
* shared_secret - Shared secret for authenticating RADIUS messages
*/
@ -45,6 +50,26 @@ struct hostapd_radius_server {
*/
size_t shared_secret_len;
/**
* ca_cert - Path to trusted CA certificate(s) for RADIUS/TLS
*/
char *ca_cert;
/**
* client_cert - Path to client certificate for RADIUS/TLS
*/
char *client_cert;
/**
* private_key - Path to clienbt private key for RADIUS/TLS
*/
char *private_key;
/**
* private_key_passwd - Password for the private key for RADIUS/TLS
*/
char *private_key_passwd;
/* Dynamic (not from configuration file) MIB data */
/**

View file

@ -1183,6 +1183,10 @@ ifdef CONFIG_TLSV12
CFLAGS += -DCONFIG_TLSV12
endif
ifdef CONFIG_RADIUS_TLS
TLS_FUNCS=y
endif
ifeq ($(CONFIG_TLS), wolfssl)
ifdef TLS_FUNCS
CFLAGS += -DWOLFSSL_DER_LOAD
@ -1921,6 +1925,9 @@ OBJS += wpa_supplicant.o events.o bssid_ignore.o wpas_glue.o scan.o
OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
OBJS_t += ../src/radius/radius_client.o
OBJS_t += ../src/radius/radius.o
ifdef CONFIG_RADIUS_TLS
CFLAGS += -DCONFIG_RADIUS_TLS
endif
OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o

View file

@ -670,6 +670,10 @@ static void test_eapol_clean(struct eapol_test_data *e,
wpa_s->eapol = NULL;
if (e->radius_conf && e->radius_conf->auth_server) {
os_free(e->radius_conf->auth_server->shared_secret);
os_free(e->radius_conf->auth_server->ca_cert);
os_free(e->radius_conf->auth_server->client_cert);
os_free(e->radius_conf->auth_server->private_key);
os_free(e->radius_conf->auth_server->private_key_passwd);
os_free(e->radius_conf->auth_server);
}
os_free(e->radius_conf);
@ -1007,7 +1011,10 @@ struct wpa_driver_ops eapol_test_drv_ops = {
static void wpa_init_conf(struct eapol_test_data *e,
struct wpa_supplicant *wpa_s, const char *authsrv,
int port, const char *secret,
int port, bool tls, const char *secret,
const char *ca_cert, const char *client_cert,
const char *private_key,
const char *private_key_passwd,
const char *cli_addr, const char *ifname)
{
struct hostapd_radius_server *as;
@ -1045,8 +1052,17 @@ static void wpa_init_conf(struct eapol_test_data *e,
}
#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
as->port = port;
as->tls = tls;
as->shared_secret = (u8 *) os_strdup(secret);
as->shared_secret_len = os_strlen(secret);
if (ca_cert)
as->ca_cert = os_strdup(ca_cert);
if (client_cert)
as->client_cert = os_strdup(client_cert);
if (private_key)
as->private_key = os_strdup(private_key);
if (private_key_passwd)
as->private_key_passwd = os_strdup(private_key_passwd);
e->radius_conf->auth_server = as;
e->radius_conf->auth_servers = as;
e->radius_conf->msg_dumps = 1;
@ -1256,11 +1272,16 @@ static void usage(void)
{
printf("usage:\n"
"eapol_test [-enWSv] -c<conf> [-a<AS IP>] [-p<AS port>] "
"[-s<AS secret>]\\\n"
"[-s<AS secret>] \\\n"
" [-X<RADIUS protocol> \\\n"
" [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
" [-M<client MAC address>] [-o<server cert file] \\\n"
" [-N<attr spec>] [-R<PC/SC reader>] "
"[-P<PC/SC PIN>] \\\n"
#ifdef CONFIG_RADIUS_TLS
" [-j<CA cert>] [-J<client cert>] \\\n"
"[-k<private key] [-K<private key passwd>] \\\n"
#endif /* CONFIG_RADIUS_TLS */
" [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
"eapol_test scard\n"
"eapol_test sim <PIN> <num triplets> [debug]\n"
@ -1269,10 +1290,11 @@ static void usage(void)
" -c<conf> = configuration file\n"
" -a<AS IP> = IP address of the authentication server, "
"default 127.0.0.1\n"
" -p<AS port> = UDP port of the authentication server, "
"default 1812\n"
" -s<AS secret> = shared secret with the authentication "
"server, default 'radius'\n"
" -p<AS port> = Port of the authentication server,\n"
" default 1812 for RADIUS/UDP and 2083 for RADIUS/TLS\n"
" -s<AS secret> = shared secret with the authentication server,\n"
" default 'radius' for RADIUS/UDP and 'radsec' for RADIUS/TLS\n"
" -X<RADIUS protocol> = RADIUS protocol to use: UDP (default) or TLS\n"
" -A<client IP> = IP address of the client, default: select "
"automatically\n"
" -r<count> = number of re-authentications\n"
@ -1310,8 +1332,11 @@ int main(int argc, char *argv[])
struct wpa_supplicant wpa_s;
int c, ret = 1, wait_for_monitor = 0, save_config = 0;
char *as_addr = "127.0.0.1";
int as_port = 1812;
char *as_secret = "radius";
int as_port = -1;
char *as_secret = NULL;
char *ca_cert = NULL, *client_cert = NULL;
char *private_key = NULL, *private_key_passwd = NULL;
bool tls = false;
char *cli_addr = NULL;
char *conf = NULL;
int timeout = 30;
@ -1334,7 +1359,8 @@ int main(int argc, char *argv[])
wpa_debug_show_keys = 1;
for (;;) {
c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW");
c = getopt(argc, argv,
"a:A:c:C:ei:j:J:k:K:M:nN:o:p:P:r:R:s:St:T:vWX:");
if (c < 0)
break;
switch (c) {
@ -1356,6 +1382,20 @@ int main(int argc, char *argv[])
case 'i':
ifname = optarg;
break;
#ifdef CONFIG_RADIUS_TLS
case 'j':
ca_cert = optarg;
break;
case 'J':
client_cert = optarg;
break;
case 'k':
private_key = optarg;
break;
case 'K':
private_key_passwd = optarg;
break;
#endif /* CONFIG_RADIUS_TLS */
case 'M':
if (hwaddr_aton(optarg, eapol_test.own_addr)) {
usage();
@ -1406,6 +1446,16 @@ int main(int argc, char *argv[])
case 'W':
wait_for_monitor++;
break;
case 'X':
if (os_strcmp(optarg, "UDP") == 0) {
tls = false;
} else if (os_strcmp(optarg, "TLS") == 0) {
tls = true;
} else {
usage();
return -1;
}
break;
case 'N':
p1 = os_zalloc(sizeof(*p1));
if (p1 == NULL)
@ -1440,6 +1490,11 @@ int main(int argc, char *argv[])
}
}
if (!as_secret)
as_secret = tls ? "radsec" : "radius";
if (as_port < 0)
as_port = tls ? 2083 : 1812;
if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
return scard_test(&eapol_test);
}
@ -1489,7 +1544,8 @@ int main(int argc, char *argv[])
wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader);
}
wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, tls, as_secret,
ca_cert, client_cert, private_key, private_key_passwd,
cli_addr, ifname);
wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
if (wpa_s.ctrl_iface == NULL) {