1189251Ssam/* 2189251Ssam * WPA Supplicant / privileged helper program 3189251Ssam * Copyright (c) 2007-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#ifdef __linux__ 11189251Ssam#include <fcntl.h> 12189251Ssam#endif /* __linux__ */ 13189251Ssam#include <sys/un.h> 14189251Ssam#include <sys/stat.h> 15189251Ssam 16189251Ssam#include "common.h" 17189251Ssam#include "eloop.h" 18214734Srpaulo#include "common/version.h" 19189251Ssam#include "drivers/driver.h" 20189251Ssam#include "l2_packet/l2_packet.h" 21214734Srpaulo#include "common/privsep_commands.h" 22214734Srpaulo#include "common/ieee802_11_defs.h" 23189251Ssam 24189251Ssam 25189251Ssamstruct wpa_priv_interface { 26189251Ssam struct wpa_priv_interface *next; 27189251Ssam char *driver_name; 28189251Ssam char *ifname; 29189251Ssam char *sock_name; 30189251Ssam int fd; 31189251Ssam 32189251Ssam struct wpa_driver_ops *driver; 33189251Ssam void *drv_priv; 34189251Ssam struct sockaddr_un drv_addr; 35189251Ssam int wpas_registered; 36189251Ssam 37189251Ssam /* TODO: add support for multiple l2 connections */ 38189251Ssam struct l2_packet_data *l2; 39189251Ssam struct sockaddr_un l2_addr; 40189251Ssam}; 41189251Ssam 42189251Ssam 43189251Ssamstatic void wpa_priv_cmd_register(struct wpa_priv_interface *iface, 44189251Ssam struct sockaddr_un *from) 45189251Ssam{ 46189251Ssam if (iface->drv_priv) { 47189251Ssam wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance"); 48189251Ssam if (iface->driver->deinit) 49189251Ssam iface->driver->deinit(iface->drv_priv); 50189251Ssam iface->drv_priv = NULL; 51189251Ssam iface->wpas_registered = 0; 52189251Ssam } 53189251Ssam 54189251Ssam if (iface->l2) { 55189251Ssam wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet " 56189251Ssam "instance"); 57189251Ssam l2_packet_deinit(iface->l2); 58189251Ssam iface->l2 = NULL; 59189251Ssam } 60189251Ssam 61189251Ssam if (iface->driver->init == NULL) 62189251Ssam return; 63189251Ssam 64189251Ssam iface->drv_priv = iface->driver->init(iface, iface->ifname); 65189251Ssam if (iface->drv_priv == NULL) { 66189251Ssam wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper"); 67189251Ssam return; 68189251Ssam } 69189251Ssam 70189251Ssam wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface " 71189251Ssam "'%s'", iface->driver_name, iface->ifname); 72189251Ssam 73189251Ssam os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr)); 74189251Ssam iface->wpas_registered = 1; 75189251Ssam 76189251Ssam if (iface->driver->set_param && 77189251Ssam iface->driver->set_param(iface->drv_priv, NULL) < 0) { 78189251Ssam wpa_printf(MSG_ERROR, "Driver interface rejected param"); 79189251Ssam } 80189251Ssam} 81189251Ssam 82189251Ssam 83189251Ssamstatic void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface, 84189251Ssam struct sockaddr_un *from) 85189251Ssam{ 86189251Ssam if (iface->drv_priv) { 87189251Ssam if (iface->driver->deinit) 88189251Ssam iface->driver->deinit(iface->drv_priv); 89189251Ssam iface->drv_priv = NULL; 90189251Ssam iface->wpas_registered = 0; 91189251Ssam } 92189251Ssam} 93189251Ssam 94189251Ssam 95189251Ssamstatic void wpa_priv_cmd_scan(struct wpa_priv_interface *iface, 96189251Ssam char *buf, size_t len) 97189251Ssam{ 98214734Srpaulo struct wpa_driver_scan_params params; 99214734Srpaulo 100189251Ssam if (iface->drv_priv == NULL) 101189251Ssam return; 102189251Ssam 103214734Srpaulo os_memset(¶ms, 0, sizeof(params)); 104214734Srpaulo if (len) { 105214734Srpaulo params.ssids[0].ssid = (u8 *) buf; 106214734Srpaulo params.ssids[0].ssid_len = len; 107214734Srpaulo params.num_ssids = 1; 108214734Srpaulo } 109214734Srpaulo 110214734Srpaulo if (iface->driver->scan2) 111214734Srpaulo iface->driver->scan2(iface->drv_priv, ¶ms); 112189251Ssam} 113189251Ssam 114189251Ssam 115189251Ssamstatic void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface, 116189251Ssam struct sockaddr_un *from) 117189251Ssam{ 118189251Ssam struct wpa_scan_results *res; 119189251Ssam u8 *buf = NULL, *pos, *end; 120189251Ssam int val; 121189251Ssam size_t i; 122189251Ssam 123189251Ssam res = iface->driver->get_scan_results2(iface->drv_priv); 124189251Ssam if (res == NULL) 125189251Ssam goto fail; 126189251Ssam 127189251Ssam buf = os_malloc(60000); 128189251Ssam if (buf == NULL) 129189251Ssam goto fail; 130189251Ssam pos = buf; 131189251Ssam end = buf + 60000; 132189251Ssam val = res->num; 133189251Ssam os_memcpy(pos, &val, sizeof(int)); 134189251Ssam pos += sizeof(int); 135189251Ssam 136189251Ssam for (i = 0; i < res->num; i++) { 137189251Ssam struct wpa_scan_res *r = res->res[i]; 138189251Ssam val = sizeof(*r) + r->ie_len; 139189251Ssam if (end - pos < (int) sizeof(int) + val) 140189251Ssam break; 141189251Ssam os_memcpy(pos, &val, sizeof(int)); 142189251Ssam pos += sizeof(int); 143189251Ssam os_memcpy(pos, r, val); 144189251Ssam pos += val; 145189251Ssam } 146189251Ssam 147189251Ssam sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from, 148189251Ssam sizeof(*from)); 149189251Ssam 150189251Ssam os_free(buf); 151209158Srpaulo wpa_scan_results_free(res); 152189251Ssam return; 153189251Ssam 154189251Ssamfail: 155189251Ssam os_free(buf); 156209158Srpaulo wpa_scan_results_free(res); 157189251Ssam sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); 158189251Ssam} 159189251Ssam 160189251Ssam 161189251Ssamstatic void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface, 162189251Ssam struct sockaddr_un *from) 163189251Ssam{ 164189251Ssam if (iface->drv_priv == NULL) 165189251Ssam return; 166189251Ssam 167189251Ssam if (iface->driver->get_scan_results2) 168189251Ssam wpa_priv_get_scan_results2(iface, from); 169189251Ssam else 170189251Ssam sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, 171189251Ssam sizeof(*from)); 172189251Ssam} 173189251Ssam 174189251Ssam 175189251Ssamstatic void wpa_priv_cmd_associate(struct wpa_priv_interface *iface, 176189251Ssam void *buf, size_t len) 177189251Ssam{ 178189251Ssam struct wpa_driver_associate_params params; 179189251Ssam struct privsep_cmd_associate *assoc; 180189251Ssam u8 *bssid; 181189251Ssam int res; 182189251Ssam 183189251Ssam if (iface->drv_priv == NULL || iface->driver->associate == NULL) 184189251Ssam return; 185189251Ssam 186189251Ssam if (len < sizeof(*assoc)) { 187189251Ssam wpa_printf(MSG_DEBUG, "Invalid association request"); 188189251Ssam return; 189189251Ssam } 190189251Ssam 191189251Ssam assoc = buf; 192189251Ssam if (sizeof(*assoc) + assoc->wpa_ie_len > len) { 193189251Ssam wpa_printf(MSG_DEBUG, "Association request overflow"); 194189251Ssam return; 195189251Ssam } 196189251Ssam 197189251Ssam os_memset(¶ms, 0, sizeof(params)); 198189251Ssam bssid = assoc->bssid; 199189251Ssam if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5]) 200189251Ssam params.bssid = bssid; 201189251Ssam params.ssid = assoc->ssid; 202189251Ssam if (assoc->ssid_len > 32) 203189251Ssam return; 204189251Ssam params.ssid_len = assoc->ssid_len; 205189251Ssam params.freq = assoc->freq; 206189251Ssam if (assoc->wpa_ie_len) { 207189251Ssam params.wpa_ie = (u8 *) (assoc + 1); 208189251Ssam params.wpa_ie_len = assoc->wpa_ie_len; 209189251Ssam } 210189251Ssam params.pairwise_suite = assoc->pairwise_suite; 211189251Ssam params.group_suite = assoc->group_suite; 212189251Ssam params.key_mgmt_suite = assoc->key_mgmt_suite; 213189251Ssam params.auth_alg = assoc->auth_alg; 214189251Ssam params.mode = assoc->mode; 215189251Ssam 216189251Ssam res = iface->driver->associate(iface->drv_priv, ¶ms); 217189251Ssam wpa_printf(MSG_DEBUG, "drv->associate: res=%d", res); 218189251Ssam} 219189251Ssam 220189251Ssam 221189251Ssamstatic void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface, 222189251Ssam struct sockaddr_un *from) 223189251Ssam{ 224189251Ssam u8 bssid[ETH_ALEN]; 225189251Ssam 226189251Ssam if (iface->drv_priv == NULL) 227189251Ssam goto fail; 228189251Ssam 229189251Ssam if (iface->driver->get_bssid == NULL || 230189251Ssam iface->driver->get_bssid(iface->drv_priv, bssid) < 0) 231189251Ssam goto fail; 232189251Ssam 233189251Ssam sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from, 234189251Ssam sizeof(*from)); 235189251Ssam return; 236189251Ssam 237189251Ssamfail: 238189251Ssam sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); 239189251Ssam} 240189251Ssam 241189251Ssam 242189251Ssamstatic void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface, 243189251Ssam struct sockaddr_un *from) 244189251Ssam{ 245189251Ssam u8 ssid[sizeof(int) + 32]; 246189251Ssam int res; 247189251Ssam 248189251Ssam if (iface->drv_priv == NULL) 249189251Ssam goto fail; 250189251Ssam 251189251Ssam if (iface->driver->get_ssid == NULL) 252189251Ssam goto fail; 253189251Ssam 254189251Ssam res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]); 255189251Ssam if (res < 0 || res > 32) 256189251Ssam goto fail; 257189251Ssam os_memcpy(ssid, &res, sizeof(int)); 258189251Ssam 259189251Ssam sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from, 260189251Ssam sizeof(*from)); 261189251Ssam return; 262189251Ssam 263189251Ssamfail: 264189251Ssam sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); 265189251Ssam} 266189251Ssam 267189251Ssam 268189251Ssamstatic void wpa_priv_cmd_set_key(struct wpa_priv_interface *iface, 269189251Ssam void *buf, size_t len) 270189251Ssam{ 271189251Ssam struct privsep_cmd_set_key *params; 272189251Ssam int res; 273189251Ssam 274189251Ssam if (iface->drv_priv == NULL || iface->driver->set_key == NULL) 275189251Ssam return; 276189251Ssam 277189251Ssam if (len != sizeof(*params)) { 278189251Ssam wpa_printf(MSG_DEBUG, "Invalid set_key request"); 279189251Ssam return; 280189251Ssam } 281189251Ssam 282189251Ssam params = buf; 283189251Ssam 284214734Srpaulo res = iface->driver->set_key(iface->ifname, iface->drv_priv, 285214734Srpaulo params->alg, 286189251Ssam params->addr, params->key_idx, 287189251Ssam params->set_tx, 288189251Ssam params->seq_len ? params->seq : NULL, 289189251Ssam params->seq_len, 290189251Ssam params->key_len ? params->key : NULL, 291189251Ssam params->key_len); 292189251Ssam wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res); 293189251Ssam} 294189251Ssam 295189251Ssam 296189251Ssamstatic void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface, 297189251Ssam struct sockaddr_un *from) 298189251Ssam{ 299189251Ssam struct wpa_driver_capa capa; 300189251Ssam 301189251Ssam if (iface->drv_priv == NULL) 302189251Ssam goto fail; 303189251Ssam 304189251Ssam if (iface->driver->get_capa == NULL || 305189251Ssam iface->driver->get_capa(iface->drv_priv, &capa) < 0) 306189251Ssam goto fail; 307189251Ssam 308189251Ssam sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from, 309189251Ssam sizeof(*from)); 310189251Ssam return; 311189251Ssam 312189251Ssamfail: 313189251Ssam sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); 314189251Ssam} 315189251Ssam 316189251Ssam 317189251Ssamstatic void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf, 318189251Ssam size_t len) 319189251Ssam{ 320189251Ssam struct wpa_priv_interface *iface = ctx; 321189251Ssam struct msghdr msg; 322189251Ssam struct iovec io[2]; 323189251Ssam 324189251Ssam io[0].iov_base = (u8 *) src_addr; 325189251Ssam io[0].iov_len = ETH_ALEN; 326189251Ssam io[1].iov_base = (u8 *) buf; 327189251Ssam io[1].iov_len = len; 328189251Ssam 329189251Ssam os_memset(&msg, 0, sizeof(msg)); 330189251Ssam msg.msg_iov = io; 331189251Ssam msg.msg_iovlen = 2; 332189251Ssam msg.msg_name = &iface->l2_addr; 333189251Ssam msg.msg_namelen = sizeof(iface->l2_addr); 334189251Ssam 335189251Ssam if (sendmsg(iface->fd, &msg, 0) < 0) { 336189251Ssam perror("sendmsg(l2 rx)"); 337189251Ssam } 338189251Ssam} 339189251Ssam 340189251Ssam 341189251Ssamstatic void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface, 342189251Ssam struct sockaddr_un *from, 343189251Ssam void *buf, size_t len) 344189251Ssam{ 345189251Ssam int *reg_cmd = buf; 346189251Ssam u8 own_addr[ETH_ALEN]; 347189251Ssam int res; 348189251Ssam u16 proto; 349189251Ssam 350189251Ssam if (len != 2 * sizeof(int)) { 351189251Ssam wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu", 352189251Ssam (unsigned long) len); 353189251Ssam return; 354189251Ssam } 355189251Ssam 356189251Ssam proto = reg_cmd[0]; 357189251Ssam if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) { 358189251Ssam wpa_printf(MSG_DEBUG, "Refused l2_packet connection for " 359189251Ssam "ethertype 0x%x", proto); 360189251Ssam return; 361189251Ssam } 362189251Ssam 363189251Ssam if (iface->l2) { 364189251Ssam wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet " 365189251Ssam "instance"); 366189251Ssam l2_packet_deinit(iface->l2); 367189251Ssam iface->l2 = NULL; 368189251Ssam } 369189251Ssam 370189251Ssam os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr)); 371189251Ssam 372189251Ssam iface->l2 = l2_packet_init(iface->ifname, NULL, proto, 373189251Ssam wpa_priv_l2_rx, iface, reg_cmd[1]); 374189251Ssam if (iface->l2 == NULL) { 375189251Ssam wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet " 376189251Ssam "instance for protocol %d", proto); 377189251Ssam return; 378189251Ssam } 379189251Ssam 380189251Ssam if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) { 381189251Ssam wpa_printf(MSG_DEBUG, "Failed to get own address from " 382189251Ssam "l2_packet"); 383189251Ssam l2_packet_deinit(iface->l2); 384189251Ssam iface->l2 = NULL; 385189251Ssam return; 386189251Ssam } 387189251Ssam 388189251Ssam res = sendto(iface->fd, own_addr, ETH_ALEN, 0, 389189251Ssam (struct sockaddr *) from, sizeof(*from)); 390189251Ssam wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res); 391189251Ssam} 392189251Ssam 393189251Ssam 394189251Ssamstatic void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface, 395189251Ssam struct sockaddr_un *from) 396189251Ssam{ 397189251Ssam if (iface->l2) { 398189251Ssam l2_packet_deinit(iface->l2); 399189251Ssam iface->l2 = NULL; 400189251Ssam } 401189251Ssam} 402189251Ssam 403189251Ssam 404189251Ssamstatic void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface, 405189251Ssam struct sockaddr_un *from) 406189251Ssam{ 407189251Ssam if (iface->l2) 408189251Ssam l2_packet_notify_auth_start(iface->l2); 409189251Ssam} 410189251Ssam 411189251Ssam 412189251Ssamstatic void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface, 413189251Ssam struct sockaddr_un *from, 414189251Ssam void *buf, size_t len) 415189251Ssam{ 416189251Ssam u8 *dst_addr; 417189251Ssam u16 proto; 418189251Ssam int res; 419189251Ssam 420189251Ssam if (iface->l2 == NULL) 421189251Ssam return; 422189251Ssam 423189251Ssam if (len < ETH_ALEN + 2) { 424189251Ssam wpa_printf(MSG_DEBUG, "Too short L2 send packet (len=%lu)", 425189251Ssam (unsigned long) len); 426189251Ssam return; 427189251Ssam } 428189251Ssam 429189251Ssam dst_addr = buf; 430189251Ssam os_memcpy(&proto, buf + ETH_ALEN, 2); 431189251Ssam 432189251Ssam if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) { 433189251Ssam wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype " 434189251Ssam "0x%x", proto); 435189251Ssam return; 436189251Ssam } 437189251Ssam 438189251Ssam res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2, 439189251Ssam len - ETH_ALEN - 2); 440189251Ssam wpa_printf(MSG_DEBUG, "L2 send: res=%d", res); 441189251Ssam} 442189251Ssam 443189251Ssam 444189251Ssamstatic void wpa_priv_cmd_set_country(struct wpa_priv_interface *iface, 445189251Ssam char *buf) 446189251Ssam{ 447189251Ssam if (iface->drv_priv == NULL || iface->driver->set_country == NULL || 448189251Ssam *buf == '\0') 449189251Ssam return; 450189251Ssam 451189251Ssam iface->driver->set_country(iface->drv_priv, buf); 452189251Ssam} 453189251Ssam 454189251Ssam 455189251Ssamstatic void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx) 456189251Ssam{ 457189251Ssam struct wpa_priv_interface *iface = eloop_ctx; 458189251Ssam char buf[2000], *pos; 459189251Ssam void *cmd_buf; 460189251Ssam size_t cmd_len; 461189251Ssam int res, cmd; 462189251Ssam struct sockaddr_un from; 463189251Ssam socklen_t fromlen = sizeof(from); 464189251Ssam 465189251Ssam res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, 466189251Ssam &fromlen); 467189251Ssam if (res < 0) { 468189251Ssam perror("recvfrom"); 469189251Ssam return; 470189251Ssam } 471189251Ssam 472189251Ssam if (res < (int) sizeof(int)) { 473189251Ssam wpa_printf(MSG_DEBUG, "Too short command (len=%d)", res); 474189251Ssam return; 475189251Ssam } 476189251Ssam 477189251Ssam os_memcpy(&cmd, buf, sizeof(int)); 478189251Ssam wpa_printf(MSG_DEBUG, "Command %d for interface %s", 479189251Ssam cmd, iface->ifname); 480189251Ssam cmd_buf = &buf[sizeof(int)]; 481189251Ssam cmd_len = res - sizeof(int); 482189251Ssam 483189251Ssam switch (cmd) { 484189251Ssam case PRIVSEP_CMD_REGISTER: 485189251Ssam wpa_priv_cmd_register(iface, &from); 486189251Ssam break; 487189251Ssam case PRIVSEP_CMD_UNREGISTER: 488189251Ssam wpa_priv_cmd_unregister(iface, &from); 489189251Ssam break; 490189251Ssam case PRIVSEP_CMD_SCAN: 491189251Ssam wpa_priv_cmd_scan(iface, cmd_buf, cmd_len); 492189251Ssam break; 493189251Ssam case PRIVSEP_CMD_GET_SCAN_RESULTS: 494189251Ssam wpa_priv_cmd_get_scan_results(iface, &from); 495189251Ssam break; 496189251Ssam case PRIVSEP_CMD_ASSOCIATE: 497189251Ssam wpa_priv_cmd_associate(iface, cmd_buf, cmd_len); 498189251Ssam break; 499189251Ssam case PRIVSEP_CMD_GET_BSSID: 500189251Ssam wpa_priv_cmd_get_bssid(iface, &from); 501189251Ssam break; 502189251Ssam case PRIVSEP_CMD_GET_SSID: 503189251Ssam wpa_priv_cmd_get_ssid(iface, &from); 504189251Ssam break; 505189251Ssam case PRIVSEP_CMD_SET_KEY: 506189251Ssam wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len); 507189251Ssam break; 508189251Ssam case PRIVSEP_CMD_GET_CAPA: 509189251Ssam wpa_priv_cmd_get_capa(iface, &from); 510189251Ssam break; 511189251Ssam case PRIVSEP_CMD_L2_REGISTER: 512189251Ssam wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len); 513189251Ssam break; 514189251Ssam case PRIVSEP_CMD_L2_UNREGISTER: 515189251Ssam wpa_priv_cmd_l2_unregister(iface, &from); 516189251Ssam break; 517189251Ssam case PRIVSEP_CMD_L2_NOTIFY_AUTH_START: 518189251Ssam wpa_priv_cmd_l2_notify_auth_start(iface, &from); 519189251Ssam break; 520189251Ssam case PRIVSEP_CMD_L2_SEND: 521189251Ssam wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len); 522189251Ssam break; 523189251Ssam case PRIVSEP_CMD_SET_COUNTRY: 524189251Ssam pos = cmd_buf; 525189251Ssam if (pos + cmd_len >= buf + sizeof(buf)) 526189251Ssam break; 527189251Ssam pos[cmd_len] = '\0'; 528189251Ssam wpa_priv_cmd_set_country(iface, pos); 529189251Ssam break; 530189251Ssam } 531189251Ssam} 532189251Ssam 533189251Ssam 534189251Ssamstatic void wpa_priv_interface_deinit(struct wpa_priv_interface *iface) 535189251Ssam{ 536189251Ssam if (iface->drv_priv && iface->driver->deinit) 537189251Ssam iface->driver->deinit(iface->drv_priv); 538189251Ssam 539189251Ssam if (iface->fd >= 0) { 540189251Ssam eloop_unregister_read_sock(iface->fd); 541189251Ssam close(iface->fd); 542189251Ssam unlink(iface->sock_name); 543189251Ssam } 544189251Ssam 545189251Ssam if (iface->l2) 546189251Ssam l2_packet_deinit(iface->l2); 547189251Ssam 548189251Ssam os_free(iface->ifname); 549189251Ssam os_free(iface->driver_name); 550189251Ssam os_free(iface->sock_name); 551189251Ssam os_free(iface); 552189251Ssam} 553189251Ssam 554189251Ssam 555214734Srpauloextern struct wpa_driver_ops *wpa_drivers[]; 556189251Ssam 557189251Ssamstatic struct wpa_priv_interface * 558189251Ssamwpa_priv_interface_init(const char *dir, const char *params) 559189251Ssam{ 560189251Ssam struct wpa_priv_interface *iface; 561189251Ssam char *pos; 562189251Ssam size_t len; 563189251Ssam struct sockaddr_un addr; 564189251Ssam int i; 565189251Ssam 566189251Ssam pos = os_strchr(params, ':'); 567189251Ssam if (pos == NULL) 568189251Ssam return NULL; 569189251Ssam 570189251Ssam iface = os_zalloc(sizeof(*iface)); 571189251Ssam if (iface == NULL) 572189251Ssam return NULL; 573189251Ssam iface->fd = -1; 574189251Ssam 575189251Ssam len = pos - params; 576189251Ssam iface->driver_name = os_malloc(len + 1); 577189251Ssam if (iface->driver_name == NULL) { 578189251Ssam wpa_priv_interface_deinit(iface); 579189251Ssam return NULL; 580189251Ssam } 581189251Ssam os_memcpy(iface->driver_name, params, len); 582189251Ssam iface->driver_name[len] = '\0'; 583189251Ssam 584214734Srpaulo for (i = 0; wpa_drivers[i]; i++) { 585189251Ssam if (os_strcmp(iface->driver_name, 586214734Srpaulo wpa_drivers[i]->name) == 0) { 587214734Srpaulo iface->driver = wpa_drivers[i]; 588189251Ssam break; 589189251Ssam } 590189251Ssam } 591189251Ssam if (iface->driver == NULL) { 592189251Ssam wpa_printf(MSG_ERROR, "Unsupported driver '%s'", 593189251Ssam iface->driver_name); 594189251Ssam wpa_priv_interface_deinit(iface); 595189251Ssam return NULL; 596189251Ssam } 597189251Ssam 598189251Ssam pos++; 599189251Ssam iface->ifname = os_strdup(pos); 600189251Ssam if (iface->ifname == NULL) { 601189251Ssam wpa_priv_interface_deinit(iface); 602189251Ssam return NULL; 603189251Ssam } 604189251Ssam 605189251Ssam len = os_strlen(dir) + 1 + os_strlen(iface->ifname); 606189251Ssam iface->sock_name = os_malloc(len + 1); 607189251Ssam if (iface->sock_name == NULL) { 608189251Ssam wpa_priv_interface_deinit(iface); 609189251Ssam return NULL; 610189251Ssam } 611189251Ssam 612189251Ssam os_snprintf(iface->sock_name, len + 1, "%s/%s", dir, iface->ifname); 613189251Ssam if (os_strlen(iface->sock_name) >= sizeof(addr.sun_path)) { 614189251Ssam wpa_priv_interface_deinit(iface); 615189251Ssam return NULL; 616189251Ssam } 617189251Ssam 618189251Ssam iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0); 619189251Ssam if (iface->fd < 0) { 620189251Ssam perror("socket(PF_UNIX)"); 621189251Ssam wpa_priv_interface_deinit(iface); 622189251Ssam return NULL; 623189251Ssam } 624189251Ssam 625189251Ssam os_memset(&addr, 0, sizeof(addr)); 626189251Ssam addr.sun_family = AF_UNIX; 627189251Ssam os_strlcpy(addr.sun_path, iface->sock_name, sizeof(addr.sun_path)); 628189251Ssam 629189251Ssam if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 630189251Ssam wpa_printf(MSG_DEBUG, "bind(PF_UNIX) failed: %s", 631189251Ssam strerror(errno)); 632189251Ssam if (connect(iface->fd, (struct sockaddr *) &addr, 633189251Ssam sizeof(addr)) < 0) { 634189251Ssam wpa_printf(MSG_DEBUG, "Socket exists, but does not " 635189251Ssam "allow connections - assuming it was " 636189251Ssam "leftover from forced program termination"); 637189251Ssam if (unlink(iface->sock_name) < 0) { 638189251Ssam perror("unlink[ctrl_iface]"); 639189251Ssam wpa_printf(MSG_ERROR, "Could not unlink " 640189251Ssam "existing ctrl_iface socket '%s'", 641189251Ssam iface->sock_name); 642189251Ssam goto fail; 643189251Ssam } 644189251Ssam if (bind(iface->fd, (struct sockaddr *) &addr, 645189251Ssam sizeof(addr)) < 0) { 646252726Srpaulo perror("wpa-priv-iface-init: bind(PF_UNIX)"); 647189251Ssam goto fail; 648189251Ssam } 649189251Ssam wpa_printf(MSG_DEBUG, "Successfully replaced leftover " 650189251Ssam "socket '%s'", iface->sock_name); 651189251Ssam } else { 652189251Ssam wpa_printf(MSG_INFO, "Socket exists and seems to be " 653189251Ssam "in use - cannot override it"); 654189251Ssam wpa_printf(MSG_INFO, "Delete '%s' manually if it is " 655189251Ssam "not used anymore", iface->sock_name); 656189251Ssam goto fail; 657189251Ssam } 658189251Ssam } 659189251Ssam 660189251Ssam if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { 661189251Ssam perror("chmod"); 662189251Ssam goto fail; 663189251Ssam } 664189251Ssam 665189251Ssam eloop_register_read_sock(iface->fd, wpa_priv_receive, iface, NULL); 666189251Ssam 667189251Ssam return iface; 668189251Ssam 669189251Ssamfail: 670189251Ssam wpa_priv_interface_deinit(iface); 671189251Ssam return NULL; 672189251Ssam} 673189251Ssam 674189251Ssam 675189251Ssamstatic int wpa_priv_send_event(struct wpa_priv_interface *iface, int event, 676189251Ssam const void *data, size_t data_len) 677189251Ssam{ 678189251Ssam struct msghdr msg; 679189251Ssam struct iovec io[2]; 680189251Ssam 681189251Ssam io[0].iov_base = &event; 682189251Ssam io[0].iov_len = sizeof(event); 683189251Ssam io[1].iov_base = (u8 *) data; 684189251Ssam io[1].iov_len = data_len; 685189251Ssam 686189251Ssam os_memset(&msg, 0, sizeof(msg)); 687189251Ssam msg.msg_iov = io; 688189251Ssam msg.msg_iovlen = data ? 2 : 1; 689189251Ssam msg.msg_name = &iface->drv_addr; 690189251Ssam msg.msg_namelen = sizeof(iface->drv_addr); 691189251Ssam 692189251Ssam if (sendmsg(iface->fd, &msg, 0) < 0) { 693189251Ssam perror("sendmsg(wpas_socket)"); 694189251Ssam return -1; 695189251Ssam } 696189251Ssam 697189251Ssam return 0; 698189251Ssam} 699189251Ssam 700189251Ssam 701189251Ssamstatic void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event, 702189251Ssam union wpa_event_data *data) 703189251Ssam{ 704189251Ssam size_t buflen = 3 * sizeof(int); 705189251Ssam u8 *buf, *pos; 706189251Ssam int len; 707189251Ssam 708189251Ssam if (data) { 709189251Ssam buflen += data->assoc_info.req_ies_len + 710189251Ssam data->assoc_info.resp_ies_len + 711189251Ssam data->assoc_info.beacon_ies_len; 712189251Ssam } 713189251Ssam 714189251Ssam buf = os_malloc(buflen); 715189251Ssam if (buf == NULL) 716189251Ssam return; 717189251Ssam 718189251Ssam pos = buf; 719189251Ssam 720189251Ssam if (data && data->assoc_info.req_ies) { 721189251Ssam len = data->assoc_info.req_ies_len; 722189251Ssam os_memcpy(pos, &len, sizeof(int)); 723189251Ssam pos += sizeof(int); 724189251Ssam os_memcpy(pos, data->assoc_info.req_ies, len); 725189251Ssam pos += len; 726189251Ssam } else { 727189251Ssam len = 0; 728189251Ssam os_memcpy(pos, &len, sizeof(int)); 729189251Ssam pos += sizeof(int); 730189251Ssam } 731189251Ssam 732189251Ssam if (data && data->assoc_info.resp_ies) { 733189251Ssam len = data->assoc_info.resp_ies_len; 734189251Ssam os_memcpy(pos, &len, sizeof(int)); 735189251Ssam pos += sizeof(int); 736189251Ssam os_memcpy(pos, data->assoc_info.resp_ies, len); 737189251Ssam pos += len; 738189251Ssam } else { 739189251Ssam len = 0; 740189251Ssam os_memcpy(pos, &len, sizeof(int)); 741189251Ssam pos += sizeof(int); 742189251Ssam } 743189251Ssam 744189251Ssam if (data && data->assoc_info.beacon_ies) { 745189251Ssam len = data->assoc_info.beacon_ies_len; 746189251Ssam os_memcpy(pos, &len, sizeof(int)); 747189251Ssam pos += sizeof(int); 748189251Ssam os_memcpy(pos, data->assoc_info.beacon_ies, len); 749189251Ssam pos += len; 750189251Ssam } else { 751189251Ssam len = 0; 752189251Ssam os_memcpy(pos, &len, sizeof(int)); 753189251Ssam pos += sizeof(int); 754189251Ssam } 755189251Ssam 756189251Ssam wpa_priv_send_event(iface, event, buf, buflen); 757189251Ssam 758189251Ssam os_free(buf); 759189251Ssam} 760189251Ssam 761189251Ssam 762189251Ssamstatic void wpa_priv_send_interface_status(struct wpa_priv_interface *iface, 763189251Ssam union wpa_event_data *data) 764189251Ssam{ 765189251Ssam int ievent; 766189251Ssam size_t len, maxlen; 767189251Ssam u8 *buf; 768189251Ssam char *ifname; 769189251Ssam 770189251Ssam if (data == NULL) 771189251Ssam return; 772189251Ssam 773189251Ssam ievent = data->interface_status.ievent; 774189251Ssam maxlen = sizeof(data->interface_status.ifname); 775189251Ssam ifname = data->interface_status.ifname; 776189251Ssam for (len = 0; len < maxlen && ifname[len]; len++) 777189251Ssam ; 778189251Ssam 779189251Ssam buf = os_malloc(sizeof(int) + len); 780189251Ssam if (buf == NULL) 781189251Ssam return; 782189251Ssam 783189251Ssam os_memcpy(buf, &ievent, sizeof(int)); 784189251Ssam os_memcpy(buf + sizeof(int), ifname, len); 785189251Ssam 786189251Ssam wpa_priv_send_event(iface, PRIVSEP_EVENT_INTERFACE_STATUS, 787189251Ssam buf, sizeof(int) + len); 788189251Ssam 789189251Ssam os_free(buf); 790189251Ssam 791189251Ssam} 792189251Ssam 793189251Ssam 794189251Ssamstatic void wpa_priv_send_ft_response(struct wpa_priv_interface *iface, 795189251Ssam union wpa_event_data *data) 796189251Ssam{ 797189251Ssam size_t len; 798189251Ssam u8 *buf, *pos; 799189251Ssam 800189251Ssam if (data == NULL || data->ft_ies.ies == NULL) 801189251Ssam return; 802189251Ssam 803189251Ssam len = sizeof(int) + ETH_ALEN + data->ft_ies.ies_len; 804189251Ssam buf = os_malloc(len); 805189251Ssam if (buf == NULL) 806189251Ssam return; 807189251Ssam 808189251Ssam pos = buf; 809189251Ssam os_memcpy(pos, &data->ft_ies.ft_action, sizeof(int)); 810189251Ssam pos += sizeof(int); 811189251Ssam os_memcpy(pos, data->ft_ies.target_ap, ETH_ALEN); 812189251Ssam pos += ETH_ALEN; 813189251Ssam os_memcpy(pos, data->ft_ies.ies, data->ft_ies.ies_len); 814189251Ssam 815189251Ssam wpa_priv_send_event(iface, PRIVSEP_EVENT_FT_RESPONSE, buf, len); 816189251Ssam 817189251Ssam os_free(buf); 818189251Ssam 819189251Ssam} 820189251Ssam 821189251Ssam 822252726Srpaulovoid wpa_supplicant_event(void *ctx, enum wpa_event_type event, 823189251Ssam union wpa_event_data *data) 824189251Ssam{ 825189251Ssam struct wpa_priv_interface *iface = ctx; 826189251Ssam 827189251Ssam wpa_printf(MSG_DEBUG, "%s - event=%d", __func__, event); 828189251Ssam 829189251Ssam if (!iface->wpas_registered) { 830189251Ssam wpa_printf(MSG_DEBUG, "Driver event received, but " 831189251Ssam "wpa_supplicant not registered"); 832189251Ssam return; 833189251Ssam } 834189251Ssam 835189251Ssam switch (event) { 836189251Ssam case EVENT_ASSOC: 837189251Ssam wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOC, data); 838189251Ssam break; 839189251Ssam case EVENT_DISASSOC: 840189251Ssam wpa_priv_send_event(iface, PRIVSEP_EVENT_DISASSOC, NULL, 0); 841189251Ssam break; 842189251Ssam case EVENT_ASSOCINFO: 843189251Ssam if (data == NULL) 844189251Ssam return; 845189251Ssam wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOCINFO, data); 846189251Ssam break; 847189251Ssam case EVENT_MICHAEL_MIC_FAILURE: 848189251Ssam if (data == NULL) 849189251Ssam return; 850189251Ssam wpa_priv_send_event(iface, PRIVSEP_EVENT_MICHAEL_MIC_FAILURE, 851189251Ssam &data->michael_mic_failure.unicast, 852189251Ssam sizeof(int)); 853189251Ssam break; 854189251Ssam case EVENT_SCAN_RESULTS: 855189251Ssam wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL, 856189251Ssam 0); 857189251Ssam break; 858189251Ssam case EVENT_INTERFACE_STATUS: 859189251Ssam wpa_priv_send_interface_status(iface, data); 860189251Ssam break; 861189251Ssam case EVENT_PMKID_CANDIDATE: 862189251Ssam if (data == NULL) 863189251Ssam return; 864189251Ssam wpa_priv_send_event(iface, PRIVSEP_EVENT_PMKID_CANDIDATE, 865189251Ssam &data->pmkid_candidate, 866189251Ssam sizeof(struct pmkid_candidate)); 867189251Ssam break; 868189251Ssam case EVENT_STKSTART: 869189251Ssam if (data == NULL) 870189251Ssam return; 871189251Ssam wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART, 872189251Ssam &data->stkstart.peer, ETH_ALEN); 873189251Ssam break; 874189251Ssam case EVENT_FT_RESPONSE: 875189251Ssam wpa_priv_send_ft_response(iface, data); 876189251Ssam break; 877189251Ssam default: 878189251Ssam wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO", 879189251Ssam event); 880189251Ssam break; 881189251Ssam } 882189251Ssam} 883189251Ssam 884189251Ssam 885189251Ssamvoid wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, 886189251Ssam const u8 *buf, size_t len) 887189251Ssam{ 888189251Ssam struct wpa_priv_interface *iface = ctx; 889189251Ssam struct msghdr msg; 890189251Ssam struct iovec io[3]; 891189251Ssam int event = PRIVSEP_EVENT_RX_EAPOL; 892189251Ssam 893189251Ssam wpa_printf(MSG_DEBUG, "RX EAPOL from driver"); 894189251Ssam io[0].iov_base = &event; 895189251Ssam io[0].iov_len = sizeof(event); 896189251Ssam io[1].iov_base = (u8 *) src_addr; 897189251Ssam io[1].iov_len = ETH_ALEN; 898189251Ssam io[2].iov_base = (u8 *) buf; 899189251Ssam io[2].iov_len = len; 900189251Ssam 901189251Ssam os_memset(&msg, 0, sizeof(msg)); 902189251Ssam msg.msg_iov = io; 903189251Ssam msg.msg_iovlen = 3; 904189251Ssam msg.msg_name = &iface->drv_addr; 905189251Ssam msg.msg_namelen = sizeof(iface->drv_addr); 906189251Ssam 907189251Ssam if (sendmsg(iface->fd, &msg, 0) < 0) 908189251Ssam perror("sendmsg(wpas_socket)"); 909189251Ssam} 910189251Ssam 911189251Ssam 912252726Srpaulostatic void wpa_priv_terminate(int sig, void *signal_ctx) 913189251Ssam{ 914189251Ssam wpa_printf(MSG_DEBUG, "wpa_priv termination requested"); 915189251Ssam eloop_terminate(); 916189251Ssam} 917189251Ssam 918189251Ssam 919189251Ssamstatic void wpa_priv_fd_workaround(void) 920189251Ssam{ 921189251Ssam#ifdef __linux__ 922189251Ssam int s, i; 923189251Ssam /* When started from pcmcia-cs scripts, wpa_supplicant might start with 924189251Ssam * fd 0, 1, and 2 closed. This will cause some issues because many 925189251Ssam * places in wpa_supplicant are still printing out to stdout. As a 926189251Ssam * workaround, make sure that fd's 0, 1, and 2 are not used for other 927189251Ssam * sockets. */ 928189251Ssam for (i = 0; i < 3; i++) { 929189251Ssam s = open("/dev/null", O_RDWR); 930189251Ssam if (s > 2) { 931189251Ssam close(s); 932189251Ssam break; 933189251Ssam } 934189251Ssam } 935189251Ssam#endif /* __linux__ */ 936189251Ssam} 937189251Ssam 938189251Ssam 939189251Ssamstatic void usage(void) 940189251Ssam{ 941189251Ssam printf("wpa_priv v" VERSION_STR "\n" 942189251Ssam "Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> and " 943189251Ssam "contributors\n" 944189251Ssam "\n" 945189251Ssam "usage:\n" 946189251Ssam " wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> " 947189251Ssam "[driver:ifname ...]\n"); 948189251Ssam} 949189251Ssam 950189251Ssam 951189251Ssamextern int wpa_debug_level; 952189251Ssam 953189251Ssamint main(int argc, char *argv[]) 954189251Ssam{ 955189251Ssam int c, i; 956189251Ssam int ret = -1; 957189251Ssam char *pid_file = NULL; 958189251Ssam int daemonize = 0; 959189251Ssam char *ctrl_dir = "/var/run/wpa_priv"; 960189251Ssam struct wpa_priv_interface *interfaces = NULL, *iface; 961189251Ssam 962189251Ssam if (os_program_init()) 963189251Ssam return -1; 964189251Ssam 965189251Ssam wpa_priv_fd_workaround(); 966189251Ssam 967189251Ssam for (;;) { 968189251Ssam c = getopt(argc, argv, "Bc:dP:"); 969189251Ssam if (c < 0) 970189251Ssam break; 971189251Ssam switch (c) { 972189251Ssam case 'B': 973189251Ssam daemonize++; 974189251Ssam break; 975189251Ssam case 'c': 976189251Ssam ctrl_dir = optarg; 977189251Ssam break; 978189251Ssam case 'd': 979189251Ssam wpa_debug_level--; 980189251Ssam break; 981189251Ssam case 'P': 982189251Ssam pid_file = os_rel2abs_path(optarg); 983189251Ssam break; 984189251Ssam default: 985189251Ssam usage(); 986189251Ssam goto out; 987189251Ssam } 988189251Ssam } 989189251Ssam 990189251Ssam if (optind >= argc) { 991189251Ssam usage(); 992189251Ssam goto out; 993189251Ssam } 994189251Ssam 995189251Ssam wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir); 996189251Ssam 997214734Srpaulo if (eloop_init()) { 998189251Ssam wpa_printf(MSG_ERROR, "Failed to initialize event loop"); 999189251Ssam goto out; 1000189251Ssam } 1001189251Ssam 1002189251Ssam for (i = optind; i < argc; i++) { 1003189251Ssam wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]); 1004189251Ssam iface = wpa_priv_interface_init(ctrl_dir, argv[i]); 1005189251Ssam if (iface == NULL) 1006189251Ssam goto out; 1007189251Ssam iface->next = interfaces; 1008189251Ssam interfaces = iface; 1009189251Ssam } 1010189251Ssam 1011189251Ssam if (daemonize && os_daemonize(pid_file)) 1012189251Ssam goto out; 1013189251Ssam 1014189251Ssam eloop_register_signal_terminate(wpa_priv_terminate, NULL); 1015189251Ssam eloop_run(); 1016189251Ssam 1017189251Ssam ret = 0; 1018189251Ssam 1019189251Ssamout: 1020189251Ssam iface = interfaces; 1021189251Ssam while (iface) { 1022189251Ssam struct wpa_priv_interface *prev = iface; 1023189251Ssam iface = iface->next; 1024189251Ssam wpa_priv_interface_deinit(prev); 1025189251Ssam } 1026189251Ssam 1027189251Ssam eloop_destroy(); 1028189251Ssam 1029189251Ssam os_daemonize_terminate(pid_file); 1030189251Ssam os_free(pid_file); 1031189251Ssam os_program_deinit(); 1032189251Ssam 1033189251Ssam return ret; 1034189251Ssam} 1035