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