1*f05cddf9SRui Paulo /* 2*f05cddf9SRui Paulo * WPA Supplicant - privilege separated driver interface 3*f05cddf9SRui Paulo * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> 4*f05cddf9SRui Paulo * 5*f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6*f05cddf9SRui Paulo * See README for more details. 7*f05cddf9SRui Paulo */ 8*f05cddf9SRui Paulo 9*f05cddf9SRui Paulo #include "includes.h" 10*f05cddf9SRui Paulo #include <sys/un.h> 11*f05cddf9SRui Paulo 12*f05cddf9SRui Paulo #include "common.h" 13*f05cddf9SRui Paulo #include "driver.h" 14*f05cddf9SRui Paulo #include "eloop.h" 15*f05cddf9SRui Paulo #include "common/privsep_commands.h" 16*f05cddf9SRui Paulo 17*f05cddf9SRui Paulo 18*f05cddf9SRui Paulo struct wpa_driver_privsep_data { 19*f05cddf9SRui Paulo void *ctx; 20*f05cddf9SRui Paulo u8 own_addr[ETH_ALEN]; 21*f05cddf9SRui Paulo int priv_socket; 22*f05cddf9SRui Paulo char *own_socket_path; 23*f05cddf9SRui Paulo int cmd_socket; 24*f05cddf9SRui Paulo char *own_cmd_path; 25*f05cddf9SRui Paulo struct sockaddr_un priv_addr; 26*f05cddf9SRui Paulo char ifname[16]; 27*f05cddf9SRui Paulo }; 28*f05cddf9SRui Paulo 29*f05cddf9SRui Paulo 30*f05cddf9SRui Paulo static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd) 31*f05cddf9SRui Paulo { 32*f05cddf9SRui Paulo int res; 33*f05cddf9SRui Paulo 34*f05cddf9SRui Paulo res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0, 35*f05cddf9SRui Paulo (struct sockaddr *) &drv->priv_addr, 36*f05cddf9SRui Paulo sizeof(drv->priv_addr)); 37*f05cddf9SRui Paulo if (res < 0) 38*f05cddf9SRui Paulo perror("sendto"); 39*f05cddf9SRui Paulo return res < 0 ? -1 : 0; 40*f05cddf9SRui Paulo } 41*f05cddf9SRui Paulo 42*f05cddf9SRui Paulo 43*f05cddf9SRui Paulo static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, 44*f05cddf9SRui Paulo const void *data, size_t data_len, 45*f05cddf9SRui Paulo void *reply, size_t *reply_len) 46*f05cddf9SRui Paulo { 47*f05cddf9SRui Paulo struct msghdr msg; 48*f05cddf9SRui Paulo struct iovec io[2]; 49*f05cddf9SRui Paulo 50*f05cddf9SRui Paulo io[0].iov_base = &cmd; 51*f05cddf9SRui Paulo io[0].iov_len = sizeof(cmd); 52*f05cddf9SRui Paulo io[1].iov_base = (u8 *) data; 53*f05cddf9SRui Paulo io[1].iov_len = data_len; 54*f05cddf9SRui Paulo 55*f05cddf9SRui Paulo os_memset(&msg, 0, sizeof(msg)); 56*f05cddf9SRui Paulo msg.msg_iov = io; 57*f05cddf9SRui Paulo msg.msg_iovlen = data ? 2 : 1; 58*f05cddf9SRui Paulo msg.msg_name = &drv->priv_addr; 59*f05cddf9SRui Paulo msg.msg_namelen = sizeof(drv->priv_addr); 60*f05cddf9SRui Paulo 61*f05cddf9SRui Paulo if (sendmsg(drv->cmd_socket, &msg, 0) < 0) { 62*f05cddf9SRui Paulo perror("sendmsg(cmd_socket)"); 63*f05cddf9SRui Paulo return -1; 64*f05cddf9SRui Paulo } 65*f05cddf9SRui Paulo 66*f05cddf9SRui Paulo if (reply) { 67*f05cddf9SRui Paulo fd_set rfds; 68*f05cddf9SRui Paulo struct timeval tv; 69*f05cddf9SRui Paulo int res; 70*f05cddf9SRui Paulo 71*f05cddf9SRui Paulo FD_ZERO(&rfds); 72*f05cddf9SRui Paulo FD_SET(drv->cmd_socket, &rfds); 73*f05cddf9SRui Paulo tv.tv_sec = 5; 74*f05cddf9SRui Paulo tv.tv_usec = 0; 75*f05cddf9SRui Paulo res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv); 76*f05cddf9SRui Paulo if (res < 0 && errno != EINTR) { 77*f05cddf9SRui Paulo perror("select"); 78*f05cddf9SRui Paulo return -1; 79*f05cddf9SRui Paulo } 80*f05cddf9SRui Paulo 81*f05cddf9SRui Paulo if (FD_ISSET(drv->cmd_socket, &rfds)) { 82*f05cddf9SRui Paulo res = recv(drv->cmd_socket, reply, *reply_len, 0); 83*f05cddf9SRui Paulo if (res < 0) { 84*f05cddf9SRui Paulo perror("recv"); 85*f05cddf9SRui Paulo return -1; 86*f05cddf9SRui Paulo } 87*f05cddf9SRui Paulo *reply_len = res; 88*f05cddf9SRui Paulo } else { 89*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting " 90*f05cddf9SRui Paulo "for reply (cmd=%d)", cmd); 91*f05cddf9SRui Paulo return -1; 92*f05cddf9SRui Paulo } 93*f05cddf9SRui Paulo } 94*f05cddf9SRui Paulo 95*f05cddf9SRui Paulo return 0; 96*f05cddf9SRui Paulo } 97*f05cddf9SRui Paulo 98*f05cddf9SRui Paulo 99*f05cddf9SRui Paulo static int wpa_driver_privsep_scan(void *priv, 100*f05cddf9SRui Paulo struct wpa_driver_scan_params *params) 101*f05cddf9SRui Paulo { 102*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 103*f05cddf9SRui Paulo const u8 *ssid = params->ssids[0].ssid; 104*f05cddf9SRui Paulo size_t ssid_len = params->ssids[0].ssid_len; 105*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); 106*f05cddf9SRui Paulo return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len, 107*f05cddf9SRui Paulo NULL, NULL); 108*f05cddf9SRui Paulo } 109*f05cddf9SRui Paulo 110*f05cddf9SRui Paulo 111*f05cddf9SRui Paulo static struct wpa_scan_results * 112*f05cddf9SRui Paulo wpa_driver_privsep_get_scan_results2(void *priv) 113*f05cddf9SRui Paulo { 114*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 115*f05cddf9SRui Paulo int res, num; 116*f05cddf9SRui Paulo u8 *buf, *pos, *end; 117*f05cddf9SRui Paulo size_t reply_len = 60000; 118*f05cddf9SRui Paulo struct wpa_scan_results *results; 119*f05cddf9SRui Paulo struct wpa_scan_res *r; 120*f05cddf9SRui Paulo 121*f05cddf9SRui Paulo buf = os_malloc(reply_len); 122*f05cddf9SRui Paulo if (buf == NULL) 123*f05cddf9SRui Paulo return NULL; 124*f05cddf9SRui Paulo res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS, 125*f05cddf9SRui Paulo NULL, 0, buf, &reply_len); 126*f05cddf9SRui Paulo if (res < 0) { 127*f05cddf9SRui Paulo os_free(buf); 128*f05cddf9SRui Paulo return NULL; 129*f05cddf9SRui Paulo } 130*f05cddf9SRui Paulo 131*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results", 132*f05cddf9SRui Paulo (unsigned long) reply_len); 133*f05cddf9SRui Paulo if (reply_len < sizeof(int)) { 134*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu", 135*f05cddf9SRui Paulo (unsigned long) reply_len); 136*f05cddf9SRui Paulo os_free(buf); 137*f05cddf9SRui Paulo return NULL; 138*f05cddf9SRui Paulo } 139*f05cddf9SRui Paulo 140*f05cddf9SRui Paulo pos = buf; 141*f05cddf9SRui Paulo end = buf + reply_len; 142*f05cddf9SRui Paulo os_memcpy(&num, pos, sizeof(int)); 143*f05cddf9SRui Paulo if (num < 0 || num > 1000) { 144*f05cddf9SRui Paulo os_free(buf); 145*f05cddf9SRui Paulo return NULL; 146*f05cddf9SRui Paulo } 147*f05cddf9SRui Paulo pos += sizeof(int); 148*f05cddf9SRui Paulo 149*f05cddf9SRui Paulo results = os_zalloc(sizeof(*results)); 150*f05cddf9SRui Paulo if (results == NULL) { 151*f05cddf9SRui Paulo os_free(buf); 152*f05cddf9SRui Paulo return NULL; 153*f05cddf9SRui Paulo } 154*f05cddf9SRui Paulo 155*f05cddf9SRui Paulo results->res = os_calloc(num, sizeof(struct wpa_scan_res *)); 156*f05cddf9SRui Paulo if (results->res == NULL) { 157*f05cddf9SRui Paulo os_free(results); 158*f05cddf9SRui Paulo os_free(buf); 159*f05cddf9SRui Paulo return NULL; 160*f05cddf9SRui Paulo } 161*f05cddf9SRui Paulo 162*f05cddf9SRui Paulo while (results->num < (size_t) num && pos + sizeof(int) < end) { 163*f05cddf9SRui Paulo int len; 164*f05cddf9SRui Paulo os_memcpy(&len, pos, sizeof(int)); 165*f05cddf9SRui Paulo pos += sizeof(int); 166*f05cddf9SRui Paulo if (len < 0 || len > 10000 || pos + len > end) 167*f05cddf9SRui Paulo break; 168*f05cddf9SRui Paulo 169*f05cddf9SRui Paulo r = os_malloc(len); 170*f05cddf9SRui Paulo if (r == NULL) 171*f05cddf9SRui Paulo break; 172*f05cddf9SRui Paulo os_memcpy(r, pos, len); 173*f05cddf9SRui Paulo pos += len; 174*f05cddf9SRui Paulo if (sizeof(*r) + r->ie_len > (size_t) len) { 175*f05cddf9SRui Paulo os_free(r); 176*f05cddf9SRui Paulo break; 177*f05cddf9SRui Paulo } 178*f05cddf9SRui Paulo 179*f05cddf9SRui Paulo results->res[results->num++] = r; 180*f05cddf9SRui Paulo } 181*f05cddf9SRui Paulo 182*f05cddf9SRui Paulo os_free(buf); 183*f05cddf9SRui Paulo return results; 184*f05cddf9SRui Paulo } 185*f05cddf9SRui Paulo 186*f05cddf9SRui Paulo 187*f05cddf9SRui Paulo static int wpa_driver_privsep_set_key(const char *ifname, void *priv, 188*f05cddf9SRui Paulo enum wpa_alg alg, const u8 *addr, 189*f05cddf9SRui Paulo int key_idx, int set_tx, 190*f05cddf9SRui Paulo const u8 *seq, size_t seq_len, 191*f05cddf9SRui Paulo const u8 *key, size_t key_len) 192*f05cddf9SRui Paulo { 193*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 194*f05cddf9SRui Paulo struct privsep_cmd_set_key cmd; 195*f05cddf9SRui Paulo 196*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", 197*f05cddf9SRui Paulo __func__, priv, alg, key_idx, set_tx); 198*f05cddf9SRui Paulo 199*f05cddf9SRui Paulo os_memset(&cmd, 0, sizeof(cmd)); 200*f05cddf9SRui Paulo cmd.alg = alg; 201*f05cddf9SRui Paulo if (addr) 202*f05cddf9SRui Paulo os_memcpy(cmd.addr, addr, ETH_ALEN); 203*f05cddf9SRui Paulo else 204*f05cddf9SRui Paulo os_memset(cmd.addr, 0xff, ETH_ALEN); 205*f05cddf9SRui Paulo cmd.key_idx = key_idx; 206*f05cddf9SRui Paulo cmd.set_tx = set_tx; 207*f05cddf9SRui Paulo if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) { 208*f05cddf9SRui Paulo os_memcpy(cmd.seq, seq, seq_len); 209*f05cddf9SRui Paulo cmd.seq_len = seq_len; 210*f05cddf9SRui Paulo } 211*f05cddf9SRui Paulo if (key && key_len > 0 && key_len < sizeof(cmd.key)) { 212*f05cddf9SRui Paulo os_memcpy(cmd.key, key, key_len); 213*f05cddf9SRui Paulo cmd.key_len = key_len; 214*f05cddf9SRui Paulo } 215*f05cddf9SRui Paulo 216*f05cddf9SRui Paulo return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd), 217*f05cddf9SRui Paulo NULL, NULL); 218*f05cddf9SRui Paulo } 219*f05cddf9SRui Paulo 220*f05cddf9SRui Paulo 221*f05cddf9SRui Paulo static int wpa_driver_privsep_associate( 222*f05cddf9SRui Paulo void *priv, struct wpa_driver_associate_params *params) 223*f05cddf9SRui Paulo { 224*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 225*f05cddf9SRui Paulo struct privsep_cmd_associate *data; 226*f05cddf9SRui Paulo int res; 227*f05cddf9SRui Paulo size_t buflen; 228*f05cddf9SRui Paulo 229*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " 230*f05cddf9SRui Paulo "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", 231*f05cddf9SRui Paulo __func__, priv, params->freq, params->pairwise_suite, 232*f05cddf9SRui Paulo params->group_suite, params->key_mgmt_suite, 233*f05cddf9SRui Paulo params->auth_alg, params->mode); 234*f05cddf9SRui Paulo 235*f05cddf9SRui Paulo buflen = sizeof(*data) + params->wpa_ie_len; 236*f05cddf9SRui Paulo data = os_zalloc(buflen); 237*f05cddf9SRui Paulo if (data == NULL) 238*f05cddf9SRui Paulo return -1; 239*f05cddf9SRui Paulo 240*f05cddf9SRui Paulo if (params->bssid) 241*f05cddf9SRui Paulo os_memcpy(data->bssid, params->bssid, ETH_ALEN); 242*f05cddf9SRui Paulo os_memcpy(data->ssid, params->ssid, params->ssid_len); 243*f05cddf9SRui Paulo data->ssid_len = params->ssid_len; 244*f05cddf9SRui Paulo data->freq = params->freq; 245*f05cddf9SRui Paulo data->pairwise_suite = params->pairwise_suite; 246*f05cddf9SRui Paulo data->group_suite = params->group_suite; 247*f05cddf9SRui Paulo data->key_mgmt_suite = params->key_mgmt_suite; 248*f05cddf9SRui Paulo data->auth_alg = params->auth_alg; 249*f05cddf9SRui Paulo data->mode = params->mode; 250*f05cddf9SRui Paulo data->wpa_ie_len = params->wpa_ie_len; 251*f05cddf9SRui Paulo if (params->wpa_ie) 252*f05cddf9SRui Paulo os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len); 253*f05cddf9SRui Paulo /* TODO: add support for other assoc parameters */ 254*f05cddf9SRui Paulo 255*f05cddf9SRui Paulo res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen, 256*f05cddf9SRui Paulo NULL, NULL); 257*f05cddf9SRui Paulo os_free(data); 258*f05cddf9SRui Paulo 259*f05cddf9SRui Paulo return res; 260*f05cddf9SRui Paulo } 261*f05cddf9SRui Paulo 262*f05cddf9SRui Paulo 263*f05cddf9SRui Paulo static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid) 264*f05cddf9SRui Paulo { 265*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 266*f05cddf9SRui Paulo int res; 267*f05cddf9SRui Paulo size_t len = ETH_ALEN; 268*f05cddf9SRui Paulo 269*f05cddf9SRui Paulo res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len); 270*f05cddf9SRui Paulo if (res < 0 || len != ETH_ALEN) 271*f05cddf9SRui Paulo return -1; 272*f05cddf9SRui Paulo return 0; 273*f05cddf9SRui Paulo } 274*f05cddf9SRui Paulo 275*f05cddf9SRui Paulo 276*f05cddf9SRui Paulo static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid) 277*f05cddf9SRui Paulo { 278*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 279*f05cddf9SRui Paulo int res, ssid_len; 280*f05cddf9SRui Paulo u8 reply[sizeof(int) + 32]; 281*f05cddf9SRui Paulo size_t len = sizeof(reply); 282*f05cddf9SRui Paulo 283*f05cddf9SRui Paulo res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len); 284*f05cddf9SRui Paulo if (res < 0 || len < sizeof(int)) 285*f05cddf9SRui Paulo return -1; 286*f05cddf9SRui Paulo os_memcpy(&ssid_len, reply, sizeof(int)); 287*f05cddf9SRui Paulo if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) { 288*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply"); 289*f05cddf9SRui Paulo return -1; 290*f05cddf9SRui Paulo } 291*f05cddf9SRui Paulo os_memcpy(ssid, &reply[sizeof(int)], ssid_len); 292*f05cddf9SRui Paulo return ssid_len; 293*f05cddf9SRui Paulo } 294*f05cddf9SRui Paulo 295*f05cddf9SRui Paulo 296*f05cddf9SRui Paulo static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, 297*f05cddf9SRui Paulo int reason_code) 298*f05cddf9SRui Paulo { 299*f05cddf9SRui Paulo //struct wpa_driver_privsep_data *drv = priv; 300*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", 301*f05cddf9SRui Paulo __func__, MAC2STR(addr), reason_code); 302*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s - TODO", __func__); 303*f05cddf9SRui Paulo return 0; 304*f05cddf9SRui Paulo } 305*f05cddf9SRui Paulo 306*f05cddf9SRui Paulo 307*f05cddf9SRui Paulo static void wpa_driver_privsep_event_assoc(void *ctx, 308*f05cddf9SRui Paulo enum wpa_event_type event, 309*f05cddf9SRui Paulo u8 *buf, size_t len) 310*f05cddf9SRui Paulo { 311*f05cddf9SRui Paulo union wpa_event_data data; 312*f05cddf9SRui Paulo int inc_data = 0; 313*f05cddf9SRui Paulo u8 *pos, *end; 314*f05cddf9SRui Paulo int ie_len; 315*f05cddf9SRui Paulo 316*f05cddf9SRui Paulo os_memset(&data, 0, sizeof(data)); 317*f05cddf9SRui Paulo 318*f05cddf9SRui Paulo pos = buf; 319*f05cddf9SRui Paulo end = buf + len; 320*f05cddf9SRui Paulo 321*f05cddf9SRui Paulo if (end - pos < (int) sizeof(int)) 322*f05cddf9SRui Paulo return; 323*f05cddf9SRui Paulo os_memcpy(&ie_len, pos, sizeof(int)); 324*f05cddf9SRui Paulo pos += sizeof(int); 325*f05cddf9SRui Paulo if (ie_len < 0 || ie_len > end - pos) 326*f05cddf9SRui Paulo return; 327*f05cddf9SRui Paulo if (ie_len) { 328*f05cddf9SRui Paulo data.assoc_info.req_ies = pos; 329*f05cddf9SRui Paulo data.assoc_info.req_ies_len = ie_len; 330*f05cddf9SRui Paulo pos += ie_len; 331*f05cddf9SRui Paulo inc_data = 1; 332*f05cddf9SRui Paulo } 333*f05cddf9SRui Paulo 334*f05cddf9SRui Paulo wpa_supplicant_event(ctx, event, inc_data ? &data : NULL); 335*f05cddf9SRui Paulo } 336*f05cddf9SRui Paulo 337*f05cddf9SRui Paulo 338*f05cddf9SRui Paulo static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf, 339*f05cddf9SRui Paulo size_t len) 340*f05cddf9SRui Paulo { 341*f05cddf9SRui Paulo union wpa_event_data data; 342*f05cddf9SRui Paulo int ievent; 343*f05cddf9SRui Paulo 344*f05cddf9SRui Paulo if (len < sizeof(int) || 345*f05cddf9SRui Paulo len - sizeof(int) > sizeof(data.interface_status.ifname)) 346*f05cddf9SRui Paulo return; 347*f05cddf9SRui Paulo 348*f05cddf9SRui Paulo os_memcpy(&ievent, buf, sizeof(int)); 349*f05cddf9SRui Paulo 350*f05cddf9SRui Paulo os_memset(&data, 0, sizeof(data)); 351*f05cddf9SRui Paulo data.interface_status.ievent = ievent; 352*f05cddf9SRui Paulo os_memcpy(data.interface_status.ifname, buf + sizeof(int), 353*f05cddf9SRui Paulo len - sizeof(int)); 354*f05cddf9SRui Paulo wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data); 355*f05cddf9SRui Paulo } 356*f05cddf9SRui Paulo 357*f05cddf9SRui Paulo 358*f05cddf9SRui Paulo static void wpa_driver_privsep_event_michael_mic_failure( 359*f05cddf9SRui Paulo void *ctx, u8 *buf, size_t len) 360*f05cddf9SRui Paulo { 361*f05cddf9SRui Paulo union wpa_event_data data; 362*f05cddf9SRui Paulo 363*f05cddf9SRui Paulo if (len != sizeof(int)) 364*f05cddf9SRui Paulo return; 365*f05cddf9SRui Paulo 366*f05cddf9SRui Paulo os_memset(&data, 0, sizeof(data)); 367*f05cddf9SRui Paulo os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int)); 368*f05cddf9SRui Paulo wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); 369*f05cddf9SRui Paulo } 370*f05cddf9SRui Paulo 371*f05cddf9SRui Paulo 372*f05cddf9SRui Paulo static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf, 373*f05cddf9SRui Paulo size_t len) 374*f05cddf9SRui Paulo { 375*f05cddf9SRui Paulo union wpa_event_data data; 376*f05cddf9SRui Paulo 377*f05cddf9SRui Paulo if (len != sizeof(struct pmkid_candidate)) 378*f05cddf9SRui Paulo return; 379*f05cddf9SRui Paulo 380*f05cddf9SRui Paulo os_memset(&data, 0, sizeof(data)); 381*f05cddf9SRui Paulo os_memcpy(&data.pmkid_candidate, buf, len); 382*f05cddf9SRui Paulo wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data); 383*f05cddf9SRui Paulo } 384*f05cddf9SRui Paulo 385*f05cddf9SRui Paulo 386*f05cddf9SRui Paulo static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len) 387*f05cddf9SRui Paulo { 388*f05cddf9SRui Paulo union wpa_event_data data; 389*f05cddf9SRui Paulo 390*f05cddf9SRui Paulo if (len != ETH_ALEN) 391*f05cddf9SRui Paulo return; 392*f05cddf9SRui Paulo 393*f05cddf9SRui Paulo os_memset(&data, 0, sizeof(data)); 394*f05cddf9SRui Paulo os_memcpy(data.stkstart.peer, buf, ETH_ALEN); 395*f05cddf9SRui Paulo wpa_supplicant_event(ctx, EVENT_STKSTART, &data); 396*f05cddf9SRui Paulo } 397*f05cddf9SRui Paulo 398*f05cddf9SRui Paulo 399*f05cddf9SRui Paulo static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf, 400*f05cddf9SRui Paulo size_t len) 401*f05cddf9SRui Paulo { 402*f05cddf9SRui Paulo union wpa_event_data data; 403*f05cddf9SRui Paulo 404*f05cddf9SRui Paulo if (len < sizeof(int) + ETH_ALEN) 405*f05cddf9SRui Paulo return; 406*f05cddf9SRui Paulo 407*f05cddf9SRui Paulo os_memset(&data, 0, sizeof(data)); 408*f05cddf9SRui Paulo os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int)); 409*f05cddf9SRui Paulo os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN); 410*f05cddf9SRui Paulo data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN; 411*f05cddf9SRui Paulo data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN; 412*f05cddf9SRui Paulo wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data); 413*f05cddf9SRui Paulo } 414*f05cddf9SRui Paulo 415*f05cddf9SRui Paulo 416*f05cddf9SRui Paulo static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len) 417*f05cddf9SRui Paulo { 418*f05cddf9SRui Paulo if (len < ETH_ALEN) 419*f05cddf9SRui Paulo return; 420*f05cddf9SRui Paulo drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN); 421*f05cddf9SRui Paulo } 422*f05cddf9SRui Paulo 423*f05cddf9SRui Paulo 424*f05cddf9SRui Paulo static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, 425*f05cddf9SRui Paulo void *sock_ctx) 426*f05cddf9SRui Paulo { 427*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = eloop_ctx; 428*f05cddf9SRui Paulo u8 *buf, *event_buf; 429*f05cddf9SRui Paulo size_t event_len; 430*f05cddf9SRui Paulo int res, event; 431*f05cddf9SRui Paulo enum privsep_event e; 432*f05cddf9SRui Paulo struct sockaddr_un from; 433*f05cddf9SRui Paulo socklen_t fromlen = sizeof(from); 434*f05cddf9SRui Paulo const size_t buflen = 2000; 435*f05cddf9SRui Paulo 436*f05cddf9SRui Paulo buf = os_malloc(buflen); 437*f05cddf9SRui Paulo if (buf == NULL) 438*f05cddf9SRui Paulo return; 439*f05cddf9SRui Paulo res = recvfrom(sock, buf, buflen, 0, 440*f05cddf9SRui Paulo (struct sockaddr *) &from, &fromlen); 441*f05cddf9SRui Paulo if (res < 0) { 442*f05cddf9SRui Paulo perror("recvfrom(priv_socket)"); 443*f05cddf9SRui Paulo os_free(buf); 444*f05cddf9SRui Paulo return; 445*f05cddf9SRui Paulo } 446*f05cddf9SRui Paulo 447*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res); 448*f05cddf9SRui Paulo 449*f05cddf9SRui Paulo if (res < (int) sizeof(int)) { 450*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res); 451*f05cddf9SRui Paulo return; 452*f05cddf9SRui Paulo } 453*f05cddf9SRui Paulo 454*f05cddf9SRui Paulo os_memcpy(&event, buf, sizeof(int)); 455*f05cddf9SRui Paulo event_buf = &buf[sizeof(int)]; 456*f05cddf9SRui Paulo event_len = res - sizeof(int); 457*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)", 458*f05cddf9SRui Paulo event, (unsigned long) event_len); 459*f05cddf9SRui Paulo 460*f05cddf9SRui Paulo e = event; 461*f05cddf9SRui Paulo switch (e) { 462*f05cddf9SRui Paulo case PRIVSEP_EVENT_SCAN_RESULTS: 463*f05cddf9SRui Paulo wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); 464*f05cddf9SRui Paulo break; 465*f05cddf9SRui Paulo case PRIVSEP_EVENT_ASSOC: 466*f05cddf9SRui Paulo wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC, 467*f05cddf9SRui Paulo event_buf, event_len); 468*f05cddf9SRui Paulo break; 469*f05cddf9SRui Paulo case PRIVSEP_EVENT_DISASSOC: 470*f05cddf9SRui Paulo wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); 471*f05cddf9SRui Paulo break; 472*f05cddf9SRui Paulo case PRIVSEP_EVENT_ASSOCINFO: 473*f05cddf9SRui Paulo wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO, 474*f05cddf9SRui Paulo event_buf, event_len); 475*f05cddf9SRui Paulo break; 476*f05cddf9SRui Paulo case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE: 477*f05cddf9SRui Paulo wpa_driver_privsep_event_michael_mic_failure( 478*f05cddf9SRui Paulo drv->ctx, event_buf, event_len); 479*f05cddf9SRui Paulo break; 480*f05cddf9SRui Paulo case PRIVSEP_EVENT_INTERFACE_STATUS: 481*f05cddf9SRui Paulo wpa_driver_privsep_event_interface_status(drv->ctx, event_buf, 482*f05cddf9SRui Paulo event_len); 483*f05cddf9SRui Paulo break; 484*f05cddf9SRui Paulo case PRIVSEP_EVENT_PMKID_CANDIDATE: 485*f05cddf9SRui Paulo wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf, 486*f05cddf9SRui Paulo event_len); 487*f05cddf9SRui Paulo break; 488*f05cddf9SRui Paulo case PRIVSEP_EVENT_STKSTART: 489*f05cddf9SRui Paulo wpa_driver_privsep_event_stkstart(drv->ctx, event_buf, 490*f05cddf9SRui Paulo event_len); 491*f05cddf9SRui Paulo break; 492*f05cddf9SRui Paulo case PRIVSEP_EVENT_FT_RESPONSE: 493*f05cddf9SRui Paulo wpa_driver_privsep_event_ft_response(drv->ctx, event_buf, 494*f05cddf9SRui Paulo event_len); 495*f05cddf9SRui Paulo break; 496*f05cddf9SRui Paulo case PRIVSEP_EVENT_RX_EAPOL: 497*f05cddf9SRui Paulo wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, 498*f05cddf9SRui Paulo event_len); 499*f05cddf9SRui Paulo break; 500*f05cddf9SRui Paulo } 501*f05cddf9SRui Paulo 502*f05cddf9SRui Paulo os_free(buf); 503*f05cddf9SRui Paulo } 504*f05cddf9SRui Paulo 505*f05cddf9SRui Paulo 506*f05cddf9SRui Paulo static void * wpa_driver_privsep_init(void *ctx, const char *ifname) 507*f05cddf9SRui Paulo { 508*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv; 509*f05cddf9SRui Paulo 510*f05cddf9SRui Paulo drv = os_zalloc(sizeof(*drv)); 511*f05cddf9SRui Paulo if (drv == NULL) 512*f05cddf9SRui Paulo return NULL; 513*f05cddf9SRui Paulo drv->ctx = ctx; 514*f05cddf9SRui Paulo drv->priv_socket = -1; 515*f05cddf9SRui Paulo drv->cmd_socket = -1; 516*f05cddf9SRui Paulo os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); 517*f05cddf9SRui Paulo 518*f05cddf9SRui Paulo return drv; 519*f05cddf9SRui Paulo } 520*f05cddf9SRui Paulo 521*f05cddf9SRui Paulo 522*f05cddf9SRui Paulo static void wpa_driver_privsep_deinit(void *priv) 523*f05cddf9SRui Paulo { 524*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 525*f05cddf9SRui Paulo 526*f05cddf9SRui Paulo if (drv->priv_socket >= 0) { 527*f05cddf9SRui Paulo wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER); 528*f05cddf9SRui Paulo eloop_unregister_read_sock(drv->priv_socket); 529*f05cddf9SRui Paulo close(drv->priv_socket); 530*f05cddf9SRui Paulo } 531*f05cddf9SRui Paulo 532*f05cddf9SRui Paulo if (drv->own_socket_path) { 533*f05cddf9SRui Paulo unlink(drv->own_socket_path); 534*f05cddf9SRui Paulo os_free(drv->own_socket_path); 535*f05cddf9SRui Paulo } 536*f05cddf9SRui Paulo 537*f05cddf9SRui Paulo if (drv->cmd_socket >= 0) { 538*f05cddf9SRui Paulo eloop_unregister_read_sock(drv->cmd_socket); 539*f05cddf9SRui Paulo close(drv->cmd_socket); 540*f05cddf9SRui Paulo } 541*f05cddf9SRui Paulo 542*f05cddf9SRui Paulo if (drv->own_cmd_path) { 543*f05cddf9SRui Paulo unlink(drv->own_cmd_path); 544*f05cddf9SRui Paulo os_free(drv->own_cmd_path); 545*f05cddf9SRui Paulo } 546*f05cddf9SRui Paulo 547*f05cddf9SRui Paulo os_free(drv); 548*f05cddf9SRui Paulo } 549*f05cddf9SRui Paulo 550*f05cddf9SRui Paulo 551*f05cddf9SRui Paulo static int wpa_driver_privsep_set_param(void *priv, const char *param) 552*f05cddf9SRui Paulo { 553*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 554*f05cddf9SRui Paulo const char *pos; 555*f05cddf9SRui Paulo char *own_dir, *priv_dir; 556*f05cddf9SRui Paulo static unsigned int counter = 0; 557*f05cddf9SRui Paulo size_t len; 558*f05cddf9SRui Paulo struct sockaddr_un addr; 559*f05cddf9SRui Paulo 560*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); 561*f05cddf9SRui Paulo if (param == NULL) 562*f05cddf9SRui Paulo pos = NULL; 563*f05cddf9SRui Paulo else 564*f05cddf9SRui Paulo pos = os_strstr(param, "own_dir="); 565*f05cddf9SRui Paulo if (pos) { 566*f05cddf9SRui Paulo char *end; 567*f05cddf9SRui Paulo own_dir = os_strdup(pos + 8); 568*f05cddf9SRui Paulo if (own_dir == NULL) 569*f05cddf9SRui Paulo return -1; 570*f05cddf9SRui Paulo end = os_strchr(own_dir, ' '); 571*f05cddf9SRui Paulo if (end) 572*f05cddf9SRui Paulo *end = '\0'; 573*f05cddf9SRui Paulo } else { 574*f05cddf9SRui Paulo own_dir = os_strdup("/tmp"); 575*f05cddf9SRui Paulo if (own_dir == NULL) 576*f05cddf9SRui Paulo return -1; 577*f05cddf9SRui Paulo } 578*f05cddf9SRui Paulo 579*f05cddf9SRui Paulo if (param == NULL) 580*f05cddf9SRui Paulo pos = NULL; 581*f05cddf9SRui Paulo else 582*f05cddf9SRui Paulo pos = os_strstr(param, "priv_dir="); 583*f05cddf9SRui Paulo if (pos) { 584*f05cddf9SRui Paulo char *end; 585*f05cddf9SRui Paulo priv_dir = os_strdup(pos + 9); 586*f05cddf9SRui Paulo if (priv_dir == NULL) { 587*f05cddf9SRui Paulo os_free(own_dir); 588*f05cddf9SRui Paulo return -1; 589*f05cddf9SRui Paulo } 590*f05cddf9SRui Paulo end = os_strchr(priv_dir, ' '); 591*f05cddf9SRui Paulo if (end) 592*f05cddf9SRui Paulo *end = '\0'; 593*f05cddf9SRui Paulo } else { 594*f05cddf9SRui Paulo priv_dir = os_strdup("/var/run/wpa_priv"); 595*f05cddf9SRui Paulo if (priv_dir == NULL) { 596*f05cddf9SRui Paulo os_free(own_dir); 597*f05cddf9SRui Paulo return -1; 598*f05cddf9SRui Paulo } 599*f05cddf9SRui Paulo } 600*f05cddf9SRui Paulo 601*f05cddf9SRui Paulo len = os_strlen(own_dir) + 50; 602*f05cddf9SRui Paulo drv->own_socket_path = os_malloc(len); 603*f05cddf9SRui Paulo if (drv->own_socket_path == NULL) { 604*f05cddf9SRui Paulo os_free(priv_dir); 605*f05cddf9SRui Paulo os_free(own_dir); 606*f05cddf9SRui Paulo return -1; 607*f05cddf9SRui Paulo } 608*f05cddf9SRui Paulo os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d", 609*f05cddf9SRui Paulo own_dir, getpid(), counter++); 610*f05cddf9SRui Paulo 611*f05cddf9SRui Paulo len = os_strlen(own_dir) + 50; 612*f05cddf9SRui Paulo drv->own_cmd_path = os_malloc(len); 613*f05cddf9SRui Paulo if (drv->own_cmd_path == NULL) { 614*f05cddf9SRui Paulo os_free(drv->own_socket_path); 615*f05cddf9SRui Paulo drv->own_socket_path = NULL; 616*f05cddf9SRui Paulo os_free(priv_dir); 617*f05cddf9SRui Paulo os_free(own_dir); 618*f05cddf9SRui Paulo return -1; 619*f05cddf9SRui Paulo } 620*f05cddf9SRui Paulo os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d", 621*f05cddf9SRui Paulo own_dir, getpid(), counter++); 622*f05cddf9SRui Paulo 623*f05cddf9SRui Paulo os_free(own_dir); 624*f05cddf9SRui Paulo 625*f05cddf9SRui Paulo drv->priv_addr.sun_family = AF_UNIX; 626*f05cddf9SRui Paulo os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path), 627*f05cddf9SRui Paulo "%s/%s", priv_dir, drv->ifname); 628*f05cddf9SRui Paulo os_free(priv_dir); 629*f05cddf9SRui Paulo 630*f05cddf9SRui Paulo drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0); 631*f05cddf9SRui Paulo if (drv->priv_socket < 0) { 632*f05cddf9SRui Paulo perror("socket(PF_UNIX)"); 633*f05cddf9SRui Paulo os_free(drv->own_socket_path); 634*f05cddf9SRui Paulo drv->own_socket_path = NULL; 635*f05cddf9SRui Paulo return -1; 636*f05cddf9SRui Paulo } 637*f05cddf9SRui Paulo 638*f05cddf9SRui Paulo os_memset(&addr, 0, sizeof(addr)); 639*f05cddf9SRui Paulo addr.sun_family = AF_UNIX; 640*f05cddf9SRui Paulo os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); 641*f05cddf9SRui Paulo if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) < 642*f05cddf9SRui Paulo 0) { 643*f05cddf9SRui Paulo perror("privsep-set-params priv-sock: bind(PF_UNIX)"); 644*f05cddf9SRui Paulo close(drv->priv_socket); 645*f05cddf9SRui Paulo drv->priv_socket = -1; 646*f05cddf9SRui Paulo unlink(drv->own_socket_path); 647*f05cddf9SRui Paulo os_free(drv->own_socket_path); 648*f05cddf9SRui Paulo drv->own_socket_path = NULL; 649*f05cddf9SRui Paulo return -1; 650*f05cddf9SRui Paulo } 651*f05cddf9SRui Paulo 652*f05cddf9SRui Paulo eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive, 653*f05cddf9SRui Paulo drv, NULL); 654*f05cddf9SRui Paulo 655*f05cddf9SRui Paulo drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0); 656*f05cddf9SRui Paulo if (drv->cmd_socket < 0) { 657*f05cddf9SRui Paulo perror("socket(PF_UNIX)"); 658*f05cddf9SRui Paulo os_free(drv->own_cmd_path); 659*f05cddf9SRui Paulo drv->own_cmd_path = NULL; 660*f05cddf9SRui Paulo return -1; 661*f05cddf9SRui Paulo } 662*f05cddf9SRui Paulo 663*f05cddf9SRui Paulo os_memset(&addr, 0, sizeof(addr)); 664*f05cddf9SRui Paulo addr.sun_family = AF_UNIX; 665*f05cddf9SRui Paulo os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path)); 666*f05cddf9SRui Paulo if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) 667*f05cddf9SRui Paulo { 668*f05cddf9SRui Paulo perror("privsep-set-params cmd-sock: bind(PF_UNIX)"); 669*f05cddf9SRui Paulo close(drv->cmd_socket); 670*f05cddf9SRui Paulo drv->cmd_socket = -1; 671*f05cddf9SRui Paulo unlink(drv->own_cmd_path); 672*f05cddf9SRui Paulo os_free(drv->own_cmd_path); 673*f05cddf9SRui Paulo drv->own_cmd_path = NULL; 674*f05cddf9SRui Paulo return -1; 675*f05cddf9SRui Paulo } 676*f05cddf9SRui Paulo 677*f05cddf9SRui Paulo if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) { 678*f05cddf9SRui Paulo wpa_printf(MSG_ERROR, "Failed to register with wpa_priv"); 679*f05cddf9SRui Paulo return -1; 680*f05cddf9SRui Paulo } 681*f05cddf9SRui Paulo 682*f05cddf9SRui Paulo return 0; 683*f05cddf9SRui Paulo } 684*f05cddf9SRui Paulo 685*f05cddf9SRui Paulo 686*f05cddf9SRui Paulo static int wpa_driver_privsep_get_capa(void *priv, 687*f05cddf9SRui Paulo struct wpa_driver_capa *capa) 688*f05cddf9SRui Paulo { 689*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 690*f05cddf9SRui Paulo int res; 691*f05cddf9SRui Paulo size_t len = sizeof(*capa); 692*f05cddf9SRui Paulo 693*f05cddf9SRui Paulo res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len); 694*f05cddf9SRui Paulo if (res < 0 || len != sizeof(*capa)) 695*f05cddf9SRui Paulo return -1; 696*f05cddf9SRui Paulo return 0; 697*f05cddf9SRui Paulo } 698*f05cddf9SRui Paulo 699*f05cddf9SRui Paulo 700*f05cddf9SRui Paulo static const u8 * wpa_driver_privsep_get_mac_addr(void *priv) 701*f05cddf9SRui Paulo { 702*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 703*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s", __func__); 704*f05cddf9SRui Paulo return drv->own_addr; 705*f05cddf9SRui Paulo } 706*f05cddf9SRui Paulo 707*f05cddf9SRui Paulo 708*f05cddf9SRui Paulo static int wpa_driver_privsep_set_country(void *priv, const char *alpha2) 709*f05cddf9SRui Paulo { 710*f05cddf9SRui Paulo struct wpa_driver_privsep_data *drv = priv; 711*f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2); 712*f05cddf9SRui Paulo return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2, 713*f05cddf9SRui Paulo os_strlen(alpha2), NULL, NULL); 714*f05cddf9SRui Paulo } 715*f05cddf9SRui Paulo 716*f05cddf9SRui Paulo 717*f05cddf9SRui Paulo struct wpa_driver_ops wpa_driver_privsep_ops = { 718*f05cddf9SRui Paulo "privsep", 719*f05cddf9SRui Paulo "wpa_supplicant privilege separated driver", 720*f05cddf9SRui Paulo .get_bssid = wpa_driver_privsep_get_bssid, 721*f05cddf9SRui Paulo .get_ssid = wpa_driver_privsep_get_ssid, 722*f05cddf9SRui Paulo .set_key = wpa_driver_privsep_set_key, 723*f05cddf9SRui Paulo .init = wpa_driver_privsep_init, 724*f05cddf9SRui Paulo .deinit = wpa_driver_privsep_deinit, 725*f05cddf9SRui Paulo .set_param = wpa_driver_privsep_set_param, 726*f05cddf9SRui Paulo .scan2 = wpa_driver_privsep_scan, 727*f05cddf9SRui Paulo .deauthenticate = wpa_driver_privsep_deauthenticate, 728*f05cddf9SRui Paulo .associate = wpa_driver_privsep_associate, 729*f05cddf9SRui Paulo .get_capa = wpa_driver_privsep_get_capa, 730*f05cddf9SRui Paulo .get_mac_addr = wpa_driver_privsep_get_mac_addr, 731*f05cddf9SRui Paulo .get_scan_results2 = wpa_driver_privsep_get_scan_results2, 732*f05cddf9SRui Paulo .set_country = wpa_driver_privsep_set_country, 733*f05cddf9SRui Paulo }; 734*f05cddf9SRui Paulo 735*f05cddf9SRui Paulo 736*f05cddf9SRui Paulo struct wpa_driver_ops *wpa_drivers[] = 737*f05cddf9SRui Paulo { 738*f05cddf9SRui Paulo &wpa_driver_privsep_ops, 739*f05cddf9SRui Paulo NULL 740*f05cddf9SRui Paulo }; 741