1 /* 2 * WPA Supplicant / UDP socket -based control interface 3 * Copyright (c) 2004-2005, Jouni Malinen <j (at) 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 #include "common.h" 18 #include "eloop.h" 19 #include "config.h" 20 #include "eapol_sm.h" 21 #include "wpa_supplicant_i.h" 22 #include "ctrl_iface.h" 23 #include "wpa_ctrl.h" 24 25 26 #define COOKIE_LEN 8 27 28 /* Per-interface ctrl_iface */ 29 30 /** 31 * struct wpa_ctrl_dst - Internal data structure of control interface monitors 32 * 33 * This structure is used to store information about registered control 34 * interface monitors into struct wpa_supplicant. This data is private to 35 * ctrl_iface_udp.c and should not be touched directly from other files. 36 */ 37 struct wpa_ctrl_dst { 38 struct wpa_ctrl_dst *next; 39 struct sockaddr_in addr; 40 socklen_t addrlen; 41 int debug_level; 42 int errors; 43 }; 44 45 46 struct ctrl_iface_priv { 47 struct wpa_supplicant *wpa_s; 48 int sock; 49 struct wpa_ctrl_dst *ctrl_dst; 50 u8 cookie[COOKIE_LEN]; 51 }; 52 53 54 static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, 55 int level, const char *buf, 56 size_t len); 57 58 59 static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, 60 struct sockaddr_in *from, 61 socklen_t fromlen) 62 { 63 struct wpa_ctrl_dst *dst; 64 65 dst = os_zalloc(sizeof(*dst)); 66 if (dst == NULL) 67 return -1; 68 os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in)); 69 dst->addrlen = fromlen; 70 dst->debug_level = MSG_INFO; 71 dst->next = priv->ctrl_dst; 72 priv->ctrl_dst = dst; 73 wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", 74 inet_ntoa(from->sin_addr), ntohs(from->sin_port)); 75 return 0; 76 } 77 78 79 static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, 80 struct sockaddr_in *from, 81 socklen_t fromlen) 82 { 83 struct wpa_ctrl_dst *dst, *prev = NULL; 84 85 dst = priv->ctrl_dst; 86 while (dst) { 87 if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && 88 from->sin_port == dst->addr.sin_port) { 89 if (prev == NULL) 90 priv->ctrl_dst = dst->next; 91 else 92 prev->next = dst->next; 93 os_free(dst); 94 wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached " 95 "%s:%d", inet_ntoa(from->sin_addr), 96 ntohs(from->sin_port)); 97 return 0; 98 } 99 prev = dst; 100 dst = dst->next; 101 } 102 return -1; 103 } 104 105 106 static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, 107 struct sockaddr_in *from, 108 socklen_t fromlen, 109 char *level) 110 { 111 struct wpa_ctrl_dst *dst; 112 113 wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); 114 115 dst = priv->ctrl_dst; 116 while (dst) { 117 if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && 118 from->sin_port == dst->addr.sin_port) { 119 wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor " 120 "level %s:%d", inet_ntoa(from->sin_addr), 121 ntohs(from->sin_port)); 122 dst->debug_level = atoi(level); 123 return 0; 124 } 125 dst = dst->next; 126 } 127 128 return -1; 129 } 130 131 132 static char * 133 wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv, 134 size_t *reply_len) 135 { 136 char *reply; 137 reply = os_malloc(7 + 2 * COOKIE_LEN + 1); 138 if (reply == NULL) { 139 *reply_len = 1; 140 return NULL; 141 } 142 143 os_memcpy(reply, "COOKIE=", 7); 144 wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, 145 priv->cookie, COOKIE_LEN); 146 147 *reply_len = 7 + 2 * COOKIE_LEN; 148 return reply; 149 } 150 151 152 static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, 153 void *sock_ctx) 154 { 155 struct wpa_supplicant *wpa_s = eloop_ctx; 156 struct ctrl_iface_priv *priv = sock_ctx; 157 char buf[256], *pos; 158 int res; 159 struct sockaddr_in from; 160 socklen_t fromlen = sizeof(from); 161 char *reply = NULL; 162 size_t reply_len = 0; 163 int new_attached = 0; 164 u8 cookie[COOKIE_LEN]; 165 166 res = recvfrom(sock, buf, sizeof(buf) - 1, 0, 167 (struct sockaddr *) &from, &fromlen); 168 if (res < 0) { 169 perror("recvfrom(ctrl_iface)"); 170 return; 171 } 172 if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { 173 /* 174 * The OS networking stack is expected to drop this kind of 175 * frames since the socket is bound to only localhost address. 176 * Just in case, drop the frame if it is coming from any other 177 * address. 178 */ 179 wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected " 180 "source %s", inet_ntoa(from.sin_addr)); 181 return; 182 } 183 buf[res] = '\0'; 184 185 if (os_strcmp(buf, "GET_COOKIE") == 0) { 186 reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len); 187 goto done; 188 } 189 190 /* 191 * Require that the client includes a prefix with the 'cookie' value 192 * fetched with GET_COOKIE command. This is used to verify that the 193 * client has access to a bidirectional link over UDP in order to 194 * avoid attacks using forged localhost IP address even if the OS does 195 * not block such frames from remote destinations. 196 */ 197 if (os_strncmp(buf, "COOKIE=", 7) != 0) { 198 wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - " 199 "drop request"); 200 return; 201 } 202 203 if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) { 204 wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the " 205 "request - drop request"); 206 return; 207 } 208 209 if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) { 210 wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - " 211 "drop request"); 212 return; 213 } 214 215 pos = buf + 7 + 2 * COOKIE_LEN; 216 while (*pos == ' ') 217 pos++; 218 219 if (os_strcmp(pos, "ATTACH") == 0) { 220 if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) 221 reply_len = 1; 222 else { 223 new_attached = 1; 224 reply_len = 2; 225 } 226 } else if (os_strcmp(pos, "DETACH") == 0) { 227 if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) 228 reply_len = 1; 229 else 230 reply_len = 2; 231 } else if (os_strncmp(pos, "LEVEL ", 6) == 0) { 232 if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen, 233 pos + 6)) 234 reply_len = 1; 235 else 236 reply_len = 2; 237 } else { 238 reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos, 239 &reply_len); 240 } 241 242 done: 243 if (reply) { 244 sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, 245 fromlen); 246 os_free(reply); 247 } else if (reply_len == 1) { 248 sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, 249 fromlen); 250 } else if (reply_len == 2) { 251 sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, 252 fromlen); 253 } 254 255 if (new_attached) 256 eapol_sm_notify_ctrl_attached(wpa_s->eapol); 257 } 258 259 260 static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, 261 const char *txt, size_t len) 262 { 263 struct wpa_supplicant *wpa_s = ctx; 264 if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) 265 return; 266 wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); 267 } 268 269 270 struct ctrl_iface_priv * 271 wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) 272 { 273 struct ctrl_iface_priv *priv; 274 struct sockaddr_in addr; 275 276 priv = os_zalloc(sizeof(*priv)); 277 if (priv == NULL) 278 return NULL; 279 priv->wpa_s = wpa_s; 280 priv->sock = -1; 281 os_get_random(priv->cookie, COOKIE_LEN); 282 283 if (wpa_s->conf->ctrl_interface == NULL) 284 return priv; 285 286 priv->sock = socket(PF_INET, SOCK_DGRAM, 0); 287 if (priv->sock < 0) { 288 perror("socket(PF_INET)"); 289 goto fail; 290 } 291 292 os_memset(&addr, 0, sizeof(addr)); 293 addr.sin_family = AF_INET; 294 addr.sin_addr.s_addr = htonl((127 << 24) | 1); 295 addr.sin_port = htons(WPA_CTRL_IFACE_PORT); 296 if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 297 perror("bind(AF_INET)"); 298 goto fail; 299 } 300 301 eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, 302 wpa_s, priv); 303 wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); 304 305 return priv; 306 307 fail: 308 if (priv->sock >= 0) 309 close(priv->sock); 310 os_free(priv); 311 return NULL; 312 } 313 314 315 void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) 316 { 317 struct wpa_ctrl_dst *dst, *prev; 318 319 if (priv->sock > -1) { 320 eloop_unregister_read_sock(priv->sock); 321 if (priv->ctrl_dst) { 322 /* 323 * Wait a second before closing the control socket if 324 * there are any attached monitors in order to allow 325 * them to receive any pending messages. 326 */ 327 wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " 328 "monitors to receive messages"); 329 os_sleep(1, 0); 330 } 331 close(priv->sock); 332 priv->sock = -1; 333 } 334 335 dst = priv->ctrl_dst; 336 while (dst) { 337 prev = dst; 338 dst = dst->next; 339 os_free(prev); 340 } 341 os_free(priv); 342 } 343 344 345 static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, 346 int level, const char *buf, 347 size_t len) 348 { 349 struct wpa_ctrl_dst *dst, *next; 350 char levelstr[10]; 351 int idx; 352 char *sbuf; 353 int llen; 354 355 dst = priv->ctrl_dst; 356 if (priv->sock < 0 || dst == NULL) 357 return; 358 359 os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); 360 361 llen = os_strlen(levelstr); 362 sbuf = os_malloc(llen + len); 363 if (sbuf == NULL) 364 return; 365 366 os_memcpy(sbuf, levelstr, llen); 367 os_memcpy(sbuf + llen, buf, len); 368 369 idx = 0; 370 while (dst) { 371 next = dst->next; 372 if (level >= dst->debug_level) { 373 wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", 374 inet_ntoa(dst->addr.sin_addr), 375 ntohs(dst->addr.sin_port)); 376 if (sendto(priv->sock, sbuf, llen + len, 0, 377 (struct sockaddr *) &dst->addr, 378 sizeof(dst->addr)) < 0) { 379 perror("sendto(CTRL_IFACE monitor)"); 380 dst->errors++; 381 if (dst->errors > 10) { 382 wpa_supplicant_ctrl_iface_detach( 383 priv, &dst->addr, 384 dst->addrlen); 385 } 386 } else 387 dst->errors = 0; 388 } 389 idx++; 390 dst = next; 391 } 392 os_free(sbuf); 393 } 394 395 396 void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) 397 { 398 wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor", 399 priv->wpa_s->ifname); 400 eloop_wait_for_read_sock(priv->sock); 401 } 402 403 404 /* Global ctrl_iface */ 405 406 struct ctrl_iface_global_priv { 407 int sock; 408 u8 cookie[COOKIE_LEN]; 409 }; 410 411 412 static char * 413 wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv, 414 size_t *reply_len) 415 { 416 char *reply; 417 reply = os_malloc(7 + 2 * COOKIE_LEN + 1); 418 if (reply == NULL) { 419 *reply_len = 1; 420 return NULL; 421 } 422 423 os_memcpy(reply, "COOKIE=", 7); 424 wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, 425 priv->cookie, COOKIE_LEN); 426 427 *reply_len = 7 + 2 * COOKIE_LEN; 428 return reply; 429 } 430 431 432 static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, 433 void *sock_ctx) 434 { 435 struct wpa_global *global = eloop_ctx; 436 struct ctrl_iface_global_priv *priv = sock_ctx; 437 char buf[256], *pos; 438 int res; 439 struct sockaddr_in from; 440 socklen_t fromlen = sizeof(from); 441 char *reply; 442 size_t reply_len; 443 u8 cookie[COOKIE_LEN]; 444 445 res = recvfrom(sock, buf, sizeof(buf) - 1, 0, 446 (struct sockaddr *) &from, &fromlen); 447 if (res < 0) { 448 perror("recvfrom(ctrl_iface)"); 449 return; 450 } 451 if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { 452 /* 453 * The OS networking stack is expected to drop this kind of 454 * frames since the socket is bound to only localhost address. 455 * Just in case, drop the frame if it is coming from any other 456 * address. 457 */ 458 wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected " 459 "source %s", inet_ntoa(from.sin_addr)); 460 return; 461 } 462 buf[res] = '\0'; 463 464 if (os_strcmp(buf, "GET_COOKIE") == 0) { 465 reply = wpa_supplicant_global_get_cookie(priv, &reply_len); 466 goto done; 467 } 468 469 if (os_strncmp(buf, "COOKIE=", 7) != 0) { 470 wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - " 471 "drop request"); 472 return; 473 } 474 475 if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) { 476 wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the " 477 "request - drop request"); 478 return; 479 } 480 481 if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) { 482 wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - " 483 "drop request"); 484 return; 485 } 486 487 pos = buf + 7 + 2 * COOKIE_LEN; 488 while (*pos == ' ') 489 pos++; 490 491 reply = wpa_supplicant_global_ctrl_iface_process(global, pos, 492 &reply_len); 493 494 done: 495 if (reply) { 496 sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, 497 fromlen); 498 os_free(reply); 499 } else if (reply_len) { 500 sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, 501 fromlen); 502 } 503 } 504 505 506 struct ctrl_iface_global_priv * 507 wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) 508 { 509 struct ctrl_iface_global_priv *priv; 510 struct sockaddr_in addr; 511 512 priv = os_zalloc(sizeof(*priv)); 513 if (priv == NULL) 514 return NULL; 515 priv->sock = -1; 516 os_get_random(priv->cookie, COOKIE_LEN); 517 518 if (global->params.ctrl_interface == NULL) 519 return priv; 520 521 wpa_printf(MSG_DEBUG, "Global control interface '%s'", 522 global->params.ctrl_interface); 523 524 priv->sock = socket(PF_INET, SOCK_DGRAM, 0); 525 if (priv->sock < 0) { 526 perror("socket(PF_INET)"); 527 goto fail; 528 } 529 530 os_memset(&addr, 0, sizeof(addr)); 531 addr.sin_family = AF_INET; 532 addr.sin_addr.s_addr = htonl((127 << 24) | 1); 533 addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT); 534 if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 535 perror("bind(AF_INET)"); 536 goto fail; 537 } 538 539 eloop_register_read_sock(priv->sock, 540 wpa_supplicant_global_ctrl_iface_receive, 541 global, priv); 542 543 return priv; 544 545 fail: 546 if (priv->sock >= 0) 547 close(priv->sock); 548 os_free(priv); 549 return NULL; 550 } 551 552 553 void 554 wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) 555 { 556 if (priv->sock >= 0) { 557 eloop_unregister_read_sock(priv->sock); 558 close(priv->sock); 559 } 560 os_free(priv); 561 } 562