xref: /freebsd/contrib/wpa/hostapd/ctrl_iface.c (revision 8fc257994d0ce2396196d7a06d50d20c8015f4b7)
1 /*
2  * hostapd / UNIX domain socket -based control interface
3  * Copyright (c) 2004-2008, 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 #ifndef CONFIG_NATIVE_WINDOWS
18 
19 #include <sys/un.h>
20 #include <sys/stat.h>
21 #include <stddef.h>
22 
23 #include "hostapd.h"
24 #include "eloop.h"
25 #include "config.h"
26 #include "ieee802_1x.h"
27 #include "wpa.h"
28 #include "radius/radius_client.h"
29 #include "ieee802_11.h"
30 #include "ctrl_iface.h"
31 #include "sta_info.h"
32 #include "accounting.h"
33 #include "wps_hostapd.h"
34 
35 
36 struct wpa_ctrl_dst {
37 	struct wpa_ctrl_dst *next;
38 	struct sockaddr_un addr;
39 	socklen_t addrlen;
40 	int debug_level;
41 	int errors;
42 };
43 
44 
45 static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
46 				    const char *buf, size_t len);
47 
48 
49 static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
50 				     struct sockaddr_un *from,
51 				     socklen_t fromlen)
52 {
53 	struct wpa_ctrl_dst *dst;
54 
55 	dst = os_zalloc(sizeof(*dst));
56 	if (dst == NULL)
57 		return -1;
58 	os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
59 	dst->addrlen = fromlen;
60 	dst->debug_level = MSG_INFO;
61 	dst->next = hapd->ctrl_dst;
62 	hapd->ctrl_dst = dst;
63 	wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
64 		    (u8 *) from->sun_path,
65 		    fromlen - offsetof(struct sockaddr_un, sun_path));
66 	return 0;
67 }
68 
69 
70 static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
71 				     struct sockaddr_un *from,
72 				     socklen_t fromlen)
73 {
74 	struct wpa_ctrl_dst *dst, *prev = NULL;
75 
76 	dst = hapd->ctrl_dst;
77 	while (dst) {
78 		if (fromlen == dst->addrlen &&
79 		    os_memcmp(from->sun_path, dst->addr.sun_path,
80 			      fromlen - offsetof(struct sockaddr_un, sun_path))
81 		    == 0) {
82 			if (prev == NULL)
83 				hapd->ctrl_dst = dst->next;
84 			else
85 				prev->next = dst->next;
86 			os_free(dst);
87 			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
88 				    (u8 *) from->sun_path,
89 				    fromlen -
90 				    offsetof(struct sockaddr_un, sun_path));
91 			return 0;
92 		}
93 		prev = dst;
94 		dst = dst->next;
95 	}
96 	return -1;
97 }
98 
99 
100 static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
101 				    struct sockaddr_un *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 = hapd->ctrl_dst;
110 	while (dst) {
111 		if (fromlen == dst->addrlen &&
112 		    os_memcmp(from->sun_path, dst->addr.sun_path,
113 			      fromlen - offsetof(struct sockaddr_un, sun_path))
114 		    == 0) {
115 			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
116 				    "level", (u8 *) from->sun_path, fromlen -
117 				    offsetof(struct sockaddr_un, sun_path));
118 			dst->debug_level = atoi(level);
119 			return 0;
120 		}
121 		dst = dst->next;
122 	}
123 
124 	return -1;
125 }
126 
127 
128 static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
129 				      struct sta_info *sta,
130 				      char *buf, size_t buflen)
131 {
132 	int len, res, ret;
133 
134 	if (sta == NULL) {
135 		ret = os_snprintf(buf, buflen, "FAIL\n");
136 		if (ret < 0 || (size_t) ret >= buflen)
137 			return 0;
138 		return ret;
139 	}
140 
141 	len = 0;
142 	ret = os_snprintf(buf + len, buflen - len, MACSTR "\n",
143 			  MAC2STR(sta->addr));
144 	if (ret < 0 || (size_t) ret >= buflen - len)
145 		return len;
146 	len += ret;
147 
148 	res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
149 	if (res >= 0)
150 		len += res;
151 	res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
152 	if (res >= 0)
153 		len += res;
154 	res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
155 	if (res >= 0)
156 		len += res;
157 
158 	return len;
159 }
160 
161 
162 static int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
163 					char *buf, size_t buflen)
164 {
165 	return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
166 }
167 
168 
169 static int hostapd_ctrl_iface_sta(struct hostapd_data *hapd,
170 				  const char *txtaddr,
171 				  char *buf, size_t buflen)
172 {
173 	u8 addr[ETH_ALEN];
174 	int ret;
175 
176 	if (hwaddr_aton(txtaddr, addr)) {
177 		ret = os_snprintf(buf, buflen, "FAIL\n");
178 		if (ret < 0 || (size_t) ret >= buflen)
179 			return 0;
180 		return ret;
181 	}
182 	return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr),
183 					  buf, buflen);
184 }
185 
186 
187 static int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd,
188 				       const char *txtaddr,
189 				       char *buf, size_t buflen)
190 {
191 	u8 addr[ETH_ALEN];
192 	struct sta_info *sta;
193 	int ret;
194 
195 	if (hwaddr_aton(txtaddr, addr) ||
196 	    (sta = ap_get_sta(hapd, addr)) == NULL) {
197 		ret = os_snprintf(buf, buflen, "FAIL\n");
198 		if (ret < 0 || (size_t) ret >= buflen)
199 			return 0;
200 		return ret;
201 	}
202 	return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
203 }
204 
205 
206 static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
207 				      const char *txtaddr)
208 {
209 	u8 addr[ETH_ALEN];
210 	struct sta_info *sta;
211 
212 	wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr);
213 
214 	if (hwaddr_aton(txtaddr, addr))
215 		return -1;
216 
217 	sta = ap_get_sta(hapd, addr);
218 	if (sta)
219 		return 0;
220 
221 	wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface "
222 		   "notification", MAC2STR(addr));
223 	sta = ap_sta_add(hapd, addr);
224 	if (sta == NULL)
225 		return -1;
226 
227 	hostapd_new_assoc_sta(hapd, sta, 0);
228 	return 0;
229 }
230 
231 
232 #ifdef CONFIG_IEEE80211W
233 static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
234 				       const char *txtaddr)
235 {
236 	u8 addr[ETH_ALEN];
237 	u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
238 
239 	wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr);
240 
241 	if (hwaddr_aton(txtaddr, addr))
242 		return -1;
243 
244 	os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
245 	ieee802_11_send_sa_query_req(hapd, addr, trans_id);
246 
247 	return 0;
248 }
249 #endif /* CONFIG_IEEE80211W */
250 
251 
252 #ifdef CONFIG_WPS
253 static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
254 {
255 	char *pin = os_strchr(txt, ' ');
256 	char *timeout_txt;
257 	int timeout;
258 
259 	if (pin == NULL)
260 		return -1;
261 	*pin++ = '\0';
262 
263 	timeout_txt = os_strchr(pin, ' ');
264 	if (timeout_txt) {
265 		*timeout_txt++ = '\0';
266 		timeout = atoi(timeout_txt);
267 	} else
268 		timeout = 0;
269 
270 	return hostapd_wps_add_pin(hapd, txt, pin, timeout);
271 }
272 #endif /* CONFIG_WPS */
273 
274 
275 static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
276 				       void *sock_ctx)
277 {
278 	struct hostapd_data *hapd = eloop_ctx;
279 	char buf[256];
280 	int res;
281 	struct sockaddr_un from;
282 	socklen_t fromlen = sizeof(from);
283 	char *reply;
284 	const int reply_size = 4096;
285 	int reply_len;
286 
287 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
288 		       (struct sockaddr *) &from, &fromlen);
289 	if (res < 0) {
290 		perror("recvfrom(ctrl_iface)");
291 		return;
292 	}
293 	buf[res] = '\0';
294 	wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res);
295 
296 	reply = os_malloc(reply_size);
297 	if (reply == NULL) {
298 		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
299 		       fromlen);
300 		return;
301 	}
302 
303 	os_memcpy(reply, "OK\n", 3);
304 	reply_len = 3;
305 
306 	if (os_strcmp(buf, "PING") == 0) {
307 		os_memcpy(reply, "PONG\n", 5);
308 		reply_len = 5;
309 	} else if (os_strcmp(buf, "MIB") == 0) {
310 		reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
311 		if (reply_len >= 0) {
312 			res = wpa_get_mib(hapd->wpa_auth, reply + reply_len,
313 					  reply_size - reply_len);
314 			if (res < 0)
315 				reply_len = -1;
316 			else
317 				reply_len += res;
318 		}
319 		if (reply_len >= 0) {
320 			res = ieee802_1x_get_mib(hapd, reply + reply_len,
321 						 reply_size - reply_len);
322 			if (res < 0)
323 				reply_len = -1;
324 			else
325 				reply_len += res;
326 		}
327 		if (reply_len >= 0) {
328 			res = radius_client_get_mib(hapd->radius,
329 						    reply + reply_len,
330 						    reply_size - reply_len);
331 			if (res < 0)
332 				reply_len = -1;
333 			else
334 				reply_len += res;
335 		}
336 	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
337 		reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
338 							 reply_size);
339 	} else if (os_strncmp(buf, "STA ", 4) == 0) {
340 		reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
341 						   reply_size);
342 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
343 		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
344 							reply_size);
345 	} else if (os_strcmp(buf, "ATTACH") == 0) {
346 		if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
347 			reply_len = -1;
348 	} else if (os_strcmp(buf, "DETACH") == 0) {
349 		if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
350 			reply_len = -1;
351 	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
352 		if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
353 						    buf + 6))
354 			reply_len = -1;
355 	} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
356 		if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
357 			reply_len = -1;
358 #ifdef CONFIG_IEEE80211W
359 	} else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
360 		if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
361 			reply_len = -1;
362 #endif /* CONFIG_IEEE80211W */
363 #ifdef CONFIG_WPS
364 	} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
365 		if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
366 			reply_len = -1;
367 	} else if (os_strcmp(buf, "WPS_PBC") == 0) {
368 		if (hostapd_wps_button_pushed(hapd))
369 			reply_len = -1;
370 #endif /* CONFIG_WPS */
371 	} else {
372 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
373 		reply_len = 16;
374 	}
375 
376 	if (reply_len < 0) {
377 		os_memcpy(reply, "FAIL\n", 5);
378 		reply_len = 5;
379 	}
380 	sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
381 	os_free(reply);
382 }
383 
384 
385 static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
386 {
387 	char *buf;
388 	size_t len;
389 
390 	if (hapd->conf->ctrl_interface == NULL)
391 		return NULL;
392 
393 	len = os_strlen(hapd->conf->ctrl_interface) +
394 		os_strlen(hapd->conf->iface) + 2;
395 	buf = os_malloc(len);
396 	if (buf == NULL)
397 		return NULL;
398 
399 	os_snprintf(buf, len, "%s/%s",
400 		    hapd->conf->ctrl_interface, hapd->conf->iface);
401 	buf[len - 1] = '\0';
402 	return buf;
403 }
404 
405 
406 static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
407 				      const char *txt, size_t len)
408 {
409 	struct hostapd_data *hapd = ctx;
410 	if (hapd == NULL)
411 		return;
412 	hostapd_ctrl_iface_send(hapd, level, txt, len);
413 }
414 
415 
416 int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
417 {
418 	struct sockaddr_un addr;
419 	int s = -1;
420 	char *fname = NULL;
421 
422 	hapd->ctrl_sock = -1;
423 
424 	if (hapd->conf->ctrl_interface == NULL)
425 		return 0;
426 
427 	if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
428 		if (errno == EEXIST) {
429 			wpa_printf(MSG_DEBUG, "Using existing control "
430 				   "interface directory.");
431 		} else {
432 			perror("mkdir[ctrl_interface]");
433 			goto fail;
434 		}
435 	}
436 
437 	if (hapd->conf->ctrl_interface_gid_set &&
438 	    chown(hapd->conf->ctrl_interface, 0,
439 		  hapd->conf->ctrl_interface_gid) < 0) {
440 		perror("chown[ctrl_interface]");
441 		return -1;
442 	}
443 
444 	if (os_strlen(hapd->conf->ctrl_interface) + 1 +
445 	    os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
446 		goto fail;
447 
448 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
449 	if (s < 0) {
450 		perror("socket(PF_UNIX)");
451 		goto fail;
452 	}
453 
454 	os_memset(&addr, 0, sizeof(addr));
455 #ifdef __FreeBSD__
456 	addr.sun_len = sizeof(addr);
457 #endif /* __FreeBSD__ */
458 	addr.sun_family = AF_UNIX;
459 	fname = hostapd_ctrl_iface_path(hapd);
460 	if (fname == NULL)
461 		goto fail;
462 	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
463 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
464 		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
465 			   strerror(errno));
466 		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
467 			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
468 				   " allow connections - assuming it was left"
469 				   "over from forced program termination");
470 			if (unlink(fname) < 0) {
471 				perror("unlink[ctrl_iface]");
472 				wpa_printf(MSG_ERROR, "Could not unlink "
473 					   "existing ctrl_iface socket '%s'",
474 					   fname);
475 				goto fail;
476 			}
477 			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
478 			    0) {
479 				perror("bind(PF_UNIX)");
480 				goto fail;
481 			}
482 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
483 				   "ctrl_iface socket '%s'", fname);
484 		} else {
485 			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
486 				   "be in use - cannot override it");
487 			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
488 				   "not used anymore", fname);
489 			os_free(fname);
490 			fname = NULL;
491 			goto fail;
492 		}
493 	}
494 
495 	if (hapd->conf->ctrl_interface_gid_set &&
496 	    chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) {
497 		perror("chown[ctrl_interface/ifname]");
498 		goto fail;
499 	}
500 
501 	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
502 		perror("chmod[ctrl_interface/ifname]");
503 		goto fail;
504 	}
505 	os_free(fname);
506 
507 	hapd->ctrl_sock = s;
508 	eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
509 				 NULL);
510 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
511 
512 	return 0;
513 
514 fail:
515 	if (s >= 0)
516 		close(s);
517 	if (fname) {
518 		unlink(fname);
519 		os_free(fname);
520 	}
521 	return -1;
522 }
523 
524 
525 void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
526 {
527 	struct wpa_ctrl_dst *dst, *prev;
528 
529 	if (hapd->ctrl_sock > -1) {
530 		char *fname;
531 		eloop_unregister_read_sock(hapd->ctrl_sock);
532 		close(hapd->ctrl_sock);
533 		hapd->ctrl_sock = -1;
534 		fname = hostapd_ctrl_iface_path(hapd);
535 		if (fname)
536 			unlink(fname);
537 		os_free(fname);
538 
539 		if (hapd->conf->ctrl_interface &&
540 		    rmdir(hapd->conf->ctrl_interface) < 0) {
541 			if (errno == ENOTEMPTY) {
542 				wpa_printf(MSG_DEBUG, "Control interface "
543 					   "directory not empty - leaving it "
544 					   "behind");
545 			} else {
546 				perror("rmdir[ctrl_interface]");
547 			}
548 		}
549 	}
550 
551 	dst = hapd->ctrl_dst;
552 	while (dst) {
553 		prev = dst;
554 		dst = dst->next;
555 		os_free(prev);
556 	}
557 }
558 
559 
560 static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
561 				    const char *buf, size_t len)
562 {
563 	struct wpa_ctrl_dst *dst, *next;
564 	struct msghdr msg;
565 	int idx;
566 	struct iovec io[2];
567 	char levelstr[10];
568 
569 	dst = hapd->ctrl_dst;
570 	if (hapd->ctrl_sock < 0 || dst == NULL)
571 		return;
572 
573 	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
574 	io[0].iov_base = levelstr;
575 	io[0].iov_len = os_strlen(levelstr);
576 	io[1].iov_base = (char *) buf;
577 	io[1].iov_len = len;
578 	os_memset(&msg, 0, sizeof(msg));
579 	msg.msg_iov = io;
580 	msg.msg_iovlen = 2;
581 
582 	idx = 0;
583 	while (dst) {
584 		next = dst->next;
585 		if (level >= dst->debug_level) {
586 			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
587 				    (u8 *) dst->addr.sun_path, dst->addrlen -
588 				    offsetof(struct sockaddr_un, sun_path));
589 			msg.msg_name = &dst->addr;
590 			msg.msg_namelen = dst->addrlen;
591 			if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) {
592 				int _errno = errno;
593 				wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
594 					   "%d - %s",
595 					   idx, errno, strerror(errno));
596 				dst->errors++;
597 				if (dst->errors > 10 || _errno == ENOENT) {
598 					hostapd_ctrl_iface_detach(
599 						hapd, &dst->addr,
600 						dst->addrlen);
601 				}
602 			} else
603 				dst->errors = 0;
604 		}
605 		idx++;
606 		dst = next;
607 	}
608 }
609 
610 #endif /* CONFIG_NATIVE_WINDOWS */
611