wpa_supplicant.c revision 10547:1a61a72b11d5
150276Speter/*
2166124Srafan * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
350276Speter * Use is subject to license terms.
450276Speter */
550276Speter
650276Speter/*
750276Speter * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
850276Speter * Sun elects to license this software under the BSD license.
950276Speter * See README for more details.
1050276Speter */
1150276Speter
1250276Speter#include <stdio.h>
1350276Speter#include <stdlib.h>
1450276Speter#include <stdarg.h>
1550276Speter#include <unistd.h>
1650276Speter#include <string.h>
1750276Speter#include <syslog.h>
1850276Speter#include <sys/stat.h>
1950276Speter#include <errno.h>
2050276Speter#include <signal.h>
2150276Speter#include <fcntl.h>
2250276Speter#include <door.h>
2350276Speter#include <libscf.h>
2450276Speter#include <libdladm.h>
2550276Speter#include <libdllink.h>
2650276Speter#include <sys/ethernet.h>
2750276Speter
2850276Speter#include "wpa_impl.h"
2950276Speter#include "wpa_enc.h"
3050276Speter#include "driver.h"
3150276Speter#include "eloop.h"
3250276Speter#include "l2_packet.h"
3350276Speter
3450276Speterextern struct wpa_driver_ops wpa_driver_wifi_ops;
3550276Speterint wpa_debug_level = MSG_ERROR;
3650276Speter
3750276Speter/*
3850276Speter * wpa_printf - conditional printf
39166124Srafan * @level: priority level (MSG_*) of the message
4050276Speter * @fmt: printf format string, followed by optional arguments
4176726Speter *
42166124Srafan * This function is used to print conditional debugging and error messages. The
43166124Srafan * output may be directed to stdout, stderr, and/or syslog based on
4462449Speter * configuration.
4562449Speter */
46166124Srafanvoid
4756639Speterwpa_printf(int level, char *fmt, ...)
48166124Srafan{
49166124Srafan	va_list ap;
50166124Srafan	char buffer[MAX_LOGBUF];
5156639Speter
52166124Srafan	if (level < wpa_debug_level)
5356639Speter		return;
5450276Speter
5556639Speter	va_start(ap, fmt);
56166124Srafan
5756639Speter	/* LINTED E_SEC_PRINTF_VAR_FMT */
58166124Srafan	(void) vsnprintf(buffer, sizeof (buffer), fmt, ap);
5962449Speter
6056639Speter	va_end(ap);
6162449Speter
62166124Srafan	syslog(LOG_NOTICE | LOG_DAEMON, "%s", buffer);
63166124Srafan}
6462449Speter
6562449Speter/*
66166124Srafan * wpa_hexdump - conditional hex dump
67166124Srafan * @level: priority level (MSG_*) of the message
68166124Srafan * @title: title of for the message
6950276Speter * @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 */
76void
77wpa_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
102static const char *
103wpa_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 */
120void
121wpa_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
145void
146wpa_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
155void
156wpa_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 */
163static void
164wpa_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
175void
176wpa_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
186void
187wpa_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
193static void
194wpa_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
210static void
211wpa_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
227static void
228wpa_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
234static int
235wpa_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
325static 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
364void
365wpa_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
378static dladm_wlan_ess_t *
379wpa_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
445static void
446wpa_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 */
492void
493wpa_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 */
535static void
536wpa_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
542static int
543wpa_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
566static int door_id = -1;
567
568/* ARGSUSED */
569static void
570event_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 */
585int
586wpa_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
626out:
627	return (error);
628}
629
630void
631wpa_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
651static int
652wpa_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
672static struct wpa_ssid *
673wpa_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
726struct wpa_config *
727wpa_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
752void
753wpa_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 */
768static boolean_t
769is_smf_context(void)
770{
771	char *fmri;
772
773	return (((fmri = getenv("SMF_FMRI")) != NULL) &&
774	    (strstr(fmri, SERVICE_NAME) != NULL));
775}
776
777int
778main(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
927cleanup:
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