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 */
31 struct 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
40 struct 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
48 static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
49 int level, const char *buf,
50 size_t len);
51
52
wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv * priv,struct sockaddr_in * from,socklen_t fromlen)53 static 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
wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv * priv,struct sockaddr_in * from,socklen_t fromlen)73 static 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
wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv * priv,struct sockaddr_in * from,socklen_t fromlen,char * level)100 static 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
126 static char *
wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv * priv,size_t * reply_len)127 wpa_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
wpa_supplicant_ctrl_iface_receive(int sock,void * eloop_ctx,void * sock_ctx)146 static 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
wpa_supplicant_ctrl_iface_msg_cb(void * ctx,int level,const char * txt,size_t len)258 static 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
268 struct ctrl_iface_priv *
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant * wpa_s)269 wpa_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 */
298 try_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
318 fail:
319 if (priv->sock >= 0)
320 close(priv->sock);
321 os_free(priv);
322 return NULL;
323 }
324
325
wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv * priv)326 void 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
wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv * priv,int level,const char * buf,size_t len)356 static 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
wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv * priv)407 void 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
417 struct ctrl_iface_global_priv {
418 int sock;
419 u8 cookie[COOKIE_LEN];
420 };
421
422
423 static char *
wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv * priv,size_t * reply_len)424 wpa_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
wpa_supplicant_global_ctrl_iface_receive(int sock,void * eloop_ctx,void * sock_ctx)443 static 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
521 struct ctrl_iface_global_priv *
wpa_supplicant_global_ctrl_iface_init(struct wpa_global * global)522 wpa_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 */
553 try_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
574 fail:
575 if (priv->sock >= 0)
576 close(priv->sock);
577 os_free(priv);
578 return NULL;
579 }
580
581
582 void
wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv * priv)583 wpa_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