xref: /freebsd/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * WPA Supplicant / UDP socket -based control interface
339beb93cSSam Leffler  * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
539beb93cSSam Leffler  * This program is free software; you can redistribute it and/or modify
639beb93cSSam Leffler  * it under the terms of the GNU General Public License version 2 as
739beb93cSSam Leffler  * published by the Free Software Foundation.
839beb93cSSam Leffler  *
939beb93cSSam Leffler  * Alternatively, this software may be distributed under the terms of BSD
1039beb93cSSam Leffler  * license.
1139beb93cSSam Leffler  *
1239beb93cSSam Leffler  * See README and COPYING for more details.
1339beb93cSSam Leffler  */
1439beb93cSSam Leffler 
1539beb93cSSam Leffler #include "includes.h"
1639beb93cSSam Leffler 
1739beb93cSSam Leffler #include "common.h"
1839beb93cSSam Leffler #include "eloop.h"
1939beb93cSSam Leffler #include "config.h"
2039beb93cSSam Leffler #include "eapol_supp/eapol_supp_sm.h"
2139beb93cSSam Leffler #include "wpa_supplicant_i.h"
2239beb93cSSam Leffler #include "ctrl_iface.h"
2339beb93cSSam Leffler #include "wpa_ctrl.h"
2439beb93cSSam Leffler 
2539beb93cSSam Leffler 
2639beb93cSSam Leffler #define COOKIE_LEN 8
2739beb93cSSam Leffler 
2839beb93cSSam Leffler /* Per-interface ctrl_iface */
2939beb93cSSam Leffler 
3039beb93cSSam Leffler /**
3139beb93cSSam Leffler  * struct wpa_ctrl_dst - Internal data structure of control interface monitors
3239beb93cSSam Leffler  *
3339beb93cSSam Leffler  * This structure is used to store information about registered control
3439beb93cSSam Leffler  * interface monitors into struct wpa_supplicant. This data is private to
3539beb93cSSam Leffler  * ctrl_iface_udp.c and should not be touched directly from other files.
3639beb93cSSam Leffler  */
3739beb93cSSam Leffler struct wpa_ctrl_dst {
3839beb93cSSam Leffler 	struct wpa_ctrl_dst *next;
3939beb93cSSam Leffler 	struct sockaddr_in addr;
4039beb93cSSam Leffler 	socklen_t addrlen;
4139beb93cSSam Leffler 	int debug_level;
4239beb93cSSam Leffler 	int errors;
4339beb93cSSam Leffler };
4439beb93cSSam Leffler 
4539beb93cSSam Leffler 
4639beb93cSSam Leffler struct ctrl_iface_priv {
4739beb93cSSam Leffler 	struct wpa_supplicant *wpa_s;
4839beb93cSSam Leffler 	int sock;
4939beb93cSSam Leffler 	struct wpa_ctrl_dst *ctrl_dst;
5039beb93cSSam Leffler 	u8 cookie[COOKIE_LEN];
5139beb93cSSam Leffler };
5239beb93cSSam Leffler 
5339beb93cSSam Leffler 
5439beb93cSSam Leffler static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
5539beb93cSSam Leffler 					   int level, const char *buf,
5639beb93cSSam Leffler 					   size_t len);
5739beb93cSSam Leffler 
5839beb93cSSam Leffler 
5939beb93cSSam Leffler static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv,
6039beb93cSSam Leffler 					    struct sockaddr_in *from,
6139beb93cSSam Leffler 					    socklen_t fromlen)
6239beb93cSSam Leffler {
6339beb93cSSam Leffler 	struct wpa_ctrl_dst *dst;
6439beb93cSSam Leffler 
6539beb93cSSam Leffler 	dst = os_zalloc(sizeof(*dst));
6639beb93cSSam Leffler 	if (dst == NULL)
6739beb93cSSam Leffler 		return -1;
6839beb93cSSam Leffler 	os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in));
6939beb93cSSam Leffler 	dst->addrlen = fromlen;
7039beb93cSSam Leffler 	dst->debug_level = MSG_INFO;
7139beb93cSSam Leffler 	dst->next = priv->ctrl_dst;
7239beb93cSSam Leffler 	priv->ctrl_dst = dst;
7339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
7439beb93cSSam Leffler 		   inet_ntoa(from->sin_addr), ntohs(from->sin_port));
7539beb93cSSam Leffler 	return 0;
7639beb93cSSam Leffler }
7739beb93cSSam Leffler 
7839beb93cSSam Leffler 
7939beb93cSSam Leffler static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
8039beb93cSSam Leffler 					    struct sockaddr_in *from,
8139beb93cSSam Leffler 					    socklen_t fromlen)
8239beb93cSSam Leffler {
8339beb93cSSam Leffler 	struct wpa_ctrl_dst *dst, *prev = NULL;
8439beb93cSSam Leffler 
8539beb93cSSam Leffler 	dst = priv->ctrl_dst;
8639beb93cSSam Leffler 	while (dst) {
8739beb93cSSam Leffler 		if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
8839beb93cSSam Leffler 		    from->sin_port == dst->addr.sin_port) {
8939beb93cSSam Leffler 			if (prev == NULL)
9039beb93cSSam Leffler 				priv->ctrl_dst = dst->next;
9139beb93cSSam Leffler 			else
9239beb93cSSam Leffler 				prev->next = dst->next;
9339beb93cSSam Leffler 			os_free(dst);
9439beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
9539beb93cSSam Leffler 				   "%s:%d", inet_ntoa(from->sin_addr),
9639beb93cSSam Leffler 				   ntohs(from->sin_port));
9739beb93cSSam Leffler 			return 0;
9839beb93cSSam Leffler 		}
9939beb93cSSam Leffler 		prev = dst;
10039beb93cSSam Leffler 		dst = dst->next;
10139beb93cSSam Leffler 	}
10239beb93cSSam Leffler 	return -1;
10339beb93cSSam Leffler }
10439beb93cSSam Leffler 
10539beb93cSSam Leffler 
10639beb93cSSam Leffler static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
10739beb93cSSam Leffler 					   struct sockaddr_in *from,
10839beb93cSSam Leffler 					   socklen_t fromlen,
10939beb93cSSam Leffler 					   char *level)
11039beb93cSSam Leffler {
11139beb93cSSam Leffler 	struct wpa_ctrl_dst *dst;
11239beb93cSSam Leffler 
11339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
11439beb93cSSam Leffler 
11539beb93cSSam Leffler 	dst = priv->ctrl_dst;
11639beb93cSSam Leffler 	while (dst) {
11739beb93cSSam Leffler 		if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
11839beb93cSSam Leffler 		    from->sin_port == dst->addr.sin_port) {
11939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor "
12039beb93cSSam Leffler 				   "level %s:%d", inet_ntoa(from->sin_addr),
12139beb93cSSam Leffler 				   ntohs(from->sin_port));
12239beb93cSSam Leffler 			dst->debug_level = atoi(level);
12339beb93cSSam Leffler 			return 0;
12439beb93cSSam Leffler 		}
12539beb93cSSam Leffler 		dst = dst->next;
12639beb93cSSam Leffler 	}
12739beb93cSSam Leffler 
12839beb93cSSam Leffler 	return -1;
12939beb93cSSam Leffler }
13039beb93cSSam Leffler 
13139beb93cSSam Leffler 
13239beb93cSSam Leffler static char *
13339beb93cSSam Leffler wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv,
13439beb93cSSam Leffler 				     size_t *reply_len)
13539beb93cSSam Leffler {
13639beb93cSSam Leffler 	char *reply;
13739beb93cSSam Leffler 	reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
13839beb93cSSam Leffler 	if (reply == NULL) {
13939beb93cSSam Leffler 		*reply_len = 1;
14039beb93cSSam Leffler 		return NULL;
14139beb93cSSam Leffler 	}
14239beb93cSSam Leffler 
14339beb93cSSam Leffler 	os_memcpy(reply, "COOKIE=", 7);
14439beb93cSSam Leffler 	wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
14539beb93cSSam Leffler 			 priv->cookie, COOKIE_LEN);
14639beb93cSSam Leffler 
14739beb93cSSam Leffler 	*reply_len = 7 + 2 * COOKIE_LEN;
14839beb93cSSam Leffler 	return reply;
14939beb93cSSam Leffler }
15039beb93cSSam Leffler 
15139beb93cSSam Leffler 
15239beb93cSSam Leffler static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
15339beb93cSSam Leffler 					      void *sock_ctx)
15439beb93cSSam Leffler {
15539beb93cSSam Leffler 	struct wpa_supplicant *wpa_s = eloop_ctx;
15639beb93cSSam Leffler 	struct ctrl_iface_priv *priv = sock_ctx;
15739beb93cSSam Leffler 	char buf[256], *pos;
15839beb93cSSam Leffler 	int res;
15939beb93cSSam Leffler 	struct sockaddr_in from;
16039beb93cSSam Leffler 	socklen_t fromlen = sizeof(from);
16139beb93cSSam Leffler 	char *reply = NULL;
16239beb93cSSam Leffler 	size_t reply_len = 0;
16339beb93cSSam Leffler 	int new_attached = 0;
16439beb93cSSam Leffler 	u8 cookie[COOKIE_LEN];
16539beb93cSSam Leffler 
16639beb93cSSam Leffler 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
16739beb93cSSam Leffler 		       (struct sockaddr *) &from, &fromlen);
16839beb93cSSam Leffler 	if (res < 0) {
16939beb93cSSam Leffler 		perror("recvfrom(ctrl_iface)");
17039beb93cSSam Leffler 		return;
17139beb93cSSam Leffler 	}
17239beb93cSSam Leffler 	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
17339beb93cSSam Leffler 		/*
17439beb93cSSam Leffler 		 * The OS networking stack is expected to drop this kind of
17539beb93cSSam Leffler 		 * frames since the socket is bound to only localhost address.
17639beb93cSSam Leffler 		 * Just in case, drop the frame if it is coming from any other
17739beb93cSSam Leffler 		 * address.
17839beb93cSSam Leffler 		 */
17939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
18039beb93cSSam Leffler 			   "source %s", inet_ntoa(from.sin_addr));
18139beb93cSSam Leffler 		return;
18239beb93cSSam Leffler 	}
18339beb93cSSam Leffler 	buf[res] = '\0';
18439beb93cSSam Leffler 
18539beb93cSSam Leffler 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
18639beb93cSSam Leffler 		reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len);
18739beb93cSSam Leffler 		goto done;
18839beb93cSSam Leffler 	}
18939beb93cSSam Leffler 
19039beb93cSSam Leffler 	/*
19139beb93cSSam Leffler 	 * Require that the client includes a prefix with the 'cookie' value
19239beb93cSSam Leffler 	 * fetched with GET_COOKIE command. This is used to verify that the
19339beb93cSSam Leffler 	 * client has access to a bidirectional link over UDP in order to
19439beb93cSSam Leffler 	 * avoid attacks using forged localhost IP address even if the OS does
19539beb93cSSam Leffler 	 * not block such frames from remote destinations.
19639beb93cSSam Leffler 	 */
19739beb93cSSam Leffler 	if (os_strncmp(buf, "COOKIE=", 7) != 0) {
19839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
19939beb93cSSam Leffler 			   "drop request");
20039beb93cSSam Leffler 		return;
20139beb93cSSam Leffler 	}
20239beb93cSSam Leffler 
20339beb93cSSam Leffler 	if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
20439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
20539beb93cSSam Leffler 			   "request - drop request");
20639beb93cSSam Leffler 		return;
20739beb93cSSam Leffler 	}
20839beb93cSSam Leffler 
20939beb93cSSam Leffler 	if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
21039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
21139beb93cSSam Leffler 			   "drop request");
21239beb93cSSam Leffler 		return;
21339beb93cSSam Leffler 	}
21439beb93cSSam Leffler 
21539beb93cSSam Leffler 	pos = buf + 7 + 2 * COOKIE_LEN;
21639beb93cSSam Leffler 	while (*pos == ' ')
21739beb93cSSam Leffler 		pos++;
21839beb93cSSam Leffler 
21939beb93cSSam Leffler 	if (os_strcmp(pos, "ATTACH") == 0) {
22039beb93cSSam Leffler 		if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen))
22139beb93cSSam Leffler 			reply_len = 1;
22239beb93cSSam Leffler 		else {
22339beb93cSSam Leffler 			new_attached = 1;
22439beb93cSSam Leffler 			reply_len = 2;
22539beb93cSSam Leffler 		}
22639beb93cSSam Leffler 	} else if (os_strcmp(pos, "DETACH") == 0) {
22739beb93cSSam Leffler 		if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen))
22839beb93cSSam Leffler 			reply_len = 1;
22939beb93cSSam Leffler 		else
23039beb93cSSam Leffler 			reply_len = 2;
23139beb93cSSam Leffler 	} else if (os_strncmp(pos, "LEVEL ", 6) == 0) {
23239beb93cSSam Leffler 		if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
23339beb93cSSam Leffler 						    pos + 6))
23439beb93cSSam Leffler 			reply_len = 1;
23539beb93cSSam Leffler 		else
23639beb93cSSam Leffler 			reply_len = 2;
23739beb93cSSam Leffler 	} else {
23839beb93cSSam Leffler 		reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
23939beb93cSSam Leffler 							  &reply_len);
24039beb93cSSam Leffler 	}
24139beb93cSSam Leffler 
24239beb93cSSam Leffler  done:
24339beb93cSSam Leffler 	if (reply) {
24439beb93cSSam Leffler 		sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
24539beb93cSSam Leffler 		       fromlen);
24639beb93cSSam Leffler 		os_free(reply);
24739beb93cSSam Leffler 	} else if (reply_len == 1) {
24839beb93cSSam Leffler 		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
24939beb93cSSam Leffler 		       fromlen);
25039beb93cSSam Leffler 	} else if (reply_len == 2) {
25139beb93cSSam Leffler 		sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
25239beb93cSSam Leffler 		       fromlen);
25339beb93cSSam Leffler 	}
25439beb93cSSam Leffler 
25539beb93cSSam Leffler 	if (new_attached)
25639beb93cSSam Leffler 		eapol_sm_notify_ctrl_attached(wpa_s->eapol);
25739beb93cSSam Leffler }
25839beb93cSSam Leffler 
25939beb93cSSam Leffler 
26039beb93cSSam Leffler static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
26139beb93cSSam Leffler 					     const char *txt, size_t len)
26239beb93cSSam Leffler {
26339beb93cSSam Leffler 	struct wpa_supplicant *wpa_s = ctx;
26439beb93cSSam Leffler 	if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
26539beb93cSSam Leffler 		return;
26639beb93cSSam Leffler 	wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
26739beb93cSSam Leffler }
26839beb93cSSam Leffler 
26939beb93cSSam Leffler 
27039beb93cSSam Leffler struct ctrl_iface_priv *
27139beb93cSSam Leffler wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
27239beb93cSSam Leffler {
27339beb93cSSam Leffler 	struct ctrl_iface_priv *priv;
27439beb93cSSam Leffler 	struct sockaddr_in addr;
27539beb93cSSam Leffler 
27639beb93cSSam Leffler 	priv = os_zalloc(sizeof(*priv));
27739beb93cSSam Leffler 	if (priv == NULL)
27839beb93cSSam Leffler 		return NULL;
27939beb93cSSam Leffler 	priv->wpa_s = wpa_s;
28039beb93cSSam Leffler 	priv->sock = -1;
28139beb93cSSam Leffler 	os_get_random(priv->cookie, COOKIE_LEN);
28239beb93cSSam Leffler 
28339beb93cSSam Leffler 	if (wpa_s->conf->ctrl_interface == NULL)
28439beb93cSSam Leffler 		return priv;
28539beb93cSSam Leffler 
28639beb93cSSam Leffler 	priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
28739beb93cSSam Leffler 	if (priv->sock < 0) {
28839beb93cSSam Leffler 		perror("socket(PF_INET)");
28939beb93cSSam Leffler 		goto fail;
29039beb93cSSam Leffler 	}
29139beb93cSSam Leffler 
29239beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
29339beb93cSSam Leffler 	addr.sin_family = AF_INET;
29439beb93cSSam Leffler 	addr.sin_addr.s_addr = htonl((127 << 24) | 1);
29539beb93cSSam Leffler 	addr.sin_port = htons(WPA_CTRL_IFACE_PORT);
29639beb93cSSam Leffler 	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
29739beb93cSSam Leffler 		perror("bind(AF_INET)");
29839beb93cSSam Leffler 		goto fail;
29939beb93cSSam Leffler 	}
30039beb93cSSam Leffler 
30139beb93cSSam Leffler 	eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
30239beb93cSSam Leffler 				 wpa_s, priv);
30339beb93cSSam Leffler 	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
30439beb93cSSam Leffler 
30539beb93cSSam Leffler 	return priv;
30639beb93cSSam Leffler 
30739beb93cSSam Leffler fail:
30839beb93cSSam Leffler 	if (priv->sock >= 0)
30939beb93cSSam Leffler 		close(priv->sock);
31039beb93cSSam Leffler 	os_free(priv);
31139beb93cSSam Leffler 	return NULL;
31239beb93cSSam Leffler }
31339beb93cSSam Leffler 
31439beb93cSSam Leffler 
31539beb93cSSam Leffler void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
31639beb93cSSam Leffler {
31739beb93cSSam Leffler 	struct wpa_ctrl_dst *dst, *prev;
31839beb93cSSam Leffler 
31939beb93cSSam Leffler 	if (priv->sock > -1) {
32039beb93cSSam Leffler 		eloop_unregister_read_sock(priv->sock);
32139beb93cSSam Leffler 		if (priv->ctrl_dst) {
32239beb93cSSam Leffler 			/*
32339beb93cSSam Leffler 			 * Wait a second before closing the control socket if
32439beb93cSSam Leffler 			 * there are any attached monitors in order to allow
32539beb93cSSam Leffler 			 * them to receive any pending messages.
32639beb93cSSam Leffler 			 */
32739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
32839beb93cSSam Leffler 				   "monitors to receive messages");
32939beb93cSSam Leffler 			os_sleep(1, 0);
33039beb93cSSam Leffler 		}
33139beb93cSSam Leffler 		close(priv->sock);
33239beb93cSSam Leffler 		priv->sock = -1;
33339beb93cSSam Leffler 	}
33439beb93cSSam Leffler 
33539beb93cSSam Leffler 	dst = priv->ctrl_dst;
33639beb93cSSam Leffler 	while (dst) {
33739beb93cSSam Leffler 		prev = dst;
33839beb93cSSam Leffler 		dst = dst->next;
33939beb93cSSam Leffler 		os_free(prev);
34039beb93cSSam Leffler 	}
34139beb93cSSam Leffler 	os_free(priv);
34239beb93cSSam Leffler }
34339beb93cSSam Leffler 
34439beb93cSSam Leffler 
34539beb93cSSam Leffler static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
34639beb93cSSam Leffler 					   int level, const char *buf,
34739beb93cSSam Leffler 					   size_t len)
34839beb93cSSam Leffler {
34939beb93cSSam Leffler 	struct wpa_ctrl_dst *dst, *next;
35039beb93cSSam Leffler 	char levelstr[10];
35139beb93cSSam Leffler 	int idx;
35239beb93cSSam Leffler 	char *sbuf;
35339beb93cSSam Leffler 	int llen;
35439beb93cSSam Leffler 
35539beb93cSSam Leffler 	dst = priv->ctrl_dst;
35639beb93cSSam Leffler 	if (priv->sock < 0 || dst == NULL)
35739beb93cSSam Leffler 		return;
35839beb93cSSam Leffler 
35939beb93cSSam Leffler 	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
36039beb93cSSam Leffler 
36139beb93cSSam Leffler 	llen = os_strlen(levelstr);
36239beb93cSSam Leffler 	sbuf = os_malloc(llen + len);
36339beb93cSSam Leffler 	if (sbuf == NULL)
36439beb93cSSam Leffler 		return;
36539beb93cSSam Leffler 
36639beb93cSSam Leffler 	os_memcpy(sbuf, levelstr, llen);
36739beb93cSSam Leffler 	os_memcpy(sbuf + llen, buf, len);
36839beb93cSSam Leffler 
36939beb93cSSam Leffler 	idx = 0;
37039beb93cSSam Leffler 	while (dst) {
37139beb93cSSam Leffler 		next = dst->next;
37239beb93cSSam Leffler 		if (level >= dst->debug_level) {
37339beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
37439beb93cSSam Leffler 				   inet_ntoa(dst->addr.sin_addr),
37539beb93cSSam Leffler 				   ntohs(dst->addr.sin_port));
37639beb93cSSam Leffler 			if (sendto(priv->sock, sbuf, llen + len, 0,
37739beb93cSSam Leffler 				   (struct sockaddr *) &dst->addr,
37839beb93cSSam Leffler 				   sizeof(dst->addr)) < 0) {
37939beb93cSSam Leffler 				perror("sendto(CTRL_IFACE monitor)");
38039beb93cSSam Leffler 				dst->errors++;
38139beb93cSSam Leffler 				if (dst->errors > 10) {
38239beb93cSSam Leffler 					wpa_supplicant_ctrl_iface_detach(
38339beb93cSSam Leffler 						priv, &dst->addr,
38439beb93cSSam Leffler 						dst->addrlen);
38539beb93cSSam Leffler 				}
38639beb93cSSam Leffler 			} else
38739beb93cSSam Leffler 				dst->errors = 0;
38839beb93cSSam Leffler 		}
38939beb93cSSam Leffler 		idx++;
39039beb93cSSam Leffler 		dst = next;
39139beb93cSSam Leffler 	}
39239beb93cSSam Leffler 	os_free(sbuf);
39339beb93cSSam Leffler }
39439beb93cSSam Leffler 
39539beb93cSSam Leffler 
39639beb93cSSam Leffler void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
39739beb93cSSam Leffler {
39839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
39939beb93cSSam Leffler 		   priv->wpa_s->ifname);
40039beb93cSSam Leffler 	eloop_wait_for_read_sock(priv->sock);
40139beb93cSSam Leffler }
40239beb93cSSam Leffler 
40339beb93cSSam Leffler 
40439beb93cSSam Leffler /* Global ctrl_iface */
40539beb93cSSam Leffler 
40639beb93cSSam Leffler struct ctrl_iface_global_priv {
40739beb93cSSam Leffler 	int sock;
40839beb93cSSam Leffler 	u8 cookie[COOKIE_LEN];
40939beb93cSSam Leffler };
41039beb93cSSam Leffler 
41139beb93cSSam Leffler 
41239beb93cSSam Leffler static char *
41339beb93cSSam Leffler wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv,
41439beb93cSSam Leffler 				 size_t *reply_len)
41539beb93cSSam Leffler {
41639beb93cSSam Leffler 	char *reply;
41739beb93cSSam Leffler 	reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
41839beb93cSSam Leffler 	if (reply == NULL) {
41939beb93cSSam Leffler 		*reply_len = 1;
42039beb93cSSam Leffler 		return NULL;
42139beb93cSSam Leffler 	}
42239beb93cSSam Leffler 
42339beb93cSSam Leffler 	os_memcpy(reply, "COOKIE=", 7);
42439beb93cSSam Leffler 	wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
42539beb93cSSam Leffler 			 priv->cookie, COOKIE_LEN);
42639beb93cSSam Leffler 
42739beb93cSSam Leffler 	*reply_len = 7 + 2 * COOKIE_LEN;
42839beb93cSSam Leffler 	return reply;
42939beb93cSSam Leffler }
43039beb93cSSam Leffler 
43139beb93cSSam Leffler 
43239beb93cSSam Leffler static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
43339beb93cSSam Leffler 						     void *sock_ctx)
43439beb93cSSam Leffler {
43539beb93cSSam Leffler 	struct wpa_global *global = eloop_ctx;
43639beb93cSSam Leffler 	struct ctrl_iface_global_priv *priv = sock_ctx;
43739beb93cSSam Leffler 	char buf[256], *pos;
43839beb93cSSam Leffler 	int res;
43939beb93cSSam Leffler 	struct sockaddr_in from;
44039beb93cSSam Leffler 	socklen_t fromlen = sizeof(from);
44139beb93cSSam Leffler 	char *reply;
44239beb93cSSam Leffler 	size_t reply_len;
44339beb93cSSam Leffler 	u8 cookie[COOKIE_LEN];
44439beb93cSSam Leffler 
44539beb93cSSam Leffler 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
44639beb93cSSam Leffler 		       (struct sockaddr *) &from, &fromlen);
44739beb93cSSam Leffler 	if (res < 0) {
44839beb93cSSam Leffler 		perror("recvfrom(ctrl_iface)");
44939beb93cSSam Leffler 		return;
45039beb93cSSam Leffler 	}
45139beb93cSSam Leffler 	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
45239beb93cSSam Leffler 		/*
45339beb93cSSam Leffler 		 * The OS networking stack is expected to drop this kind of
45439beb93cSSam Leffler 		 * frames since the socket is bound to only localhost address.
45539beb93cSSam Leffler 		 * Just in case, drop the frame if it is coming from any other
45639beb93cSSam Leffler 		 * address.
45739beb93cSSam Leffler 		 */
45839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
45939beb93cSSam Leffler 			   "source %s", inet_ntoa(from.sin_addr));
46039beb93cSSam Leffler 		return;
46139beb93cSSam Leffler 	}
46239beb93cSSam Leffler 	buf[res] = '\0';
46339beb93cSSam Leffler 
46439beb93cSSam Leffler 	if (os_strcmp(buf, "GET_COOKIE") == 0) {
46539beb93cSSam Leffler 		reply = wpa_supplicant_global_get_cookie(priv, &reply_len);
46639beb93cSSam Leffler 		goto done;
46739beb93cSSam Leffler 	}
46839beb93cSSam Leffler 
46939beb93cSSam Leffler 	if (os_strncmp(buf, "COOKIE=", 7) != 0) {
47039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
47139beb93cSSam Leffler 			   "drop request");
47239beb93cSSam Leffler 		return;
47339beb93cSSam Leffler 	}
47439beb93cSSam Leffler 
47539beb93cSSam Leffler 	if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
47639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
47739beb93cSSam Leffler 			   "request - drop request");
47839beb93cSSam Leffler 		return;
47939beb93cSSam Leffler 	}
48039beb93cSSam Leffler 
48139beb93cSSam Leffler 	if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
48239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
48339beb93cSSam Leffler 			   "drop request");
48439beb93cSSam Leffler 		return;
48539beb93cSSam Leffler 	}
48639beb93cSSam Leffler 
48739beb93cSSam Leffler 	pos = buf + 7 + 2 * COOKIE_LEN;
48839beb93cSSam Leffler 	while (*pos == ' ')
48939beb93cSSam Leffler 		pos++;
49039beb93cSSam Leffler 
49139beb93cSSam Leffler 	reply = wpa_supplicant_global_ctrl_iface_process(global, pos,
49239beb93cSSam Leffler 							 &reply_len);
49339beb93cSSam Leffler 
49439beb93cSSam Leffler  done:
49539beb93cSSam Leffler 	if (reply) {
49639beb93cSSam Leffler 		sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
49739beb93cSSam Leffler 		       fromlen);
49839beb93cSSam Leffler 		os_free(reply);
49939beb93cSSam Leffler 	} else if (reply_len) {
50039beb93cSSam Leffler 		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
50139beb93cSSam Leffler 		       fromlen);
50239beb93cSSam Leffler 	}
50339beb93cSSam Leffler }
50439beb93cSSam Leffler 
50539beb93cSSam Leffler 
50639beb93cSSam Leffler struct ctrl_iface_global_priv *
50739beb93cSSam Leffler wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
50839beb93cSSam Leffler {
50939beb93cSSam Leffler 	struct ctrl_iface_global_priv *priv;
51039beb93cSSam Leffler 	struct sockaddr_in addr;
51139beb93cSSam Leffler 
51239beb93cSSam Leffler 	priv = os_zalloc(sizeof(*priv));
51339beb93cSSam Leffler 	if (priv == NULL)
51439beb93cSSam Leffler 		return NULL;
51539beb93cSSam Leffler 	priv->sock = -1;
51639beb93cSSam Leffler 	os_get_random(priv->cookie, COOKIE_LEN);
51739beb93cSSam Leffler 
51839beb93cSSam Leffler 	if (global->params.ctrl_interface == NULL)
51939beb93cSSam Leffler 		return priv;
52039beb93cSSam Leffler 
52139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Global control interface '%s'",
52239beb93cSSam Leffler 		   global->params.ctrl_interface);
52339beb93cSSam Leffler 
52439beb93cSSam Leffler 	priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
52539beb93cSSam Leffler 	if (priv->sock < 0) {
52639beb93cSSam Leffler 		perror("socket(PF_INET)");
52739beb93cSSam Leffler 		goto fail;
52839beb93cSSam Leffler 	}
52939beb93cSSam Leffler 
53039beb93cSSam Leffler 	os_memset(&addr, 0, sizeof(addr));
53139beb93cSSam Leffler 	addr.sin_family = AF_INET;
53239beb93cSSam Leffler 	addr.sin_addr.s_addr = htonl((127 << 24) | 1);
53339beb93cSSam Leffler 	addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT);
53439beb93cSSam Leffler 	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
53539beb93cSSam Leffler 		perror("bind(AF_INET)");
53639beb93cSSam Leffler 		goto fail;
53739beb93cSSam Leffler 	}
53839beb93cSSam Leffler 
53939beb93cSSam Leffler 	eloop_register_read_sock(priv->sock,
54039beb93cSSam Leffler 				 wpa_supplicant_global_ctrl_iface_receive,
54139beb93cSSam Leffler 				 global, priv);
54239beb93cSSam Leffler 
54339beb93cSSam Leffler 	return priv;
54439beb93cSSam Leffler 
54539beb93cSSam Leffler fail:
54639beb93cSSam Leffler 	if (priv->sock >= 0)
54739beb93cSSam Leffler 		close(priv->sock);
54839beb93cSSam Leffler 	os_free(priv);
54939beb93cSSam Leffler 	return NULL;
55039beb93cSSam Leffler }
55139beb93cSSam Leffler 
55239beb93cSSam Leffler 
55339beb93cSSam Leffler void
55439beb93cSSam Leffler wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
55539beb93cSSam Leffler {
55639beb93cSSam Leffler 	if (priv->sock >= 0) {
55739beb93cSSam Leffler 		eloop_unregister_read_sock(priv->sock);
55839beb93cSSam Leffler 		close(priv->sock);
55939beb93cSSam Leffler 	}
56039beb93cSSam Leffler 	os_free(priv);
56139beb93cSSam Leffler }
562