]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
RADIUS: Preliminary support RADIUS/TLS as an alternative to RADIUS/UDP
authorJouni Malinen <j@w1.fi>
Sun, 25 Feb 2024 15:15:01 +0000 (17:15 +0200)
committerJouni Malinen <j@w1.fi>
Sun, 25 Feb 2024 18:54:14 +0000 (20:54 +0200)
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>
hostapd/Android.mk
hostapd/Makefile
hostapd/android.config
hostapd/config_file.c
hostapd/defconfig
hostapd/hostapd.conf
src/ap/ap_config.c
src/radius/radius_client.c
src/radius/radius_client.h
wpa_supplicant/Makefile
wpa_supplicant/eapol_test.c

index 29579c526b60e41882e36404151894830066a7fd..cec865744fe8185f47743598b64599e9e0e072bd 100644 (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)
index 94421d43bdd7d375eb845c6aa1aa951cca6ddc6f..b3cb686734469ebeb1088df47667cd0ed8649b34 100644 (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)
index 34cc125c1b4176202de0b33d970486087abe4e94..522de87266d5e268aa6a77b8d43d305aece3bbaf 100644 (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
 
index 1af083917052fd6448001ed486ad57c86b2faa2f..d30f2fe9f2729e3a689bf8eacc39968e8a53b4bc 100644 (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) {
index 2f5f3f6d047109fdd5e2a646c5fab39a2945e51f..550db697bc0ad99febbb2874f383623db55769fb 100644 (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
 
index 13576499a358f317e85770d20851da6fd7e5d626..f8b4f9c65957e2fbdc897fb377b46d5d9068811e 100644 (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,
index f760e340b57bcf096e1e9c8abed2dc686db25e36..a28ebbb5d82037e86dbdb9f0c78a931273271ddb 100644 (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);
 }
index 68e6d23d397e37f012078ba83cc401a2c97a6bdb..2a7f361702641a208bbbc5acd19ff7190a18e1b9 100644 (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,7 +1301,119 @@ 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 */
 
 
 /**
@@ -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);
 }
 
index 687cd81aed915be03be4fcbe2011380a11c8940c..db40637ea03bbef4f1c52797237a4fc368d64fba 100644 (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 */
 
        /**
index 976da5962a014a3853eaa46f9697f3553eb5fbc1..dd13308f7bf2a34bb9d60d64f3d112adbdea815d 100644 (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
index 9641062fd6b50253e37b09085d819fc9f2743899..95953de928bd2e11ac679d55655c966ca69de68a 100644 (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) {