ctrl_iface_udp.c
Go to the documentation of this file.
00001 /*
00002  * WPA Supplicant / UDP socket -based control interface
00003  * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 2 as
00007  * published by the Free Software Foundation.
00008  *
00009  * Alternatively, this software may be distributed under the terms of BSD
00010  * license.
00011  *
00012  * See README and COPYING for more details.
00013  */
00014 
00015 #include "includes.h"
00016 
00017 #include "common.h"
00018 #include "eloop.h"
00019 #include "config.h"
00020 #include "eapol_supp/eapol_supp_sm.h"
00021 #include "wpa_supplicant_i.h"
00022 #include "ctrl_iface.h"
00023 #include "common/wpa_ctrl.h"
00024 
00025 
00026 #define COOKIE_LEN 8
00027 
00028 /* Per-interface ctrl_iface */
00029 
00037 struct wpa_ctrl_dst {
00038         struct wpa_ctrl_dst *next;
00039         struct sockaddr_in addr;
00040         socklen_t addrlen;
00041         int debug_level;
00042         int errors;
00043 };
00044 
00045 
00046 struct ctrl_iface_priv {
00047         struct wpa_supplicant *wpa_s;
00048         int sock;
00049         struct wpa_ctrl_dst *ctrl_dst;
00050         u8 cookie[COOKIE_LEN];
00051 };
00052 
00053 
00054 static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
00055                                            int level, const char *buf,
00056                                            size_t len);
00057 
00058 
00059 static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv,
00060                                             struct sockaddr_in *from,
00061                                             socklen_t fromlen)
00062 {
00063         struct wpa_ctrl_dst *dst;
00064 
00065         dst = os_zalloc(sizeof(*dst));
00066         if (dst == NULL)
00067                 return -1;
00068         os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in));
00069         dst->addrlen = fromlen;
00070         dst->debug_level = MSG_INFO;
00071         dst->next = priv->ctrl_dst;
00072         priv->ctrl_dst = dst;
00073         wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
00074                    inet_ntoa(from->sin_addr), ntohs(from->sin_port));
00075         return 0;
00076 }
00077 
00078 
00079 static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
00080                                             struct sockaddr_in *from,
00081                                             socklen_t fromlen)
00082 {
00083         struct wpa_ctrl_dst *dst, *prev = NULL;
00084 
00085         dst = priv->ctrl_dst;
00086         while (dst) {
00087                 if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
00088                     from->sin_port == dst->addr.sin_port) {
00089                         if (prev == NULL)
00090                                 priv->ctrl_dst = dst->next;
00091                         else
00092                                 prev->next = dst->next;
00093                         os_free(dst);
00094                         wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
00095                                    "%s:%d", inet_ntoa(from->sin_addr),
00096                                    ntohs(from->sin_port));
00097                         return 0;
00098                 }
00099                 prev = dst;
00100                 dst = dst->next;
00101         }
00102         return -1;
00103 }
00104 
00105 
00106 static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
00107                                            struct sockaddr_in *from,
00108                                            socklen_t fromlen,
00109                                            char *level)
00110 {
00111         struct wpa_ctrl_dst *dst;
00112 
00113         wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
00114 
00115         dst = priv->ctrl_dst;
00116         while (dst) {
00117                 if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
00118                     from->sin_port == dst->addr.sin_port) {
00119                         wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor "
00120                                    "level %s:%d", inet_ntoa(from->sin_addr),
00121                                    ntohs(from->sin_port));
00122                         dst->debug_level = atoi(level);
00123                         return 0;
00124                 }
00125                 dst = dst->next;
00126         }
00127 
00128         return -1;
00129 }
00130 
00131 
00132 static char *
00133 wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv,
00134                                      size_t *reply_len)
00135 {
00136         char *reply;
00137         reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
00138         if (reply == NULL) {
00139                 *reply_len = 1;
00140                 return NULL;
00141         }
00142 
00143         os_memcpy(reply, "COOKIE=", 7);
00144         wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
00145                          priv->cookie, COOKIE_LEN);
00146 
00147         *reply_len = 7 + 2 * COOKIE_LEN;
00148         return reply;
00149 }
00150 
00151 
00152 static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
00153                                               void *sock_ctx)
00154 {
00155         struct wpa_supplicant *wpa_s = eloop_ctx;
00156         struct ctrl_iface_priv *priv = sock_ctx;
00157         char buf[256], *pos;
00158         int res;
00159         struct sockaddr_in from;
00160         socklen_t fromlen = sizeof(from);
00161         char *reply = NULL;
00162         size_t reply_len = 0;
00163         int new_attached = 0;
00164         u8 cookie[COOKIE_LEN];
00165 
00166         res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
00167                        (struct sockaddr *) &from, &fromlen);
00168         if (res < 0) {
00169                 perror("recvfrom(ctrl_iface)");
00170                 return;
00171         }
00172         if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
00173                 /*
00174                  * The OS networking stack is expected to drop this kind of
00175                  * frames since the socket is bound to only localhost address.
00176                  * Just in case, drop the frame if it is coming from any other
00177                  * address.
00178                  */
00179                 wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
00180                            "source %s", inet_ntoa(from.sin_addr));
00181                 return;
00182         }
00183         buf[res] = '\0';
00184 
00185         if (os_strcmp(buf, "GET_COOKIE") == 0) {
00186                 reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len);
00187                 goto done;
00188         }
00189 
00190         /*
00191          * Require that the client includes a prefix with the 'cookie' value
00192          * fetched with GET_COOKIE command. This is used to verify that the
00193          * client has access to a bidirectional link over UDP in order to
00194          * avoid attacks using forged localhost IP address even if the OS does
00195          * not block such frames from remote destinations.
00196          */
00197         if (os_strncmp(buf, "COOKIE=", 7) != 0) {
00198                 wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
00199                            "drop request");
00200                 return;
00201         }
00202 
00203         if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
00204                 wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
00205                            "request - drop request");
00206                 return;
00207         }
00208 
00209         if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
00210                 wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
00211                            "drop request");
00212                 return;
00213         }
00214 
00215         pos = buf + 7 + 2 * COOKIE_LEN;
00216         while (*pos == ' ')
00217                 pos++;
00218 
00219         if (os_strcmp(pos, "ATTACH") == 0) {
00220                 if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen))
00221                         reply_len = 1;
00222                 else {
00223                         new_attached = 1;
00224                         reply_len = 2;
00225                 }
00226         } else if (os_strcmp(pos, "DETACH") == 0) {
00227                 if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen))
00228                         reply_len = 1;
00229                 else
00230                         reply_len = 2;
00231         } else if (os_strncmp(pos, "LEVEL ", 6) == 0) {
00232                 if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
00233                                                     pos + 6))
00234                         reply_len = 1;
00235                 else
00236                         reply_len = 2;
00237         } else {
00238                 reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
00239                                                           &reply_len);
00240         }
00241 
00242  done:
00243         if (reply) {
00244                 sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
00245                        fromlen);
00246                 os_free(reply);
00247         } else if (reply_len == 1) {
00248                 sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
00249                        fromlen);
00250         } else if (reply_len == 2) {
00251                 sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
00252                        fromlen);
00253         }
00254 
00255         if (new_attached)
00256                 eapol_sm_notify_ctrl_attached(wpa_s->eapol);
00257 }
00258 
00259 
00260 static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
00261                                              const char *txt, size_t len)
00262 {
00263         struct wpa_supplicant *wpa_s = ctx;
00264         if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
00265                 return;
00266         wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
00267 }
00268 
00269 
00270 struct ctrl_iface_priv *
00271 wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
00272 {
00273         struct ctrl_iface_priv *priv;
00274         struct sockaddr_in addr;
00275 
00276         priv = os_zalloc(sizeof(*priv));
00277         if (priv == NULL)
00278                 return NULL;
00279         priv->wpa_s = wpa_s;
00280         priv->sock = -1;
00281         os_get_random(priv->cookie, COOKIE_LEN);
00282 
00283         if (wpa_s->conf->ctrl_interface == NULL)
00284                 return priv;
00285 
00286         priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
00287         if (priv->sock < 0) {
00288                 perror("socket(PF_INET)");
00289                 goto fail;
00290         }
00291 
00292         os_memset(&addr, 0, sizeof(addr));
00293         addr.sin_family = AF_INET;
00294         addr.sin_addr.s_addr = htonl((127 << 24) | 1);
00295         addr.sin_port = htons(WPA_CTRL_IFACE_PORT);
00296         if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
00297                 perror("bind(AF_INET)");
00298                 goto fail;
00299         }
00300 
00301         eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
00302                                  wpa_s, priv);
00303         wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
00304 
00305         return priv;
00306 
00307 fail:
00308         if (priv->sock >= 0)
00309                 close(priv->sock);
00310         os_free(priv);
00311         return NULL;
00312 }
00313 
00314 
00315 void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
00316 {
00317         struct wpa_ctrl_dst *dst, *prev;
00318 
00319         if (priv->sock > -1) {
00320                 eloop_unregister_read_sock(priv->sock);
00321                 if (priv->ctrl_dst) {
00322                         /*
00323                          * Wait a second before closing the control socket if
00324                          * there are any attached monitors in order to allow
00325                          * them to receive any pending messages.
00326                          */
00327                         wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
00328                                    "monitors to receive messages");
00329                         os_sleep(1, 0);
00330                 }
00331                 close(priv->sock);
00332                 priv->sock = -1;
00333         }
00334 
00335         dst = priv->ctrl_dst;
00336         while (dst) {
00337                 prev = dst;
00338                 dst = dst->next;
00339                 os_free(prev);
00340         }
00341         os_free(priv);
00342 }
00343 
00344 
00345 static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
00346                                            int level, const char *buf,
00347                                            size_t len)
00348 {
00349         struct wpa_ctrl_dst *dst, *next;
00350         char levelstr[10];
00351         int idx;
00352         char *sbuf;
00353         int llen;
00354 
00355         dst = priv->ctrl_dst;
00356         if (priv->sock < 0 || dst == NULL)
00357                 return;
00358 
00359         os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
00360 
00361         llen = os_strlen(levelstr);
00362         sbuf = os_malloc(llen + len);
00363         if (sbuf == NULL)
00364                 return;
00365 
00366         os_memcpy(sbuf, levelstr, llen);
00367         os_memcpy(sbuf + llen, buf, len);
00368 
00369         idx = 0;
00370         while (dst) {
00371                 next = dst->next;
00372                 if (level >= dst->debug_level) {
00373                         wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
00374                                    inet_ntoa(dst->addr.sin_addr),
00375                                    ntohs(dst->addr.sin_port));
00376                         if (sendto(priv->sock, sbuf, llen + len, 0,
00377                                    (struct sockaddr *) &dst->addr,
00378                                    sizeof(dst->addr)) < 0) {
00379                                 perror("sendto(CTRL_IFACE monitor)");
00380                                 dst->errors++;
00381                                 if (dst->errors > 10) {
00382                                         wpa_supplicant_ctrl_iface_detach(
00383                                                 priv, &dst->addr,
00384                                                 dst->addrlen);
00385                                 }
00386                         } else
00387                                 dst->errors = 0;
00388                 }
00389                 idx++;
00390                 dst = next;
00391         }
00392         os_free(sbuf);
00393 }
00394 
00395 
00396 void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
00397 {
00398         wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
00399                    priv->wpa_s->ifname);
00400         eloop_wait_for_read_sock(priv->sock);
00401 }
00402 
00403 
00404 /* Global ctrl_iface */
00405 
00406 struct ctrl_iface_global_priv {
00407         int sock;
00408         u8 cookie[COOKIE_LEN];
00409 };
00410 
00411 
00412 static char *
00413 wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv,
00414                                  size_t *reply_len)
00415 {
00416         char *reply;
00417         reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
00418         if (reply == NULL) {
00419                 *reply_len = 1;
00420                 return NULL;
00421         }
00422 
00423         os_memcpy(reply, "COOKIE=", 7);
00424         wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
00425                          priv->cookie, COOKIE_LEN);
00426 
00427         *reply_len = 7 + 2 * COOKIE_LEN;
00428         return reply;
00429 }
00430 
00431 
00432 static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
00433                                                      void *sock_ctx)
00434 {
00435         struct wpa_global *global = eloop_ctx;
00436         struct ctrl_iface_global_priv *priv = sock_ctx;
00437         char buf[256], *pos;
00438         int res;
00439         struct sockaddr_in from;
00440         socklen_t fromlen = sizeof(from);
00441         char *reply;
00442         size_t reply_len;
00443         u8 cookie[COOKIE_LEN];
00444 
00445         res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
00446                        (struct sockaddr *) &from, &fromlen);
00447         if (res < 0) {
00448                 perror("recvfrom(ctrl_iface)");
00449                 return;
00450         }
00451         if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
00452                 /*
00453                  * The OS networking stack is expected to drop this kind of
00454                  * frames since the socket is bound to only localhost address.
00455                  * Just in case, drop the frame if it is coming from any other
00456                  * address.
00457                  */
00458                 wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
00459                            "source %s", inet_ntoa(from.sin_addr));
00460                 return;
00461         }
00462         buf[res] = '\0';
00463 
00464         if (os_strcmp(buf, "GET_COOKIE") == 0) {
00465                 reply = wpa_supplicant_global_get_cookie(priv, &reply_len);
00466                 goto done;
00467         }
00468 
00469         if (os_strncmp(buf, "COOKIE=", 7) != 0) {
00470                 wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
00471                            "drop request");
00472                 return;
00473         }
00474 
00475         if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
00476                 wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
00477                            "request - drop request");
00478                 return;
00479         }
00480 
00481         if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
00482                 wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
00483                            "drop request");
00484                 return;
00485         }
00486 
00487         pos = buf + 7 + 2 * COOKIE_LEN;
00488         while (*pos == ' ')
00489                 pos++;
00490 
00491         reply = wpa_supplicant_global_ctrl_iface_process(global, pos,
00492                                                          &reply_len);
00493 
00494  done:
00495         if (reply) {
00496                 sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
00497                        fromlen);
00498                 os_free(reply);
00499         } else if (reply_len) {
00500                 sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
00501                        fromlen);
00502         }
00503 }
00504 
00505 
00506 struct ctrl_iface_global_priv *
00507 wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
00508 {
00509         struct ctrl_iface_global_priv *priv;
00510         struct sockaddr_in addr;
00511 
00512         priv = os_zalloc(sizeof(*priv));
00513         if (priv == NULL)
00514                 return NULL;
00515         priv->sock = -1;
00516         os_get_random(priv->cookie, COOKIE_LEN);
00517 
00518         if (global->params.ctrl_interface == NULL)
00519                 return priv;
00520 
00521         wpa_printf(MSG_DEBUG, "Global control interface '%s'",
00522                    global->params.ctrl_interface);
00523 
00524         priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
00525         if (priv->sock < 0) {
00526                 perror("socket(PF_INET)");
00527                 goto fail;
00528         }
00529 
00530         os_memset(&addr, 0, sizeof(addr));
00531         addr.sin_family = AF_INET;
00532         addr.sin_addr.s_addr = htonl((127 << 24) | 1);
00533         addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT);
00534         if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
00535                 perror("bind(AF_INET)");
00536                 goto fail;
00537         }
00538 
00539         eloop_register_read_sock(priv->sock,
00540                                  wpa_supplicant_global_ctrl_iface_receive,
00541                                  global, priv);
00542 
00543         return priv;
00544 
00545 fail:
00546         if (priv->sock >= 0)
00547                 close(priv->sock);
00548         os_free(priv);
00549         return NULL;
00550 }
00551 
00552 
00553 void
00554 wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
00555 {
00556         if (priv->sock >= 0) {
00557                 eloop_unregister_read_sock(priv->sock);
00558                 close(priv->sock);
00559         }
00560         os_free(priv);
00561 }


wpa_supplicant
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Apr 24 2014 15:34:34