diff mbox series

[1/1] Add FQDN support for auth and acct server addresses

Message ID 20211203111841.3631616-2-magnusmalm@gmail.com
State New
Headers show
Series Add FQDN support for auth and acct server addresses | expand

Commit Message

Magnus Malm Dec. 3, 2021, 11:18 a.m. UTC
This patch adds support to configure Fully Qualified Domain Names (FQDN)
for authentication and accounting server addresses.

Signed-off-by: Magnus Malm <magnusmalm@gmail.com>
---
 hostapd/config_file.c      | 31 ++++++++++++++-
 src/radius/radius_client.c | 13 ++++++-
 src/radius/radius_client.h |  3 ++
 src/utils/ip_addr.c        | 77 ++++++++++++++++++++++++++++++++++++++
 src/utils/ip_addr.h        |  1 +
 5 files changed, 123 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index daf3f37ad..2e4aef0b5 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -24,6 +24,7 @@ 
 #include "ap/wpa_auth.h"
 #include "ap/ap_config.h"
 #include "config_file.h"
+#include "ip_addr.h"
 
 
 #ifndef CONFIG_NO_VLAN
@@ -627,6 +628,21 @@  static int hostapd_config_read_eap_user(const char *fname,
 
 
 #ifndef CONFIG_NO_RADIUS
+
+static int radius_parse_fqdn(const char *fqdn,
+			     struct hostapd_radius_server *nserv)
+{
+	size_t len = strnlen(fqdn, NI_MAXHOST);
+
+	if (len < 1)
+		return -1;
+
+	strncpy(nserv->fqdn_addr, fqdn, len);
+	nserv->fqdn_addr[len + 1] = '\0';
+
+	return 1;
+}
+
 static int
 hostapd_config_read_radius_addr(struct hostapd_radius_server **server,
 				int *num_server, const char *val, int def_port,
@@ -648,7 +664,20 @@  hostapd_config_read_radius_addr(struct hostapd_radius_server **server,
 	os_memset(nserv, 0, sizeof(*nserv));
 	nserv->port = def_port;
 	ret = hostapd_parse_ip_addr(val, &nserv->addr);
-	nserv->index = server_index++;
+
+	/* RADIUS address is not a valid IP address, see if it's a FQDN */
+	if (ret) {
+		if ((radius_parse_fqdn(val, nserv) < 0)) {
+			wpa_printf(MSG_ERROR, "FQDN %s parse failed", val);
+			return -1;
+		} else {
+			nserv->resolved = resolve_fqdn(nserv->fqdn_addr, &nserv->addr);
+                        if (!nserv->resolved)
+				wpa_printf(MSG_INFO, "FQDN %s resolve failed", nserv->fqdn_addr);
+                }
+		ret = 0;
+        }
+        nserv->index = server_index++;
 
 	return ret;
 }
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index ee9e46d2a..47f2aa96a 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -1136,7 +1136,18 @@  radius_change_server(struct radius_client_data *radius,
 				       radius_client_timer, radius, NULL);
 	}
 
-	switch (nserv->addr.af) {
+	/* If FQDN is confiured but it was not resolved when the config file was
+	 * read at startup, try again. */
+	if ((os_strncmp("", nserv->fqdn_addr, sizeof(nserv->fqdn_addr)) != 0) &&
+	    !nserv->resolved) {
+		nserv->resolved = resolve_fqdn(nserv->fqdn_addr, &nserv->addr);
+		if (!nserv->resolved) {
+			wpa_printf(MSG_ERROR, "FQDN %s failed to be resolved", nserv->fqdn_addr);
+			return -1;
+		}
+	}
+
+        switch (nserv->addr.af) {
 	case AF_INET:
 		os_memset(&serv, 0, sizeof(serv));
 		serv.sin_family = AF_INET;
diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
index 687cd81ae..989209ef3 100644
--- a/src/radius/radius_client.h
+++ b/src/radius/radius_client.h
@@ -9,6 +9,7 @@ 
 #ifndef RADIUS_CLIENT_H
 #define RADIUS_CLIENT_H
 
+#include <netdb.h>
 #include "ip_addr.h"
 
 struct radius_msg;
@@ -29,6 +30,8 @@  struct hostapd_radius_server {
 	 * addr - radiusAuthServerAddress or radiusAccServerAddress
 	 */
 	struct hostapd_ip_addr addr;
+	char fqdn_addr[NI_MAXHOST];
+	int resolved;
 
 	/**
 	 * port - radiusAuthClientServerPortNumber or radiusAccClientServerPortNumber
diff --git a/src/utils/ip_addr.c b/src/utils/ip_addr.c
index 92a359039..96a7b3b29 100644
--- a/src/utils/ip_addr.c
+++ b/src/utils/ip_addr.c
@@ -6,11 +6,88 @@ 
  * See README for more details.
  */
 
+#include <netdb.h>
+#include <resolv.h>
+
 #include "includes.h"
 
 #include "common.h"
 #include "ip_addr.h"
 
+/**
+ * Do a DNS lookup on fqdn_addr and, if successful, set addr accordingly. If
+ * resolve fails, return 0, otherwise 1.
+ */
+int resolve_fqdn(const char *fqdn_addr, struct hostapd_ip_addr *addr)
+{
+	struct addrinfo *servinfo = NULL;
+	struct addrinfo hints;
+	struct addrinfo *next = NULL;
+	int sfd = 0;
+	int rc = 0;
+
+	/* Ensure we do not get old DNS entries from getaddrinfo() */
+	res_init();
+
+	os_memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_family = AF_UNSPEC;     /* IPv4 or IPv6*/
+	hints.ai_socktype = SOCK_DGRAM;  /* DGRAM sockets only */
+	hints.ai_protocol = IPPROTO_UDP; /* UDP only */
+	hints.ai_flags = AI_NUMERICSERV; /* No service name lookup */
+
+	rc = getaddrinfo(fqdn_addr, NULL, &hints, &servinfo);
+	if (rc != 0 || !servinfo) {
+		freeaddrinfo(servinfo);
+		return 0;
+	}
+
+	/* For each addrinfo entry (if any), validate it by trying to connect to
+	 * it. Break out of the loop with first entry we can connect with. */
+	for (next = servinfo; next != NULL; next = next->ai_next) {
+		/* We're only interested in IPv4 or IPv6 entries */
+		if (next->ai_family == AF_INET || next->ai_family == AF_INET6) {
+			sfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol);
+			if (sfd == -1)
+				continue;
+
+			if (connect(sfd, next->ai_addr, next->ai_addrlen) == -1) {
+				close(sfd);
+				continue;
+			}
+
+			/* Connection established. Use this entry. */
+			close(sfd);
+			break;
+                }
+        }
+
+	/* Could not connect using any entry returned by getaddrinfo() */
+	if (next == NULL) {
+		freeaddrinfo(servinfo);
+		return 0;
+	}
+
+	/* Update addr with the entry we could connect with */
+	addr->af = next->ai_family;
+	switch (next->ai_family) {
+	case AF_INET:
+		addr->u.v4.s_addr = (*(struct sockaddr_in *)next->ai_addr).sin_addr.s_addr;
+		break;
+
+#ifdef CONFIG_IPV6
+	case AF_INET6:
+		os_memcpy(addr->u.v6.s6_addr, &(*(struct sockaddr_in6 *)next->ai_addr).sin6_addr,
+			  sizeof(struct in6_addr));
+		break;
+#endif /* CONFIG_IPV6 */
+	default:
+		return 0;
+	}
+
+	freeaddrinfo(servinfo);
+	return 1;
+}
+
 const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
 			    size_t buflen)
 {
diff --git a/src/utils/ip_addr.h b/src/utils/ip_addr.h
index 0670411cc..25d69b544 100644
--- a/src/utils/ip_addr.h
+++ b/src/utils/ip_addr.h
@@ -24,4 +24,5 @@  const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
 			    size_t buflen);
 int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr);
 
+int resolve_fqdn(const char *fqdn_addr, struct hostapd_ip_addr *addr);
 #endif /* IP_ADDR_H */