xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c (revision 1f6352c601b7f3c744b4684ff673c813927da24c)
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 #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 static int
766 daemon(boolean_t nochdir, boolean_t noclose)
767 {
768 	int retv;
769 
770 	if ((retv = fork()) == -1)
771 		return (-1);
772 	if (retv != 0)
773 		_exit(EXIT_SUCCESS);
774 	if (setsid() == -1)
775 		return (-1);
776 
777 	if (!nochdir && chdir("/") == -1)
778 		return (-1);
779 
780 	if (!noclose) {
781 		(void) close(0);
782 		(void) close(1);
783 		(void) close(2);
784 		if ((retv = open("/dev/null", O_RDWR)) != -1) {
785 			(void) dup2(retv, 1);
786 			(void) dup2(retv, 2);
787 		}
788 	}
789 
790 	return (0);
791 }
792 
793 /*
794  * make sure wpad is running under SMF context.
795  */
796 static boolean_t
797 is_smf_context(void)
798 {
799 	char *fmri;
800 
801 	return (((fmri = getenv("SMF_FMRI")) != NULL) &&
802 	    (strstr(fmri, SERVICE_NAME) != NULL));
803 }
804 
805 int
806 main(int argc, char *argv[])
807 {
808 	struct wpa_supplicant wpa_s;
809 	char *link = NULL;
810 	char *key = NULL;
811 	dlpi_handle_t dh = NULL;
812 	datalink_id_t linkid;
813 	dladm_phys_attr_t dpa;
814 	int c;
815 	int exitcode;
816 	char door_file[MAXPATHLEN];
817 	dladm_handle_t handle;
818 
819 	if (!is_smf_context()) {
820 		(void) fprintf(stderr,
821 		    "wpad is an smf(5) managed service and cannot be run from "
822 		    "the command line; please use dladm(1M).\n");
823 		return (SMF_EXIT_ERR_NOSMF);
824 	}
825 
826 	for (;;) {
827 		c = getopt(argc, argv, "i:k:");
828 		if (c < 0)
829 			break;
830 		switch (c) {
831 		case 'i':
832 			link = optarg;
833 			break;
834 		case 'k':
835 			key = optarg;
836 			break;
837 		default:
838 			return (SMF_EXIT_ERR_CONFIG);
839 		}
840 	}
841 
842 	/*
843 	 * key name is required to retrieve PSK value through libwdladm APIs.
844 	 * key is saved by dladm command by keyname
845 	 * see dladm.
846 	 */
847 	if ((link == NULL) || (key == NULL)) {
848 		wpa_printf(MSG_ERROR, "\nLink & key is required.");
849 		return (-1);
850 	}
851 
852 	if ((strlen(key) >= sizeof (wpa_s.kname)))  {
853 		wpa_printf(MSG_ERROR, "Too long key name '%s'.", key);
854 		return (-1);
855 	}
856 
857 	if (daemon(0, 0))
858 		return (-1);
859 
860 	/*
861 	 * Hold this link open to prevent a link renaming operation.
862 	 */
863 	if (dlpi_open(link, &dh, 0) != DLPI_SUCCESS) {
864 		wpa_printf(MSG_ERROR, "Failed to open link '%s'.", link);
865 		return (-1);
866 	}
867 
868 	/* This handle is stored in wpa_s when that struct is filled. */
869 	if (dladm_open(&handle) != DLADM_STATUS_OK) {
870 		wpa_printf(MSG_ERROR, "Failed to open dladm handle");
871 		dlpi_close(dh);
872 		return (-1);
873 	}
874 
875 	if (dladm_name2info(handle, link, &linkid, NULL, NULL, NULL) !=
876 	    DLADM_STATUS_OK) {
877 		wpa_printf(MSG_ERROR, "Invalid link name '%s'.", link);
878 		dladm_close(handle);
879 		dlpi_close(dh);
880 		return (-1);
881 	}
882 
883 	/*
884 	 * Get the device name of the link, which will be used as the door
885 	 * file name used to communicate with the driver. Note that different
886 	 * links use different doors.
887 	 */
888 	if (dladm_phys_info(handle, linkid, &dpa, DLADM_OPT_ACTIVE) !=
889 	    DLADM_STATUS_OK) {
890 		wpa_printf(MSG_ERROR,
891 		    "Failed to get device name of link '%s'.", link);
892 		dladm_close(handle);
893 		dlpi_close(dh);
894 		return (-1);
895 	}
896 	(void) snprintf(door_file, MAXPATHLEN, "%s_%s", WPA_DOOR, dpa.dp_dev);
897 
898 	(void) memset(&wpa_s, 0, sizeof (wpa_s));
899 	wpa_s.driver = &wpa_driver_wifi_ops;
900 	wpa_s.handle = handle;
901 	wpa_s.linkid = linkid;
902 	(void) strlcpy(wpa_s.kname, key, sizeof (wpa_s.kname));
903 	eloop_init(&wpa_s);
904 
905 	/*
906 	 * Setup default WPA/WPA2 configuration
907 	 * get ESSID and PSK value
908 	 */
909 	wpa_s.conf = wpa_config_read(&wpa_s);
910 	if (wpa_s.conf == NULL || wpa_s.conf->ssid == NULL) {
911 		wpa_printf(MSG_ERROR, "\nNo networks (SSID) configured.\n");
912 		exitcode = -1;
913 		goto cleanup;
914 	}
915 
916 	exitcode = 0;
917 
918 	/*
919 	 * Setup door file to communicate with driver
920 	 */
921 	if (wpa_supplicant_door_setup(&wpa_s, door_file) != 0) {
922 		wpa_printf(MSG_ERROR, "Failed to setup door(%s)", door_file);
923 		exitcode = -1;
924 		goto cleanup;
925 	}
926 
927 	wpa_s.renew_snonce = 1;
928 	if (wpa_supplicant_driver_init(link, &wpa_s) < 0) {
929 		exitcode = -1;
930 		goto cleanup;
931 	}
932 
933 	/*
934 	 * This link is hold again in wpa_supplicant_driver_init(), so that
935 	 * we release the first reference.
936 	 */
937 	dlpi_close(dh);
938 	dh = NULL;
939 
940 	wpa_printf(MSG_DEBUG, "=> eloop_run");
941 
942 	(void) eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL);
943 	(void) eloop_register_signal(SIGTERM, wpa_supplicant_terminate, NULL);
944 	(void) eloop_register_signal(SIGKILL, wpa_supplicant_terminate, NULL);
945 
946 	eloop_run();
947 
948 	wpa_printf(MSG_DEBUG, "<= eloop_run()");
949 	wpa_supplicant_disassociate(&wpa_s, REASON_DEAUTH_LEAVING);
950 
951 	if (wpa_s.driver->set_wpa(wpa_s.handle, wpa_s.linkid, 0) < 0) {
952 		wpa_printf(MSG_ERROR, "Failed to disable WPA in the driver.\n");
953 	}
954 
955 cleanup:
956 	wpa_supplicant_door_destroy(door_file);
957 	/* The libdladm handle is closed in the following method */
958 	wpa_supplicant_cleanup(&wpa_s);
959 	eloop_destroy();
960 
961 	if (dh != NULL)
962 		dlpi_close(dh);
963 
964 	return (exitcode);
965 }
966