1189251Ssam/* 2189251Ssam * WPA Supplicant / UNIX domain socket -based control interface 3214734Srpaulo * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam#include <sys/un.h> 11189251Ssam#include <sys/stat.h> 12189251Ssam#include <grp.h> 13189775Ssam#include <stddef.h> 14252726Srpaulo#include <unistd.h> 15252726Srpaulo#include <fcntl.h> 16252726Srpaulo#ifdef ANDROID 17252726Srpaulo#include <cutils/sockets.h> 18252726Srpaulo#endif /* ANDROID */ 19189251Ssam 20214734Srpaulo#include "utils/common.h" 21214734Srpaulo#include "utils/eloop.h" 22214734Srpaulo#include "utils/list.h" 23214734Srpaulo#include "eapol_supp/eapol_supp_sm.h" 24189251Ssam#include "config.h" 25189251Ssam#include "wpa_supplicant_i.h" 26189251Ssam#include "ctrl_iface.h" 27189251Ssam 28189251Ssam/* Per-interface ctrl_iface */ 29189251Ssam 30189251Ssam/** 31189251Ssam * struct wpa_ctrl_dst - Internal data structure of control interface monitors 32189251Ssam * 33189251Ssam * This structure is used to store information about registered control 34189251Ssam * interface monitors into struct wpa_supplicant. This data is private to 35189251Ssam * ctrl_iface_unix.c and should not be touched directly from other files. 36189251Ssam */ 37189251Ssamstruct wpa_ctrl_dst { 38214734Srpaulo struct dl_list list; 39189251Ssam struct sockaddr_un addr; 40189251Ssam socklen_t addrlen; 41189251Ssam int debug_level; 42189251Ssam int errors; 43189251Ssam}; 44189251Ssam 45189251Ssam 46189251Ssamstruct ctrl_iface_priv { 47189251Ssam struct wpa_supplicant *wpa_s; 48189251Ssam int sock; 49214734Srpaulo struct dl_list ctrl_dst; 50189251Ssam}; 51189251Ssam 52189251Ssam 53189251Ssamstatic void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, 54189251Ssam int level, const char *buf, 55189251Ssam size_t len); 56189251Ssam 57189251Ssam 58189251Ssamstatic int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, 59189251Ssam struct sockaddr_un *from, 60189251Ssam socklen_t fromlen) 61189251Ssam{ 62189251Ssam struct wpa_ctrl_dst *dst; 63189251Ssam 64189251Ssam dst = os_zalloc(sizeof(*dst)); 65189251Ssam if (dst == NULL) 66189251Ssam return -1; 67189251Ssam os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); 68189251Ssam dst->addrlen = fromlen; 69189251Ssam dst->debug_level = MSG_INFO; 70214734Srpaulo dl_list_add(&priv->ctrl_dst, &dst->list); 71189251Ssam wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", 72189775Ssam (u8 *) from->sun_path, 73189775Ssam fromlen - offsetof(struct sockaddr_un, sun_path)); 74189251Ssam return 0; 75189251Ssam} 76189251Ssam 77189251Ssam 78189251Ssamstatic int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, 79189251Ssam struct sockaddr_un *from, 80189251Ssam socklen_t fromlen) 81189251Ssam{ 82214734Srpaulo struct wpa_ctrl_dst *dst; 83189251Ssam 84214734Srpaulo dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) { 85189251Ssam if (fromlen == dst->addrlen && 86189251Ssam os_memcmp(from->sun_path, dst->addr.sun_path, 87209158Srpaulo fromlen - offsetof(struct sockaddr_un, sun_path)) 88209158Srpaulo == 0) { 89214734Srpaulo dl_list_del(&dst->list); 90189251Ssam os_free(dst); 91189251Ssam wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", 92189251Ssam (u8 *) from->sun_path, 93209158Srpaulo fromlen - 94209158Srpaulo offsetof(struct sockaddr_un, sun_path)); 95189251Ssam return 0; 96189251Ssam } 97189251Ssam } 98189251Ssam return -1; 99189251Ssam} 100189251Ssam 101189251Ssam 102189251Ssamstatic int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, 103189251Ssam struct sockaddr_un *from, 104189251Ssam socklen_t fromlen, 105189251Ssam char *level) 106189251Ssam{ 107189251Ssam struct wpa_ctrl_dst *dst; 108189251Ssam 109189251Ssam wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); 110189251Ssam 111214734Srpaulo dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) { 112189251Ssam if (fromlen == dst->addrlen && 113189251Ssam os_memcmp(from->sun_path, dst->addr.sun_path, 114209158Srpaulo fromlen - offsetof(struct sockaddr_un, sun_path)) 115209158Srpaulo == 0) { 116189251Ssam wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " 117189251Ssam "level", (u8 *) from->sun_path, 118209158Srpaulo fromlen - 119209158Srpaulo offsetof(struct sockaddr_un, sun_path)); 120189251Ssam dst->debug_level = atoi(level); 121189251Ssam return 0; 122189251Ssam } 123189251Ssam } 124189251Ssam 125189251Ssam return -1; 126189251Ssam} 127189251Ssam 128189251Ssam 129189251Ssamstatic void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, 130189251Ssam void *sock_ctx) 131189251Ssam{ 132189251Ssam struct wpa_supplicant *wpa_s = eloop_ctx; 133189251Ssam struct ctrl_iface_priv *priv = sock_ctx; 134252726Srpaulo char buf[4096]; 135189251Ssam int res; 136189251Ssam struct sockaddr_un from; 137189251Ssam socklen_t fromlen = sizeof(from); 138189251Ssam char *reply = NULL; 139189251Ssam size_t reply_len = 0; 140189251Ssam int new_attached = 0; 141189251Ssam 142189251Ssam res = recvfrom(sock, buf, sizeof(buf) - 1, 0, 143189251Ssam (struct sockaddr *) &from, &fromlen); 144189251Ssam if (res < 0) { 145189251Ssam perror("recvfrom(ctrl_iface)"); 146189251Ssam return; 147189251Ssam } 148189251Ssam buf[res] = '\0'; 149189251Ssam 150189251Ssam if (os_strcmp(buf, "ATTACH") == 0) { 151189251Ssam if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) 152189251Ssam reply_len = 1; 153189251Ssam else { 154189251Ssam new_attached = 1; 155189251Ssam reply_len = 2; 156189251Ssam } 157189251Ssam } else if (os_strcmp(buf, "DETACH") == 0) { 158189251Ssam if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) 159189251Ssam reply_len = 1; 160189251Ssam else 161189251Ssam reply_len = 2; 162189251Ssam } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { 163189251Ssam if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen, 164189251Ssam buf + 6)) 165189251Ssam reply_len = 1; 166189251Ssam else 167189251Ssam reply_len = 2; 168189251Ssam } else { 169189251Ssam reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf, 170189251Ssam &reply_len); 171189251Ssam } 172189251Ssam 173189251Ssam if (reply) { 174189251Ssam sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, 175189251Ssam fromlen); 176189251Ssam os_free(reply); 177189251Ssam } else if (reply_len == 1) { 178189251Ssam sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, 179189251Ssam fromlen); 180189251Ssam } else if (reply_len == 2) { 181189251Ssam sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, 182189251Ssam fromlen); 183189251Ssam } 184189251Ssam 185189251Ssam if (new_attached) 186189251Ssam eapol_sm_notify_ctrl_attached(wpa_s->eapol); 187189251Ssam} 188189251Ssam 189189251Ssam 190189251Ssamstatic char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) 191189251Ssam{ 192189251Ssam char *buf; 193189251Ssam size_t len; 194189251Ssam char *pbuf, *dir = NULL, *gid_str = NULL; 195189251Ssam int res; 196189251Ssam 197189251Ssam if (wpa_s->conf->ctrl_interface == NULL) 198189251Ssam return NULL; 199189251Ssam 200189251Ssam pbuf = os_strdup(wpa_s->conf->ctrl_interface); 201189251Ssam if (pbuf == NULL) 202189251Ssam return NULL; 203189251Ssam if (os_strncmp(pbuf, "DIR=", 4) == 0) { 204189251Ssam dir = pbuf + 4; 205189251Ssam gid_str = os_strstr(dir, " GROUP="); 206189251Ssam if (gid_str) { 207189251Ssam *gid_str = '\0'; 208189251Ssam gid_str += 7; 209189251Ssam } 210189251Ssam } else 211189251Ssam dir = pbuf; 212189251Ssam 213189251Ssam len = os_strlen(dir) + os_strlen(wpa_s->ifname) + 2; 214189251Ssam buf = os_malloc(len); 215189251Ssam if (buf == NULL) { 216189251Ssam os_free(pbuf); 217189251Ssam return NULL; 218189251Ssam } 219189251Ssam 220189251Ssam res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname); 221189251Ssam if (res < 0 || (size_t) res >= len) { 222189251Ssam os_free(pbuf); 223189251Ssam os_free(buf); 224189251Ssam return NULL; 225189251Ssam } 226189251Ssam#ifdef __CYGWIN__ 227189251Ssam { 228189251Ssam /* Windows/WinPcap uses interface names that are not suitable 229189251Ssam * as a file name - convert invalid chars to underscores */ 230189251Ssam char *pos = buf; 231189251Ssam while (*pos) { 232189251Ssam if (*pos == '\\') 233189251Ssam *pos = '_'; 234189251Ssam pos++; 235189251Ssam } 236189251Ssam } 237189251Ssam#endif /* __CYGWIN__ */ 238189251Ssam os_free(pbuf); 239189251Ssam return buf; 240189251Ssam} 241189251Ssam 242189251Ssam 243189251Ssamstatic void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, 244189251Ssam const char *txt, size_t len) 245189251Ssam{ 246189251Ssam struct wpa_supplicant *wpa_s = ctx; 247189251Ssam if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) 248189251Ssam return; 249189251Ssam wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); 250189251Ssam} 251189251Ssam 252189251Ssam 253189251Ssamstruct ctrl_iface_priv * 254189251Ssamwpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) 255189251Ssam{ 256189251Ssam struct ctrl_iface_priv *priv; 257189251Ssam struct sockaddr_un addr; 258189251Ssam char *fname = NULL; 259189251Ssam gid_t gid = 0; 260189251Ssam int gid_set = 0; 261189251Ssam char *buf, *dir = NULL, *gid_str = NULL; 262189251Ssam struct group *grp; 263189251Ssam char *endp; 264252726Srpaulo int flags; 265189251Ssam 266189251Ssam priv = os_zalloc(sizeof(*priv)); 267189251Ssam if (priv == NULL) 268189251Ssam return NULL; 269214734Srpaulo dl_list_init(&priv->ctrl_dst); 270189251Ssam priv->wpa_s = wpa_s; 271189251Ssam priv->sock = -1; 272189251Ssam 273189251Ssam if (wpa_s->conf->ctrl_interface == NULL) 274189251Ssam return priv; 275189251Ssam 276189251Ssam buf = os_strdup(wpa_s->conf->ctrl_interface); 277189251Ssam if (buf == NULL) 278189251Ssam goto fail; 279252726Srpaulo#ifdef ANDROID 280252726Srpaulo os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s", 281252726Srpaulo wpa_s->conf->ctrl_interface); 282252726Srpaulo priv->sock = android_get_control_socket(addr.sun_path); 283252726Srpaulo if (priv->sock >= 0) 284252726Srpaulo goto havesock; 285252726Srpaulo#endif /* ANDROID */ 286189251Ssam if (os_strncmp(buf, "DIR=", 4) == 0) { 287189251Ssam dir = buf + 4; 288189251Ssam gid_str = os_strstr(dir, " GROUP="); 289189251Ssam if (gid_str) { 290189251Ssam *gid_str = '\0'; 291189251Ssam gid_str += 7; 292189251Ssam } 293189251Ssam } else { 294189251Ssam dir = buf; 295189251Ssam gid_str = wpa_s->conf->ctrl_interface_group; 296189251Ssam } 297189251Ssam 298189251Ssam if (mkdir(dir, S_IRWXU | S_IRWXG) < 0) { 299189251Ssam if (errno == EEXIST) { 300189251Ssam wpa_printf(MSG_DEBUG, "Using existing control " 301189251Ssam "interface directory."); 302189251Ssam } else { 303189251Ssam perror("mkdir[ctrl_interface]"); 304189251Ssam goto fail; 305189251Ssam } 306189251Ssam } 307189251Ssam 308252726Srpaulo#ifdef ANDROID 309252726Srpaulo /* 310252726Srpaulo * wpa_supplicant is started from /init.*.rc on Android and that seems 311252726Srpaulo * to be using umask 0077 which would leave the control interface 312252726Srpaulo * directory without group access. This breaks things since Wi-Fi 313252726Srpaulo * framework assumes that this directory can be accessed by other 314252726Srpaulo * applications in the wifi group. Fix this by adding group access even 315252726Srpaulo * if umask value would prevent this. 316252726Srpaulo */ 317252726Srpaulo if (chmod(dir, S_IRWXU | S_IRWXG) < 0) { 318252726Srpaulo wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s", 319252726Srpaulo strerror(errno)); 320252726Srpaulo /* Try to continue anyway */ 321252726Srpaulo } 322252726Srpaulo#endif /* ANDROID */ 323252726Srpaulo 324189251Ssam if (gid_str) { 325189251Ssam grp = getgrnam(gid_str); 326189251Ssam if (grp) { 327189251Ssam gid = grp->gr_gid; 328189251Ssam gid_set = 1; 329189251Ssam wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" 330189251Ssam " (from group name '%s')", 331189251Ssam (int) gid, gid_str); 332189251Ssam } else { 333189251Ssam /* Group name not found - try to parse this as gid */ 334189251Ssam gid = strtol(gid_str, &endp, 10); 335189251Ssam if (*gid_str == '\0' || *endp != '\0') { 336189251Ssam wpa_printf(MSG_ERROR, "CTRL: Invalid group " 337189251Ssam "'%s'", gid_str); 338189251Ssam goto fail; 339189251Ssam } 340189251Ssam gid_set = 1; 341189251Ssam wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", 342189251Ssam (int) gid); 343189251Ssam } 344189251Ssam } 345189251Ssam 346189251Ssam if (gid_set && chown(dir, -1, gid) < 0) { 347189251Ssam perror("chown[ctrl_interface]"); 348189251Ssam goto fail; 349189251Ssam } 350189251Ssam 351209158Srpaulo /* Make sure the group can enter and read the directory */ 352209158Srpaulo if (gid_set && 353209158Srpaulo chmod(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP) < 0) { 354209158Srpaulo wpa_printf(MSG_ERROR, "CTRL: chmod[ctrl_interface]: %s", 355209158Srpaulo strerror(errno)); 356209158Srpaulo goto fail; 357209158Srpaulo } 358209158Srpaulo 359189251Ssam if (os_strlen(dir) + 1 + os_strlen(wpa_s->ifname) >= 360189251Ssam sizeof(addr.sun_path)) { 361189251Ssam wpa_printf(MSG_ERROR, "ctrl_iface path limit exceeded"); 362189251Ssam goto fail; 363189251Ssam } 364189251Ssam 365189251Ssam priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); 366189251Ssam if (priv->sock < 0) { 367189251Ssam perror("socket(PF_UNIX)"); 368189251Ssam goto fail; 369189251Ssam } 370189251Ssam 371189251Ssam os_memset(&addr, 0, sizeof(addr)); 372214734Srpaulo#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 373189775Ssam addr.sun_len = sizeof(addr); 374209158Srpaulo#endif /* __FreeBSD__ */ 375189251Ssam addr.sun_family = AF_UNIX; 376189251Ssam fname = wpa_supplicant_ctrl_iface_path(wpa_s); 377189251Ssam if (fname == NULL) 378189251Ssam goto fail; 379189251Ssam os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); 380189251Ssam if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 381189251Ssam wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", 382189251Ssam strerror(errno)); 383189251Ssam if (connect(priv->sock, (struct sockaddr *) &addr, 384189251Ssam sizeof(addr)) < 0) { 385189251Ssam wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" 386189251Ssam " allow connections - assuming it was left" 387189251Ssam "over from forced program termination"); 388189251Ssam if (unlink(fname) < 0) { 389189251Ssam perror("unlink[ctrl_iface]"); 390189251Ssam wpa_printf(MSG_ERROR, "Could not unlink " 391189251Ssam "existing ctrl_iface socket '%s'", 392189251Ssam fname); 393189251Ssam goto fail; 394189251Ssam } 395189251Ssam if (bind(priv->sock, (struct sockaddr *) &addr, 396189251Ssam sizeof(addr)) < 0) { 397252726Srpaulo perror("supp-ctrl-iface-init: bind(PF_UNIX)"); 398189251Ssam goto fail; 399189251Ssam } 400189251Ssam wpa_printf(MSG_DEBUG, "Successfully replaced leftover " 401189251Ssam "ctrl_iface socket '%s'", fname); 402189251Ssam } else { 403189251Ssam wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " 404189251Ssam "be in use - cannot override it"); 405189251Ssam wpa_printf(MSG_INFO, "Delete '%s' manually if it is " 406189251Ssam "not used anymore", fname); 407189251Ssam os_free(fname); 408189251Ssam fname = NULL; 409189251Ssam goto fail; 410189251Ssam } 411189251Ssam } 412189251Ssam 413189251Ssam if (gid_set && chown(fname, -1, gid) < 0) { 414189251Ssam perror("chown[ctrl_interface/ifname]"); 415189251Ssam goto fail; 416189251Ssam } 417189251Ssam 418189251Ssam if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { 419189251Ssam perror("chmod[ctrl_interface/ifname]"); 420189251Ssam goto fail; 421189251Ssam } 422189251Ssam os_free(fname); 423189251Ssam 424252726Srpaulo#ifdef ANDROID 425252726Srpaulohavesock: 426252726Srpaulo#endif /* ANDROID */ 427252726Srpaulo 428252726Srpaulo /* 429252726Srpaulo * Make socket non-blocking so that we don't hang forever if 430252726Srpaulo * target dies unexpectedly. 431252726Srpaulo */ 432252726Srpaulo flags = fcntl(priv->sock, F_GETFL); 433252726Srpaulo if (flags >= 0) { 434252726Srpaulo flags |= O_NONBLOCK; 435252726Srpaulo if (fcntl(priv->sock, F_SETFL, flags) < 0) { 436252726Srpaulo perror("fcntl(ctrl, O_NONBLOCK)"); 437252726Srpaulo /* Not fatal, continue on.*/ 438252726Srpaulo } 439252726Srpaulo } 440252726Srpaulo 441189251Ssam eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, 442189251Ssam wpa_s, priv); 443189251Ssam wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); 444189251Ssam 445189251Ssam os_free(buf); 446189251Ssam return priv; 447189251Ssam 448189251Ssamfail: 449189251Ssam if (priv->sock >= 0) 450189251Ssam close(priv->sock); 451189251Ssam os_free(priv); 452189251Ssam if (fname) { 453189251Ssam unlink(fname); 454189251Ssam os_free(fname); 455189251Ssam } 456189251Ssam os_free(buf); 457189251Ssam return NULL; 458189251Ssam} 459189251Ssam 460189251Ssam 461189251Ssamvoid wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) 462189251Ssam{ 463189251Ssam struct wpa_ctrl_dst *dst, *prev; 464189251Ssam 465189251Ssam if (priv->sock > -1) { 466189251Ssam char *fname; 467189251Ssam char *buf, *dir = NULL, *gid_str = NULL; 468189251Ssam eloop_unregister_read_sock(priv->sock); 469214734Srpaulo if (!dl_list_empty(&priv->ctrl_dst)) { 470189251Ssam /* 471189251Ssam * Wait a second before closing the control socket if 472189251Ssam * there are any attached monitors in order to allow 473189251Ssam * them to receive any pending messages. 474189251Ssam */ 475189251Ssam wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " 476189251Ssam "monitors to receive messages"); 477189251Ssam os_sleep(1, 0); 478189251Ssam } 479189251Ssam close(priv->sock); 480189251Ssam priv->sock = -1; 481189251Ssam fname = wpa_supplicant_ctrl_iface_path(priv->wpa_s); 482189251Ssam if (fname) { 483189251Ssam unlink(fname); 484189251Ssam os_free(fname); 485189251Ssam } 486189251Ssam 487189251Ssam buf = os_strdup(priv->wpa_s->conf->ctrl_interface); 488189251Ssam if (buf == NULL) 489189251Ssam goto free_dst; 490189251Ssam if (os_strncmp(buf, "DIR=", 4) == 0) { 491189251Ssam dir = buf + 4; 492189251Ssam gid_str = os_strstr(dir, " GROUP="); 493189251Ssam if (gid_str) { 494189251Ssam *gid_str = '\0'; 495189251Ssam gid_str += 7; 496189251Ssam } 497189251Ssam } else 498189251Ssam dir = buf; 499189251Ssam 500189251Ssam if (rmdir(dir) < 0) { 501189251Ssam if (errno == ENOTEMPTY) { 502189251Ssam wpa_printf(MSG_DEBUG, "Control interface " 503189251Ssam "directory not empty - leaving it " 504189251Ssam "behind"); 505189251Ssam } else { 506189251Ssam perror("rmdir[ctrl_interface]"); 507189251Ssam } 508189251Ssam } 509189251Ssam os_free(buf); 510189251Ssam } 511189251Ssam 512189251Ssamfree_dst: 513214734Srpaulo dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst, 514214734Srpaulo list) 515214734Srpaulo os_free(dst); 516189251Ssam os_free(priv); 517189251Ssam} 518189251Ssam 519189251Ssam 520189251Ssam/** 521189251Ssam * wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors 522189251Ssam * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init() 523189251Ssam * @level: Priority level of the message 524189251Ssam * @buf: Message data 525189251Ssam * @len: Message length 526189251Ssam * 527189251Ssam * Send a packet to all monitor programs attached to the control interface. 528189251Ssam */ 529189251Ssamstatic void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, 530189251Ssam int level, const char *buf, 531189251Ssam size_t len) 532189251Ssam{ 533189251Ssam struct wpa_ctrl_dst *dst, *next; 534189251Ssam char levelstr[10]; 535189251Ssam int idx, res; 536189251Ssam struct msghdr msg; 537189251Ssam struct iovec io[2]; 538189251Ssam 539214734Srpaulo if (priv->sock < 0 || dl_list_empty(&priv->ctrl_dst)) 540189251Ssam return; 541189251Ssam 542189251Ssam res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); 543189251Ssam if (res < 0 || (size_t) res >= sizeof(levelstr)) 544189251Ssam return; 545189251Ssam io[0].iov_base = levelstr; 546189251Ssam io[0].iov_len = os_strlen(levelstr); 547189251Ssam io[1].iov_base = (char *) buf; 548189251Ssam io[1].iov_len = len; 549189251Ssam os_memset(&msg, 0, sizeof(msg)); 550189251Ssam msg.msg_iov = io; 551189251Ssam msg.msg_iovlen = 2; 552189251Ssam 553189251Ssam idx = 0; 554214734Srpaulo dl_list_for_each_safe(dst, next, &priv->ctrl_dst, struct wpa_ctrl_dst, 555214734Srpaulo list) { 556189251Ssam if (level >= dst->debug_level) { 557189251Ssam wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", 558209158Srpaulo (u8 *) dst->addr.sun_path, dst->addrlen - 559209158Srpaulo offsetof(struct sockaddr_un, sun_path)); 560189251Ssam msg.msg_name = (void *) &dst->addr; 561189251Ssam msg.msg_namelen = dst->addrlen; 562189251Ssam if (sendmsg(priv->sock, &msg, 0) < 0) { 563209158Srpaulo int _errno = errno; 564209158Srpaulo wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " 565209158Srpaulo "%d - %s", 566209158Srpaulo idx, errno, strerror(errno)); 567189251Ssam dst->errors++; 568214734Srpaulo if (dst->errors > 1000 || 569214734Srpaulo (_errno != ENOBUFS && dst->errors > 10) || 570214734Srpaulo _errno == ENOENT) { 571189251Ssam wpa_supplicant_ctrl_iface_detach( 572189251Ssam priv, &dst->addr, 573189251Ssam dst->addrlen); 574189251Ssam } 575189251Ssam } else 576189251Ssam dst->errors = 0; 577189251Ssam } 578189251Ssam idx++; 579189251Ssam } 580189251Ssam} 581189251Ssam 582189251Ssam 583189251Ssamvoid wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) 584189251Ssam{ 585189251Ssam char buf[256]; 586189251Ssam int res; 587189251Ssam struct sockaddr_un from; 588189251Ssam socklen_t fromlen = sizeof(from); 589189251Ssam 590189251Ssam for (;;) { 591189251Ssam wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to " 592189251Ssam "attach", priv->wpa_s->ifname); 593189251Ssam eloop_wait_for_read_sock(priv->sock); 594189251Ssam 595189251Ssam res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0, 596189251Ssam (struct sockaddr *) &from, &fromlen); 597189251Ssam if (res < 0) { 598189251Ssam perror("recvfrom(ctrl_iface)"); 599189251Ssam continue; 600189251Ssam } 601189251Ssam buf[res] = '\0'; 602189251Ssam 603189251Ssam if (os_strcmp(buf, "ATTACH") == 0) { 604189251Ssam /* handle ATTACH signal of first monitor interface */ 605189251Ssam if (!wpa_supplicant_ctrl_iface_attach(priv, &from, 606189251Ssam fromlen)) { 607189251Ssam sendto(priv->sock, "OK\n", 3, 0, 608189251Ssam (struct sockaddr *) &from, fromlen); 609189251Ssam /* OK to continue */ 610189251Ssam return; 611189251Ssam } else { 612189251Ssam sendto(priv->sock, "FAIL\n", 5, 0, 613189251Ssam (struct sockaddr *) &from, fromlen); 614189251Ssam } 615189251Ssam } else { 616189251Ssam /* return FAIL for all other signals */ 617189251Ssam sendto(priv->sock, "FAIL\n", 5, 0, 618189251Ssam (struct sockaddr *) &from, fromlen); 619189251Ssam } 620189251Ssam } 621189251Ssam} 622189251Ssam 623189251Ssam 624189251Ssam/* Global ctrl_iface */ 625189251Ssam 626189251Ssamstruct ctrl_iface_global_priv { 627189251Ssam struct wpa_global *global; 628189251Ssam int sock; 629189251Ssam}; 630189251Ssam 631189251Ssam 632189251Ssamstatic void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, 633189251Ssam void *sock_ctx) 634189251Ssam{ 635189251Ssam struct wpa_global *global = eloop_ctx; 636189251Ssam char buf[256]; 637189251Ssam int res; 638189251Ssam struct sockaddr_un from; 639189251Ssam socklen_t fromlen = sizeof(from); 640189251Ssam char *reply; 641189251Ssam size_t reply_len; 642189251Ssam 643189251Ssam res = recvfrom(sock, buf, sizeof(buf) - 1, 0, 644189251Ssam (struct sockaddr *) &from, &fromlen); 645189251Ssam if (res < 0) { 646189251Ssam perror("recvfrom(ctrl_iface)"); 647189251Ssam return; 648189251Ssam } 649189251Ssam buf[res] = '\0'; 650189251Ssam 651189251Ssam reply = wpa_supplicant_global_ctrl_iface_process(global, buf, 652189251Ssam &reply_len); 653189251Ssam 654189251Ssam if (reply) { 655189251Ssam sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, 656189251Ssam fromlen); 657189251Ssam os_free(reply); 658189251Ssam } else if (reply_len) { 659189251Ssam sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, 660189251Ssam fromlen); 661189251Ssam } 662189251Ssam} 663189251Ssam 664189251Ssam 665189251Ssamstruct ctrl_iface_global_priv * 666189251Ssamwpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) 667189251Ssam{ 668189251Ssam struct ctrl_iface_global_priv *priv; 669189251Ssam struct sockaddr_un addr; 670189251Ssam 671189251Ssam priv = os_zalloc(sizeof(*priv)); 672189251Ssam if (priv == NULL) 673189251Ssam return NULL; 674189251Ssam priv->global = global; 675189251Ssam priv->sock = -1; 676189251Ssam 677189251Ssam if (global->params.ctrl_interface == NULL) 678189251Ssam return priv; 679189251Ssam 680252726Srpaulo#ifdef ANDROID 681252726Srpaulo priv->sock = android_get_control_socket(global->params.ctrl_interface); 682252726Srpaulo if (priv->sock >= 0) 683252726Srpaulo goto havesock; 684252726Srpaulo#endif /* ANDROID */ 685252726Srpaulo 686189251Ssam wpa_printf(MSG_DEBUG, "Global control interface '%s'", 687189251Ssam global->params.ctrl_interface); 688189251Ssam 689189251Ssam priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); 690189251Ssam if (priv->sock < 0) { 691189251Ssam perror("socket(PF_UNIX)"); 692189251Ssam goto fail; 693189251Ssam } 694189251Ssam 695189251Ssam os_memset(&addr, 0, sizeof(addr)); 696214734Srpaulo#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) 697189775Ssam addr.sun_len = sizeof(addr); 698209158Srpaulo#endif /* __FreeBSD__ */ 699189251Ssam addr.sun_family = AF_UNIX; 700189251Ssam os_strlcpy(addr.sun_path, global->params.ctrl_interface, 701189251Ssam sizeof(addr.sun_path)); 702189251Ssam if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 703252726Srpaulo perror("supp-global-ctrl-iface-init (will try fixup): " 704252726Srpaulo "bind(PF_UNIX)"); 705189251Ssam if (connect(priv->sock, (struct sockaddr *) &addr, 706189251Ssam sizeof(addr)) < 0) { 707189251Ssam wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" 708189251Ssam " allow connections - assuming it was left" 709189251Ssam "over from forced program termination"); 710189251Ssam if (unlink(global->params.ctrl_interface) < 0) { 711189251Ssam perror("unlink[ctrl_iface]"); 712189251Ssam wpa_printf(MSG_ERROR, "Could not unlink " 713189251Ssam "existing ctrl_iface socket '%s'", 714189251Ssam global->params.ctrl_interface); 715189251Ssam goto fail; 716189251Ssam } 717189251Ssam if (bind(priv->sock, (struct sockaddr *) &addr, 718189251Ssam sizeof(addr)) < 0) { 719252726Srpaulo perror("supp-glb-iface-init: bind(PF_UNIX)"); 720189251Ssam goto fail; 721189251Ssam } 722189251Ssam wpa_printf(MSG_DEBUG, "Successfully replaced leftover " 723189251Ssam "ctrl_iface socket '%s'", 724189251Ssam global->params.ctrl_interface); 725189251Ssam } else { 726189251Ssam wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " 727189251Ssam "be in use - cannot override it"); 728189251Ssam wpa_printf(MSG_INFO, "Delete '%s' manually if it is " 729189251Ssam "not used anymore", 730189251Ssam global->params.ctrl_interface); 731189251Ssam goto fail; 732189251Ssam } 733189251Ssam } 734189251Ssam 735252726Srpaulo#ifdef ANDROID 736252726Srpaulohavesock: 737252726Srpaulo#endif /* ANDROID */ 738189251Ssam eloop_register_read_sock(priv->sock, 739189251Ssam wpa_supplicant_global_ctrl_iface_receive, 740189251Ssam global, NULL); 741189251Ssam 742189251Ssam return priv; 743189251Ssam 744189251Ssamfail: 745189251Ssam if (priv->sock >= 0) 746189251Ssam close(priv->sock); 747189251Ssam os_free(priv); 748189251Ssam return NULL; 749189251Ssam} 750189251Ssam 751189251Ssam 752189251Ssamvoid 753189251Ssamwpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) 754189251Ssam{ 755189251Ssam if (priv->sock >= 0) { 756189251Ssam eloop_unregister_read_sock(priv->sock); 757189251Ssam close(priv->sock); 758189251Ssam } 759189251Ssam if (priv->global->params.ctrl_interface) 760189251Ssam unlink(priv->global->params.ctrl_interface); 761189251Ssam os_free(priv); 762189251Ssam} 763