1/*
2 * WPA Supplicant / UDP socket -based control interface
3 * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "eloop.h"
13#include "config.h"
14#include "eapol_supp/eapol_supp_sm.h"
15#include "wpa_supplicant_i.h"
16#include "ctrl_iface.h"
17#include "common/wpa_ctrl.h"
18
19
20#define COOKIE_LEN 8
21
22/* Per-interface ctrl_iface */
23
24/**
25 * struct wpa_ctrl_dst - Internal data structure of control interface monitors
26 *
27 * This structure is used to store information about registered control
28 * interface monitors into struct wpa_supplicant. This data is private to
29 * ctrl_iface_udp.c and should not be touched directly from other files.
30 */
31struct wpa_ctrl_dst {
32	struct wpa_ctrl_dst *next;
33	struct sockaddr_in addr;
34	socklen_t addrlen;
35	int debug_level;
36	int errors;
37};
38
39
40struct ctrl_iface_priv {
41	struct wpa_supplicant *wpa_s;
42	int sock;
43	struct wpa_ctrl_dst *ctrl_dst;
44	u8 cookie[COOKIE_LEN];
45};
46
47
48static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
49					   int level, const char *buf,
50					   size_t len);
51
52
53static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv,
54					    struct sockaddr_in *from,
55					    socklen_t fromlen)
56{
57	struct wpa_ctrl_dst *dst;
58
59	dst = os_zalloc(sizeof(*dst));
60	if (dst == NULL)
61		return -1;
62	os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in));
63	dst->addrlen = fromlen;
64	dst->debug_level = MSG_INFO;
65	dst->next = priv->ctrl_dst;
66	priv->ctrl_dst = dst;
67	wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
68		   inet_ntoa(from->sin_addr), ntohs(from->sin_port));
69	return 0;
70}
71
72
73static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
74					    struct sockaddr_in *from,
75					    socklen_t fromlen)
76{
77	struct wpa_ctrl_dst *dst, *prev = NULL;
78
79	dst = priv->ctrl_dst;
80	while (dst) {
81		if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
82		    from->sin_port == dst->addr.sin_port) {
83			if (prev == NULL)
84				priv->ctrl_dst = dst->next;
85			else
86				prev->next = dst->next;
87			os_free(dst);
88			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
89				   "%s:%d", inet_ntoa(from->sin_addr),
90				   ntohs(from->sin_port));
91			return 0;
92		}
93		prev = dst;
94		dst = dst->next;
95	}
96	return -1;
97}
98
99
100static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
101					   struct sockaddr_in *from,
102					   socklen_t fromlen,
103					   char *level)
104{
105	struct wpa_ctrl_dst *dst;
106
107	wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
108
109	dst = priv->ctrl_dst;
110	while (dst) {
111		if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
112		    from->sin_port == dst->addr.sin_port) {
113			wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor "
114				   "level %s:%d", inet_ntoa(from->sin_addr),
115				   ntohs(from->sin_port));
116			dst->debug_level = atoi(level);
117			return 0;
118		}
119		dst = dst->next;
120	}
121
122	return -1;
123}
124
125
126static char *
127wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv,
128				     size_t *reply_len)
129{
130	char *reply;
131	reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
132	if (reply == NULL) {
133		*reply_len = 1;
134		return NULL;
135	}
136
137	os_memcpy(reply, "COOKIE=", 7);
138	wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
139			 priv->cookie, COOKIE_LEN);
140
141	*reply_len = 7 + 2 * COOKIE_LEN;
142	return reply;
143}
144
145
146static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
147					      void *sock_ctx)
148{
149	struct wpa_supplicant *wpa_s = eloop_ctx;
150	struct ctrl_iface_priv *priv = sock_ctx;
151	char buf[256], *pos;
152	int res;
153	struct sockaddr_in from;
154	socklen_t fromlen = sizeof(from);
155	char *reply = NULL;
156	size_t reply_len = 0;
157	int new_attached = 0;
158	u8 cookie[COOKIE_LEN];
159
160	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
161		       (struct sockaddr *) &from, &fromlen);
162	if (res < 0) {
163		perror("recvfrom(ctrl_iface)");
164		return;
165	}
166
167#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
168	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
169		/*
170		 * The OS networking stack is expected to drop this kind of
171		 * frames since the socket is bound to only localhost address.
172		 * Just in case, drop the frame if it is coming from any other
173		 * address.
174		 */
175		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
176			   "source %s", inet_ntoa(from.sin_addr));
177		return;
178	}
179#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
180
181	buf[res] = '\0';
182
183	if (os_strcmp(buf, "GET_COOKIE") == 0) {
184		reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len);
185		goto done;
186	}
187
188	/*
189	 * Require that the client includes a prefix with the 'cookie' value
190	 * fetched with GET_COOKIE command. This is used to verify that the
191	 * client has access to a bidirectional link over UDP in order to
192	 * avoid attacks using forged localhost IP address even if the OS does
193	 * not block such frames from remote destinations.
194	 */
195	if (os_strncmp(buf, "COOKIE=", 7) != 0) {
196		wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
197			   "drop request");
198		return;
199	}
200
201	if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
202		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
203			   "request - drop request");
204		return;
205	}
206
207	if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
208		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
209			   "drop request");
210		return;
211	}
212
213	pos = buf + 7 + 2 * COOKIE_LEN;
214	while (*pos == ' ')
215		pos++;
216
217	if (os_strcmp(pos, "ATTACH") == 0) {
218		if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen))
219			reply_len = 1;
220		else {
221			new_attached = 1;
222			reply_len = 2;
223		}
224	} else if (os_strcmp(pos, "DETACH") == 0) {
225		if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen))
226			reply_len = 1;
227		else
228			reply_len = 2;
229	} else if (os_strncmp(pos, "LEVEL ", 6) == 0) {
230		if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
231						    pos + 6))
232			reply_len = 1;
233		else
234			reply_len = 2;
235	} else {
236		reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
237							  &reply_len);
238	}
239
240 done:
241	if (reply) {
242		sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
243		       fromlen);
244		os_free(reply);
245	} else if (reply_len == 1) {
246		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
247		       fromlen);
248	} else if (reply_len == 2) {
249		sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
250		       fromlen);
251	}
252
253	if (new_attached)
254		eapol_sm_notify_ctrl_attached(wpa_s->eapol);
255}
256
257
258static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
259					     const char *txt, size_t len)
260{
261	struct wpa_supplicant *wpa_s = ctx;
262	if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
263		return;
264	wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
265}
266
267
268struct ctrl_iface_priv *
269wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
270{
271	struct ctrl_iface_priv *priv;
272	struct sockaddr_in addr;
273	int port = WPA_CTRL_IFACE_PORT;
274
275	priv = os_zalloc(sizeof(*priv));
276	if (priv == NULL)
277		return NULL;
278	priv->wpa_s = wpa_s;
279	priv->sock = -1;
280	os_get_random(priv->cookie, COOKIE_LEN);
281
282	if (wpa_s->conf->ctrl_interface == NULL)
283		return priv;
284
285	priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
286	if (priv->sock < 0) {
287		perror("socket(PF_INET)");
288		goto fail;
289	}
290
291	os_memset(&addr, 0, sizeof(addr));
292	addr.sin_family = AF_INET;
293#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
294	addr.sin_addr.s_addr = INADDR_ANY;
295#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
296	addr.sin_addr.s_addr = htonl((127 << 24) | 1);
297#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
298try_again:
299	addr.sin_port = htons(port);
300	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
301		port--;
302		if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
303			goto try_again;
304		perror("bind(AF_INET)");
305		goto fail;
306	}
307
308#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
309	wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
310#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
311
312	eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
313				 wpa_s, priv);
314	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
315
316	return priv;
317
318fail:
319	if (priv->sock >= 0)
320		close(priv->sock);
321	os_free(priv);
322	return NULL;
323}
324
325
326void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
327{
328	struct wpa_ctrl_dst *dst, *prev;
329
330	if (priv->sock > -1) {
331		eloop_unregister_read_sock(priv->sock);
332		if (priv->ctrl_dst) {
333			/*
334			 * Wait a second before closing the control socket if
335			 * there are any attached monitors in order to allow
336			 * them to receive any pending messages.
337			 */
338			wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
339				   "monitors to receive messages");
340			os_sleep(1, 0);
341		}
342		close(priv->sock);
343		priv->sock = -1;
344	}
345
346	dst = priv->ctrl_dst;
347	while (dst) {
348		prev = dst;
349		dst = dst->next;
350		os_free(prev);
351	}
352	os_free(priv);
353}
354
355
356static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
357					   int level, const char *buf,
358					   size_t len)
359{
360	struct wpa_ctrl_dst *dst, *next;
361	char levelstr[10];
362	int idx;
363	char *sbuf;
364	int llen;
365
366	dst = priv->ctrl_dst;
367	if (priv->sock < 0 || dst == NULL)
368		return;
369
370	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
371
372	llen = os_strlen(levelstr);
373	sbuf = os_malloc(llen + len);
374	if (sbuf == NULL)
375		return;
376
377	os_memcpy(sbuf, levelstr, llen);
378	os_memcpy(sbuf + llen, buf, len);
379
380	idx = 0;
381	while (dst) {
382		next = dst->next;
383		if (level >= dst->debug_level) {
384			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
385				   inet_ntoa(dst->addr.sin_addr),
386				   ntohs(dst->addr.sin_port));
387			if (sendto(priv->sock, sbuf, llen + len, 0,
388				   (struct sockaddr *) &dst->addr,
389				   sizeof(dst->addr)) < 0) {
390				perror("sendto(CTRL_IFACE monitor)");
391				dst->errors++;
392				if (dst->errors > 10) {
393					wpa_supplicant_ctrl_iface_detach(
394						priv, &dst->addr,
395						dst->addrlen);
396				}
397			} else
398				dst->errors = 0;
399		}
400		idx++;
401		dst = next;
402	}
403	os_free(sbuf);
404}
405
406
407void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
408{
409	wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
410		   priv->wpa_s->ifname);
411	eloop_wait_for_read_sock(priv->sock);
412}
413
414
415/* Global ctrl_iface */
416
417struct ctrl_iface_global_priv {
418	int sock;
419	u8 cookie[COOKIE_LEN];
420};
421
422
423static char *
424wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv,
425				 size_t *reply_len)
426{
427	char *reply;
428	reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
429	if (reply == NULL) {
430		*reply_len = 1;
431		return NULL;
432	}
433
434	os_memcpy(reply, "COOKIE=", 7);
435	wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
436			 priv->cookie, COOKIE_LEN);
437
438	*reply_len = 7 + 2 * COOKIE_LEN;
439	return reply;
440}
441
442
443static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
444						     void *sock_ctx)
445{
446	struct wpa_global *global = eloop_ctx;
447	struct ctrl_iface_global_priv *priv = sock_ctx;
448	char buf[256], *pos;
449	int res;
450	struct sockaddr_in from;
451	socklen_t fromlen = sizeof(from);
452	char *reply;
453	size_t reply_len;
454	u8 cookie[COOKIE_LEN];
455
456	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
457		       (struct sockaddr *) &from, &fromlen);
458	if (res < 0) {
459		perror("recvfrom(ctrl_iface)");
460		return;
461	}
462
463#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
464	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
465		/*
466		 * The OS networking stack is expected to drop this kind of
467		 * frames since the socket is bound to only localhost address.
468		 * Just in case, drop the frame if it is coming from any other
469		 * address.
470		 */
471		wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
472			   "source %s", inet_ntoa(from.sin_addr));
473		return;
474	}
475#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
476
477	buf[res] = '\0';
478
479	if (os_strcmp(buf, "GET_COOKIE") == 0) {
480		reply = wpa_supplicant_global_get_cookie(priv, &reply_len);
481		goto done;
482	}
483
484	if (os_strncmp(buf, "COOKIE=", 7) != 0) {
485		wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
486			   "drop request");
487		return;
488	}
489
490	if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
491		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
492			   "request - drop request");
493		return;
494	}
495
496	if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
497		wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
498			   "drop request");
499		return;
500	}
501
502	pos = buf + 7 + 2 * COOKIE_LEN;
503	while (*pos == ' ')
504		pos++;
505
506	reply = wpa_supplicant_global_ctrl_iface_process(global, pos,
507							 &reply_len);
508
509 done:
510	if (reply) {
511		sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
512		       fromlen);
513		os_free(reply);
514	} else if (reply_len) {
515		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
516		       fromlen);
517	}
518}
519
520
521struct ctrl_iface_global_priv *
522wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
523{
524	struct ctrl_iface_global_priv *priv;
525	struct sockaddr_in addr;
526	int port = WPA_GLOBAL_CTRL_IFACE_PORT;
527
528	priv = os_zalloc(sizeof(*priv));
529	if (priv == NULL)
530		return NULL;
531	priv->sock = -1;
532	os_get_random(priv->cookie, COOKIE_LEN);
533
534	if (global->params.ctrl_interface == NULL)
535		return priv;
536
537	wpa_printf(MSG_DEBUG, "Global control interface '%s'",
538		   global->params.ctrl_interface);
539
540	priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
541	if (priv->sock < 0) {
542		perror("socket(PF_INET)");
543		goto fail;
544	}
545
546	os_memset(&addr, 0, sizeof(addr));
547	addr.sin_family = AF_INET;
548#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
549	addr.sin_addr.s_addr = INADDR_ANY;
550#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
551	addr.sin_addr.s_addr = htonl((127 << 24) | 1);
552#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
553try_again:
554	addr.sin_port = htons(port);
555	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
556		port++;
557		if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) <
558		    WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT)
559			goto try_again;
560		perror("bind(AF_INET)");
561		goto fail;
562	}
563
564#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
565	wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port);
566#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
567
568	eloop_register_read_sock(priv->sock,
569				 wpa_supplicant_global_ctrl_iface_receive,
570				 global, priv);
571
572	return priv;
573
574fail:
575	if (priv->sock >= 0)
576		close(priv->sock);
577	os_free(priv);
578	return NULL;
579}
580
581
582void
583wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
584{
585	if (priv->sock >= 0) {
586		eloop_unregister_read_sock(priv->sock);
587		close(priv->sock);
588	}
589	os_free(priv);
590}
591