Mercurial > illumos > illumos-gate
changeset 4126:31652d91f33e
PSARC 2006/046 Wireless WPA Supplicant
PSARC 2007/223 Wireless WPA Supplicant Addendum
6363273 Need access to WPA/TKIP functionality
line wrap: on
line diff
--- a/usr/src/Makefile.lint Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/Makefile.lint Fri Apr 27 09:21:03 2007 -0700 @@ -78,6 +78,7 @@ 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 \
--- a/usr/src/cmd/Makefile Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/cmd/Makefile Fri Apr 27 09:21:03 2007 -0700 @@ -808,6 +808,7 @@ 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 \
--- a/usr/src/cmd/cmd-inet/lib/nwamd/structures.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/cmd/cmd-inet/lib/nwamd/structures.h Fri Apr 27 09:21:03 2007 -0700 @@ -160,9 +160,9 @@ 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; };
--- a/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c Fri Apr 27 09:21:03 2007 -0700 @@ -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 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 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 @@ * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ (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 @@ } /* - * 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 @@ 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 @@ 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 @@ } 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 @@ * 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 =
--- a/usr/src/cmd/cmd-inet/usr.lib/Makefile Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/cmd/cmd-inet/usr.lib/Makefile Fri Apr 27 09:21:03 2007 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile Fri Apr 27 09:21:03 2007 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/README Fri Apr 27 09:21:03 2007 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c Fri Apr 27 09:21:03 2007 -0700 @@ -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 +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.c Fri Apr 27 09:21:03 2007 -0700 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/eloop.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.c Fri Apr 27 09:21:03 2007 -0700 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/l2_packet.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c Fri Apr 27 09:21:03 2007 -0700 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.xml Fri Apr 27 09:21:03 2007 -0700 @@ -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>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.c Fri Apr 27 09:21:03 2007 -0700 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_enc.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c Fri Apr 27 09:21:03 2007 -0700 @@ -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); +}
--- a/usr/src/cmd/dladm/dladm.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/cmd/dladm/dladm.c Fri Apr 27 09:21:03 2007 -0700 @@ -219,7 +219,7 @@ "\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 @@ } 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; - - sp = split(str, DLADM_WLAN_MAX_WEPKEYS, DLADM_WLAN_MAX_WEPKEYNAME_LEN); + dladm_wlan_key_t *wk; + + 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 @@ 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 @@ 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 @@ if (status != DLADM_STATUS_OK) goto fail; } + wk[i].wk_class = class; } *keys = wk; *key_countp = i; @@ -2086,10 +2087,11 @@ 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 @@ 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 @@ break; case 'c': flags |= DLADM_WLAN_CONNECT_CREATEIBSS; + flags |= DLADM_WLAN_CONNECT_CREATEIBSS; break; default: die_opterr(optopt, option); @@ -2168,16 +2174,17 @@ } 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 @@ { 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 @@ 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 @@ 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);
--- a/usr/src/lib/libdladm/Makefile.com Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/lib/libdladm/Makefile.com Fri Apr 27 09:21:03 2007 -0700 @@ -35,7 +35,7 @@ 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)
--- a/usr/src/lib/libdladm/common/libdllink.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/lib/libdladm/common/libdllink.h Fri Apr 27 09:21:03 2007 -0700 @@ -64,6 +64,7 @@ #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 *);
--- a/usr/src/lib/libdladm/common/libdlwlan.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/lib/libdladm/common/libdlwlan.c Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ 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_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 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 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ case DLADM_WLAN_SECMODE_WEP: encryption = WL_ENC_WEP; break; + case DLADM_WLAN_SECMODE_WPA: + return (0); default: return (-1); } @@ -1703,13 +1749,13 @@ } 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 @@ (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); +}
--- a/usr/src/lib/libdladm/common/libdlwlan.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/lib/libdladm/common/libdlwlan.h Fri Apr 27 09:21:03 2007 -0700 @@ -49,6 +49,8 @@ #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 @@ 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 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 @@ #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 @@ 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 *);
--- a/usr/src/lib/libdladm/common/mapfile-vers Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/lib/libdladm/common/mapfile-vers Fri Apr 27 09:21:03 2007 -0700 @@ -91,6 +91,12 @@ 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: *;
--- a/usr/src/lib/libdladm/common/secobj.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/lib/libdladm/common/secobj.c Fri Apr 27 09:21:03 2007 -0700 @@ -51,7 +51,8 @@ } 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 @@ 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);
--- a/usr/src/lib/libsecdb/auth_attr.txt Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/lib/libsecdb/auth_attr.txt Fri Apr 27 09:21:03 2007 -0700 @@ -98,6 +98,7 @@ 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
--- a/usr/src/lib/libsecdb/help/auths/Makefile Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/lib/libsecdb/help/auths/Makefile Fri Apr 27 09:21:03 2007 -0700 @@ -84,6 +84,7 @@ SmfValueHeader.html \ SmfValueNWAM.html \ SmfValueRouting.html \ + SmfWpaStates.html \ NetworkHeader.html \ WifiConfig.html \ WifiWep.html \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/lib/libsecdb/help/auths/SmfWpaStates.html Fri Apr 27 09:21:03 2007 -0700 @@ -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> +</BODY> +</HTML>
--- a/usr/src/lib/libsecdb/prof_attr.txt Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/lib/libsecdb/prof_attr.txt Fri Apr 27 09:21:03 2007 -0700 @@ -49,7 +49,7 @@ 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
--- a/usr/src/lib/libsecdb/user_attr.txt Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/lib/libsecdb/user_attr.txt Fri Apr 27 09:21:03 2007 -0700 @@ -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
--- a/usr/src/pkgdefs/Makefile Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/pkgdefs/Makefile Fri Apr 27 09:21:03 2007 -0700 @@ -381,6 +381,8 @@ SUNWxcu4 \ SUNWwlanr \ SUNWwlanu \ + SUNWwpar \ + SUNWwpau \ SUNWxcu6 \ SUNWxwdv \ SUNWpmr \
--- a/usr/src/pkgdefs/SUNW0on/prototype_com Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/pkgdefs/SUNW0on/prototype_com Fri Apr 27 09:21:03 2007 -0700 @@ -244,6 +244,7 @@ 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
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/pkgdefs/SUNWcsu/prototype_com Fri Apr 27 09:21:03 2007 -0700 @@ -490,6 +490,7 @@ 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpar/Makefile Fri Apr 27 09:21:03 2007 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpar/depend Fri Apr 27 09:21:03 2007 -0700 @@ -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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpar/pkginfo.tmpl Fri Apr 27 09:21:03 2007 -0700 @@ -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>"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpar/prototype_com Fri Apr 27 09:21:03 2007 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpar/prototype_i386 Fri Apr 27 09:21:03 2007 -0700 @@ -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 +#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpar/prototype_sparc Fri Apr 27 09:21:03 2007 -0700 @@ -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 +# +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpau/Makefile Fri Apr 27 09:21:03 2007 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpau/depend Fri Apr 27 09:21:03 2007 -0700 @@ -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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpau/pkginfo.tmpl Fri Apr 27 09:21:03 2007 -0700 @@ -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>"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpau/prototype_com Fri Apr 27 09:21:03 2007 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpau/prototype_i386 Fri Apr 27 09:21:03 2007 -0700 @@ -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 +#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWwpau/prototype_sparc Fri Apr 27 09:21:03 2007 -0700 @@ -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 +#
--- a/usr/src/pkgdefs/etc/exception_list_i386 Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/pkgdefs/etc/exception_list_i386 Fri Apr 27 09:21:03 2007 -0700 @@ -613,6 +613,7 @@ # 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 #
--- a/usr/src/pkgdefs/etc/exception_list_sparc Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/pkgdefs/etc/exception_list_sparc Fri Apr 27 09:21:03 2007 -0700 @@ -614,6 +614,7 @@ # 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 #
--- a/usr/src/uts/common/Makefile.files Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/Makefile.files Fri Apr 27 09:21:03 2007 -0700 @@ -562,7 +562,8 @@ 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
--- a/usr/src/uts/common/inet/wifi_ioctl.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/inet/wifi_ioctl.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ #define WL_NOENCRYPTION 0x0 #define WL_ENC_WEP 0x1 +#define WL_ENC_WPA 0x2 #define WL_OPENSYSTEM 0x1 #define WL_SHAREDKEY 0x2
--- a/usr/src/uts/common/io/ath/ath_aux.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/ath/ath_aux.c Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ } /* + * 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,17 +664,140 @@ ath_key_alloc(ieee80211com_t *ic, const struct ieee80211_key *k, ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) { - *keyix = *rxkeyix = 0; + 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) +{ + 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_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); } -int -ath_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k) +static void +ath_keyprint(const char *tag, uint_t ix, + const HAL_KEYVAL *hk, const uint8_t mac[IEEE80211_ADDR_LEN]) { - struct ath_hal *ah = ((ath_t *)ic)->asc_ah; + 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)); +} - ATH_HAL_KEYRESET(ah, k->wk_keyix); - return (1); +/* + * 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 } /* @@ -612,7 +813,6 @@ 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 @@ 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)); + } } /*
--- a/usr/src/uts/common/io/ath/ath_impl.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/ath/ath_impl.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ #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 @@ 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);
--- a/usr/src/uts/common/io/ath/ath_main.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/ath/ath_main.c Fri Apr 27 09:21:03 2007 -0700 @@ -668,7 +668,7 @@ * 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_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 @@ 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 @@ } } 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 @@ "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 @@ 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 @@ 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 @@ 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 */ };
--- a/usr/src/uts/common/io/dld/dld_drv.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/dld/dld_drv.c Fri Apr 27 09:21:03 2007 -0700 @@ -726,7 +726,8 @@ 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 @@ failed: ASSERT(err != 0); miocnak(q, mp, 0, err); - } typedef struct dld_secobj_state {
--- a/usr/src/uts/common/io/ipw/ipw2100.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/ipw/ipw2100.c Fri Apr 27 09:21:03 2007 -0700 @@ -1530,7 +1530,6 @@ 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;
--- a/usr/src/uts/common/io/mac/plugins/mac_wifi.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/mac/plugins/mac_wifi.c Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ 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 @@ * 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 @@ 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 @@ * 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);
--- a/usr/src/uts/common/io/net80211/net80211.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/net80211/net80211.c Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ } /* + * 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 @@ { 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 @@ { 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 @@ 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_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 = {
--- a/usr/src/uts/common/io/net80211/net80211_crypto.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/net80211/net80211_crypto.c Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ 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 @@ 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 @@ 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_delglobalkeys(ic); ieee80211_crypto_unregister(&wep); + ieee80211_crypto_unregister(&tkip); + ieee80211_crypto_unregister(&ccmp); } /*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c Fri Apr 27 09:21:03 2007 -0700 @@ -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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c Fri Apr 27 09:21:03 2007 -0700 @@ -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); +}
--- a/usr/src/uts/common/io/net80211/net80211_impl.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/net80211/net80211_impl.h Fri Apr 27 09:21:03 2007 -0700 @@ -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_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 *);
--- a/usr/src/uts/common/io/net80211/net80211_input.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/net80211/net80211_input.c Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ } } +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 @@ 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,"
--- a/usr/src/uts/common/io/net80211/net80211_ioctl.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/net80211/net80211_ioctl.c Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ 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 @@ (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 @@ 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 @@ 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 @@ 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 @@ 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;
--- a/usr/src/uts/common/io/net80211/net80211_node.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/net80211/net80211_node.c Fri Apr 27 09:21:03 2007 -0700 @@ -382,7 +382,7 @@ */ 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_node_t *selbs; ieee80211_cancel_scan(ic); + /* notify SCAN done */ + ieee80211_notify(ic, EVENT_SCAN_RESULTS); IEEE80211_LOCK(ic); /* @@ -547,7 +549,7 @@ */ /* 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 @@ 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 @@ } 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 @@ (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 @@ * 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 @@ 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 @@ } /* + * 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 @@ 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 @@ * 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,
--- a/usr/src/uts/common/io/net80211/net80211_output.c Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/io/net80211/net80211_output.c Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ 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 @@ 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); }
--- a/usr/src/uts/common/net/Makefile Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/net/Makefile Fri Apr 27 09:21:03 2007 -0700 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/net/wpa.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 */
--- a/usr/src/uts/common/sys/dld.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/sys/dld.h Fri Apr 27 09:21:03 2007 -0700 @@ -123,7 +123,8 @@ * 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
--- a/usr/src/uts/common/sys/ethernet.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/sys/ethernet.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ #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 */ /*
--- a/usr/src/uts/common/sys/mac_wifi.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/sys/mac_wifi.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ * 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_secmode { WIFI_SEC_NONE, - WIFI_SEC_WEP + WIFI_SEC_WEP, + WIFI_SEC_WPA }; /*
--- a/usr/src/uts/common/sys/net80211.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/sys/net80211.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ #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 @@ 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 @@ /* 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_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_start_watchdog(ieee80211com_t *, uint32_t); void ieee80211_stop_watchdog(ieee80211com_t *); +void *ieee80211_malloc(size_t); +void ieee80211_free(void *); + #ifdef __cplusplus } #endif
--- a/usr/src/uts/common/sys/net80211_crypto.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/sys/net80211_crypto.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ #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 \
--- a/usr/src/uts/common/sys/net80211_proto.h Fri Apr 27 08:49:43 2007 -0700 +++ b/usr/src/uts/common/sys/net80211_proto.h Fri Apr 27 09:21:03 2007 -0700 @@ -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 @@ 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 @@ #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 /*