1526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt/*
2526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * wpa_supplicant - WPA/RSN IE and KDE processing
3526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
4526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt *
5526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * This program is free software; you can redistribute it and/or modify
6526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * it under the terms of the GNU General Public License version 2 as
7526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * published by the Free Software Foundation.
8526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt *
9526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * Alternatively, this software may be distributed under the terms of BSD
10526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * license.
11526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt *
12526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * See README and COPYING for more details.
13526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt */
14526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
15526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#include "includes.h"
16526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
17526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#include "common.h"
18526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#include "wpa.h"
19526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#include "pmksa_cache.h"
20526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#include "ieee802_11_defs.h"
21526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#include "wpa_i.h"
22526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#include "wpa_ie.h"
23526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
24526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
25526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidtstatic int wpa_selector_to_bitfield(const u8 *s)
26526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt{
27526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
28526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return WPA_CIPHER_NONE;
29526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
30526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return WPA_CIPHER_WEP40;
31526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
32526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return WPA_CIPHER_TKIP;
33526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
34526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return WPA_CIPHER_CCMP;
35526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
36526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return WPA_CIPHER_WEP104;
37526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	return 0;
38526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt}
39526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
40526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
41526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidtstatic int wpa_key_mgmt_to_bitfield(const u8 *s)
42526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt{
43526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
44526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return WPA_KEY_MGMT_IEEE8021X;
45526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
46526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return WPA_KEY_MGMT_PSK;
47526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
48526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return WPA_KEY_MGMT_WPA_NONE;
49526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	return 0;
50526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt}
51526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
52526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
53526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidtstatic int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
54526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt				struct wpa_ie_data *data)
55526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt{
56526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	const struct wpa_ie_hdr *hdr;
57526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	const u8 *pos;
58526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	int left;
59526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	int i, count;
60526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
61526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	os_memset(data, 0, sizeof(*data));
62526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	data->proto = WPA_PROTO_WPA;
63526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	data->pairwise_cipher = WPA_CIPHER_TKIP;
64526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	data->group_cipher = WPA_CIPHER_TKIP;
65526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
66526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	data->capabilities = 0;
67526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	data->pmkid = NULL;
68526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	data->num_pmkid = 0;
69526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	data->mgmt_group_cipher = 0;
70526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
71526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (wpa_ie_len == 0) {
72526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		/* No WPA IE - fail silently */
73526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
74526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
75526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
76526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
77526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
78526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   __func__, (unsigned long) wpa_ie_len);
79526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
80526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
81526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
82526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	hdr = (const struct wpa_ie_hdr *) wpa_ie;
83526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
84526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
85526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    hdr->len != wpa_ie_len - 2 ||
86526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
87526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    WPA_GET_LE16(hdr->version) != WPA_VERSION) {
88526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
89526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   __func__);
90526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
91526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
92526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
93526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	pos = (const u8 *) (hdr + 1);
94526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	left = wpa_ie_len - sizeof(*hdr);
95526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
96526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (left >= WPA_SELECTOR_LEN) {
97526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		data->group_cipher = wpa_selector_to_bitfield(pos);
98526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		pos += WPA_SELECTOR_LEN;
99526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		left -= WPA_SELECTOR_LEN;
100526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (left > 0) {
101526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
102526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   __func__, left);
103526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
104526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
105526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
106526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (left >= 2) {
107526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		data->pairwise_cipher = 0;
108526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		count = WPA_GET_LE16(pos);
109526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		pos += 2;
110526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		left -= 2;
111526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		if (count == 0 || left < count * WPA_SELECTOR_LEN) {
112526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
113526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt				   "count %u left %u", __func__, count, left);
114526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			return -1;
115526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		}
116526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		for (i = 0; i < count; i++) {
117526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
118526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			pos += WPA_SELECTOR_LEN;
119526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			left -= WPA_SELECTOR_LEN;
120526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		}
121526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (left == 1) {
122526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
123526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   __func__);
124526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
125526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
126526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
127526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (left >= 2) {
128526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		data->key_mgmt = 0;
129526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		count = WPA_GET_LE16(pos);
130526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		pos += 2;
131526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		left -= 2;
132526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		if (count == 0 || left < count * WPA_SELECTOR_LEN) {
133526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
134526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt				   "count %u left %u", __func__, count, left);
135526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			return -1;
136526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		}
137526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		for (i = 0; i < count; i++) {
138526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
139526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			pos += WPA_SELECTOR_LEN;
140526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			left -= WPA_SELECTOR_LEN;
141526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		}
142526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (left == 1) {
143526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
144526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   __func__);
145526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
146526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
147526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
148526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (left >= 2) {
149526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		data->capabilities = WPA_GET_LE16(pos);
150526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		pos += 2;
151526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		left -= 2;
152526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
153526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
154526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (left > 0) {
155526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
156526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   __func__, left);
157526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
158526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
159526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	return 0;
160526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt}
161526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
162526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
163526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt/**
164526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * wpa_parse_wpa_ie - Parse WPA/RSN IE
165526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @wpa_ie: Pointer to WPA or RSN IE
166526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @wpa_ie_len: Length of the WPA/RSN IE
167526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @data: Pointer to data area for parsing results
168526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * Returns: 0 on success, -1 on failure
169526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt *
170526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * Parse the contents of WPA or RSN IE and write the parsed data into data.
171526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt */
172526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidtint wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
173526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		     struct wpa_ie_data *data)
174526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt{
175526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN)
176526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
177526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	else
178526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
179526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt}
180526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
181526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
182526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidtstatic int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
183526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			      int pairwise_cipher, int group_cipher,
184526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			      int key_mgmt)
185526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt{
186526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	u8 *pos;
187526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	struct wpa_ie_hdr *hdr;
188526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
189526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
190526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
191526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
192526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
193526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	hdr = (struct wpa_ie_hdr *) wpa_ie;
194526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
195526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
196526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	WPA_PUT_LE16(hdr->version, WPA_VERSION);
197526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	pos = (u8 *) (hdr + 1);
198526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
199526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (group_cipher == WPA_CIPHER_CCMP) {
200526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
201526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (group_cipher == WPA_CIPHER_TKIP) {
202526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
203526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (group_cipher == WPA_CIPHER_WEP104) {
204526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
205526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (group_cipher == WPA_CIPHER_WEP40) {
206526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
207526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else {
208526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
209526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   group_cipher);
210526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
211526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
212526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	pos += WPA_SELECTOR_LEN;
213526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
214526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	*pos++ = 1;
215526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	*pos++ = 0;
216526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pairwise_cipher == WPA_CIPHER_CCMP) {
217526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
218526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (pairwise_cipher == WPA_CIPHER_TKIP) {
219526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
220526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (pairwise_cipher == WPA_CIPHER_NONE) {
221526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
222526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else {
223526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
224526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   pairwise_cipher);
225526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
226526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
227526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	pos += WPA_SELECTOR_LEN;
228526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
229526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	*pos++ = 1;
230526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	*pos++ = 0;
231526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
232526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
233526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
234526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
235526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
236526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
237526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else {
238526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
239526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   key_mgmt);
240526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
241526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
242526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	pos += WPA_SELECTOR_LEN;
243526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
244526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	/* WPA Capabilities; use defaults, so no need to include it */
245526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
246526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	hdr->len = (pos - wpa_ie) - 2;
247526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
248526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
249526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
250526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	return pos - wpa_ie;
251526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt}
252526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
253526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
254526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidtstatic int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
255526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			      int pairwise_cipher, int group_cipher,
256526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			      int key_mgmt, int mgmt_group_cipher,
257526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			      struct wpa_sm *sm)
258526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt{
259526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#ifndef CONFIG_NO_WPA2
260526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	u8 *pos;
261526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	struct rsn_ie_hdr *hdr;
262526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	u16 capab;
263526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
264526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
265526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
266526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) {
267526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)",
268526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   (unsigned long) rsn_ie_len);
269526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
270526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
271526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
272526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	hdr = (struct rsn_ie_hdr *) rsn_ie;
273526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	hdr->elem_id = WLAN_EID_RSN;
274526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	WPA_PUT_LE16(hdr->version, RSN_VERSION);
275526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	pos = (u8 *) (hdr + 1);
276526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
277526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (group_cipher == WPA_CIPHER_CCMP) {
278526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
279526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (group_cipher == WPA_CIPHER_TKIP) {
280526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
281526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (group_cipher == WPA_CIPHER_WEP104) {
282526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
283526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (group_cipher == WPA_CIPHER_WEP40) {
284526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
285526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else {
286526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
287526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   group_cipher);
288526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
289526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
290526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	pos += RSN_SELECTOR_LEN;
291526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
292526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	*pos++ = 1;
293526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	*pos++ = 0;
294526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pairwise_cipher == WPA_CIPHER_CCMP) {
295526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
296526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (pairwise_cipher == WPA_CIPHER_TKIP) {
297526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
298526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (pairwise_cipher == WPA_CIPHER_NONE) {
299526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
300526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else {
301526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
302526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   pairwise_cipher);
303526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
304526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
305526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	pos += RSN_SELECTOR_LEN;
306526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
307526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	*pos++ = 1;
308526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	*pos++ = 0;
309526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
310526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
311526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
312526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
313526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#ifdef CONFIG_IEEE80211R
314526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
315526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
316526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
317526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
318526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#endif /* CONFIG_IEEE80211R */
319526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#ifdef CONFIG_IEEE80211W
320526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) {
321526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
322526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
323526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
324526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#endif /* CONFIG_IEEE80211W */
325526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	} else {
326526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
327526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			   key_mgmt);
328526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return -1;
329526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
330526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	pos += RSN_SELECTOR_LEN;
331526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
332526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	/* RSN Capabilities */
333526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	capab = 0;
334526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#ifdef CONFIG_IEEE80211W
335526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
336526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		capab |= WPA_CAPABILITY_MFPC;
337526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#endif /* CONFIG_IEEE80211W */
338526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	WPA_PUT_LE16(pos, capab);
339526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	pos += 2;
340526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
341526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (sm->cur_pmksa) {
342526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		/* PMKID Count (2 octets, little endian) */
343526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		*pos++ = 1;
344526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		*pos++ = 0;
345526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		/* PMKID */
346526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
347526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		pos += PMKID_LEN;
348526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
349526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
350526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#ifdef CONFIG_IEEE80211W
351526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
352526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		if (!sm->cur_pmksa) {
353526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			/* PMKID Count */
354526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			WPA_PUT_LE16(pos, 0);
355526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			pos += 2;
356526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		}
357526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
358526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		/* Management Group Cipher Suite */
359526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
360526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		pos += RSN_SELECTOR_LEN;
361526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
362526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#endif /* CONFIG_IEEE80211W */
363526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
364526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	hdr->len = (pos - rsn_ie) - 2;
365526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
366526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len);
367526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
368526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	return pos - rsn_ie;
369526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#else /* CONFIG_NO_WPA2 */
370526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	return -1;
371526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#endif /* CONFIG_NO_WPA2 */
372526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt}
373526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
374526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
375526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt/**
376526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
377526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @sm: Pointer to WPA state machine data from wpa_sm_init()
378526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE
379526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @wpa_ie_len: Maximum length of the generated WPA/RSN IE
380526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * Returns: Length of the generated WPA/RSN IE or -1 on failure
381526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt */
382526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidtint wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
383526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt{
384526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (sm->proto == WPA_PROTO_RSN)
385526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len,
386526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt					  sm->pairwise_cipher,
387526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt					  sm->group_cipher,
388526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt					  sm->key_mgmt, sm->mgmt_group_cipher,
389526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt					  sm);
390526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	else
391526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
392526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt					  sm->pairwise_cipher,
393526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt					  sm->group_cipher,
394526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt					  sm->key_mgmt);
395526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt}
396526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
397526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
398526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt/**
399526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
400526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @pos: Pointer to the IE header
401526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @end: Pointer to the end of the Key Data buffer
402526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @ie: Pointer to parsed IE data
403526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * Returns: 0 on success, 1 if end mark is found, -1 on failure
404526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt */
405526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidtstatic int wpa_parse_generic(const u8 *pos, const u8 *end,
406526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			     struct wpa_eapol_ie_parse *ie)
407526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt{
408526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pos[1] == 0)
409526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return 1;
410526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
411526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pos[1] >= 6 &&
412526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
413526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    pos[2 + WPA_SELECTOR_LEN] == 1 &&
414526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
415526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->wpa_ie = pos;
416526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->wpa_ie_len = pos[1] + 2;
417526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return 0;
418526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
419526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
420526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pos + 1 + RSN_SELECTOR_LEN < end &&
421526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
422526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
423526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
424526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return 0;
425526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
426526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
427526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
428526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
429526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
430526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
431526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return 0;
432526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
433526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
434526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
435526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
436526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
437526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
438526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return 0;
439526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
440526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
441526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#ifdef CONFIG_PEERKEY
442526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
443526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
444526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->smk = pos + 2 + RSN_SELECTOR_LEN;
445526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
446526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return 0;
447526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
448526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
449526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
450526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
451526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
452526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
453526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return 0;
454526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
455526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
456526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
457526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
458526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
459526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
460526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return 0;
461526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
462526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
463526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
464526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
465526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->error = pos + 2 + RSN_SELECTOR_LEN;
466526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->error_len = pos[1] - RSN_SELECTOR_LEN;
467526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return 0;
468526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
469526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#endif /* CONFIG_PEERKEY */
470526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
471526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#ifdef CONFIG_IEEE80211W
472526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
473526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
474526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
475526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
476526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		return 0;
477526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
478526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#endif /* CONFIG_IEEE80211W */
479526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
480526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	return 0;
481526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt}
482526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
483526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
484526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt/**
485526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs
486526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @buf: Pointer to the Key Data buffer
487526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @len: Key Data Length
488526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * @ie: Pointer to parsed IE data
489526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt * Returns: 0 on success, -1 on failure
490526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt */
491526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidtint wpa_supplicant_parse_ies(const u8 *buf, size_t len,
492526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			     struct wpa_eapol_ie_parse *ie)
493526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt{
494526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	const u8 *pos, *end;
495526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	int ret = 0;
496526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
497526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	os_memset(ie, 0, sizeof(*ie));
498526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
499526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		if (pos[0] == 0xdd &&
500526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		    ((pos == buf + len - 1) || pos[1] == 0)) {
501526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			/* Ignore padding */
502526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			break;
503526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		}
504526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		if (pos + 2 + pos[1] > end) {
505526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
506526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt				   "underflow (ie=%d len=%d pos=%d)",
507526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt				   pos[0], pos[1], (int) (pos - buf));
508526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
509526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt					buf, len);
510526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			ret = -1;
511526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			break;
512526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		}
513526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		if (*pos == WLAN_EID_RSN) {
514526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			ie->rsn_ie = pos;
515526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			ie->rsn_ie_len = pos[1] + 2;
516526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#ifdef CONFIG_IEEE80211R
517526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
518526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			ie->mdie = pos;
519526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			ie->mdie_len = pos[1] + 2;
520526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt#endif /* CONFIG_IEEE80211R */
521526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
522526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			ret = wpa_parse_generic(pos, end, ie);
523526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			if (ret < 0)
524526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt				break;
525526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			if (ret > 0) {
526526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt				ret = 0;
527526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt				break;
528526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			}
529526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		} else {
530526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt			wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
531526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt				    "Key Data IE", pos, 2 + pos[1]);
532526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt		}
533526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	}
534526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt
535526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt	return ret;
536526fc2a7dc09b4450086cdec313a5c44d36b10fdDmitry Shmidt}
537