xref: /freebsd/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * WPA Supplicant / UDP socket -based control interface
3*c1d255d3SCy Schubert  * Copyright (c) 2004-2020, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "eloop.h"
1339beb93cSSam Leffler #include "config.h"
1439beb93cSSam Leffler #include "eapol_supp/eapol_supp_sm.h"
1539beb93cSSam Leffler #include "wpa_supplicant_i.h"
1639beb93cSSam Leffler #include "ctrl_iface.h"
17e28a4053SRui Paulo #include "common/wpa_ctrl.h"
1839beb93cSSam Leffler 
1939beb93cSSam Leffler 
2039beb93cSSam Leffler #define COOKIE_LEN 8
2139beb93cSSam Leffler 
2239beb93cSSam Leffler /* Per-interface ctrl_iface */
2339beb93cSSam Leffler 
2439beb93cSSam Leffler /**
2539beb93cSSam Leffler  * struct wpa_ctrl_dst - Internal data structure of control interface monitors
2639beb93cSSam Leffler  *
2739beb93cSSam Leffler  * This structure is used to store information about registered control
2839beb93cSSam Leffler  * interface monitors into struct wpa_supplicant. This data is private to
2939beb93cSSam Leffler  * ctrl_iface_udp.c and should not be touched directly from other files.
3039beb93cSSam Leffler  */
3139beb93cSSam Leffler struct wpa_ctrl_dst {
3239beb93cSSam Leffler 	struct wpa_ctrl_dst *next;
335b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
345b9c547cSRui Paulo 	struct sockaddr_in6 addr;
355b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
3639beb93cSSam Leffler 	struct sockaddr_in addr;
375b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
3839beb93cSSam Leffler 	socklen_t addrlen;
3939beb93cSSam Leffler 	int debug_level;
4039beb93cSSam Leffler 	int errors;
4139beb93cSSam Leffler };
4239beb93cSSam Leffler 
4339beb93cSSam Leffler 
4439beb93cSSam Leffler struct ctrl_iface_priv {
4539beb93cSSam Leffler 	struct wpa_supplicant *wpa_s;
4639beb93cSSam Leffler 	int sock;
4739beb93cSSam Leffler 	struct wpa_ctrl_dst *ctrl_dst;
4839beb93cSSam Leffler 	u8 cookie[COOKIE_LEN];
4939beb93cSSam Leffler };
5039beb93cSSam Leffler 
51780fb4a2SCy Schubert struct ctrl_iface_global_priv {
52780fb4a2SCy Schubert 	int sock;
53780fb4a2SCy Schubert 	struct wpa_ctrl_dst *ctrl_dst;
54780fb4a2SCy Schubert 	u8 cookie[COOKIE_LEN];
55780fb4a2SCy Schubert };
5639beb93cSSam Leffler 
57780fb4a2SCy Schubert 
58780fb4a2SCy Schubert static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
59780fb4a2SCy Schubert 					   const char *ifname, int sock,
60780fb4a2SCy Schubert 					   struct wpa_ctrl_dst **head,
6139beb93cSSam Leffler 					   int level, const char *buf,
6239beb93cSSam Leffler 					   size_t len);
6339beb93cSSam Leffler 
6439beb93cSSam Leffler 
wpas_ctrl_iface_free_dst(struct wpa_ctrl_dst * dst)65780fb4a2SCy Schubert static void wpas_ctrl_iface_free_dst(struct wpa_ctrl_dst *dst)
66780fb4a2SCy Schubert {
67780fb4a2SCy Schubert 	struct wpa_ctrl_dst *prev;
68780fb4a2SCy Schubert 
69780fb4a2SCy Schubert 	while (dst) {
70780fb4a2SCy Schubert 		prev = dst;
71780fb4a2SCy Schubert 		dst = dst->next;
72780fb4a2SCy Schubert 		os_free(prev);
73780fb4a2SCy Schubert 	}
74780fb4a2SCy Schubert }
75780fb4a2SCy Schubert 
76780fb4a2SCy Schubert 
wpa_supplicant_ctrl_iface_attach(struct wpa_ctrl_dst ** head,struct sockaddr_in6 * from,socklen_t fromlen)77780fb4a2SCy Schubert static int wpa_supplicant_ctrl_iface_attach(struct wpa_ctrl_dst **head,
785b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
795b9c547cSRui Paulo 					    struct sockaddr_in6 *from,
805b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
8139beb93cSSam Leffler 					    struct sockaddr_in *from,
825b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
8339beb93cSSam Leffler 					    socklen_t fromlen)
8439beb93cSSam Leffler {
8539beb93cSSam Leffler 	struct wpa_ctrl_dst *dst;
865b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
875b9c547cSRui Paulo 	char addr[INET6_ADDRSTRLEN];
885b9c547cSRui Paulo #endif /* CONFIG_UDP_IPV6 */
8939beb93cSSam Leffler 
9039beb93cSSam Leffler 	dst = os_zalloc(sizeof(*dst));
9139beb93cSSam Leffler 	if (dst == NULL)
9239beb93cSSam Leffler 		return -1;
935b9c547cSRui Paulo 	os_memcpy(&dst->addr, from, sizeof(*from));
9439beb93cSSam Leffler 	dst->addrlen = fromlen;
9539beb93cSSam Leffler 	dst->debug_level = MSG_INFO;
96780fb4a2SCy Schubert 	dst->next = *head;
97780fb4a2SCy Schubert 	*head = dst;
985b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
995b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
1005b9c547cSRui Paulo 		   inet_ntop(AF_INET6, &from->sin6_addr, addr, sizeof(*from)),
1015b9c547cSRui Paulo 		   ntohs(from->sin6_port));
1025b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
10339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
10439beb93cSSam Leffler 		   inet_ntoa(from->sin_addr), ntohs(from->sin_port));
1055b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
10639beb93cSSam Leffler 	return 0;
10739beb93cSSam Leffler }
10839beb93cSSam Leffler 
10939beb93cSSam Leffler 
wpa_supplicant_ctrl_iface_detach(struct wpa_ctrl_dst ** head,struct sockaddr_in6 * from,socklen_t fromlen)110780fb4a2SCy Schubert static int wpa_supplicant_ctrl_iface_detach(struct wpa_ctrl_dst **head,
1115b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
1125b9c547cSRui Paulo 					    struct sockaddr_in6 *from,
1135b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
11439beb93cSSam Leffler 					    struct sockaddr_in *from,
1155b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
11639beb93cSSam Leffler 					    socklen_t fromlen)
11739beb93cSSam Leffler {
11839beb93cSSam Leffler 	struct wpa_ctrl_dst *dst, *prev = NULL;
1195b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
1205b9c547cSRui Paulo 	char addr[INET6_ADDRSTRLEN];
1215b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
12239beb93cSSam Leffler 
123780fb4a2SCy Schubert 	dst = *head;
12439beb93cSSam Leffler 	while (dst) {
1255b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
1265b9c547cSRui Paulo 		if (from->sin6_port == dst->addr.sin6_port &&
1275b9c547cSRui Paulo 		    !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
1285b9c547cSRui Paulo 			       sizeof(from->sin6_addr))) {
1295b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s:%d",
1305b9c547cSRui Paulo 				   inet_ntop(AF_INET6, &from->sin6_addr, addr,
1315b9c547cSRui Paulo 					     sizeof(*from)),
1325b9c547cSRui Paulo 				   ntohs(from->sin6_port));
1335b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
13439beb93cSSam Leffler 		if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
13539beb93cSSam Leffler 		    from->sin_port == dst->addr.sin_port) {
1365b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
1375b9c547cSRui Paulo 				   "%s:%d", inet_ntoa(from->sin_addr),
1385b9c547cSRui Paulo 				   ntohs(from->sin_port));
1395b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
14039beb93cSSam Leffler 			if (prev == NULL)
141780fb4a2SCy Schubert 				*head = dst->next;
14239beb93cSSam Leffler 			else
14339beb93cSSam Leffler 				prev->next = dst->next;
14439beb93cSSam Leffler 			os_free(dst);
14539beb93cSSam Leffler 			return 0;
14639beb93cSSam Leffler 		}
14739beb93cSSam Leffler 		prev = dst;
14839beb93cSSam Leffler 		dst = dst->next;
14939beb93cSSam Leffler 	}
15039beb93cSSam Leffler 	return -1;
15139beb93cSSam Leffler }
15239beb93cSSam Leffler 
15339beb93cSSam Leffler 
15439beb93cSSam Leffler static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
1555b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
1565b9c547cSRui Paulo 					   struct sockaddr_in6 *from,
1575b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
15839beb93cSSam Leffler 					   struct sockaddr_in *from,
1595b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
16039beb93cSSam Leffler 					   socklen_t fromlen,
16139beb93cSSam Leffler 					   char *level)
16239beb93cSSam Leffler {
16339beb93cSSam Leffler 	struct wpa_ctrl_dst *dst;
1645b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
1655b9c547cSRui Paulo 	char addr[INET6_ADDRSTRLEN];
1665b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
16739beb93cSSam Leffler 
16839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
16939beb93cSSam Leffler 
17039beb93cSSam Leffler 	dst = priv->ctrl_dst;
17139beb93cSSam Leffler 	while (dst) {
1725b9c547cSRui Paulo #if CONFIG_CTRL_IFACE_UDP_IPV6
1735b9c547cSRui Paulo 		if (from->sin6_port == dst->addr.sin6_port &&
1745b9c547cSRui Paulo 		    !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
1755b9c547cSRui Paulo 			       sizeof(from->sin6_addr))) {
1765b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level %s:%d",
1775b9c547cSRui Paulo 				   inet_ntop(AF_INET6, &from->sin6_addr, addr,
1785b9c547cSRui Paulo 					     sizeof(*from)),
1795b9c547cSRui Paulo 				   ntohs(from->sin6_port));
1805b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
18139beb93cSSam Leffler 		if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
18239beb93cSSam Leffler 		    from->sin_port == dst->addr.sin_port) {
18339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor "
18439beb93cSSam Leffler 				   "level %s:%d", inet_ntoa(from->sin_addr),
18539beb93cSSam Leffler 				   ntohs(from->sin_port));
1865b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
18739beb93cSSam Leffler 			dst->debug_level = atoi(level);
18839beb93cSSam Leffler 			return 0;
18939beb93cSSam Leffler 		}
19039beb93cSSam Leffler 		dst = dst->next;
19139beb93cSSam Leffler 	}
19239beb93cSSam Leffler 
19339beb93cSSam Leffler 	return -1;
19439beb93cSSam Leffler }
19539beb93cSSam Leffler 
19639beb93cSSam Leffler 
19739beb93cSSam Leffler static char *
19839beb93cSSam Leffler wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv,
19939beb93cSSam Leffler 				     size_t *reply_len)
20039beb93cSSam Leffler {
20139beb93cSSam Leffler 	char *reply;
20239beb93cSSam Leffler 	reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
20339beb93cSSam Leffler 	if (reply == NULL) {
20439beb93cSSam Leffler 		*reply_len = 1;
20539beb93cSSam Leffler 		return NULL;
20639beb93cSSam Leffler 	}
20739beb93cSSam Leffler 
20839beb93cSSam Leffler 	os_memcpy(reply, "COOKIE=", 7);
20939beb93cSSam Leffler 	wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
21039beb93cSSam Leffler 			 priv->cookie, COOKIE_LEN);
21139beb93cSSam Leffler 
21239beb93cSSam Leffler 	*reply_len = 7 + 2 * COOKIE_LEN;
21339beb93cSSam Leffler 	return reply;
21439beb93cSSam Leffler }
21539beb93cSSam Leffler 
21639beb93cSSam Leffler 
21739beb93cSSam Leffler static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
21839beb93cSSam Leffler 					      void *sock_ctx)
21939beb93cSSam Leffler {
22039beb93cSSam Leffler 	struct wpa_supplicant *wpa_s = eloop_ctx;
22139beb93cSSam Leffler 	struct ctrl_iface_priv *priv = sock_ctx;
222*c1d255d3SCy Schubert 	char *buf, *pos;
22339beb93cSSam Leffler 	int res;
2245b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
2255b9c547cSRui Paulo 	struct sockaddr_in6 from;
2265b9c547cSRui Paulo #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
2275b9c547cSRui Paulo 	char addr[INET6_ADDRSTRLEN];
2285b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
2295b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
23039beb93cSSam Leffler 	struct sockaddr_in from;
2315b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
23239beb93cSSam Leffler 	socklen_t fromlen = sizeof(from);
23339beb93cSSam Leffler 	char *reply = NULL;
23439beb93cSSam Leffler 	size_t reply_len = 0;
23539beb93cSSam Leffler 	int new_attached = 0;
23639beb93cSSam Leffler 	u8 cookie[COOKIE_LEN];
23739beb93cSSam Leffler 
238*c1d255d3SCy Schubert 	buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
239*c1d255d3SCy Schubert 	if (!buf)
240*c1d255d3SCy Schubert 		return;
241*c1d255d3SCy Schubert 	res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN, 0,
24239beb93cSSam Leffler 		       (struct sockaddr *) &from, &fromlen);
24339beb93cSSam Leffler 	if (res < 0) {
2445b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
2455b9c547cSRui Paulo 			   strerror(errno));
246*c1d255d3SCy Schubert 		os_free(buf);
24739beb93cSSam Leffler 		return;
24839beb93cSSam Leffler 	}
249f05cddf9SRui Paulo 
250f05cddf9SRui Paulo #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
2515b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
2525b9c547cSRui Paulo 	inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from));
2535b9c547cSRui Paulo 	if (os_strcmp(addr, "::1")) {
2545b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
2555b9c547cSRui Paulo 			   addr);
256*c1d255d3SCy Schubert 		os_free(buf);
257*c1d255d3SCy Schubert 		return;
2585b9c547cSRui Paulo 	}
2595b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
26039beb93cSSam Leffler 	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
26139beb93cSSam Leffler 		/*
26239beb93cSSam Leffler 		 * The OS networking stack is expected to drop this kind of
26339beb93cSSam Leffler 		 * frames since the socket is bound to only localhost address.
26439beb93cSSam Leffler 		 * Just in case, drop the frame if it is coming from any other
26539beb93cSSam Leffler 		 * address.
26639beb93cSSam Leffler 		 */
26739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
26839beb93cSSam Leffler 			   "source %s", inet_ntoa(from.sin_addr));
269*c1d255d3SCy Schubert 		os_free(buf);
27039beb93cSSam Leffler 		return;
27139beb93cSSam Leffler 	}
2725b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
273f05cddf9SRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
274f05cddf9SRui Paulo 
275*c1d255d3SCy Schubert 	if ((size_t) res > CTRL_IFACE_MAX_LEN) {
276*c1d255d3SCy Schubert 		wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
277*c1d255d3SCy Schubert 		os_free(buf);
278*c1d255d3SCy Schubert 		return;
279*c1d255d3SCy Schubert 	}
28039beb93cSSam Leffler 	buf[res] = '\0';
28139beb93cSSam Leffler 
28239beb93cSSam Leffler 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
28339beb93cSSam Leffler 		reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len);
28439beb93cSSam Leffler 		goto done;
28539beb93cSSam Leffler 	}
28639beb93cSSam Leffler 
28739beb93cSSam Leffler 	/*
28839beb93cSSam Leffler 	 * Require that the client includes a prefix with the 'cookie' value
28939beb93cSSam Leffler 	 * fetched with GET_COOKIE command. This is used to verify that the
29039beb93cSSam Leffler 	 * client has access to a bidirectional link over UDP in order to
29139beb93cSSam Leffler 	 * avoid attacks using forged localhost IP address even if the OS does
29239beb93cSSam Leffler 	 * not block such frames from remote destinations.
29339beb93cSSam Leffler 	 */
29439beb93cSSam Leffler 	if (os_strncmp(buf, "COOKIE=", 7) != 0) {
29539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
29639beb93cSSam Leffler 			   "drop request");
297*c1d255d3SCy Schubert 		os_free(buf);
29839beb93cSSam Leffler 		return;
29939beb93cSSam Leffler 	}
30039beb93cSSam Leffler 
30139beb93cSSam Leffler 	if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
30239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
30339beb93cSSam Leffler 			   "request - drop request");
304*c1d255d3SCy Schubert 		os_free(buf);
30539beb93cSSam Leffler 		return;
30639beb93cSSam Leffler 	}
30739beb93cSSam Leffler 
30839beb93cSSam Leffler 	if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
30939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
31039beb93cSSam Leffler 			   "drop request");
311*c1d255d3SCy Schubert 		os_free(buf);
31239beb93cSSam Leffler 		return;
31339beb93cSSam Leffler 	}
31439beb93cSSam Leffler 
31539beb93cSSam Leffler 	pos = buf + 7 + 2 * COOKIE_LEN;
31639beb93cSSam Leffler 	while (*pos == ' ')
31739beb93cSSam Leffler 		pos++;
31839beb93cSSam Leffler 
31939beb93cSSam Leffler 	if (os_strcmp(pos, "ATTACH") == 0) {
320780fb4a2SCy Schubert 		if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
321780fb4a2SCy Schubert 						     &from, fromlen))
32239beb93cSSam Leffler 			reply_len = 1;
32339beb93cSSam Leffler 		else {
32439beb93cSSam Leffler 			new_attached = 1;
32539beb93cSSam Leffler 			reply_len = 2;
32639beb93cSSam Leffler 		}
32739beb93cSSam Leffler 	} else if (os_strcmp(pos, "DETACH") == 0) {
328780fb4a2SCy Schubert 		if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst,
329780fb4a2SCy Schubert 						     &from, fromlen))
33039beb93cSSam Leffler 			reply_len = 1;
33139beb93cSSam Leffler 		else
33239beb93cSSam Leffler 			reply_len = 2;
33339beb93cSSam Leffler 	} else if (os_strncmp(pos, "LEVEL ", 6) == 0) {
33439beb93cSSam Leffler 		if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
33539beb93cSSam Leffler 						    pos + 6))
33639beb93cSSam Leffler 			reply_len = 1;
33739beb93cSSam Leffler 		else
33839beb93cSSam Leffler 			reply_len = 2;
33939beb93cSSam Leffler 	} else {
34039beb93cSSam Leffler 		reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
34139beb93cSSam Leffler 							  &reply_len);
34239beb93cSSam Leffler 	}
34339beb93cSSam Leffler 
34439beb93cSSam Leffler  done:
34539beb93cSSam Leffler 	if (reply) {
34639beb93cSSam Leffler 		sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
34739beb93cSSam Leffler 		       fromlen);
34839beb93cSSam Leffler 		os_free(reply);
34939beb93cSSam Leffler 	} else if (reply_len == 1) {
35039beb93cSSam Leffler 		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
35139beb93cSSam Leffler 		       fromlen);
35239beb93cSSam Leffler 	} else if (reply_len == 2) {
35339beb93cSSam Leffler 		sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
35439beb93cSSam Leffler 		       fromlen);
35539beb93cSSam Leffler 	}
35639beb93cSSam Leffler 
357*c1d255d3SCy Schubert 	os_free(buf);
358*c1d255d3SCy Schubert 
35939beb93cSSam Leffler 	if (new_attached)
36039beb93cSSam Leffler 		eapol_sm_notify_ctrl_attached(wpa_s->eapol);
36139beb93cSSam Leffler }
36239beb93cSSam Leffler 
36339beb93cSSam Leffler 
364325151a3SRui Paulo static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
365325151a3SRui Paulo 					     enum wpa_msg_type type,
36639beb93cSSam Leffler 					     const char *txt, size_t len)
36739beb93cSSam Leffler {
36839beb93cSSam Leffler 	struct wpa_supplicant *wpa_s = ctx;
369780fb4a2SCy Schubert 
370780fb4a2SCy Schubert 	if (!wpa_s)
37139beb93cSSam Leffler 		return;
372780fb4a2SCy Schubert 
373780fb4a2SCy Schubert 	if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) {
374780fb4a2SCy Schubert 		struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface;
375780fb4a2SCy Schubert 
376780fb4a2SCy Schubert 		if (priv->ctrl_dst) {
377780fb4a2SCy Schubert 			wpa_supplicant_ctrl_iface_send(
378780fb4a2SCy Schubert 				wpa_s,
379780fb4a2SCy Schubert 				type != WPA_MSG_PER_INTERFACE ?
380780fb4a2SCy Schubert 				NULL : wpa_s->ifname,
381780fb4a2SCy Schubert 				priv->sock, &priv->ctrl_dst, level, txt, len);
382780fb4a2SCy Schubert 		}
383780fb4a2SCy Schubert 	}
384780fb4a2SCy Schubert 
385780fb4a2SCy Schubert 	if (type == WPA_MSG_ONLY_GLOBAL || !wpa_s->ctrl_iface)
386780fb4a2SCy Schubert 		return;
387780fb4a2SCy Schubert 
388780fb4a2SCy Schubert 	wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
389780fb4a2SCy Schubert 				       &wpa_s->ctrl_iface->ctrl_dst,
390780fb4a2SCy Schubert 				       level, txt, len);
39139beb93cSSam Leffler }
39239beb93cSSam Leffler 
39339beb93cSSam Leffler 
39439beb93cSSam Leffler struct ctrl_iface_priv *
39539beb93cSSam Leffler wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
39639beb93cSSam Leffler {
39739beb93cSSam Leffler 	struct ctrl_iface_priv *priv;
398780fb4a2SCy Schubert 	char port_str[40];
399f05cddf9SRui Paulo 	int port = WPA_CTRL_IFACE_PORT;
400780fb4a2SCy Schubert 	char *pos;
4015b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
4025b9c547cSRui Paulo 	struct sockaddr_in6 addr;
4035b9c547cSRui Paulo 	int domain = PF_INET6;
4045b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
4055b9c547cSRui Paulo 	struct sockaddr_in addr;
4065b9c547cSRui Paulo 	int domain = PF_INET;
4075b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
40839beb93cSSam Leffler 
40939beb93cSSam Leffler 	priv = os_zalloc(sizeof(*priv));
41039beb93cSSam Leffler 	if (priv == NULL)
41139beb93cSSam Leffler 		return NULL;
41239beb93cSSam Leffler 	priv->wpa_s = wpa_s;
41339beb93cSSam Leffler 	priv->sock = -1;
41439beb93cSSam Leffler 	os_get_random(priv->cookie, COOKIE_LEN);
41539beb93cSSam Leffler 
41639beb93cSSam Leffler 	if (wpa_s->conf->ctrl_interface == NULL)
41739beb93cSSam Leffler 		return priv;
41839beb93cSSam Leffler 
419780fb4a2SCy Schubert 	pos = os_strstr(wpa_s->conf->ctrl_interface, "udp:");
420780fb4a2SCy Schubert 	if (pos) {
421780fb4a2SCy Schubert 		pos += 4;
422780fb4a2SCy Schubert 		port = atoi(pos);
423780fb4a2SCy Schubert 		if (port <= 0) {
424780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port: %s",
425780fb4a2SCy Schubert 				   wpa_s->conf->ctrl_interface);
426780fb4a2SCy Schubert 			goto fail;
427780fb4a2SCy Schubert 		}
428780fb4a2SCy Schubert 	}
429780fb4a2SCy Schubert 
4305b9c547cSRui Paulo 	priv->sock = socket(domain, SOCK_DGRAM, 0);
43139beb93cSSam Leffler 	if (priv->sock < 0) {
4325b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
43339beb93cSSam Leffler 		goto fail;
43439beb93cSSam Leffler 	}
43539beb93cSSam Leffler 
43639beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
4375b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
4385b9c547cSRui Paulo 	addr.sin6_family = AF_INET6;
4395b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
4405b9c547cSRui Paulo 	addr.sin6_addr = in6addr_any;
4415b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
4425b9c547cSRui Paulo 	inet_pton(AF_INET6, "::1", &addr.sin6_addr);
4435b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
4445b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
44539beb93cSSam Leffler 	addr.sin_family = AF_INET;
446f05cddf9SRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
447f05cddf9SRui Paulo 	addr.sin_addr.s_addr = INADDR_ANY;
448f05cddf9SRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
44939beb93cSSam Leffler 	addr.sin_addr.s_addr = htonl((127 << 24) | 1);
450f05cddf9SRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
4515b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
452f05cddf9SRui Paulo try_again:
4535b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
4545b9c547cSRui Paulo 	addr.sin6_port = htons(port);
4555b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
456f05cddf9SRui Paulo 	addr.sin_port = htons(port);
4575b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
45839beb93cSSam Leffler 	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
459f05cddf9SRui Paulo 		port--;
460f05cddf9SRui Paulo 		if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
461f05cddf9SRui Paulo 			goto try_again;
4625b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
46339beb93cSSam Leffler 		goto fail;
46439beb93cSSam Leffler 	}
46539beb93cSSam Leffler 
466780fb4a2SCy Schubert 	/* Update the ctrl_interface value to match the selected port */
467780fb4a2SCy Schubert 	os_snprintf(port_str, sizeof(port_str), "udp:%d", port);
468780fb4a2SCy Schubert 	os_free(wpa_s->conf->ctrl_interface);
469780fb4a2SCy Schubert 	wpa_s->conf->ctrl_interface = os_strdup(port_str);
470780fb4a2SCy Schubert 	if (!wpa_s->conf->ctrl_interface) {
471780fb4a2SCy Schubert 		wpa_msg(wpa_s, MSG_ERROR, "Failed to malloc ctrl_interface");
472780fb4a2SCy Schubert 		goto fail;
473780fb4a2SCy Schubert 	}
474780fb4a2SCy Schubert 
475f05cddf9SRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
476f05cddf9SRui Paulo 	wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
477f05cddf9SRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
478f05cddf9SRui Paulo 
47939beb93cSSam Leffler 	eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
48039beb93cSSam Leffler 				 wpa_s, priv);
48139beb93cSSam Leffler 	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
48239beb93cSSam Leffler 
48339beb93cSSam Leffler 	return priv;
48439beb93cSSam Leffler 
48539beb93cSSam Leffler fail:
48639beb93cSSam Leffler 	if (priv->sock >= 0)
48739beb93cSSam Leffler 		close(priv->sock);
48839beb93cSSam Leffler 	os_free(priv);
48939beb93cSSam Leffler 	return NULL;
49039beb93cSSam Leffler }
49139beb93cSSam Leffler 
49239beb93cSSam Leffler 
493*c1d255d3SCy Schubert void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
494*c1d255d3SCy Schubert 				      struct ctrl_iface_priv *priv)
49539beb93cSSam Leffler {
496*c1d255d3SCy Schubert 	if (!priv)
497*c1d255d3SCy Schubert 		return;
498*c1d255d3SCy Schubert 
49939beb93cSSam Leffler 	if (priv->sock > -1) {
50039beb93cSSam Leffler 		eloop_unregister_read_sock(priv->sock);
50139beb93cSSam Leffler 		if (priv->ctrl_dst) {
50239beb93cSSam Leffler 			/*
5035b9c547cSRui Paulo 			 * Wait before closing the control socket if
50439beb93cSSam Leffler 			 * there are any attached monitors in order to allow
50539beb93cSSam Leffler 			 * them to receive any pending messages.
50639beb93cSSam Leffler 			 */
50739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
50839beb93cSSam Leffler 				   "monitors to receive messages");
5095b9c547cSRui Paulo 			os_sleep(0, 100000);
51039beb93cSSam Leffler 		}
51139beb93cSSam Leffler 		close(priv->sock);
51239beb93cSSam Leffler 		priv->sock = -1;
51339beb93cSSam Leffler 	}
51439beb93cSSam Leffler 
515780fb4a2SCy Schubert 	wpas_ctrl_iface_free_dst(priv->ctrl_dst);
51639beb93cSSam Leffler 	os_free(priv);
51739beb93cSSam Leffler }
51839beb93cSSam Leffler 
51939beb93cSSam Leffler 
520780fb4a2SCy Schubert static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
521780fb4a2SCy Schubert 					   const char *ifname, int sock,
522780fb4a2SCy Schubert 					   struct wpa_ctrl_dst **head,
52339beb93cSSam Leffler 					   int level, const char *buf,
52439beb93cSSam Leffler 					   size_t len)
52539beb93cSSam Leffler {
52639beb93cSSam Leffler 	struct wpa_ctrl_dst *dst, *next;
527780fb4a2SCy Schubert 	char levelstr[64];
52839beb93cSSam Leffler 	int idx;
52939beb93cSSam Leffler 	char *sbuf;
53039beb93cSSam Leffler 	int llen;
5315b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
5325b9c547cSRui Paulo 	char addr[INET6_ADDRSTRLEN];
5335b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
53439beb93cSSam Leffler 
535780fb4a2SCy Schubert 	dst = *head;
536780fb4a2SCy Schubert 	if (sock < 0 || dst == NULL)
53739beb93cSSam Leffler 		return;
53839beb93cSSam Leffler 
539780fb4a2SCy Schubert 	if (ifname)
540*c1d255d3SCy Schubert 		os_snprintf(levelstr, sizeof(levelstr), "IFNAME=%s <%d>",
541780fb4a2SCy Schubert 			    ifname, level);
542780fb4a2SCy Schubert 	else
54339beb93cSSam Leffler 		os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
54439beb93cSSam Leffler 
54539beb93cSSam Leffler 	llen = os_strlen(levelstr);
54639beb93cSSam Leffler 	sbuf = os_malloc(llen + len);
54739beb93cSSam Leffler 	if (sbuf == NULL)
54839beb93cSSam Leffler 		return;
54939beb93cSSam Leffler 
55039beb93cSSam Leffler 	os_memcpy(sbuf, levelstr, llen);
55139beb93cSSam Leffler 	os_memcpy(sbuf + llen, buf, len);
55239beb93cSSam Leffler 
55339beb93cSSam Leffler 	idx = 0;
55439beb93cSSam Leffler 	while (dst) {
55539beb93cSSam Leffler 		next = dst->next;
55639beb93cSSam Leffler 		if (level >= dst->debug_level) {
5575b9c547cSRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
5585b9c547cSRui Paulo 			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
5595b9c547cSRui Paulo 				   inet_ntop(AF_INET6, &dst->addr.sin6_addr,
5605b9c547cSRui Paulo 					     addr, sizeof(dst->addr)),
5615b9c547cSRui Paulo 				   ntohs(dst->addr.sin6_port));
5625b9c547cSRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
56339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
56439beb93cSSam Leffler 				   inet_ntoa(dst->addr.sin_addr),
56539beb93cSSam Leffler 				   ntohs(dst->addr.sin_port));
5665b9c547cSRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
567780fb4a2SCy Schubert 			if (sendto(sock, sbuf, llen + len, 0,
56839beb93cSSam Leffler 				   (struct sockaddr *) &dst->addr,
56939beb93cSSam Leffler 				   sizeof(dst->addr)) < 0) {
5705b9c547cSRui Paulo 				wpa_printf(MSG_ERROR,
5715b9c547cSRui Paulo 					   "sendto(CTRL_IFACE monitor): %s",
5725b9c547cSRui Paulo 					   strerror(errno));
57339beb93cSSam Leffler 				dst->errors++;
57439beb93cSSam Leffler 				if (dst->errors > 10) {
57539beb93cSSam Leffler 					wpa_supplicant_ctrl_iface_detach(
576780fb4a2SCy Schubert 						head, &dst->addr,
57739beb93cSSam Leffler 						dst->addrlen);
57839beb93cSSam Leffler 				}
57939beb93cSSam Leffler 			} else
58039beb93cSSam Leffler 				dst->errors = 0;
58139beb93cSSam Leffler 		}
58239beb93cSSam Leffler 		idx++;
58339beb93cSSam Leffler 		dst = next;
58439beb93cSSam Leffler 	}
58539beb93cSSam Leffler 	os_free(sbuf);
58639beb93cSSam Leffler }
58739beb93cSSam Leffler 
58839beb93cSSam Leffler 
58939beb93cSSam Leffler void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
59039beb93cSSam Leffler {
59139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
59239beb93cSSam Leffler 		   priv->wpa_s->ifname);
59339beb93cSSam Leffler 	eloop_wait_for_read_sock(priv->sock);
59439beb93cSSam Leffler }
59539beb93cSSam Leffler 
59639beb93cSSam Leffler 
59739beb93cSSam Leffler /* Global ctrl_iface */
59839beb93cSSam Leffler 
59939beb93cSSam Leffler static char *
60039beb93cSSam Leffler wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv,
60139beb93cSSam Leffler 				 size_t *reply_len)
60239beb93cSSam Leffler {
60339beb93cSSam Leffler 	char *reply;
60439beb93cSSam Leffler 	reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
60539beb93cSSam Leffler 	if (reply == NULL) {
60639beb93cSSam Leffler 		*reply_len = 1;
60739beb93cSSam Leffler 		return NULL;
60839beb93cSSam Leffler 	}
60939beb93cSSam Leffler 
61039beb93cSSam Leffler 	os_memcpy(reply, "COOKIE=", 7);
61139beb93cSSam Leffler 	wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
61239beb93cSSam Leffler 			 priv->cookie, COOKIE_LEN);
61339beb93cSSam Leffler 
61439beb93cSSam Leffler 	*reply_len = 7 + 2 * COOKIE_LEN;
61539beb93cSSam Leffler 	return reply;
61639beb93cSSam Leffler }
61739beb93cSSam Leffler 
61839beb93cSSam Leffler 
61939beb93cSSam Leffler static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
62039beb93cSSam Leffler 						     void *sock_ctx)
62139beb93cSSam Leffler {
62239beb93cSSam Leffler 	struct wpa_global *global = eloop_ctx;
62339beb93cSSam Leffler 	struct ctrl_iface_global_priv *priv = sock_ctx;
624*c1d255d3SCy Schubert 	char *buf, *pos;
62539beb93cSSam Leffler 	int res;
626780fb4a2SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
627780fb4a2SCy Schubert 	struct sockaddr_in6 from;
628*c1d255d3SCy Schubert #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
629*c1d255d3SCy Schubert 	char addr[INET6_ADDRSTRLEN];
630*c1d255d3SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
631780fb4a2SCy Schubert #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
63239beb93cSSam Leffler 	struct sockaddr_in from;
633780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
63439beb93cSSam Leffler 	socklen_t fromlen = sizeof(from);
635780fb4a2SCy Schubert 	char *reply = NULL;
63639beb93cSSam Leffler 	size_t reply_len;
63739beb93cSSam Leffler 	u8 cookie[COOKIE_LEN];
63839beb93cSSam Leffler 
639*c1d255d3SCy Schubert 	buf = os_malloc(CTRL_IFACE_MAX_LEN + 1);
640*c1d255d3SCy Schubert 	if (!buf)
641*c1d255d3SCy Schubert 		return;
642*c1d255d3SCy Schubert 	res = recvfrom(sock, buf, CTRL_IFACE_MAX_LEN, 0,
64339beb93cSSam Leffler 		       (struct sockaddr *) &from, &fromlen);
64439beb93cSSam Leffler 	if (res < 0) {
6455b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
6465b9c547cSRui Paulo 			   strerror(errno));
647*c1d255d3SCy Schubert 		os_free(buf);
64839beb93cSSam Leffler 		return;
64939beb93cSSam Leffler 	}
650f05cddf9SRui Paulo 
651f05cddf9SRui Paulo #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
652*c1d255d3SCy Schubert #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
653*c1d255d3SCy Schubert 	inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from));
654*c1d255d3SCy Schubert 	if (os_strcmp(addr, "::1")) {
655*c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
656*c1d255d3SCy Schubert 			   addr);
657*c1d255d3SCy Schubert 		os_free(buf);
658*c1d255d3SCy Schubert 		return;
659*c1d255d3SCy Schubert 	}
660*c1d255d3SCy Schubert #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
66139beb93cSSam Leffler 	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
66239beb93cSSam Leffler 		/*
66339beb93cSSam Leffler 		 * The OS networking stack is expected to drop this kind of
66439beb93cSSam Leffler 		 * frames since the socket is bound to only localhost address.
66539beb93cSSam Leffler 		 * Just in case, drop the frame if it is coming from any other
66639beb93cSSam Leffler 		 * address.
66739beb93cSSam Leffler 		 */
66839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
66939beb93cSSam Leffler 			   "source %s", inet_ntoa(from.sin_addr));
670*c1d255d3SCy Schubert 		os_free(buf);
67139beb93cSSam Leffler 		return;
67239beb93cSSam Leffler 	}
673780fb4a2SCy Schubert #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
674f05cddf9SRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
675f05cddf9SRui Paulo 
676*c1d255d3SCy Schubert 	if ((size_t) res > CTRL_IFACE_MAX_LEN) {
677*c1d255d3SCy Schubert 		wpa_printf(MSG_ERROR, "recvform(ctrl_iface): input truncated");
678*c1d255d3SCy Schubert 		os_free(buf);
679*c1d255d3SCy Schubert 		return;
680*c1d255d3SCy Schubert 	}
68139beb93cSSam Leffler 	buf[res] = '\0';
68239beb93cSSam Leffler 
68339beb93cSSam Leffler 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
68439beb93cSSam Leffler 		reply = wpa_supplicant_global_get_cookie(priv, &reply_len);
68539beb93cSSam Leffler 		goto done;
68639beb93cSSam Leffler 	}
68739beb93cSSam Leffler 
68839beb93cSSam Leffler 	if (os_strncmp(buf, "COOKIE=", 7) != 0) {
68939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
69039beb93cSSam Leffler 			   "drop request");
691*c1d255d3SCy Schubert 		os_free(buf);
69239beb93cSSam Leffler 		return;
69339beb93cSSam Leffler 	}
69439beb93cSSam Leffler 
69539beb93cSSam Leffler 	if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
69639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
69739beb93cSSam Leffler 			   "request - drop request");
698*c1d255d3SCy Schubert 		os_free(buf);
69939beb93cSSam Leffler 		return;
70039beb93cSSam Leffler 	}
70139beb93cSSam Leffler 
70239beb93cSSam Leffler 	if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
70339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
70439beb93cSSam Leffler 			   "drop request");
705*c1d255d3SCy Schubert 		os_free(buf);
70639beb93cSSam Leffler 		return;
70739beb93cSSam Leffler 	}
70839beb93cSSam Leffler 
70939beb93cSSam Leffler 	pos = buf + 7 + 2 * COOKIE_LEN;
71039beb93cSSam Leffler 	while (*pos == ' ')
71139beb93cSSam Leffler 		pos++;
71239beb93cSSam Leffler 
713780fb4a2SCy Schubert 	if (os_strcmp(pos, "ATTACH") == 0) {
714780fb4a2SCy Schubert 		if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
715780fb4a2SCy Schubert 						     &from, fromlen))
716780fb4a2SCy Schubert 			reply_len = 1;
717780fb4a2SCy Schubert 		else
718780fb4a2SCy Schubert 			reply_len = 2;
719780fb4a2SCy Schubert 	} else if (os_strcmp(pos, "DETACH") == 0) {
720780fb4a2SCy Schubert 		if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst,
721780fb4a2SCy Schubert 						     &from, fromlen))
722780fb4a2SCy Schubert 			reply_len = 1;
723780fb4a2SCy Schubert 		else
724780fb4a2SCy Schubert 			reply_len = 2;
725780fb4a2SCy Schubert 	} else {
72639beb93cSSam Leffler 		reply = wpa_supplicant_global_ctrl_iface_process(global, pos,
72739beb93cSSam Leffler 								 &reply_len);
728780fb4a2SCy Schubert 	}
72939beb93cSSam Leffler 
73039beb93cSSam Leffler  done:
73139beb93cSSam Leffler 	if (reply) {
73239beb93cSSam Leffler 		sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
73339beb93cSSam Leffler 		       fromlen);
73439beb93cSSam Leffler 		os_free(reply);
735780fb4a2SCy Schubert 	} else if (reply_len == 1) {
73639beb93cSSam Leffler 		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
73739beb93cSSam Leffler 		       fromlen);
738780fb4a2SCy Schubert 	} else if (reply_len == 2) {
739780fb4a2SCy Schubert 		sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
740780fb4a2SCy Schubert 		       fromlen);
74139beb93cSSam Leffler 	}
742*c1d255d3SCy Schubert 
743*c1d255d3SCy Schubert 	os_free(buf);
74439beb93cSSam Leffler }
74539beb93cSSam Leffler 
74639beb93cSSam Leffler 
74739beb93cSSam Leffler struct ctrl_iface_global_priv *
74839beb93cSSam Leffler wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
74939beb93cSSam Leffler {
75039beb93cSSam Leffler 	struct ctrl_iface_global_priv *priv;
75139beb93cSSam Leffler 	struct sockaddr_in addr;
752780fb4a2SCy Schubert 	char *pos;
753f05cddf9SRui Paulo 	int port = WPA_GLOBAL_CTRL_IFACE_PORT;
75439beb93cSSam Leffler 
75539beb93cSSam Leffler 	priv = os_zalloc(sizeof(*priv));
75639beb93cSSam Leffler 	if (priv == NULL)
75739beb93cSSam Leffler 		return NULL;
75839beb93cSSam Leffler 	priv->sock = -1;
75939beb93cSSam Leffler 	os_get_random(priv->cookie, COOKIE_LEN);
76039beb93cSSam Leffler 
76139beb93cSSam Leffler 	if (global->params.ctrl_interface == NULL)
76239beb93cSSam Leffler 		return priv;
76339beb93cSSam Leffler 
76439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Global control interface '%s'",
76539beb93cSSam Leffler 		   global->params.ctrl_interface);
76639beb93cSSam Leffler 
767780fb4a2SCy Schubert 	pos = os_strstr(global->params.ctrl_interface, "udp:");
768780fb4a2SCy Schubert 	if (pos) {
769780fb4a2SCy Schubert 		pos += 4;
770780fb4a2SCy Schubert 		port = atoi(pos);
771780fb4a2SCy Schubert 		if (port <= 0) {
772780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port %s",
773780fb4a2SCy Schubert 				   global->params.ctrl_interface);
774780fb4a2SCy Schubert 			goto fail;
775780fb4a2SCy Schubert 		}
776780fb4a2SCy Schubert 	}
777780fb4a2SCy Schubert 
77839beb93cSSam Leffler 	priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
77939beb93cSSam Leffler 	if (priv->sock < 0) {
7805b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
78139beb93cSSam Leffler 		goto fail;
78239beb93cSSam Leffler 	}
78339beb93cSSam Leffler 
78439beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
78539beb93cSSam Leffler 	addr.sin_family = AF_INET;
786f05cddf9SRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
787f05cddf9SRui Paulo 	addr.sin_addr.s_addr = INADDR_ANY;
788f05cddf9SRui Paulo #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
78939beb93cSSam Leffler 	addr.sin_addr.s_addr = htonl((127 << 24) | 1);
790f05cddf9SRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
791f05cddf9SRui Paulo try_again:
792f05cddf9SRui Paulo 	addr.sin_port = htons(port);
79339beb93cSSam Leffler 	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
794f05cddf9SRui Paulo 		port++;
795f05cddf9SRui Paulo 		if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) <
796780fb4a2SCy Schubert 		    WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos)
797f05cddf9SRui Paulo 			goto try_again;
7985b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
79939beb93cSSam Leffler 		goto fail;
80039beb93cSSam Leffler 	}
80139beb93cSSam Leffler 
802f05cddf9SRui Paulo #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
803f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port);
804f05cddf9SRui Paulo #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
805f05cddf9SRui Paulo 
80639beb93cSSam Leffler 	eloop_register_read_sock(priv->sock,
80739beb93cSSam Leffler 				 wpa_supplicant_global_ctrl_iface_receive,
80839beb93cSSam Leffler 				 global, priv);
809780fb4a2SCy Schubert 	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
81039beb93cSSam Leffler 
81139beb93cSSam Leffler 	return priv;
81239beb93cSSam Leffler 
81339beb93cSSam Leffler fail:
81439beb93cSSam Leffler 	if (priv->sock >= 0)
81539beb93cSSam Leffler 		close(priv->sock);
81639beb93cSSam Leffler 	os_free(priv);
81739beb93cSSam Leffler 	return NULL;
81839beb93cSSam Leffler }
81939beb93cSSam Leffler 
82039beb93cSSam Leffler 
82139beb93cSSam Leffler void
82239beb93cSSam Leffler wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
82339beb93cSSam Leffler {
82439beb93cSSam Leffler 	if (priv->sock >= 0) {
82539beb93cSSam Leffler 		eloop_unregister_read_sock(priv->sock);
82639beb93cSSam Leffler 		close(priv->sock);
82739beb93cSSam Leffler 	}
828780fb4a2SCy Schubert 
829780fb4a2SCy Schubert 	wpas_ctrl_iface_free_dst(priv->ctrl_dst);
83039beb93cSSam Leffler 	os_free(priv);
83139beb93cSSam Leffler }
832