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