xref: /titanic_44/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c (revision 98157a7002f4f2cf7978f3084ca5577f0a1d72b2)
1 /*
2  * Copyright 2008 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->linkid)) {
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->linkid, 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->linkid, 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->linkid, 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->linkid, 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->linkid, 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->linkid,
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->linkid, 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->linkid, 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->linkid,
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(const char *link, struct wpa_supplicant *wpa_s)
554 {
555 	wpa_s->l2 = l2_packet_init(link, 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->linkid, 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_id == -1)
645 		return;
646 
647 	if (door_revoke(door_id) == -1) {
648 		wpa_printf(MSG_ERROR, "failed to door_revoke(%d) %s, exiting.",
649 		    door_id, strerror(errno));
650 	}
651 
652 	if (fdetach(doorname) == -1) {
653 		wpa_printf(MSG_ERROR, "failed to fdetach %s: %s, exiting.",
654 		    doorname, strerror(errno));
655 	}
656 
657 	(void) close(door_id);
658 }
659 
660 static int
661 wpa_config_parse_ssid(struct wpa_ssid *ssid, int line, const char *value)
662 {
663 	free(ssid->ssid);
664 
665 	ssid->ssid = (uint8_t *)strdup(value);
666 	ssid->ssid_len = strlen(value);
667 
668 	if (ssid->ssid == NULL) {
669 		wpa_printf(MSG_ERROR, "Invalid SSID '%s'.", line, value);
670 		return (-1);
671 	}
672 	if (ssid->ssid_len > MAX_ESSID_LENGTH) {
673 		free(ssid->ssid);
674 		wpa_printf(MSG_ERROR, "Too long SSID '%s'.", line, value);
675 		return (-1);
676 	}
677 	wpa_printf(MSG_MSGDUMP, "SSID: %s", ssid->ssid);
678 	return (0);
679 }
680 
681 static struct wpa_ssid *
682 wpa_config_read_network(struct wpa_supplicant *wpa_s)
683 {
684 	struct wpa_ssid *ssid;
685 	char buf[MAX_ESSID_LENGTH + 1];
686 	dladm_secobj_class_t cl;
687 	uint8_t psk[MAX_PSK_LENGTH + 1];
688 	uint_t key_len;
689 
690 	wpa_printf(MSG_MSGDUMP, "Start of a new network configration");
691 
692 	ssid = (struct wpa_ssid *)malloc(sizeof (*ssid));
693 	if (ssid == NULL)
694 		return (NULL);
695 	(void) memset(ssid, 0, sizeof (*ssid));
696 
697 	/*
698 	 * Set default supported values
699 	 */
700 	ssid->proto = WPA_PROTO_WPA | WPA_PROTO_RSN;
701 	ssid->pairwise_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP;
702 	ssid->group_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP |
703 	    WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40;
704 	ssid->key_mgmt = WPA_KEY_MGMT_PSK; /* | WPA_KEY_MGMT_IEEE8021X; */
705 
706 	(void) memset(buf, 0, MAX_ESSID_LENGTH + 1);
707 	wpa_s->driver->get_ssid(wpa_s->linkid, (char *)buf);
708 
709 	(void) wpa_config_parse_ssid(ssid, 0, buf);
710 
711 	key_len = sizeof (psk);
712 	(void) dladm_get_secobj((const char *)wpa_s->kname, &cl, psk, &key_len,
713 	    DLADM_OPT_ACTIVE);
714 	psk[key_len] = '\0';
715 	ssid->passphrase = strdup((const char *)psk);
716 
717 	if (ssid->passphrase) {
718 		pbkdf2_sha1(ssid->passphrase, (char *)ssid->ssid,
719 		    ssid->ssid_len, 4096, ssid->psk, PMK_LEN);
720 		wpa_hexdump(MSG_MSGDUMP, "PSK (from passphrase)",
721 		    ssid->psk, PMK_LEN);
722 		ssid->psk_set = 1;
723 	}
724 
725 	if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) && !ssid->psk_set) {
726 		wpa_printf(MSG_ERROR, "WPA-PSK accepted for key "
727 		    "management, but no PSK configured.");
728 		free(ssid);
729 		ssid = NULL;
730 	}
731 
732 	return (ssid);
733 }
734 
735 struct wpa_config *
736 wpa_config_read(void *arg)
737 {
738 	struct wpa_ssid *ssid;
739 	struct wpa_config *config;
740 	struct wpa_supplicant *wpa_s = arg;
741 
742 	config = malloc(sizeof (*config));
743 	if (config == NULL)
744 		return (NULL);
745 	(void) memset(config, 0, sizeof (*config));
746 	config->eapol_version = 1;	/* fixed value */
747 
748 	wpa_printf(MSG_DEBUG, "Reading configuration parameters from driver\n");
749 
750 	ssid = wpa_config_read_network(wpa_s);
751 	if (ssid == NULL) {
752 		wpa_config_free(config);
753 		config = NULL;
754 	} else {
755 		config->ssid = ssid;
756 	}
757 
758 	return (config);
759 }
760 
761 void
762 wpa_config_free(struct wpa_config *config)
763 {
764 	struct wpa_ssid *ssid = config->ssid;
765 
766 	if (ssid != NULL) {
767 		free(ssid->ssid);
768 		free(ssid->passphrase);
769 		free(ssid);
770 	}
771 	free(config);
772 }
773 
774 static int
775 daemon(boolean_t nochdir, boolean_t noclose)
776 {
777 	int retv;
778 
779 	if ((retv = fork()) == -1)
780 		return (-1);
781 	if (retv != 0)
782 		_exit(EXIT_SUCCESS);
783 	if (setsid() == -1)
784 		return (-1);
785 
786 	if (!nochdir && chdir("/") == -1)
787 		return (-1);
788 
789 	if (!noclose) {
790 		(void) close(0);
791 		(void) close(1);
792 		(void) close(2);
793 		if ((retv = open("/dev/null", O_RDWR)) != -1) {
794 			(void) dup2(retv, 1);
795 			(void) dup2(retv, 2);
796 		}
797 	}
798 
799 	return (0);
800 }
801 
802 static void
803 usage(void)
804 {
805 	(void) printf("%s\n\n"
806 	    "usage:\n"
807 	    "  wpa_supplicant [-hv] -i<ifname> -k<keyname>\n"
808 	    "options:\n"
809 	    "  -h = show this help text\n"
810 	    "  -v = show version\n",
811 	    wpa_supplicant_version);
812 }
813 
814 int
815 main(int argc, char *argv[])
816 {
817 	struct wpa_supplicant wpa_s;
818 	char *link = NULL;
819 	char *key = NULL;
820 	dlpi_handle_t dh = NULL;
821 	datalink_id_t linkid;
822 	dladm_phys_attr_t dpa;
823 	int c;
824 	int exitcode;
825 	char door_file[WPA_STRSIZE];
826 
827 	for (;;) {
828 		c = getopt(argc, argv, "Dk:hi:v");
829 		if (c < 0)
830 			break;
831 		switch (c) {
832 		case 'D':
833 			wpa_debug_level = MSG_DEBUG;
834 			break;
835 		case 'h':
836 			usage();
837 			return (-1);
838 		case 'i':
839 			link = optarg;
840 			break;
841 		case 'k':
842 			key = optarg;
843 			break;
844 		case 'v':
845 			(void) printf("%s\n", wpa_supplicant_version);
846 			return (-1);
847 		default:
848 			usage();
849 			return (-1);
850 		}
851 	}
852 
853 	/*
854 	 * key name is required to retrieve PSK value through libwdladm APIs.
855 	 * key is saved by dladm command by keyname
856 	 * see dladm.
857 	 */
858 	if ((link == NULL) || (key == NULL)) {
859 		wpa_printf(MSG_ERROR, "\nLink & key is required.");
860 		return (-1);
861 	}
862 
863 	if ((strlen(key) >= sizeof (wpa_s.kname)))  {
864 		wpa_printf(MSG_ERROR, "Too long key name '%s'.", key);
865 		return (-1);
866 	}
867 
868 	if (daemon(0, 0))
869 		return (-1);
870 
871 	/*
872 	 * Hold this link open to prevent a link renaming operation.
873 	 */
874 	if (dlpi_open(link, &dh, 0) != DLPI_SUCCESS) {
875 		wpa_printf(MSG_ERROR, "Failed to open link '%s'.", link);
876 		return (-1);
877 	}
878 
879 	if (dladm_name2info(link, &linkid, NULL, NULL, NULL) !=
880 	    DLADM_STATUS_OK) {
881 		wpa_printf(MSG_ERROR, "Invalid link name '%s'.", link);
882 		dlpi_close(dh);
883 		return (-1);
884 	}
885 
886 	/*
887 	 * Get the device name of the link, which will be used as the door
888 	 * file name used to communicate with the driver. Note that different
889 	 * links use different doors.
890 	 */
891 	if (dladm_phys_info(linkid, &dpa, DLADM_OPT_ACTIVE) !=
892 	    DLADM_STATUS_OK) {
893 		wpa_printf(MSG_ERROR,
894 		    "Failed to get device name of link '%s'.", link);
895 		dlpi_close(dh);
896 		return (-1);
897 	}
898 	(void) snprintf(door_file, WPA_STRSIZE, "%s_%s", WPA_DOOR, dpa.dp_dev);
899 
900 	(void) memset(&wpa_s, 0, sizeof (wpa_s));
901 	wpa_s.driver = &wpa_driver_wifi_ops;
902 	wpa_s.linkid = linkid;
903 	(void) strlcpy(wpa_s.kname, key, sizeof (wpa_s.kname));
904 	eloop_init(&wpa_s);
905 
906 	/*
907 	 * Setup default WPA/WPA2 configuration
908 	 * get ESSID and PSK value
909 	 */
910 	wpa_s.conf = wpa_config_read(&wpa_s);
911 	if (wpa_s.conf == NULL || wpa_s.conf->ssid == NULL) {
912 		wpa_printf(MSG_ERROR, "\nNo networks (SSID) configured.\n");
913 		exitcode = -1;
914 		goto cleanup;
915 	}
916 
917 	exitcode = 0;
918 
919 	/*
920 	 * Setup door file to communicate with driver
921 	 */
922 	if (wpa_supplicant_door_setup(&wpa_s, door_file) != 0) {
923 		wpa_printf(MSG_ERROR, "Failed to setup door(%s)", door_file);
924 		exitcode = -1;
925 		goto cleanup;
926 	}
927 
928 	wpa_s.renew_snonce = 1;
929 	if (wpa_supplicant_driver_init(link, &wpa_s) < 0) {
930 		exitcode = -1;
931 		goto cleanup;
932 	}
933 
934 	/*
935 	 * This link is hold again in wpa_supplicant_driver_init(), so that
936 	 * we release the first reference.
937 	 */
938 	dlpi_close(dh);
939 	dh = NULL;
940 
941 	wpa_printf(MSG_DEBUG, "=> eloop_run");
942 
943 	(void) eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL);
944 	(void) eloop_register_signal(SIGTERM, wpa_supplicant_terminate, NULL);
945 	(void) eloop_register_signal(SIGKILL, wpa_supplicant_terminate, NULL);
946 
947 	eloop_run();
948 
949 	wpa_printf(MSG_DEBUG, "<= eloop_run()");
950 	wpa_supplicant_disassociate(&wpa_s, REASON_DEAUTH_LEAVING);
951 
952 	if (wpa_s.driver->set_wpa(wpa_s.linkid, 0) < 0) {
953 		wpa_printf(MSG_ERROR, "Failed to disable WPA in the driver.\n");
954 	}
955 
956 cleanup:
957 	wpa_supplicant_door_destroy(door_file);
958 	wpa_supplicant_cleanup(&wpa_s);
959 	eloop_destroy();
960 
961 	if (dh != NULL)
962 		dlpi_close(dh);
963 
964 	return (exitcode);
965 }
966