diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 29579c526..cec865744 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -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) diff --git a/hostapd/Makefile b/hostapd/Makefile index 94421d43b..b3cb68673 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -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) diff --git a/hostapd/android.config b/hostapd/android.config index 34cc125c1..522de8726 100644 --- a/hostapd/android.config +++ b/hostapd/android.config @@ -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 diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 1af083917..d30f2fe9f 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -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) { diff --git a/hostapd/defconfig b/hostapd/defconfig index 2f5f3f6d0..550db697b 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -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 diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 13576499a..f8b4f9c65 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -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= +#auth_server_client_cert= +#auth_server_private_key= +#auth_server_private_key_passwd= + # 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, diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index f760e340b..a28ebbb5d 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -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); } diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 68e6d23d3..2a7f36170 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -1,18 +1,20 @@ /* * RADIUS client - * Copyright (c) 2002-2015, Jouni Malinen + * Copyright (c) 2002-2024, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" +#include #include #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(¶ms, 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, ¶ms)) { + 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,9 +1690,16 @@ radius_change_server(struct radius_client_data *radius, } if (connect(sel_sock, addr, addrlen) < 0) { - wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno)); - close(sel_sock); - return -2; + 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 @@ -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, - auth ? (void *) RADIUS_AUTH : - (void *) RADIUS_ACCT); + 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); } diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h index 687cd81ae..db40637ea 100644 --- a/src/radius/radius_client.h +++ b/src/radius/radius_client.h @@ -1,6 +1,6 @@ /* * RADIUS client - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2024, Jouni Malinen * * 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 */ /** diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 976da5962..dd13308f7 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -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 diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c index 9641062fd..95953de92 100644 --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c @@ -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 [-a] [-p] " - "[-s]\\\n" + "[-s] \\\n" + " [-X \\\n" " [-r] [-t] [-C] \\\n" " [-M] [-o] [-R] " "[-P] \\\n" +#ifdef CONFIG_RADIUS_TLS + " [-j] [-J] \\\n" + "[-k] \\\n" +#endif /* CONFIG_RADIUS_TLS */ " [-A] [-i] [-T]\n" "eapol_test scard\n" "eapol_test sim [debug]\n" @@ -1269,10 +1290,11 @@ static void usage(void) " -c = configuration file\n" " -a = IP address of the authentication server, " "default 127.0.0.1\n" - " -p = UDP port of the authentication server, " - "default 1812\n" - " -s = shared secret with the authentication " - "server, default 'radius'\n" + " -p = Port of the authentication server,\n" + " default 1812 for RADIUS/UDP and 2083 for RADIUS/TLS\n" + " -s = shared secret with the authentication server,\n" + " default 'radius' for RADIUS/UDP and 'radsec' for RADIUS/TLS\n" + " -X = RADIUS protocol to use: UDP (default) or TLS\n" " -A = IP address of the client, default: select " "automatically\n" " -r = 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) {