Home | History | Annotate | Download | only in wpad
      1 /*
      2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*
      7  * Copyright (c) 2003-2004, Jouni Malinen <jkmaline (at) cc.hut.fi>
      8  * Sun elects to license this software under the BSD license.
      9  * See README for more details.
     10  */
     11 
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <stdarg.h>
     15 #include <unistd.h>
     16 #include <string.h>
     17 #include <syslog.h>
     18 #include <sys/stat.h>
     19 #include <errno.h>
     20 #include <signal.h>
     21 #include <fcntl.h>
     22 #include <door.h>
     23 #include <libscf.h>
     24 #include <libdladm.h>
     25 #include <libdllink.h>
     26 #include <sys/ethernet.h>
     27 
     28 #include "wpa_impl.h"
     29 #include "wpa_enc.h"
     30 #include "driver.h"
     31 #include "eloop.h"
     32 #include "l2_packet.h"
     33 
     34 extern struct wpa_driver_ops wpa_driver_wifi_ops;
     35 int wpa_debug_level = MSG_ERROR;
     36 
     37 /*
     38  * wpa_printf - conditional printf
     39  * @level: priority level (MSG_*) of the message
     40  * @fmt: printf format string, followed by optional arguments
     41  *
     42  * This function is used to print conditional debugging and error messages. The
     43  * output may be directed to stdout, stderr, and/or syslog based on
     44  * configuration.
     45  */
     46 void
     47 wpa_printf(int level, char *fmt, ...)
     48 {
     49 	va_list ap;
     50 	char buffer[MAX_LOGBUF];
     51 
     52 	if (level < wpa_debug_level)
     53 		return;
     54 
     55 	va_start(ap, fmt);
     56 
     57 	/* LINTED E_SEC_PRINTF_VAR_FMT */
     58 	(void) vsnprintf(buffer, sizeof (buffer), fmt, ap);
     59 
     60 	va_end(ap);
     61 
     62 	syslog(LOG_NOTICE | LOG_DAEMON, "%s", buffer);
     63 }
     64 
     65 /*
     66  * wpa_hexdump - conditional hex dump
     67  * @level: priority level (MSG_*) of the message
     68  * @title: title of for the message
     69  * @buf: data buffer to be dumped
     70  * @len: length of the @buf
     71  *
     72  * This function is used to print conditional debugging and error messages. The
     73  * output may be directed to stdout, stderr, and/or syslog based on
     74  * configuration. The contents of @buf is printed out has hex dump.
     75  */
     76 void
     77 wpa_hexdump(int level, const char *title, const uint8_t *buf, size_t len)
     78 {
     79 	size_t i;
     80 	char buffer[MAX_LOGBUF], tmp[4];
     81 	int n;
     82 
     83 	if (level < wpa_debug_level)
     84 		return;
     85 
     86 	(void) snprintf(buffer, sizeof (buffer), "%s - hexdump(len=%d):",
     87 	    title, len);
     88 	n = strlen(buffer);
     89 
     90 	for (i = 0; i < len; i++) {
     91 		(void) sprintf(tmp, " %02x", buf[i]);
     92 
     93 		n += strlen(tmp);
     94 		if (n >= MAX_LOGBUF) break;
     95 
     96 		(void) strlcat(buffer, tmp, sizeof (buffer));
     97 	}
     98 
     99 	syslog(LOG_NOTICE | LOG_DAEMON, "%s", buffer);
    100 }
    101 
    102 static const char *
    103 wpa_ssid_txt(char *ssid, size_t ssid_len)
    104 {
    105 	static char ssid_txt[MAX_ESSID_LENGTH + 1];
    106 	char *pos;
    107 
    108 	if (ssid_len > MAX_ESSID_LENGTH)
    109 		ssid_len = MAX_ESSID_LENGTH;
    110 	(void) memcpy(ssid_txt, ssid, ssid_len);
    111 	ssid_txt[ssid_len] = '\0';
    112 	for (pos = ssid_txt; *pos != '\0'; pos ++) {
    113 		if ((uint8_t)*pos < 32 || (uint8_t)*pos >= 127)
    114 			*pos = '_';
    115 	}
    116 	return (ssid_txt);
    117 }
    118 
    119 /* ARGSUSED */
    120 void
    121 wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
    122 {
    123 	struct wpa_supplicant *wpa_s = eloop_ctx;
    124 	struct wpa_ssid *ssid;
    125 
    126 	if (wpa_s->conf == NULL)
    127 		return;
    128 
    129 	if (wpa_s->wpa_state == WPA_DISCONNECTED)
    130 		wpa_s->wpa_state = WPA_SCANNING;
    131 
    132 	ssid = wpa_s->conf->ssid;
    133 	wpa_printf(MSG_DEBUG, "Starting AP scan (%s SSID)",
    134 	    ssid ? "specific": "broadcast");
    135 
    136 	if (ssid) {
    137 		wpa_printf(MSG_DEBUG, "Scan SSID: %s", ssid->ssid);
    138 	}
    139 
    140 	if (wpa_s->driver->scan(wpa_s->handle, wpa_s->linkid)) {
    141 		wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
    142 	}
    143 }
    144 
    145 void
    146 wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
    147 {
    148 	wpa_printf(MSG_DEBUG, "Setting scan request: %d sec %d usec",
    149 	    sec, usec);
    150 	(void) eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
    151 	(void) eloop_register_timeout(sec, usec, wpa_supplicant_scan,
    152 	    wpa_s, NULL);
    153 }
    154 
    155 void
    156 wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
    157 {
    158 	wpa_printf(MSG_DEBUG, "Cancelling scan request");
    159 	eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
    160 }
    161 
    162 /* ARGSUSED */
    163 static void
    164 wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
    165 {
    166 	struct wpa_supplicant *wpa_s = eloop_ctx;
    167 
    168 	wpa_printf(MSG_INFO, "Authentication with " MACSTR " timed out.",
    169 	    MAC2STR(wpa_s->bssid));
    170 
    171 	wpa_s->reassociate = 1;
    172 	wpa_supplicant_req_scan(wpa_s, 0, 0);
    173 }
    174 
    175 void
    176 wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
    177 				int sec, int usec)
    178 {
    179 	wpa_printf(MSG_DEBUG, "Setting authentication timeout: %d sec "
    180 	    "%d usec", sec, usec);
    181 	eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
    182 	(void) eloop_register_timeout(sec, usec, wpa_supplicant_timeout,
    183 	    wpa_s, NULL);
    184 }
    185 
    186 void
    187 wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
    188 {
    189 	wpa_printf(MSG_DEBUG, "Cancelling authentication timeout");
    190 	eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
    191 }
    192 
    193 static void
    194 wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
    195 {
    196 	l2_packet_deinit(wpa_s->l2);
    197 	wpa_s->l2 = NULL;
    198 
    199 	if (wpa_s->conf != NULL) {
    200 		wpa_config_free(wpa_s->conf);
    201 		wpa_s->conf = NULL;
    202 	}
    203 
    204 	dladm_close(wpa_s->handle);
    205 	free(wpa_s->ap_wpa_ie);
    206 	pmksa_candidate_free(wpa_s);
    207 	pmksa_cache_free(wpa_s);
    208 }
    209 
    210 static void
    211 wpa_clear_keys(struct wpa_supplicant *wpa_s, uint8_t *addr)
    212 {
    213 	wpa_s->driver->set_key(wpa_s->handle, wpa_s->linkid, WPA_ALG_NONE,
    214 	    (uint8_t *)"\xff\xff\xff\xff\xff\xff", 0, 0, NULL, 0, NULL, 0);
    215 	wpa_s->driver->set_key(wpa_s->handle, wpa_s->linkid, WPA_ALG_NONE,
    216 	    (uint8_t *)"\xff\xff\xff\xff\xff\xff", 1, 0, NULL, 0, NULL, 0);
    217 	wpa_s->driver->set_key(wpa_s->handle, wpa_s->linkid, WPA_ALG_NONE,
    218 	    (uint8_t *)"\xff\xff\xff\xff\xff\xff", 2, 0, NULL, 0, NULL, 0);
    219 	wpa_s->driver->set_key(wpa_s->handle, wpa_s->linkid, WPA_ALG_NONE,
    220 	    (uint8_t *)"\xff\xff\xff\xff\xff\xff", 3, 0, NULL, 0, NULL, 0);
    221 	if (addr) {
    222 		wpa_s->driver->set_key(wpa_s->handle, wpa_s->linkid,
    223 		    WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, 0);
    224 	}
    225 }
    226 
    227 static void
    228 wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
    229 {
    230 	wpa_s->wpa_state = WPA_DISCONNECTED;
    231 	(void) memset(wpa_s->bssid, 0, IEEE80211_ADDR_LEN);
    232 }
    233 
    234 static int
    235 wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
    236     dladm_wlan_ess_t *bss, struct wpa_ssid *ssid,
    237     uint8_t *wpa_ie, int *wpa_ie_len)
    238 {
    239 	struct wpa_ie_data ie;
    240 	int sel, proto;
    241 	uint8_t *ap_ie;
    242 	size_t ap_ie_len;
    243 
    244 	/* RSN or WPA */
    245 	if (bss->we_wpa_ie_len && bss->we_wpa_ie[0] == RSN_INFO_ELEM &&
    246 	    (ssid->proto & WPA_PROTO_RSN)) {
    247 		wpa_printf(MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
    248 		proto = WPA_PROTO_RSN;
    249 	} else {
    250 		wpa_printf(MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
    251 		proto = WPA_PROTO_WPA;
    252 	}
    253 
    254 	ap_ie = bss->we_wpa_ie;
    255 	ap_ie_len = bss->we_wpa_ie_len;
    256 
    257 	if (wpa_parse_wpa_ie(wpa_s, ap_ie, ap_ie_len, &ie)) {
    258 		wpa_printf(MSG_WARNING, "WPA: Failed to parse WPA IE for "
    259 		    "the selected BSS.");
    260 		return (-1);
    261 	}
    262 
    263 	wpa_s->proto = proto;
    264 	free(wpa_s->ap_wpa_ie);
    265 	wpa_s->ap_wpa_ie = malloc(ap_ie_len);
    266 	(void) memcpy(wpa_s->ap_wpa_ie, ap_ie, ap_ie_len);
    267 	wpa_s->ap_wpa_ie_len = ap_ie_len;
    268 
    269 	sel = ie.group_cipher & ssid->group_cipher;
    270 	if (sel & WPA_CIPHER_CCMP) {
    271 		wpa_s->group_cipher = WPA_CIPHER_CCMP;
    272 	} else if (sel & WPA_CIPHER_TKIP) {
    273 		wpa_s->group_cipher = WPA_CIPHER_TKIP;
    274 	} else if (sel & WPA_CIPHER_WEP104) {
    275 		wpa_s->group_cipher = WPA_CIPHER_WEP104;
    276 	} else if (sel & WPA_CIPHER_WEP40) {
    277 		wpa_s->group_cipher = WPA_CIPHER_WEP40;
    278 	} else {
    279 		wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher.");
    280 		return (-1);
    281 	}
    282 
    283 	sel = ie.pairwise_cipher & ssid->pairwise_cipher;
    284 	if (sel & WPA_CIPHER_CCMP) {
    285 		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
    286 	} else if (sel & WPA_CIPHER_TKIP) {
    287 		wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
    288 	} else if (sel & WPA_CIPHER_NONE) {
    289 		wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
    290 	} else {
    291 		wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
    292 		    "cipher.");
    293 		return (-1);
    294 	}
    295 
    296 	sel = ie.key_mgmt & ssid->key_mgmt;
    297 	if (sel & WPA_KEY_MGMT_IEEE8021X) {
    298 		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
    299 	} else if (sel & WPA_KEY_MGMT_PSK) {
    300 		wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
    301 	} else {
    302 		wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated "
    303 		    "key management type.");
    304 		return (-1);
    305 	}
    306 
    307 	*wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
    308 	if (*wpa_ie_len < 0) {
    309 		wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE.");
    310 		return (-1);
    311 	}
    312 	wpa_hexdump(MSG_DEBUG, "WPA: Own WPA IE", wpa_ie, *wpa_ie_len);
    313 
    314 	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
    315 		(void) memcpy(wpa_s->pmk, ssid->psk, PMK_LEN);
    316 	else if (wpa_s->cur_pmksa)
    317 		(void) memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, PMK_LEN);
    318 	else {
    319 		(void) memset(wpa_s->pmk, 0, PMK_LEN);
    320 	}
    321 
    322 	return (0);
    323 }
    324 
    325 static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
    326     dladm_wlan_ess_t *bss, struct wpa_ssid *ssid)
    327 {
    328 	uint8_t wpa_ie[IEEE80211_MAX_OPT_IE];
    329 	int wpa_ie_len;
    330 
    331 	wpa_s->reassociate = 0;
    332 	wpa_printf(MSG_DEBUG, "Trying to associate with " MACSTR
    333 	    " (SSID='%s' freq=%d MHz)", MAC2STR(bss->we_bssid.wb_bytes),
    334 	    wpa_ssid_txt((char *)ssid->ssid, ssid->ssid_len), bss->we_freq);
    335 	wpa_supplicant_cancel_scan(wpa_s);
    336 
    337 	if (bss->we_wpa_ie_len &&
    338 	    (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK))) {
    339 		wpa_s->cur_pmksa = pmksa_cache_get(wpa_s,
    340 		    bss->we_bssid.wb_bytes, NULL);
    341 		if (wpa_s->cur_pmksa) {
    342 			wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
    343 			    wpa_s->cur_pmksa->pmkid, PMKID_LEN);
    344 		}
    345 		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
    346 		    wpa_ie, &wpa_ie_len)) {
    347 			wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
    348 			    "management and encryption suites");
    349 			return;
    350 		}
    351 	} else {
    352 		wpa_ie_len = 0;
    353 	}
    354 
    355 	wpa_clear_keys(wpa_s, bss->we_bssid.wb_bytes);
    356 	wpa_s->wpa_state = WPA_ASSOCIATING;
    357 	wpa_s->driver->associate(wpa_s->handle, wpa_s->linkid,
    358 	    (const char *)bss->we_bssid.wb_bytes, wpa_ie, wpa_ie_len);
    359 
    360 	/* Timeout for IEEE 802.11 authentication and association */
    361 	wpa_supplicant_req_auth_timeout(wpa_s, 15, 0);
    362 }
    363 
    364 void
    365 wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, int reason_code)
    366 {
    367 	uint8_t *addr = NULL;
    368 	wpa_s->wpa_state = WPA_DISCONNECTED;
    369 	if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00",
    370 	    IEEE80211_ADDR_LEN) != 0) {
    371 		wpa_s->driver->disassociate(wpa_s->handle, wpa_s->linkid,
    372 		    reason_code);
    373 		addr = wpa_s->bssid;
    374 	}
    375 	wpa_clear_keys(wpa_s, addr);
    376 }
    377 
    378 static dladm_wlan_ess_t *
    379 wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group,
    380     dladm_wlan_ess_t *results, int num, struct wpa_ssid **selected_ssid)
    381 {
    382 	struct wpa_ssid *ssid;
    383 	dladm_wlan_ess_t *bss, *selected = NULL;
    384 	int i;
    385 
    386 	struct wpa_ie_data ie;
    387 
    388 	wpa_printf(MSG_DEBUG, "Selecting BSS from scan results (%d)", num);
    389 
    390 	bss = NULL;
    391 	ssid = NULL;
    392 
    393 	/* try to find matched AP */
    394 	for (i = 0; i < num && !selected; i++) {
    395 		bss = &results[i];
    396 		wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
    397 		    "wpa_ie_len=%d",
    398 		    i, MAC2STR(bss->we_bssid.wb_bytes),
    399 		    wpa_ssid_txt(bss->we_ssid.we_bytes, bss->we_ssid_len),
    400 		    bss->we_wpa_ie_len);
    401 		if (bss->we_wpa_ie_len == 0) {
    402 			wpa_printf(MSG_DEBUG, "   skip - no WPA/RSN IE");
    403 		}
    404 
    405 		ssid = group;
    406 		if (bss->we_ssid_len != ssid->ssid_len ||
    407 		    memcmp(bss->we_ssid.we_bytes, ssid->ssid,
    408 		    bss->we_ssid_len) != 0) {
    409 			wpa_printf(MSG_DEBUG, "   skip - SSID mismatch");
    410 			continue;
    411 		}
    412 		if (!((ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_WPA)) &&
    413 		    wpa_parse_wpa_ie(wpa_s, bss->we_wpa_ie,
    414 		    bss->we_wpa_ie_len, &ie) == 0)) {
    415 			wpa_printf(MSG_DEBUG, "   skip - "
    416 			    "could not parse WPA/RSN IE");
    417 			continue;
    418 		}
    419 		if (!(ie.proto & ssid->proto)) {
    420 			wpa_printf(MSG_DEBUG, "   skip - proto mismatch");
    421 			continue;
    422 		}
    423 		if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
    424 			wpa_printf(MSG_DEBUG, "   skip - PTK cipher mismatch");
    425 			continue;
    426 		}
    427 		if (!(ie.group_cipher & ssid->group_cipher)) {
    428 			wpa_printf(MSG_DEBUG, "   skip - GTK cipher mismatch");
    429 			continue;
    430 		}
    431 		if (!(ie.key_mgmt & ssid->key_mgmt)) {
    432 			wpa_printf(MSG_DEBUG, "   skip - key mgmt mismatch");
    433 			continue;
    434 		}
    435 
    436 		selected = bss;
    437 		*selected_ssid = ssid;
    438 		wpa_printf(MSG_DEBUG, "   selected");
    439 	}
    440 
    441 	return (selected);
    442 }
    443 
    444 
    445 static void
    446 wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s)
    447 {
    448 	dladm_wlan_ess_t results[MAX_SCANRESULTS];
    449 	int num;
    450 	dladm_wlan_ess_t *selected = NULL;
    451 	struct wpa_ssid *ssid;
    452 
    453 	(void) memset(results, 0, sizeof (dladm_wlan_ess_t) * MAX_SCANRESULTS);
    454 	num = wpa_s->driver->get_scan_results(wpa_s->handle, wpa_s->linkid,
    455 	    results, MAX_SCANRESULTS);
    456 	wpa_printf(MSG_DEBUG, "Scan results: %d", num);
    457 	if (num < 0)
    458 		return;
    459 	if (num > MAX_SCANRESULTS) {
    460 		wpa_printf(MSG_INFO, "Not enough room for all APs (%d < %d)",
    461 		    num, MAX_SCANRESULTS);
    462 		num = MAX_SCANRESULTS;
    463 	}
    464 
    465 	selected = wpa_supplicant_select_bss(wpa_s,
    466 	    wpa_s->conf->ssid, results, num, &ssid);
    467 
    468 	if (selected) {
    469 		if (wpa_s->reassociate ||
    470 		    memcmp(selected->we_bssid.wb_bytes, wpa_s->bssid,
    471 		    IEEE80211_ADDR_LEN) != 0) {
    472 			wpa_supplicant_associate(wpa_s, selected, ssid);
    473 		} else {
    474 			wpa_printf(MSG_DEBUG, "Already associated with the "
    475 			    "selected AP.");
    476 		}
    477 	} else {
    478 		wpa_printf(MSG_DEBUG, "No suitable AP found.");
    479 		wpa_supplicant_req_scan(wpa_s, 5, 0);	/* wait 5 seconds */
    480 	}
    481 }
    482 
    483 /*
    484  * wpa_event_handler - report a driver event for wpa_supplicant
    485  * @wpa_s: pointer to wpa_supplicant data; this is the @ctx variable registered
    486  *	with wpa_driver_events_init()
    487  * @event: event type (defined above)
    488  *
    489  * Driver wrapper code should call this function whenever an event is received
    490  * from the driver.
    491  */
    492 void
    493 wpa_event_handler(void *cookie, wpa_event_type event)
    494 {
    495 	struct wpa_supplicant *wpa_s = cookie;
    496 
    497 	switch (event) {
    498 	case EVENT_ASSOC:
    499 		wpa_printf(MSG_DEBUG, "\nAssociation event\n");
    500 		/* async event */
    501 		if (wpa_s->wpa_state < WPA_ASSOCIATED) {
    502 			wpa_s->wpa_state = WPA_ASSOCIATED;
    503 			if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
    504 				wpa_supplicant_cancel_auth_timeout(wpa_s);
    505 			} else {
    506 				/* Timeout for receiving first EAPOL packet */
    507 				wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
    508 			}
    509 		}
    510 		break;
    511 	case EVENT_DISASSOC:
    512 		if (wpa_s->wpa_state >= WPA_ASSOCIATED)
    513 			wpa_supplicant_req_scan(wpa_s, 0, 100000);
    514 		wpa_supplicant_mark_disassoc(wpa_s);
    515 		wpa_printf(MSG_DEBUG, "Disconnect event - remove keys");
    516 		if (wpa_s->key_mgmt != WPA_KEY_MGMT_NONE)
    517 			wpa_clear_keys(wpa_s, wpa_s->bssid);
    518 		break;
    519 	case EVENT_SCAN_RESULTS:
    520 		wpa_supplicant_scan_results(wpa_s);
    521 		/* reset vars */
    522 		(void) memset(wpa_s->rx_replay_counter, 0,
    523 		    WPA_REPLAY_COUNTER_LEN);
    524 		wpa_s->rx_replay_counter_set = 0;
    525 		wpa_s->renew_snonce = 1;
    526 		wpa_s->eapol_received = 0;
    527 		break;
    528 	default:
    529 		wpa_printf(MSG_INFO, "Unknown event %d", event);
    530 		break;
    531 	}
    532 }
    533 
    534 /* ARGSUSED */
    535 static void
    536 wpa_supplicant_terminate(int sig, void *eloop_ctx, void *signal_ctx)
    537 {
    538 	wpa_printf(MSG_INFO, "Signal %d received - terminating", sig);
    539 	eloop_terminate();
    540 }
    541 
    542 static int
    543 wpa_supplicant_driver_init(const char *link, struct wpa_supplicant *wpa_s)
    544 {
    545 	wpa_s->l2 = l2_packet_init(link, ETHERTYPE_EAPOL,
    546 	    wpa_supplicant_rx_eapol, wpa_s);
    547 	if (wpa_s->l2 == NULL)
    548 		return (-1);
    549 
    550 	if (l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
    551 		(void) fprintf(stderr, "Failed to get own L2 address\n");
    552 		return (-1);
    553 	}
    554 
    555 	if (wpa_s->driver->set_wpa(wpa_s->handle, wpa_s->linkid, 1) < 0) {
    556 		wpa_printf(MSG_ERROR, "Failed to enable WPA in the driver.");
    557 		return (-1);
    558 	}
    559 
    560 	wpa_clear_keys(wpa_s, NULL);
    561 	wpa_supplicant_req_scan(wpa_s, 0, 100000);
    562 
    563 	return (0);
    564 }
    565 
    566 static int door_id = -1;
    567 
    568 /* ARGSUSED */
    569 static void
    570 event_handler(void *cookie, char *argp, size_t asize,
    571     door_desc_t *dp, uint_t n_desc)
    572 {
    573 	wpa_event_type event;
    574 
    575 	/* LINTED E_BAD_PTR_CAST_ALIGN */
    576 	event = ((wl_events_t *)argp)->event;
    577 	wpa_event_handler(cookie, event);
    578 
    579 	(void) door_return(NULL, 0, NULL, 0);
    580 }
    581 
    582 /*
    583  * Create the driver to wpad door
    584  */
    585 int
    586 wpa_supplicant_door_setup(void *cookie, char *doorname)
    587 {
    588 	struct stat stbuf;
    589 	int error = 0;
    590 
    591 	wpa_printf(MSG_DEBUG, "wpa_supplicant_door_setup(%s)", doorname);
    592 	/*
    593 	 * Create the door
    594 	 */
    595 	door_id = door_create(event_handler, cookie,
    596 	    DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
    597 
    598 	if (door_id < 0) {
    599 		error = -1;
    600 		goto out;
    601 	}
    602 
    603 	if (stat(doorname, &stbuf) < 0) {
    604 		int newfd;
    605 		if ((newfd = creat(doorname, 0666)) < 0) {
    606 			(void) door_revoke(door_id);
    607 			door_id = -1;
    608 			error = -1;
    609 
    610 			goto out;
    611 		}
    612 		(void) close(newfd);
    613 	}
    614 
    615 	if (fattach(door_id, doorname) < 0) {
    616 		if ((errno != EBUSY) || (fdetach(doorname) < 0) ||
    617 		    (fattach(door_id, doorname) < 0)) {
    618 			(void) door_revoke(door_id);
    619 			door_id = -1;
    620 			error = -1;
    621 
    622 			goto out;
    623 		}
    624 	}
    625 
    626 out:
    627 	return (error);
    628 }
    629 
    630 void
    631 wpa_supplicant_door_destroy(char *doorname)
    632 {
    633 	wpa_printf(MSG_DEBUG, "wpa_supplicant_door_destroy(%s)\n", doorname);
    634 
    635 	if (door_id == -1)
    636 		return;
    637 
    638 	if (door_revoke(door_id) == -1) {
    639 		wpa_printf(MSG_ERROR, "failed to door_revoke(%d) %s, exiting.",
    640 		    door_id, strerror(errno));
    641 	}
    642 
    643 	if (fdetach(doorname) == -1) {
    644 		wpa_printf(MSG_ERROR, "failed to fdetach %s: %s, exiting.",
    645 		    doorname, strerror(errno));
    646 	}
    647 
    648 	(void) close(door_id);
    649 }
    650 
    651 static int
    652 wpa_config_parse_ssid(struct wpa_ssid *ssid, int line, const char *value)
    653 {
    654 	free(ssid->ssid);
    655 
    656 	ssid->ssid = (uint8_t *)strdup(value);
    657 	ssid->ssid_len = strlen(value);
    658 
    659 	if (ssid->ssid == NULL) {
    660 		wpa_printf(MSG_ERROR, "Invalid SSID '%s'.", line, value);
    661 		return (-1);
    662 	}
    663 	if (ssid->ssid_len > MAX_ESSID_LENGTH) {
    664 		free(ssid->ssid);
    665 		wpa_printf(MSG_ERROR, "Too long SSID '%s'.", line, value);
    666 		return (-1);
    667 	}
    668 	wpa_printf(MSG_MSGDUMP, "SSID: %s", ssid->ssid);
    669 	return (0);
    670 }
    671 
    672 static struct wpa_ssid *
    673 wpa_config_read_network(struct wpa_supplicant *wpa_s)
    674 {
    675 	struct wpa_ssid *ssid;
    676 	char buf[MAX_ESSID_LENGTH + 1];
    677 	dladm_secobj_class_t cl;
    678 	uint8_t psk[MAX_PSK_LENGTH + 1];
    679 	uint_t key_len;
    680 
    681 	wpa_printf(MSG_MSGDUMP, "Start of a new network configration");
    682 
    683 	ssid = (struct wpa_ssid *)malloc(sizeof (*ssid));
    684 	if (ssid == NULL)
    685 		return (NULL);
    686 	(void) memset(ssid, 0, sizeof (*ssid));
    687 
    688 	/*
    689 	 * Set default supported values
    690 	 */
    691 	ssid->proto = WPA_PROTO_WPA | WPA_PROTO_RSN;
    692 	ssid->pairwise_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP;
    693 	ssid->group_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP |
    694 	    WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40;
    695 	ssid->key_mgmt = WPA_KEY_MGMT_PSK; /* | WPA_KEY_MGMT_IEEE8021X; */
    696 
    697 	(void) memset(buf, 0, MAX_ESSID_LENGTH + 1);
    698 	wpa_s->driver->get_ssid(wpa_s->handle, wpa_s->linkid, (char *)buf);
    699 
    700 	(void) wpa_config_parse_ssid(ssid, 0, buf);
    701 
    702 	key_len = sizeof (psk);
    703 	(void) dladm_get_secobj(wpa_s->handle, (const char *)wpa_s->kname, &cl,
    704 	    psk, &key_len, DLADM_OPT_ACTIVE);
    705 	psk[key_len] = '\0';
    706 	ssid->passphrase = strdup((const char *)psk);
    707 
    708 	if (ssid->passphrase) {
    709 		pbkdf2_sha1(ssid->passphrase, (char *)ssid->ssid,
    710 		    ssid->ssid_len, 4096, ssid->psk, PMK_LEN);
    711 		wpa_hexdump(MSG_MSGDUMP, "PSK (from passphrase)",
    712 		    ssid->psk, PMK_LEN);
    713 		ssid->psk_set = 1;
    714 	}
    715 
    716 	if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) && !ssid->psk_set) {
    717 		wpa_printf(MSG_ERROR, "WPA-PSK accepted for key "
    718 		    "management, but no PSK configured.");
    719 		free(ssid);
    720 		ssid = NULL;
    721 	}
    722 
    723 	return (ssid);
    724 }
    725 
    726 struct wpa_config *
    727 wpa_config_read(void *arg)
    728 {
    729 	struct wpa_ssid *ssid;
    730 	struct wpa_config *config;
    731 	struct wpa_supplicant *wpa_s = arg;
    732 
    733 	config = malloc(sizeof (*config));
    734 	if (config == NULL)
    735 		return (NULL);
    736 	(void) memset(config, 0, sizeof (*config));
    737 	config->eapol_version = 1;	/* fixed value */
    738 
    739 	wpa_printf(MSG_DEBUG, "Reading configuration parameters from driver\n");
    740 
    741 	ssid = wpa_config_read_network(wpa_s);
    742 	if (ssid == NULL) {
    743 		wpa_config_free(config);
    744 		config = NULL;
    745 	} else {
    746 		config->ssid = ssid;
    747 	}
    748 
    749 	return (config);
    750 }
    751 
    752 void
    753 wpa_config_free(struct wpa_config *config)
    754 {
    755 	struct wpa_ssid *ssid = config->ssid;
    756 
    757 	if (ssid != NULL) {
    758 		free(ssid->ssid);
    759 		free(ssid->passphrase);
    760 		free(ssid);
    761 	}
    762 	free(config);
    763 }
    764 
    765 /*
    766  * make sure wpad is running under SMF context.
    767  */
    768 static boolean_t
    769 is_smf_context(void)
    770 {
    771 	char *fmri;
    772 
    773 	return (((fmri = getenv("SMF_FMRI")) != NULL) &&
    774 	    (strstr(fmri, SERVICE_NAME) != NULL));
    775 }
    776 
    777 int
    778 main(int argc, char *argv[])
    779 {
    780 	struct wpa_supplicant wpa_s;
    781 	char *link = NULL;
    782 	char *key = NULL;
    783 	dlpi_handle_t dh = NULL;
    784 	datalink_id_t linkid;
    785 	dladm_phys_attr_t dpa;
    786 	int c;
    787 	int exitcode;
    788 	char door_file[MAXPATHLEN];
    789 	dladm_handle_t handle;
    790 
    791 	if (!is_smf_context()) {
    792 		(void) fprintf(stderr,
    793 		    "wpad is an smf(5) managed service and cannot be run from "
    794 		    "the command line; please use dladm(1M).\n");
    795 		return (SMF_EXIT_ERR_NOSMF);
    796 	}
    797 
    798 	for (;;) {
    799 		c = getopt(argc, argv, "i:k:");
    800 		if (c < 0)
    801 			break;
    802 		switch (c) {
    803 		case 'i':
    804 			link = optarg;
    805 			break;
    806 		case 'k':
    807 			key = optarg;
    808 			break;
    809 		default:
    810 			return (SMF_EXIT_ERR_CONFIG);
    811 		}
    812 	}
    813 
    814 	/*
    815 	 * key name is required to retrieve PSK value through libwdladm APIs.
    816 	 * key is saved by dladm command by keyname
    817 	 * see dladm.
    818 	 */
    819 	if ((link == NULL) || (key == NULL)) {
    820 		wpa_printf(MSG_ERROR, "\nLink & key is required.");
    821 		return (-1);
    822 	}
    823 
    824 	if ((strlen(key) >= sizeof (wpa_s.kname)))  {
    825 		wpa_printf(MSG_ERROR, "Too long key name '%s'.", key);
    826 		return (-1);
    827 	}
    828 
    829 	if (daemon(0, 0))
    830 		return (-1);
    831 
    832 	/*
    833 	 * Hold this link open to prevent a link renaming operation.
    834 	 */
    835 	if (dlpi_open(link, &dh, 0) != DLPI_SUCCESS) {
    836 		wpa_printf(MSG_ERROR, "Failed to open link '%s'.", link);
    837 		return (-1);
    838 	}
    839 
    840 	/* This handle is stored in wpa_s when that struct is filled. */
    841 	if (dladm_open(&handle) != DLADM_STATUS_OK) {
    842 		wpa_printf(MSG_ERROR, "Failed to open dladm handle");
    843 		dlpi_close(dh);
    844 		return (-1);
    845 	}
    846 
    847 	if (dladm_name2info(handle, link, &linkid, NULL, NULL, NULL) !=
    848 	    DLADM_STATUS_OK) {
    849 		wpa_printf(MSG_ERROR, "Invalid link name '%s'.", link);
    850 		dladm_close(handle);
    851 		dlpi_close(dh);
    852 		return (-1);
    853 	}
    854 
    855 	/*
    856 	 * Get the device name of the link, which will be used as the door
    857 	 * file name used to communicate with the driver. Note that different
    858 	 * links use different doors.
    859 	 */
    860 	if (dladm_phys_info(handle, linkid, &dpa, DLADM_OPT_ACTIVE) !=
    861 	    DLADM_STATUS_OK) {
    862 		wpa_printf(MSG_ERROR,
    863 		    "Failed to get device name of link '%s'.", link);
    864 		dladm_close(handle);
    865 		dlpi_close(dh);
    866 		return (-1);
    867 	}
    868 	(void) snprintf(door_file, MAXPATHLEN, "%s_%s", WPA_DOOR, dpa.dp_dev);
    869 
    870 	(void) memset(&wpa_s, 0, sizeof (wpa_s));
    871 	wpa_s.driver = &wpa_driver_wifi_ops;
    872 	wpa_s.handle = handle;
    873 	wpa_s.linkid = linkid;
    874 	(void) strlcpy(wpa_s.kname, key, sizeof (wpa_s.kname));
    875 	eloop_init(&wpa_s);
    876 
    877 	/*
    878 	 * Setup default WPA/WPA2 configuration
    879 	 * get ESSID and PSK value
    880 	 */
    881 	wpa_s.conf = wpa_config_read(&wpa_s);
    882 	if (wpa_s.conf == NULL || wpa_s.conf->ssid == NULL) {
    883 		wpa_printf(MSG_ERROR, "\nNo networks (SSID) configured.\n");
    884 		exitcode = -1;
    885 		goto cleanup;
    886 	}
    887 
    888 	exitcode = 0;
    889 
    890 	/*
    891 	 * Setup door file to communicate with driver
    892 	 */
    893 	if (wpa_supplicant_door_setup(&wpa_s, door_file) != 0) {
    894 		wpa_printf(MSG_ERROR, "Failed to setup door(%s)", door_file);
    895 		exitcode = -1;
    896 		goto cleanup;
    897 	}
    898 
    899 	wpa_s.renew_snonce = 1;
    900 	if (wpa_supplicant_driver_init(link, &wpa_s) < 0) {
    901 		exitcode = -1;
    902 		goto cleanup;
    903 	}
    904 
    905 	/*
    906 	 * This link is hold again in wpa_supplicant_driver_init(), so that
    907 	 * we release the first reference.
    908 	 */
    909 	dlpi_close(dh);
    910 	dh = NULL;
    911 
    912 	wpa_printf(MSG_DEBUG, "=> eloop_run");
    913 
    914 	(void) eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL);
    915 	(void) eloop_register_signal(SIGTERM, wpa_supplicant_terminate, NULL);
    916 	(void) eloop_register_signal(SIGKILL, wpa_supplicant_terminate, NULL);
    917 
    918 	eloop_run();
    919 
    920 	wpa_printf(MSG_DEBUG, "<= eloop_run()");
    921 	wpa_supplicant_disassociate(&wpa_s, REASON_DEAUTH_LEAVING);
    922 
    923 	if (wpa_s.driver->set_wpa(wpa_s.handle, wpa_s.linkid, 0) < 0) {
    924 		wpa_printf(MSG_ERROR, "Failed to disable WPA in the driver.\n");
    925 	}
    926 
    927 cleanup:
    928 	wpa_supplicant_door_destroy(door_file);
    929 	/* The libdladm handle is closed in the following method */
    930 	wpa_supplicant_cleanup(&wpa_s);
    931 	eloop_destroy();
    932 
    933 	if (dh != NULL)
    934 		dlpi_close(dh);
    935 
    936 	return (exitcode);
    937 }
    938