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