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