From: Tobias Brunner Date: Thu, 16 Jan 2025 10:02:13 +0000 (+0100) Subject: eap-radius: Add support to specify and bind a specific source address X-Git-Tag: 6.0.1rc1~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=57703fa0892e8fac9b132bcf504c00eca53e6aeb;p=thirdparty%2Fstrongswan.git eap-radius: Add support to specify and bind a specific source address Using a specific address can be useful in scenarios where dynamic routing could change the path to the RADIUS server and a changing source address is a problem for the server. Closes strongswan/strongswan#2598 --- diff --git a/conf/plugins/eap-radius.opt b/conf/plugins/eap-radius.opt index dfed781659..06b86f7642 100644 --- a/conf/plugins/eap-radius.opt +++ b/conf/plugins/eap-radius.opt @@ -84,6 +84,9 @@ charon.plugins.eap-radius.secret = charon.plugins.eap-radius.server = IP/Hostname of RADIUS server. +charon.plugins.eap-radius.source = + Optional specific source IP to use. + charon.plugins.eap-radius.retransmit_base = 1.4 Base to use for calculating exponential back off. @@ -96,12 +99,12 @@ charon.plugins.eap-radius.retransmit_tries = 4 charon.plugins.eap-radius.servers {} Section to specify multiple RADIUS servers. - Section to specify multiple RADIUS servers. The **nas_identifier**, - **secret**, **sockets** and **port** (or **auth_port**) options can be - specified for each server. A server's IP/Hostname can be configured using - the **address** option. The **acct_port** [1813] option can be used to - specify the port used for RADIUS accounting. For each RADIUS server a - priority can be specified using the **preference** [0] option. The + Section to specify multiple RADIUS servers. The **source**, + **nas_identifier**, **secret**, **sockets** and **port** (or **auth_port**) + options can be specified for each server. A server's IP/Hostname can be + configured using the **address** option. The **acct_port** [1813] option can + be used to specify the port used for RADIUS accounting. For each RADIUS + server a priority can be specified using the **preference** [0] option. The retransmission time for each server can set set using **retransmit_base**, **retransmit_timeout** and **retransmit_tries**. diff --git a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c index b6dc640886..5051542615 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_plugin.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_plugin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Tobias Brunner + * Copyright (C) 2013-2025 Tobias Brunner * Copyright (C) 2009 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -119,7 +119,7 @@ static void load_configs(private_eap_radius_plugin_t *this) { enumerator_t *enumerator; radius_config_t *config; - char *nas_identifier, *secret, *address, *section; + char *nas_identifier, *secret, *address, *source, *section; int auth_port, acct_port, sockets, preference; u_int retransmit_tries; double retransmit_timeout, retransmit_base; @@ -135,6 +135,8 @@ static void load_configs(private_eap_radius_plugin_t *this) DBG1(DBG_CFG, "no RADIUS secret defined"); return; } + source = lib->settings->get_str(lib->settings, + "%s.plugins.eap-radius.source", NULL, lib->ns); nas_identifier = lib->settings->get_str(lib->settings, "%s.plugins.eap-radius.nas_identifier", "strongSwan", lib->ns); @@ -150,7 +152,7 @@ static void load_configs(private_eap_radius_plugin_t *this) retransmit_base = lib->settings->get_double(lib->settings, "%s.plugins.eap-radius.retransmit_base", 1.4, lib->ns); - config = radius_config_create(address, address, auth_port, ACCT_PORT, + config = radius_config_create(address, address, source, auth_port, ACCT_PORT, nas_identifier, secret, sockets, 0, retransmit_tries, retransmit_timeout, retransmit_base); @@ -183,6 +185,11 @@ static void load_configs(private_eap_radius_plugin_t *this) DBG1(DBG_CFG, "RADIUS server '%s' misses secret, skipped", section); continue; } + source = lib->settings->get_str(lib->settings, + "%s.plugins.eap-radius.servers.%s.source", + lib->settings->get_str(lib->settings, + "%s.plugins.eap-radius.source", NULL, lib->ns), + lib->ns, section); nas_identifier = lib->settings->get_str(lib->settings, "%s.plugins.eap-radius.servers.%s.nas_identifier", lib->settings->get_str(lib->settings, @@ -228,7 +235,7 @@ static void load_configs(private_eap_radius_plugin_t *this) "%s.plugins.eap-radius.servers.%s.preference", 0, lib->ns, section); - config = radius_config_create(section, address, auth_port, acct_port, + config = radius_config_create(section, address, source, auth_port, acct_port, nas_identifier, secret, sockets, preference, retransmit_tries, retransmit_timeout, retransmit_base); diff --git a/src/libradius/radius_config.c b/src/libradius/radius_config.c index 4ab9f640db..bde408e90d 100644 --- a/src/libradius/radius_config.c +++ b/src/libradius/radius_config.c @@ -200,7 +200,7 @@ METHOD(radius_config_t, destroy, void, /** * See header */ -radius_config_t *radius_config_create(char *name, char *address, +radius_config_t *radius_config_create(char *name, char *address, char *source, uint16_t auth_port, uint16_t acct_port, char *nas_identifier, char *secret, int sockets, int preference, @@ -232,7 +232,7 @@ radius_config_t *radius_config_create(char *name, char *address, while (sockets--) { - socket = radius_socket_create(address, auth_port, acct_port, + socket = radius_socket_create(address, source, auth_port, acct_port, chunk_create(secret, strlen(secret)), tries, timeout, base); if (!socket) diff --git a/src/libradius/radius_config.h b/src/libradius/radius_config.h index 2cb1ee1386..5a4d301e4c 100644 --- a/src/libradius/radius_config.h +++ b/src/libradius/radius_config.h @@ -108,6 +108,7 @@ struct radius_config_t { * * @param name server name * @param address server address + * @param source optional source address * @param auth_port server port for authentication * @param acct_port server port for accounting * @param nas_identifier NAS-Identifier to use with this server @@ -118,7 +119,7 @@ struct radius_config_t { * @param timeout retransmission timeout * @param base base to calculate retransmission timeout */ -radius_config_t *radius_config_create(char *name, char *address, +radius_config_t *radius_config_create(char *name, char *address, char *source, uint16_t auth_port, uint16_t acct_port, char *nas_identifier, char *secret, int sockets, int preference, diff --git a/src/libradius/radius_socket.c b/src/libradius/radius_socket.c index 8635e93cb4..4b93fbda18 100644 --- a/src/libradius/radius_socket.c +++ b/src/libradius/radius_socket.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015-2025 Tobias Brunner * Copyright (C) 2010 Martin Willi * * Copyright (C) secunet Security Networks AG @@ -83,6 +84,11 @@ struct private_radius_socket_t { */ char *address; + /** + * Source address + */ + char *source; + /** * current RADIUS identifier */ @@ -130,10 +136,10 @@ struct private_radius_socket_t { static bool check_connection(private_radius_socket_t *this, int *fd, uint16_t port) { + host_t *server, *src = NULL; + if (*fd == -1) { - host_t *server; - server = host_create_from_dns(this->address, AF_UNSPEC, port); if (!server) { @@ -149,19 +155,42 @@ static bool check_connection(private_radius_socket_t *this, server->destroy(server); return FALSE; } + if (this->source) + { + src = host_create_from_string_and_family(this->source, + server->get_family(server), 0); + if (!src) + { + DBG1(DBG_CFG, "invalid source address '%s' to reach RADIUS " + "server %#H", this->source, server); + goto error; + } + if (bind(*fd, src->get_sockaddr(src), + *src->get_sockaddr_len(src)) == -1) + { + DBG1(DBG_CFG, "binding RADIUS socket to %H failed: %s", src, + strerror(errno)); + goto error; + } + } if (connect(*fd, server->get_sockaddr(server), *server->get_sockaddr_len(server)) < 0) { DBG1(DBG_CFG, "connecting RADIUS socket to %#H failed: %s", server, strerror(errno)); - server->destroy(server); - close(*fd); - *fd = -1; - return FALSE; + goto error; } server->destroy(server); + DESTROY_IF(src); } return TRUE; + +error: + server->destroy(server); + DESTROY_IF(src); + close(*fd); + *fd = -1; + return FALSE; } /** @@ -383,7 +412,8 @@ METHOD(radius_socket_t, destroy, void, /** * See header */ -radius_socket_t *radius_socket_create(char *address, uint16_t auth_port, +radius_socket_t *radius_socket_create(char *address, char *source, + uint16_t auth_port, uint16_t acct_port, chunk_t secret, u_int tries, double timeout, double base) { @@ -396,6 +426,7 @@ radius_socket_t *radius_socket_create(char *address, uint16_t auth_port, .destroy = _destroy, }, .address = address, + .source = source, .auth_port = auth_port, .auth_fd = -1, .acct_port = acct_port, diff --git a/src/libradius/radius_socket.h b/src/libradius/radius_socket.h index 69ee7c4eeb..f9415b9aed 100644 --- a/src/libradius/radius_socket.h +++ b/src/libradius/radius_socket.h @@ -90,6 +90,7 @@ struct radius_socket_t { * Create a radius_socket instance. * * @param address server name + * @param source optional source address * @param auth_port server port for authentication * @param acct_port server port for accounting * @param secret RADIUS secret @@ -97,8 +98,9 @@ struct radius_socket_t { * @param timeout retransmission timeout * @param base base to calculate retransmission timeout */ -radius_socket_t *radius_socket_create(char *address, uint16_t auth_port, - uint16_t acct_port, chunk_t secret, - u_int tries, double timeout, double base); +radius_socket_t *radius_socket_create(char *address, char *source, + uint16_t auth_port, uint16_t acct_port, + chunk_t secret, u_int tries, + double timeout, double base); #endif /** RADIUS_SOCKET_H_ @}*/