summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/Makefile.lint1
-rw-r--r--usr/src/cmd/Makefile1
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/structures.h6
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/wireless.c262
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/Makefile10
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile64
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/README772
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h43
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c372
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.c322
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.h110
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.c162
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.h57
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c1796
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.xml95
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.c272
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.h51
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h294
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c927
-rw-r--r--usr/src/cmd/dladm/dladm.c89
-rw-r--r--usr/src/lib/libdladm/Makefile.com2
-rw-r--r--usr/src/lib/libdladm/common/libdllink.h1
-rw-r--r--usr/src/lib/libdladm/common/libdlwlan.c674
-rw-r--r--usr/src/lib/libdladm/common/libdlwlan.h66
-rw-r--r--usr/src/lib/libdladm/common/mapfile-vers6
-rw-r--r--usr/src/lib/libdladm/common/secobj.c7
-rw-r--r--usr/src/lib/libsecdb/auth_attr.txt1
-rw-r--r--usr/src/lib/libsecdb/help/auths/Makefile1
-rw-r--r--usr/src/lib/libsecdb/help/auths/SmfWpaStates.html44
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt2
-rw-r--r--usr/src/lib/libsecdb/user_attr.txt8
-rw-r--r--usr/src/pkgdefs/Makefile2
-rw-r--r--usr/src/pkgdefs/SUNW0on/prototype_com1
-rw-r--r--usr/src/pkgdefs/SUNWcsu/prototype_com1
-rw-r--r--usr/src/pkgdefs/SUNWwpar/Makefile38
-rw-r--r--usr/src/pkgdefs/SUNWwpar/depend52
-rw-r--r--usr/src/pkgdefs/SUNWwpar/pkginfo.tmpl59
-rw-r--r--usr/src/pkgdefs/SUNWwpar/prototype_com52
-rw-r--r--usr/src/pkgdefs/SUNWwpar/prototype_i38650
-rw-r--r--usr/src/pkgdefs/SUNWwpar/prototype_sparc50
-rw-r--r--usr/src/pkgdefs/SUNWwpau/Makefile35
-rw-r--r--usr/src/pkgdefs/SUNWwpau/depend52
-rw-r--r--usr/src/pkgdefs/SUNWwpau/pkginfo.tmpl59
-rw-r--r--usr/src/pkgdefs/SUNWwpau/prototype_com49
-rw-r--r--usr/src/pkgdefs/SUNWwpau/prototype_i38650
-rw-r--r--usr/src/pkgdefs/SUNWwpau/prototype_sparc50
-rw-r--r--usr/src/pkgdefs/etc/exception_list_i3861
-rw-r--r--usr/src/pkgdefs/etc/exception_list_sparc1
-rw-r--r--usr/src/uts/common/Makefile.files3
-rw-r--r--usr/src/uts/common/inet/wifi_ioctl.h8
-rw-r--r--usr/src/uts/common/io/ath/ath_aux.c221
-rw-r--r--usr/src/uts/common/io/ath/ath_impl.h11
-rw-r--r--usr/src/uts/common/io/ath/ath_main.c62
-rw-r--r--usr/src/uts/common/io/dld/dld_drv.c4
-rw-r--r--usr/src/uts/common/io/ipw/ipw2100.c1
-rw-r--r--usr/src/uts/common/io/mac/plugins/mac_wifi.c27
-rw-r--r--usr/src/uts/common/io/net80211/net80211.c115
-rw-r--r--usr/src/uts/common/io/net80211/net80211_crypto.c24
-rw-r--r--usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c217
-rw-r--r--usr/src/uts/common/io/net80211/net80211_crypto_tkip.c267
-rw-r--r--usr/src/uts/common/io/net80211/net80211_impl.h7
-rw-r--r--usr/src/uts/common/io/net80211/net80211_input.c13
-rw-r--r--usr/src/uts/common/io/net80211/net80211_ioctl.c495
-rw-r--r--usr/src/uts/common/io/net80211/net80211_node.c76
-rw-r--r--usr/src/uts/common/io/net80211/net80211_output.c19
-rw-r--r--usr/src/uts/common/net/Makefile5
-rw-r--r--usr/src/uts/common/net/wpa.h195
-rw-r--r--usr/src/uts/common/sys/dld.h3
-rw-r--r--usr/src/uts/common/sys/ethernet.h4
-rw-r--r--usr/src/uts/common/sys/mac_wifi.h8
-rw-r--r--usr/src/uts/common/sys/net80211.h18
-rw-r--r--usr/src/uts/common/sys/net80211_crypto.h13
-rw-r--r--usr/src/uts/common/sys/net80211_proto.h17
73 files changed, 8706 insertions, 247 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index 4c9f107de7..f0e9bcadec 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -78,6 +78,7 @@ COMMON_SUBDIRS = \
cmd/cmd-inet/usr.lib/mipagent \
cmd/cmd-inet/usr.lib/pppoe \
cmd/cmd-inet/usr.lib/slpd \
+ cmd/cmd-inet/usr.lib/wpad \
cmd/cmd-inet/usr.lib/wanboot \
cmd/cmd-inet/usr.sadm \
cmd/cmd-inet/usr.sbin \
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 6771adec3f..1d741cb234 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -808,6 +808,7 @@ MANIFEST_SUBDIRS= \
cmd-inet/usr.lib/in.timed \
cmd-inet/usr.lib/inetd \
cmd-inet/usr.lib/slpd \
+ cmd-inet/usr.lib/wpad \
cmd-inet/usr.sbin \
cmd-inet/usr.sbin/in.ftpd \
cmd-inet/usr.sbin/in.rdisc \
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/structures.h b/usr/src/cmd/cmd-inet/lib/nwamd/structures.h
index d6e89ba3ce..76d400fcea 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/structures.h
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/structures.h
@@ -160,9 +160,9 @@ struct wireless_lan {
char *essid;
char *bssid;
char *signal_strength;
- char *raw_wepkey;
- dladm_wlan_wepkey_t *cooked_wepkey;
- boolean_t need_wepkey;
+ char *raw_key;
+ dladm_wlan_key_t *cooked_key;
+ dladm_wlan_secmode_t sec_mode;
char *wl_if_name;
};
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c b/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c
index a649866472..7a88027851 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c
@@ -36,7 +36,7 @@
* finish and if one of the WiFi interfaces is chosen to be active, the
* code will pop up a window showing the scan results and wait for the
* user's input on which AP to connect to and then complete the AP
- * connection and IP interface set up. WEP is supported to connect to
+ * connection and IP interface set up. WEP/WPA is supported to connect to
* those APs which require it. The code also maintains a list of known
* WiFi APs in the file KNOWN_WIFI_NETS. Whenever the code successfully
* connects to an AP, the AP's ESSID/BSSID will be added to that file.
@@ -104,6 +104,13 @@
#include "functions.h"
#include "variables.h"
+#define WLAN_ENC(sec) \
+ ((sec == DLADM_WLAN_SECMODE_WPA ? "WPA" : \
+ (sec == DLADM_WLAN_SECMODE_WEP ? "WEP" : "none")))
+
+#define NEED_ENC(sec) \
+ (sec == DLADM_WLAN_SECMODE_WPA || sec == DLADM_WLAN_SECMODE_WEP)
+
static pthread_mutex_t wifi_mutex;
static pthread_mutexattr_t wifi_mutex_attr;
static pthread_mutex_t wifi_init_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -129,12 +136,14 @@ static struct wireless_lan *wlans = NULL;
static uint_t wireless_lan_count = 0; /* allocated */
static uint_t wireless_lan_used = 0; /* used entries */
-static int wepkey_string_to_secobj_value(char *, uint8_t *, uint_t *);
-static int store_wepkey(struct wireless_lan *);
-static dladm_wlan_wepkey_t *retrieve_wepkey(const char *, const char *);
+static int key_string_to_secobj_value(char *, uint8_t *, uint_t *,
+ dladm_secobj_class_t);
+static int store_key(struct wireless_lan *);
+static dladm_wlan_key_t *retrieve_key(const char *, const char *,
+ dladm_secobj_class_t);
static boolean_t add_wlan_entry(struct interface *, char *, char *, char *,
- boolean_t);
+ dladm_wlan_secmode_t);
static boolean_t already_in_visited_wlan_list(const struct wireless_lan *);
static boolean_t check_wlan(const char *, const char *);
static boolean_t connect_or_autoconf(struct wireless_lan *, const char *);
@@ -143,7 +152,7 @@ static return_vals_t connect_to_new_wlan(const struct wireless_lan *, int,
static boolean_t find_wlan_entry(struct interface *, char *, char *);
static void free_wireless_lan(struct wireless_lan *);
static struct wireless_lan *get_specific_lan(void);
-static void get_user_wepkey(struct wireless_lan *);
+static void get_user_key(struct wireless_lan *);
static char *get_zenity_response(const char *);
static boolean_t wlan_autoconf(const char *ifname);
static int zenity_height(int);
@@ -178,27 +187,30 @@ init_mutexes(void)
* wlan is expected to be non-NULL.
*/
static void
-get_user_wepkey(struct wireless_lan *wlan)
+get_user_key(struct wireless_lan *wlan)
{
char zenity_cmd[1024];
char buf[1024];
FILE *zcptr;
+ dladm_secobj_class_t class;
/*
- * First, test if we have wepkey stored as secobj. If so,
+ * First, test if we have key stored as secobj. If so,
* no need to prompt for it.
*/
- wlan->cooked_wepkey = retrieve_wepkey(wlan->essid, wlan->bssid);
- if (wlan->cooked_wepkey != NULL) {
- dprintf("get_user_wepkey: retrieve_wepkey() returns non NULL");
+ class = (wlan->sec_mode == DLADM_WLAN_SECMODE_WEP ?
+ DLADM_SECOBJ_CLASS_WEP : DLADM_SECOBJ_CLASS_WPA);
+ wlan->cooked_key = retrieve_key(wlan->essid, wlan->bssid, class);
+ if (wlan->cooked_key != NULL) {
+ dprintf("get_user_key: retrieve_key() returns non NULL");
return;
}
(void) snprintf(zenity_cmd, sizeof (zenity_cmd),
"%s --entry --text=\"%s %s\""
" --title=\"%s\" --hide-text", ZENITY,
- gettext("Enter WEP key for WiFi network"), wlan->essid,
- gettext("Enter WEP key"));
+ gettext("Enter key for WiFi network"), wlan->essid,
+ gettext("Enter key"));
if (!valid_graphical_user(B_TRUE))
return;
@@ -206,17 +218,17 @@ get_user_wepkey(struct wireless_lan *wlan)
zcptr = popen(zenity_cmd, "r");
if (zcptr != NULL) {
if (fgets(buf, sizeof (buf), zcptr) != NULL) {
- wlan->raw_wepkey = strdup(buf);
- if (wlan->raw_wepkey != NULL) {
- /* Store WEP key persistently */
- if (store_wepkey(wlan) != 0) {
+ wlan->raw_key = strdup(buf);
+ if (wlan->raw_key != NULL) {
+ /* Store key persistently */
+ if (store_key(wlan) != 0) {
syslog(LOG_ERR,
- "get_user_wepkey: failed to store"
- " user specified WEP key");
+ "get_user_key: failed to store"
+ " user specified key");
}
} else {
syslog(LOG_ERR,
- "get_user_wepkey: strdup failed");
+ "get_user_key: strdup failed");
}
}
(void) pclose(zcptr);
@@ -261,17 +273,17 @@ free_wireless_lan(struct wireless_lan *wlp)
wlp->bssid = NULL;
free(wlp->signal_strength);
wlp->signal_strength = NULL;
- free(wlp->raw_wepkey);
- wlp->raw_wepkey = NULL;
- free(wlp->cooked_wepkey);
- wlp->cooked_wepkey = NULL;
+ free(wlp->raw_key);
+ wlp->raw_key = NULL;
+ free(wlp->cooked_key);
+ wlp->cooked_key = NULL;
free(wlp->wl_if_name);
wlp->wl_if_name = NULL;
}
static boolean_t
add_wlan_entry(struct interface *intf, char *essid, char *bssid,
- char *signal_strength, boolean_t wep)
+ char *signal_strength, dladm_wlan_secmode_t sec)
{
int n;
@@ -300,9 +312,9 @@ add_wlan_entry(struct interface *intf, char *essid, char *bssid,
wlans[n].bssid = strdup(bssid);
wlans[n].signal_strength = strdup(signal_strength);
wlans[n].wl_if_name = strdup(intf->if_name);
- wlans[n].need_wepkey = wep;
- wlans[n].raw_wepkey = NULL;
- wlans[n].cooked_wepkey = NULL;
+ wlans[n].sec_mode = sec;
+ wlans[n].raw_key = NULL;
+ wlans[n].cooked_key = NULL;
if (wlans[n].essid == NULL || wlans[n].bssid == NULL ||
wlans[n].signal_strength == NULL || wlans[n].wl_if_name == NULL) {
syslog(LOG_ERR, "add_wlan_entry: strdup failed");
@@ -435,8 +447,7 @@ wireless_scan(struct interface *ifp, void *arg)
static boolean_t
get_scan_results(void *arg, dladm_wlan_attr_t *attrp)
{
-
- boolean_t wep;
+ dladm_wlan_secmode_t sec;
char essid_name[DLADM_STRSIZE];
char bssid_name[DLADM_STRSIZE];
char strength[DLADM_STRSIZE];
@@ -445,10 +456,10 @@ get_scan_results(void *arg, dladm_wlan_attr_t *attrp)
(void) dladm_wlan_bssid2str(&attrp->wa_bssid, bssid_name);
(void) dladm_wlan_strength2str(&attrp->wa_strength, strength);
- wep = (attrp->wa_secmode == DLADM_WLAN_SECMODE_WEP);
+ sec = attrp->wa_secmode;
if (!find_wlan_entry(arg, essid_name, bssid_name) &&
- add_wlan_entry(arg, essid_name, bssid_name, strength, wep)) {
+ add_wlan_entry(arg, essid_name, bssid_name, strength, sec)) {
return (B_TRUE);
}
return (B_FALSE);
@@ -536,32 +547,50 @@ periodic_wireless_scan(void *arg)
}
/*
- * Below are functions used to handle storage/retrieval of WEP keys
+ * Below are functions used to handle storage/retrieval of keys
* for a given WLAN. The keys are stored/retrieved using dladm_set_secobj()
* and dladm_get_secobj().
*/
/*
- * Convert wepkey hexascii string to raw secobj value. This
+ * Convert key hexascii string to raw secobj value. This
* code is very similar to convert_secobj() in dladm.c, it would
* be good to have a libdladm function to convert values.
*/
static int
-wepkey_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp)
+key_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp,
+ dladm_secobj_class_t class)
{
size_t buf_len = strlen(buf);
- dprintf("before: wepkey_string_to_secobj_value: buf_len = %d", buf_len);
+ dprintf("before: key_string_to_secobj_value: buf_len = %d", buf_len);
if (buf_len == 0) {
syslog(LOG_ERR,
- "wepkey_string_to_secobj_value: empty WEP key");
+ "key_string_to_secobj_value: empty key");
return (-1);
}
if (buf[buf_len - 1] == '\n')
buf[--buf_len] = '\0';
- dprintf("after: wepkey_string_to_secobj_value: buf_len = %d", buf_len);
+ dprintf("after: key_string_to_secobj_value: buf_len = %d", buf_len);
+
+ if (class == DLADM_SECOBJ_CLASS_WPA) {
+ /*
+ * Per IEEE802.11i spec, the Pre-shared key (PSK) length should
+ * be between 8 and 63.
+ */
+ if (buf_len < 8 || buf_len > 63) {
+ syslog(LOG_ERR,
+ "key_string_to_secobj_value:"
+ " invalid WPA key length: buf_len = %d", buf_len);
+ return (-1);
+ }
+ (void) memcpy(obj_val, buf, (uint_t)buf_len);
+ *obj_lenp = buf_len;
+ return (0);
+ }
+
switch (buf_len) {
case 5: /* ASCII key sizes */
case 13:
@@ -573,7 +602,7 @@ wepkey_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp)
if (hexascii_to_octet(buf, (uint_t)buf_len, obj_val, obj_lenp)
!= 0) {
syslog(LOG_ERR,
- "wepkey_string_to_secobj_value: invalid WEP key");
+ "key_string_to_secobj_value: invalid WEP key");
return (-1);
}
break;
@@ -583,13 +612,13 @@ wepkey_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp)
hexascii_to_octet(buf + 2, (uint_t)buf_len - 2, obj_val,
obj_lenp) != 0) {
syslog(LOG_ERR,
- "wepkey_string_to_secobj_value: invalid WEP key");
+ "key_string_to_secobj_value: invalid WEP key");
return (-1);
}
break;
default:
syslog(LOG_ERR,
- "wepkey_string_to_secobj_value: invalid WEP key length");
+ "key_string_to_secobj_value: invalid WEP key length");
return (-1);
}
return (0);
@@ -617,33 +646,36 @@ set_key_name(const char *essid, const char *bssid, char *name, size_t nsz)
}
static int
-store_wepkey(struct wireless_lan *wlan)
+store_key(struct wireless_lan *wlan)
{
uint8_t obj_val[DLADM_SECOBJ_VAL_MAX];
uint_t obj_len = sizeof (obj_val);
char obj_name[DLADM_SECOBJ_NAME_MAX];
dladm_status_t status;
char errmsg[DLADM_STRSIZE];
+ dladm_secobj_class_t class;
/*
- * Name wepkey object for this WLAN so it can be later retrieved
+ * Name key object for this WLAN so it can be later retrieved
* (name is unique for each ESSID/BSSID combination).
*/
set_key_name(wlan->essid, wlan->bssid, obj_name, sizeof (obj_name));
- dprintf("store_wepkey: obj_name is %s", obj_name);
+ dprintf("store_key: obj_name is %s", obj_name);
- if (wepkey_string_to_secobj_value(wlan->raw_wepkey, obj_val, &obj_len)
- != 0) {
+ class = (wlan->sec_mode == DLADM_WLAN_SECMODE_WEP ?
+ DLADM_SECOBJ_CLASS_WEP : DLADM_SECOBJ_CLASS_WPA);
+ if (key_string_to_secobj_value(wlan->raw_key, obj_val, &obj_len,
+ class) != 0) {
/* above function logs internally on failure */
return (-1);
}
- status = dladm_set_secobj(obj_name, DLADM_SECOBJ_CLASS_WEP,
+ status = dladm_set_secobj(obj_name, class,
obj_val, obj_len,
DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_TEMP);
if (status != DLADM_STATUS_OK) {
- syslog(LOG_ERR, "store_wepkey: could not create secure object "
- "'%s' for wepkey: %s", obj_name,
+ syslog(LOG_ERR, "store_key: could not create secure object "
+ "'%s' for key: %s", obj_name,
dladm_status2str(status, errmsg));
return (-1);
}
@@ -654,77 +686,84 @@ store_wepkey(struct wireless_lan *wlan)
* besides just copying the value, so it is simpler just to call
* the retrieve function instead of doing it all here.
*
- * Since we just stored the key, retrieve_wepkey() "shouldn't"
+ * Since we just stored the key, retrieve_key() "shouldn't"
* fail. If it does fail, it's not the end of the world; a NULL
- * value for wlan->cooked_wepkey simply means this particular
+ * value for wlan->cooked_key simply means this particular
* attempt to connect will fail, and alternative connection
* options will be used.
*/
- wlan->cooked_wepkey = retrieve_wepkey(wlan->essid, wlan->bssid);
+ wlan->cooked_key = retrieve_key(wlan->essid, wlan->bssid, class);
return (0);
}
/*
- * retrieve_wepkey returns NULL if no wepkey was recovered from dladm
+ * retrieve_key returns NULL if no key was recovered from libdladm
*/
-static dladm_wlan_wepkey_t *
-retrieve_wepkey(const char *essid, const char *bssid)
+static dladm_wlan_key_t *
+retrieve_key(const char *essid, const char *bssid, dladm_secobj_class_t req)
{
dladm_status_t status;
char errmsg[DLADM_STRSIZE];
- dladm_wlan_wepkey_t *cooked_wepkey;
+ dladm_wlan_key_t *cooked_key;
dladm_secobj_class_t class;
/*
- * Newly-allocated wepkey must be freed by caller, or by
- * subsequent call to retrieve_wepkey().
+ * Newly-allocated key must be freed by caller, or by
+ * subsequent call to retrieve_key().
*/
- if ((cooked_wepkey = malloc(sizeof (dladm_wlan_wepkey_t))) == NULL) {
- syslog(LOG_ERR, "retrieve_wepkey: malloc failed");
+ if ((cooked_key = malloc(sizeof (dladm_wlan_key_t))) == NULL) {
+ syslog(LOG_ERR, "retrieve_key: malloc failed");
return (NULL);
}
- /* Set name appropriately to retrieve wepkey for this WLAN */
- set_key_name(essid, bssid, cooked_wepkey->wk_name,
+ /* Set name appropriately to retrieve key for this WLAN */
+ set_key_name(essid, bssid, cooked_key->wk_name,
DLADM_SECOBJ_NAME_MAX);
- dprintf("retrieve_wepkey: len = %d, object = %s\n",
- strlen(cooked_wepkey->wk_name), cooked_wepkey->wk_name);
- cooked_wepkey->wk_len = DLADM_SECOBJ_NAME_MAX;
- cooked_wepkey->wk_idx = 1;
+ dprintf("retrieve_key: len = %d, object = %s\n",
+ strlen(cooked_key->wk_name), cooked_key->wk_name);
+ cooked_key->wk_len = DLADM_SECOBJ_NAME_MAX;
+ cooked_key->wk_idx = 1;
/* Try the kernel first, then fall back to persistent storage. */
- status = dladm_get_secobj(cooked_wepkey->wk_name, &class,
- cooked_wepkey->wk_val, &cooked_wepkey->wk_len,
+ status = dladm_get_secobj(cooked_key->wk_name, &class,
+ cooked_key->wk_val, &cooked_key->wk_len,
DLADM_OPT_TEMP);
if (status != DLADM_STATUS_OK) {
- dprintf("retrieve_wepkey: dladm_get_secobj(TEMP) failed: %s",
+ dprintf("retrieve_key: dladm_get_secobj(TEMP) failed: %s",
dladm_status2str(status, errmsg));
- status = dladm_get_secobj(cooked_wepkey->wk_name, &class,
- cooked_wepkey->wk_val, &cooked_wepkey->wk_len,
+ status = dladm_get_secobj(cooked_key->wk_name, &class,
+ cooked_key->wk_val, &cooked_key->wk_len,
DLADM_OPT_PERSIST);
}
switch (status) {
case DLADM_STATUS_OK:
- dprintf("retrieve_wepkey: dladm_get_secobj succeeded: len %d",
- cooked_wepkey->wk_len);
+ dprintf("retrieve_key: dladm_get_secobj succeeded: len %d",
+ cooked_key->wk_len);
break;
case DLADM_STATUS_NOTFOUND:
/*
* We do not want an error in the case that the secobj
* is not found, since we then prompt for it.
*/
- free(cooked_wepkey);
+ free(cooked_key);
return (NULL);
default:
- syslog(LOG_ERR, "retrieve_wepkey: could not get wepkey "
- "from secure object '%s': %s", cooked_wepkey->wk_name,
+ syslog(LOG_ERR, "retrieve_key: could not get key "
+ "from secure object '%s': %s", cooked_key->wk_name,
dladm_status2str(status, errmsg));
- free(cooked_wepkey);
+ free(cooked_key);
return (NULL);
}
- return (cooked_wepkey);
+ if (class != req) { /* the key mismatch */
+ syslog(LOG_ERR, "retrieve_key: key type mismatch"
+ " from secure object '%s'", cooked_key->wk_name);
+ free(cooked_key);
+ return (NULL);
+ }
+
+ return (cooked_key);
}
/* Create the KNOWN_WIFI_NETS using info from the interface list. */
@@ -860,7 +899,7 @@ boolean_t
connect_chosen_lan(struct wireless_lan *reqlan, const char *ifname)
{
uint_t keycount;
- dladm_wlan_wepkey_t *key;
+ dladm_wlan_key_t *key;
dladm_wlan_attr_t attr;
dladm_status_t status;
uint_t flags = DLADM_WLAN_CONNECT_NOSCAN;
@@ -897,14 +936,14 @@ connect_chosen_lan(struct wireless_lan *reqlan, const char *ifname)
attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
}
- /* First check for the wepkey */
- if (reqlan->need_wepkey) {
- get_user_wepkey(reqlan);
- if (reqlan->cooked_wepkey == NULL)
+ /* First check for the key */
+ if (NEED_ENC(reqlan->sec_mode)) {
+ get_user_key(reqlan);
+ if (reqlan->cooked_key == NULL)
return (B_FALSE);
attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
- attr.wa_secmode = DLADM_WLAN_SECMODE_WEP;
- key = reqlan->cooked_wepkey;
+ attr.wa_secmode = reqlan->sec_mode;
+ key = reqlan->cooked_key;
keycount = 1;
dprintf("connect_chosen_lan: retrieved key");
} else {
@@ -1023,7 +1062,7 @@ connect_to_new_wlan(const struct wireless_lan *lanlist, int num,
buflen += snprintf(endbuf, sizeof (buf) - buflen,
"%d '%s' %s %s '%s' ", j,
lanlist[i].essid, lanlist[i].bssid,
- lanlist[i].need_wepkey ? "WEP" : "none",
+ WLAN_ENC(lanlist[i].sec_mode),
lanlist[i].signal_strength);
endbuf = buf + buflen;
}
@@ -1072,12 +1111,12 @@ connect_to_new_wlan(const struct wireless_lan *lanlist, int num,
}
dprintf("get_user_preference() returned essid %s, bssid %s, encr %s",
reqlan->essid, STRING(reqlan->bssid),
- reqlan->need_wepkey ? "WEP" : "none");
+ WLAN_ENC(reqlan->sec_mode));
- /* set wepkey before first time connection */
- if (reqlan->need_wepkey && reqlan->raw_wepkey == NULL &&
- reqlan->cooked_wepkey == NULL)
- get_user_wepkey(reqlan);
+ /* set key before first time connection */
+ if (NEED_ENC(reqlan->sec_mode) && reqlan->raw_key == NULL &&
+ reqlan->cooked_key == NULL)
+ get_user_key(reqlan);
/*
* now attempt to connect to selection, backing
@@ -1137,15 +1176,15 @@ prompt_for_visited(void)
sizeof (buf) - buflen,
"%d '%s' %s %s '%s' ",
i, wlp->essid, wlp->bssid,
- wlp->need_wepkey ? "WEP" : gettext("none"),
+ WLAN_ENC(wlp->sec_mode),
wlp->signal_strength);
endbuf = buf + buflen;
}
list[i-1].essid = wlp->essid;
list[i-1].bssid = wlp->bssid;
- list[i-1].need_wepkey = wlp->need_wepkey;
- list[i-1].raw_wepkey = wlp->raw_wepkey;
- list[i-1].cooked_wepkey = wlp->cooked_wepkey;
+ list[i-1].sec_mode = wlp->sec_mode;
+ list[i-1].raw_key = wlp->raw_key;
+ list[i-1].cooked_key = wlp->cooked_key;
list[i-1].signal_strength = wlp->signal_strength;
list[i-1].wl_if_name = wlp->wl_if_name;
}
@@ -1343,17 +1382,17 @@ get_user_preference(const char *cmd, const char *compare_other,
if ((sel->bssid != NULL) && ((wlp->bssid = strdup(sel->bssid)) == NULL))
goto dup_error;
- wlp->need_wepkey = sel->need_wepkey;
+ wlp->sec_mode = sel->sec_mode;
- if ((sel->raw_wepkey != NULL) &&
- ((wlp->raw_wepkey = strdup(sel->raw_wepkey)) == NULL))
+ if ((sel->raw_key != NULL) &&
+ ((wlp->raw_key = strdup(sel->raw_key)) == NULL))
goto dup_error;
- if (sel->cooked_wepkey != NULL) {
- wlp->cooked_wepkey = malloc(sizeof (dladm_wlan_wepkey_t));
- if (wlp->cooked_wepkey == NULL)
+ if (sel->cooked_key != NULL) {
+ wlp->cooked_key = malloc(sizeof (dladm_wlan_key_t));
+ if (wlp->cooked_key == NULL)
goto dup_error;
- *(wlp->cooked_wepkey) = *(sel->cooked_wepkey);
+ *(wlp->cooked_key) = *(sel->cooked_key);
}
if ((sel->signal_strength != NULL) &&
@@ -1365,7 +1404,7 @@ get_user_preference(const char *cmd, const char *compare_other,
goto dup_error;
dprintf("selected: %s, %s, %s, '%s', %s", wlp->essid,
- STRING(wlp->bssid), wlp->need_wepkey ? "WEP" : "none",
+ STRING(wlp->bssid), WLAN_ENC(wlp->sec_mode),
STRING(wlp->signal_strength), STRING(wlp->wl_if_name));
free(response);
@@ -1414,13 +1453,18 @@ get_specific_lan(void)
wlp->essid = response;
(void) snprintf(specify_str, sizeof (specify_str), ZENITY
- " --list --title=\"%s\" --text=\"%s\" --column=\"%s\" none wep",
+ " --list --title=\"%s\" --text=\"%s\" --column=\"%s\" none wep wpa",
gettext("Security"), gettext("Enter security"),
gettext("Type"));
response = get_zenity_response(specify_str);
- if (response != NULL && strcmp(response, "wep") == 0)
- wlp->need_wepkey = B_TRUE;
+ wlp->sec_mode = DLADM_WLAN_SECMODE_NONE;
+ if (response != NULL) {
+ if (strcmp(response, "wep") == 0)
+ wlp->sec_mode = DLADM_WLAN_SECMODE_WEP;
+ else if (strcmp(response, "wpa") == 0)
+ wlp->sec_mode = DLADM_WLAN_SECMODE_WPA;
+ }
free(response);
return (wlp);
@@ -1513,9 +1557,9 @@ start_over:
}
new_wlan->wifi_net->essid = strdup(cur_wlans[i].essid);
new_wlan->wifi_net->bssid = strdup(cur_wlans[i].bssid);
- new_wlan->wifi_net->raw_wepkey = NULL;
- new_wlan->wifi_net->cooked_wepkey = NULL;
- new_wlan->wifi_net->need_wepkey = cur_wlans[i].need_wepkey;
+ new_wlan->wifi_net->raw_key = NULL;
+ new_wlan->wifi_net->cooked_key = NULL;
+ new_wlan->wifi_net->sec_mode = cur_wlans[i].sec_mode;
new_wlan->wifi_net->signal_strength =
strdup(cur_wlans[i].signal_strength);
new_wlan->wifi_net->wl_if_name =
diff --git a/usr/src/cmd/cmd-inet/usr.lib/Makefile b/usr/src/cmd/cmd-inet/usr.lib/Makefile
index 4f283a0e74..64a06e1009 100644
--- a/usr/src/cmd/cmd-inet/usr.lib/Makefile
+++ b/usr/src/cmd/cmd-inet/usr.lib/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -28,7 +27,8 @@
SUBDIRS= dhcp dsvclockd in.chargend in.daytimed \
in.discardd in.echod in.dhcpd in.mpathd in.ndpd \
- in.ripngd in.timed inetd mipagent ncaconfd pppoe slpd wanboot
+ in.ripngd in.timed inetd mipagent ncaconfd pppoe slpd wanboot \
+ wpad
MSGSUBDIRS= dsvclockd in.dhcpd inetd ncaconfd wanboot
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile
new file mode 100644
index 0000000000..7f649e7668
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile
@@ -0,0 +1,64 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = wpad
+MANIFEST = wpa.xml
+OBJS = wpa_supplicant.o wpa.o wpa_enc.o eloop.o \
+ driver_wifi.o l2_packet.o
+SRCS = $(OBJS:%.o=%.c)
+
+include ../../../Makefile.cmd
+
+ROOTMANIFESTDIR = $(ROOTSVCNETWORK)
+
+LDFLAGS += -L/usr/sfw/lib -R/usr/sfw/lib
+LDLIBS += -ldladm -ldlpi
+all install := LDLIBS += -lcrypto
+
+CPPFLAGS += -I/usr/sfw/include
+LINTFLAGS += -u -erroff=E_BAD_PTR_CAST_ALIGN
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+install: all $(ROOTLIBINETPROG) $(ROOTMANIFEST)
+
+check: $(CHKMANIFEST)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/README b/usr/src/cmd/cmd-inet/usr.lib/wpad/README
new file mode 100644
index 0000000000..ad3f334324
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/README
@@ -0,0 +1,772 @@
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+WPA Supplicant
+==============
+
+Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+All Rights Reserved.
+
+Sun elects to license this software under the BSD license.
+
+
+License
+-------
+
+BSD license:
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the name(s) of the above-listed copyright holder(s) nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+Features
+--------
+
+Supported WPA/IEEE 802.11i features:
+- WPA-PSK ("WPA-Personal")
+- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise")
+ Following authentication methods are supported with an integrate IEEE 802.1X
+ Supplicant:
+ * EAP-TLS
+ * EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)
+ * EAP-PEAP/TLS (both PEAPv0 and PEAPv1)
+ * EAP-PEAP/GTC (both PEAPv0 and PEAPv1)
+ * EAP-PEAP/OTP (both PEAPv0 and PEAPv1)
+ * EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)
+ * EAP-TTLS/EAP-MD5-Challenge
+ * EAP-TTLS/EAP-GTC
+ * EAP-TTLS/EAP-OTP
+ * EAP-TTLS/EAP-MSCHAPv2
+ * EAP-TTLS/EAP-TLS
+ * EAP-TTLS/MSCHAPv2
+ * EAP-TTLS/MSCHAP
+ * EAP-TTLS/PAP
+ * EAP-TTLS/CHAP
+ * EAP-SIM
+ * LEAP (note: only with WEP keys, i.e., not for WPA; in addition, LEAP
+ requires special support from the driver for IEEE 802.11
+ authentication)
+ (following methods are supported, but since they do not generate keying
+ material, they cannot be used with WPA or IEEE 802.1X WEP keying)
+ * EAP-MD5-Challenge
+ * EAP-MSCHAPv2
+ * EAP-GTC
+ * EAP-OTP
+ Alternatively, an external program, e.g., Xsupplicant, can be used for EAP
+ authentication.
+- key management for CCMP, TKIP, WEP104, WEP40
+- RSN/WPA2 (IEEE 802.11i)
+ * pre-authentication
+ * PMKSA caching
+
+
+
+Requirements
+------------
+
+Current hardware/software requirements:
+- Linux kernel 2.4.x or 2.6.x
+- Linux Wireless Extensions v15 or newer
+- drivers:
+ Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x)
+ in Managed mode ('iwconfig wlan0 mode managed'). Please note that
+ station firmware version needs to be 1.7.0 or newer to work in
+ WPA mode.
+
+ Linuxant DriverLoader (http://www.linuxant.com/driverloader/)
+ with Windows NDIS driver for your wlan card supporting WPA.
+
+ Agere Systems Inc. Linux Driver
+ (http://www.agere.com/support/drivers/)
+ Please note that the driver interface file (driver_hermes.c) and
+ hardware specific include files are not included in the
+ wpa_supplicant distribution. You will need to copy these from the
+ source package of the Agere driver.
+
+ madwifi driver for cards based on Atheros chip set (ar521x)
+ (http://sourceforge.net/projects/madwifi/)
+ Please note that you will need to modify the wpa_supplicant Makefile
+ to use correct path for madwifi driver root directory
+ (CFLAGS += -I../madwifi/wpa line in Makefile).
+
+ ATMEL AT76C5XXx driver for USB and PCMCIA cards
+ (http://atmelwlandriver.sourceforge.net/).
+
+ Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with
+ Windows NDIS driver.
+
+ In theory, any driver that supports Linux wireless extensions can be
+ used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in
+ configuration file.
+
+wpa_supplicant was designed to be portable for different drivers and
+operating systems. Hopefully, support for more wlan cards will be
+added in the future. See developer.txt for more information about the
+design of wpa_supplicant and porting to other drivers. One main goal
+is to add full WPA/WPA2 support to Linux wireless extensions to allow
+new drivers to be supported without having to implement new
+driver-specific interface code in wpa_supplicant.
+
+Optional libraries for layer2 packet processing:
+- libpcap (tested with 0.7.2, most relatively recent versions assumed to work,
+ this is likely to be available with most distributions,
+ http://tcpdump.org/)
+- libdnet (tested with v1.4, most versions assumed to work,
+ http://libdnet.sourceforge.net/)
+
+These libraries are _not_ used in the default build. Instead, internal
+Linux specific implementation is used. libpcap/libdnet are more
+portable and they can be used by modifying Makefile (define
+USE_DNET_PCAP and link with these libraries).
+
+
+Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS:
+- openssl (tested with 0.9.7c and 0.9.7d, assumed to work with most
+ relatively recent versions; this is likely to be available with most
+ distributions, http://www.openssl.org/)
+
+This library is only needed when EAP-TLS, EAP-PEAP, or EAP-TTLS
+support is enabled. WPA-PSK mode does not require this or EAPOL/EAP
+implementation. A configuration file, .config, for compilation is
+needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5,
+EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so
+they should only be enabled if testing the EAPOL/EAP state
+machines. However, there can be used as inner authentication
+algorithms with EAP-PEAP and EAP-TTLS.
+
+See Building and installing section below for more detailed
+information about the wpa_supplicant build time configuration.
+
+
+
+WPA
+---
+
+The original security mechanism of IEEE 802.11 standard was not
+designed to be strong and has proved to be insufficient for most
+networks that require some kind of security. Task group I (Security)
+of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
+to address the flaws of the base standard and has in practice
+completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
+802.11 standard was approved in June 2004 and this amendment is likely
+to be published in July 2004.
+
+Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
+IEEE 802.11i work (draft 3.0) to define a subset of the security
+enhancements that can be implemented with existing wlan hardware. This
+is called Wi-Fi Protected Access<TM> (WPA). This has now become a
+mandatory component of interoperability testing and certification done
+by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web
+site (http://www.wi-fi.org/OpenSection/protected_access.asp).
+
+IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
+for protecting wireless networks. WEP uses RC4 with 40-bit keys,
+24-bit initialization vector (IV), and CRC32 to protect against packet
+forgery. All these choice have proved to be insufficient: key space is
+too small against current attacks, RC4 key scheduling is insufficient
+(beginning of the pseudorandom stream should be skipped), IV space is
+too small and IV reuse makes attacks easier, there is no replay
+protection, and non-keyed authentication does not protect against bit
+flipping packet data.
+
+WPA is an intermediate solution for the security issues. It uses
+temporal key integrity protocol (TKIP) to replace WEP. TKIP is a
+compromise on strong security and possibility to use existing
+hardware. It still uses RC4 for the encryption like WEP, but with
+per-packet RC4 keys. In addition, it implements replay protection,
+keyed packet authentication mechanism (Michael MIC).
+
+Keys can be managed using two different mechanisms. WPA can either use
+an external authentication server (e.g., RADIUS) and EAP just like
+IEEE 802.1X is using or pre-shared keys without need for additional
+servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal",
+respectively. Both mechanisms will generate a master session key for
+the Authenticator (AP) and Supplicant (client station).
+
+WPA implements a new key handshake (4-Way Handshake and Group Key
+Handshake) for generating and exchanging data encryption keys between
+the Authenticator and Supplicant. This handshake is also used to
+verify that both Authenticator and Supplicant know the master session
+key. These handshakes are identical regardless of the selected key
+management mechanism (only the method for generating master session
+key changes).
+
+
+
+IEEE 802.11i / WPA2
+-------------------
+
+The design for parts of IEEE 802.11i that were not included in WPA has
+finished (May 2004) and this amendment to IEEE 802.11 was approved in
+June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new
+version of WPA called WPA2. This includes, e.g., support for more
+robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC)
+to replace TKIP and optimizations for handoff (reduced number of
+messages in initial key handshake, pre-authentication, key caching).
+
+Some wireless LAN vendors are already providing support for CCMP in
+their WPA products. There is no "official" interoperability
+certification for CCMP and/or mixed modes using both TKIP and CCMP, so
+some interoperability issues can be expected even though many
+combinations seem to be working with equipment from different vendors.
+Certification for WPA2 is likely to start during the second half of
+2004.
+
+
+
+wpa_supplicant
+--------------
+
+wpa_supplicant is an implementation of the WPA Supplicant component,
+i.e., the part that runs in the client stations. It implements WPA key
+negotiation with a WPA Authenticator and EAP authentication with
+Authentication Server. In addition, it controls the roaming and IEEE
+802.11 authentication/association of the wlan driver.
+
+wpa_supplicant is designed to be a "daemon" program that runs in the
+background and acts as the backend component controlling the wireless
+connection. wpa_supplicant supports separate frontend programs and an
+example text-based frontend, wpa_cli, is included with wpa_supplicant.
+
+Following steps are used when associating with an AP using WPA:
+
+- wpa_supplicant requests the kernel driver to scan neighboring BSSes
+- wpa_supplicant selects a BSS based on its configuration
+- wpa_supplicant requests the kernel driver to associate with the chosen
+ BSS
+- If WPA-EAP: integrated IEEE 802.1X Supplicant or external Xsupplicant
+ completes EAP authentication with the authentication server (proxied
+ by the Authenticator in the AP)
+- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant
+- If WPA-PSK: wpa_supplicant uses PSK as the master session key
+- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake
+ with the Authenticator (AP)
+- wpa_supplicant configures encryption keys for unicast and broadcast
+- normal data packets can be transmitted and received
+
+
+
+Building and installing
+-----------------------
+
+In order to be able to build wpa_supplicant, you will first need to
+select which parts of it will be included. This is done by creating a
+build time configuration file, .config, in the wpa_supplicant root
+directory. Configuration options are text lines using following
+format: CONFIG_<option>=y. Lines starting with # are considered
+comments and are ignored.
+
+The build time configuration can be used to select only the needed
+features and limit the binary size and requirements for external
+libraries. The main configuration parts are the selection of which
+driver interfaces (e.g., hostap, madwifi, ..) and which authentication
+methods (e.g., EAP-TLS, EAP-PEAP, ..) are included.
+
+Following build time configuration options are used to control IEEE
+802.1X/EAPOL and EAP state machines and all EAP methods. Including
+TLS, PEAP, or TTLS will require linking wpa_supplicant with openssl
+library for TLS implementation.
+
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_LEAP=y
+
+Following option can be used to include GSM SIM/USIM interface for GSM
+authentication algorithm (for EAP-SIM). This requires pcsc-lite
+(http://www.linuxnet.com/) for smart card access.
+
+CONFIG_PCSC=y
+
+Following options can be added to .config to select which driver
+interfaces are included. Prism54.org driver is not yet complete and
+Hermes driver interface needs to be downloaded from Agere (see above).
+Most Linux driver need to include CONFIG_WIRELESS_EXTENSION.
+
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_DRIVER_HOSTAP=y
+CONFIG_DRIVER_PRISM54=y
+CONFIG_DRIVER_HERMES=y
+CONFIG_DRIVER_MADWIFI=y
+CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NDISWRAPPER=y
+
+Following example includes all features and driver interfaces that are
+included in the wpa_supplicant package:
+
+CONFIG_DRIVER_HOSTAP=y
+CONFIG_DRIVER_PRISM54=y
+CONFIG_DRIVER_HERMES=y
+CONFIG_DRIVER_MADWIFI=y
+CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NDISWRAPPER=y
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_LEAP=y
+CONFIG_PCSC=y
+
+EAP-PEAP and EAP-TTLS will automatically include configured EAP
+methods (MD5, OTP, GTC, MSCHAPV2) for inner authentication selection.
+
+
+After you have created a configuration file, you can build
+wpa_supplicant and wpa_cli with 'make' command. You may then install
+the binaries to a suitable system directory, e.g., /usr/local/bin.
+
+Example commands:
+
+# build wpa_supplicant and wpa_cli
+make
+# install binaries (this may need root privileges)
+cp wpa_cli wpa_supplicant /usr/local/bin
+
+
+You will need to make a configuration file, e.g.,
+/etc/wpa_supplicant.conf, with network configuration for the networks
+you are going to use. Configuration file section below includes
+explanation fo the configuration file format and includes various
+examples. Once the configuration is ready, you can test whether the
+configuration work by first running wpa_supplicant with following
+command to start it on foreground with debugging enabled:
+
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
+
+Assuming everything goes fine, you can start using following command
+to start wpa_supplicant on background without debugging:
+
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
+
+Please note that if you included more than one driver interface in the
+build time configuration (.config), you may need to specify which
+interface to use by including -D<driver name> option on the command
+line. See following section for more details on command line options
+for wpa_supplicant.
+
+
+
+Command line options
+--------------------
+
+usage:
+ wpa_supplicant [-BddehLqqvw] -i<ifname> -c<config file> [-D<driver>]
+
+options:
+ -B = run daemon in the background
+ -d = increase debugging verbosity (-dd even more)
+ -e = use external IEEE 802.1X Supplicant (e.g., xsupplicant)
+ (this disables the internal Supplicant)
+ -h = show this help text
+ -L = show license (GPL and BSD)
+ -q = decrease debugging verbosity (-qq even less)
+ -v = show version
+ -w = wait for interface to be added, if needed
+
+drivers:
+ hostap = Host AP driver (Intersil Prism2/2.5/3) [default]
+ (this can also be used with Linuxant DriverLoader)
+ prism54 = Prism54.org driver (Intersil Prism GT/Duette/Indigo)
+ not yet fully implemented
+ hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II)
+ madwifi = MADWIFI 802.11 support (Atheros, etc.)
+ atmel = ATMEL AT76C5XXx (USB, PCMCIA)
+ wext = Linux wireless extensions (generic)
+ ndiswrapper = Linux ndiswrapper
+
+In most common cases, wpa_supplicant is started with
+
+wpa_supplicant -Bw -c/etc/wpa_supplicant.conf -iwlan0
+
+This makes the process fork into background and wait for the wlan0
+interface if it is not available at startup time.
+
+
+
+Configuration file
+------------------
+
+wpa_supplicant is configured using a text file that lists all accepted
+networks and security policies, including pre-shared keys. See
+example configuration file, wpa_supplicant.conf, for detailed
+information about the configuration format and supported fields.
+
+Changes to configuration file can be reloaded be sending SIGHUP signal
+to wpa_supplicant ('killall -HUP wpa_supplicant'). Similarily,
+reloading can be triggered with 'wpa_cli reconfigure' command.
+
+Configuration file can include one or more network blocks, e.g., one
+for each used SSID. wpa_supplicant will automatically select the best
+betwork based on the order of network blocks in the configuration
+file, network security level (WPA/WPA2 is prefered), and signal
+strength.
+
+Example configuration files for some common configurations:
+
+1) WPA-Personal (PSK) as home network and WPA-Enterprise with EAP-TLS as work
+ network
+
+# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+#
+# home network; allow all valid ciphers
+network={
+ ssid="home"
+ scan_ssid=1
+ key_mgmt=WPA-PSK
+ psk="very secret passphrase"
+}
+#
+# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
+network={
+ ssid="work"
+ scan_ssid=1
+ key_mgmt=WPA-EAP
+ pairwise=CCMP TKIP
+ group=CCMP TKIP
+ eap=TLS
+ identity="user@example.com"
+ ca_cert="/etc/cert/ca.pem"
+ client_cert="/etc/cert/user.pem"
+ private_key="/etc/cert/user.prv"
+ private_key_passwd="password"
+}
+
+
+2) WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel
+ (e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series)
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="example"
+ scan_ssid=1
+ key_mgmt=WPA-EAP
+ eap=PEAP
+ identity="user@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ phase1="peaplabel=0"
+ phase2="auth=MSCHAPV2"
+}
+
+
+3) EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
+ unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="example"
+ scan_ssid=1
+ key_mgmt=WPA-EAP
+ eap=TTLS
+ identity="user@example.com"
+ anonymous_identity="anonymous@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ phase2="auth=MD5"
+}
+
+
+4) IEEE 802.1X (i.e., no WPA) with dynamic WEP keys (require both unicast and
+ broadcast); use EAP-TLS for authentication
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="1x-test"
+ scan_ssid=1
+ key_mgmt=IEEE8021X
+ eap=TLS
+ identity="user@example.com"
+ ca_cert="/etc/cert/ca.pem"
+ client_cert="/etc/cert/user.pem"
+ private_key="/etc/cert/user.prv"
+ private_key_passwd="password"
+ eapol_flags=3
+}
+
+
+5) Catch all example that allows more or less all configuration modes. The
+ configuration options are used based on what security policy is used in the
+ selected SSID. This is mostly for testing and is not recommended for normal
+ use.
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="example"
+ scan_ssid=1
+ key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+ pairwise=CCMP TKIP
+ group=CCMP TKIP WEP104 WEP40
+ psk="very secret passphrase"
+ eap=TTLS PEAP TLS
+ identity="user@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ client_cert="/etc/cert/user.pem"
+ private_key="/etc/cert/user.prv"
+ private_key_passwd="password"
+ phase1="peaplabel=0"
+ ca_cert2="/etc/cert/ca2.pem"
+ client_cert2="/etc/cer/user.pem"
+ private_key2="/etc/cer/user.prv"
+ private_key2_passwd="password"
+}
+
+
+
+Certificates
+------------
+
+Some EAP authentication methods require use of certificates. EAP-TLS
+uses both server side and client certificates whereas EAP-PEAP and
+EAP-TTLS only require the server side certificate. When client
+certificate is used, a matching private key file has to also be
+included in configuration. If the private key uses a passphrase, this
+has to be configured in wpa_supplicant.conf ("private_key_passwd").
+
+wpa_supplicant supports X.509 certificates in PEM and DER
+formats. User certificate and private key can be included in the same
+file.
+
+If the user certificate and private key is received in PKCS#12/PFX
+format, they need to be converted to suitable PEM/DER format for
+wpa_supplicant. This can be done, e.g., with following commands:
+
+# convert client certificate and private key to PEM format
+openssl pkcs12 -in example.pfx -out user.pem -clcerts
+# convert CA certificate (if included in PFX file) to PEM format
+openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
+
+
+
+wpa_cli
+-------
+
+wpa_cli is a text-based frontend program for interacting with
+wpa_supplicant. It is used to query current status, change
+configuration, trigger events, and request interactive user input.
+
+wpa_cli can show the current authentication status, selected security
+mode, dot11 and dot1x MIBs, etc. In addition, it can configuring some
+variables like EAPOL state machine parameters and trigger events like
+reassociation and IEEE 802.1X logoff/logon. wpa_cli provides a user
+interface to request authentication information, like username and
+password, if these are not included in the configuration. This can be
+used to implement, e.g., one-time-passwords or generic token card
+authentication where the authentication is based on a
+challenge-response that uses an external device for generating the
+response.
+
+The control interface of wpa_supplicant can be configured to allow
+non-root user access (ctrl_interface_group in the configuration
+file). This makes it possible to run wpa_cli with a normal user
+account.
+
+wpa_cli supports two modes: interactive and command line. Both modes
+share the same command set and the main difference is in interactive
+mode providing access to unsolicited messages (event messages,
+username/password requests).
+
+Interactive mode is started when wpa_cli is executed without including
+the command as a command line parameter. Commands are then entered on
+the wpa_cli prompt. In command line mode, the same commands are
+entered as command line arguments for wpa_cli.
+
+
+Interactive authentication parameters request
+
+When wpa_supplicant need authentication parameters, like username and
+password, which are not present in the configuration file, it sends a
+request message to all attached frontend programs, e.g., wpa_cli in
+interactive mode. wpa_cli shows these requests with
+"CTRL-REQ-<type>-<id>:<text>" prefix. <type> is IDENTITY, PASSWORD, or
+OTP (one-time-password). <id> is a unique identifier for the current
+network. <text> is description of the request. In case of OTP request,
+it includes the challenge from the authentication server.
+
+The reply to these requests can be given with 'identity', 'password',
+and 'otp' commands. <id> needs to be copied from the the matching
+request. 'password' and 'otp' commands can be used regardless of
+whether the request was for PASSWORD or OTP. The main difference
+between these two commands is that values given with 'password' are
+remembered as long as wpa_supplicant is running whereas values given
+with 'otp' are used only once and then forgotten, i.e., wpa_supplicant
+will ask frontend for a new value for every use. This can be used to
+implement one-time-password lists and generic token card -based
+authentication.
+
+Example request for password and a matching reply:
+
+CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
+> password 1 mysecretpassword
+
+Example request for generic token card challenge-response:
+
+CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
+> otp 2 9876
+
+
+wpa_cli commands
+
+ status = get current WPA/EAPOL/EAP status
+ mib = get MIB variables (dot1x, dot11)
+ help = show this usage help
+ interface [ifname] = show interfaces/select interface
+ level <debug level> = change debug level
+ license = show full wpa_cli license
+ logoff = IEEE 802.1X EAPOL state machine logoff
+ logon = IEEE 802.1X EAPOL state machine logon
+ set = set variables (shows list of variables when run without arguments)
+ pmksa = show PMKSA cache
+ reassociate = force reassociation
+ reconfigure = force wpa_supplicant to re-read its configuration file
+ preauthenticate <BSSID> = force preauthentication
+ identity <network id> <identity> = configure identity for an SSID
+ password <network id> <password> = configure password for an SSID
+ otp <network id> <password> = configure one-time-password for an SSID
+ quit = exit wpa_cli
+
+
+
+Integrating with pcmcia-cs/cardmgr scripts
+------------------------------------------
+
+wpa_supplicant needs to be running when using a wireless network with
+WPA. It can be started either from system startup scripts or from
+pcmcia-cs/cardmgr scripts (when using PC Cards). WPA handshake must be
+completed before data frames can be exchanged, so wpa_supplicant
+should be started before DHCP client.
+
+Command line option '-w' can be used if wpa_supplicant is started
+before the wireless LAN interface is present (e.g., before inserting
+the PC Card) or is not yet up.
+
+For example, following small changes to pcmcia-cs scripts can be used
+to enable WPA support:
+
+Add MODE="Managed" and WPA="y" to the network scheme in
+/etc/pcmcia/wireless.opts.
+
+Add the following block to the end of 'start' action handler in
+/etc/pcmcia/wireless:
+
+ if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+ /usr/local/bin/wpa_supplicant -Bw -c/etc/wpa_supplicant.conf \
+ -i$DEVICE
+ fi
+
+Add the following block to the end of 'stop' action handler (may need
+to be separated from other actions) in /etc/pcmcia/wireless:
+
+ if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+ killall wpa_supplicant
+ fi
+
+This will make cardmgr start wpa_supplicant when the card is plugged
+in. wpa_supplicant will wait until the interface is set up--either
+when a static IP address is configured or when DHCP client is
+started--and will then negotiate keys with the AP.
+
+
+
+Optional integration with Xsupplicant
+-------------------------------------
+
+wpa_supplicant has an integrated IEEE 802.1X Supplicant that supports
+most commonly used EAP methods. In addition, wpa_supplicant has an
+experimental interface for integrating it with Xsupplicant
+(http://www.open1x.org/) for the WPA with EAP authentication.
+
+Xsupplicant needs to be modified to send master session key to
+wpa_supplicant after successful EAP authentication. The included patch
+(xsupplicant.patch) shows the changes needed. This was merged into
+xsupplicant CVS on February 6, 2004, so any snapshot after that should
+have the needed functionality already included.
+
+When using WPA-EAP, both wpa_supplicant and Xsupplicant must be
+configured with the network security policy. See Xsupplicant documents
+for information about its configuration. Please also note, that a new
+command line option -W (enable WPA; added by xsupplicant.patch) must
+be used when starting xsupplicant.
+
+Example configuration for xsupplicant:
+
+network_list = all
+default_netname = jkm
+
+jkm
+{
+ type = wireless
+ allow_types = eap_peap
+ identity = <BEGIN_ID>jkm<END_ID>
+ eap-peap {
+ random_file = /dev/urandom
+ root_cert = /home/jkm/CA.pem
+ chunk_size = 1398
+ allow_types = eap_mschapv2
+ eap-mschapv2 {
+ username = <BEGIN_UNAME>jkm<END_UNAME>
+ password = <BEGIN_PASS>jkm<END_PASS>
+ }
+ }
+}
+
+
+Example configuration for wpa_supplicant:
+
+network={
+ ssid="jkm"
+ key_mgmt=WPA-EAP
+}
+
+
+Both wpa_supplicant and xsupplicant need to be started. Please remember
+to add '-W' option for xsupplicant in order to provide keying material
+for wpa_supplicant and '-e' option for wpa_supplicant to disable internal
+IEEE 802.1X implementation.
+
+wpa_supplicant -iwlan0 -cwpa_supplicant.conf -e
+xsupplicant -iwlan0 -cxsupplicant.conf -W
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h
new file mode 100644
index 0000000000..dc13c719ce
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+#ifndef __DRIVER_H
+#define __DRIVER_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libdlwlan.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg;
+typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP,
+ CIPHER_WEP104 } wpa_cipher;
+typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE } wpa_key_mgmt;
+
+struct wpa_driver_ops {
+ int (*get_bssid)(const char *, char *);
+ int (*get_ssid)(const char *ifname, char *);
+ int (*set_wpa)(const char *, boolean_t);
+ int (*set_key)(const char *, wpa_alg, uint8_t *,
+ int, boolean_t, uint8_t *, uint32_t, uint8_t *, uint32_t);
+ int (*scan)(const char *);
+ int (*get_scan_results)(const char *, dladm_wlan_ess_t *, uint32_t);
+ int (*disassociate)(const char *, int);
+ int (*associate)(const char *, const char *, uint8_t *, uint32_t);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRIVER_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c
new file mode 100644
index 0000000000..4ab82a2669
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <string.h>
+#include <stddef.h>
+
+#include "wpa_impl.h"
+#include "driver.h"
+
+#define WPA_STATUS(status) (status == DLADM_STATUS_OK? 0 : -1)
+
+/*
+ * get_bssid - get the current BSSID
+ * @ifname: interface name, e.g., wlan0
+ * @bssid: buffer for BSSID (IEEE80211_ADDR_LEN = 6 bytes)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Query kernel driver for the current BSSID and copy it to @bssid.
+ * Setting @bssid to 00:00:00:00:00:00 is recommended if the STA is not
+ * associated.
+ */
+int
+wpa_driver_wifi_get_bssid(const char *ifname, char *bssid)
+{
+ int ret;
+ dladm_wlan_linkattr_t attr;
+ dladm_wlan_attr_t *wl_attrp;
+
+ ret = dladm_wlan_get_linkattr(ifname, &attr);
+ if (ret != DLADM_STATUS_OK)
+ return (-1);
+
+ wl_attrp = &attr.la_wlan_attr;
+ if ((attr.la_valid & DLADM_WLAN_LINKATTR_WLAN) == 0 ||
+ (wl_attrp->wa_valid & DLADM_WLAN_ATTR_BSSID) == 0)
+ return (-1);
+
+ (void) memcpy(bssid, wl_attrp->wa_bssid.wb_bytes, DLADM_WLAN_BSSID_LEN);
+
+ wpa_printf(MSG_DEBUG, "wpa_driver_wifi_get_bssid: " MACSTR,
+ MAC2STR((unsigned char *)bssid));
+
+ return (WPA_STATUS(ret));
+}
+
+/*
+ * get_ssid - get the current SSID
+ * @ifname: interface name, e.g., wlan0
+ * @ssid: buffer for SSID (at least 32 bytes)
+ *
+ * Returns: length of the SSID on success, -1 on failure
+ *
+ * Query kernel driver for the current SSID and copy it to @ssid.
+ * Returning zero is recommended if the STA is not associated.
+ */
+int
+wpa_driver_wifi_get_ssid(const char *ifname, char *ssid)
+{
+ int ret;
+ dladm_wlan_linkattr_t attr;
+ dladm_wlan_attr_t *wl_attrp;
+
+ ret = dladm_wlan_get_linkattr(ifname, &attr);
+ if (ret != DLADM_STATUS_OK)
+ return (-1);
+
+ wl_attrp = &attr.la_wlan_attr;
+ if ((attr.la_valid & DLADM_WLAN_LINKATTR_WLAN) == 0 ||
+ (wl_attrp->wa_valid & DLADM_WLAN_ATTR_ESSID) == 0)
+ return (-1);
+
+ (void) memcpy(ssid, wl_attrp->wa_essid.we_bytes, MAX_ESSID_LENGTH);
+ ret = strlen(ssid);
+
+ wpa_printf(MSG_DEBUG, "wpa_driver_wifi_get_ssid: ssid=%s len=%d",
+ ssid, ret);
+
+ return (ret);
+}
+
+static int
+wpa_driver_wifi_set_wpa_ie(const char *ifname,
+ uint8_t *wpa_ie, uint32_t wpa_ie_len)
+{
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s", "wpa_driver_wifi_set_wpa_ie");
+ ret = dladm_wlan_wpa_set_ie(ifname, wpa_ie, wpa_ie_len);
+
+ return (WPA_STATUS(ret));
+}
+
+/*
+ * set_wpa - enable/disable WPA support
+ * @ifname: interface name, e.g., wlan0
+ * @enabled: 1 = enable, 0 = disable
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure the kernel driver to enable/disable WPA support. This may
+ * be empty function, if WPA support is always enabled. Common
+ * configuration items are WPA IE (clearing it when WPA support is
+ * disabled), Privacy flag for capability field, roaming mode (need to
+ * allow wpa_supplicant to control roaming).
+ */
+static int
+wpa_driver_wifi_set_wpa(const char *ifname, boolean_t enabled)
+{
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "wpa_driver_wifi_set_wpa: enable=%d", enabled);
+
+ if (!enabled && wpa_driver_wifi_set_wpa_ie(ifname, NULL, 0) < 0)
+ return (-1);
+
+ ret = dladm_wlan_wpa_set_wpa(ifname, enabled);
+
+ return (WPA_STATUS(ret));
+}
+
+static int
+wpa_driver_wifi_del_key(const char *ifname, int key_idx, unsigned char *addr)
+{
+ int ret;
+ dladm_wlan_bssid_t bss;
+
+ wpa_printf(MSG_DEBUG, "%s: id=%d", "wpa_driver_wifi_del_key",
+ key_idx);
+
+ (void) memcpy(bss.wb_bytes, addr, DLADM_WLAN_BSSID_LEN);
+ ret = dladm_wlan_wpa_del_key(ifname, key_idx, &bss);
+
+ return (WPA_STATUS(ret));
+}
+
+/*
+ * set_key - configure encryption key
+ * @ifname: interface name, e.g., wlan0
+ * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
+ * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key.
+ * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for
+ * broadcast/default keys
+ * @key_idx: key index (0..3), always 0 for unicast keys
+ * @set_tx: configure this key as the default Tx key (only used when
+ * driver does not support separate unicast/individual key
+ * @seq: sequence number/packet number, @seq_len octets, the next
+ * packet number to be used for in replay protection; configured
+ * for Rx keys (in most cases, this is only used with broadcast
+ * keys and set to zero for unicast keys)
+ * @seq_len: length of the @seq, depends on the algorithm:
+ * TKIP: 6 octets, CCMP: 6 octets
+ * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
+ * 8-byte Rx Mic Key
+ * @key_len: length of the key buffer in octets (WEP: 5 or 13,
+ * TKIP: 32, CCMP: 16)
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Configure the given key for the kernel driver. If the driver
+ * supports separate individual keys (4 default keys + 1 individual),
+ * @addr can be used to determine whether the key is default or
+ * individual. If only 4 keys are supported, the default key with key
+ * index 0 is used as the individual key. STA must be configured to use
+ * it as the default Tx key (@set_tx is set) and accept Rx for all the
+ * key indexes. In most cases, WPA uses only key indexes 1 and 2 for
+ * broadcast keys, so key index 0 is available for this kind of
+ * configuration.
+ */
+static int
+wpa_driver_wifi_set_key(const char *ifname, wpa_alg alg,
+ unsigned char *addr, int key_idx,
+ boolean_t set_tx, uint8_t *seq, uint32_t seq_len,
+ uint8_t *key, uint32_t key_len)
+{
+ char *alg_name;
+ dladm_wlan_cipher_t cipher;
+ dladm_wlan_bssid_t bss;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s", "wpa_driver_wifi_set_key");
+ if (alg == WPA_ALG_NONE)
+ return (wpa_driver_wifi_del_key(ifname, key_idx, addr));
+
+ switch (alg) {
+ case WPA_ALG_WEP:
+ alg_name = "WEP";
+ cipher = DLADM_WLAN_CIPHER_WEP;
+ break;
+ case WPA_ALG_TKIP:
+ alg_name = "TKIP";
+ cipher = DLADM_WLAN_CIPHER_TKIP;
+ break;
+ case WPA_ALG_CCMP:
+ alg_name = "CCMP";
+ cipher = DLADM_WLAN_CIPHER_AES_CCM;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "wpa_driver_wifi_set_key:"
+ " unknown/unsupported algorithm %d", alg);
+ return (-1);
+ }
+
+ wpa_printf(MSG_DEBUG, "wpa_driver_wifi_set_key: alg=%s key_idx=%d"
+ " set_tx=%d seq_len=%d seq=%d key_len=%d",
+ alg_name, key_idx, set_tx,
+ seq_len, *(uint64_t *)seq, key_len);
+
+ if (seq_len > sizeof (uint64_t)) {
+ wpa_printf(MSG_DEBUG, "wpa_driver_wifi_set_key:"
+ " seq_len %d too big", seq_len);
+ return (-1);
+ }
+ (void) memcpy(bss.wb_bytes, addr, DLADM_WLAN_BSSID_LEN);
+
+ ret = dladm_wlan_wpa_set_key(ifname, cipher, &bss, set_tx,
+ *(uint64_t *)seq, key_idx, key, key_len);
+
+ return (WPA_STATUS(ret));
+}
+
+/*
+ * disassociate - request driver to disassociate
+ * @ifname: interface name, e.g., wlan0
+ * @reason_code: 16-bit reason code to be sent in the disassociation
+ * frame
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int
+wpa_driver_wifi_disassociate(const char *ifname, int reason_code)
+{
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "wpa_driver_wifi_disassociate");
+
+ ret = dladm_wlan_wpa_set_mlme(ifname, DLADM_WLAN_MLME_DISASSOC,
+ reason_code, NULL);
+
+ return (WPA_STATUS(ret));
+}
+
+/*
+ * associate - request driver to associate
+ * @ifname: interface name, e.g., wlan0
+ * @bssid: BSSID of the selected AP
+ * @wpa_ie: WPA information element to be included in (Re)Association
+ * Request (including information element id and length). Use of
+ * this WPA IE is optional. If the driver generates the WPA IE, it
+ * can use @pairwise_suite, @group_suite, and @key_mgmt_suite
+ * to select proper algorithms. In this case, the driver has to
+ * notify wpa_supplicant about the used WPA IE by generating an
+ * event that the interface code will convert into EVENT_ASSOCINFO
+ * data (see wpa_supplicant.h). When using WPA2/IEEE 802.11i,
+ * @wpa_ie is used for RSN IE instead. The driver can determine
+ * which version is used by looking at the first byte of the IE
+ * (0xdd for WPA, 0x30 for WPA2/RSN).
+ * @wpa_ie_len: length of the @wpa_ie
+ *
+ * Return: 0 on success, -1 on failure
+ */
+static int
+wpa_driver_wifi_associate(const char *ifname, const char *bssid,
+ uint8_t *wpa_ie, uint32_t wpa_ie_len)
+{
+ int ret;
+ dladm_wlan_bssid_t bss;
+
+ wpa_printf(MSG_DEBUG, "wpa_driver_wifi_associate : "
+ MACSTR, MAC2STR(bssid));
+
+ /*
+ * NB: Don't need to set the freq or cipher-related state as
+ * this is implied by the bssid which is used to locate
+ * the scanned node state which holds it.
+ */
+ if (wpa_driver_wifi_set_wpa_ie(ifname, wpa_ie, wpa_ie_len) < 0)
+ return (-1);
+
+ (void) memcpy(bss.wb_bytes, bssid, DLADM_WLAN_BSSID_LEN);
+ ret = dladm_wlan_wpa_set_mlme(ifname, DLADM_WLAN_MLME_ASSOC,
+ 0, &bss);
+
+ return (WPA_STATUS(ret));
+}
+
+/*
+ * scan - request the driver to initiate scan
+ * @ifname: interface name, e.g., wlan0
+ *
+ * Return: 0 on success, -1 on failure
+ *
+ * Once the scan results are ready, the driver should report scan
+ * results event for wpa_supplicant which will eventually request the
+ * results with wpa_driver_get_scan_results().
+ */
+static int
+wpa_driver_wifi_scan(const char *ifname)
+{
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "%s", "wpa_driver_wifi_scan");
+ /*
+ * We force the state to INIT before calling ieee80211_new_state
+ * to get ieee80211_begin_scan called. We really want to scan w/o
+ * altering the current state but that's not possible right now.
+ */
+ (void) wpa_driver_wifi_disassociate(ifname,
+ DLADM_WLAN_REASON_DISASSOC_LEAVING);
+
+ ret = dladm_wlan_scan(ifname, NULL, NULL);
+
+ wpa_printf(MSG_DEBUG, "%s: return", "wpa_driver_wifi_scan");
+ return (WPA_STATUS(ret));
+}
+
+/*
+ * get_scan_results - fetch the latest scan results
+ * @ifname: interface name, e.g., wlan0
+ * @results: pointer to buffer for scan results
+ * @max_size: maximum number of entries (buffer size)
+ *
+ * Return: number of scan result entries used on success, -1 on failure
+ *
+ * If scan results include more than @max_size BSSes, @max_size will be
+ * returned and the remaining entries will not be included in the
+ * buffer.
+ */
+int
+wpa_driver_wifi_get_scan_results(const char *ifname,
+ dladm_wlan_ess_t *results, uint32_t max_size)
+{
+ uint_t ret;
+
+ wpa_printf(MSG_DEBUG, "%s: interface name =%s max size=%d\n",
+ "wpa_driver_wifi_get_scan_results", ifname, max_size);
+
+ if (dladm_wlan_wpa_get_sr(ifname, results, max_size, &ret)
+ != DLADM_STATUS_OK) {
+ return (-1);
+ }
+
+ return (ret);
+}
+
+struct wpa_driver_ops wpa_driver_wifi_ops = {
+ wpa_driver_wifi_get_bssid,
+ wpa_driver_wifi_get_ssid,
+ wpa_driver_wifi_set_wpa,
+ wpa_driver_wifi_set_key,
+ wpa_driver_wifi_scan,
+ wpa_driver_wifi_get_scan_results,
+ wpa_driver_wifi_disassociate,
+ wpa_driver_wifi_associate
+};
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.c b/usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.c
new file mode 100644
index 0000000000..22ef0dd75e
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <poll.h>
+
+#include "eloop.h"
+
+static struct eloop_data eloop;
+/*
+ * Initialize global event loop data - must be called before any other eloop_*
+ * function. user_data is a pointer to global data structure and will be passed
+ * as eloop_ctx to signal handlers.
+ */
+void
+eloop_init(void *user_data)
+{
+ (void) memset(&eloop, 0, sizeof (eloop));
+ eloop.user_data = user_data;
+}
+
+/*
+ * Register handler for read event
+ */
+int
+eloop_register_read_sock(int sock,
+ void (*handler)(int sock, void *eloop_ctx,
+ void *sock_ctx), void *eloop_data, void *user_data)
+{
+ struct eloop_sock *tmp;
+
+ tmp = (struct eloop_sock *)realloc(eloop.readers,
+ (eloop.reader_count + 1) * sizeof (struct eloop_sock));
+ if (tmp == NULL)
+ return (-1);
+
+ tmp[eloop.reader_count].sock = sock;
+ tmp[eloop.reader_count].eloop_data = eloop_data;
+ tmp[eloop.reader_count].user_data = user_data;
+ tmp[eloop.reader_count].handler = handler;
+ eloop.reader_count++;
+ eloop.readers = tmp;
+ if (sock > eloop.max_sock)
+ eloop.max_sock = sock;
+
+ return (0);
+}
+
+void
+eloop_unregister_read_sock(int sock)
+{
+ int i;
+
+ if (eloop.readers == NULL || eloop.reader_count == 0)
+ return;
+
+ for (i = 0; i < eloop.reader_count; i++) {
+ if (eloop.readers[i].sock == sock)
+ break;
+ }
+ if (i == eloop.reader_count)
+ return;
+ if (i != eloop.reader_count - 1) {
+ (void) memmove(&eloop.readers[i], &eloop.readers[i + 1],
+ (eloop.reader_count - i - 1) *
+ sizeof (struct eloop_sock));
+ }
+ eloop.reader_count--;
+}
+
+/*
+ * Register timeout routines
+ */
+int
+eloop_register_timeout(unsigned int secs, unsigned int usecs,
+ void (*handler)(void *eloop_ctx, void *timeout_ctx),
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *timeout, *tmp, *prev;
+
+ timeout = (struct eloop_timeout *)malloc(sizeof (*timeout));
+ if (timeout == NULL)
+ return (-1);
+ (void) gettimeofday(&timeout->time, NULL);
+ timeout->time.tv_sec += secs;
+ timeout->time.tv_usec += usecs;
+ while (timeout->time.tv_usec >= 1000000) {
+ timeout->time.tv_sec++;
+ timeout->time.tv_usec -= 1000000;
+ }
+ timeout->eloop_data = eloop_data;
+ timeout->user_data = user_data;
+ timeout->handler = handler;
+ timeout->next = NULL;
+
+ if (eloop.timeout == NULL) {
+ eloop.timeout = timeout;
+ return (0);
+ }
+
+ prev = NULL;
+ tmp = eloop.timeout;
+ while (tmp != NULL) {
+ if (timercmp(&timeout->time, &tmp->time, < /* */))
+ break;
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ if (prev == NULL) {
+ timeout->next = eloop.timeout;
+ eloop.timeout = timeout;
+ } else {
+ timeout->next = prev->next;
+ prev->next = timeout;
+ }
+
+ return (0);
+}
+
+/*
+ * Cancel timeouts matching <handler,eloop_data,user_data>.
+ * ELOOP_ALL_CTX can be used as a wildcard for cancelling all timeouts
+ * regardless of eloop_data/user_data.
+ */
+void
+eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx),
+ void *eloop_data, void *user_data)
+{
+ struct eloop_timeout *timeout, *prev, *next;
+
+ prev = NULL;
+ timeout = eloop.timeout;
+ while (timeout != NULL) {
+ next = timeout->next;
+
+ if (timeout->handler == handler &&
+ (timeout->eloop_data == eloop_data ||
+ eloop_data == ELOOP_ALL_CTX) &&
+ (timeout->user_data == user_data ||
+ user_data == ELOOP_ALL_CTX)) {
+ if (prev == NULL)
+ eloop.timeout = next;
+ else
+ prev->next = next;
+ free(timeout);
+ } else
+ prev = timeout;
+
+ timeout = next;
+ }
+}
+
+static void eloop_handle_signal(int sig)
+{
+ int i;
+
+ eloop.signaled++;
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].sig == sig) {
+ eloop.signals[i].signaled++;
+ break;
+ }
+ }
+}
+
+static void eloop_process_pending_signals(void)
+{
+ int i;
+
+ if (eloop.signaled == 0)
+ return;
+ eloop.signaled = 0;
+
+ for (i = 0; i < eloop.signal_count; i++) {
+ if (eloop.signals[i].signaled) {
+ eloop.signals[i].signaled = 0;
+ eloop.signals[i].handler(eloop.signals[i].sig,
+ eloop.user_data, eloop.signals[i].user_data);
+ }
+ }
+}
+
+/*
+ * Register handler for signal.
+ * Note: signals are 'global' events and there is no local eloop_data pointer
+ * like with other handlers. The (global) pointer given to eloop_init() will be
+ * used as eloop_ctx for signal handlers.
+ */
+int
+eloop_register_signal(int sig,
+ void (*handler)(int sig, void *eloop_ctx, void *signal_ctx),
+ void *user_data)
+{
+ struct eloop_signal *tmp;
+
+ tmp = (struct eloop_signal *)
+ realloc(eloop.signals,
+ (eloop.signal_count + 1) *
+ sizeof (struct eloop_signal));
+ if (tmp == NULL)
+ return (-1);
+
+ tmp[eloop.signal_count].sig = sig;
+ tmp[eloop.signal_count].user_data = user_data;
+ tmp[eloop.signal_count].handler = handler;
+ tmp[eloop.signal_count].signaled = 0;
+ eloop.signal_count++;
+ eloop.signals = tmp;
+ (void) signal(sig, eloop_handle_signal);
+
+ return (0);
+}
+
+/*
+ * Start event loop and continue running as long as there are any registered
+ * event handlers.
+ */
+void
+eloop_run(void)
+{
+ struct pollfd pfds[MAX_POLLFDS]; /* array of polled fd */
+ int i, res;
+ int default_t, t;
+ struct timeval tv, now;
+
+ default_t = 5 * 1000; /* 5 seconds */
+ while (!eloop.terminate &&
+ (eloop.timeout || eloop.reader_count > 0)) {
+ if (eloop.timeout) {
+ (void) gettimeofday(&now, NULL);
+ if (timercmp(&now, &eloop.timeout->time, < /* */))
+ /* LINTED E_CONSTANT_CONDITION */
+ timersub(&eloop.timeout->time, &now, &tv);
+ else
+ tv.tv_sec = tv.tv_usec = 0;
+ }
+
+ t = (eloop.timeout == NULL ?
+ default_t : (tv.tv_sec * 1000 + tv.tv_usec / 1000));
+ for (i = 0; i < eloop.reader_count; i++) {
+ pfds[i].fd = eloop.readers[i].sock;
+ pfds[i].events = POLLIN | POLLPRI;
+ }
+ res = poll(pfds, eloop.reader_count, t);
+ if (res < 0 && errno != EINTR)
+ return;
+
+ eloop_process_pending_signals();
+
+ /* check if some registered timeouts have occurred */
+ if (eloop.timeout) {
+ struct eloop_timeout *tmp;
+
+ (void) gettimeofday(&now, NULL);
+ if (!timercmp(&now, &eloop.timeout->time, < /* */)) {
+ tmp = eloop.timeout;
+ eloop.timeout = eloop.timeout->next;
+ tmp->handler(tmp->eloop_data, tmp->user_data);
+ free(tmp);
+ }
+
+ }
+
+ if (res <= 0)
+ continue;
+
+ for (i = 0; i < eloop.reader_count; i++) {
+ if (pfds[i].revents) {
+ eloop.readers[i].handler(
+ eloop.readers[i].sock,
+ eloop.readers[i].eloop_data,
+ eloop.readers[i].user_data);
+ }
+ }
+ }
+}
+
+/*
+ * Terminate event loop even if there are registered events.
+ */
+void
+eloop_terminate(void)
+{
+ eloop.terminate = 1;
+}
+
+
+/*
+ * Free any reserved resources. After calling eloop_destoy(), other eloop_*
+ * functions must not be called before re-running eloop_init().
+ */
+void
+eloop_destroy(void)
+{
+ struct eloop_timeout *timeout, *prev;
+
+ timeout = eloop.timeout;
+ while (timeout != NULL) {
+ prev = timeout;
+ timeout = timeout->next;
+ free(prev);
+ }
+ free(eloop.readers);
+ free(eloop.signals);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.h b/usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.h
new file mode 100644
index 0000000000..2079908bb4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+#ifndef __ELOOP_H
+#define __ELOOP_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef timersub
+#define timersub(tvp, uvp, vvp) \
+ do \
+ { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) \
+ { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif /* !timersub */
+
+#ifndef timeradd
+#define timeradd(tvp, uvp, vvp) \
+ do \
+ { \
+ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
+ if ((vvp)->tv_usec >= 1000000) \
+ { \
+ (vvp)->tv_sec++; \
+ (vvp)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+#endif /* !timeradd */
+
+/* Magic number for eloop_cancel_timeout() */
+#define ELOOP_ALL_CTX (void *) -1
+#define MAX_POLLFDS 32
+
+struct eloop_sock {
+ int sock;
+ void *eloop_data;
+ void *user_data;
+ void (*handler)(int, void *, void *);
+};
+
+struct eloop_timeout {
+ struct timeval time;
+ void *eloop_data;
+ void *user_data;
+ void (*handler)(void *, void *);
+ struct eloop_timeout *next;
+};
+
+struct eloop_signal {
+ int sig;
+ void *user_data;
+ void (*handler)(int, void *, void *);
+ int signaled;
+};
+
+struct eloop_data {
+ void *user_data;
+
+ int max_sock, reader_count;
+ struct eloop_sock *readers;
+
+ struct eloop_timeout *timeout;
+
+ int signal_count;
+ struct eloop_signal *signals;
+ int signaled;
+
+ int terminate;
+};
+
+void eloop_init(void *);
+
+int eloop_register_read_sock(int,
+ void (*handler)(int, void *, void *), void *, void *);
+
+void eloop_unregister_read_sock(int);
+
+int eloop_register_timeout(unsigned int, unsigned int,
+ void (*handler)(void *, void *), void *, void *);
+
+void eloop_cancel_timeout(void (*handler)(void *, void *), void *, void *);
+int eloop_register_signal(int, void (*handler)(int, void *, void *), void *);
+
+void eloop_run(void);
+void eloop_terminate(void);
+void eloop_destroy(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELOOP_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.c b/usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.c
new file mode 100644
index 0000000000..30a91c5220
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libdlpi.h>
+#include <sys/ethernet.h>
+#include <netinet/in.h>
+
+#include "wpa_impl.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+static int
+link_init(struct l2_packet_data *l2)
+{
+ int retval;
+ uint8_t paddr[DLPI_PHYSADDR_MAX];
+ size_t paddrlen = sizeof (paddr);
+
+ retval = dlpi_bind(l2->dh, DLPI_ANY_SAP, NULL);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "cannot bind on %s: %s",
+ l2->ifname, dlpi_strerror(retval));
+ return (-1);
+ }
+
+ retval = dlpi_promiscon(l2->dh, DL_PROMISC_SAP);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "cannot enable promiscous"
+ " mode (SAP) on %s: %s",
+ l2->ifname, dlpi_strerror(retval));
+ return (-1);
+ }
+
+ retval = dlpi_get_physaddr(l2->dh, DL_CURR_PHYS_ADDR, paddr, &paddrlen);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "cannot get physical address for %s: %s",
+ l2->ifname, dlpi_strerror(retval));
+ return (-1);
+ }
+ if (paddrlen != sizeof (l2->own_addr)) {
+ wpa_printf(MSG_ERROR, "physical address for %s is not %d bytes",
+ l2->ifname, sizeof (l2->own_addr));
+ return (-1);
+ }
+ (void) memcpy(l2->own_addr, paddr, sizeof (l2->own_addr));
+
+ return (0);
+}
+
+/*
+ * layer2 packet handling.
+ */
+int
+l2_packet_get_own_addr(struct l2_packet_data *l2, uint8_t *addr)
+{
+ (void) memcpy(addr, l2->own_addr, sizeof (l2->own_addr));
+ return (0);
+}
+
+int
+l2_packet_send(struct l2_packet_data *l2, uint8_t *buf, size_t buflen)
+{
+ int retval;
+
+ retval = dlpi_send(l2->dh, NULL, 0, buf, buflen, NULL);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "l2_packet_send: cannot send "
+ "message on %s: %s", l2->ifname, dlpi_strerror(retval));
+ return (-1);
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+l2_packet_receive(int fd, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ uint64_t buf[IEEE80211_MTU_MAX / sizeof (uint64_t)];
+ size_t buflen = sizeof (buf);
+ struct l2_ethhdr *ethhdr;
+ int retval;
+
+ retval = dlpi_recv(l2->dh, NULL, NULL, buf, &buflen, 0, NULL);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "l2_packet_receive: cannot receive "
+ "message on %s: %s", l2->ifname, dlpi_strerror(retval));
+ return;
+ }
+
+ ethhdr = (struct l2_ethhdr *)buf;
+ if (buflen < sizeof (*ethhdr) ||
+ (ntohs(ethhdr->h_proto) != ETHERTYPE_EAPOL &&
+ ntohs(ethhdr->h_proto) != ETHERTYPE_RSN_PREAUTH))
+ return;
+
+ l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source,
+ (unsigned char *)(ethhdr + 1), buflen - sizeof (*ethhdr));
+}
+
+/* ARGSUSED */
+struct l2_packet_data *
+l2_packet_init(const char *ifname, unsigned short protocol,
+ void (*rx_callback)(void *, unsigned char *, unsigned char *, size_t),
+ void *rx_callback_ctx)
+{
+ int retval;
+ struct l2_packet_data *l2;
+
+ l2 = calloc(1, sizeof (struct l2_packet_data));
+ if (l2 == NULL)
+ return (NULL);
+
+ (void) strlcpy(l2->ifname, ifname, sizeof (l2->ifname));
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+
+ retval = dlpi_open(l2->ifname, &l2->dh, DLPI_RAW);
+ if (retval != DLPI_SUCCESS) {
+ wpa_printf(MSG_ERROR, "unable to open DLPI link %s: %s",
+ l2->ifname, dlpi_strerror(retval));
+ free(l2);
+ return (NULL);
+ }
+
+ /* NOTE: link_init() sets l2->own_addr */
+ if (link_init(l2) < 0) {
+ dlpi_close(l2->dh);
+ free(l2);
+ return (NULL);
+ }
+
+ (void) eloop_register_read_sock(dlpi_fd(l2->dh), l2_packet_receive, l2,
+ NULL);
+
+ return (l2);
+}
+
+void
+l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 == NULL)
+ return;
+
+ eloop_unregister_read_sock(dlpi_fd(l2->dh));
+ dlpi_close(l2->dh);
+ free(l2);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.h b/usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.h
new file mode 100644
index 0000000000..ec7ccdb20d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+#ifndef __L2_PACKET_H
+#define __L2_PACKET_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <net/if.h>
+#include <libdlpi.h>
+
+#define IEEE80211_MTU_MAX 2304
+
+struct l2_packet_data {
+ dlpi_handle_t dh; /* dlpi handle for EAPOL frames */
+ char ifname[DLPI_LINKNAME_MAX];
+ uint8_t own_addr[IEEE80211_ADDR_LEN];
+ void (*rx_callback)(void *, unsigned char *,
+ unsigned char *, size_t);
+ void *rx_callback_ctx;
+};
+
+#pragma pack(1)
+struct l2_ethhdr {
+ uint8_t h_dest[IEEE80211_ADDR_LEN];
+ uint8_t h_source[IEEE80211_ADDR_LEN];
+ uint16_t h_proto;
+};
+#pragma pack()
+
+struct l2_packet_data *l2_packet_init(
+ const char *, unsigned short,
+ void (*rx_callback)(void *, unsigned char *,
+ unsigned char *, size_t),
+ void *);
+void l2_packet_deinit(struct l2_packet_data *);
+
+int l2_packet_get_own_addr(struct l2_packet_data *, uint8_t *);
+int l2_packet_send(struct l2_packet_data *, uint8_t *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __L2_PACKET_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c
new file mode 100644
index 0000000000..7a7fbed6a8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c
@@ -0,0 +1,1796 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <sys/ethernet.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "wpa_impl.h"
+#include "wpa_enc.h"
+#include "driver.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+static void pmksa_cache_set_expiration(struct wpa_supplicant *);
+
+/*
+ * IEEE 802.11i/D3.0
+ */
+static const int WPA_SELECTOR_LEN = 4;
+static const uint8_t WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 };
+static const uint16_t WPA_VERSION = 1;
+static const uint8_t
+WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 };
+static const uint8_t
+WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 };
+static const uint8_t WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 };
+static const uint8_t WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 };
+static const uint8_t WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 };
+static const uint8_t WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 };
+static const uint8_t WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 };
+
+/*
+ * WPA IE version 1
+ * 00-50-f2:1 (OUI:OUI type)
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: TKIP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: TKIP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ * (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ * (default: unspec 802.1x)
+ * WPA Capabilities (2 octets, little endian) (default: 0)
+ */
+#pragma pack(1)
+struct wpa_ie_hdr {
+ uint8_t elem_id;
+ uint8_t len;
+ uint8_t oui[3];
+ uint8_t oui_type;
+ uint16_t version;
+};
+#pragma pack()
+
+/*
+ * IEEE 802.11i/D9.0
+ */
+static const int RSN_SELECTOR_LEN = 4;
+static const uint16_t RSN_VERSION = 1;
+static const uint8_t
+RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 };
+static const uint8_t
+RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 };
+static const uint8_t RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 };
+static const uint8_t RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 };
+static const uint8_t RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 };
+static const uint8_t RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 };
+static const uint8_t RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 };
+
+/*
+ * EAPOL-Key Key Data Encapsulation
+ * GroupKey and STAKey require encryption, otherwise, encryption is optional.
+ */
+static const uint8_t RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 };
+static const uint8_t RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 };
+
+/*
+ * 1/4: PMKID
+ * 2/4: RSN IE
+ * 3/4: one or two RSN IEs + GTK IE (encrypted)
+ * 4/4: empty
+ * 1/2: GTK IE (encrypted)
+ * 2/2: empty
+ */
+
+/*
+ * RSN IE version 1
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: CCMP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: CCMP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ * (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ * (default: unspec 802.1x)
+ * RSN Capabilities (2 octets, little endian) (default: 0)
+ * PMKID Count (2 octets) (default: 0)
+ * PMKID List (16 * n octets)
+ */
+#pragma pack(1)
+struct rsn_ie_hdr {
+ uint8_t elem_id; /* WLAN_EID_RSN */
+ uint8_t len;
+ uint16_t version;
+};
+#pragma pack()
+
+static int
+random_get_pseudo_bytes(uint8_t *ptr, size_t len)
+{
+ int fd;
+ size_t resid = len;
+ size_t bytes;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1) {
+ wpa_printf(MSG_ERROR, "Could not open /dev/urandom.\n");
+ return (-1);
+ }
+
+ while (resid != 0) {
+ bytes = read(fd, ptr, resid);
+ ptr += bytes;
+ resid -= bytes;
+ }
+
+ (void) close(fd);
+
+ return (0);
+}
+
+static void
+inc_byte_array(uint8_t *counter, size_t len)
+{
+ int pos = len - 1;
+ while (pos >= 0) {
+ counter[pos]++;
+ if (counter[pos] != 0)
+ break;
+ pos--;
+ }
+}
+
+static int
+wpa_selector_to_bitfield(uint8_t *s)
+{
+ if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0)
+ return (WPA_CIPHER_NONE);
+ if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0)
+ return (WPA_CIPHER_WEP40);
+ if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0)
+ return (WPA_CIPHER_TKIP);
+ if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0)
+ return (WPA_CIPHER_CCMP);
+ if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0)
+ return (WPA_CIPHER_WEP104);
+ return (0);
+}
+
+static int
+wpa_key_mgmt_to_bitfield(uint8_t *s)
+{
+ if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0)
+ return (WPA_KEY_MGMT_IEEE8021X);
+ if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) ==
+ 0)
+ return (WPA_KEY_MGMT_PSK);
+ return (0);
+}
+
+static int
+rsn_selector_to_bitfield(uint8_t *s)
+{
+ if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0)
+ return (WPA_CIPHER_NONE);
+ if (memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN) == 0)
+ return (WPA_CIPHER_WEP40);
+ if (memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN) == 0)
+ return (WPA_CIPHER_TKIP);
+ if (memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN) == 0)
+ return (WPA_CIPHER_CCMP);
+ if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0)
+ return (WPA_CIPHER_WEP104);
+ return (0);
+}
+
+static int
+rsn_key_mgmt_to_bitfield(uint8_t *s)
+{
+ if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0)
+ return (WPA_KEY_MGMT_IEEE8021X);
+ if (memcmp(s, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN) ==
+ 0)
+ return (WPA_KEY_MGMT_PSK);
+ return (0);
+}
+
+static void
+pmksa_cache_free_entry(struct wpa_supplicant *wpa_s,
+ struct rsn_pmksa_cache *entry)
+{
+ wpa_s->pmksa_count--;
+ if (wpa_s->cur_pmksa == entry) {
+ wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry");
+ wpa_s->cur_pmksa = NULL;
+ }
+ free(entry);
+}
+
+/* ARGSUSED */
+static void
+pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ time_t now;
+
+ (void) time(&now);
+ while (wpa_s->pmksa && wpa_s->pmksa->expiration <= now) {
+ struct rsn_pmksa_cache *entry = wpa_s->pmksa;
+ wpa_s->pmksa = entry->next;
+ wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+ MACSTR, MAC2STR(entry->aa));
+ pmksa_cache_free_entry(wpa_s, entry);
+ }
+
+ pmksa_cache_set_expiration(wpa_s);
+}
+
+static void
+pmksa_cache_set_expiration(struct wpa_supplicant *wpa_s)
+{
+ int sec;
+ eloop_cancel_timeout(pmksa_cache_expire, wpa_s, NULL);
+ if (wpa_s->pmksa == NULL)
+ return;
+ sec = wpa_s->pmksa->expiration - time(NULL);
+ if (sec < 0)
+ sec = 0;
+ (void) eloop_register_timeout(sec + 1, 0, pmksa_cache_expire,
+ wpa_s, NULL);
+}
+
+void
+pmksa_cache_free(struct wpa_supplicant *wpa_s)
+{
+ struct rsn_pmksa_cache *entry, *prev;
+
+ entry = wpa_s->pmksa;
+ wpa_s->pmksa = NULL;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ free(prev);
+ }
+ pmksa_cache_set_expiration(wpa_s);
+ wpa_s->cur_pmksa = NULL;
+}
+
+struct rsn_pmksa_cache *
+pmksa_cache_get(struct wpa_supplicant *wpa_s,
+ uint8_t *aa, uint8_t *pmkid)
+{
+ struct rsn_pmksa_cache *entry = wpa_s->pmksa;
+ while (entry) {
+ if ((aa == NULL ||
+ memcmp(entry->aa, aa, IEEE80211_ADDR_LEN) == 0) &&
+ (pmkid == NULL ||
+ memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
+ return (entry);
+ entry = entry->next;
+ }
+ return (NULL);
+}
+
+int
+pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len)
+{
+ int i, j;
+ char *pos = buf;
+ struct rsn_pmksa_cache *entry;
+ time_t now;
+
+ (void) time(&now);
+ pos += snprintf(pos, buf + len - pos,
+ "Index / AA / PMKID / expiration (in seconds)\n");
+ i = 0;
+ entry = wpa_s->pmksa;
+ while (entry) {
+ i++;
+ pos += snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+ i, MAC2STR(entry->aa));
+ for (j = 0; j < PMKID_LEN; j++)
+ pos += snprintf(pos, buf + len - pos, "%02x",
+ entry->pmkid[j]);
+ pos += snprintf(pos, buf + len - pos, " %d\n",
+ (int)(entry->expiration - now));
+ entry = entry->next;
+ }
+ return (pos - buf);
+}
+
+void
+pmksa_candidate_free(struct wpa_supplicant *wpa_s)
+{
+ struct rsn_pmksa_candidate *entry, *prev;
+
+ entry = wpa_s->pmksa_candidates;
+ wpa_s->pmksa_candidates = NULL;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ free(prev);
+ }
+}
+
+/* ARGSUSED */
+static int
+wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, uint8_t *wpa_ie,
+ size_t wpa_ie_len, struct wpa_ie_data *data)
+{
+ struct wpa_ie_hdr *hdr;
+ uint8_t *pos;
+ int left;
+ int i, count;
+
+ data->proto = WPA_PROTO_WPA;
+ data->pairwise_cipher = WPA_CIPHER_TKIP;
+ data->group_cipher = WPA_CIPHER_TKIP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ data->capabilities = 0;
+
+ if (wpa_ie_len == 0) {
+ /* No WPA IE - fail silently */
+ return (-1);
+ }
+
+ if (wpa_ie_len < sizeof (struct wpa_ie_hdr)) {
+ wpa_printf(MSG_DEBUG, "%s: ie len too short %u",
+ "wpa_parse_wpa_ie_wpa", wpa_ie_len);
+ return (-1);
+ }
+
+ hdr = (struct wpa_ie_hdr *)wpa_ie;
+
+ if (hdr->elem_id != GENERIC_INFO_ELEM ||
+ hdr->len != wpa_ie_len - 2 ||
+ memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 ||
+ LE_16(hdr->version) != WPA_VERSION) {
+ wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+ "wpa_parse_wpa_ie_wpa");
+ return (-1);
+ }
+
+ pos = (uint8_t *)(hdr + 1);
+ left = wpa_ie_len - sizeof (*hdr);
+
+ if (left >= WPA_SELECTOR_LEN) {
+ data->group_cipher = wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ } else if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+ "wpa_parse_wpa_ie_wpa", left);
+ return (-1);
+ }
+
+ if (left >= 2) {
+ data->pairwise_cipher = 0;
+ count = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+ "count %u left %u",
+ "wpa_parse_wpa_ie_wpa", count, left);
+ return (-1);
+ }
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+ "wpa_parse_wpa_ie_wpa");
+ return (-1);
+ }
+
+ if (left >= 2) {
+ data->key_mgmt = 0;
+ count = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+ "count %u left %u",
+ "wpa_parse_wpa_ie_wpa", count, left);
+ return (-1);
+ }
+ for (i = 0; i < count; i++) {
+ data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+ "wpa_parse_wpa_ie_wpa");
+ return (-1);
+ }
+
+ if (left >= 2) {
+ data->capabilities = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes",
+ "wpa_parse_wpa_ie_wpa", left);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, uint8_t *rsn_ie,
+ size_t rsn_ie_len, struct wpa_ie_data *data)
+{
+ struct rsn_ie_hdr *hdr;
+ uint8_t *pos;
+ int left;
+ int i, count;
+
+ data->proto = WPA_PROTO_RSN;
+ data->pairwise_cipher = WPA_CIPHER_CCMP;
+ data->group_cipher = WPA_CIPHER_CCMP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ data->capabilities = 0;
+
+ if (rsn_ie_len == 0) {
+ /* No RSN IE - fail silently */
+ return (-1);
+ }
+
+ if (rsn_ie_len < sizeof (struct rsn_ie_hdr)) {
+ wpa_printf(MSG_DEBUG, "%s: ie len too short %u",
+ "wpa_parse_wpa_ie_rsn", rsn_ie_len);
+ return (-1);
+ }
+
+ hdr = (struct rsn_ie_hdr *)rsn_ie;
+
+ if (hdr->elem_id != RSN_INFO_ELEM ||
+ hdr->len != rsn_ie_len - 2 ||
+ LE_16(hdr->version) != RSN_VERSION) {
+ wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+ "wpa_parse_wpa_ie_rsn");
+ return (-1);
+ }
+
+ pos = (uint8_t *)(hdr + 1);
+ left = rsn_ie_len - sizeof (*hdr);
+
+ if (left >= RSN_SELECTOR_LEN) {
+ data->group_cipher = rsn_selector_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ } else if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+ "wpa_parse_wpa_ie_rsn", left);
+ return (-1);
+ }
+
+ if (left >= 2) {
+ data->pairwise_cipher = 0;
+ count = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+ "count %u left %u",
+ "wpa_parse_wpa_ie_rsn", count, left);
+ return (-1);
+ }
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+ "wpa_parse_wpa_ie_rsn");
+ return (-1);
+ }
+
+ if (left >= 2) {
+ data->key_mgmt = 0;
+ count = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+ "count %u left %u",
+ "wpa_parse_wpa_ie_rsn", count, left);
+ return (-1);
+ }
+ for (i = 0; i < count; i++) {
+ data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
+ pos += RSN_SELECTOR_LEN;
+ left -= RSN_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+ "wpa_parse_wpa_ie_rsn");
+ return (-1);
+ }
+
+ if (left >= 2) {
+ data->capabilities = pos[0] | (pos[1] << 8);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left > 0) {
+ /*
+ * RSN IE could include PMKID data, but Authenticator should
+ * never include it, so no need to parse it in the Supplicant.
+ */
+ wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
+ "wpa_parse_wpa_ie_rsn", left);
+ }
+
+ return (0);
+}
+
+int
+wpa_parse_wpa_ie(struct wpa_supplicant *wpa_s, uint8_t *wpa_ie,
+ size_t wpa_ie_len, struct wpa_ie_data *data)
+{
+ if (wpa_ie_len >= 1 && wpa_ie[0] == RSN_INFO_ELEM)
+ return (wpa_parse_wpa_ie_rsn(wpa_s, wpa_ie, wpa_ie_len, data));
+ else
+ return (wpa_parse_wpa_ie_wpa(wpa_s, wpa_ie, wpa_ie_len, data));
+}
+
+static int
+wpa_gen_wpa_ie_wpa(struct wpa_supplicant *wpa_s, uint8_t *wpa_ie)
+{
+ uint8_t *pos;
+ struct wpa_ie_hdr *hdr;
+
+ hdr = (struct wpa_ie_hdr *)wpa_ie;
+ hdr->elem_id = GENERIC_INFO_ELEM;
+ (void) memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN);
+ hdr->version = LE_16(WPA_VERSION);
+ pos = (uint8_t *)(hdr + 1);
+
+ if (wpa_s->group_cipher == WPA_CIPHER_CCMP) {
+ (void) memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+ (void) memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) {
+ (void) memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) {
+ (void) memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ wpa_s->group_cipher);
+ return (-1);
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) {
+ (void) memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
+ } else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+ (void) memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
+ } else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+ (void) memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ wpa_s->pairwise_cipher);
+ return (-1);
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ (void) memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X,
+ WPA_SELECTOR_LEN);
+ } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
+ (void) memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X,
+ WPA_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+ wpa_s->key_mgmt);
+ return (-1);
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ /*
+ * WPA Capabilities; use defaults, so no need to include it
+ */
+ hdr->len = (pos - wpa_ie) - 2;
+
+ return (pos - wpa_ie);
+}
+
+static int
+wpa_gen_wpa_ie_rsn(struct wpa_supplicant *wpa_s, uint8_t *rsn_ie)
+{
+ uint8_t *pos;
+ struct rsn_ie_hdr *hdr;
+
+ hdr = (struct rsn_ie_hdr *)rsn_ie;
+ hdr->elem_id = RSN_INFO_ELEM;
+ hdr->version = LE_16(RSN_VERSION);
+ pos = (uint8_t *)(hdr + 1);
+
+ if (wpa_s->group_cipher == WPA_CIPHER_CCMP) {
+ (void) memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+ (void) memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) {
+ (void) memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN);
+ } else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) {
+ (void) memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ wpa_s->group_cipher);
+ return (-1);
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) {
+ (void) memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN);
+ } else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+ (void) memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN);
+ } else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+ (void) memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ wpa_s->pairwise_cipher);
+ return (-1);
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ (void) memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X,
+ RSN_SELECTOR_LEN);
+ } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
+ (void) memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X,
+ RSN_SELECTOR_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+ wpa_s->key_mgmt);
+ return (-1);
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ *pos++ = 0;
+ *pos++ = 0;
+
+ if (wpa_s->cur_pmksa) {
+ /* PMKID Count (2 octets, little endian) */
+ *pos++ = 1;
+ *pos++ = 0;
+ /* PMKID */
+ (void) memcpy(pos, wpa_s->cur_pmksa->pmkid, PMKID_LEN);
+ pos += PMKID_LEN;
+ }
+
+ hdr->len = (pos - rsn_ie) - 2;
+
+ return (pos - rsn_ie);
+}
+
+int
+wpa_gen_wpa_ie(struct wpa_supplicant *wpa_s, uint8_t *wpa_ie)
+{
+ if (wpa_s->proto == WPA_PROTO_RSN)
+ return (wpa_gen_wpa_ie_rsn(wpa_s, wpa_ie));
+ else
+ return (wpa_gen_wpa_ie_wpa(wpa_s, wpa_ie));
+}
+
+static void
+wpa_pmk_to_ptk(uint8_t *pmk, uint8_t *addr1, uint8_t *addr2,
+ uint8_t *nonce1, uint8_t *nonce2, uint8_t *ptk, size_t ptk_len)
+{
+ uint8_t data[2 * IEEE80211_ADDR_LEN + 2 * WPA_PMK_LEN];
+
+ /*
+ * PTK = PRF-X(PMK, "Pairwise key expansion",
+ * Min(AA, SA) || Max(AA, SA) ||
+ * Min(ANonce, SNonce) || Max(ANonce, SNonce))
+ */
+
+ if (memcmp(addr1, addr2, IEEE80211_ADDR_LEN) < 0) {
+ (void) memcpy(data, addr1, IEEE80211_ADDR_LEN);
+ (void) memcpy(data + IEEE80211_ADDR_LEN, addr2,
+ IEEE80211_ADDR_LEN);
+ } else {
+ (void) memcpy(data, addr2, IEEE80211_ADDR_LEN);
+ (void) memcpy(data + IEEE80211_ADDR_LEN, addr1,
+ IEEE80211_ADDR_LEN);
+ }
+
+ if (memcmp(nonce1, nonce2, WPA_PMK_LEN) < 0) {
+ (void) memcpy(data + 2 * IEEE80211_ADDR_LEN, nonce1,
+ WPA_PMK_LEN);
+ (void) memcpy(data + 2 * IEEE80211_ADDR_LEN + WPA_PMK_LEN,
+ nonce2, WPA_PMK_LEN);
+ } else {
+ (void) memcpy(data + 2 * IEEE80211_ADDR_LEN, nonce2,
+ WPA_PMK_LEN);
+ (void) memcpy(data + 2 * IEEE80211_ADDR_LEN + WPA_PMK_LEN,
+ nonce1, WPA_PMK_LEN);
+ }
+
+ sha1_prf(pmk, WPA_PMK_LEN, "Pairwise key expansion", data,
+ sizeof (data), ptk, ptk_len);
+
+ wpa_hexdump(MSG_DEBUG, "WPA: PMK", pmk, WPA_PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: PTK", ptk, ptk_len);
+}
+
+struct wpa_ssid *
+wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *entry;
+ uint8_t ssid[MAX_ESSID_LENGTH];
+ int ssid_len;
+ uint8_t bssid[IEEE80211_ADDR_LEN];
+
+ (void) memset(ssid, 0, MAX_ESSID_LENGTH);
+ ssid_len = wpa_s->driver->get_ssid(wpa_s->ifname, (char *)ssid);
+ if (ssid_len < 0) {
+ wpa_printf(MSG_WARNING, "Could not read SSID from driver.");
+ return (NULL);
+ }
+
+ if (wpa_s->driver->get_bssid(wpa_s->ifname, (char *)bssid) < 0) {
+ wpa_printf(MSG_WARNING, "Could not read BSSID from driver.");
+ return (NULL);
+ }
+
+ entry = wpa_s->conf->ssid;
+ wpa_printf(MSG_DEBUG, "entry len=%d ssid=%s,"
+ " driver len=%d ssid=%s",
+ entry->ssid_len, entry->ssid, ssid_len, ssid);
+
+ if (ssid_len == entry->ssid_len &&
+ memcmp(ssid, entry->ssid, ssid_len) == 0 &&
+ (!entry->bssid_set ||
+ memcmp(bssid, entry->bssid, IEEE80211_ADDR_LEN) == 0))
+ return (entry);
+
+ return (NULL);
+}
+
+static void
+wpa_eapol_key_mic(uint8_t *key, int ver, uint8_t *buf, size_t len, uint8_t *mic)
+{
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+ hmac_md5(key, 16, buf, len, mic);
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ uint8_t hash[SHA1_MAC_LEN];
+ hmac_sha1(key, 16, buf, len, hash);
+ (void) memcpy(mic, hash, MD5_MAC_LEN);
+ }
+}
+
+void
+wpa_supplicant_key_request(struct wpa_supplicant *wpa_s,
+ int error, int pairwise)
+{
+ int rlen;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *reply;
+ unsigned char *rbuf;
+ struct l2_ethhdr *ethhdr;
+ int key_info, ver;
+ uint8_t bssid[IEEE80211_ADDR_LEN];
+
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ if (wpa_s->driver->get_bssid(wpa_s->ifname, (char *)bssid) < 0) {
+ wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
+ "request");
+ return;
+ }
+
+ rlen = sizeof (*ethhdr) + sizeof (*hdr) + sizeof (*reply);
+ rbuf = malloc(rlen);
+ if (rbuf == NULL)
+ return;
+
+ (void) memset(rbuf, 0, rlen);
+ ethhdr = (struct l2_ethhdr *)rbuf;
+ (void) memcpy(ethhdr->h_dest, bssid, IEEE80211_ADDR_LEN);
+ (void) memcpy(ethhdr->h_source, wpa_s->own_addr, IEEE80211_ADDR_LEN);
+ ethhdr->h_proto = htons(ETHERTYPE_EAPOL);
+
+ hdr = (struct ieee802_1x_hdr *)(ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = htons(sizeof (*reply));
+
+ reply = (struct wpa_eapol_key *)(hdr + 1);
+ reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ key_info = WPA_KEY_INFO_REQUEST | ver;
+ if (wpa_s->ptk_set)
+ key_info |= WPA_KEY_INFO_MIC;
+ if (error)
+ key_info |= WPA_KEY_INFO_ERROR;
+ if (pairwise)
+ key_info |= WPA_KEY_INFO_KEY_TYPE;
+ reply->key_info = BE_16(key_info);
+ reply->key_length = 0;
+ (void) memcpy(reply->replay_counter, wpa_s->request_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(wpa_s->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+ reply->key_data_length = BE_16(0);
+
+ if (key_info & WPA_KEY_INFO_MIC) {
+ wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (uint8_t *)hdr,
+ rlen - sizeof (*ethhdr), reply->key_mic);
+ }
+
+ wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d "
+ "pairwise=%d ptk_set=%d len=%d)",
+ error, pairwise, wpa_s->ptk_set, rlen);
+ wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key Request", rbuf, rlen);
+ (void) l2_packet_send(wpa_s->l2, rbuf, rlen);
+ free(rbuf);
+}
+
+static void
+wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr, struct wpa_eapol_key *key, int ver)
+{
+ int rlen;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *reply;
+ unsigned char *rbuf;
+ struct l2_ethhdr *ethhdr;
+ struct wpa_ssid *ssid;
+ struct wpa_ptk *ptk;
+ uint8_t buf[8], wpa_ie_buf[80], *wpa_ie, *pmkid = NULL;
+ int wpa_ie_len;
+
+ wpa_s->wpa_state = WPA_4WAY_HANDSHAKE;
+ wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ ssid = wpa_supplicant_get_ssid(wpa_s);
+ if (ssid == NULL) {
+ wpa_printf(MSG_WARNING,
+ "WPA: No SSID info found (msg 1 of 4).");
+ return;
+ }
+
+ if (wpa_s->proto == WPA_PROTO_RSN) {
+ /* RSN: msg 1/4 should contain PMKID for the selected PMK */
+ uint8_t *pos = (uint8_t *)(key + 1);
+ uint8_t *end = pos + BE_16(key->key_data_length);
+
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
+ pos, BE_16(key->key_data_length));
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "RSN: key data "
+ "underflow (ie=%d len=%d)",
+ pos[0], pos[1]);
+ break;
+ }
+ if (pos[0] == GENERIC_INFO_ELEM &&
+ pos + 1 + RSN_SELECTOR_LEN < end &&
+ pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+ memcmp(pos + 2, RSN_KEY_DATA_PMKID,
+ RSN_SELECTOR_LEN) == 0) {
+ pmkid = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
+ "Authenticator", pmkid, PMKID_LEN);
+ break;
+ } else if (pos[0] == GENERIC_INFO_ELEM && pos[1] == 0)
+ break;
+ pos += 2 + pos[1];
+ }
+ }
+
+ wpa_ie = wpa_ie_buf;
+ wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
+ if (wpa_ie_len < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to generate "
+ "WPA IE (for msg 2 of 4).");
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
+
+ rlen = sizeof (*ethhdr) + sizeof (*hdr) + sizeof (*reply) + wpa_ie_len;
+ rbuf = malloc(rlen);
+ if (rbuf == NULL)
+ return;
+
+ (void) memset(rbuf, 0, rlen);
+ ethhdr = (struct l2_ethhdr *)rbuf;
+ (void) memcpy(ethhdr->h_dest, src_addr, IEEE80211_ADDR_LEN);
+ (void) memcpy(ethhdr->h_source, wpa_s->own_addr, IEEE80211_ADDR_LEN);
+ ethhdr->h_proto = htons(ETHERTYPE_EAPOL);
+
+ hdr = (struct ieee802_1x_hdr *)(ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = htons(sizeof (*reply) + wpa_ie_len);
+
+ reply = (struct wpa_eapol_key *)(hdr + 1);
+ reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ reply->key_info = BE_16(ver | WPA_KEY_INFO_KEY_TYPE |
+ WPA_KEY_INFO_MIC);
+ reply->key_length = key->key_length;
+ (void) memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ reply->key_data_length = BE_16(wpa_ie_len);
+ (void) memcpy(reply + 1, wpa_ie, wpa_ie_len);
+
+ if (wpa_s->renew_snonce) {
+ if (random_get_pseudo_bytes(wpa_s->snonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to get "
+ "random data for SNonce");
+ free(rbuf);
+ return;
+ }
+
+ wpa_s->renew_snonce = 0;
+ wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+ wpa_s->snonce, WPA_NONCE_LEN);
+ }
+ (void) memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN);
+ ptk = &wpa_s->tptk;
+ (void) memcpy(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN);
+
+ wpa_pmk_to_ptk(wpa_s->pmk, wpa_s->own_addr, src_addr,
+ wpa_s->snonce, key->key_nonce, (uint8_t *)ptk, sizeof (*ptk));
+
+ /*
+ * Supplicant: swap tx/rx Mic keys
+ */
+ (void) memcpy(buf, ptk->u.auth.tx_mic_key, 8);
+ (void) memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
+ (void) memcpy(ptk->u.auth.rx_mic_key, buf, 8);
+ wpa_s->tptk_set = 1;
+ wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, (uint8_t *)hdr,
+ rlen - sizeof (*ethhdr), reply->key_mic);
+ wpa_hexdump(MSG_DEBUG, "WPA: EAPOL-Key MIC", reply->key_mic, 16);
+
+ wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
+ wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/4", rbuf, rlen);
+ (void) l2_packet_send(wpa_s->l2, rbuf, rlen);
+
+ free(rbuf);
+}
+
+static void
+wpa_supplicant_process_3_of_4_gtk(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr, struct wpa_eapol_key *key,
+ uint8_t *gtk, int gtk_len)
+{
+ int keyidx, tx, key_rsc_len = 0, alg;
+
+ wpa_hexdump(MSG_DEBUG,
+ "WPA: received GTK in pairwise handshake", gtk, gtk_len);
+
+ keyidx = gtk[0] & 0x3;
+ tx = !!(gtk[0] & BIT(2));
+ if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) {
+ /*
+ * Ignore Tx bit in GTK IE if a pairwise key is used.
+ * One AP seemed to set this bit (incorrectly, since Tx
+ * is only when doing Group Key only APs) and without
+ * this workaround, the data connection does not work
+ * because wpa_supplicant configured non-zero keyidx to
+ * be used for unicast.
+ */
+ wpa_printf(MSG_INFO, "RSN: Tx bit set for GTK IE, but "
+ "pairwise keys are used - ignore Tx bit");
+ tx = 0;
+ }
+
+ gtk += 2;
+ gtk_len -= 2;
+ wpa_hexdump(MSG_DEBUG, "WPA: Group Key", gtk, gtk_len);
+
+ switch (wpa_s->group_cipher) {
+ case WPA_CIPHER_CCMP:
+ if (gtk_len != 16) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported CCMP"
+ " Group Cipher key length %d.", gtk_len);
+ return;
+ }
+ key_rsc_len = 6;
+ alg = WPA_ALG_CCMP;
+ break;
+ case WPA_CIPHER_TKIP:
+ if (gtk_len != 32) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported TKIP"
+ " Group Cipher key length %d.", gtk_len);
+ return;
+ }
+ key_rsc_len = 6;
+ alg = WPA_ALG_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ if (gtk_len != 13) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported "
+ "WEP104 Group Cipher key length " "%d.", gtk_len);
+ return;
+ }
+ alg = WPA_ALG_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ if (gtk_len != 5) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported "
+ "WEP40 Group Cipher key length %d.", gtk_len);
+ return;
+ }
+ alg = WPA_ALG_WEP;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "WPA: Unsupport Group Cipher "
+ "%d", wpa_s->group_cipher);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver "
+ "(keyidx=%d tx=%d).", keyidx, tx);
+ wpa_hexdump(MSG_DEBUG, "WPA: RSC", key->key_rsc, key_rsc_len);
+ if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+ uint8_t tmpbuf[8];
+ /*
+ * Swap Tx/Rx keys for Michael MIC
+ */
+ (void) memcpy(tmpbuf, gtk + 16, 8);
+ (void) memcpy(gtk + 16, gtk + 24, 8);
+ (void) memcpy(gtk + 24, tmpbuf, 8);
+ }
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+ if (wpa_s->driver->set_key(wpa_s->ifname, alg,
+ (uint8_t *)"\xff\xff\xff\xff\xff\xff",
+ keyidx, 1, key->key_rsc,
+ key_rsc_len, gtk, gtk_len) < 0)
+ wpa_printf(MSG_WARNING, "WPA: Failed to set "
+ "GTK to the driver (Group only).");
+ } else if (wpa_s->driver->set_key(wpa_s->ifname, alg,
+ (uint8_t *)"\xff\xff\xff\xff\xff\xff",
+ keyidx, tx,
+ key->key_rsc, key_rsc_len,
+ gtk, gtk_len) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to "
+ "the driver.");
+ }
+
+ wpa_printf(MSG_INFO, "WPA: Key negotiation completed with "
+ MACSTR, MAC2STR(src_addr));
+ eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_s->wpa_state = WPA_COMPLETED;
+}
+
+static void
+wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr, struct wpa_eapol_key *key,
+ int extra_len, int ver)
+{
+ int rlen;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *reply;
+ unsigned char *rbuf;
+ struct l2_ethhdr *ethhdr;
+ int key_info, ie_len = 0, keylen, gtk_len = 0;
+ uint8_t *ie = NULL, *gtk = NULL, *key_rsc;
+ uint8_t null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ wpa_s->wpa_state = WPA_4WAY_HANDSHAKE;
+ wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ key_info = BE_16(key->key_info);
+
+ if (wpa_s->proto == WPA_PROTO_RSN) {
+ uint8_t *pos = (uint8_t *)(key + 1);
+ uint8_t *end = pos + BE_16(key->key_data_length);
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "RSN: key data "
+ "underflow (ie=%d len=%d)",
+ pos[0], pos[1]);
+ break;
+ }
+ if (*pos == RSN_INFO_ELEM) {
+ ie = pos;
+ ie_len = pos[1] + 2;
+ } else if (pos[0] == GENERIC_INFO_ELEM &&
+ pos + 1 + RSN_SELECTOR_LEN < end &&
+ pos[1] > RSN_SELECTOR_LEN + 2 &&
+ memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY,
+ RSN_SELECTOR_LEN) == 0) {
+ if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_printf(MSG_WARNING, "WPA: GTK IE "
+ "in unencrypted key data");
+ return;
+ }
+ gtk = pos + 2 + RSN_SELECTOR_LEN;
+ gtk_len = pos[1] - RSN_SELECTOR_LEN;
+ } else if (pos[0] == GENERIC_INFO_ELEM && pos[1] == 0)
+ break;
+
+ pos += 2 + pos[1];
+ }
+ } else {
+ ie = (uint8_t *)(key + 1);
+ ie_len = BE_16(key->key_data_length);
+ if (ie_len > extra_len) {
+ wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
+ " ie_len=%d > extra_len=%d",
+ ie_len, extra_len);
+ return;
+ }
+ }
+
+ if (wpa_s->ap_wpa_ie &&
+ (wpa_s->ap_wpa_ie_len != ie_len ||
+ memcmp(wpa_s->ap_wpa_ie, ie, ie_len) != 0)) {
+ wpa_printf(MSG_WARNING, "WPA: WPA IE in 3/4 msg does not match"
+ " with WPA IE in Beacon/ProbeResp (src=" MACSTR ")",
+ MAC2STR(src_addr));
+ wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp",
+ wpa_s->ap_wpa_ie, wpa_s->ap_wpa_ie_len);
+ wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg", ie, ie_len);
+ wpa_supplicant_disassociate(wpa_s, REASON_IE_IN_4WAY_DIFFERS);
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+ return;
+ }
+
+ if (memcmp(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way "
+ "Handshake differs from 3 of 4-Way Handshake - drop"
+ " packet (src=" MACSTR ")", MAC2STR(src_addr));
+ return;
+ }
+
+ keylen = BE_16(key->key_length);
+ switch (wpa_s->pairwise_cipher) {
+ case WPA_CIPHER_CCMP:
+ if (keylen != 16) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length "
+ "%d (src=" MACSTR ")",
+ keylen, MAC2STR(src_addr));
+ return;
+ }
+ break;
+ case WPA_CIPHER_TKIP:
+ if (keylen != 32) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length "
+ "%d (src=" MACSTR ")",
+ keylen, MAC2STR(src_addr));
+ return;
+ }
+ break;
+ }
+
+ rlen = sizeof (*ethhdr) + sizeof (*hdr) + sizeof (*reply);
+ rbuf = malloc(rlen);
+ if (rbuf == NULL)
+ return;
+
+ (void) memset(rbuf, 0, rlen);
+ ethhdr = (struct l2_ethhdr *)rbuf;
+ (void) memcpy(ethhdr->h_dest, src_addr, IEEE80211_ADDR_LEN);
+ (void) memcpy(ethhdr->h_source, wpa_s->own_addr, IEEE80211_ADDR_LEN);
+ ethhdr->h_proto = htons(ETHERTYPE_EAPOL);
+
+ hdr = (struct ieee802_1x_hdr *)(ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = htons(sizeof (*reply));
+
+ reply = (struct wpa_eapol_key *)(hdr + 1);
+ reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ reply->key_info = BE_16(ver | WPA_KEY_INFO_KEY_TYPE |
+ WPA_KEY_INFO_MIC |
+ (key_info & WPA_KEY_INFO_SECURE));
+ reply->key_length = key->key_length;
+ (void) memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ reply->key_data_length = BE_16(0);
+
+ (void) memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN);
+ wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (uint8_t *)hdr,
+ rlen - sizeof (*ethhdr), reply->key_mic);
+
+ wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
+ wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 4/4", rbuf, rlen);
+ (void) l2_packet_send(wpa_s->l2, rbuf, rlen);
+
+ free(rbuf);
+
+ /*
+ * SNonce was successfully used in msg 3/4, so mark it to be renewed
+ * for the next 4-Way Handshake. If msg 3 is received again, the old
+ * SNonce will still be used to avoid changing PTK.
+ */
+ wpa_s->renew_snonce = 1;
+
+ if (key_info & WPA_KEY_INFO_INSTALL) {
+ int alg, keylen, rsclen;
+ wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver.");
+ switch (wpa_s->pairwise_cipher) {
+ case WPA_CIPHER_CCMP:
+ alg = WPA_ALG_CCMP;
+ keylen = 16;
+ rsclen = 6;
+ break;
+ case WPA_CIPHER_TKIP:
+ alg = WPA_ALG_TKIP;
+ keylen = 32;
+ rsclen = 6;
+ break;
+ case WPA_CIPHER_NONE:
+ wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: "
+ "NONE - do not use pairwise keys");
+ return;
+ default:
+ wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise "
+ "cipher %d", wpa_s->pairwise_cipher);
+ return;
+ }
+ if (wpa_s->proto == WPA_PROTO_RSN) {
+ key_rsc = null_rsc;
+ } else {
+ key_rsc = key->key_rsc;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
+ }
+
+ if (wpa_s->driver->set_key(wpa_s->ifname, alg, src_addr,
+ 0, 1, key_rsc, rsclen,
+ (uint8_t *)&wpa_s->ptk.tk1, keylen) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the"
+ " driver.");
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "%s: key_info=%x gtk=%p\n",
+ "wpa_supplicant_process_3_of_4", key_info, gtk);
+ wpa_s->wpa_state = WPA_GROUP_HANDSHAKE;
+
+ if (gtk)
+ wpa_supplicant_process_3_of_4_gtk(wpa_s,
+ src_addr, key, gtk, gtk_len);
+}
+
+static void
+wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr, struct wpa_eapol_key *key,
+ int extra_len, int ver)
+{
+ int rlen;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *reply;
+ unsigned char *rbuf;
+ struct l2_ethhdr *ethhdr;
+ int key_info, keylen, keydatalen, maxkeylen, keyidx, key_rsc_len = 0;
+ int alg, tx;
+ uint8_t ek[32], tmpbuf[8], gtk[32];
+ uint8_t *gtk_ie = NULL;
+ size_t gtk_ie_len = 0;
+
+ wpa_s->wpa_state = WPA_GROUP_HANDSHAKE;
+ wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ key_info = BE_16(key->key_info);
+ keydatalen = BE_16(key->key_data_length);
+
+ if (wpa_s->proto == WPA_PROTO_RSN) {
+ uint8_t *pos = (uint8_t *)(key + 1);
+ uint8_t *end = pos + keydatalen;
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "RSN: key data "
+ "underflow (ie=%d len=%d)",
+ pos[0], pos[1]);
+ break;
+ }
+ if (pos[0] == GENERIC_INFO_ELEM &&
+ pos + 1 + RSN_SELECTOR_LEN < end &&
+ pos[1] > RSN_SELECTOR_LEN + 2 &&
+ memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY,
+ RSN_SELECTOR_LEN) == 0) {
+ if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_printf(MSG_WARNING, "WPA: GTK IE "
+ "in unencrypted key data");
+ return;
+ }
+ gtk_ie = pos + 2 + RSN_SELECTOR_LEN;
+ gtk_ie_len = pos[1] - RSN_SELECTOR_LEN;
+ break;
+ } else if (pos[0] == GENERIC_INFO_ELEM &&
+ pos[1] == 0)
+ break;
+
+ pos += 2 + pos[1];
+ }
+
+ if (gtk_ie == NULL) {
+ wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key "
+ "message 1/2");
+ return;
+ }
+ maxkeylen = keylen = gtk_ie_len - 2;
+ } else {
+ keylen = BE_16(key->key_length);
+ maxkeylen = keydatalen;
+ if (keydatalen > extra_len) {
+ wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
+ " key_data_length=%d > extra_len=%d",
+ keydatalen, extra_len);
+ return;
+ }
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES)
+ maxkeylen -= 8;
+ }
+
+ switch (wpa_s->group_cipher) {
+ case WPA_CIPHER_CCMP:
+ if (keylen != 16 || maxkeylen < 16) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported CCMP Group "
+ "Cipher key length %d (%d).", keylen, maxkeylen);
+ return;
+ }
+ key_rsc_len = 6;
+ alg = WPA_ALG_CCMP;
+ break;
+ case WPA_CIPHER_TKIP:
+ if (keylen != 32 || maxkeylen < 32) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported TKIP Group "
+ "Cipher key length %d (%d).", keylen, maxkeylen);
+ return;
+ }
+ key_rsc_len = 6; /* key->key_data; */
+ alg = WPA_ALG_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ if (keylen != 13 || maxkeylen < 13) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported WEP104 Group"
+ " Cipher key length %d (%d).", keylen, maxkeylen);
+ return;
+ }
+ alg = WPA_ALG_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ if (keylen != 5 || maxkeylen < 5) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported WEP40 Group "
+ "Cipher key length %d (%d).", keylen, maxkeylen);
+ return;
+ }
+ alg = WPA_ALG_WEP;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "WPA: Unsupport Group Cipher %d",
+ wpa_s->group_cipher);
+ return;
+ }
+
+ if (wpa_s->proto == WPA_PROTO_RSN) {
+ wpa_hexdump(MSG_DEBUG,
+ "WPA: received GTK in group key handshake",
+ gtk_ie, gtk_ie_len);
+ keyidx = gtk_ie[0] & 0x3;
+ tx = !!(gtk_ie[0] & BIT(2));
+ if (gtk_ie_len - 2 > sizeof (gtk)) {
+ wpa_printf(MSG_INFO, "WPA: Too long GTK in GTK IE "
+ "(len=%d)", gtk_ie_len - 2);
+ return;
+ }
+ (void) memcpy(gtk, gtk_ie + 2, gtk_ie_len - 2);
+ } else {
+ keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT;
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+ (void) memcpy(ek, key->key_iv, 16);
+ (void) memcpy(ek + 16, wpa_s->ptk.encr_key, 16);
+ rc4_skip(ek, 32, 256, (uint8_t *)(key + 1), keydatalen);
+ (void) memcpy(gtk, key + 1, keylen);
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ if (keydatalen % 8) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported "
+ "AES-WRAP len %d", keydatalen);
+ return;
+ }
+ if (aes_unwrap(wpa_s->ptk.encr_key, maxkeylen / 8,
+ (uint8_t *)(key + 1), gtk)) {
+ wpa_printf(MSG_WARNING, "WPA: AES unwrap "
+ "failed - could not decrypt GTK");
+ return;
+ }
+ }
+ tx = !!(key_info & WPA_KEY_INFO_TXRX);
+ if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) {
+ /*
+ * Ignore Tx bit in Group Key message if a pairwise key
+ * is used. Some APs seem to setting this bit
+ * (incorrectly, since Tx is only when doing Group Key
+ * only APs) and without this workaround, the data
+ * connection does not work because wpa_supplicant
+ * configured non-zero keyidx to be used for unicast.
+ */
+ wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but "
+ "pairwise keys are used - ignore Tx bit");
+ tx = 0;
+ }
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: Group Key", gtk, keylen);
+ wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver (keyidx=%d "
+ "tx=%d).", keyidx, tx);
+ wpa_hexdump(MSG_DEBUG, "WPA: RSC", key->key_rsc, key_rsc_len);
+ if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+ /*
+ * Swap Tx/Rx keys for Michael MIC
+ */
+ (void) memcpy(tmpbuf, gtk + 16, 8);
+ (void) memcpy(gtk + 16, gtk + 24, 8);
+ (void) memcpy(gtk + 24, tmpbuf, 8);
+ }
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+ if (wpa_s->driver->set_key(wpa_s->ifname, alg,
+ (uint8_t *)"\xff\xff\xff\xff\xff\xff",
+ keyidx, 1, key->key_rsc,
+ key_rsc_len, gtk, keylen) < 0)
+ wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the"
+ " driver (Group only).");
+ } else if (wpa_s->driver->set_key(wpa_s->ifname, alg,
+ (uint8_t *)"\xff\xff\xff\xff\xff\xff",
+ keyidx, tx,
+ key->key_rsc, key_rsc_len,
+ gtk, keylen) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
+ "driver.");
+ }
+
+ rlen = sizeof (*ethhdr) + sizeof (*hdr) + sizeof (*reply);
+ rbuf = malloc(rlen);
+ if (rbuf == NULL)
+ return;
+
+ (void) memset(rbuf, 0, rlen);
+ ethhdr = (struct l2_ethhdr *)rbuf;
+ (void) memcpy(ethhdr->h_dest, src_addr, IEEE80211_ADDR_LEN);
+ (void) memcpy(ethhdr->h_source, wpa_s->own_addr, IEEE80211_ADDR_LEN);
+ ethhdr->h_proto = htons(ETHERTYPE_EAPOL);
+
+ hdr = (struct ieee802_1x_hdr *)(ethhdr + 1);
+ hdr->version = wpa_s->conf->eapol_version;
+ hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
+ hdr->length = htons(sizeof (*reply));
+
+ reply = (struct wpa_eapol_key *)(hdr + 1);
+ reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ reply->key_info =
+ BE_16(ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE |
+ (key_info & WPA_KEY_INFO_KEY_INDEX_MASK));
+ reply->key_length = key->key_length;
+ (void) memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ reply->key_data_length = BE_16(0);
+
+ wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (uint8_t *)hdr,
+ rlen - sizeof (*ethhdr), reply->key_mic);
+
+ wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
+ wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/2", rbuf, rlen);
+ (void) l2_packet_send(wpa_s->l2, rbuf, rlen);
+ free(rbuf);
+
+ wpa_printf(MSG_INFO, "WPA: Key negotiation completed with " MACSTR,
+ MAC2STR(src_addr));
+ eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ wpa_s->wpa_state = WPA_COMPLETED;
+ wpa_printf(MSG_INFO, "-----------------------------------\n");
+}
+
+static int
+wpa_supplicant_verify_eapol_key_mic(struct wpa_supplicant *wpa_s,
+ struct wpa_eapol_key *key, int ver, uint8_t *buf, size_t len)
+{
+ uint8_t mic[16];
+ int ok = 0;
+
+ (void) memcpy(mic, key->key_mic, 16);
+ if (wpa_s->tptk_set) {
+ (void) memset(key->key_mic, 0, 16);
+ wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, buf, len,
+ key->key_mic);
+ if (memcmp(mic, key->key_mic, 16) != 0) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
+ "when using TPTK - ignoring TPTK");
+ } else {
+ ok = 1;
+ wpa_s->tptk_set = 0;
+ wpa_s->ptk_set = 1;
+ (void) memcpy(&wpa_s->ptk, &wpa_s->tptk,
+ sizeof (wpa_s->ptk));
+ }
+ }
+
+ if (!ok && wpa_s->ptk_set) {
+ (void) memset(key->key_mic, 0, 16);
+ wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, buf, len,
+ key->key_mic);
+ if (memcmp(mic, key->key_mic, 16) != 0) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
+ "- dropping packet");
+ return (-1);
+ }
+ ok = 1;
+ }
+
+ if (!ok) {
+ wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC "
+ "- dropping packet");
+ return (-1);
+ }
+
+ (void) memcpy(wpa_s->rx_replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ wpa_s->rx_replay_counter_set = 1;
+
+ return (0);
+}
+
+/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
+static int
+wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s,
+ struct wpa_eapol_key *key, int ver)
+{
+ int keydatalen = BE_16(key->key_data_length);
+
+ wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
+ (uint8_t *)(key + 1), keydatalen);
+ if (!wpa_s->ptk_set) {
+ wpa_printf(MSG_WARNING, "WPA: PTK not available, "
+ "cannot decrypt EAPOL-Key key data.");
+ return (-1);
+ }
+
+ /*
+ * Decrypt key data here so that this operation does not need
+ * to be implemented separately for each message type.
+ */
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+ uint8_t ek[32];
+ (void) memcpy(ek, key->key_iv, 16);
+ (void) memcpy(ek + 16, wpa_s->ptk.encr_key, 16);
+ rc4_skip(ek, 32, 256, (uint8_t *)(key + 1), keydatalen);
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ uint8_t *buf;
+ if (keydatalen % 8) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported "
+ "AES-WRAP len %d", keydatalen);
+ return (-1);
+ }
+ keydatalen -= 8; /* AES-WRAP adds 8 bytes */
+ buf = malloc(keydatalen);
+ if (buf == NULL) {
+ wpa_printf(MSG_WARNING, "WPA: No memory for "
+ "AES-UNWRAP buffer");
+ return (-1);
+ }
+ if (aes_unwrap(wpa_s->ptk.encr_key, keydatalen / 8,
+ (uint8_t *)(key + 1), buf)) {
+ free(buf);
+ wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - "
+ "could not decrypt EAPOL-Key key data");
+ return (-1);
+ }
+ (void) memcpy(key + 1, buf, keydatalen);
+ free(buf);
+ key->key_data_length = BE_16(keydatalen);
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
+ (uint8_t *)(key + 1), keydatalen);
+
+ return (0);
+}
+
+static void
+wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s,
+ unsigned char *src_addr, unsigned char *buf, size_t len)
+{
+ size_t plen, data_len, extra_len;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ int key_info, ver;
+
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL frame len %u\n ", len);
+
+ hdr = (struct ieee802_1x_hdr *)buf;
+ key = (struct wpa_eapol_key *)(hdr + 1);
+ wpa_printf(MSG_DEBUG, "hdr_len=%u, key_len=%u",
+ sizeof (*hdr), sizeof (*key));
+ if (len < sizeof (*hdr) + sizeof (*key)) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short, len %u, "
+ "expecting at least %u",
+ len, sizeof (*hdr) + sizeof (*key));
+ return;
+ }
+ plen = ntohs(hdr->length);
+ data_len = plen + sizeof (*hdr);
+ wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%d",
+ hdr->version, hdr->type, plen);
+
+ if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, "
+ "not a Key frame", hdr->type);
+ return;
+ }
+ if (plen > len - sizeof (*hdr) || plen < sizeof (*key)) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %u "
+ "invalid (frame size %u)", plen, len);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type);
+ if (key->type != EAPOL_KEY_TYPE_WPA && key->type !=
+ EAPOL_KEY_TYPE_RSN) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, "
+ "discarded", key->type);
+ return;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
+ if (data_len < len) {
+ wpa_printf(MSG_DEBUG, "WPA: ignoring %d bytes after the IEEE "
+ "802.1X data", len - data_len);
+ }
+ key_info = BE_16(key->key_info);
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor "
+ "version %d.", ver);
+ return;
+ }
+
+ if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key "
+ "descriptor version (%d) is not 2.", ver);
+ if (wpa_s->group_cipher != WPA_CIPHER_CCMP &&
+ !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+ /*
+ * Earlier versions of IEEE 802.11i did not explicitly
+ * require version 2 descriptor for all EAPOL-Key
+ * packets, so allow group keys to use version 1 if
+ * CCMP is not used for them.
+ */
+ wpa_printf(MSG_INFO, "WPA: Backwards compatibility: "
+ "allow invalid version for non-CCMP group keys");
+ } else
+ return;
+ }
+
+ if (wpa_s->rx_replay_counter_set &&
+ memcmp(key->replay_counter, wpa_s->rx_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not"
+ " increase - dropping packet");
+ return;
+ }
+
+ if (!(key_info & WPA_KEY_INFO_ACK)) {
+ wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info");
+ return;
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - "
+ "dropped");
+ return;
+ }
+
+ if ((key_info & WPA_KEY_INFO_MIC) &&
+ wpa_supplicant_verify_eapol_key_mic(wpa_s, key, ver, buf,
+ data_len))
+ return;
+
+ extra_len = data_len - sizeof (*hdr) - sizeof (*key);
+
+ if (wpa_s->proto == WPA_PROTO_RSN &&
+ (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+ wpa_supplicant_decrypt_key_data(wpa_s, key, ver))
+ return;
+
+ if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+ if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
+ wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key "
+ "(Pairwise) with non-zero key index");
+ return;
+ }
+ if (key_info & WPA_KEY_INFO_MIC) {
+ /* 3/4 4-Way Handshake */
+ wpa_supplicant_process_3_of_4(wpa_s, src_addr, key,
+ extra_len, ver);
+ } else {
+ /* 1/4 4-Way Handshake */
+ wpa_supplicant_process_1_of_4(wpa_s, src_addr, key,
+ ver);
+ }
+ } else {
+ if (key_info & WPA_KEY_INFO_MIC) {
+ /* 1/2 Group Key Handshake */
+ wpa_supplicant_process_1_of_2(wpa_s, src_addr, key,
+ extra_len, ver);
+ } else {
+ wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) "
+ "without Mic bit - dropped");
+ }
+ }
+}
+
+void
+wpa_supplicant_rx_eapol(void *ctx, unsigned char *src_addr,
+ unsigned char *buf, size_t len)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
+ wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
+
+ if (wpa_s->eapol_received == 0) {
+ /* Timeout for completing IEEE 802.1X and WPA authentication */
+ wpa_supplicant_req_auth_timeout(
+ wpa_s, wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X ?
+ 70 : 10, 0);
+ }
+ wpa_s->eapol_received++;
+
+ /*
+ * Source address of the incoming EAPOL frame could be compared to the
+ * current BSSID. However, it is possible that a centralized
+ * Authenticator could be using another MAC address than the BSSID of
+ * an AP, so just allow any address to be used for now. The replies are
+ * still sent to the current BSSID (if available), though.
+ */
+ wpa_sm_rx_eapol(wpa_s, src_addr, buf, len);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.xml b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.xml
new file mode 100644
index 0000000000..40ab72b46d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWsupr:wpad'>
+
+<service
+ name='network/wpa'
+ type='service'
+ version='1'>
+
+ <!--
+ The wpa service will use the crypto framework of
+ PKCS #11 when we come to the enterprise mode.
+ -->
+ <dependency
+ name='cryptosvc'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/cryptosvc' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/lib/inet/wpad'
+ timeout_seconds='60' >
+ <method_context>
+ <method_credential
+ user='root'
+ group='root'
+ limit_privileges=':default'
+ privileges='basic,sys_net_config,net_rawaccess'
+ />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='60' />
+
+ <property_group name='general' type='framework'>
+ <!-- to start stop wpad -->
+ <propval name='action_authorization' type='astring'
+ value='solaris.smf.manage.wpa' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ Wireless WPA Supplicant
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='wpad' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.c b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.c
new file mode 100644
index 0000000000..f4e634b4a8
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+
+#include <openssl/aes.h>
+#include <openssl/hmac.h>
+#include <openssl/rc4.h>
+
+#include "wpa_enc.h"
+
+/*
+ * @kek: key encryption key (KEK)
+ * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes
+ * @plain: plaintext key to be wrapped, n * 64 bit
+ * @cipher: wrapped key, (n + 1) * 64 bit
+ */
+void
+aes_wrap(uint8_t *kek, int n, uint8_t *plain, uint8_t *cipher)
+{
+ uint8_t *a, *r, b[16];
+ int i, j;
+ AES_KEY key;
+
+ a = cipher;
+ r = cipher + 8;
+
+ /* 1) Initialize variables. */
+ (void) memset(a, 0xa6, 8);
+ (void) memcpy(r, plain, 8 * n);
+
+ AES_set_encrypt_key(kek, 128, &key);
+
+ /*
+ * 2) Calculate intermediate values.
+ * For j = 0 to 5
+ * For i=1 to n
+ * B = AES(K, A | R[i])
+ * A = MSB(64, B) ^ t where t = (n*j)+i
+ * R[i] = LSB(64, B)
+ */
+ for (j = 0; j <= 5; j++) {
+ r = cipher + 8;
+ for (i = 1; i <= n; i++) {
+ (void) memcpy(b, a, 8);
+ (void) memcpy(b + 8, r, 8);
+ AES_encrypt(b, b, &key);
+ (void) memcpy(a, b, 8);
+ a[7] ^= n * j + i;
+ (void) memcpy(r, b + 8, 8);
+ r += 8;
+ }
+ }
+
+ /*
+ * 3) Output the results.
+ *
+ * These are already in @cipher due to the location of temporary
+ * variables.
+ */
+}
+
+/*
+ * @kek: key encryption key (KEK)
+ * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes
+ * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit
+ * @plain: plaintext key, n * 64 bit
+ */
+int
+aes_unwrap(uint8_t *kek, int n, uint8_t *cipher, uint8_t *plain)
+{
+ uint8_t a[8], *r, b[16];
+ int i, j;
+ AES_KEY key;
+
+ /* 1) Initialize variables. */
+ (void) memcpy(a, cipher, 8);
+ r = plain;
+ (void) memcpy(r, cipher + 8, 8 * n);
+
+ AES_set_decrypt_key(kek, 128, &key);
+
+ /*
+ * 2) Compute intermediate values.
+ * For j = 5 to 0
+ * For i = n to 1
+ * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
+ * A = MSB(64, B)
+ * R[i] = LSB(64, B)
+ */
+ for (j = 5; j >= 0; j--) {
+ r = plain + (n - 1) * 8;
+ for (i = n; i >= 1; i--) {
+ (void) memcpy(b, a, 8);
+ b[7] ^= n * j + i;
+
+ (void) memcpy(b + 8, r, 8);
+ AES_decrypt(b, b, &key);
+ (void) memcpy(a, b, 8);
+ (void) memcpy(r, b + 8, 8);
+ r -= 8;
+ }
+ }
+
+ /*
+ * 3) Output results.
+ *
+ * These are already in @plain due to the location of temporary
+ * variables. Just verify that the IV matches with the expected value.
+ */
+ for (i = 0; i < 8; i++) {
+ if (a[i] != 0xa6) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/* RFC 2104 */
+void
+hmac_sha1(unsigned char *key, unsigned int key_len,
+ unsigned char *data, unsigned int data_len, unsigned char *mac)
+{
+ unsigned int mac_len = 0;
+ HMAC(EVP_sha1(), key, key_len, data, data_len, mac, &mac_len);
+}
+
+
+void
+hmac_sha1_vector(unsigned char *key, unsigned int key_len, size_t num_elem,
+ unsigned char *addr[], unsigned int *len, unsigned char *mac)
+{
+ unsigned char *buf, *ptr;
+ int i, buf_len;
+
+ buf_len = 0;
+ for (i = 0; i < num_elem; i ++)
+ buf_len += len[i];
+
+ buf = malloc(buf_len);
+ ptr = buf;
+
+ for (i = 0; i < num_elem; i ++) {
+ (void) memcpy(ptr, addr[i], len[i]);
+ ptr += len[i];
+ }
+
+ hmac_sha1(key, key_len, buf, buf_len, mac);
+
+ free(buf);
+}
+
+
+void
+sha1_prf(unsigned char *key, unsigned int key_len,
+ char *label, unsigned char *data, unsigned int data_len,
+ unsigned char *buf, size_t buf_len)
+{
+ uint8_t zero = 0, counter = 0;
+ size_t pos, plen;
+ uint8_t hash[SHA1_MAC_LEN];
+ size_t label_len = strlen(label);
+
+ unsigned char *addr[4];
+ unsigned int len[4];
+
+ addr[0] = (uint8_t *)label;
+ len[0] = label_len;
+ addr[1] = &zero;
+ len[1] = 1;
+ addr[2] = data;
+ len[2] = data_len;
+ addr[3] = &counter;
+ len[3] = 1;
+
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ if (plen >= SHA1_MAC_LEN) {
+ hmac_sha1_vector(key, key_len, 4, addr, len, &buf[pos]);
+ pos += SHA1_MAC_LEN;
+ } else {
+ hmac_sha1_vector(key, key_len, 4, addr, len, hash);
+ (void) memcpy(&buf[pos], hash, plen);
+ break;
+ }
+ counter++;
+ }
+}
+
+void
+pbkdf2_sha1(char *passphrase, char *ssid, size_t ssid_len, int iterations,
+ unsigned char *buf, size_t buflen)
+{
+ PKCS5_PBKDF2_HMAC_SHA1(passphrase, -1, (unsigned char *)ssid, ssid_len,
+ iterations, buflen, buf);
+}
+
+void
+rc4_skip(uint8_t *key, size_t keylen, size_t skip,
+ uint8_t *data, size_t data_len)
+{
+ uint8_t *buf;
+ size_t buf_len;
+
+ buf_len = skip + data_len;
+ buf = malloc(buf_len);
+
+ bzero(buf, buf_len);
+ bcopy(data, buf + skip, data_len);
+
+ rc4(buf, buf_len, key, keylen);
+
+ bcopy(buf + skip, data, data_len);
+ free(buf);
+}
+
+void
+rc4(uint8_t *buf, size_t len, uint8_t *key, size_t key_len)
+{
+ RC4_KEY k;
+
+ RC4_set_key(&k, key_len, key);
+ RC4(&k, len, buf, buf);
+}
+
+void
+hmac_md5_vector(uint8_t *key, size_t key_len, size_t num_elem,
+ uint8_t *addr[], size_t *len, uint8_t *mac)
+{
+ unsigned char *buf, *ptr;
+ int i, buf_len;
+
+ buf_len = 0;
+ for (i = 0; i < num_elem; i ++)
+ buf_len += len[i];
+
+ buf = malloc(buf_len);
+ ptr = buf;
+
+ for (i = 0; i < num_elem; i ++) {
+ (void) memcpy(ptr, addr[i], len[i]);
+ ptr += len[i];
+ }
+
+ hmac_md5(key, key_len, buf, buf_len, mac);
+ free(buf);
+}
+
+/* RFC 2104 */
+void
+hmac_md5(uint8_t *key, size_t key_len, uint8_t *data,
+ size_t data_len, uint8_t *mac)
+{
+ unsigned int mac_len = 0;
+ HMAC(EVP_md5(), key, key_len, data, data_len, mac, &mac_len);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.h b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.h
new file mode 100644
index 0000000000..34ca57a98a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+#ifndef __WPA_ENC_H
+#define __WPA_ENC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <openssl/sha.h>
+#include <openssl/md5.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SHA1_MAC_LEN SHA_DIGEST_LENGTH
+#define MD5_MAC_LEN MD5_DIGEST_LENGTH
+
+void aes_wrap(uint8_t *, int, uint8_t *, uint8_t *);
+int aes_unwrap(uint8_t *, int, uint8_t *, uint8_t *);
+
+void hmac_sha1_vector(unsigned char *, unsigned int,
+ size_t, unsigned char *[], unsigned int *, unsigned char *);
+
+void hmac_sha1(unsigned char *, unsigned int,
+ unsigned char *, unsigned int, unsigned char *);
+
+void sha1_prf(unsigned char *, unsigned int,
+ char *, unsigned char *, unsigned int, unsigned char *, size_t);
+
+void pbkdf2_sha1(char *, char *, size_t, int, unsigned char *, size_t);
+
+void rc4_skip(uint8_t *, size_t, size_t, uint8_t *, size_t);
+void rc4(uint8_t *, size_t, uint8_t *, size_t);
+
+void hmac_md5_vector(uint8_t *, size_t, size_t,
+ uint8_t *[], size_t *, uint8_t *);
+void hmac_md5(uint8_t *, size_t, uint8_t *, size_t, uint8_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WPA_ENC_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h
new file mode 100644
index 0000000000..e5e6f8f808
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+#ifndef __WPA_IMPL_H
+#define __WPA_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <net/wpa.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BIT(n) (1 << (n))
+
+#define WPA_CIPHER_NONE BIT(0)
+#define WPA_CIPHER_WEP40 BIT(1)
+#define WPA_CIPHER_WEP104 BIT(2)
+#define WPA_CIPHER_TKIP BIT(3)
+#define WPA_CIPHER_CCMP BIT(4)
+
+#define WPA_KEY_MGMT_IEEE8021X BIT(0)
+#define WPA_KEY_MGMT_PSK BIT(1)
+#define WPA_KEY_MGMT_NONE BIT(2)
+#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3)
+
+#define WPA_PROTO_WPA BIT(0)
+#define WPA_PROTO_RSN BIT(1)
+
+#pragma pack(1)
+struct ieee802_1x_hdr {
+ uint8_t version;
+ uint8_t type;
+ uint16_t length;
+ /* followed by length octets of data */
+};
+#pragma pack()
+
+#define EAPOL_VERSION 2
+
+enum { IEEE802_1X_TYPE_EAP_PACKET = 0,
+ IEEE802_1X_TYPE_EAPOL_START = 1,
+ IEEE802_1X_TYPE_EAPOL_LOGOFF = 2,
+ IEEE802_1X_TYPE_EAPOL_KEY = 3,
+ IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4
+};
+
+enum { EAPOL_KEY_TYPE_RC4 = 1,
+ EAPOL_KEY_TYPE_RSN = 2,
+ EAPOL_KEY_TYPE_WPA = 254
+};
+
+#define WPA_NONCE_LEN 32
+#define WPA_REPLAY_COUNTER_LEN 8
+#define MAX_PSK_LENGTH 64
+#define WPA_PMK_LEN 32
+
+#pragma pack(1)
+struct wpa_eapol_key {
+ uint8_t type;
+ uint16_t key_info;
+ uint16_t key_length;
+ uint8_t replay_counter[WPA_REPLAY_COUNTER_LEN];
+ uint8_t key_nonce[WPA_NONCE_LEN];
+ uint8_t key_iv[16];
+ uint8_t key_rsc[8];
+ uint8_t key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+ uint8_t key_mic[16];
+ uint16_t key_data_length;
+ /* followed by key_data_length bytes of key_data */
+};
+#pragma pack()
+
+#define WPA_KEY_INFO_TYPE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0)
+#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1)
+#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1: Pairwise, 0: Group key */
+/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
+#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5))
+#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4
+#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */
+#define WPA_KEY_INFO_TXRX BIT(6) /* group */
+#define WPA_KEY_INFO_ACK BIT(7)
+#define WPA_KEY_INFO_MIC BIT(8)
+#define WPA_KEY_INFO_SECURE BIT(9)
+#define WPA_KEY_INFO_ERROR BIT(10)
+#define WPA_KEY_INFO_REQUEST BIT(11)
+#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */
+
+#define WPA_CAPABILITY_PREAUTH BIT(0)
+
+#define GENERIC_INFO_ELEM 0xdd
+#define RSN_INFO_ELEM 0x30
+
+#define MAX_LOGBUF 4096
+#define MAX_SCANRESULTS 64
+
+enum {
+ REASON_UNSPECIFIED = 1,
+ REASON_DEAUTH_LEAVING = 3,
+ REASON_INVALID_IE = 13,
+ REASON_MICHAEL_MIC_FAILURE = 14,
+ REASON_4WAY_HANDSHAKE_TIMEOUT = 15,
+ REASON_GROUP_KEY_UPDATE_TIMEOUT = 16,
+ REASON_IE_IN_4WAY_DIFFERS = 17,
+ REASON_GROUP_CIPHER_NOT_VALID = 18,
+ REASON_PAIRWISE_CIPHER_NOT_VALID = 19,
+ REASON_AKMP_NOT_VALID = 20,
+ REASON_UNSUPPORTED_RSN_IE_VERSION = 21,
+ REASON_INVALID_RSN_IE_CAPAB = 22,
+ REASON_IEEE_802_1X_AUTH_FAILED = 23,
+ REASON_CIPHER_SUITE_REJECTED = 24
+};
+
+/*
+ * wpa_supplicant
+ */
+#define PMKID_LEN 16
+#define PMK_LEN 32
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+struct rsn_pmksa_cache {
+ struct rsn_pmksa_cache *next;
+ uint8_t pmkid[PMKID_LEN];
+ uint8_t pmk[PMK_LEN];
+ time_t expiration;
+ int akmp; /* WPA_KEY_MGMT_* */
+ uint8_t aa[IEEE80211_ADDR_LEN];
+};
+
+struct rsn_pmksa_candidate {
+ struct rsn_pmksa_candidate *next;
+ uint8_t bssid[IEEE80211_ADDR_LEN];
+};
+
+
+#pragma pack(1)
+struct wpa_ptk {
+ uint8_t mic_key[16]; /* EAPOL-Key MIC Key (MK) */
+ uint8_t encr_key[16]; /* EAPOL-Key Encryption Key (EK) */
+ uint8_t tk1[16]; /* Temporal Key 1 (TK1) */
+ union {
+ uint8_t tk2[16]; /* Temporal Key 2 (TK2) */
+ struct {
+ uint8_t tx_mic_key[8];
+ uint8_t rx_mic_key[8];
+ } auth;
+ } u;
+};
+#pragma pack()
+
+
+struct wpa_supplicant {
+ struct l2_packet_data *l2;
+ unsigned char own_addr[IEEE80211_ADDR_LEN];
+
+ char ifname[WPA_STRSIZE];
+ char kname[WPA_STRSIZE];
+
+ uint8_t pmk[PMK_LEN];
+
+ uint8_t snonce[WPA_NONCE_LEN];
+ uint8_t anonce[WPA_NONCE_LEN];
+ /* ANonce from the last 1/4 msg */
+
+ struct wpa_ptk ptk, tptk;
+ int ptk_set, tptk_set;
+ int renew_snonce;
+
+ struct wpa_config *conf;
+
+ uint8_t request_counter[WPA_REPLAY_COUNTER_LEN];
+ uint8_t rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
+ int rx_replay_counter_set;
+
+ uint8_t bssid[IEEE80211_ADDR_LEN];
+ int reassociate; /* reassociation requested */
+
+ uint8_t *ap_wpa_ie;
+ size_t ap_wpa_ie_len;
+
+ /*
+ * Selected configuration
+ * based on Beacon/ProbeResp WPA IE
+ */
+ int proto;
+ int pairwise_cipher;
+ int group_cipher;
+ int key_mgmt;
+
+ struct wpa_driver_ops *driver;
+
+ enum {
+ WPA_DISCONNECTED,
+ WPA_SCANNING,
+ WPA_ASSOCIATING,
+ WPA_ASSOCIATED,
+ WPA_4WAY_HANDSHAKE,
+ WPA_GROUP_HANDSHAKE,
+ WPA_COMPLETED
+ } wpa_state;
+
+ struct rsn_pmksa_cache *pmksa; /* PMKSA cache */
+ int pmksa_count; /* number of entries in PMKSA cache */
+ struct rsn_pmksa_cache *cur_pmksa; /* current PMKSA entry */
+ struct rsn_pmksa_candidate *pmksa_candidates;
+
+ /*
+ * number of EAPOL packets received after the
+ * previous association event
+ */
+ int eapol_received;
+};
+
+struct wpa_ie_data {
+ int proto;
+ int pairwise_cipher;
+ int group_cipher;
+ int key_mgmt;
+ int capabilities;
+};
+
+/* WPA configuration */
+struct wpa_ssid {
+ uint8_t *ssid;
+ size_t ssid_len;
+
+ uint8_t bssid[IEEE80211_ADDR_LEN];
+ int bssid_set;
+
+ uint8_t psk[PMK_LEN];
+ int psk_set;
+ char *passphrase;
+
+ /* Bitfields of allowed Pairwise/Group Ciphers, WPA_CIPHER_* */
+ int pairwise_cipher;
+ int group_cipher;
+
+ int key_mgmt;
+ int proto; /* Bitfield of allowed protocols (WPA_PROTO_*) */
+};
+
+struct wpa_config {
+ struct wpa_ssid *ssid; /* global network list */
+ int eapol_version;
+ /* int ap_scan; */
+};
+
+struct wpa_config *wpa_config_read(void *);
+void wpa_config_free(struct wpa_config *);
+
+/*
+ * Debugging function - conditional printf and hex dump.
+ * Driver wrappers can use these for debugging purposes.
+ */
+enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
+
+void wpa_printf(int, char *, ...);
+void wpa_hexdump(int, const char *, const uint8_t *, size_t);
+
+void wpa_event_handler(void *, wpa_event_type);
+void wpa_supplicant_rx_eapol(void *, unsigned char *, unsigned char *, size_t);
+
+void wpa_supplicant_scan(void *, void *);
+void wpa_supplicant_req_scan(struct wpa_supplicant *, int, int);
+
+void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *, int, int);
+void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *);
+void wpa_supplicant_disassociate(struct wpa_supplicant *, int);
+
+void pmksa_cache_free(struct wpa_supplicant *);
+void pmksa_candidate_free(struct wpa_supplicant *);
+struct rsn_pmksa_cache *pmksa_cache_get(struct wpa_supplicant *,
+ uint8_t *, uint8_t *);
+
+int wpa_parse_wpa_ie(struct wpa_supplicant *, uint8_t *,
+ size_t, struct wpa_ie_data *);
+int wpa_gen_wpa_ie(struct wpa_supplicant *, uint8_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WPA_IMPL_H */
diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c
new file mode 100644
index 0000000000..270b9133ed
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c
@@ -0,0 +1,927 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Sun elects to license this software under the BSD license.
+ * See README for more details.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <door.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <sys/ethernet.h>
+
+#include "wpa_impl.h"
+#include "wpa_enc.h"
+#include "driver.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+static const char *wpa_supplicant_version =
+"wpa_supplicant v1.0";
+
+extern struct wpa_driver_ops wpa_driver_wifi_ops;
+int wpa_debug_level = MSG_ERROR;
+
+/*
+ * wpa_printf - conditional printf
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ */
+void
+wpa_printf(int level, char *fmt, ...)
+{
+ va_list ap;
+ char buffer[MAX_LOGBUF];
+
+ if (level < wpa_debug_level)
+ return;
+
+ va_start(ap, fmt);
+
+ /* LINTED E_SEC_PRINTF_VAR_FMT */
+ (void) vsnprintf(buffer, sizeof (buffer), fmt, ap);
+
+ va_end(ap);
+
+ syslog(LOG_NOTICE | LOG_DAEMON, "%s", buffer);
+}
+
+/*
+ * wpa_hexdump - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the @buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of @buf is printed out has hex dump.
+ */
+void
+wpa_hexdump(int level, const char *title, const uint8_t *buf, size_t len)
+{
+ size_t i;
+ char buffer[MAX_LOGBUF], tmp[4];
+ int n;
+
+ if (level < wpa_debug_level)
+ return;
+
+ (void) snprintf(buffer, sizeof (buffer), "%s - hexdump(len=%d):",
+ title, len);
+ n = strlen(buffer);
+
+ for (i = 0; i < len; i++) {
+ (void) sprintf(tmp, " %02x", buf[i]);
+
+ n += strlen(tmp);
+ if (n >= MAX_LOGBUF) break;
+
+ (void) strlcat(buffer, tmp, sizeof (buffer));
+ }
+
+ syslog(LOG_NOTICE | LOG_DAEMON, "%s", buffer);
+}
+
+static const char *
+wpa_ssid_txt(char *ssid, size_t ssid_len)
+{
+ static char ssid_txt[MAX_ESSID_LENGTH + 1];
+ char *pos;
+
+ if (ssid_len > MAX_ESSID_LENGTH)
+ ssid_len = MAX_ESSID_LENGTH;
+ (void) memcpy(ssid_txt, ssid, ssid_len);
+ ssid_txt[ssid_len] = '\0';
+ for (pos = ssid_txt; *pos != '\0'; pos ++) {
+ if ((uint8_t)*pos < 32 || (uint8_t)*pos >= 127)
+ *pos = '_';
+ }
+ return (ssid_txt);
+}
+
+/* ARGSUSED */
+void
+wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_ssid *ssid;
+
+ if (wpa_s->conf == NULL)
+ return;
+
+ if (wpa_s->wpa_state == WPA_DISCONNECTED)
+ wpa_s->wpa_state = WPA_SCANNING;
+
+ ssid = wpa_s->conf->ssid;
+ wpa_printf(MSG_DEBUG, "Starting AP scan (%s SSID)",
+ ssid ? "specific": "broadcast");
+
+ if (ssid) {
+ wpa_printf(MSG_DEBUG, "Scan SSID: %s", ssid->ssid);
+ }
+
+ if (wpa_s->driver->scan(wpa_s->ifname)) {
+ wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
+ }
+}
+
+void
+wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
+{
+ wpa_printf(MSG_DEBUG, "Setting scan request: %d sec %d usec",
+ sec, usec);
+ (void) eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+ (void) eloop_register_timeout(sec, usec, wpa_supplicant_scan,
+ wpa_s, NULL);
+}
+
+void
+wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "Cancelling scan request");
+ eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+}
+
+/* ARGSUSED */
+static void
+wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_printf(MSG_INFO, "Authentication with " MACSTR " timed out.",
+ MAC2STR(wpa_s->bssid));
+
+ wpa_s->reassociate = 1;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+void
+wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
+ int sec, int usec)
+{
+ wpa_printf(MSG_DEBUG, "Setting authentication timeout: %d sec "
+ "%d usec", sec, usec);
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+ (void) eloop_register_timeout(sec, usec, wpa_supplicant_timeout,
+ wpa_s, NULL);
+}
+
+void
+wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "Cancelling authentication timeout");
+ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+static void
+wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
+{
+ l2_packet_deinit(wpa_s->l2);
+ wpa_s->l2 = NULL;
+
+ if (wpa_s->conf != NULL) {
+ wpa_config_free(wpa_s->conf);
+ wpa_s->conf = NULL;
+ }
+
+ free(wpa_s->ap_wpa_ie);
+ pmksa_candidate_free(wpa_s);
+ pmksa_cache_free(wpa_s);
+}
+
+static void
+wpa_clear_keys(struct wpa_supplicant *wpa_s, uint8_t *addr)
+{
+ wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE,
+ (uint8_t *)"\xff\xff\xff\xff\xff\xff", 0, 0, NULL, 0, NULL, 0);
+ wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE,
+ (uint8_t *)"\xff\xff\xff\xff\xff\xff", 1, 0, NULL, 0, NULL, 0);
+ wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE,
+ (uint8_t *)"\xff\xff\xff\xff\xff\xff", 2, 0, NULL, 0, NULL, 0);
+ wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE,
+ (uint8_t *)"\xff\xff\xff\xff\xff\xff", 3, 0, NULL, 0, NULL, 0);
+ if (addr) {
+ wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, addr,
+ 0, 0, NULL, 0, NULL, 0);
+ }
+}
+
+static void
+wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->wpa_state = WPA_DISCONNECTED;
+ (void) memset(wpa_s->bssid, 0, IEEE80211_ADDR_LEN);
+}
+
+static int
+wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
+ dladm_wlan_ess_t *bss, struct wpa_ssid *ssid,
+ uint8_t *wpa_ie, int *wpa_ie_len)
+{
+ struct wpa_ie_data ie;
+ int sel, proto;
+ uint8_t *ap_ie;
+ size_t ap_ie_len;
+
+ /* RSN or WPA */
+ if (bss->we_wpa_ie_len && bss->we_wpa_ie[0] == RSN_INFO_ELEM &&
+ (ssid->proto & WPA_PROTO_RSN)) {
+ wpa_printf(MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
+ proto = WPA_PROTO_RSN;
+ } else {
+ wpa_printf(MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
+ proto = WPA_PROTO_WPA;
+ }
+
+ ap_ie = bss->we_wpa_ie;
+ ap_ie_len = bss->we_wpa_ie_len;
+
+ if (wpa_parse_wpa_ie(wpa_s, ap_ie, ap_ie_len, &ie)) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to parse WPA IE for "
+ "the selected BSS.");
+ return (-1);
+ }
+
+ wpa_s->proto = proto;
+ free(wpa_s->ap_wpa_ie);
+ wpa_s->ap_wpa_ie = malloc(ap_ie_len);
+ (void) memcpy(wpa_s->ap_wpa_ie, ap_ie, ap_ie_len);
+ wpa_s->ap_wpa_ie_len = ap_ie_len;
+
+ sel = ie.group_cipher & ssid->group_cipher;
+ if (sel & WPA_CIPHER_CCMP) {
+ wpa_s->group_cipher = WPA_CIPHER_CCMP;
+ } else if (sel & WPA_CIPHER_TKIP) {
+ wpa_s->group_cipher = WPA_CIPHER_TKIP;
+ } else if (sel & WPA_CIPHER_WEP104) {
+ wpa_s->group_cipher = WPA_CIPHER_WEP104;
+ } else if (sel & WPA_CIPHER_WEP40) {
+ wpa_s->group_cipher = WPA_CIPHER_WEP40;
+ } else {
+ wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher.");
+ return (-1);
+ }
+
+ sel = ie.pairwise_cipher & ssid->pairwise_cipher;
+ if (sel & WPA_CIPHER_CCMP) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
+ } else if (sel & WPA_CIPHER_TKIP) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
+ } else if (sel & WPA_CIPHER_NONE) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+ } else {
+ wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
+ "cipher.");
+ return (-1);
+ }
+
+ sel = ie.key_mgmt & ssid->key_mgmt;
+ if (sel & WPA_KEY_MGMT_IEEE8021X) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ } else if (sel & WPA_KEY_MGMT_PSK) {
+ wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
+ } else {
+ wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated "
+ "key management type.");
+ return (-1);
+ }
+
+ *wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
+ if (*wpa_ie_len < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE.");
+ return (-1);
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: Own WPA IE", wpa_ie, *wpa_ie_len);
+
+ if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
+ (void) memcpy(wpa_s->pmk, ssid->psk, PMK_LEN);
+ else if (wpa_s->cur_pmksa)
+ (void) memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, PMK_LEN);
+ else {
+ (void) memset(wpa_s->pmk, 0, PMK_LEN);
+ }
+
+ return (0);
+}
+
+static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
+ dladm_wlan_ess_t *bss, struct wpa_ssid *ssid)
+{
+ uint8_t wpa_ie[IEEE80211_MAX_OPT_IE];
+ int wpa_ie_len;
+
+ wpa_s->reassociate = 0;
+ wpa_printf(MSG_DEBUG, "Trying to associate with " MACSTR
+ " (SSID='%s' freq=%d MHz)", MAC2STR(bss->we_bssid.wb_bytes),
+ wpa_ssid_txt((char *)ssid->ssid, ssid->ssid_len), bss->we_freq);
+ wpa_supplicant_cancel_scan(wpa_s);
+
+ if (bss->we_wpa_ie_len &&
+ (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK))) {
+ wpa_s->cur_pmksa = pmksa_cache_get(wpa_s,
+ bss->we_bssid.wb_bytes, NULL);
+ if (wpa_s->cur_pmksa) {
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
+ wpa_s->cur_pmksa->pmkid, PMKID_LEN);
+ }
+ if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+ wpa_ie, &wpa_ie_len)) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
+ "management and encryption suites");
+ return;
+ }
+ } else {
+ wpa_ie_len = 0;
+ }
+
+ wpa_clear_keys(wpa_s, bss->we_bssid.wb_bytes);
+ wpa_s->wpa_state = WPA_ASSOCIATING;
+ wpa_s->driver->associate(wpa_s->ifname,
+ (const char *)bss->we_bssid.wb_bytes, wpa_ie, wpa_ie_len);
+
+ /* Timeout for IEEE 802.11 authentication and association */
+ wpa_supplicant_req_auth_timeout(wpa_s, 15, 0);
+}
+
+void
+wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, int reason_code)
+{
+ uint8_t *addr = NULL;
+ wpa_s->wpa_state = WPA_DISCONNECTED;
+ if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00",
+ IEEE80211_ADDR_LEN) != 0) {
+ wpa_s->driver->disassociate(wpa_s->ifname, reason_code);
+ addr = wpa_s->bssid;
+ }
+ wpa_clear_keys(wpa_s, addr);
+}
+
+static dladm_wlan_ess_t *
+wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group,
+ dladm_wlan_ess_t *results, int num, struct wpa_ssid **selected_ssid)
+{
+ struct wpa_ssid *ssid;
+ dladm_wlan_ess_t *bss, *selected = NULL;
+ int i;
+
+ struct wpa_ie_data ie;
+
+ wpa_printf(MSG_DEBUG, "Selecting BSS from scan results (%d)", num);
+
+ bss = NULL;
+ ssid = NULL;
+
+ /* try to find matched AP */
+ for (i = 0; i < num && !selected; i++) {
+ bss = &results[i];
+ wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
+ "wpa_ie_len=%d",
+ i, MAC2STR(bss->we_bssid.wb_bytes),
+ wpa_ssid_txt(bss->we_ssid.we_bytes, bss->we_ssid_len),
+ bss->we_wpa_ie_len);
+ if (bss->we_wpa_ie_len == 0) {
+ wpa_printf(MSG_DEBUG, " skip - no WPA/RSN IE");
+ }
+
+ ssid = group;
+ if (bss->we_ssid_len != ssid->ssid_len ||
+ memcmp(bss->we_ssid.we_bytes, ssid->ssid,
+ bss->we_ssid_len) != 0) {
+ wpa_printf(MSG_DEBUG, " skip - SSID mismatch");
+ continue;
+ }
+ if (!((ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_WPA)) &&
+ wpa_parse_wpa_ie(wpa_s, bss->we_wpa_ie,
+ bss->we_wpa_ie_len, &ie) == 0)) {
+ wpa_printf(MSG_DEBUG, " skip - "
+ "could not parse WPA/RSN IE");
+ continue;
+ }
+ if (!(ie.proto & ssid->proto)) {
+ wpa_printf(MSG_DEBUG, " skip - proto mismatch");
+ continue;
+ }
+ if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+ wpa_printf(MSG_DEBUG, " skip - PTK cipher mismatch");
+ continue;
+ }
+ if (!(ie.group_cipher & ssid->group_cipher)) {
+ wpa_printf(MSG_DEBUG, " skip - GTK cipher mismatch");
+ continue;
+ }
+ if (!(ie.key_mgmt & ssid->key_mgmt)) {
+ wpa_printf(MSG_DEBUG, " skip - key mgmt mismatch");
+ continue;
+ }
+
+ selected = bss;
+ *selected_ssid = ssid;
+ wpa_printf(MSG_DEBUG, " selected");
+ }
+
+ return (selected);
+}
+
+
+static void
+wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s)
+{
+ dladm_wlan_ess_t results[MAX_SCANRESULTS];
+ int num;
+ dladm_wlan_ess_t *selected = NULL;
+ struct wpa_ssid *ssid;
+
+ (void) memset(results, 0, sizeof (dladm_wlan_ess_t) * MAX_SCANRESULTS);
+ num = wpa_s->driver->get_scan_results(wpa_s->ifname, results,
+ MAX_SCANRESULTS);
+ wpa_printf(MSG_DEBUG, "Scan results: %d", num);
+ if (num < 0)
+ return;
+ if (num > MAX_SCANRESULTS) {
+ wpa_printf(MSG_INFO, "Not enough room for all APs (%d < %d)",
+ num, MAX_SCANRESULTS);
+ num = MAX_SCANRESULTS;
+ }
+
+ selected = wpa_supplicant_select_bss(wpa_s,
+ wpa_s->conf->ssid, results, num, &ssid);
+
+ if (selected) {
+ if (wpa_s->reassociate ||
+ memcmp(selected->we_bssid.wb_bytes, wpa_s->bssid,
+ IEEE80211_ADDR_LEN) != 0) {
+ wpa_supplicant_associate(wpa_s, selected, ssid);
+ } else {
+ wpa_printf(MSG_DEBUG, "Already associated with the "
+ "selected AP.");
+ }
+ } else {
+ wpa_printf(MSG_DEBUG, "No suitable AP found.");
+ wpa_supplicant_req_scan(wpa_s, 5, 0); /* wait 5 seconds */
+ }
+}
+
+/*
+ * wpa_event_handler - report a driver event for wpa_supplicant
+ * @wpa_s: pointer to wpa_supplicant data; this is the @ctx variable registered
+ * with wpa_driver_events_init()
+ * @event: event type (defined above)
+ *
+ * Driver wrapper code should call this function whenever an event is received
+ * from the driver.
+ */
+void
+wpa_event_handler(void *cookie, wpa_event_type event)
+{
+ struct wpa_supplicant *wpa_s = cookie;
+ uint8_t bssid[IEEE80211_ADDR_LEN];
+
+ switch (event) {
+ case EVENT_ASSOC:
+ wpa_s->wpa_state = WPA_ASSOCIATED;
+ wpa_printf(MSG_DEBUG, "\nAssociation event - clear replay "
+ "counter\n");
+ (void) memset(wpa_s->rx_replay_counter, 0,
+ WPA_REPLAY_COUNTER_LEN);
+ wpa_s->rx_replay_counter_set = 0;
+ wpa_s->renew_snonce = 1;
+ if (wpa_s->driver->get_bssid(wpa_s->ifname,
+ (char *)bssid) >= 0 &&
+ memcmp(bssid, wpa_s->bssid, IEEE80211_ADDR_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "Associated to a new BSS: "
+ "BSSID=" MACSTR, MAC2STR(bssid));
+ (void) memcpy(wpa_s->bssid, bssid, IEEE80211_ADDR_LEN);
+ if (wpa_s->key_mgmt != WPA_KEY_MGMT_NONE)
+ wpa_clear_keys(wpa_s, bssid);
+ }
+
+ wpa_s->eapol_received = 0;
+ if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
+ wpa_supplicant_cancel_auth_timeout(wpa_s);
+ } else {
+ /* Timeout for receiving the first EAPOL packet */
+ wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
+ }
+ break;
+ case EVENT_DISASSOC:
+ if (wpa_s->wpa_state >= WPA_ASSOCIATED)
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+ wpa_supplicant_mark_disassoc(wpa_s);
+ wpa_printf(MSG_DEBUG, "Disconnect event - remove keys");
+ if (wpa_s->key_mgmt != WPA_KEY_MGMT_NONE)
+ wpa_clear_keys(wpa_s, wpa_s->bssid);
+ break;
+ case EVENT_SCAN_RESULTS:
+ wpa_supplicant_scan_results(wpa_s);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "Unknown event %d", event);
+ break;
+ }
+}
+
+/* ARGSUSED */
+static void
+wpa_supplicant_terminate(int sig, void *eloop_ctx, void *signal_ctx)
+{
+ wpa_printf(MSG_INFO, "Signal %d received - terminating", sig);
+ eloop_terminate();
+}
+
+static int
+wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->l2 = l2_packet_init(wpa_s->ifname, ETHERTYPE_EAPOL,
+ wpa_supplicant_rx_eapol, wpa_s);
+ if (wpa_s->l2 == NULL)
+ return (-1);
+
+ if (l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
+ (void) fprintf(stderr, "Failed to get own L2 address\n");
+ return (-1);
+ }
+
+ if (wpa_s->driver->set_wpa(wpa_s->ifname, 1) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to enable WPA in the driver.");
+ return (-1);
+ }
+
+ wpa_clear_keys(wpa_s, NULL);
+ wpa_supplicant_req_scan(wpa_s, 0, 100000);
+
+ return (0);
+}
+
+static int door_id = -1;
+
+/* ARGSUSED */
+static void
+event_handler(void *cookie, char *argp, size_t asize,
+ door_desc_t *dp, uint_t n_desc)
+{
+ wpa_event_type event;
+
+ event = ((wl_events_t *)argp)->event;
+ wpa_event_handler(cookie, event);
+
+ (void) door_return(NULL, 0, NULL, 0);
+}
+
+/*
+ * Create the driver to wpad door
+ */
+int
+wpa_supplicant_door_setup(void *cookie, char *doorname)
+{
+ struct stat stbuf;
+ int error = 0;
+
+ wpa_printf(MSG_DEBUG, "wpa_supplicant_door_setup(%s)", doorname);
+ /*
+ * Create the door
+ */
+ door_id = door_create(event_handler, cookie,
+ DOOR_UNREF | DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
+
+ if (door_id < 0) {
+ error = -1;
+ goto out;
+ }
+
+ if (stat(doorname, &stbuf) < 0) {
+ int newfd;
+ if ((newfd = creat(doorname, 0666)) < 0) {
+ (void) door_revoke(door_id);
+ door_id = -1;
+ error = -1;
+
+ goto out;
+ }
+ (void) close(newfd);
+ }
+
+ if (fattach(door_id, doorname) < 0) {
+ if ((errno != EBUSY) || (fdetach(doorname) < 0) ||
+ (fattach(door_id, doorname) < 0)) {
+ (void) door_revoke(door_id);
+ door_id = -1;
+ error = -1;
+
+ goto out;
+ }
+ }
+
+out:
+ return (error);
+}
+
+void
+wpa_supplicant_door_destroy(char *doorname)
+{
+ wpa_printf(MSG_DEBUG, "wpa_supplicant_door_destroy(%s)\n", doorname);
+
+ if (door_revoke(door_id) == -1) {
+ wpa_printf(MSG_ERROR, "failed to door_revoke(%d) %s, exiting.",
+ door_id, strerror(errno));
+ }
+
+ if (fdetach(doorname) == -1) {
+ wpa_printf(MSG_ERROR, "failed to fdetach %s: %s, exiting.",
+ doorname, strerror(errno));
+ }
+
+ (void) close(door_id);
+}
+
+static int
+wpa_config_parse_ssid(struct wpa_ssid *ssid, int line, const char *value)
+{
+ free(ssid->ssid);
+
+ ssid->ssid = (uint8_t *)strdup(value);
+ ssid->ssid_len = strlen(value);
+
+ if (ssid->ssid == NULL) {
+ wpa_printf(MSG_ERROR, "Invalid SSID '%s'.", line, value);
+ return (-1);
+ }
+ if (ssid->ssid_len > MAX_ESSID_LENGTH) {
+ free(ssid->ssid);
+ wpa_printf(MSG_ERROR, "Too long SSID '%s'.", line, value);
+ return (-1);
+ }
+ wpa_printf(MSG_MSGDUMP, "SSID: %s", ssid->ssid);
+ return (0);
+}
+
+static struct wpa_ssid *
+wpa_config_read_network(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_ssid *ssid;
+ char buf[MAX_ESSID_LENGTH + 1];
+ dladm_secobj_class_t cl;
+ uint8_t psk[MAX_PSK_LENGTH + 1];
+ uint_t key_len;
+
+ wpa_printf(MSG_MSGDUMP, "Start of a new network configration");
+
+ ssid = (struct wpa_ssid *)malloc(sizeof (*ssid));
+ if (ssid == NULL)
+ return (NULL);
+ (void) memset(ssid, 0, sizeof (*ssid));
+
+ /*
+ * Set default supported values
+ */
+ ssid->proto = WPA_PROTO_WPA | WPA_PROTO_RSN;
+ ssid->pairwise_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP;
+ ssid->group_cipher = WPA_CIPHER_CCMP | WPA_CIPHER_TKIP |
+ WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40;
+ ssid->key_mgmt = WPA_KEY_MGMT_PSK; /* | WPA_KEY_MGMT_IEEE8021X; */
+
+ (void) memset(buf, 0, MAX_ESSID_LENGTH + 1);
+ wpa_s->driver->get_ssid(wpa_s->ifname, (char *)buf);
+
+ (void) wpa_config_parse_ssid(ssid, 0, buf);
+
+ key_len = sizeof (psk);
+ (void) dladm_get_secobj((const char *)wpa_s->kname, &cl, psk, &key_len,
+ DLADM_OPT_TEMP);
+ psk[key_len] = '\0';
+ ssid->passphrase = strdup((const char *)psk);
+
+ if (ssid->passphrase) {
+ pbkdf2_sha1(ssid->passphrase, (char *)ssid->ssid,
+ ssid->ssid_len, 4096, ssid->psk, PMK_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "PSK (from passphrase)",
+ ssid->psk, PMK_LEN);
+ ssid->psk_set = 1;
+ }
+
+ if ((ssid->key_mgmt & WPA_KEY_MGMT_PSK) && !ssid->psk_set) {
+ wpa_printf(MSG_ERROR, "WPA-PSK accepted for key "
+ "management, but no PSK configured.");
+ free(ssid);
+ ssid = NULL;
+ }
+
+ return (ssid);
+}
+
+struct wpa_config *
+wpa_config_read(void *arg)
+{
+ struct wpa_ssid *ssid;
+ struct wpa_config *config;
+ struct wpa_supplicant *wpa_s = arg;
+
+ config = malloc(sizeof (*config));
+ if (config == NULL)
+ return (NULL);
+ (void) memset(config, 0, sizeof (*config));
+ config->eapol_version = 1; /* fixed value */
+
+ wpa_printf(MSG_DEBUG, "Reading configuration parameters from driver\n");
+
+ ssid = wpa_config_read_network(wpa_s);
+ if (ssid == NULL) {
+ wpa_config_free(config);
+ config = NULL;
+ } else {
+ config->ssid = ssid;
+ }
+
+ return (config);
+}
+
+void
+wpa_config_free(struct wpa_config *config)
+{
+ struct wpa_ssid *ssid = config->ssid;
+
+ free(ssid->ssid);
+ free(ssid->passphrase);
+ free(ssid);
+ free(config);
+}
+
+static int
+daemon(boolean_t nochdir, boolean_t noclose)
+{
+ int retv;
+
+ if ((retv = fork()) == -1)
+ return (-1);
+ if (retv != 0)
+ _exit(EXIT_SUCCESS);
+ if (setsid() == -1)
+ return (-1);
+
+ if (!nochdir && chdir("/") == -1)
+ return (-1);
+
+ if (!noclose) {
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ if ((retv = open("/dev/null", O_RDWR)) != -1) {
+ (void) dup2(retv, 1);
+ (void) dup2(retv, 2);
+ }
+ }
+
+ return (0);
+}
+
+static void
+usage(void)
+{
+ (void) printf("%s\n\n"
+ "usage:\n"
+ " wpa_supplicant [-hv] -i<ifname> -k<keyname>"
+ "\n"
+ "options:\n"
+ " -h = show this help text\n"
+ " -v = show version\n",
+ wpa_supplicant_version);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct wpa_supplicant wpa_s;
+ char *link = NULL;
+ char *key = NULL;
+ int c;
+ int exitcode;
+ char door_file[WPA_STRSIZE];
+
+ (void) memset(&wpa_s, 0, sizeof (wpa_s));
+
+ for (;;) {
+ c = getopt(argc, argv, "Dk:hi:v");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'D':
+ wpa_debug_level = MSG_DEBUG;
+ break;
+ case 'h':
+ usage();
+ return (-1);
+ case 'i':
+ link = optarg;
+ break;
+ case 'k':
+ key = optarg;
+ break;
+ case 'v':
+ (void) printf("%s\n", wpa_supplicant_version);
+ return (-1);
+ default:
+ usage();
+ return (-1);
+ }
+ }
+
+ wpa_s.driver = &wpa_driver_wifi_ops;
+ eloop_init(&wpa_s);
+ /*
+ * key name is required to retrieve PSK value through libwdladm APIs.
+ * key is saved by dladm command by keyname
+ * see dladm.
+ */
+ if ((link == NULL) || (key == NULL)) {
+ wpa_printf(MSG_ERROR, "\nLink & key is required.");
+ return (-1);
+ }
+
+ if ((strlen(link) >= sizeof (wpa_s.ifname)) ||
+ (strlen(key) >= sizeof (wpa_s.kname))) {
+ wpa_printf(MSG_ERROR, "Too long link/key name '%s', '%s'.",
+ link, key);
+ return (-1);
+ }
+
+ (void) strlcpy(wpa_s.ifname, link, sizeof (wpa_s.ifname));
+ (void) strlcpy(wpa_s.kname, key, sizeof (wpa_s.kname));
+
+ /*
+ * Setup door file to communicate with driver
+ * Since this is multiple instance service, different instance
+ * has different doors.
+ */
+ (void) snprintf(door_file, WPA_STRSIZE, "%s_%s", WPA_DOOR, link);
+
+ /*
+ * Setup default WPA/WPA2 configuration
+ * get ESSID and PSK value
+ */
+ wpa_s.conf = wpa_config_read(&wpa_s);
+ if (wpa_s.conf == NULL || wpa_s.conf->ssid == NULL) {
+ wpa_printf(MSG_ERROR, "\nNo networks (SSID) configured.\n");
+ return (-1);
+ }
+
+ exitcode = 0;
+
+ if (daemon(0, 0)) {
+ exitcode = -1;
+ goto cleanup;
+ }
+
+ if (wpa_supplicant_door_setup(&wpa_s, door_file) != 0) {
+ wpa_printf(MSG_ERROR, "Failed to setup door(%s)", door_file);
+ exitcode = -1;
+ goto cleanup;
+ }
+
+ wpa_s.renew_snonce = 1;
+ if (wpa_supplicant_driver_init(&wpa_s) < 0) {
+ exitcode = -1;
+ goto cleanup;
+ }
+
+ wpa_printf(MSG_DEBUG, "=> eloop_run");
+
+ (void) eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL);
+ (void) eloop_register_signal(SIGTERM, wpa_supplicant_terminate, NULL);
+ (void) eloop_register_signal(SIGKILL, wpa_supplicant_terminate, NULL);
+
+ eloop_run();
+
+ wpa_printf(MSG_DEBUG, "<= eloop_run()");
+ wpa_supplicant_disassociate(&wpa_s, REASON_DEAUTH_LEAVING);
+
+cleanup:
+ if (wpa_s.driver->set_wpa(wpa_s.ifname, 0) < 0) {
+ wpa_printf(MSG_ERROR, "Failed to disable WPA in the driver.\n");
+ }
+
+ wpa_supplicant_door_destroy(door_file);
+ wpa_supplicant_cleanup(&wpa_s);
+ eloop_destroy();
+
+ return (exitcode);
+}
diff --git a/usr/src/cmd/dladm/dladm.c b/usr/src/cmd/dladm/dladm.c
index 1c35aa38a3..34a1448663 100644
--- a/usr/src/cmd/dladm/dladm.c
+++ b/usr/src/cmd/dladm/dladm.c
@@ -219,7 +219,7 @@ usage(void)
"\n"
"\tscan-wifi [-p] [-o <field>,...] [<name>]\n"
"\tconnect-wifi [-e <essid>] [-i <bssid>] [-k <key>,...]"
- " [-s wep]\n"
+ " [-s wep|wpa]\n"
"\t [-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n"
"\t [-T <time>] [<name>]\n"
"\tdisconnect-wifi [-a] [<name>]\n"
@@ -2024,17 +2024,17 @@ do_count_wlan(void *arg, const char *link)
}
static int
-parse_wep_keys(char *str, dladm_wlan_wepkey_t **keys, uint_t *key_countp)
+parse_wlan_keys(char *str, dladm_wlan_key_t **keys, uint_t *key_countp)
{
uint_t i;
split_t *sp;
- dladm_wlan_wepkey_t *wk;
+ dladm_wlan_key_t *wk;
- sp = split(str, DLADM_WLAN_MAX_WEPKEYS, DLADM_WLAN_MAX_WEPKEYNAME_LEN);
+ sp = split(str, DLADM_WLAN_MAX_WEPKEYS, DLADM_WLAN_MAX_KEYNAME_LEN);
if (sp == NULL)
return (-1);
- wk = malloc(sp->s_nfields * sizeof (dladm_wlan_wepkey_t));
+ wk = malloc(sp->s_nfields * sizeof (dladm_wlan_key_t));
if (wk == NULL)
goto fail;
@@ -2044,7 +2044,7 @@ parse_wep_keys(char *str, dladm_wlan_wepkey_t **keys, uint_t *key_countp)
dladm_status_t status;
(void) strlcpy(wk[i].wk_name, sp->s_fields[i],
- DLADM_WLAN_MAX_WEPKEYNAME_LEN);
+ DLADM_WLAN_MAX_KEYNAME_LEN);
wk[i].wk_idx = 1;
if ((s = strrchr(wk[i].wk_name, ':')) != NULL) {
@@ -2054,7 +2054,7 @@ parse_wep_keys(char *str, dladm_wlan_wepkey_t **keys, uint_t *key_countp)
wk[i].wk_idx = (uint_t)(s[1] - '0');
*s = '\0';
}
- wk[i].wk_len = DLADM_WLAN_MAX_WEPKEY_LEN;
+ wk[i].wk_len = DLADM_WLAN_MAX_KEY_LEN;
status = dladm_get_secobj(wk[i].wk_name, &class,
wk[i].wk_val, &wk[i].wk_len, 0);
@@ -2067,6 +2067,7 @@ parse_wep_keys(char *str, dladm_wlan_wepkey_t **keys, uint_t *key_countp)
if (status != DLADM_STATUS_OK)
goto fail;
}
+ wk[i].wk_class = class;
}
*keys = wk;
*key_countp = i;
@@ -2086,10 +2087,11 @@ do_connect_wifi(int argc, char **argv)
dladm_status_t status = DLADM_STATUS_OK;
int timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT;
const char *link = NULL;
- dladm_wlan_wepkey_t *keys = NULL;
+ dladm_wlan_key_t *keys = NULL;
uint_t key_count = 0;
uint_t flags = 0;
dladm_wlan_secmode_t keysecmode = DLADM_WLAN_SECMODE_NONE;
+ char buf[DLADM_STRSIZE];
opterr = 0;
(void) memset(&attr, 0, sizeof (attr));
@@ -2145,10 +2147,13 @@ do_connect_wifi(int argc, char **argv)
attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
break;
case 'k':
- if (parse_wep_keys(optarg, &keys, &key_count) < 0)
+ if (parse_wlan_keys(optarg, &keys, &key_count) < 0)
die("invalid key(s) '%s'", optarg);
- keysecmode = DLADM_WLAN_SECMODE_WEP;
+ if (keys[0].wk_class == DLADM_SECOBJ_CLASS_WEP)
+ keysecmode = DLADM_WLAN_SECMODE_WEP;
+ else
+ keysecmode = DLADM_WLAN_SECMODE_WPA;
break;
case 'T':
if (strcasecmp(optarg, "forever") == 0) {
@@ -2160,6 +2165,7 @@ do_connect_wifi(int argc, char **argv)
break;
case 'c':
flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
+ flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
break;
default:
die_opterr(optopt, option);
@@ -2168,16 +2174,17 @@ do_connect_wifi(int argc, char **argv)
}
if (keysecmode == DLADM_WLAN_SECMODE_NONE) {
- if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
- attr.wa_secmode == DLADM_WLAN_SECMODE_WEP)
- die("key required for security mode 'wep'");
+ if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0) {
+ die("key required for security mode '%s'",
+ dladm_wlan_secmode2str(&attr.wa_secmode, buf));
+ }
} else {
if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
attr.wa_secmode != keysecmode)
die("incompatible -s and -k options");
+ attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
+ attr.wa_secmode = keysecmode;
}
- attr.wa_secmode = keysecmode;
- attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
if (optind == (argc - 1))
link = argv[optind];
@@ -2803,29 +2810,39 @@ convert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp,
{
int error = 0;
- if (class != DLADM_SECOBJ_CLASS_WEP)
- return (ENOENT);
-
- switch (len) {
- case 5: /* ASCII key sizes */
- case 13:
+ if (class == DLADM_SECOBJ_CLASS_WPA) {
+ if (len < 8 || len > 63)
+ return (EINVAL);
(void) memcpy(obj_val, buf, len);
*obj_lenp = len;
- break;
- case 10: /* Hex key sizes, not preceded by 0x */
- case 26:
- error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
- break;
- case 12: /* Hex key sizes, preceded by 0x */
- case 28:
- if (strncmp(buf, "0x", 2) != 0)
+ return (error);
+ }
+
+ if (class == DLADM_SECOBJ_CLASS_WEP) {
+ switch (len) {
+ case 5: /* ASCII key sizes */
+ case 13:
+ (void) memcpy(obj_val, buf, len);
+ *obj_lenp = len;
+ break;
+ case 10: /* Hex key sizes, not preceded by 0x */
+ case 26:
+ error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
+ break;
+ case 12: /* Hex key sizes, preceded by 0x */
+ case 28:
+ if (strncmp(buf, "0x", 2) != 0)
+ return (EINVAL);
+ error = hexascii_to_octet(buf + 2, len - 2,
+ obj_val, obj_lenp);
+ break;
+ default:
return (EINVAL);
- error = hexascii_to_octet(buf + 2, len - 2, obj_val, obj_lenp);
- break;
- default:
- return (EINVAL);
+ }
+ return (error);
}
- return (error);
+
+ return (ENOENT);
}
/* ARGSUSED */
@@ -3026,7 +3043,7 @@ do_create_secobj(int argc, char **argv)
status = dladm_str2secobjclass(optarg, &class);
if (status != DLADM_STATUS_OK) {
die("invalid secure object class '%s', "
- "valid values are: wep", optarg);
+ "valid values are: wep, wpa", optarg);
}
break;
case 't':
@@ -3139,7 +3156,7 @@ do_delete_secobj(int argc, char **argv)
die("secure object name required");
success = check_auth(LINK_SEC_AUTH);
- audit_secobj(LINK_SEC_AUTH, "wep", argv[optind], success, B_FALSE);
+ audit_secobj(LINK_SEC_AUTH, "unknown", argv[optind], success, B_FALSE);
if (!success)
die("authorization '%s' is required", LINK_SEC_AUTH);
diff --git a/usr/src/lib/libdladm/Makefile.com b/usr/src/lib/libdladm/Makefile.com
index a3da9a4770..6e17165bd9 100644
--- a/usr/src/lib/libdladm/Makefile.com
+++ b/usr/src/lib/libdladm/Makefile.com
@@ -35,7 +35,7 @@ include ../../Makefile.lib
include ../../Makefile.rootfs
LIBS = $(DYNLIB) $(LINTLIB)
-LDLIBS += -ldevinfo -ldlpi -lc -linetutil -lsocket
+LDLIBS += -ldevinfo -ldlpi -lc -linetutil -lsocket -lscf
SRCDIR = ../common
$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
diff --git a/usr/src/lib/libdladm/common/libdllink.h b/usr/src/lib/libdladm/common/libdllink.h
index 2f5bfd145f..12327456a5 100644
--- a/usr/src/lib/libdladm/common/libdllink.h
+++ b/usr/src/lib/libdladm/common/libdllink.h
@@ -64,6 +64,7 @@ typedef struct dladm_attr {
#define DLADM_PROP_VAL_MAX 25
#define DLADM_SECOBJ_CLASS_WEP 0
+#define DLADM_SECOBJ_CLASS_WPA 1
typedef int dladm_secobj_class_t;
typedef void (dladm_walkcb_t)(void *, const char *);
diff --git a/usr/src/lib/libdladm/common/libdlwlan.c b/usr/src/lib/libdladm/common/libdlwlan.c
index d7608eede1..6693a614d5 100644
--- a/usr/src/lib/libdladm/common/libdlwlan.c
+++ b/usr/src/lib/libdladm/common/libdlwlan.c
@@ -37,9 +37,10 @@
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
+#include <libscf.h>
#include <libdlwlan.h>
#include <libdlwlan_impl.h>
-#include <inet/wifi_ioctl.h>
+#include <net/wpa.h>
typedef struct val_desc {
char *vd_name;
@@ -63,6 +64,9 @@ typedef struct prop_desc {
wl_pd_checkf_t *pd_check;
} prop_desc_t;
+static int wpa_instance_create(const char *, void *);
+static int wpa_instance_delete(const char *);
+
static int do_get_bsstype(int, wldp_t *);
static int do_get_essid(int, wldp_t *);
static int do_get_bssid(int, wldp_t *);
@@ -76,13 +80,15 @@ static int do_get_phyconf(int, wldp_t *);
static int do_get_powermode(int, wldp_t *);
static int do_get_radio(int, wldp_t *);
static int do_get_mode(int, wldp_t *);
+static int do_get_capability(int, wldp_t *);
+static int do_get_wpamode(int, wldp_t *);
static int do_set_bsstype(int, wldp_t *, dladm_wlan_bsstype_t *);
static int do_set_authmode(int, wldp_t *, dladm_wlan_auth_t *);
static int do_set_encryption(int, wldp_t *, dladm_wlan_secmode_t *);
static int do_set_essid(int, wldp_t *, dladm_wlan_essid_t *);
static int do_set_createibss(int, wldp_t *, boolean_t *);
-static int do_set_wepkey(int, wldp_t *, dladm_wlan_wepkey_t *, uint_t);
+static int do_set_key(int, wldp_t *, dladm_wlan_key_t *, uint_t);
static int do_set_rate(int, wldp_t *, dladm_wlan_rates_t *);
static int do_set_powermode(int, wldp_t *, dladm_wlan_powermode_t *);
static int do_set_radio(int, wldp_t *, dladm_wlan_radio_t *);
@@ -90,7 +96,7 @@ static int do_set_channel(int, wldp_t *, dladm_wlan_channel_t *);
static int open_link(const char *);
static int do_scan(int, wldp_t *);
-static int do_disconnect(int, wldp_t *);
+static int do_disconnect(const char *, int, wldp_t *);
static boolean_t find_val_by_name(const char *, val_desc_t *, uint_t, uint_t *);
static boolean_t find_name_by_val(uint_t, val_desc_t *, uint_t, char **);
static void generate_essid(dladm_wlan_essid_t *);
@@ -110,7 +116,8 @@ static val_desc_t linkstatus_vals[] = {
static val_desc_t secmode_vals[] = {
{ "none", DLADM_WLAN_SECMODE_NONE },
- { "wep", DLADM_WLAN_SECMODE_WEP }
+ { "wep", DLADM_WLAN_SECMODE_WEP },
+ { "wpa", DLADM_WLAN_SECMODE_WPA }
};
static val_desc_t strength_vals[] = {
@@ -293,6 +300,8 @@ fill_wlan_attr(wl_ess_conf_t *wlp, dladm_wlan_attr_t *attrp)
attrp->wa_secmode = (wlp->wl_ess_conf_wepenabled ==
WL_ENC_WEP ? DLADM_WLAN_SECMODE_WEP : DLADM_WLAN_SECMODE_NONE);
+ if (wlp->wl_ess_conf_reserved[0] > 0)
+ attrp->wa_secmode = DLADM_WLAN_SECMODE_WPA;
attrp->wa_valid |= DLADM_WLAN_ATTR_SECMODE;
attrp->wa_bsstype = (wlp->wl_ess_conf_bsstype == WL_BSS_BSS ?
@@ -353,6 +362,11 @@ dladm_wlan_scan(const char *link, void *arg,
goto done;
}
+ if (func == NULL) {
+ status = DLADM_STATUS_OK;
+ goto done;
+ }
+
if (do_get_esslist(fd, gbuf) < 0) {
status = DLADM_STATUS_FAILED;
goto done;
@@ -373,7 +387,7 @@ dladm_wlan_scan(const char *link, void *arg,
goto done;
}
if (IS_CONNECTED(gbuf))
- (void) do_disconnect(fd, gbuf);
+ (void) do_disconnect(link, fd, gbuf);
}
status = DLADM_STATUS_OK;
@@ -490,17 +504,20 @@ append:
return (B_TRUE);
}
+#define IEEE80211_C_WPA 0x01800000
+
static dladm_status_t
-do_connect(int fd, wldp_t *gbuf, dladm_wlan_attr_t *attrp,
+do_connect(const char *link, int fd, wldp_t *gbuf, dladm_wlan_attr_t *attrp,
boolean_t create_ibss, void *keys, uint_t key_count, int timeout)
{
dladm_wlan_secmode_t secmode;
dladm_wlan_auth_t authmode;
dladm_wlan_bsstype_t bsstype;
dladm_wlan_essid_t essid;
- boolean_t essid_valid = B_FALSE;
+ boolean_t essid_valid = B_FALSE;
dladm_wlan_channel_t channel;
- hrtime_t start;
+ hrtime_t start;
+ wl_capability_t *caps;
if ((attrp->wa_valid & DLADM_WLAN_ATTR_CHANNEL) != 0) {
channel = attrp->wa_channel;
@@ -529,8 +546,16 @@ do_connect(int fd, wldp_t *gbuf, dladm_wlan_attr_t *attrp,
if (secmode == DLADM_WLAN_SECMODE_WEP) {
if (keys == NULL || key_count == 0 || key_count > MAX_NWEPKEYS)
return (DLADM_STATUS_BADARG);
- if (do_set_wepkey(fd, gbuf, keys, key_count) < 0)
+ if (do_set_key(fd, gbuf, keys, key_count) < 0)
+ goto fail;
+ } else if (secmode == DLADM_WLAN_SECMODE_WPA) {
+ if (keys == NULL || key_count == 0 || key_count > MAX_NWEPKEYS)
+ return (DLADM_STATUS_BADARG);
+ if (do_get_capability(fd, gbuf) < 0)
goto fail;
+ caps = (wl_capability_t *)(gbuf->wldp_buf);
+ if ((caps->caps & IEEE80211_C_WPA) == 0)
+ return (DLADM_STATUS_NOTSUP);
}
if (create_ibss) {
@@ -556,6 +581,13 @@ do_connect(int fd, wldp_t *gbuf, dladm_wlan_attr_t *attrp,
if (do_set_essid(fd, gbuf, &essid) < 0)
goto fail;
+ /*
+ * Because wpa daemon needs getting essid from driver,
+ * we need call do_set_essid() first, then call wpa_instance_create().
+ */
+ if (secmode == DLADM_WLAN_SECMODE_WPA && keys != NULL)
+ (void) wpa_instance_create(link, keys);
+
start = gethrtime();
for (;;) {
if (do_get_linkstatus(fd, gbuf) < 0)
@@ -614,7 +646,7 @@ dladm_wlan_connect(const char *link, dladm_wlan_attr_t *attrp,
if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0 ||
(create_ibss && attrp != NULL &&
(attrp->wa_valid & DLADM_WLAN_ATTR_ESSID) == 0)) {
- status = do_connect(fd, gbuf, attrp,
+ status = do_connect(link, fd, gbuf, attrp,
create_ibss, keys, key_count, timeout);
goto done;
}
@@ -632,7 +664,7 @@ dladm_wlan_connect(const char *link, dladm_wlan_attr_t *attrp,
status = DLADM_STATUS_NOTFOUND;
goto done;
}
- status = do_connect(fd, gbuf, attrp, create_ibss,
+ status = do_connect(link, fd, gbuf, attrp, create_ibss,
keys, key_count, timeout);
goto done;
}
@@ -654,7 +686,7 @@ dladm_wlan_connect(const char *link, dladm_wlan_attr_t *attrp,
for (i = 0; i < state.cs_count; i++) {
dladm_wlan_attr_t *ap = wl_list[i];
- status = do_connect(fd, gbuf, ap, create_ibss, keys,
+ status = do_connect(link, fd, gbuf, ap, create_ibss, keys,
key_count, timeout);
if (status == DLADM_STATUS_OK)
break;
@@ -662,15 +694,15 @@ dladm_wlan_connect(const char *link, dladm_wlan_attr_t *attrp,
if (!set_authmode) {
ap->wa_auth = DLADM_WLAN_AUTH_SHARED;
ap->wa_valid |= DLADM_WLAN_ATTR_AUTH;
- status = do_connect(fd, gbuf, ap, create_ibss, keys,
- key_count, timeout);
+ status = do_connect(link, fd, gbuf, ap, create_ibss,
+ keys, key_count, timeout);
if (status == DLADM_STATUS_OK)
break;
}
}
done:
if ((status != DLADM_STATUS_OK) && (status != DLADM_STATUS_ISCONN))
- (void) do_disconnect(fd, gbuf);
+ (void) do_disconnect(link, fd, gbuf);
while (state.cs_list != NULL) {
nodep = state.cs_list;
@@ -708,7 +740,7 @@ dladm_wlan_disconnect(const char *link)
goto done;
}
- if (do_disconnect(fd, gbuf) < 0) {
+ if (do_disconnect(link, fd, gbuf) < 0) {
status = DLADM_STATUS_FAILED;
goto done;
}
@@ -834,10 +866,9 @@ dladm_wlan_get_linkattr(const char *link, dladm_wlan_linkattr_t *attrp)
attrp->la_valid |= DLADM_WLAN_LINKATTR_STATUS;
if (!IS_CONNECTED(gbuf)) {
attrp->la_status = DLADM_WLAN_LINKSTATUS_DISCONNECTED;
- status = DLADM_STATUS_OK;
- goto done;
+ } else {
+ attrp->la_status = DLADM_WLAN_LINKSTATUS_CONNECTED;
}
- attrp->la_status = DLADM_WLAN_LINKSTATUS_CONNECTED;
if (do_get_essid(fd, gbuf) < 0)
goto done;
@@ -856,6 +887,12 @@ dladm_wlan_get_linkattr(const char *link, dladm_wlan_linkattr_t *attrp)
wl_attrp->wa_valid |= DLADM_WLAN_ATTR_BSSID;
+ if (attrp->la_status == DLADM_WLAN_LINKSTATUS_DISCONNECTED) {
+ attrp->la_valid |= DLADM_WLAN_LINKATTR_WLAN;
+ status = DLADM_STATUS_OK;
+ goto done;
+ }
+
if (do_get_encryption(fd, gbuf) < 0)
goto done;
@@ -869,6 +906,9 @@ dladm_wlan_get_linkattr(const char *link, dladm_wlan_linkattr_t *attrp)
case WL_ENC_WEP:
wl_attrp->wa_secmode = DLADM_WLAN_SECMODE_WEP;
break;
+ case WL_ENC_WPA:
+ wl_attrp->wa_secmode = DLADM_WLAN_SECMODE_WPA;
+ break;
default:
wl_attrp->wa_valid &= ~DLADM_WLAN_ATTR_SECMODE;
break;
@@ -1445,8 +1485,12 @@ do_scan(int fd, wldp_t *gbuf)
}
static int
-do_disconnect(int fd, wldp_t *gbuf)
+do_disconnect(const char *link, int fd, wldp_t *gbuf)
{
+ if (do_get_wpamode(fd, gbuf) == 0 && ((wl_wpa_t *)(gbuf->
+ wldp_buf))->wpa_flag > 0)
+ (void) wpa_instance_delete(link);
+
return (do_cmd_ioctl(fd, gbuf, WL_DISASSOCIATE));
}
@@ -1695,6 +1739,8 @@ do_set_encryption(int fd, wldp_t *gbuf, dladm_wlan_secmode_t *secmode)
case DLADM_WLAN_SECMODE_WEP:
encryption = WL_ENC_WEP;
break;
+ case DLADM_WLAN_SECMODE_WPA:
+ return (0);
default:
return (-1);
}
@@ -1703,13 +1749,13 @@ do_set_encryption(int fd, wldp_t *gbuf, dladm_wlan_secmode_t *secmode)
}
static int
-do_set_wepkey(int fd, wldp_t *gbuf, dladm_wlan_wepkey_t *keys,
+do_set_key(int fd, wldp_t *gbuf, dladm_wlan_key_t *keys,
uint_t key_count)
{
int i;
wl_wep_key_t *wkp;
wl_wep_key_tab_t wepkey_tab;
- dladm_wlan_wepkey_t *kp;
+ dladm_wlan_key_t *kp;
if (key_count == 0 || key_count > MAX_NWEPKEYS || keys == NULL)
return (-1);
@@ -1935,3 +1981,587 @@ generate_essid(dladm_wlan_essid_t *essid)
(void) snprintf(essid->we_bytes, DLADM_WLAN_MAX_ESSID_LEN, "%d",
random());
}
+
+static int
+do_get_capability(int fd, wldp_t *gbuf)
+{
+ return (do_get_ioctl(fd, gbuf, WL_CAPABILITY));
+}
+
+static int
+do_get_wpamode(int fd, wldp_t *gbuf)
+{
+ return (do_get_ioctl(fd, gbuf, WL_WPA));
+}
+
+static dladm_status_t
+ioctl_get(const char *link, int id, void *gbuf)
+{
+ int fd;
+
+ if ((fd = open_link(link)) < 0)
+ return (DLADM_STATUS_LINKINVAL);
+ (void) do_get_ioctl(fd, gbuf, id);
+
+ (void) close(fd);
+ return (dladm_wlan_wlresult2status(gbuf));
+}
+
+static dladm_status_t
+ioctl_set(const char *link, int id, void *buf, uint_t buflen)
+{
+ int fd;
+ wldp_t *gbuf;
+ dladm_status_t status;
+
+ if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ if ((fd = open_link(link)) < 0)
+ return (DLADM_STATUS_LINKINVAL);
+ (void) do_set_ioctl(fd, gbuf, id, buf, buflen);
+
+ (void) close(fd);
+ status = dladm_wlan_wlresult2status(gbuf);
+ free(gbuf);
+
+ return (status);
+}
+
+dladm_status_t
+dladm_wlan_wpa_get_sr(const char *link, dladm_wlan_ess_t *sr, uint_t escnt,
+ uint_t *estot)
+{
+ int i, n;
+ wldp_t *gbuf;
+ wl_wpa_ess_t *es;
+ dladm_status_t status;
+
+ if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ status = ioctl_get(link, WL_SCANRESULTS, gbuf);
+
+ if (status == DLADM_STATUS_OK) {
+ es = (wl_wpa_ess_t *)(gbuf->wldp_buf);
+ n = (es->count > escnt) ? escnt : es->count;
+ for (i = 0; i < n; i ++) {
+ (void) memcpy(sr[i].we_bssid.wb_bytes, es->ess[i].bssid,
+ DLADM_WLAN_BSSID_LEN);
+ sr[i].we_ssid_len = es->ess[i].ssid_len;
+ (void) memcpy(sr[i].we_ssid.we_bytes, es->ess[i].ssid,
+ es->ess[i].ssid_len);
+ sr[i].we_wpa_ie_len = es->ess[i].wpa_ie_len;
+ (void) memcpy(sr[i].we_wpa_ie, es->ess[i].wpa_ie,
+ es->ess[i].wpa_ie_len);
+ sr[i].we_freq = es->ess[i].freq;
+ }
+ *estot = n;
+ }
+
+ free(gbuf);
+ return (status);
+}
+
+dladm_status_t
+dladm_wlan_wpa_set_ie(const char *link, uint8_t *wpa_ie,
+ uint_t wpa_ie_len)
+{
+ wl_wpa_ie_t *ie;
+ uint_t len;
+ dladm_status_t status;
+
+ if (wpa_ie_len > DLADM_WLAN_MAX_WPA_IE_LEN)
+ return (DLADM_STATUS_BADARG);
+ len = sizeof (wl_wpa_ie_t) + wpa_ie_len;
+ ie = malloc(len);
+ if (ie == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ (void) memset(ie, 0, len);
+ ie->wpa_ie_len = wpa_ie_len;
+ (void) memcpy(ie->wpa_ie, wpa_ie, wpa_ie_len);
+
+ status = ioctl_set(link, WL_SETOPTIE, ie, len);
+ free(ie);
+
+ return (status);
+}
+
+dladm_status_t
+dladm_wlan_wpa_set_wpa(const char *link, boolean_t flag)
+{
+ wl_wpa_t wpa;
+
+ wpa.wpa_flag = flag;
+ return (ioctl_set(link, WL_WPA, &wpa, sizeof (wl_wpa_t)));
+}
+
+dladm_status_t
+dladm_wlan_wpa_del_key(const char *link, uint_t key_idx,
+ const dladm_wlan_bssid_t *addr)
+{
+ wl_del_key_t wk;
+
+ wk.idk_keyix = key_idx;
+ if (addr != NULL)
+ (void) memcpy((char *)wk.idk_macaddr, addr->wb_bytes,
+ DLADM_WLAN_BSSID_LEN);
+
+ return (ioctl_set(link, WL_DELKEY, &wk, sizeof (wl_del_key_t)));
+}
+
+dladm_status_t
+dladm_wlan_wpa_set_key(const char *link, dladm_wlan_cipher_t cipher,
+ const dladm_wlan_bssid_t *addr, boolean_t set_tx, uint64_t seq,
+ uint_t key_idx, uint8_t *key, uint_t key_len)
+{
+ wl_key_t wk;
+
+ (void) memset(&wk, 0, sizeof (wl_key_t));
+ switch (cipher) {
+ case DLADM_WLAN_CIPHER_WEP:
+ wk.ik_type = IEEE80211_CIPHER_WEP;
+ break;
+ case DLADM_WLAN_CIPHER_TKIP:
+ wk.ik_type = IEEE80211_CIPHER_TKIP;
+ break;
+ case DLADM_WLAN_CIPHER_AES_OCB:
+ wk.ik_type = IEEE80211_CIPHER_AES_OCB;
+ break;
+ case DLADM_WLAN_CIPHER_AES_CCM:
+ wk.ik_type = IEEE80211_CIPHER_AES_CCM;
+ break;
+ case DLADM_WLAN_CIPHER_CKIP:
+ wk.ik_type = IEEE80211_CIPHER_CKIP;
+ break;
+ case DLADM_WLAN_CIPHER_NONE:
+ wk.ik_type = IEEE80211_CIPHER_NONE;
+ break;
+ default:
+ return (DLADM_STATUS_BADARG);
+ }
+ wk.ik_flags = IEEE80211_KEY_RECV;
+ if (set_tx) {
+ wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT;
+ (void) memcpy(wk.ik_macaddr, addr->wb_bytes,
+ DLADM_WLAN_BSSID_LEN);
+ } else
+ (void) memset(wk.ik_macaddr, 0, DLADM_WLAN_BSSID_LEN);
+ wk.ik_keyix = key_idx;
+ wk.ik_keylen = key_len;
+ (void) memcpy(&wk.ik_keyrsc, &seq, 6); /* only use 48-bit of seq */
+ (void) memcpy(wk.ik_keydata, key, key_len);
+
+ return (ioctl_set(link, WL_KEY, &wk, sizeof (wl_key_t)));
+}
+
+dladm_status_t
+dladm_wlan_wpa_set_mlme(const char *link, dladm_wlan_mlme_op_t op,
+ dladm_wlan_reason_t reason, dladm_wlan_bssid_t *bssid)
+{
+ wl_mlme_t mlme;
+
+ (void) memset(&mlme, 0, sizeof (wl_mlme_t));
+ switch (op) {
+ case DLADM_WLAN_MLME_ASSOC:
+ mlme.im_op = IEEE80211_MLME_ASSOC;
+ break;
+ case DLADM_WLAN_MLME_DISASSOC:
+ mlme.im_op = IEEE80211_MLME_DISASSOC;
+ break;
+ default:
+ return (DLADM_STATUS_BADARG);
+ }
+ mlme.im_reason = reason;
+ if (bssid != NULL)
+ (void) memcpy(mlme.im_macaddr, bssid->wb_bytes,
+ DLADM_WLAN_BSSID_LEN);
+
+ return (ioctl_set(link, WL_MLME, &mlme, sizeof (wl_mlme_t)));
+}
+
+/*
+ * routines of create instance
+ */
+static scf_propertygroup_t *
+add_property_group_to_instance(scf_handle_t *handle, scf_instance_t *instance,
+ const char *pg_name, const char *pg_type)
+{
+ scf_propertygroup_t *pg;
+
+ pg = scf_pg_create(handle);
+ if (pg == NULL)
+ return (NULL);
+
+ if (scf_instance_add_pg(instance, pg_name, pg_type, 0, pg) != 0) {
+ scf_pg_destroy(pg);
+ return (NULL);
+ }
+
+ return (pg);
+}
+
+static int
+add_new_property(scf_handle_t *handle, const char *prop_name,
+ scf_type_t type, const char *val, scf_transaction_t *tx)
+{
+ scf_value_t *value = NULL;
+ scf_transaction_entry_t *entry = NULL;
+
+ entry = scf_entry_create(handle);
+ if (entry == NULL)
+ goto out;
+
+ value = scf_value_create(handle);
+ if (value == NULL)
+ goto out;
+
+ if (scf_transaction_property_new(tx, entry, prop_name, type) != 0)
+ goto out;
+
+ if (scf_value_set_from_string(value, type, val) != 0)
+ goto out;
+
+ if (scf_entry_add_value(entry, value) != 0)
+ goto out;
+
+ return (DLADM_WLAN_SVC_SUCCESS);
+
+out:
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (entry != NULL)
+ scf_entry_destroy(entry);
+
+ return (DLADM_WLAN_SVC_FAILURE);
+}
+
+/*
+ * DLADM_WLAN_SVC_APP_FAILURE means allocate buffer failed.
+ */
+static int
+add_pg_method(scf_handle_t *handle, scf_instance_t *instance,
+ const char *pg_name, const char *flags)
+{
+ int rv, size;
+ int status = DLADM_WLAN_SVC_FAILURE;
+ char *command = NULL;
+ scf_transaction_t *tran = NULL;
+ scf_propertygroup_t *pg;
+
+ pg = add_property_group_to_instance(handle, instance,
+ pg_name, SCF_GROUP_METHOD);
+ if (pg == NULL)
+ goto out;
+
+ tran = scf_transaction_create(handle);
+ if (tran == NULL)
+ goto out;
+
+ size = strlen(SVC_METHOD) + strlen(" ") + strlen(flags) + 1;
+ command = malloc(size);
+ if (command == NULL) {
+ status = DLADM_WLAN_SVC_APP_FAILURE;
+ goto out;
+ }
+ (void) snprintf(command, size, "%s %s", SVC_METHOD, flags);
+
+ do {
+ if (scf_transaction_start(tran, pg) != 0)
+ goto out;
+
+ if (add_new_property(handle, SCF_PROPERTY_EXEC,
+ SCF_TYPE_ASTRING, command, tran) !=
+ DLADM_WLAN_SVC_SUCCESS) {
+ goto out;
+ }
+
+ rv = scf_transaction_commit(tran);
+ switch (rv) {
+ case 1:
+ status = DLADM_WLAN_SVC_SUCCESS;
+ goto out;
+ case 0:
+ scf_transaction_destroy_children(tran);
+ if (scf_pg_update(pg) == -1) {
+ goto out;
+ }
+ break;
+ case -1:
+ default:
+ goto out;
+ }
+ } while (rv == 0);
+
+out:
+ if (tran != NULL) {
+ scf_transaction_destroy_children(tran);
+ scf_transaction_destroy(tran);
+ }
+
+ if (pg != NULL)
+ scf_pg_destroy(pg);
+
+ if (command != NULL)
+ free(command);
+
+ return (status);
+}
+
+static int
+do_create_instance(scf_handle_t *handle, scf_service_t *svc,
+ const char *instance_name, const char *command)
+{
+ int status = DLADM_WLAN_SVC_FAILURE;
+ char *buf;
+ ssize_t max_fmri_len;
+ scf_instance_t *instance;
+
+ instance = scf_instance_create(handle);
+ if (instance == NULL)
+ goto out;
+
+ if (scf_service_add_instance(svc, instance_name, instance) != 0) {
+ if (scf_error() == SCF_ERROR_EXISTS)
+ /* Let the caller deal with the duplicate instance */
+ status = DLADM_WLAN_SVC_INSTANCE_EXISTS;
+ goto out;
+ }
+
+ if (add_pg_method(handle, instance, "start",
+ command) != DLADM_WLAN_SVC_SUCCESS) {
+ goto out;
+ }
+
+ /* enabling the instance */
+ max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
+ if ((buf = malloc(max_fmri_len + 1)) == NULL)
+ goto out;
+
+ if (scf_instance_to_fmri(instance, buf, max_fmri_len + 1) > 0) {
+ if ((smf_disable_instance(buf, 0) != 0) ||
+ (smf_enable_instance(buf, SMF_TEMPORARY) != 0)) {
+ goto out;
+ }
+ status = DLADM_WLAN_SVC_SUCCESS;
+ }
+
+out:
+ if (instance != NULL)
+ scf_instance_destroy(instance);
+ return (status);
+}
+
+static int
+create_instance(const char *instance_name, const char *command)
+{
+ int status = DLADM_WLAN_SVC_FAILURE;
+ scf_service_t *svc = NULL;
+ scf_handle_t *handle = NULL;
+
+ handle = scf_handle_create(SCF_VERSION);
+ if (handle == NULL)
+ goto out;
+
+ if (scf_handle_bind(handle) == -1)
+ goto out;
+
+ if ((svc = scf_service_create(handle)) == NULL)
+ goto out;
+
+ if (scf_handle_decode_fmri(handle, SERVICE_NAME, NULL, svc,
+ NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) != 0)
+ goto out;
+
+ status = do_create_instance(handle, svc, instance_name, command);
+
+out:
+ if (svc != NULL)
+ scf_service_destroy(svc);
+
+ if (handle != NULL) {
+ (void) scf_handle_unbind(handle);
+ scf_handle_destroy(handle);
+ }
+
+ return (status);
+}
+
+/*
+ * routines of delete instance
+ */
+#define DEFAULT_TIMEOUT 60000000
+#define INIT_WAIT_USECS 50000
+
+static void
+wait_until_disabled(scf_handle_t *handle, char *fmri)
+{
+ char *state;
+ useconds_t max;
+ useconds_t usecs;
+ uint64_t *cp = NULL;
+ scf_simple_prop_t *sp = NULL;
+
+ max = DEFAULT_TIMEOUT;
+
+ if (((sp = scf_simple_prop_get(handle, fmri, "stop",
+ SCF_PROPERTY_TIMEOUT)) != NULL) &&
+ ((cp = scf_simple_prop_next_count(sp)) != NULL) && (*cp != 0))
+ max = (*cp) * 1000000; /* convert to usecs */
+
+ if (sp != NULL)
+ scf_simple_prop_free(sp);
+
+ for (usecs = INIT_WAIT_USECS; max > 0; max -= usecs) {
+ /* incremental wait */
+ usecs *= 2;
+ usecs = (usecs > max) ? max : usecs;
+
+ (void) usleep(usecs);
+
+ /* Check state after the wait */
+ if ((state = smf_get_state(fmri)) != NULL) {
+ if (strcmp(state, "disabled") == 0)
+ return;
+ }
+ }
+}
+
+static int
+delete_instance(const char *instance_name)
+{
+ int status = DLADM_WLAN_SVC_FAILURE;
+ char *buf;
+ ssize_t max_fmri_len;
+ scf_scope_t *scope = NULL;
+ scf_service_t *svc = NULL;
+ scf_handle_t *handle = NULL;
+ scf_instance_t *instance;
+
+ handle = scf_handle_create(SCF_VERSION);
+ if (handle == NULL)
+ goto out;
+
+ if (scf_handle_bind(handle) == -1)
+ goto out;
+
+ if ((scope = scf_scope_create(handle)) == NULL)
+ goto out;
+
+ if ((svc = scf_service_create(handle)) == NULL)
+ goto out;
+
+ if (scf_handle_get_scope(handle, SCF_SCOPE_LOCAL, scope) == -1)
+ goto out;
+
+ if (scf_scope_get_service(scope, SERVICE_NAME, svc) < 0)
+ goto out;
+
+ instance = scf_instance_create(handle);
+ if (instance == NULL)
+ goto out;
+
+ if (scf_service_get_instance(svc, instance_name, instance) != 0) {
+ scf_error_t scf_errnum = scf_error();
+
+ if (scf_errnum == SCF_ERROR_NOT_FOUND)
+ status = DLADM_WLAN_SVC_SUCCESS;
+
+ scf_instance_destroy(instance);
+ goto out;
+ }
+
+ max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
+ if ((buf = malloc(max_fmri_len + 1)) == NULL) {
+ scf_instance_destroy(instance);
+ goto out;
+ }
+
+ if (scf_instance_to_fmri(instance, buf, max_fmri_len + 1) > 0) {
+ char *state;
+
+ state = smf_get_state(buf);
+ if (state && (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
+ strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)) {
+ if (smf_disable_instance(buf, 0) == 0) {
+ /*
+ * Wait for some time till timeout to avoid
+ * a race with scf_instance_delete() below.
+ */
+ wait_until_disabled(handle, buf);
+ }
+ }
+ }
+
+ if (scf_instance_delete(instance) != 0) {
+ scf_instance_destroy(instance);
+ goto out;
+ }
+
+ scf_instance_destroy(instance);
+
+ status = DLADM_WLAN_SVC_SUCCESS;
+
+out:
+ if (svc != NULL)
+ scf_service_destroy(svc);
+
+ if (scope != NULL)
+ scf_scope_destroy(scope);
+
+ if (handle != NULL) {
+ (void) scf_handle_unbind(handle);
+ scf_handle_destroy(handle);
+ }
+
+ return (status);
+}
+
+/*
+ * DLADM_WLAN_SVC_APP_FAILURE means allocate buffer failed.
+ */
+static int
+wpa_instance_create(const char *instance_name, void *key)
+{
+ int status = DLADM_WLAN_SVC_FAILURE;
+ char *command = NULL;
+ char *wk_name = ((dladm_wlan_key_t *)key)->wk_name;
+ int size;
+
+ size = strlen(instance_name) + strlen(" -i -k ") + strlen(wk_name) + 1;
+ command = malloc(size);
+ if (command == NULL) {
+ status = DLADM_WLAN_SVC_APP_FAILURE;
+ goto out;
+ }
+ (void) snprintf(command, size, "-i %s -k %s", instance_name, wk_name);
+
+ status = create_instance(instance_name, command);
+ if (status == DLADM_WLAN_SVC_INSTANCE_EXISTS) {
+ /*
+ * Delete the existing instance and create a new instance
+ * with the supplied arguments.
+ */
+ if ((status = delete_instance(instance_name)) ==
+ DLADM_WLAN_SVC_SUCCESS) {
+ status = create_instance(instance_name, command);
+ }
+ }
+
+out:
+ if (command != NULL)
+ free(command);
+
+ return (status);
+}
+
+static int
+wpa_instance_delete(const char *instance_name)
+{
+ int status;
+
+ status = delete_instance(instance_name);
+
+ return (status);
+}
diff --git a/usr/src/lib/libdladm/common/libdlwlan.h b/usr/src/lib/libdladm/common/libdlwlan.h
index ccb2e7fc22..b1729ae658 100644
--- a/usr/src/lib/libdladm/common/libdlwlan.h
+++ b/usr/src/lib/libdladm/common/libdlwlan.h
@@ -49,6 +49,8 @@ extern "C" {
#define DLADM_WLAN_MAX_ESSID_LEN 32 /* per 802.11 spec */
#define DLADM_WLAN_BSSID_LEN 6 /* per 802.11 spec */
+#define DLADM_WLAN_WPA_KEY_LEN 32 /* per 802.11i spec */
+#define DLADM_WLAN_MAX_WPA_IE_LEN 40 /* per 802.11i spec */
#define DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT 10
#define DLADM_WLAN_CONNECT_CREATEIBSS 0x00000001
@@ -62,9 +64,38 @@ typedef struct dladm_wlan_bssid {
uint8_t wb_bytes[DLADM_WLAN_BSSID_LEN];
} dladm_wlan_bssid_t;
+typedef struct dladm_wlan_ess {
+ dladm_wlan_bssid_t we_bssid;
+ dladm_wlan_essid_t we_ssid;
+ uint_t we_ssid_len;
+ uint8_t we_wpa_ie[DLADM_WLAN_MAX_WPA_IE_LEN];
+ uint_t we_wpa_ie_len;
+ int we_freq;
+} dladm_wlan_ess_t;
+
+typedef enum {
+ DLADM_WLAN_CIPHER_WEP = 0,
+ DLADM_WLAN_CIPHER_TKIP,
+ DLADM_WLAN_CIPHER_AES_OCB,
+ DLADM_WLAN_CIPHER_AES_CCM,
+ DLADM_WLAN_CIPHER_CKIP,
+ DLADM_WLAN_CIPHER_NONE
+} dladm_wlan_cipher_t;
+
+typedef enum {
+ DLADM_WLAN_MLME_ASSOC = 1, /* associate station */
+ DLADM_WLAN_MLME_DISASSOC = 2 /* disassociate station */
+} dladm_wlan_mlme_op_t;
+
+typedef enum {
+ DLADM_WLAN_REASON_UNSPECIFIED = 1,
+ DLADM_WLAN_REASON_DISASSOC_LEAVING = 5
+} dladm_wlan_reason_t;
+
typedef enum {
DLADM_WLAN_SECMODE_NONE = 1,
- DLADM_WLAN_SECMODE_WEP
+ DLADM_WLAN_SECMODE_WEP,
+ DLADM_WLAN_SECMODE_WPA
} dladm_wlan_secmode_t;
typedef enum {
@@ -101,6 +132,13 @@ typedef enum {
typedef uint32_t dladm_wlan_speed_t;
typedef uint32_t dladm_wlan_channel_t;
+typedef enum {
+ DLADM_WLAN_SVC_SUCCESS,
+ DLADM_WLAN_SVC_FAILURE,
+ DLADM_WLAN_SVC_APP_FAILURE,
+ DLADM_WLAN_SVC_INSTANCE_EXISTS
+} dladm_wlan_svc_status_t;
+
enum {
DLADM_WLAN_ATTR_ESSID = 0x00000001,
DLADM_WLAN_ATTR_BSSID = 0x00000002,
@@ -137,15 +175,16 @@ typedef struct dladm_wlan_linkattr {
#define DLADM_WLAN_WEPKEY64_LEN 5 /* per WEP spec */
#define DLADM_WLAN_WEPKEY128_LEN 13 /* per WEP spec */
-#define DLADM_WLAN_MAX_WEPKEY_LEN 13 /* per WEP spec */
+#define DLADM_WLAN_MAX_KEY_LEN 64 /* per WEP/WPA spec */
#define DLADM_WLAN_MAX_WEPKEYS 4 /* MAX_NWEPKEYS */
-#define DLADM_WLAN_MAX_WEPKEYNAME_LEN 64
-typedef struct dladm_wlan_wepkey {
+#define DLADM_WLAN_MAX_KEYNAME_LEN 64
+typedef struct dladm_wlan_key {
uint_t wk_idx;
uint_t wk_len;
- uint8_t wk_val[DLADM_WLAN_MAX_WEPKEY_LEN];
- char wk_name[DLADM_WLAN_MAX_WEPKEYNAME_LEN];
-} dladm_wlan_wepkey_t;
+ uint8_t wk_val[DLADM_WLAN_MAX_KEY_LEN];
+ char wk_name[DLADM_WLAN_MAX_KEYNAME_LEN];
+ uint_t wk_class;
+} dladm_wlan_key_t;
extern dladm_status_t dladm_wlan_scan(const char *, void *,
boolean_t (*)(void *, dladm_wlan_attr_t *));
@@ -163,6 +202,19 @@ extern dladm_status_t dladm_wlan_walk_prop(const char *, void *,
boolean_t (*)(void *, const char *));
extern dladm_status_t dladm_wlan_get_prop(const char *, dladm_prop_type_t,
const char *, char **, uint_t *);
+/* WPA support routines */
+extern dladm_status_t dladm_wlan_wpa_get_sr(const char *,
+ dladm_wlan_ess_t *, uint_t, uint_t *);
+extern dladm_status_t dladm_wlan_wpa_set_ie(const char *, uint8_t *, uint_t);
+extern dladm_status_t dladm_wlan_wpa_set_wpa(const char *, boolean_t);
+extern dladm_status_t dladm_wlan_wpa_del_key(const char *,
+ uint_t, const dladm_wlan_bssid_t *);
+extern dladm_status_t dladm_wlan_wpa_set_key(const char *,
+ dladm_wlan_cipher_t, const dladm_wlan_bssid_t *,
+ boolean_t, uint64_t, uint_t, uint8_t *, uint_t);
+extern dladm_status_t dladm_wlan_wpa_set_mlme(const char *,
+ dladm_wlan_mlme_op_t,
+ dladm_wlan_reason_t, dladm_wlan_bssid_t *);
extern const char *dladm_wlan_essid2str(dladm_wlan_essid_t *, char *);
extern const char *dladm_wlan_bssid2str(dladm_wlan_bssid_t *, char *);
diff --git a/usr/src/lib/libdladm/common/mapfile-vers b/usr/src/lib/libdladm/common/mapfile-vers
index 57c0999585..8ba960a93d 100644
--- a/usr/src/lib/libdladm/common/mapfile-vers
+++ b/usr/src/lib/libdladm/common/mapfile-vers
@@ -91,6 +91,12 @@ SUNWprivate_1.1 {
dladm_wlan_str2auth;
dladm_wlan_str2bsstype;
dladm_wlan_str2linkstatus;
+ dladm_wlan_wpa_get_sr;
+ dladm_wlan_wpa_set_ie;
+ dladm_wlan_wpa_set_wpa;
+ dladm_wlan_wpa_del_key;
+ dladm_wlan_wpa_set_key;
+ dladm_wlan_wpa_set_mlme;
local:
*;
diff --git a/usr/src/lib/libdladm/common/secobj.c b/usr/src/lib/libdladm/common/secobj.c
index 5deb1590dc..61a1cd72e5 100644
--- a/usr/src/lib/libdladm/common/secobj.c
+++ b/usr/src/lib/libdladm/common/secobj.c
@@ -51,7 +51,8 @@ typedef struct secobj_class_info {
} secobj_class_info_t;
static secobj_class_info_t secobj_class_table[] = {
- {"wep", DLD_SECOBJ_CLASS_WEP}
+ {"wep", DLD_SECOBJ_CLASS_WEP},
+ {"wpa", DLD_SECOBJ_CLASS_WPA}
};
#define NSECOBJCLASS \
@@ -154,10 +155,12 @@ dladm_set_secobj(const char *obj_name, dladm_secobj_class_t class,
return (dladm_errno2status(errno));
if (i_dladm_ioctl(fd, DLDIOCSECOBJSET, &secobj_set,
- sizeof (secobj_set)) < 0)
+ sizeof (secobj_set)) < 0) {
status = dladm_errno2status(errno);
+ }
(void) close(fd);
+
if (status != DLADM_STATUS_OK)
return (status);
diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt
index 46521fbc2d..ec95de975d 100644
--- a/usr/src/lib/libsecdb/auth_attr.txt
+++ b/usr/src/lib/libsecdb/auth_attr.txt
@@ -98,6 +98,7 @@ solaris.smf.manage.rpc.bind:::Manage RPC Program number mapper::help=SmfRPCBind.
solaris.smf.manage.sendmail:::Manage Sendmail Service States::help=SmfSendmailStates.html
solaris.smf.manage.ssh:::Manage Secure Shell Service States::help=SmfSshStates.html
solaris.smf.manage.system-log:::Manage Syslog Service States::help=SmfSyslogStates.html
+solaris.smf.manage.wpa:::Manage WPA Service States::help=SmfWpaStates.html
solaris.smf.value.:::Change Values of SMF Service Properties::help=SmfValueHeader.html
solaris.smf.value.nwam:::Change Values of SMF Network Auto-Magic Properties::help=SmfValueNWAM.html
solaris.smf.value.routing:::Change Values of SMF Routing Properties::help=SmfValueRouting.html
diff --git a/usr/src/lib/libsecdb/help/auths/Makefile b/usr/src/lib/libsecdb/help/auths/Makefile
index a6b50e871d..45e7c359c3 100644
--- a/usr/src/lib/libsecdb/help/auths/Makefile
+++ b/usr/src/lib/libsecdb/help/auths/Makefile
@@ -84,6 +84,7 @@ HTMLENTS = \
SmfValueHeader.html \
SmfValueNWAM.html \
SmfValueRouting.html \
+ SmfWpaStates.html \
NetworkHeader.html \
WifiConfig.html \
WifiWep.html \
diff --git a/usr/src/lib/libsecdb/help/auths/SmfWpaStates.html b/usr/src/lib/libsecdb/help/auths/SmfWpaStates.html
new file mode 100644
index 0000000000..00792d10cf
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/SmfWpaStates.html
@@ -0,0 +1,44 @@
+<HTML>
+
+<!--
+ Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+-->
+<!-- SCCS keyword
+#pragma ident "%Z%%M% %I% %E% SMI"
+-->
+
+<HEAD>
+<!--
+META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"
+-->
+</HEAD>
+<BODY>
+When Manage WPA Service States is in the Authorizations
+Include column, it grants the authorization to enable, disable or
+refresh the wpa daemon.
+<p>
+If Manage WPA Service States is grayed, then you are not
+entitled to Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt
index fc9e312077..90fe709baf 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -49,7 +49,7 @@ Mail Management:::Manage sendmail & queues:auths=solaris.smf.manage.sendmail;hel
Maintenance and Repair:::Maintain and repair a system:auths=solaris.smf.manage.system-log;help=RtMaintAndRepair.html
Media Backup:::Backup files and file systems:help=RtMediaBkup.html
Media Restore:::Restore files and file systems from backups:help=RtMediaRestore.html
-Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam;profiles=Network Wifi Management;help=RtNetMngmnt.html
+Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.wpa;profiles=Network Wifi Management;help=RtNetMngmnt.html
Network Security:::Manage network and host security:auths=solaris.smf.manage.ssh;profiles=Network Wifi Security,Network Link Security;help=RtNetSecure.html
Network Wifi Management:::Manage wifi network configuration:auths=solaris.network.wifi.config;help=RtNetWifiMngmnt.html
Network Wifi Security:::Manage wifi network security:auths=solaris.network.wifi.wep;help=RtNetWifiSecure.html
diff --git a/usr/src/lib/libsecdb/user_attr.txt b/usr/src/lib/libsecdb/user_attr.txt
index de173ce94f..70a20356f3 100644
--- a/usr/src/lib/libsecdb/user_attr.txt
+++ b/usr/src/lib/libsecdb/user_attr.txt
@@ -1,13 +1,12 @@
#
-# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -31,3 +30,4 @@
root::::auths=solaris.*,solaris.grant;profiles=All;lock_after_retries=no
lp::::profiles=Printer Management
adm::::profiles=Log Management
+dladm::::auths=solaris.smf.manage.wpa,solaris.smf.modify
diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile
index 35d1ffee7c..64e89b9e5d 100644
--- a/usr/src/pkgdefs/Makefile
+++ b/usr/src/pkgdefs/Makefile
@@ -381,6 +381,8 @@ COMMON_SUBDIRS= \
SUNWxcu4 \
SUNWwlanr \
SUNWwlanu \
+ SUNWwpar \
+ SUNWwpau \
SUNWxcu6 \
SUNWxwdv \
SUNWpmr \
diff --git a/usr/src/pkgdefs/SUNW0on/prototype_com b/usr/src/pkgdefs/SUNW0on/prototype_com
index 37ae40865d..f8cd692f98 100644
--- a/usr/src/pkgdefs/SUNW0on/prototype_com
+++ b/usr/src/pkgdefs/SUNW0on/prototype_com
@@ -244,6 +244,7 @@ f none usr/lib/help/auths/locale/SmfSyslogStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfValueHeader.html 444 root bin
f none usr/lib/help/auths/locale/SmfValueNWAM.html 444 root bin
f none usr/lib/help/auths/locale/SmfValueRouting.html 444 root bin
+f none usr/lib/help/auths/locale/SmfWpaStates.html 444 root bin
f none usr/lib/help/auths/locale/NetworkHeader.html 444 root bin
f none usr/lib/help/auths/locale/WifiConfig.html 444 root bin
f none usr/lib/help/auths/locale/WifiWep.html 444 root bin
diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com
index 92a7bac2cc..33e316646b 100644
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com
@@ -490,6 +490,7 @@ f none usr/lib/help/auths/locale/C/SmfSyslogStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfValueHeader.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfValueNWAM.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfValueRouting.html 444 root bin
+f none usr/lib/help/auths/locale/C/SmfWpaStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SysDate.html 444 root bin
f none usr/lib/help/auths/locale/C/SysHeader.html 444 root bin
f none usr/lib/help/auths/locale/C/SysShutdown.html 444 root bin
diff --git a/usr/src/pkgdefs/SUNWwpar/Makefile b/usr/src/pkgdefs/SUNWwpar/Makefile
new file mode 100644
index 0000000000..0d3d48d234
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpar/Makefile
@@ -0,0 +1,38 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+DATAFILES += i.manifest r.manifest
+
+.KEEP_STATE:
+
+all: $(FILES)
+install: all pkg
+
+include ../Makefile.targ
+include ../Makefile.prtarg
diff --git a/usr/src/pkgdefs/SUNWwpar/depend b/usr/src/pkgdefs/SUNWwpar/depend
new file mode 100644
index 0000000000..771bd67e51
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpar/depend
@@ -0,0 +1,52 @@
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This package information file defines software dependencies associated
+# with the pkg. You can define three types of pkg dependencies with this file:
+# P indicates a prerequisite for installation
+# I indicates an incompatible package
+# R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# (<arch>)<version>
+# (<arch>)<version>
+# ...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar Core Architecture, (Root)
+P SUNWcakr Core Solaris Kernel Architecture (Root)
+P SUNWkvm Core Architecture, (Kvm)
+P SUNWcsr Core Solaris, (Root)
+P SUNWckr Core Solaris Kernel (Root)
+P SUNWcnetr Core Solaris Network Infrastructure (Root)
+P SUNWcsu Core Solaris, (Usr)
+P SUNWcsl Core Solaris Libraries
+P SUNWcsd Core Solaris Devices
+R SUNWwpau Wireless WPA Supplicant, (Usr)
diff --git a/usr/src/pkgdefs/SUNWwpar/pkginfo.tmpl b/usr/src/pkgdefs/SUNWwpar/pkginfo.tmpl
new file mode 100644
index 0000000000..aa723dffe0
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpar/pkginfo.tmpl
@@ -0,0 +1,59 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWwpar"
+NAME="Wireless WPA Supplicant, (Root)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="root"
+SUNW_PKGVERS="1.0"
+MAXINST="1000"
+CATEGORY="system"
+DESC="The service implements the IEEE802.11i (WPA/WPA2) specification."
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none manifest"
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+SUNW_PKG_THISZONE="false"
+#VSTOCK="<reserved by Release Engineering for package part #>"
+#ISTATES="<developer defined>"
+#RSTATES='<developer defined>'
+#ULIMIT="<developer defined>"
+#ORDER="<developer defined>"
+#PSTAMP="<developer defined>"
+#INTONLY="<developer defined>"
diff --git a/usr/src/pkgdefs/SUNWwpar/prototype_com b/usr/src/pkgdefs/SUNWwpar/prototype_com
new file mode 100644
index 0000000000..0596729e84
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpar/prototype_com
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+i i.manifest
+i r.manifest
+#
+# source locations relative to the prototype file
+#
+# SUNWwpar
+#
+d none var 0755 root sys
+d none var/svc 0755 root sys
+d none var/svc/manifest 0755 root sys
+d none var/svc/manifest/network 0755 root sys
+f manifest var/svc/manifest/network/wpa.xml 0444 root sys
diff --git a/usr/src/pkgdefs/SUNWwpar/prototype_i386 b/usr/src/pkgdefs/SUNWwpar/prototype_i386
new file mode 100644
index 0000000000..4ca14e8d57
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpar/prototype_i386
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are I386 specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWwpar
+#
diff --git a/usr/src/pkgdefs/SUNWwpar/prototype_sparc b/usr/src/pkgdefs/SUNWwpar/prototype_sparc
new file mode 100644
index 0000000000..9cafe9aebb
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpar/prototype_sparc
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWwpar
+#
+
diff --git a/usr/src/pkgdefs/SUNWwpau/Makefile b/usr/src/pkgdefs/SUNWwpau/Makefile
new file mode 100644
index 0000000000..502f85dcaf
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpau/Makefile
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(FILES)
+install: all pkg
+
+include ../Makefile.targ
diff --git a/usr/src/pkgdefs/SUNWwpau/depend b/usr/src/pkgdefs/SUNWwpau/depend
new file mode 100644
index 0000000000..81e5bc28c7
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpau/depend
@@ -0,0 +1,52 @@
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This package information file defines software dependencies associated
+# with the pkg. You can define three types of pkg dependencies with this file:
+# P indicates a prerequisite for installation
+# I indicates an incompatible package
+# R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# (<arch>)<version>
+# (<arch>)<version>
+# ...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar Core Architecture, (Root)
+P SUNWcakr Core Solaris Kernel Architecture (Root)
+P SUNWkvm Core Architecture, (Kvm)
+P SUNWcsr Core Solaris, (Root)
+P SUNWckr Core Solaris Kernel (Root)
+P SUNWcnetr Core Solaris Network Infrastructure (Root)
+P SUNWcsu Core Solaris, (Usr)
+P SUNWcsl Core Solaris Libraries
+P SUNWcsd Core Solaris Devices
+P SUNWwpar Wireless WPA Supplicant, (Root)
diff --git a/usr/src/pkgdefs/SUNWwpau/pkginfo.tmpl b/usr/src/pkgdefs/SUNWwpau/pkginfo.tmpl
new file mode 100644
index 0000000000..fa7cf7bb95
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpau/pkginfo.tmpl
@@ -0,0 +1,59 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWwpau"
+NAME="Wireless WPA Supplicant, (Usr)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="usr"
+SUNW_PKGVERS="1.0"
+MAXINST="1000"
+CATEGORY="system"
+DESC="The service implements the IEEE802.11i (WPA/WPA2) specification."
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none"
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+SUNW_PKG_THISZONE="false"
+#VSTOCK="<reserved by Release Engineering for package part #>"
+#ISTATES="<developer defined>"
+#RSTATES='<developer defined>'
+#ULIMIT="<developer defined>"
+#ORDER="<developer defined>"
+#PSTAMP="<developer defined>"
+#INTONLY="<developer defined>"
diff --git a/usr/src/pkgdefs/SUNWwpau/prototype_com b/usr/src/pkgdefs/SUNWwpau/prototype_com
new file mode 100644
index 0000000000..56ca4929b8
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpau/prototype_com
@@ -0,0 +1,49 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+#
+# source locations relative to the prototype file
+#
+# SUNWwpau
+#
+d none usr 0755 root sys
+d none usr/lib 0755 root bin
+d none usr/lib/inet 755 root bin
+f none usr/lib/inet/wpad 555 root bin
diff --git a/usr/src/pkgdefs/SUNWwpau/prototype_i386 b/usr/src/pkgdefs/SUNWwpau/prototype_i386
new file mode 100644
index 0000000000..a361daaa9a
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpau/prototype_i386
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are I386 specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWwpau
+#
diff --git a/usr/src/pkgdefs/SUNWwpau/prototype_sparc b/usr/src/pkgdefs/SUNWwpau/prototype_sparc
new file mode 100644
index 0000000000..9e5a94cfd0
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWwpau/prototype_sparc
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
+# SUNWwpau
+#
diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386
index c39da4ce35..1157e2bd5a 100644
--- a/usr/src/pkgdefs/etc/exception_list_i386
+++ b/usr/src/pkgdefs/etc/exception_list_i386
@@ -613,6 +613,7 @@ usr/include/sys/1394/id1394.h i386
# PPPoE files not delivered to customers.
usr/include/net/pppoe.h i386
usr/include/net/sppptun.h i386
+usr/include/net/wpa.h i386
#
# The ses driver is not currently delivered on Intel
#
diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc
index 85278c1f68..2d09927495 100644
--- a/usr/src/pkgdefs/etc/exception_list_sparc
+++ b/usr/src/pkgdefs/etc/exception_list_sparc
@@ -614,6 +614,7 @@ usr/platform/sun4u/include/sys/fcode.h sparc
#
usr/include/net/pppoe.h sparc
usr/include/net/sppptun.h sparc
+usr/include/net/wpa.h sparc
#
# User<->kernel interface used by cfgadm/USB only
#
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index af1f0d5137..8fd0f7c9b6 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -562,7 +562,8 @@ AGGR_OBJS += aggr_dev.o aggr_ctl.o aggr_grp.o aggr_port.o \
NET80211_OBJS += net80211.o net80211_proto.o net80211_input.o \
net80211_output.o net80211_node.o net80211_crypto.o \
- net80211_crypto_none.o net80211_crypto_wep.o net80211_ioctl.o
+ net80211_crypto_none.o net80211_crypto_wep.o net80211_ioctl.o \
+ net80211_crypto_tkip.o net80211_crypto_ccmp.o
IB_OBJS += ibnex.o ibnex_ioctl.o
diff --git a/usr/src/uts/common/inet/wifi_ioctl.h b/usr/src/uts/common/inet/wifi_ioctl.h
index 5363fadaa8..14e94aea86 100644
--- a/usr/src/uts/common/inet/wifi_ioctl.h
+++ b/usr/src/uts/common/inet/wifi_ioctl.h
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -164,6 +163,7 @@ extern "C" {
#define WL_NOENCRYPTION 0x0
#define WL_ENC_WEP 0x1
+#define WL_ENC_WPA 0x2
#define WL_OPENSYSTEM 0x1
#define WL_SHAREDKEY 0x2
diff --git a/usr/src/uts/common/io/ath/ath_aux.c b/usr/src/uts/common/io/ath/ath_aux.c
index 7ef30ab1b6..26d9acbbd4 100644
--- a/usr/src/uts/common/io/ath/ath_aux.c
+++ b/usr/src/uts/common/io/ath/ath_aux.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -573,6 +573,84 @@ ath_beacon_config(ath_t *asc)
}
/*
+ * Allocate tx/rx key slots for TKIP. We allocate two slots for
+ * each key, one for decrypt/encrypt and the other for the MIC.
+ */
+static int
+key_alloc_2pair(ath_t *asc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
+{
+ uint16_t i, keyix;
+
+ ASSERT(asc->asc_splitmic);
+ for (i = 0; i < ATH_N(asc->asc_keymap)/4; i++) {
+ uint8_t b = asc->asc_keymap[i];
+ if (b != 0xff) {
+ /*
+ * One or more slots in this byte are free.
+ */
+ keyix = i*NBBY;
+ while (b & 1) {
+ again:
+ keyix++;
+ b >>= 1;
+ }
+ /* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
+ if (isset(asc->asc_keymap, keyix+32) ||
+ isset(asc->asc_keymap, keyix+64) ||
+ isset(asc->asc_keymap, keyix+32+64)) {
+ /* full pair unavailable */
+ if (keyix == (i+1)*NBBY) {
+ /* no slots were appropriate, advance */
+ continue;
+ }
+ goto again;
+ }
+ setbit(asc->asc_keymap, keyix);
+ setbit(asc->asc_keymap, keyix+64);
+ setbit(asc->asc_keymap, keyix+32);
+ setbit(asc->asc_keymap, keyix+32+64);
+ ATH_DEBUG((ATH_DBG_AUX,
+ "key_alloc_2pair: key pair %u,%u %u,%u\n",
+ keyix, keyix+64,
+ keyix+32, keyix+32+64));
+ *txkeyix = *rxkeyix = keyix;
+ return (1);
+ }
+ }
+ ATH_DEBUG((ATH_DBG_AUX, "key_alloc_2pair:"
+ " out of pair space\n"));
+ return (0);
+}
+/*
+ * Allocate a single key cache slot.
+ */
+static int
+key_alloc_single(ath_t *asc, ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
+{
+ uint16_t i, keyix;
+
+ /* try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
+ for (i = 0; i < ATH_N(asc->asc_keymap); i++) {
+ uint8_t b = asc->asc_keymap[i];
+
+ if (b != 0xff) {
+ /*
+ * One or more slots are free.
+ */
+ keyix = i*NBBY;
+ while (b & 1)
+ keyix++, b >>= 1;
+ setbit(asc->asc_keymap, keyix);
+ ATH_DEBUG((ATH_DBG_AUX, "key_alloc_single:"
+ " key %u\n", keyix));
+ *txkeyix = *rxkeyix = keyix;
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
* Allocate one or more key cache slots for a unicast key. The
* key itself is needed only to identify the cipher. For hardware
* TKIP with split cipher+MIC keys we allocate two key cache slot
@@ -586,19 +664,142 @@ int
ath_key_alloc(ieee80211com_t *ic, const struct ieee80211_key *k,
ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
{
- *keyix = *rxkeyix = 0;
- return (1);
+ ath_t *asc = (ath_t *)ic;
+
+ /*
+ * We allocate two pair for TKIP when using the h/w to do
+ * the MIC. For everything else, including software crypto,
+ * we allocate a single entry. Note that s/w crypto requires
+ * a pass-through slot on the 5211 and 5212. The 5210 does
+ * not support pass-through cache entries and we map all
+ * those requests to slot 0.
+ */
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+ return (key_alloc_single(asc, keyix, rxkeyix));
+ } else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
+ (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && asc->asc_splitmic) {
+ return (key_alloc_2pair(asc, keyix, rxkeyix));
+ } else {
+ return (key_alloc_single(asc, keyix, rxkeyix));
+ }
}
+/*
+ * Delete an entry in the key cache allocated by ath_key_alloc.
+ */
int
ath_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k)
{
- struct ath_hal *ah = ((ath_t *)ic)->asc_ah;
+ ath_t *asc = (ath_t *)ic;
+ struct ath_hal *ah = asc->asc_ah;
+ const struct ieee80211_cipher *cip = k->wk_cipher;
+ ieee80211_keyix keyix = k->wk_keyix;
- ATH_HAL_KEYRESET(ah, k->wk_keyix);
+ ATH_DEBUG((ATH_DBG_AUX, "ath_key_delete:"
+ " delete key %u ic_cipher=0x%x\n", keyix, cip->ic_cipher));
+
+ ATH_HAL_KEYRESET(ah, keyix);
+ /*
+ * Handle split tx/rx keying required for TKIP with h/w MIC.
+ */
+ if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
+ (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && asc->asc_splitmic)
+ ATH_HAL_KEYRESET(ah, keyix+32); /* RX key */
+
+ if (keyix >= IEEE80211_WEP_NKID) {
+ /*
+ * Don't touch keymap entries for global keys so
+ * they are never considered for dynamic allocation.
+ */
+ clrbit(asc->asc_keymap, keyix);
+ if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
+ (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
+ asc->asc_splitmic) {
+ clrbit(asc->asc_keymap, keyix+64); /* TX key MIC */
+ clrbit(asc->asc_keymap, keyix+32); /* RX key */
+ clrbit(asc->asc_keymap, keyix+32+64); /* RX key MIC */
+ }
+ }
return (1);
}
+static void
+ath_keyprint(const char *tag, uint_t ix,
+ const HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ static const char *ciphers[] = {
+ "WEP",
+ "AES-OCB",
+ "AES-CCM",
+ "CKIP",
+ "TKIP",
+ "CLR",
+ };
+ int i, n;
+ char buf[MAX_IEEE80211STR], buft[32];
+
+ (void) snprintf(buf, sizeof (buf), "%s: [%02u] %s ",
+ tag, ix, ciphers[hk->kv_type]);
+ for (i = 0, n = hk->kv_len; i < n; i++) {
+ (void) snprintf(buft, sizeof (buft), "%02x", hk->kv_val[i]);
+ (void) strlcat(buf, buft, sizeof (buf));
+ }
+ (void) snprintf(buft, sizeof (buft), " mac %s",
+ ieee80211_macaddr_sprintf(mac));
+ (void) strlcat(buf, buft, sizeof (buf));
+ if (hk->kv_type == HAL_CIPHER_TKIP) {
+ (void) snprintf(buft, sizeof (buft), " mic ");
+ (void) strlcat(buf, buft, sizeof (buf));
+ for (i = 0; i < sizeof (hk->kv_mic); i++) {
+ (void) snprintf(buft, sizeof (buft), "%02x",
+ hk->kv_mic[i]);
+ (void) strlcat(buf, buft, sizeof (buf));
+ }
+ }
+ ATH_DEBUG((ATH_DBG_AUX, "%s", buf));
+}
+
+/*
+ * Set a TKIP key into the hardware. This handles the
+ * potential distribution of key state to multiple key
+ * cache slots for TKIP.
+ */
+static int
+ath_keyset_tkip(ath_t *asc, const struct ieee80211_key *k,
+ HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+#define IEEE80211_KEY_XR (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)
+ static const uint8_t zerobssid[IEEE80211_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
+ struct ath_hal *ah = asc->asc_ah;
+
+ ASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP);
+ if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
+ /*
+ * TX key goes at first index, RX key at +32.
+ * The hal handles the MIC keys at index+64.
+ */
+ (void) memcpy(hk->kv_mic, k->wk_txmic, sizeof (hk->kv_mic));
+ ath_keyprint("ath_keyset_tkip:", k->wk_keyix, hk, zerobssid);
+ if (!ATH_HAL_KEYSET(ah, k->wk_keyix, hk, zerobssid))
+ return (0);
+
+ (void) memcpy(hk->kv_mic, k->wk_rxmic, sizeof (hk->kv_mic));
+ ath_keyprint("ath_keyset_tkip:", k->wk_keyix+32, hk, mac);
+ return (ATH_HAL_KEYSET(ah, k->wk_keyix+32, hk, mac));
+ } else if (k->wk_flags & IEEE80211_KEY_XR) {
+ /*
+ * TX/RX key goes at first index.
+ * The hal handles the MIC keys are index+64.
+ */
+ (void) memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ?
+ k->wk_txmic : k->wk_rxmic, sizeof (hk->kv_mic));
+ ath_keyprint("ath_keyset_tkip:", k->wk_keyix, hk, zerobssid);
+ return (ATH_HAL_KEYSET(ah, k->wk_keyix, hk, zerobssid));
+ }
+ return (0);
+#undef IEEE80211_KEY_XR
+}
+
/*
* Set the key cache contents for the specified key. Key cache
* slot(s) must already have been allocated by ath_key_alloc.
@@ -612,7 +813,6 @@ ath_key_set(ieee80211com_t *ic, const struct ieee80211_key *k,
HAL_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */
HAL_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */
HAL_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */
- (uint8_t)-1, /* 4 is not allocated */
HAL_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */
HAL_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */
};
@@ -636,7 +836,14 @@ ath_key_set(ieee80211com_t *ic, const struct ieee80211_key *k,
hk.kv_type = HAL_CIPHER_CLR;
}
- return (ATH_HAL_KEYSET(ah, k->wk_keyix, &hk, mac));
+ if (hk.kv_type == HAL_CIPHER_TKIP &&
+ (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
+ asc->asc_splitmic) {
+ return (ath_keyset_tkip(asc, k, &hk, mac));
+ } else {
+ ath_keyprint("ath_keyset:", k->wk_keyix, &hk, mac);
+ return (ATH_HAL_KEYSET(ah, k->wk_keyix, &hk, mac));
+ }
}
/*
diff --git a/usr/src/uts/common/io/ath/ath_impl.h b/usr/src/uts/common/io/ath/ath_impl.h
index b99cfbd238..f311d66493 100644
--- a/usr/src/uts/common/io/ath/ath_impl.h
+++ b/usr/src/uts/common/io/ath/ath_impl.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -61,6 +61,12 @@ extern "C" {
#include <sys/net80211.h>
#include "ath_hal.h"
+/* Bit map related macros. */
+#define setbit(a, i) ((a)[(i)/NBBY] |= (1 << ((i)%NBBY)))
+#define clrbit(a, i) ((a)[(i)/NBBY] &= ~(1 << ((i)%NBBY)))
+#define isset(a, i) ((a)[(i)/NBBY] & (1 << ((i)%NBBY)))
+#define isclr(a, i) (!((a)[(i)/NBBY] & (1 << ((i)%NBBY))))
+
/*
* Bit flags in the ath_dbg_flags
*/
@@ -298,6 +304,9 @@ typedef struct ath {
boolean_t asc_resched_needed;
kmutex_t asc_resched_lock;
+ uint32_t asc_keymax; /* size of key cache */
+ uint8_t asc_keymap[16]; /* bit map of key cache use */
+
timeout_id_t asc_scan_timer;
int (*asc_newstate)(ieee80211com_t *,
enum ieee80211_state, int);
diff --git a/usr/src/uts/common/io/ath/ath_main.c b/usr/src/uts/common/io/ath/ath_main.c
index 4a6f987d7d..0e40660aa7 100644
--- a/usr/src/uts/common/io/ath/ath_main.c
+++ b/usr/src/uts/common/io/ath/ath_main.c
@@ -668,7 +668,7 @@ ath_tx_start(ath_t *asc, struct ieee80211_node *in, struct ath_buf *bf,
* packet length.
*/
hdrlen += cip->ic_header;
- pktlen += cip->ic_header + cip->ic_trailer;
+ pktlen += cip->ic_trailer;
if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
pktlen += cip->ic_miclen;
keyix = k->wk_keyix;
@@ -992,6 +992,7 @@ ath_m_tx(void *arg, mblk_t *mp)
ath_t *asc = arg;
ieee80211com_t *ic = (ieee80211com_t *)asc;
mblk_t *next;
+ int error = 0;
/*
* No data frames go out unless we're associated; this
@@ -1009,10 +1010,15 @@ ath_m_tx(void *arg, mblk_t *mp)
while (mp != NULL) {
next = mp->b_next;
mp->b_next = NULL;
-
- if (ath_xmit(ic, mp, IEEE80211_FC0_TYPE_DATA) != 0) {
+ error = ath_xmit(ic, mp, IEEE80211_FC0_TYPE_DATA);
+ if (error != 0) {
mp->b_next = next;
- break;
+ if (error == ENOMEM) {
+ break;
+ } else {
+ freemsgchain(mp); /* CR6501759 issues */
+ return (NULL);
+ }
}
mp = next;
}
@@ -1173,6 +1179,8 @@ ath_node_free(struct ieee80211_node *in)
}
}
ic->ic_node_cleanup(in);
+ if (in->in_wpa_ie != NULL)
+ ieee80211_free(in->in_wpa_ie);
kmem_free(in, sizeof (struct ath_node));
}
@@ -1898,6 +1906,30 @@ ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
"multi rate retry support=%x\n",
asc->asc_mrretry));
+ /*
+ * Get the hardware key cache size.
+ */
+ asc->asc_keymax = ATH_HAL_KEYCACHESIZE(ah);
+ if (asc->asc_keymax > sizeof (asc->asc_keymap) * NBBY) {
+ ATH_DEBUG((ATH_DBG_ATTACH, "ath_attach:"
+ " Warning, using only %u entries in %u key cache\n",
+ sizeof (asc->asc_keymap) * NBBY, asc->asc_keymax));
+ asc->asc_keymax = sizeof (asc->asc_keymap) * NBBY;
+ }
+ /*
+ * Reset the key cache since some parts do not
+ * reset the contents on initial power up.
+ */
+ for (i = 0; i < asc->asc_keymax; i++)
+ ATH_HAL_KEYRESET(ah, i);
+
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ setbit(asc->asc_keymap, i);
+ setbit(asc->asc_keymap, i+32);
+ setbit(asc->asc_keymap, i+64);
+ setbit(asc->asc_keymap, i+32+64);
+ }
+
ATH_HAL_GETREGDOMAIN(ah, (uint32_t *)&ath_regdomain);
ATH_HAL_GETCOUNTRYCODE(ah, &ath_countrycode);
/*
@@ -1951,20 +1983,29 @@ ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
ic->ic_caps |= IEEE80211_C_WEP;
if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_OCB))
ic->ic_caps |= IEEE80211_C_AES;
- if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_CCM))
+ if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_CCM)) {
+ ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W CCMP\n"));
ic->ic_caps |= IEEE80211_C_AES_CCM;
- if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CKIP)) {
+ }
+ if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CKIP))
ic->ic_caps |= IEEE80211_C_CKIP;
+ if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_TKIP)) {
+ ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W TKIP\n"));
+ ic->ic_caps |= IEEE80211_C_TKIP;
/*
* Check if h/w does the MIC and/or whether the
* separate key cache entries are required to
* handle both tx+rx MIC keys.
*/
- if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_MIC))
+ if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_MIC)) {
+ ATH_DEBUG((ATH_DBG_ATTACH, "Support H/W TKIP MIC\n"));
ic->ic_caps |= IEEE80211_C_TKIPMIC;
+ }
if (ATH_HAL_TKIPSPLIT(ah))
asc->asc_splitmic = 1;
}
+ ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */
+
asc->asc_hasclrkey = ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CLR);
ic->ic_phytype = IEEE80211_T_OFDM;
ic->ic_opmode = IEEE80211_M_STA;
@@ -1974,6 +2015,11 @@ ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
ic->ic_xmit = ath_xmit;
ieee80211_attach(ic);
+ /* different instance has different WPA door */
+ (void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR,
+ ddi_driver_name(devinfo),
+ ddi_get_instance(devinfo));
+
/* Override 80211 default routines */
ic->ic_reset = ath_reset;
asc->asc_newstate = ic->ic_newstate;
@@ -2147,7 +2193,7 @@ DDI_DEFINE_STREAM_OPS(ath_dev_ops, nulldev, nulldev, ath_attach, ath_detach,
static struct modldrv ath_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
- "ath driver 1.2/HAL 0.9.17.2", /* short description */
+ "ath driver 1.3/HAL 0.9.17.2", /* short description */
&ath_dev_ops /* driver specific ops */
};
diff --git a/usr/src/uts/common/io/dld/dld_drv.c b/usr/src/uts/common/io/dld/dld_drv.c
index f556944956..ad3440e2d8 100644
--- a/usr/src/uts/common/io/dld/dld_drv.c
+++ b/usr/src/uts/common/io/dld/dld_drv.c
@@ -726,7 +726,8 @@ drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp)
ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr;
sobjp = &ssp->ss_obj;
- if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP)
+ if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
+ sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
goto failed;
if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
@@ -767,7 +768,6 @@ drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp)
failed:
ASSERT(err != 0);
miocnak(q, mp, 0, err);
-
}
typedef struct dld_secobj_state {
diff --git a/usr/src/uts/common/io/ipw/ipw2100.c b/usr/src/uts/common/io/ipw/ipw2100.c
index 029ddfc62d..1196747abb 100644
--- a/usr/src/uts/common/io/ipw/ipw2100.c
+++ b/usr/src/uts/common/io/ipw/ipw2100.c
@@ -1530,7 +1530,6 @@ ipw2100_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg)
wd.wd_secalloc = WIFI_SEC_NONE;
wd.wd_opmode = ic->ic_opmode;
IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
- ieee80211_free_node(in);
(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
break;
diff --git a/usr/src/uts/common/io/mac/plugins/mac_wifi.c b/usr/src/uts/common/io/mac/plugins/mac_wifi.c
index 81da3e878c..85b8735c09 100644
--- a/usr/src/uts/common/io/mac/plugins/mac_wifi.c
+++ b/usr/src/uts/common/io/mac/plugins/mac_wifi.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -230,10 +230,11 @@ mac_wifi_header(const void *saddr, const void *daddr, uint32_t sap,
break;
}
- /*
- * Fill in the fixed parts of the WEP-portion of the frame.
- */
- if (wdp->wd_secalloc == WIFI_SEC_WEP) {
+ switch (wdp->wd_secalloc) {
+ case WIFI_SEC_WEP:
+ /*
+ * Fill in the fixed parts of the WEP-portion of the frame.
+ */
wh->i_fc[1] |= IEEE80211_FC1_WEP;
/*
* The actual contents of the WEP-portion of the packet
@@ -241,6 +242,16 @@ mac_wifi_header(const void *saddr, const void *daddr, uint32_t sap,
* just need to account for the size.
*/
mp->b_wptr += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+ break;
+
+ case WIFI_SEC_WPA:
+ wh->i_fc[1] |= IEEE80211_FC1_WEP;
+ mp->b_wptr += IEEE80211_WEP_IVLEN +
+ IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN;
+ break;
+
+ default:
+ break;
}
/*
@@ -265,6 +276,7 @@ mac_wifi_header_info(mblk_t *mp, void *pdata, mac_header_info_t *mhp)
struct ieee80211_frame *wh;
struct ieee80211_llc *llc;
uchar_t *llcp;
+ wifi_data_t *wdp = pdata;
if (MBLKL(mp) < sizeof (struct ieee80211_frame))
return (EINVAL);
@@ -279,8 +291,11 @@ mac_wifi_header_info(mblk_t *mp, void *pdata, mac_header_info_t *mhp)
* packets, it will still have a WEP portion. Skip past it to get to
* the LLC header.
*/
- if (wh->i_fc[1] & IEEE80211_FC1_WEP)
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
llcp += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+ if (wdp->wd_secalloc == WIFI_SEC_WPA)
+ llcp += IEEE80211_WEP_EXTIVLEN;
+ }
if (mp->b_wptr - llcp < sizeof (struct ieee80211_llc))
return (EINVAL);
diff --git a/usr/src/uts/common/io/net80211/net80211.c b/usr/src/uts/common/io/net80211/net80211.c
index 5a6eef0f06..bdeacc4561 100644
--- a/usr/src/uts/common/io/net80211/net80211.c
+++ b/usr/src/uts/common/io/net80211/net80211.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -45,6 +45,8 @@
#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/modctl.h>
+#include <sys/stropts.h>
+#include <sys/door.h>
#include "net80211_impl.h"
uint32_t ieee80211_debug = 0x0; /* debug msg flags */
@@ -88,6 +90,111 @@ ieee80211_dbg(uint32_t flag, const int8_t *fmt, ...)
}
/*
+ * Alloc memory, and save the size
+ */
+void *
+ieee80211_malloc(size_t size)
+{
+ void *p = kmem_zalloc((size + 4), KM_SLEEP);
+ *(int *)p = size;
+ p = (char *)p + 4;
+
+ return (p);
+}
+
+void
+ieee80211_free(void *p)
+{
+ void *tp = (char *)p - 4;
+ kmem_free((char *)p - 4, *(int *)tp + 4);
+}
+
+void
+ieee80211_mac_update(ieee80211com_t *ic)
+{
+ wifi_data_t wd = { 0 };
+ ieee80211_node_t *in;
+
+ /*
+ * We can send data now; update the fastpath with our
+ * current associated BSSID and other relevant settings.
+ */
+ in = ic->ic_bss;
+ wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
+ wd.wd_opmode = ic->ic_opmode;
+ IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid);
+ (void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
+ mac_tx_update(ic->ic_mach);
+ ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_mac_update"
+ "(cipher = %d)\n", wd.wd_secalloc);
+}
+
+/*
+ * ieee80211_event_thread
+ * open door of wpa, send event to wpad service
+ */
+static void
+ieee80211_event_thread(void *arg)
+{
+ ieee80211com_t *ic = arg;
+ door_handle_t event_door = NULL; /* Door for upcalls */
+ wl_events_t ev;
+ door_arg_t darg;
+
+ mutex_enter(&ic->ic_doorlock);
+
+ ev.event = ic->ic_eventq[ic->ic_evq_head];
+ ic->ic_evq_head ++;
+ if (ic->ic_evq_head >= MAX_EVENT)
+ ic->ic_evq_head = 0;
+
+ ieee80211_dbg(IEEE80211_MSG_DEBUG, "ieee80211_event(%d)\n", ev.event);
+ /*
+ * Locate the door used for upcalls
+ */
+ if (door_ki_open(ic->ic_wpadoor, &event_door) != 0) {
+ ieee80211_err("ieee80211_event: door_ki_open(%s) failed\n",
+ ic->ic_wpadoor);
+ goto out;
+ }
+
+ darg.data_ptr = (char *)&ev;
+ darg.data_size = sizeof (wl_events_t);
+ darg.desc_ptr = NULL;
+ darg.desc_num = 0;
+ darg.rbuf = NULL;
+ darg.rsize = 0;
+
+ if (door_ki_upcall(event_door, &darg) != 0) {
+ ieee80211_err("ieee80211_event: door_ki_upcall() failed\n");
+ }
+
+ if (event_door) { /* release our hold (if any) */
+ door_ki_rele(event_door);
+ }
+
+out:
+ mutex_exit(&ic->ic_doorlock);
+}
+
+/*
+ * Notify state transition event message to WPA daemon
+ */
+void
+ieee80211_notify(ieee80211com_t *ic, wpa_event_type event)
+{
+ if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
+ return; /* Not running on WPA mode */
+
+ ic->ic_eventq[ic->ic_evq_tail] = event;
+ ic->ic_evq_tail ++;
+ if (ic->ic_evq_tail >= MAX_EVENT) ic->ic_evq_tail = 0;
+
+ /* async */
+ (void) timeout(ieee80211_event_thread, (void *)ic, 0);
+}
+
+/*
* Default reset method for use with the ioctl support. This
* method is invoked after any state change in the 802.11
* layer that should be propagated to the hardware but not
@@ -435,6 +542,7 @@ ieee80211_notify_node_join(ieee80211com_t *ic, ieee80211_node_t *in)
{
if (in == ic->ic_bss)
mac_link_update(ic->ic_mach, LINK_STATE_UP);
+ ieee80211_notify(ic, EVENT_ASSOC); /* notify WPA service */
}
/*
@@ -447,6 +555,7 @@ ieee80211_notify_node_leave(ieee80211com_t *ic, ieee80211_node_t *in)
{
if (in == ic->ic_bss)
mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
+ ieee80211_notify(ic, EVENT_DISASSOC); /* notify WPA service */
}
/*
@@ -518,6 +627,7 @@ ieee80211_attach(ieee80211com_t *ic)
ASSERT(ic->ic_xmit != NULL);
mutex_init(&ic->ic_genlock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&ic->ic_doorlock, NULL, MUTEX_DRIVER, NULL);
im = kmem_alloc(sizeof (ieee80211_impl_t), KM_SLEEP);
ic->ic_private = im;
@@ -599,11 +709,12 @@ ieee80211_detach(ieee80211com_t *ic)
ieee80211_crypto_detach(ic);
mutex_destroy(&ic->ic_genlock);
+ mutex_destroy(&ic->ic_doorlock);
}
static struct modlmisc i_wifi_modlmisc = {
&mod_miscops,
- "IEEE80211 Kernel Misc Module"
+ "IEEE80211 Kernel Module v1.2"
};
static struct modlinkage i_wifi_modlinkage = {
diff --git a/usr/src/uts/common/io/net80211/net80211_crypto.c b/usr/src/uts/common/io/net80211/net80211_crypto.c
index 214e1700ec..49b68e2d74 100644
--- a/usr/src/uts/common/io/net80211/net80211_crypto.c
+++ b/usr/src/uts/common/io/net80211/net80211_crypto.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -45,6 +45,8 @@
#include "net80211_impl.h"
extern const struct ieee80211_cipher wep;
+extern const struct ieee80211_cipher tkip;
+extern const struct ieee80211_cipher ccmp;
/*
* Table of registered cipher modules.
@@ -160,6 +162,17 @@ ieee80211_crypto_newkey(ieee80211com_t *ic, int cipher, int flags,
cip->ic_name);
flags |= IEEE80211_KEY_SWCRYPT;
}
+ /*
+ * Hardware TKIP with software MIC is an important
+ * combination; we handle it by flagging each key,
+ * the cipher modules honor it.
+ */
+ if (cipher == IEEE80211_CIPHER_TKIP &&
+ (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) {
+ ieee80211_dbg(IEEE80211_MSG_CRYPTO,
+ "no h/w support for TKIP MIC, falling back to s/w\n");
+ flags |= IEEE80211_KEY_SWMIC;
+ }
/*
* Bind cipher to key instance. Note we do this
@@ -345,10 +358,9 @@ ieee80211_crypto_getciphertype(ieee80211com_t *ic)
uint32_t cipher;
static const uint8_t ciphermap[] = {
WIFI_SEC_WEP, /* IEEE80211_CIPHER_WEP */
- (uint8_t)-1, /* IEEE80211_CIPHER_TKIP */
+ WIFI_SEC_WPA, /* IEEE80211_CIPHER_TKIP */
(uint8_t)-1, /* IEEE80211_CIPHER_AES_OCB */
- (uint8_t)-1, /* IEEE80211_CIPHER_AES_CCM */
- (uint8_t)-1, /* 4 is not allocated */
+ WIFI_SEC_WPA, /* IEEE80211_CIPHER_AES_CCM */
(uint8_t)-1, /* IEEE80211_CIPHER_CKIP */
WIFI_SEC_NONE, /* IEEE80211_CIPHER_NONE */
};
@@ -453,6 +465,8 @@ ieee80211_crypto_attach(ieee80211com_t *ic)
cs->cs_key_update_end = nulldev_key_update;
ieee80211_crypto_register(&wep);
+ ieee80211_crypto_register(&tkip);
+ ieee80211_crypto_register(&ccmp);
}
/*
@@ -464,6 +478,8 @@ ieee80211_crypto_detach(ieee80211com_t *ic)
ieee80211_crypto_delglobalkeys(ic);
ieee80211_crypto_unregister(&wep);
+ ieee80211_crypto_unregister(&tkip);
+ ieee80211_crypto_unregister(&ccmp);
}
/*
diff --git a/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c b/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c
new file mode 100644
index 0000000000..53a0626bf1
--- /dev/null
+++ b/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * IEEE 802.11i CCMP crypto support.
+ */
+#include <sys/byteorder.h>
+#include <sys/crypto/common.h>
+#include <sys/crypto/api.h>
+#include <sys/crc32.h>
+#include <sys/random.h>
+#include "net80211_impl.h"
+
+struct ccmp_ctx {
+ struct ieee80211com *cc_ic; /* for diagnostics */
+};
+
+static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *);
+static void ccmp_detach(struct ieee80211_key *);
+static int ccmp_setkey(struct ieee80211_key *);
+static int ccmp_encap(struct ieee80211_key *k, mblk_t *, uint8_t);
+static int ccmp_decap(struct ieee80211_key *, mblk_t *, int);
+static int ccmp_enmic(struct ieee80211_key *, mblk_t *, int);
+static int ccmp_demic(struct ieee80211_key *, mblk_t *, int);
+
+const struct ieee80211_cipher ccmp = {
+ "AES-CCM",
+ IEEE80211_CIPHER_AES_CCM,
+ IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+ IEEE80211_WEP_EXTIVLEN,
+ IEEE80211_WEP_MICLEN,
+ 0,
+ ccmp_attach,
+ ccmp_detach,
+ ccmp_setkey,
+ ccmp_encap,
+ ccmp_decap,
+ ccmp_enmic,
+ ccmp_demic,
+};
+
+/* ARGSUSED */
+static void *
+ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+ struct ccmp_ctx *ctx;
+
+ ctx = kmem_zalloc(sizeof (struct ccmp_ctx), KM_SLEEP);
+ if (ctx == NULL)
+ return (NULL);
+
+ ctx->cc_ic = ic;
+ return (ctx);
+}
+
+static void
+ccmp_detach(struct ieee80211_key *k)
+{
+ struct ccmp_ctx *ctx = k->wk_private;
+
+ if (ctx != NULL)
+ kmem_free(ctx, sizeof (struct ccmp_ctx));
+}
+
+static int
+ccmp_setkey(struct ieee80211_key *k)
+{
+ if (k->wk_keylen != (128/NBBY))
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+ccmp_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
+{
+ uint8_t *ivp;
+ int hdrlen;
+
+ hdrlen = ieee80211_hdrspace(mp->b_rptr);
+ /*
+ * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+ */
+ ivp = mp->b_rptr;
+ ivp += hdrlen;
+
+ k->wk_keytsc++; /* wrap at 48 bits */
+ ivp[0] = k->wk_keytsc >> 0; /* PN0 */
+ ivp[1] = k->wk_keytsc >> 8; /* PN1 */
+ ivp[2] = 0; /* Reserved */
+ ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */
+ ivp[4] = k->wk_keytsc >> 16; /* PN2 */
+ ivp[5] = k->wk_keytsc >> 24; /* PN3 */
+ ivp[6] = k->wk_keytsc >> 32; /* PN4 */
+ ivp[7] = k->wk_keytsc >> 40; /* PN5 */
+
+ /*
+ * NB: software CCMP is not supported.
+ */
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame. The specified key should be correct but
+ * is also verified.
+ */
+static int
+ccmp_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
+{
+ struct ieee80211_frame tmp;
+ uint8_t *ivp;
+ uint64_t pn;
+
+ /*
+ * Header should have extended IV and sequence number;
+ * verify the former and validate the latter.
+ */
+ ivp = mp->b_rptr + hdrlen;
+ if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+ /*
+ * No extended IV; discard frame.
+ */
+ return (0);
+ }
+
+ pn = ieee80211_read_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
+ if (pn <= k->wk_keyrsc) {
+ /*
+ * Replay violation.
+ */
+ return (0);
+ }
+
+ /*
+ * NB: software CCMP is not supported.
+ */
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+ return (0);
+
+ /*
+ * Copy up 802.11 header and strip crypto bits.
+ */
+ bcopy(mp->b_rptr, &tmp, hdrlen);
+ bcopy(&tmp, mp->b_rptr + ccmp.ic_header, hdrlen);
+ mp->b_rptr += ccmp.ic_header;
+ mp->b_wptr -= ccmp.ic_trailer;
+
+ /*
+ * Ok to update rsc now.
+ */
+ k->wk_keyrsc = pn;
+
+ return (1);
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+/* ARGSUSED */
+static int
+ccmp_enmic(struct ieee80211_key *k, mblk_t *mp, int force)
+{
+ return (1);
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+/* ARGSUSED */
+static int
+ccmp_demic(struct ieee80211_key *k, mblk_t *mp, int force)
+{
+ return (1);
+}
diff --git a/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c b/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c
new file mode 100644
index 0000000000..f900d01d29
--- /dev/null
+++ b/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * IEEE 802.11i TKIP crypto support.
+ */
+#include <sys/byteorder.h>
+#include <sys/crypto/common.h>
+#include <sys/crypto/api.h>
+#include <sys/crc32.h>
+#include <sys/random.h>
+#include "net80211_impl.h"
+
+static void *tkip_attach(struct ieee80211com *, struct ieee80211_key *);
+static void tkip_detach(struct ieee80211_key *);
+static int tkip_setkey(struct ieee80211_key *);
+static int tkip_encap(struct ieee80211_key *, mblk_t *, uint8_t);
+static int tkip_decap(struct ieee80211_key *, mblk_t *, int);
+static int tkip_enmic(struct ieee80211_key *, mblk_t *, int);
+static int tkip_demic(struct ieee80211_key *, mblk_t *, int);
+
+const struct ieee80211_cipher tkip = {
+ "TKIP",
+ IEEE80211_CIPHER_TKIP,
+ IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+ IEEE80211_WEP_EXTIVLEN,
+ IEEE80211_WEP_CRCLEN,
+ IEEE80211_WEP_MICLEN,
+ tkip_attach,
+ tkip_detach,
+ tkip_setkey,
+ tkip_encap,
+ tkip_decap,
+ tkip_enmic,
+ tkip_demic,
+};
+
+struct tkip_ctx {
+ struct ieee80211com *tc_ic; /* for diagnostics */
+ uint16_t tx_ttak[5];
+ int tx_phase1_done;
+ uint8_t tx_rc4key[16];
+ uint16_t rx_ttak[5];
+ int rx_phase1_done;
+ uint8_t rx_rc4key[16];
+ uint64_t rx_rsc; /* held until MIC verified */
+};
+
+/* ARGSUSED */
+static void *
+tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+ struct tkip_ctx *ctx;
+
+ ctx = kmem_zalloc(sizeof (struct tkip_ctx), KM_SLEEP);
+ if (ctx == NULL)
+ return (NULL);
+
+ ctx->tc_ic = ic;
+ return (ctx);
+}
+
+static void
+tkip_detach(struct ieee80211_key *k)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+
+ if (ctx != NULL)
+ kmem_free(ctx, sizeof (struct tkip_ctx));
+}
+
+static int
+tkip_setkey(struct ieee80211_key *k)
+{
+ if (k->wk_keylen != (128/NBBY))
+ return (0);
+
+ k->wk_keytsc = 1; /* TSC starts at 1 */
+ return (1);
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+tkip_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+ struct ieee80211com *ic = ctx->tc_ic;
+ uint8_t *ivp;
+ int hdrlen;
+
+ /*
+ * Handle TKIP counter measures requirement.
+ */
+ if (ic->ic_flags & IEEE80211_F_COUNTERM)
+ return (0);
+
+ hdrlen = ieee80211_hdrspace(mp->b_rptr);
+ /*
+ * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+ */
+ ivp = mp->b_rptr;
+ ivp += hdrlen;
+
+ ivp[0] = k->wk_keytsc >> 8; /* TSC1 */
+ ivp[1] = (ivp[0] | 0x20) & 0x7f; /* WEP seed */
+ ivp[2] = k->wk_keytsc >> 0; /* TSC0 */
+ ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */
+ ivp[4] = k->wk_keytsc >> 16; /* TSC2 */
+ ivp[5] = k->wk_keytsc >> 24; /* TSC3 */
+ ivp[6] = k->wk_keytsc >> 32; /* TSC4 */
+ ivp[7] = k->wk_keytsc >> 40; /* TSC5 */
+
+ /*
+ * NB: software TKIP is not supported.
+ */
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+ return (0);
+ else
+ k->wk_keytsc++; /* wrap at 48 bits */
+
+ return (1);
+}
+
+uint64_t
+ieee80211_read_6(uint8_t b0, uint8_t b1, uint8_t b2,
+ uint8_t b3, uint8_t b4, uint8_t b5)
+{
+ uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
+ uint16_t iv16 = (b4 << 0) | (b5 << 8);
+ return ((((uint64_t)iv16) << 32) | iv32);
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame. If necessary, decrypt the frame using
+ * the specified key.
+ */
+static int
+tkip_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+ struct ieee80211com *ic = ctx->tc_ic;
+ struct ieee80211_frame tmp;
+ uint8_t *ivp;
+ uint64_t pn;
+
+ /*
+ * Header should have extended IV and sequence number;
+ * verify the former and validate the latter.
+ */
+ ivp = mp->b_rptr + hdrlen;
+ if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+ /*
+ * No extended IV; discard frame.
+ */
+ return (0);
+ }
+ /*
+ * Handle TKIP counter measures requirement.
+ */
+ if (ic->ic_flags & IEEE80211_F_COUNTERM)
+ return (0);
+
+ /* NB: assume IEEEE80211_WEP_MINLEN covers the extended IV */
+ pn = ieee80211_read_6(ivp[2], ivp[0], ivp[4], ivp[5], ivp[6], ivp[7]);
+ ctx->rx_rsc = pn;
+ if (ctx->rx_rsc <= k->wk_keyrsc)
+ return (0);
+ /*
+ * NB: We can't update the rsc in the key until MIC is verified.
+ *
+ * We assume we are not preempted between doing the check above
+ * and updating wk_keyrsc when stripping the MIC in tkip_demic.
+ * Otherwise we might process another packet and discard it as
+ * a replay.
+ */
+
+ /*
+ * NB: software TKIP is not supported.
+ */
+ if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+ return (0);
+
+ /*
+ * Copy up 802.11 header and strip crypto bits.
+ */
+ bcopy(mp->b_rptr, &tmp, hdrlen);
+ bcopy(&tmp, mp->b_rptr + tkip.ic_header, hdrlen);
+ mp->b_rptr += tkip.ic_header;
+ mp->b_wptr -= tkip.ic_trailer;
+
+ return (1);
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+/* ARGSUSED */
+static int
+tkip_enmic(struct ieee80211_key *k, mblk_t *mp, int force)
+{
+ return (1);
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+/* ARGSUSED */
+static int
+tkip_demic(struct ieee80211_key *k, mblk_t *mp, int force)
+{
+ struct tkip_ctx *ctx = k->wk_private;
+
+ /*
+ * NB: software TKIP is not supported.
+ */
+ if (k->wk_flags & IEEE80211_KEY_SWMIC)
+ return (0);
+ /*
+ * Strip MIC from the tail.
+ */
+ mp->b_wptr -= tkip.ic_miclen;
+ /*
+ * Ok to update rsc now that MIC has been verified.
+ */
+ k->wk_keyrsc = ctx->rx_rsc;
+
+ return (1);
+}
diff --git a/usr/src/uts/common/io/net80211/net80211_impl.h b/usr/src/uts/common/io/net80211/net80211_impl.h
index 2b535f4170..823f0e2365 100644
--- a/usr/src/uts/common/io/net80211/net80211_impl.h
+++ b/usr/src/uts/common/io/net80211/net80211_impl.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -354,6 +354,11 @@ void ieee80211_err(const int8_t *, ...);
void ieee80211_dbg(uint32_t, const int8_t *, ...);
int ieee80211_hdrspace(const void *);
+void ieee80211_notify(ieee80211com_t *, wpa_event_type);
+void ieee80211_mac_update(ieee80211com_t *);
+
+uint64_t ieee80211_read_6(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
+
/* node */
void ieee80211_node_attach(ieee80211com_t *);
void ieee80211_node_lateattach(ieee80211com_t *);
diff --git a/usr/src/uts/common/io/net80211/net80211_input.c b/usr/src/uts/common/io/net80211/net80211_input.c
index 39d5db2df0..67f32e8680 100644
--- a/usr/src/uts/common/io/net80211/net80211_input.c
+++ b/usr/src/uts/common/io/net80211/net80211_input.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -645,6 +645,13 @@ bad:
}
}
+static int
+iswpaoui(const uint8_t *frm)
+{
+ uint32_t c = *(uint32_t *)(frm + 2);
+ return (frm[1] > 3 && c == ((WPA_OUI_TYPE << 24) | WPA_OUI));
+}
+
/*
* Process a beacon/probe response frame.
* When the device is in station mode, create a node and add it
@@ -762,6 +769,10 @@ ieee80211_recv_beacon(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
case IEEE80211_ELEMID_RSN:
scan.wpa = frm;
break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm))
+ scan.wpa = frm; /* IEEE802.11i D3.0 */
+ break;
default:
ieee80211_dbg(IEEE80211_MSG_ELEMID,
"ieee80211_recv_mgmt: ignore %s,"
diff --git a/usr/src/uts/common/io/net80211/net80211_ioctl.c b/usr/src/uts/common/io/net80211/net80211_ioctl.c
index 3701baa6f7..6479ab998e 100644
--- a/usr/src/uts/common/io/net80211/net80211_ioctl.c
+++ b/usr/src/uts/common/io/net80211/net80211_ioctl.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -570,6 +570,8 @@ wifi_cfg_encrypt(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
switch (cmd) {
case WLAN_GET_PARAM:
*ow_encryp = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
+ if (ic->ic_flags & IEEE80211_F_WPA)
+ *ow_encryp = WL_ENC_WPA;
break;
case WLAN_SET_PARAM:
ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_encrypt: "
@@ -993,6 +995,7 @@ wifi_read_ap(void *arg, struct ieee80211_node *in)
(in->in_capinfo & IEEE80211_CAPINFO_ESS ?
WL_BSS_BSS : WL_BSS_IBSS);
conf->wl_ess_conf_sl = wifi_getrssi(in);
+ conf->wl_ess_conf_reserved[0] = (in->in_wpa_ie == NULL? 0 : 1);
/* physical (FH, DS, ERP) parameters */
if (IEEE80211_IS_CHAN_A(chan) || IEEE80211_IS_CHAN_T(chan)) {
@@ -1130,13 +1133,17 @@ wifi_cmd_scan(struct ieee80211com *ic, mblk_t *mp)
return (0);
IEEE80211_UNLOCK(ic);
+
ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
IEEE80211_LOCK(ic);
if (ostate == IEEE80211_S_INIT)
ic->ic_flags |= IEEE80211_F_SCANONLY;
- /* wait scan complete */
- wifi_wait_scan(ic);
+ /* Don't wait on WPA mode */
+ if ((ic->ic_flags & IEEE80211_F_WPA) == 0) {
+ /* wait scan complete */
+ wifi_wait_scan(ic);
+ }
wifi_setupoutmsg(mp, 0);
return (0);
@@ -1158,6 +1165,8 @@ wifi_loaddefdata(struct ieee80211com *ic)
bzero(ic->ic_nickname, IEEE80211_NWID_LEN);
in->in_authmode = IEEE80211_AUTH_OPEN;
ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+ ic->ic_flags &= ~IEEE80211_F_WPA; /* mask WPA mode */
+ ic->ic_evq_head = ic->ic_evq_tail = 0; /* reset Queue */
ic->ic_def_txkey = 0;
for (i = 0; i < MAX_NWEPKEYS; i++) {
ic->ic_nw_keys[i].wk_keylen = 0;
@@ -1187,6 +1196,462 @@ wifi_cmd_disassoc(struct ieee80211com *ic, mblk_t *mp)
return (0);
}
+/*
+ * Get the capabilities of drivers.
+ */
+static int
+wifi_cfg_caps(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wl_capability_t *o_caps;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_capability_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ o_caps = (wl_capability_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_caps: "
+ "ic_caps = %u\n", ic->ic_caps);
+ o_caps->caps = ic->ic_caps;
+ break;
+ case WLAN_SET_PARAM:
+ outp->wldp_result = WL_READONLY;
+ err = EINVAL;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_caps: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+/*
+ * Operating on WPA mode.
+ */
+static int
+wifi_cfg_wpa(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wl_wpa_t *wpa = (wl_wpa_t *)inp->wldp_buf;
+ wl_wpa_t *o_wpa;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_wpa_t))) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+ o_wpa = (wl_wpa_t *)outp->wldp_buf;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpa: "
+ "get wpa=%u\n", wpa->wpa_flag);
+ o_wpa->wpa_flag = ((ic->ic_flags & IEEE80211_F_WPA)? 1 : 0);
+ break;
+ case WLAN_SET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpa: "
+ "set wpa=%u\n", wpa->wpa_flag);
+ if (wpa->wpa_flag > 0) { /* enable WPA mode */
+ ic->ic_flags |= IEEE80211_F_PRIVACY;
+ ic->ic_flags |= IEEE80211_F_WPA;
+ } else {
+ ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+ ic->ic_flags &= ~IEEE80211_F_WPA;
+ }
+ break;
+ default:
+ ieee80211_err("wifi_cfg_wpa: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+/*
+ * WPA daemon set the WPA keys.
+ * The WPA keys are negotiated with APs through wpa service.
+ */
+static int
+wifi_cfg_wpakey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wl_key_t *ik = (wl_key_t *)(inp->wldp_buf);
+ struct ieee80211_node *in;
+ struct ieee80211_key *wk;
+ uint16_t kid;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ outp->wldp_result = WL_WRITEONLY;
+ err = EINVAL;
+ break;
+ case WLAN_SET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_wpakey: "
+ "idx=%d\n", ik->ik_keyix);
+ /* NB: cipher support is verified by ieee80211_crypt_newkey */
+ /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
+ if (ik->ik_keylen > sizeof (ik->ik_keydata)) {
+ ieee80211_err("wifi_cfg_wpakey: key too long\n");
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+ kid = ik->ik_keyix;
+ if (kid == IEEE80211_KEYIX_NONE || kid >= IEEE80211_WEP_NKID) {
+ ieee80211_err("wifi_cfg_wpakey: incorrect keyix\n");
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+
+ } else {
+ wk = &ic->ic_nw_keys[kid];
+ /*
+ * Global slots start off w/o any assigned key index.
+ * Force one here for consistency with WEPKEY.
+ */
+ if (wk->wk_keyix == IEEE80211_KEYIX_NONE)
+ wk->wk_keyix = kid;
+ /* in = ic->ic_bss; */
+ in = NULL;
+ }
+
+ KEY_UPDATE_BEGIN(ic);
+ if (ieee80211_crypto_newkey(ic, ik->ik_type,
+ ik->ik_flags, wk)) {
+ wk->wk_keylen = ik->ik_keylen;
+ /* NB: MIC presence is implied by cipher type */
+ if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
+ wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
+ wk->wk_keyrsc = ik->ik_keyrsc;
+ wk->wk_keytsc = 0; /* new key, reset */
+ wk->wk_flags |= ik->ik_flags &
+ (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
+ (void) memset(wk->wk_key, 0, sizeof (wk->wk_key));
+ (void) memcpy(wk->wk_key, ik->ik_keydata,
+ ik->ik_keylen);
+ if (!ieee80211_crypto_setkey(ic, wk,
+ in != NULL ? in->in_macaddr : ik->ik_macaddr)) {
+ err = EIO;
+ outp->wldp_result = WL_HW_ERROR;
+ } else if ((ik->ik_flags & IEEE80211_KEY_DEFAULT)) {
+ ic->ic_def_txkey = kid;
+ ieee80211_mac_update(ic);
+ }
+ } else {
+ err = EIO;
+ outp->wldp_result = WL_HW_ERROR;
+ }
+ KEY_UPDATE_END(ic);
+ break;
+ default:
+ ieee80211_err("wifi_cfg_wpakey: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+/*
+ * Delete obsolete keys - keys are dynamically exchanged between APs
+ * and wpa daemon.
+ */
+static int
+wifi_cfg_delkey(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wl_del_key_t *dk = (wl_del_key_t *)inp->wldp_buf;
+ int kid;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ outp->wldp_result = WL_WRITEONLY;
+ err = EINVAL;
+ break;
+ case WLAN_SET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_delkey: "
+ "keyix=%d\n", dk->idk_keyix);
+ kid = dk->idk_keyix;
+ if (kid == IEEE80211_KEYIX_NONE || kid >= IEEE80211_WEP_NKID) {
+ ieee80211_err("wifi_cfg_delkey: incorrect keyix\n");
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+
+ } else {
+ (void) ieee80211_crypto_delkey(ic,
+ &ic->ic_nw_keys[kid]);
+ ieee80211_mac_update(ic);
+ }
+ break;
+ default:
+ ieee80211_err("wifi_cfg_delkey: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+/*
+ * The OPTIE will be used in the association request.
+ */
+static int
+wifi_cfg_setoptie(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wl_wpa_ie_t *ie_in = (wl_wpa_ie_t *)inp->wldp_buf;
+ char *ie;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ outp->wldp_result = WL_WRITEONLY;
+ err = EINVAL;
+ break;
+ case WLAN_SET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_setoptie\n");
+ /*
+ * NB: Doing this for ap operation could be useful (e.g. for
+ * WPA and/or WME) except that it typically is worthless
+ * without being able to intervene when processing
+ * association response frames--so disallow it for now.
+ */
+ if (ic->ic_opmode != IEEE80211_M_STA) {
+ ieee80211_err("wifi_cfg_setoptie: opmode err\n");
+ err = EINVAL;
+ outp->wldp_result = WL_NOTSUPPORTED;
+ break;
+ }
+ if (ie_in->wpa_ie_len > IEEE80211_MAX_OPT_IE) {
+ ieee80211_err("wifi_cfg_setoptie: optie too long\n");
+ err = EINVAL;
+ outp->wldp_result = WL_NOTSUPPORTED;
+ break;
+ }
+
+ ie = ieee80211_malloc(ie_in->wpa_ie_len);
+ (void) memcpy(ie, ie_in->wpa_ie, ie_in->wpa_ie_len);
+ if (ic->ic_opt_ie != NULL)
+ ieee80211_free(ic->ic_opt_ie);
+ ic->ic_opt_ie = ie;
+ ic->ic_opt_ie_len = ie_in->wpa_ie_len;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_setoptie: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+/*
+ * To be compatible with drivers/tools of OpenSolaris.org,
+ * we use a different ID to filter out those APs of WPA mode.
+ */
+static int
+wifi_cfg_scanresults(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wl_wpa_ess_t *sr;
+ ieee80211_node_t *in;
+ ieee80211_node_table_t *nt;
+ int len, ap_num = 0;
+ int err = 0;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, MAX_BUF_LEN - WIFI_BUF_OFFSET)) ==
+ NULL) {
+ return (ENOMEM);
+ }
+ outp = (wldp_t *)omp->b_rptr;
+ sr = (wl_wpa_ess_t *)outp->wldp_buf;
+ sr->count = 0;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_scanresults\n");
+ nt = &ic->ic_scan;
+ IEEE80211_NODE_LOCK(nt);
+ in = list_head(&nt->nt_node);
+ while (in != NULL) {
+ /* filter out non-WPA APs */
+ if (in->in_wpa_ie == NULL) {
+ in = list_next(&nt->nt_node, in);
+ continue;
+ }
+ bcopy(in->in_bssid, sr->ess[ap_num].bssid,
+ IEEE80211_ADDR_LEN);
+ sr->ess[ap_num].ssid_len = in->in_esslen;
+ bcopy(in->in_essid, sr->ess[ap_num].ssid,
+ in->in_esslen);
+ sr->ess[ap_num].freq = in->in_chan->ich_freq;
+
+ len = in->in_wpa_ie[1] + 2;
+ bcopy(in->in_wpa_ie, sr->ess[ap_num].wpa_ie, len);
+ sr->ess[ap_num].wpa_ie_len = len;
+
+ ap_num ++;
+ in = list_next(&nt->nt_node, in);
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+ sr->count = ap_num;
+ outp->wldp_length = WIFI_BUF_OFFSET +
+ offsetof(wl_wpa_ess_t, ess) +
+ sr->count * sizeof (struct wpa_ess);
+ omp->b_wptr = omp->b_rptr + outp->wldp_length;
+ break;
+ case WLAN_SET_PARAM:
+ outp->wldp_result = WL_READONLY;
+ err = EINVAL;
+ break;
+ default:
+ ieee80211_err("wifi_cfg_scanresults: unknown cmmand %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
+/*
+ * Manually control the state of AUTH | DEAUTH | DEASSOC | ASSOC
+ */
+static int
+wifi_cfg_setmlme(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+ mblk_t *omp;
+ wldp_t *outp;
+ wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+ wl_mlme_t *mlme = (wl_mlme_t *)inp->wldp_buf;
+ ieee80211_node_t *in;
+ int err = 0;
+ uint32_t flags;
+
+ if ((omp = wifi_getoutmsg(*mp, cmd, 0)) == NULL)
+ return (ENOMEM);
+ outp = (wldp_t *)omp->b_rptr;
+
+ switch (cmd) {
+ case WLAN_GET_PARAM:
+ outp->wldp_result = WL_WRITEONLY;
+ err = EINVAL;
+ break;
+ case WLAN_SET_PARAM:
+ ieee80211_dbg(IEEE80211_MSG_WPA, "wifi_cfg_setmlme: "
+ "op=%d\n", mlme->im_op);
+ switch (mlme->im_op) {
+ case IEEE80211_MLME_DISASSOC:
+ case IEEE80211_MLME_DEAUTH:
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ /*
+ * Mask ic_flags of IEEE80211_F_WPA to disable
+ * ieee80211_notify temporarily.
+ */
+ flags = ic->ic_flags;
+ ic->ic_flags &= ~IEEE80211_F_WPA;
+
+ IEEE80211_UNLOCK(ic);
+ ieee80211_new_state(ic, IEEE80211_S_INIT,
+ mlme->im_reason);
+ IEEE80211_LOCK(ic);
+
+ ic->ic_flags = flags;
+ }
+ break;
+ case IEEE80211_MLME_ASSOC:
+ if (ic->ic_opmode != IEEE80211_M_STA) {
+ ieee80211_err("wifi_cfg_setmlme: opmode err\n");
+ err = EINVAL;
+ outp->wldp_result = WL_NOTSUPPORTED;
+ break;
+ }
+ if (ic->ic_des_esslen != 0) {
+ /*
+ * Desired ssid specified; must match both bssid and
+ * ssid to distinguish ap advertising multiple ssid's.
+ */
+ in = ieee80211_find_node_with_ssid(&ic->ic_scan,
+ mlme->im_macaddr,
+ ic->ic_des_esslen, ic->ic_des_essid);
+ } else {
+ /*
+ * Normal case; just match bssid.
+ */
+ in = ieee80211_find_node(&ic->ic_scan,
+ mlme->im_macaddr);
+ }
+ if (in == NULL) {
+ ieee80211_err("wifi_cfg_setmlme: "
+ "no matched node\n");
+ err = EINVAL;
+ outp->wldp_result = WL_NOTSUPPORTED;
+ break;
+ }
+ IEEE80211_UNLOCK(ic);
+ ieee80211_sta_join(ic, in);
+ IEEE80211_LOCK(ic);
+ }
+ break;
+ default:
+ ieee80211_err("wifi_cfg_delkey: unknown command %x\n", cmd);
+ outp->wldp_result = WL_NOTSUPPORTED;
+ err = EINVAL;
+ break;
+ }
+
+ freemsg(*mp);
+ *mp = omp;
+ return (err);
+}
+
static int
wifi_cfg_getset(struct ieee80211com *ic, mblk_t **mp, uint32_t cmd)
{
@@ -1256,6 +1721,30 @@ wifi_cfg_getset(struct ieee80211com *ic, mblk_t **mp, uint32_t cmd)
case WL_RSSI:
err = wifi_cfg_rssi(ic, cmd, mp);
break;
+ /*
+ * WPA IOCTLs
+ */
+ case WL_CAPABILITY:
+ err = wifi_cfg_caps(ic, cmd, mp);
+ break;
+ case WL_WPA:
+ err = wifi_cfg_wpa(ic, cmd, mp);
+ break;
+ case WL_KEY:
+ err = wifi_cfg_wpakey(ic, cmd, mp);
+ break;
+ case WL_DELKEY:
+ err = wifi_cfg_delkey(ic, cmd, mp);
+ break;
+ case WL_SETOPTIE:
+ err = wifi_cfg_setoptie(ic, cmd, mp);
+ break;
+ case WL_SCANRESULTS:
+ err = wifi_cfg_scanresults(ic, cmd, mp);
+ break;
+ case WL_MLME:
+ err = wifi_cfg_setmlme(ic, cmd, mp);
+ break;
default:
wifi_setupoutmsg(mp1, 0);
wp->wldp_result = WL_LACK_FEATURE;
diff --git a/usr/src/uts/common/io/net80211/net80211_node.c b/usr/src/uts/common/io/net80211/net80211_node.c
index cdc9789986..2413921dee 100644
--- a/usr/src/uts/common/io/net80211/net80211_node.c
+++ b/usr/src/uts/common/io/net80211/net80211_node.c
@@ -382,7 +382,7 @@ ieee80211_create_ibss(ieee80211com_t *ic, struct ieee80211_channel *chan)
*/
ieee80211_setbasicrates(&in->in_rates, ic->ic_curmode);
IEEE80211_UNLOCK(ic);
- ieee80211_sta_join(ic, in);
+ ieee80211_sta_join(ic, ieee80211_ref_node(in));
IEEE80211_LOCK(ic);
}
@@ -539,6 +539,8 @@ ieee80211_end_scan(ieee80211com_t *ic)
ieee80211_node_t *selbs;
ieee80211_cancel_scan(ic);
+ /* notify SCAN done */
+ ieee80211_notify(ic, EVENT_SCAN_RESULTS);
IEEE80211_LOCK(ic);
/*
@@ -547,7 +549,7 @@ ieee80211_end_scan(ieee80211com_t *ic)
*/
/* NB: unlocked read should be ok */
in = list_head(&nt->nt_node);
- if (in == NULL) {
+ if (in == NULL && (ic->ic_flags & IEEE80211_F_WPA) == 0) {
ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_end_scan: "
"no scan candidate\n");
notfound:
@@ -570,7 +572,8 @@ ieee80211_end_scan(ieee80211com_t *ic)
return;
}
- if (ic->ic_flags & IEEE80211_F_SCANONLY) { /* scan only */
+ if (ic->ic_flags & IEEE80211_F_SCANONLY ||
+ ic->ic_flags & IEEE80211_F_WPA) { /* scan only */
ic->ic_flags &= ~IEEE80211_F_SCANONLY;
IEEE80211_UNLOCK(ic);
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
@@ -608,6 +611,8 @@ ieee80211_end_scan(ieee80211com_t *ic)
}
in = list_next(&nt->nt_node, in);
}
+ if (selbs != NULL) /* grab ref while dropping lock */
+ (void) ieee80211_ref_node(selbs);
IEEE80211_NODE_UNLOCK(nt);
if (selbs == NULL)
goto notfound;
@@ -648,7 +653,7 @@ ieee80211_ibss_merge(ieee80211_node_t *in)
(ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long",
(ic->ic_flags & IEEE80211_F_SHSLOT) ? "short" : "long",
(ic->ic_flags&IEEE80211_F_USEPROT) ? ", protection" : "");
- ieee80211_sta_join(ic, in);
+ ieee80211_sta_join(ic, ieee80211_ref_node(in));
return (B_TRUE);
}
@@ -685,7 +690,7 @@ ieee80211_sta_join(ieee80211com_t *ic, ieee80211_node_t *selbs)
* Committed to selbs, setup state.
*/
obss = ic->ic_bss;
- ic->ic_bss = ieee80211_ref_node(selbs); /* Grab reference */
+ ic->ic_bss = selbs; /* caller assumed to bump refcnt */
if (obss != NULL) {
ieee80211_copy_bss(selbs, obss);
ieee80211_free_node(obss);
@@ -763,6 +768,8 @@ ieee80211_node_free(ieee80211_node_t *in)
ieee80211com_t *ic = in->in_ic;
ic->ic_node_cleanup(in);
+ if (in->in_wpa_ie != NULL)
+ ieee80211_free(in->in_wpa_ie);
kmem_free(in, sizeof (ieee80211_node_t));
}
@@ -944,6 +951,35 @@ ieee80211_find_node(ieee80211_node_table_t *nt, const uint8_t *macaddr)
}
/*
+ * Like find but search based on the ssid too.
+ */
+ieee80211_node_t *
+ieee80211_find_node_with_ssid(ieee80211_node_table_t *nt,
+ const uint8_t *macaddr, uint32_t ssidlen, const uint8_t *ssid)
+{
+ ieee80211_node_t *in;
+ int hash;
+
+ IEEE80211_NODE_LOCK(nt);
+
+ hash = ieee80211_node_hash(macaddr);
+ in = list_head(&nt->nt_hash[hash]);
+ while (in != NULL) {
+ if (IEEE80211_ADDR_EQ(in->in_macaddr, macaddr) &&
+ in->in_esslen == ssidlen &&
+ memcmp(in->in_essid, ssid, ssidlen) == 0)
+ break;
+ in = list_next(&nt->nt_hash[hash], in);
+ }
+ if (in != NULL) {
+ (void) ieee80211_ref_node(in); /* mark referenced */
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+
+ return (in);
+}
+
+/*
* Fake up a node; this handles node discovery in adhoc mode.
* Note that for the driver's benefit we treat this like an
* association so the driver has an opportunity to setup it's
@@ -968,6 +1004,31 @@ ieee80211_fakeup_adhoc_node(ieee80211_node_table_t *nt, const uint8_t *macaddr)
return (in);
}
+static void
+ieee80211_saveie(uint8_t **iep, const uint8_t *ie)
+{
+ uint_t ielen = ie[1]+2;
+ /*
+ * Record information element for later use.
+ */
+ if (*iep == NULL || (*iep)[1] != ie[1]) {
+ if (*iep != NULL)
+ ieee80211_free(*iep);
+ *iep = ieee80211_malloc(ielen);
+ }
+ if (*iep != NULL)
+ (void) memcpy(*iep, ie, ielen);
+}
+
+static void
+saveie(uint8_t **iep, const uint8_t *ie)
+{
+ if (ie == NULL)
+ *iep = NULL;
+ else
+ ieee80211_saveie(iep, ie);
+}
+
/*
* Process a beacon or probe response frame.
*/
@@ -1037,6 +1098,11 @@ ieee80211_add_scan(ieee80211com_t *ic, const struct ieee80211_scanparams *sp,
* processing of beacon frames.
*/
in->in_tim_off = sp->timoff;
+ /*
+ * Record optional information elements that might be
+ * used by applications or drivers.
+ */
+ saveie(&in->in_wpa_ie, sp->wpa);
/* NB: must be after in_chan is setup */
(void) ieee80211_setup_rates(in, sp->rates, sp->xrates,
diff --git a/usr/src/uts/common/io/net80211/net80211_output.c b/usr/src/uts/common/io/net80211/net80211_output.c
index ca04fb0b4e..d32ab7f88b 100644
--- a/usr/src/uts/common/io/net80211/net80211_output.c
+++ b/usr/src/uts/common/io/net80211/net80211_output.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -175,6 +175,7 @@ mblk_t *
ieee80211_encap(ieee80211com_t *ic, mblk_t *mp, ieee80211_node_t *in)
{
struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
ASSERT(mp != NULL && MBLKL(mp) >= sizeof (struct ieee80211_frame));
wh = (struct ieee80211_frame *)mp->b_rptr;
@@ -183,6 +184,22 @@ ieee80211_encap(ieee80211com_t *ic, mblk_t *mp, ieee80211_node_t *in)
LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
in->in_txseqs[0]++;
+ if (ic->ic_flags & IEEE80211_F_PRIVACY)
+ key = ieee80211_crypto_getkey(ic);
+ else
+ key = NULL;
+
+ /*
+ * IEEE 802.1X: send EAPOL frames always in the clear.
+ * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
+ */
+ if (key != NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
+ wh->i_fc[1] |= IEEE80211_FC1_WEP;
+ if (!ieee80211_crypto_enmic(isc, key, mp, 0)) {
+ ieee80211_err("ieee80211_crypto_enmic failed.\n");
+ }
+ }
+
return (mp);
}
diff --git a/usr/src/uts/common/net/Makefile b/usr/src/uts/common/net/Makefile
index d87bf040b2..7d56e769c6 100644
--- a/usr/src/uts/common/net/Makefile
+++ b/usr/src/uts/common/net/Makefile
@@ -21,7 +21,7 @@
#
# ident "%Z%%M% %I% %E% SMI"
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# uts/common/net/Makefile
@@ -30,7 +30,8 @@
include ../../../Makefile.master
HDRS= af.h if.h if_arp.h if_dl.h if_types.h route.h pfkeyv2.h pfpolicy.h \
- ppp-comp.h ppp_defs.h pppio.h vjcompress.h sppptun.h pppoe.h radix.h
+ ppp-comp.h ppp_defs.h pppio.h vjcompress.h sppptun.h pppoe.h radix.h \
+ wpa.h
ROOTDIRS= $(ROOT)/usr/include/net
diff --git a/usr/src/uts/common/net/wpa.h b/usr/src/uts/common/net/wpa.h
new file mode 100644
index 0000000000..08e8342744
--- /dev/null
+++ b/usr/src/uts/common/net/wpa.h
@@ -0,0 +1,195 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Macro and data structures defined for 802.11i.
+ */
+
+#ifndef __WPA_H
+#define __WPA_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <inet/wifi_ioctl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SERVICE_NAME "network/wpa"
+#define WPA_DOOR "/var/run/wpa_door"
+#define SVC_METHOD "/usr/lib/inet/wpad"
+
+#define IEEE80211_ADDR_LEN 6
+#define IEEE80211_MAX_WPA_IE 40 /* IEEE802.11i */
+#define WPA_STRSIZE 256
+/*
+ * Max size of optional information elements. We artificially
+ * constrain this; it's limited only by the max frame size (and
+ * the max parameter size of the wireless extensions).
+ */
+#define IEEE80211_MAX_OPT_IE 256
+
+/*
+ * Parameters.
+ * WL_WPA_BASE + 0x1, 5, 6 reserved to be compatible with FreeBSD.
+ */
+#define WL_WPA_BASE (WL_PARAMETERS_BASE + 0x500)
+#define WL_SETOPTIE (WL_WPA_BASE + 0x0)
+#define WL_WPA (WL_WPA_BASE + 0x2)
+#define WL_KEY (WL_WPA_BASE + 0x3)
+#define WL_DELKEY (WL_WPA_BASE + 0x4)
+#define WL_SCANRESULTS (WL_WPA_BASE + 0x7)
+#define WL_MLME (WL_WPA_BASE + 0x8)
+#define WL_CAPABILITY (WL_WPA_BASE + 0x9)
+
+typedef struct wl_wpa_ie {
+ uint32_t wpa_ie_len;
+ char wpa_ie[1]; /* it's the head of wpa_ie */
+} wl_wpa_ie_t;
+
+typedef struct wl_wpa {
+ uint32_t wpa_flag;
+} wl_wpa_t;
+
+typedef struct wl_capability {
+ uint32_t caps;
+} wl_capability_t;
+
+#define IEEE80211_KEYBUF_SIZE 16 /* 128-bit TKIP & CCMP key */
+#define IEEE80211_MICBUF_SIZE (8+8) /* 8 byte tx, 8 byte rx */
+
+/*
+ * NB: these values are ordered carefully; there are lots of
+ * of implications in any reordering. In particular beware
+ * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ */
+#define IEEE80211_CIPHER_WEP 0
+#define IEEE80211_CIPHER_TKIP 1
+#define IEEE80211_CIPHER_AES_OCB 2
+#define IEEE80211_CIPHER_AES_CCM 3
+#define IEEE80211_CIPHER_CKIP 4
+#define IEEE80211_CIPHER_NONE 5 /* pseudo value */
+
+#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1)
+
+/* Key Flags */
+#define IEEE80211_KEY_XMIT 0x01 /* key used for xmit */
+#define IEEE80211_KEY_RECV 0x02 /* key used for recv */
+
+#define IEEE80211_KEY_DEFAULT 0x80 /* default xmit key */
+
+/*
+ * WPA/RSN get/set key request. Specify the key/cipher
+ * type and whether the key is to be used for sending and/or
+ * receiving. The key index should be set only when working
+ * with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
+ * Otherwise a unicast/pairwise key is specified by the bssid
+ * (on a station) or mac address (on an ap). They key length
+ * must include any MIC key data; otherwise it should be no
+ * more than IEEE80211_KEYBUF_SIZE.
+ */
+#pragma pack(1)
+typedef struct wl_key {
+ uint8_t ik_type; /* key/cipher type */
+ uint8_t ik_pad;
+
+ uint16_t ik_keyix; /* key index */
+ uint8_t ik_keylen; /* key length in bytes */
+ uint8_t ik_flags;
+
+ uint8_t ik_macaddr[IEEE80211_ADDR_LEN];
+ uint64_t ik_keyrsc; /* key receive sequence counter */
+ uint64_t ik_keytsc; /* key transmit sequence counter */
+
+ uint8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+} wl_key_t;
+#pragma pack()
+
+struct wpa_ess {
+ uint8_t bssid[IEEE80211_ADDR_LEN];
+ uint8_t ssid[MAX_ESSID_LENGTH];
+ uint32_t ssid_len;
+
+ uint8_t wpa_ie[IEEE80211_MAX_WPA_IE];
+ uint32_t wpa_ie_len;
+ int freq;
+};
+
+typedef struct wl_del_key {
+ uint8_t idk_keyix; /* key index */
+ uint8_t idk_macaddr[IEEE80211_ADDR_LEN];
+}wl_del_key_t;
+
+typedef struct wl_countermeasures {
+ uint32_t cm_flag;
+} wl_countermeasures_t;
+
+typedef struct wl_drop_unenc {
+ uint32_t drop_flag;
+} wl_drop_unenc_t;
+
+typedef struct wl_wpa_ess {
+ uint32_t count;
+ struct wpa_ess ess[1];
+} wl_wpa_ess_t;
+
+#define IEEE80211_MLME_ASSOC 1 /* associate station */
+#define IEEE80211_MLME_DISASSOC 2 /* disassociate station */
+#define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */
+#define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */
+#define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */
+
+/*
+ * * MLME state manipulation request. IEEE80211_MLME_ASSOC
+ * * only makes sense when operating as a station. The other
+ * * requests can be used when operating as a station or an
+ * * ap (to effect a station).
+ */
+typedef struct wl_mlme {
+ uint8_t im_op; /* operation to perform */
+ uint16_t im_reason; /* 802.11 reason code */
+ uint8_t im_macaddr[IEEE80211_ADDR_LEN];
+} wl_mlme_t;
+
+/*
+ * State machine events
+ */
+typedef enum {
+ EVENT_ASSOC,
+ EVENT_DISASSOC,
+ EVENT_SCAN_RESULTS
+} wpa_event_type;
+
+typedef struct wl_events {
+ wpa_event_type event;
+} wl_events_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WPA_H */
diff --git a/usr/src/uts/common/sys/dld.h b/usr/src/uts/common/sys/dld.h
index 14363ff219..71555d364b 100644
--- a/usr/src/uts/common/sys/dld.h
+++ b/usr/src/uts/common/sys/dld.h
@@ -123,7 +123,8 @@ typedef struct dld_hold_vlan {
* Secure objects ioctls
*/
typedef enum {
- DLD_SECOBJ_CLASS_WEP = 1
+ DLD_SECOBJ_CLASS_WEP = 1,
+ DLD_SECOBJ_CLASS_WPA
} dld_secobj_class_t;
#define DLD_SECOBJ_OPT_CREATE 0x00000001
diff --git a/usr/src/uts/common/sys/ethernet.h b/usr/src/uts/common/sys/ethernet.h
index 9e35b2ec3d..97531e917c 100644
--- a/usr/src/uts/common/sys/ethernet.h
+++ b/usr/src/uts/common/sys/ethernet.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -92,6 +92,8 @@ struct ether_vlan_extinfo {
#define ETHERTYPE_SLOW (0x8809) /* Slow Protocol */
#define ETHERTYPE_PPPOED (0x8863) /* PPPoE Discovery Stage */
#define ETHERTYPE_PPPOES (0x8864) /* PPPoE Session Stage */
+#define ETHERTYPE_EAPOL (0x888e) /* EAPOL protocol */
+#define ETHERTYPE_RSN_PREAUTH (0x88c7) /* RSN PRE-Authentication */
#define ETHERTYPE_MAX (0xffff) /* Max valid ethernet type */
/*
diff --git a/usr/src/uts/common/sys/mac_wifi.h b/usr/src/uts/common/sys/mac_wifi.h
index 7ed4a9c0a1..a7d5523118 100644
--- a/usr/src/uts/common/sys/mac_wifi.h
+++ b/usr/src/uts/common/sys/mac_wifi.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -48,7 +48,8 @@ extern "C" {
* May change in the future as new features are added.
*/
#define WIFI_HDRSIZE (sizeof (struct ieee80211_frame) + \
- IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + sizeof (struct ieee80211_llc))
+ IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN + \
+ sizeof (struct ieee80211_llc))
enum wifi_stat {
/* statistics described in ieee802.11(5) */
@@ -72,7 +73,8 @@ enum wifi_stat {
*/
enum wifi_secmode {
WIFI_SEC_NONE,
- WIFI_SEC_WEP
+ WIFI_SEC_WEP,
+ WIFI_SEC_WPA
};
/*
diff --git a/usr/src/uts/common/sys/net80211.h b/usr/src/uts/common/sys/net80211.h
index 539e0a3e10..0483b152c0 100644
--- a/usr/src/uts/common/sys/net80211.h
+++ b/usr/src/uts/common/sys/net80211.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -44,6 +44,7 @@
#include <sys/ethernet.h>
#include <sys/net80211_proto.h>
#include <sys/net80211_crypto.h>
+#include <net/wpa.h>
/*
* IEEE802.11 kernel support module
@@ -167,6 +168,9 @@ extern "C" {
#define WME_AC_VI 2 /* video */
#define WME_AC_VO 3 /* voice */
+#define MAX_EVENT 16
+#define MAX_IEEE80211STR 256
+
/*
* Authentication mode.
*/
@@ -308,6 +312,7 @@ struct ieee80211_node {
uint32_t *in_challenge; /* shared-key challenge */
struct ieee80211_key in_ucastkey; /* unicast key */
+ uint8_t *in_wpa_ie; /* captured WPA/RSN ie */
/* others */
int32_t in_fails; /* failure count to associate */
@@ -360,6 +365,12 @@ struct ieee80211com {
/* Cipher state/configuration. */
struct ieee80211_crypto_state ic_crypto;
+ kmutex_t ic_doorlock;
+ char ic_wpadoor[MAX_IEEE80211STR];
+
+ wpa_event_type ic_eventq[MAX_EVENT];
+ uint32_t ic_evq_head, ic_evq_tail;
+
/* Runtime states */
uint32_t ic_flags; /* state/conf flags */
uint32_t ic_flags_ext; /* extended state flags */
@@ -493,6 +504,8 @@ void ieee80211_iterate_nodes(ieee80211_node_table_t *, ieee80211_iter_func *,
void *);
ieee80211_node_t *ieee80211_find_node(ieee80211_node_table_t *,
const uint8_t *);
+ieee80211_node_t *ieee80211_find_node_with_ssid(ieee80211_node_table_t *,
+ const uint8_t *, uint32_t, const uint8_t *);
ieee80211_node_t *ieee80211_find_txnode(ieee80211com_t *,
const uint8_t daddr[IEEE80211_ADDR_LEN]);
ieee80211_node_t *ieee80211_find_rxnode(ieee80211com_t *,
@@ -521,6 +534,9 @@ void ieee80211_watchdog(void *);
void ieee80211_start_watchdog(ieee80211com_t *, uint32_t);
void ieee80211_stop_watchdog(ieee80211com_t *);
+void *ieee80211_malloc(size_t);
+void ieee80211_free(void *);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/net80211_crypto.h b/usr/src/uts/common/sys/net80211_crypto.h
index 89cd81095b..ee6c569bc2 100644
--- a/usr/src/uts/common/sys/net80211_crypto.h
+++ b/usr/src/uts/common/sys/net80211_crypto.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -91,6 +91,17 @@ extern "C" {
#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */
#define IEEE80211_WEP_NKID 4 /* number of key ids */
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP. For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+#define IEEE80211_WEP_EXTIV 0x20
+#define IEEE80211_WEP_EXTIVLEN 4 /* extended IV length */
+#define IEEE80211_WEP_MICLEN 8 /* trailing MIC */
+
#define IEEE80211_WEP_HDRLEN \
(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
#define IEEE80211_WEP_MINLEN \
diff --git a/usr/src/uts/common/sys/net80211_proto.h b/usr/src/uts/common/sys/net80211_proto.h
index 6842798445..8d053fcd42 100644
--- a/usr/src/uts/common/sys/net80211_proto.h
+++ b/usr/src/uts/common/sys/net80211_proto.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -385,6 +385,10 @@ enum {
IEEE80211_ELEMID_VENDOR = 221 /* vendor private */
};
+#define WPA_OUI 0xf25000
+#define WPA_OUI_TYPE 0x01
+#define WPA_VERSION 1 /* current supported version */
+
#define IEEE80211_CHALLENGE_LEN 128
#define IEEE80211_RATE_BASIC 0x80
@@ -466,6 +470,17 @@ enum {
#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */
#define IEEE80211_WEP_NKID 4 /* number of key ids */
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP. For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+#define IEEE80211_WEP_EXTIV 0x20
+#define IEEE80211_WEP_EXTIVLEN 4 /* extended IV length */
+#define IEEE80211_WEP_MICLEN 8 /* trailing MIC */
+
#define IEEE80211_CRC_LEN 4
/*