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