1e28a4053SRui Paulo /*
2e28a4053SRui Paulo * hostapd - IEEE 802.11r - Fast BSS Transition
385732ac8SCy Schubert * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
7e28a4053SRui Paulo */
8e28a4053SRui Paulo
9e28a4053SRui Paulo #include "utils/includes.h"
10e28a4053SRui Paulo
11e28a4053SRui Paulo #include "utils/common.h"
125b9c547cSRui Paulo #include "utils/eloop.h"
135b9c547cSRui Paulo #include "utils/list.h"
14e28a4053SRui Paulo #include "common/ieee802_11_defs.h"
15e28a4053SRui Paulo #include "common/ieee802_11_common.h"
164bc52338SCy Schubert #include "common/ocv.h"
17c1d255d3SCy Schubert #include "common/wpa_ctrl.h"
184bc52338SCy Schubert #include "drivers/driver.h"
1985732ac8SCy Schubert #include "crypto/aes.h"
2085732ac8SCy Schubert #include "crypto/aes_siv.h"
21e28a4053SRui Paulo #include "crypto/aes_wrap.h"
2285732ac8SCy Schubert #include "crypto/sha384.h"
23*a90b9d01SCy Schubert #include "crypto/sha512.h"
24f05cddf9SRui Paulo #include "crypto/random.h"
25e28a4053SRui Paulo #include "ap_config.h"
26e28a4053SRui Paulo #include "ieee802_11.h"
27e28a4053SRui Paulo #include "wmm.h"
28e28a4053SRui Paulo #include "wpa_auth.h"
29e28a4053SRui Paulo #include "wpa_auth_i.h"
30206b73d0SCy Schubert #include "pmksa_cache_auth.h"
31e28a4053SRui Paulo
32e28a4053SRui Paulo
3385732ac8SCy Schubert #ifdef CONFIG_IEEE80211R_AP
3485732ac8SCy Schubert
3585732ac8SCy Schubert const unsigned int ftRRBseqTimeout = 10;
3685732ac8SCy Schubert const unsigned int ftRRBmaxQueueLen = 100;
3785732ac8SCy Schubert
38*a90b9d01SCy Schubert /* TODO: make these configurable */
39*a90b9d01SCy Schubert static const int dot11RSNAConfigPMKLifetime = 43200;
40*a90b9d01SCy Schubert
41e28a4053SRui Paulo
425b9c547cSRui Paulo static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
435b9c547cSRui Paulo const u8 *current_ap, const u8 *sta_addr,
445b9c547cSRui Paulo u16 status, const u8 *resp_ies,
455b9c547cSRui Paulo size_t resp_ies_len);
4685732ac8SCy Schubert static void ft_finish_pull(struct wpa_state_machine *sm);
4785732ac8SCy Schubert static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx);
4885732ac8SCy Schubert static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
495b9c547cSRui Paulo
5085732ac8SCy Schubert struct tlv_list {
5185732ac8SCy Schubert u16 type;
5285732ac8SCy Schubert size_t len;
5385732ac8SCy Schubert const u8 *data;
5485732ac8SCy Schubert };
5585732ac8SCy Schubert
5685732ac8SCy Schubert
5785732ac8SCy Schubert /**
5885732ac8SCy Schubert * wpa_ft_rrb_decrypt - Decrypt FT RRB message
5985732ac8SCy Schubert * @key: AES-SIV key for AEAD
6085732ac8SCy Schubert * @key_len: Length of key in octets
6185732ac8SCy Schubert * @enc: Pointer to encrypted TLVs
6285732ac8SCy Schubert * @enc_len: Length of encrypted TLVs in octets
6385732ac8SCy Schubert * @auth: Pointer to authenticated TLVs
6485732ac8SCy Schubert * @auth_len: Length of authenticated TLVs in octets
6585732ac8SCy Schubert * @src_addr: MAC address of the frame sender
6685732ac8SCy Schubert * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
6785732ac8SCy Schubert * @plain: Pointer to return the pointer to the allocated plaintext buffer;
6885732ac8SCy Schubert * needs to be freed by the caller if not NULL;
6985732ac8SCy Schubert * will only be returned on success
7085732ac8SCy Schubert * @plain_len: Pointer to return the length of the allocated plaintext buffer
7185732ac8SCy Schubert * in octets
7285732ac8SCy Schubert * Returns: 0 on success, -1 on error
7385732ac8SCy Schubert */
wpa_ft_rrb_decrypt(const u8 * key,const size_t key_len,const u8 * enc,size_t enc_len,const u8 * auth,const size_t auth_len,const u8 * src_addr,u8 type,u8 ** plain,size_t * plain_size)7485732ac8SCy Schubert static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
754bc52338SCy Schubert const u8 *enc, size_t enc_len,
7685732ac8SCy Schubert const u8 *auth, const size_t auth_len,
7785732ac8SCy Schubert const u8 *src_addr, u8 type,
7885732ac8SCy Schubert u8 **plain, size_t *plain_size)
7985732ac8SCy Schubert {
8085732ac8SCy Schubert const u8 *ad[3] = { src_addr, auth, &type };
8185732ac8SCy Schubert size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
8285732ac8SCy Schubert
834bc52338SCy Schubert wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
844bc52338SCy Schubert MAC2STR(src_addr), type);
8585732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
864bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len);
874bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
8885732ac8SCy Schubert
8985732ac8SCy Schubert if (!key) { /* skip decryption */
9085732ac8SCy Schubert *plain = os_memdup(enc, enc_len);
9185732ac8SCy Schubert if (enc_len > 0 && !*plain)
9285732ac8SCy Schubert goto err;
9385732ac8SCy Schubert
9485732ac8SCy Schubert *plain_size = enc_len;
9585732ac8SCy Schubert
9685732ac8SCy Schubert return 0;
9785732ac8SCy Schubert }
9885732ac8SCy Schubert
9985732ac8SCy Schubert *plain = NULL;
10085732ac8SCy Schubert
10185732ac8SCy Schubert /* SIV overhead */
10285732ac8SCy Schubert if (enc_len < AES_BLOCK_SIZE)
10385732ac8SCy Schubert goto err;
10485732ac8SCy Schubert
10585732ac8SCy Schubert *plain = os_zalloc(enc_len - AES_BLOCK_SIZE);
10685732ac8SCy Schubert if (!*plain)
10785732ac8SCy Schubert goto err;
10885732ac8SCy Schubert
10985732ac8SCy Schubert if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
1104bc52338SCy Schubert *plain) < 0) {
1114bc52338SCy Schubert if (enc_len < AES_BLOCK_SIZE + 2)
1124bc52338SCy Schubert goto err;
1134bc52338SCy Schubert
1144bc52338SCy Schubert /* Try to work around Ethernet devices that add extra
1154bc52338SCy Schubert * two octet padding even if the frame is longer than
1164bc52338SCy Schubert * the minimum Ethernet frame. */
1174bc52338SCy Schubert enc_len -= 2;
1184bc52338SCy Schubert if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
11985732ac8SCy Schubert *plain) < 0)
12085732ac8SCy Schubert goto err;
1214bc52338SCy Schubert }
12285732ac8SCy Schubert
12385732ac8SCy Schubert *plain_size = enc_len - AES_BLOCK_SIZE;
12485732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs",
12585732ac8SCy Schubert *plain, *plain_size);
12685732ac8SCy Schubert return 0;
12785732ac8SCy Schubert err:
12885732ac8SCy Schubert os_free(*plain);
12985732ac8SCy Schubert *plain = NULL;
13085732ac8SCy Schubert *plain_size = 0;
13185732ac8SCy Schubert
13285732ac8SCy Schubert wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt");
13385732ac8SCy Schubert
13485732ac8SCy Schubert return -1;
13585732ac8SCy Schubert }
13685732ac8SCy Schubert
13785732ac8SCy Schubert
13885732ac8SCy Schubert /* get first tlv record in packet matching type
13985732ac8SCy Schubert * @data (decrypted) packet
14085732ac8SCy Schubert * @return 0 on success else -1
14185732ac8SCy Schubert */
wpa_ft_rrb_get_tlv(const u8 * plain,size_t plain_len,u16 type,size_t * tlv_len,const u8 ** tlv_data)14285732ac8SCy Schubert static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len,
14385732ac8SCy Schubert u16 type, size_t *tlv_len, const u8 **tlv_data)
14485732ac8SCy Schubert {
14585732ac8SCy Schubert const struct ft_rrb_tlv *f;
14685732ac8SCy Schubert size_t left;
14785732ac8SCy Schubert le16 type16;
14885732ac8SCy Schubert size_t len;
14985732ac8SCy Schubert
15085732ac8SCy Schubert left = plain_len;
15185732ac8SCy Schubert type16 = host_to_le16(type);
15285732ac8SCy Schubert
15385732ac8SCy Schubert while (left >= sizeof(*f)) {
15485732ac8SCy Schubert f = (const struct ft_rrb_tlv *) plain;
15585732ac8SCy Schubert
15685732ac8SCy Schubert left -= sizeof(*f);
15785732ac8SCy Schubert plain += sizeof(*f);
15885732ac8SCy Schubert len = le_to_host16(f->len);
15985732ac8SCy Schubert
16085732ac8SCy Schubert if (left < len) {
16185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
16285732ac8SCy Schubert break;
16385732ac8SCy Schubert }
16485732ac8SCy Schubert
16585732ac8SCy Schubert if (f->type == type16) {
16685732ac8SCy Schubert *tlv_len = len;
16785732ac8SCy Schubert *tlv_data = plain;
16885732ac8SCy Schubert return 0;
16985732ac8SCy Schubert }
17085732ac8SCy Schubert
17185732ac8SCy Schubert left -= len;
17285732ac8SCy Schubert plain += len;
17385732ac8SCy Schubert }
17485732ac8SCy Schubert
17585732ac8SCy Schubert return -1;
17685732ac8SCy Schubert }
17785732ac8SCy Schubert
17885732ac8SCy Schubert
wpa_ft_rrb_dump(const u8 * plain,const size_t plain_len)17985732ac8SCy Schubert static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len)
18085732ac8SCy Schubert {
18185732ac8SCy Schubert const struct ft_rrb_tlv *f;
18285732ac8SCy Schubert size_t left;
18385732ac8SCy Schubert size_t len;
18485732ac8SCy Schubert
18585732ac8SCy Schubert left = plain_len;
18685732ac8SCy Schubert
18785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: RRB dump message");
18885732ac8SCy Schubert while (left >= sizeof(*f)) {
18985732ac8SCy Schubert f = (const struct ft_rrb_tlv *) plain;
19085732ac8SCy Schubert
19185732ac8SCy Schubert left -= sizeof(*f);
19285732ac8SCy Schubert plain += sizeof(*f);
19385732ac8SCy Schubert len = le_to_host16(f->len);
19485732ac8SCy Schubert
19585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu",
19685732ac8SCy Schubert le_to_host16(f->type), len);
19785732ac8SCy Schubert
19885732ac8SCy Schubert if (left < len) {
19985732ac8SCy Schubert wpa_printf(MSG_DEBUG,
20085732ac8SCy Schubert "FT: RRB message truncated: left %zu bytes, need %zu",
20185732ac8SCy Schubert left, len);
20285732ac8SCy Schubert break;
20385732ac8SCy Schubert }
20485732ac8SCy Schubert
20585732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len);
20685732ac8SCy Schubert
20785732ac8SCy Schubert left -= len;
20885732ac8SCy Schubert plain += len;
20985732ac8SCy Schubert }
21085732ac8SCy Schubert
21185732ac8SCy Schubert if (left > 0)
21285732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left);
21385732ac8SCy Schubert
21485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: RRB dump message end");
21585732ac8SCy Schubert }
21685732ac8SCy Schubert
21785732ac8SCy Schubert
cmp_int(const void * a,const void * b)21885732ac8SCy Schubert static int cmp_int(const void *a, const void *b)
21985732ac8SCy Schubert {
22085732ac8SCy Schubert int x, y;
22185732ac8SCy Schubert
22285732ac8SCy Schubert x = *((int *) a);
22385732ac8SCy Schubert y = *((int *) b);
22485732ac8SCy Schubert return x - y;
22585732ac8SCy Schubert }
22685732ac8SCy Schubert
22785732ac8SCy Schubert
wpa_ft_rrb_get_tlv_vlan(const u8 * plain,const size_t plain_len,struct vlan_description * vlan)22885732ac8SCy Schubert static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len,
22985732ac8SCy Schubert struct vlan_description *vlan)
23085732ac8SCy Schubert {
23185732ac8SCy Schubert struct ft_rrb_tlv *f;
23285732ac8SCy Schubert size_t left;
23385732ac8SCy Schubert size_t len;
23485732ac8SCy Schubert int taggedidx;
23585732ac8SCy Schubert int vlan_id;
23685732ac8SCy Schubert int type;
23785732ac8SCy Schubert
23885732ac8SCy Schubert left = plain_len;
23985732ac8SCy Schubert taggedidx = 0;
24085732ac8SCy Schubert os_memset(vlan, 0, sizeof(*vlan));
24185732ac8SCy Schubert
24285732ac8SCy Schubert while (left >= sizeof(*f)) {
24385732ac8SCy Schubert f = (struct ft_rrb_tlv *) plain;
24485732ac8SCy Schubert
24585732ac8SCy Schubert left -= sizeof(*f);
24685732ac8SCy Schubert plain += sizeof(*f);
24785732ac8SCy Schubert
24885732ac8SCy Schubert len = le_to_host16(f->len);
24985732ac8SCy Schubert type = le_to_host16(f->type);
25085732ac8SCy Schubert
25185732ac8SCy Schubert if (left < len) {
25285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
25385732ac8SCy Schubert return -1;
25485732ac8SCy Schubert }
25585732ac8SCy Schubert
25685732ac8SCy Schubert if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED)
25785732ac8SCy Schubert goto skip;
25885732ac8SCy Schubert
25985732ac8SCy Schubert if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) {
26085732ac8SCy Schubert wpa_printf(MSG_DEBUG,
26185732ac8SCy Schubert "FT: RRB VLAN_UNTAGGED invalid length");
26285732ac8SCy Schubert return -1;
26385732ac8SCy Schubert }
26485732ac8SCy Schubert
26585732ac8SCy Schubert if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) {
26685732ac8SCy Schubert wpa_printf(MSG_DEBUG,
26785732ac8SCy Schubert "FT: RRB VLAN_TAGGED invalid length");
26885732ac8SCy Schubert return -1;
26985732ac8SCy Schubert }
27085732ac8SCy Schubert
27185732ac8SCy Schubert while (len >= sizeof(le16)) {
27285732ac8SCy Schubert vlan_id = WPA_GET_LE16(plain);
27385732ac8SCy Schubert plain += sizeof(le16);
27485732ac8SCy Schubert left -= sizeof(le16);
27585732ac8SCy Schubert len -= sizeof(le16);
27685732ac8SCy Schubert
27785732ac8SCy Schubert if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) {
27885732ac8SCy Schubert wpa_printf(MSG_DEBUG,
27985732ac8SCy Schubert "FT: RRB VLAN ID invalid %d",
28085732ac8SCy Schubert vlan_id);
28185732ac8SCy Schubert continue;
28285732ac8SCy Schubert }
28385732ac8SCy Schubert
28485732ac8SCy Schubert if (type == FT_RRB_VLAN_UNTAGGED)
28585732ac8SCy Schubert vlan->untagged = vlan_id;
28685732ac8SCy Schubert
28785732ac8SCy Schubert if (type == FT_RRB_VLAN_TAGGED &&
28885732ac8SCy Schubert taggedidx < MAX_NUM_TAGGED_VLAN) {
28985732ac8SCy Schubert vlan->tagged[taggedidx] = vlan_id;
29085732ac8SCy Schubert taggedidx++;
29185732ac8SCy Schubert } else if (type == FT_RRB_VLAN_TAGGED) {
29285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs");
29385732ac8SCy Schubert }
29485732ac8SCy Schubert }
29585732ac8SCy Schubert
29685732ac8SCy Schubert skip:
29785732ac8SCy Schubert left -= len;
29885732ac8SCy Schubert plain += len;
29985732ac8SCy Schubert }
30085732ac8SCy Schubert
30185732ac8SCy Schubert if (taggedidx)
30285732ac8SCy Schubert qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int);
30385732ac8SCy Schubert
30485732ac8SCy Schubert vlan->notempty = vlan->untagged || vlan->tagged[0];
30585732ac8SCy Schubert
30685732ac8SCy Schubert return 0;
30785732ac8SCy Schubert }
30885732ac8SCy Schubert
30985732ac8SCy Schubert
wpa_ft_tlv_len(const struct tlv_list * tlvs)31085732ac8SCy Schubert static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
31185732ac8SCy Schubert {
31285732ac8SCy Schubert size_t tlv_len = 0;
31385732ac8SCy Schubert int i;
31485732ac8SCy Schubert
31585732ac8SCy Schubert if (!tlvs)
31685732ac8SCy Schubert return 0;
31785732ac8SCy Schubert
31885732ac8SCy Schubert for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
31985732ac8SCy Schubert tlv_len += sizeof(struct ft_rrb_tlv);
32085732ac8SCy Schubert tlv_len += tlvs[i].len;
32185732ac8SCy Schubert }
32285732ac8SCy Schubert
32385732ac8SCy Schubert return tlv_len;
32485732ac8SCy Schubert }
32585732ac8SCy Schubert
32685732ac8SCy Schubert
wpa_ft_tlv_lin(const struct tlv_list * tlvs,u8 * start,u8 * endpos)32785732ac8SCy Schubert static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start,
32885732ac8SCy Schubert u8 *endpos)
32985732ac8SCy Schubert {
33085732ac8SCy Schubert int i;
33185732ac8SCy Schubert size_t tlv_len;
33285732ac8SCy Schubert struct ft_rrb_tlv *hdr;
33385732ac8SCy Schubert u8 *pos;
33485732ac8SCy Schubert
33585732ac8SCy Schubert if (!tlvs)
33685732ac8SCy Schubert return 0;
33785732ac8SCy Schubert
33885732ac8SCy Schubert tlv_len = 0;
33985732ac8SCy Schubert pos = start;
34085732ac8SCy Schubert for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
34185732ac8SCy Schubert if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start))
34285732ac8SCy Schubert return tlv_len;
34385732ac8SCy Schubert tlv_len += sizeof(*hdr);
34485732ac8SCy Schubert hdr = (struct ft_rrb_tlv *) pos;
34585732ac8SCy Schubert hdr->type = host_to_le16(tlvs[i].type);
34685732ac8SCy Schubert hdr->len = host_to_le16(tlvs[i].len);
34785732ac8SCy Schubert pos = start + tlv_len;
34885732ac8SCy Schubert
34985732ac8SCy Schubert if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
35085732ac8SCy Schubert return tlv_len;
35185732ac8SCy Schubert if (tlvs[i].len == 0)
35285732ac8SCy Schubert continue;
35385732ac8SCy Schubert tlv_len += tlvs[i].len;
35485732ac8SCy Schubert os_memcpy(pos, tlvs[i].data, tlvs[i].len);
35585732ac8SCy Schubert pos = start + tlv_len;
35685732ac8SCy Schubert }
35785732ac8SCy Schubert
35885732ac8SCy Schubert return tlv_len;
35985732ac8SCy Schubert }
36085732ac8SCy Schubert
36185732ac8SCy Schubert
wpa_ft_vlan_len(const struct vlan_description * vlan)36285732ac8SCy Schubert static size_t wpa_ft_vlan_len(const struct vlan_description *vlan)
36385732ac8SCy Schubert {
36485732ac8SCy Schubert size_t tlv_len = 0;
36585732ac8SCy Schubert int i;
36685732ac8SCy Schubert
36785732ac8SCy Schubert if (!vlan || !vlan->notempty)
36885732ac8SCy Schubert return 0;
36985732ac8SCy Schubert
37085732ac8SCy Schubert if (vlan->untagged) {
37185732ac8SCy Schubert tlv_len += sizeof(struct ft_rrb_tlv);
37285732ac8SCy Schubert tlv_len += sizeof(le16);
37385732ac8SCy Schubert }
37485732ac8SCy Schubert if (vlan->tagged[0])
37585732ac8SCy Schubert tlv_len += sizeof(struct ft_rrb_tlv);
37685732ac8SCy Schubert for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++)
37785732ac8SCy Schubert tlv_len += sizeof(le16);
37885732ac8SCy Schubert
37985732ac8SCy Schubert return tlv_len;
38085732ac8SCy Schubert }
38185732ac8SCy Schubert
38285732ac8SCy Schubert
wpa_ft_vlan_lin(const struct vlan_description * vlan,u8 * start,u8 * endpos)38385732ac8SCy Schubert static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan,
38485732ac8SCy Schubert u8 *start, u8 *endpos)
38585732ac8SCy Schubert {
38685732ac8SCy Schubert size_t tlv_len;
38785732ac8SCy Schubert int i, len;
38885732ac8SCy Schubert struct ft_rrb_tlv *hdr;
38985732ac8SCy Schubert u8 *pos = start;
39085732ac8SCy Schubert
39185732ac8SCy Schubert if (!vlan || !vlan->notempty)
39285732ac8SCy Schubert return 0;
39385732ac8SCy Schubert
39485732ac8SCy Schubert tlv_len = 0;
39585732ac8SCy Schubert if (vlan->untagged) {
39685732ac8SCy Schubert tlv_len += sizeof(*hdr);
39785732ac8SCy Schubert if (start + tlv_len > endpos)
39885732ac8SCy Schubert return tlv_len;
39985732ac8SCy Schubert hdr = (struct ft_rrb_tlv *) pos;
40085732ac8SCy Schubert hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED);
40185732ac8SCy Schubert hdr->len = host_to_le16(sizeof(le16));
40285732ac8SCy Schubert pos = start + tlv_len;
40385732ac8SCy Schubert
40485732ac8SCy Schubert tlv_len += sizeof(le16);
40585732ac8SCy Schubert if (start + tlv_len > endpos)
40685732ac8SCy Schubert return tlv_len;
40785732ac8SCy Schubert WPA_PUT_LE16(pos, vlan->untagged);
40885732ac8SCy Schubert pos = start + tlv_len;
40985732ac8SCy Schubert }
41085732ac8SCy Schubert
41185732ac8SCy Schubert if (!vlan->tagged[0])
41285732ac8SCy Schubert return tlv_len;
41385732ac8SCy Schubert
41485732ac8SCy Schubert tlv_len += sizeof(*hdr);
41585732ac8SCy Schubert if (start + tlv_len > endpos)
41685732ac8SCy Schubert return tlv_len;
41785732ac8SCy Schubert hdr = (struct ft_rrb_tlv *) pos;
41885732ac8SCy Schubert hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED);
41985732ac8SCy Schubert len = 0; /* len is computed below */
42085732ac8SCy Schubert pos = start + tlv_len;
42185732ac8SCy Schubert
42285732ac8SCy Schubert for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) {
42385732ac8SCy Schubert tlv_len += sizeof(le16);
42485732ac8SCy Schubert if (start + tlv_len > endpos)
42585732ac8SCy Schubert break;
42685732ac8SCy Schubert len += sizeof(le16);
42785732ac8SCy Schubert WPA_PUT_LE16(pos, vlan->tagged[i]);
42885732ac8SCy Schubert pos = start + tlv_len;
42985732ac8SCy Schubert }
43085732ac8SCy Schubert
43185732ac8SCy Schubert hdr->len = host_to_le16(len);
43285732ac8SCy Schubert
43385732ac8SCy Schubert return tlv_len;
43485732ac8SCy Schubert }
43585732ac8SCy Schubert
43685732ac8SCy Schubert
wpa_ft_rrb_lin(const struct tlv_list * tlvs1,const struct tlv_list * tlvs2,const struct vlan_description * vlan,u8 ** plain,size_t * plain_len)43785732ac8SCy Schubert static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
43885732ac8SCy Schubert const struct tlv_list *tlvs2,
43985732ac8SCy Schubert const struct vlan_description *vlan,
44085732ac8SCy Schubert u8 **plain, size_t *plain_len)
44185732ac8SCy Schubert {
44285732ac8SCy Schubert u8 *pos, *endpos;
44385732ac8SCy Schubert size_t tlv_len;
44485732ac8SCy Schubert
44585732ac8SCy Schubert tlv_len = wpa_ft_tlv_len(tlvs1);
44685732ac8SCy Schubert tlv_len += wpa_ft_tlv_len(tlvs2);
44785732ac8SCy Schubert tlv_len += wpa_ft_vlan_len(vlan);
44885732ac8SCy Schubert
44985732ac8SCy Schubert *plain_len = tlv_len;
45085732ac8SCy Schubert *plain = os_zalloc(tlv_len);
45185732ac8SCy Schubert if (!*plain) {
45285732ac8SCy Schubert wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext");
45385732ac8SCy Schubert goto err;
45485732ac8SCy Schubert }
45585732ac8SCy Schubert
45685732ac8SCy Schubert pos = *plain;
45785732ac8SCy Schubert endpos = *plain + tlv_len;
45885732ac8SCy Schubert pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
45985732ac8SCy Schubert pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
46085732ac8SCy Schubert pos += wpa_ft_vlan_lin(vlan, pos, endpos);
46185732ac8SCy Schubert
4624b72b91aSCy Schubert /* validity check */
46385732ac8SCy Schubert if (pos != endpos) {
46485732ac8SCy Schubert wpa_printf(MSG_ERROR, "FT: Length error building RRB");
46585732ac8SCy Schubert goto err;
46685732ac8SCy Schubert }
46785732ac8SCy Schubert
46885732ac8SCy Schubert return 0;
46985732ac8SCy Schubert
47085732ac8SCy Schubert err:
47185732ac8SCy Schubert os_free(*plain);
47285732ac8SCy Schubert *plain = NULL;
47385732ac8SCy Schubert *plain_len = 0;
47485732ac8SCy Schubert return -1;
47585732ac8SCy Schubert }
47685732ac8SCy Schubert
47785732ac8SCy Schubert
wpa_ft_rrb_encrypt(const u8 * key,const size_t key_len,const u8 * plain,const size_t plain_len,const u8 * auth,const size_t auth_len,const u8 * src_addr,u8 type,u8 * enc)47885732ac8SCy Schubert static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
47985732ac8SCy Schubert const u8 *plain, const size_t plain_len,
48085732ac8SCy Schubert const u8 *auth, const size_t auth_len,
48185732ac8SCy Schubert const u8 *src_addr, u8 type, u8 *enc)
48285732ac8SCy Schubert {
48385732ac8SCy Schubert const u8 *ad[3] = { src_addr, auth, &type };
48485732ac8SCy Schubert size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
48585732ac8SCy Schubert
4864bc52338SCy Schubert wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
4874bc52338SCy Schubert MAC2STR(src_addr), type);
48885732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message",
48985732ac8SCy Schubert plain, plain_len);
49085732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len);
4914bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
49285732ac8SCy Schubert
49385732ac8SCy Schubert if (!key) {
49485732ac8SCy Schubert /* encryption not needed, return plaintext as packet */
49585732ac8SCy Schubert os_memcpy(enc, plain, plain_len);
49685732ac8SCy Schubert } else if (aes_siv_encrypt(key, key_len, plain, plain_len,
49785732ac8SCy Schubert 3, ad, ad_len, enc) < 0) {
49885732ac8SCy Schubert wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
49985732ac8SCy Schubert return -1;
50085732ac8SCy Schubert }
5014bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs",
5024bc52338SCy Schubert enc, plain_len + AES_BLOCK_SIZE);
50385732ac8SCy Schubert
50485732ac8SCy Schubert return 0;
50585732ac8SCy Schubert }
50685732ac8SCy Schubert
50785732ac8SCy Schubert
50885732ac8SCy Schubert /**
50985732ac8SCy Schubert * wpa_ft_rrb_build - Build and encrypt an FT RRB message
51085732ac8SCy Schubert * @key: AES-SIV key for AEAD
51185732ac8SCy Schubert * @key_len: Length of key in octets
51285732ac8SCy Schubert * @tlvs_enc0: First set of to-be-encrypted TLVs
51385732ac8SCy Schubert * @tlvs_enc1: Second set of to-be-encrypted TLVs
51485732ac8SCy Schubert * @tlvs_auth: Set of to-be-authenticated TLVs
51585732ac8SCy Schubert * @src_addr: MAC address of the frame sender
51685732ac8SCy Schubert * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
51785732ac8SCy Schubert * @packet Pointer to return the pointer to the allocated packet buffer;
51885732ac8SCy Schubert * needs to be freed by the caller if not null;
51985732ac8SCy Schubert * will only be returned on success
52085732ac8SCy Schubert * @packet_len: Pointer to return the length of the allocated buffer in octets
52185732ac8SCy Schubert * Returns: 0 on success, -1 on error
52285732ac8SCy Schubert */
wpa_ft_rrb_build(const u8 * key,const size_t key_len,const struct tlv_list * tlvs_enc0,const struct tlv_list * tlvs_enc1,const struct tlv_list * tlvs_auth,const struct vlan_description * vlan,const u8 * src_addr,u8 type,u8 ** packet,size_t * packet_len)52385732ac8SCy Schubert static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
52485732ac8SCy Schubert const struct tlv_list *tlvs_enc0,
52585732ac8SCy Schubert const struct tlv_list *tlvs_enc1,
52685732ac8SCy Schubert const struct tlv_list *tlvs_auth,
52785732ac8SCy Schubert const struct vlan_description *vlan,
52885732ac8SCy Schubert const u8 *src_addr, u8 type,
52985732ac8SCy Schubert u8 **packet, size_t *packet_len)
53085732ac8SCy Schubert {
5314bc52338SCy Schubert u8 *plain = NULL, *auth = NULL, *pos, *tmp;
53285732ac8SCy Schubert size_t plain_len = 0, auth_len = 0;
53385732ac8SCy Schubert int ret = -1;
5344bc52338SCy Schubert size_t pad_len = 0;
53585732ac8SCy Schubert
53685732ac8SCy Schubert *packet = NULL;
53785732ac8SCy Schubert if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
53885732ac8SCy Schubert goto out;
53985732ac8SCy Schubert
54085732ac8SCy Schubert if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0)
54185732ac8SCy Schubert goto out;
54285732ac8SCy Schubert
54385732ac8SCy Schubert *packet_len = sizeof(u16) + auth_len + plain_len;
54485732ac8SCy Schubert if (key)
54585732ac8SCy Schubert *packet_len += AES_BLOCK_SIZE;
5464bc52338SCy Schubert #define RRB_MIN_MSG_LEN 64
5474bc52338SCy Schubert if (*packet_len < RRB_MIN_MSG_LEN) {
5484bc52338SCy Schubert pad_len = RRB_MIN_MSG_LEN - *packet_len;
5494bc52338SCy Schubert if (pad_len < sizeof(struct ft_rrb_tlv))
5504bc52338SCy Schubert pad_len = sizeof(struct ft_rrb_tlv);
5514bc52338SCy Schubert wpa_printf(MSG_DEBUG,
5524bc52338SCy Schubert "FT: Pad message to minimum Ethernet frame length (%d --> %d)",
5534bc52338SCy Schubert (int) *packet_len, (int) (*packet_len + pad_len));
5544bc52338SCy Schubert *packet_len += pad_len;
5554bc52338SCy Schubert tmp = os_realloc(auth, auth_len + pad_len);
5564bc52338SCy Schubert if (!tmp)
5574bc52338SCy Schubert goto out;
5584bc52338SCy Schubert auth = tmp;
5594bc52338SCy Schubert pos = auth + auth_len;
5604bc52338SCy Schubert WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY);
5614bc52338SCy Schubert pos += 2;
5624bc52338SCy Schubert WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv));
5634bc52338SCy Schubert pos += 2;
5644bc52338SCy Schubert os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv));
5654bc52338SCy Schubert auth_len += pad_len;
5664bc52338SCy Schubert
5674bc52338SCy Schubert }
56885732ac8SCy Schubert *packet = os_zalloc(*packet_len);
56985732ac8SCy Schubert if (!*packet)
57085732ac8SCy Schubert goto out;
57185732ac8SCy Schubert
57285732ac8SCy Schubert pos = *packet;
57385732ac8SCy Schubert WPA_PUT_LE16(pos, auth_len);
57485732ac8SCy Schubert pos += 2;
57585732ac8SCy Schubert os_memcpy(pos, auth, auth_len);
57685732ac8SCy Schubert pos += auth_len;
57785732ac8SCy Schubert if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
57885732ac8SCy Schubert auth_len, src_addr, type, pos) < 0)
57985732ac8SCy Schubert goto out;
5804bc52338SCy Schubert wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len);
58185732ac8SCy Schubert
58285732ac8SCy Schubert ret = 0;
58385732ac8SCy Schubert
58485732ac8SCy Schubert out:
58585732ac8SCy Schubert bin_clear_free(plain, plain_len);
58685732ac8SCy Schubert os_free(auth);
58785732ac8SCy Schubert
58885732ac8SCy Schubert if (ret) {
58985732ac8SCy Schubert wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message");
59085732ac8SCy Schubert os_free(*packet);
59185732ac8SCy Schubert *packet = NULL;
59285732ac8SCy Schubert *packet_len = 0;
59385732ac8SCy Schubert }
59485732ac8SCy Schubert
59585732ac8SCy Schubert return ret;
59685732ac8SCy Schubert }
59785732ac8SCy Schubert
59885732ac8SCy Schubert
59985732ac8SCy Schubert #define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \
60085732ac8SCy Schubert if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
60185732ac8SCy Schubert &f_##field##_len, &f_##field) < 0 || \
60285732ac8SCy Schubert (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
60385732ac8SCy Schubert wpa_printf(MSG_INFO, "FT: Missing required " #field \
60485732ac8SCy Schubert " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
60585732ac8SCy Schubert wpa_ft_rrb_dump(srcfield, srcfield##_len); \
60685732ac8SCy Schubert goto out; \
60785732ac8SCy Schubert } \
60885732ac8SCy Schubert } while (0)
60985732ac8SCy Schubert
61085732ac8SCy Schubert #define RRB_GET(type, field, txt, checklength) \
61185732ac8SCy Schubert RRB_GET_SRC(plain, type, field, txt, checklength)
61285732ac8SCy Schubert #define RRB_GET_AUTH(type, field, txt, checklength) \
61385732ac8SCy Schubert RRB_GET_SRC(auth, type, field, txt, checklength)
61485732ac8SCy Schubert
61585732ac8SCy Schubert #define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \
61685732ac8SCy Schubert if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
61785732ac8SCy Schubert &f_##field##_len, &f_##field) < 0 || \
61885732ac8SCy Schubert (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
61985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \
62085732ac8SCy Schubert " in %s from " MACSTR, txt, MAC2STR(src_addr)); \
62185732ac8SCy Schubert f_##field##_len = 0; \
62285732ac8SCy Schubert f_##field = NULL; \
62385732ac8SCy Schubert } \
62485732ac8SCy Schubert } while (0)
62585732ac8SCy Schubert
62685732ac8SCy Schubert #define RRB_GET_OPTIONAL(type, field, txt, checklength) \
62785732ac8SCy Schubert RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength)
62885732ac8SCy Schubert #define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \
62985732ac8SCy Schubert RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength)
6305b9c547cSRui Paulo
wpa_ft_rrb_send(struct wpa_authenticator * wpa_auth,const u8 * dst,const u8 * data,size_t data_len)631e28a4053SRui Paulo static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
632e28a4053SRui Paulo const u8 *data, size_t data_len)
633e28a4053SRui Paulo {
63485732ac8SCy Schubert if (wpa_auth->cb->send_ether == NULL)
635e28a4053SRui Paulo return -1;
636e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
63785732ac8SCy Schubert return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB,
638e28a4053SRui Paulo data, data_len);
639e28a4053SRui Paulo }
640e28a4053SRui Paulo
641e28a4053SRui Paulo
wpa_ft_rrb_oui_send(struct wpa_authenticator * wpa_auth,const u8 * dst,u8 oui_suffix,const u8 * data,size_t data_len)64285732ac8SCy Schubert static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth,
64385732ac8SCy Schubert const u8 *dst, u8 oui_suffix,
64485732ac8SCy Schubert const u8 *data, size_t data_len)
64585732ac8SCy Schubert {
64685732ac8SCy Schubert if (!wpa_auth->cb->send_oui)
64785732ac8SCy Schubert return -1;
6484bc52338SCy Schubert wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)",
6494bc52338SCy Schubert oui_suffix, MAC2STR(dst), (unsigned int) data_len);
65085732ac8SCy Schubert return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
65185732ac8SCy Schubert data_len);
65285732ac8SCy Schubert }
65385732ac8SCy Schubert
65485732ac8SCy Schubert
wpa_ft_action_send(struct wpa_authenticator * wpa_auth,const u8 * dst,const u8 * data,size_t data_len)655e28a4053SRui Paulo static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
656e28a4053SRui Paulo const u8 *dst, const u8 *data, size_t data_len)
657e28a4053SRui Paulo {
65885732ac8SCy Schubert if (wpa_auth->cb->send_ft_action == NULL)
659e28a4053SRui Paulo return -1;
66085732ac8SCy Schubert return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst,
661e28a4053SRui Paulo data, data_len);
662e28a4053SRui Paulo }
663e28a4053SRui Paulo
664e28a4053SRui Paulo
wpa_ft_get_psk(struct wpa_authenticator * wpa_auth,const u8 * addr,const u8 * p2p_dev_addr,const u8 * prev_psk)66585732ac8SCy Schubert static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
66685732ac8SCy Schubert const u8 *addr, const u8 *p2p_dev_addr,
66785732ac8SCy Schubert const u8 *prev_psk)
66885732ac8SCy Schubert {
66985732ac8SCy Schubert if (wpa_auth->cb->get_psk == NULL)
67085732ac8SCy Schubert return NULL;
67185732ac8SCy Schubert return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
6724bc52338SCy Schubert prev_psk, NULL, NULL);
67385732ac8SCy Schubert }
67485732ac8SCy Schubert
67585732ac8SCy Schubert
676e28a4053SRui Paulo static struct wpa_state_machine *
wpa_ft_add_sta(struct wpa_authenticator * wpa_auth,const u8 * sta_addr)677e28a4053SRui Paulo wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
678e28a4053SRui Paulo {
67985732ac8SCy Schubert if (wpa_auth->cb->add_sta == NULL)
680e28a4053SRui Paulo return NULL;
68185732ac8SCy Schubert return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr);
68285732ac8SCy Schubert }
68385732ac8SCy Schubert
68485732ac8SCy Schubert
wpa_ft_set_vlan(struct wpa_authenticator * wpa_auth,const u8 * sta_addr,struct vlan_description * vlan)68585732ac8SCy Schubert static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth,
68685732ac8SCy Schubert const u8 *sta_addr, struct vlan_description *vlan)
68785732ac8SCy Schubert {
68885732ac8SCy Schubert if (!wpa_auth->cb->set_vlan)
68985732ac8SCy Schubert return -1;
69085732ac8SCy Schubert return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
69185732ac8SCy Schubert }
69285732ac8SCy Schubert
69385732ac8SCy Schubert
wpa_ft_get_vlan(struct wpa_authenticator * wpa_auth,const u8 * sta_addr,struct vlan_description * vlan)69485732ac8SCy Schubert static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth,
69585732ac8SCy Schubert const u8 *sta_addr, struct vlan_description *vlan)
69685732ac8SCy Schubert {
69785732ac8SCy Schubert if (!wpa_auth->cb->get_vlan)
69885732ac8SCy Schubert return -1;
69985732ac8SCy Schubert return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
70085732ac8SCy Schubert }
70185732ac8SCy Schubert
70285732ac8SCy Schubert
70385732ac8SCy Schubert static int
wpa_ft_set_identity(struct wpa_authenticator * wpa_auth,const u8 * sta_addr,const u8 * identity,size_t identity_len)70485732ac8SCy Schubert wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
70585732ac8SCy Schubert const u8 *identity, size_t identity_len)
70685732ac8SCy Schubert {
70785732ac8SCy Schubert if (!wpa_auth->cb->set_identity)
70885732ac8SCy Schubert return -1;
70985732ac8SCy Schubert return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity,
71085732ac8SCy Schubert identity_len);
71185732ac8SCy Schubert }
71285732ac8SCy Schubert
71385732ac8SCy Schubert
71485732ac8SCy Schubert static size_t
wpa_ft_get_identity(struct wpa_authenticator * wpa_auth,const u8 * sta_addr,const u8 ** buf)71585732ac8SCy Schubert wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
71685732ac8SCy Schubert const u8 **buf)
71785732ac8SCy Schubert {
71885732ac8SCy Schubert *buf = NULL;
71985732ac8SCy Schubert if (!wpa_auth->cb->get_identity)
72085732ac8SCy Schubert return 0;
72185732ac8SCy Schubert return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf);
72285732ac8SCy Schubert }
72385732ac8SCy Schubert
72485732ac8SCy Schubert
72585732ac8SCy Schubert static int
wpa_ft_set_radius_cui(struct wpa_authenticator * wpa_auth,const u8 * sta_addr,const u8 * radius_cui,size_t radius_cui_len)72685732ac8SCy Schubert wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
72785732ac8SCy Schubert const u8 *radius_cui, size_t radius_cui_len)
72885732ac8SCy Schubert {
72985732ac8SCy Schubert if (!wpa_auth->cb->set_radius_cui)
73085732ac8SCy Schubert return -1;
73185732ac8SCy Schubert return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr,
73285732ac8SCy Schubert radius_cui, radius_cui_len);
73385732ac8SCy Schubert }
73485732ac8SCy Schubert
73585732ac8SCy Schubert
73685732ac8SCy Schubert static size_t
wpa_ft_get_radius_cui(struct wpa_authenticator * wpa_auth,const u8 * sta_addr,const u8 ** buf)73785732ac8SCy Schubert wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
73885732ac8SCy Schubert const u8 **buf)
73985732ac8SCy Schubert {
74085732ac8SCy Schubert *buf = NULL;
74185732ac8SCy Schubert if (!wpa_auth->cb->get_radius_cui)
74285732ac8SCy Schubert return 0;
74385732ac8SCy Schubert return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf);
74485732ac8SCy Schubert }
74585732ac8SCy Schubert
74685732ac8SCy Schubert
74785732ac8SCy Schubert static void
wpa_ft_set_session_timeout(struct wpa_authenticator * wpa_auth,const u8 * sta_addr,int session_timeout)74885732ac8SCy Schubert wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth,
74985732ac8SCy Schubert const u8 *sta_addr, int session_timeout)
75085732ac8SCy Schubert {
75185732ac8SCy Schubert if (!wpa_auth->cb->set_session_timeout)
75285732ac8SCy Schubert return;
75385732ac8SCy Schubert wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr,
75485732ac8SCy Schubert session_timeout);
75585732ac8SCy Schubert }
75685732ac8SCy Schubert
75785732ac8SCy Schubert
75885732ac8SCy Schubert static int
wpa_ft_get_session_timeout(struct wpa_authenticator * wpa_auth,const u8 * sta_addr)75985732ac8SCy Schubert wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth,
76085732ac8SCy Schubert const u8 *sta_addr)
76185732ac8SCy Schubert {
76285732ac8SCy Schubert if (!wpa_auth->cb->get_session_timeout)
76385732ac8SCy Schubert return 0;
76485732ac8SCy Schubert return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr);
765e28a4053SRui Paulo }
766e28a4053SRui Paulo
767e28a4053SRui Paulo
wpa_ft_add_tspec(struct wpa_authenticator * wpa_auth,const u8 * sta_addr,u8 * tspec_ie,size_t tspec_ielen)768f05cddf9SRui Paulo static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
769f05cddf9SRui Paulo const u8 *sta_addr,
770f05cddf9SRui Paulo u8 *tspec_ie, size_t tspec_ielen)
771f05cddf9SRui Paulo {
77285732ac8SCy Schubert if (wpa_auth->cb->add_tspec == NULL) {
773f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
774f05cddf9SRui Paulo return -1;
775f05cddf9SRui Paulo }
77685732ac8SCy Schubert return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie,
777f05cddf9SRui Paulo tspec_ielen);
778f05cddf9SRui Paulo }
779f05cddf9SRui Paulo
780f05cddf9SRui Paulo
7814bc52338SCy Schubert #ifdef CONFIG_OCV
wpa_channel_info(struct wpa_authenticator * wpa_auth,struct wpa_channel_info * ci)7824bc52338SCy Schubert static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
7834bc52338SCy Schubert struct wpa_channel_info *ci)
7844bc52338SCy Schubert {
7854bc52338SCy Schubert if (!wpa_auth->cb->channel_info)
7864bc52338SCy Schubert return -1;
7874bc52338SCy Schubert return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
7884bc52338SCy Schubert }
7894bc52338SCy Schubert #endif /* CONFIG_OCV */
7904bc52338SCy Schubert
7914bc52338SCy Schubert
wpa_write_mdie(struct wpa_auth_config * conf,u8 * buf,size_t len)792e28a4053SRui Paulo int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
793e28a4053SRui Paulo {
794e28a4053SRui Paulo u8 *pos = buf;
795e28a4053SRui Paulo u8 capab;
796e28a4053SRui Paulo if (len < 2 + sizeof(struct rsn_mdie))
797e28a4053SRui Paulo return -1;
798e28a4053SRui Paulo
799e28a4053SRui Paulo *pos++ = WLAN_EID_MOBILITY_DOMAIN;
800e28a4053SRui Paulo *pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
801e28a4053SRui Paulo os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
802e28a4053SRui Paulo pos += MOBILITY_DOMAIN_ID_LEN;
803f05cddf9SRui Paulo capab = 0;
804f05cddf9SRui Paulo if (conf->ft_over_ds)
805f05cddf9SRui Paulo capab |= RSN_FT_CAPAB_FT_OVER_DS;
806e28a4053SRui Paulo *pos++ = capab;
807e28a4053SRui Paulo
808e28a4053SRui Paulo return pos - buf;
809e28a4053SRui Paulo }
810e28a4053SRui Paulo
811e28a4053SRui Paulo
wpa_write_ftie(struct wpa_auth_config * conf,int key_mgmt,size_t key_len,const u8 * r0kh_id,size_t r0kh_id_len,const u8 * anonce,const u8 * snonce,u8 * buf,size_t len,const u8 * subelem,size_t subelem_len,int rsnxe_used)812*a90b9d01SCy Schubert int wpa_write_ftie(struct wpa_auth_config *conf, int key_mgmt, size_t key_len,
81385732ac8SCy Schubert const u8 *r0kh_id, size_t r0kh_id_len,
814e28a4053SRui Paulo const u8 *anonce, const u8 *snonce,
815e28a4053SRui Paulo u8 *buf, size_t len, const u8 *subelem,
816c1d255d3SCy Schubert size_t subelem_len, int rsnxe_used)
817e28a4053SRui Paulo {
818e28a4053SRui Paulo u8 *pos = buf, *ielen;
819*a90b9d01SCy Schubert size_t hdrlen;
820*a90b9d01SCy Schubert u16 mic_control = rsnxe_used ? FTE_MIC_CTRL_RSNXE_USED : 0;
821*a90b9d01SCy Schubert
822*a90b9d01SCy Schubert if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
823*a90b9d01SCy Schubert key_len == SHA256_MAC_LEN)
824*a90b9d01SCy Schubert hdrlen = sizeof(struct rsn_ftie);
825*a90b9d01SCy Schubert else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
826*a90b9d01SCy Schubert key_len == SHA384_MAC_LEN)
827*a90b9d01SCy Schubert hdrlen = sizeof(struct rsn_ftie_sha384);
828*a90b9d01SCy Schubert else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
829*a90b9d01SCy Schubert key_len == SHA512_MAC_LEN)
830*a90b9d01SCy Schubert hdrlen = sizeof(struct rsn_ftie_sha512);
831*a90b9d01SCy Schubert else if (wpa_key_mgmt_sha384(key_mgmt))
832*a90b9d01SCy Schubert hdrlen = sizeof(struct rsn_ftie_sha384);
833*a90b9d01SCy Schubert else
834*a90b9d01SCy Schubert hdrlen = sizeof(struct rsn_ftie);
835e28a4053SRui Paulo
83685732ac8SCy Schubert if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
837e28a4053SRui Paulo subelem_len)
838e28a4053SRui Paulo return -1;
839e28a4053SRui Paulo
840e28a4053SRui Paulo *pos++ = WLAN_EID_FAST_BSS_TRANSITION;
841e28a4053SRui Paulo ielen = pos++;
842e28a4053SRui Paulo
843*a90b9d01SCy Schubert if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
844*a90b9d01SCy Schubert key_len == SHA512_MAC_LEN) {
845*a90b9d01SCy Schubert struct rsn_ftie_sha512 *hdr = (struct rsn_ftie_sha512 *) pos;
846*a90b9d01SCy Schubert
847*a90b9d01SCy Schubert os_memset(hdr, 0, sizeof(*hdr));
848*a90b9d01SCy Schubert pos += sizeof(*hdr);
849*a90b9d01SCy Schubert mic_control |= FTE_MIC_LEN_32 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
850*a90b9d01SCy Schubert WPA_PUT_LE16(hdr->mic_control, mic_control);
851*a90b9d01SCy Schubert if (anonce)
852*a90b9d01SCy Schubert os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
853*a90b9d01SCy Schubert if (snonce)
854*a90b9d01SCy Schubert os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
855*a90b9d01SCy Schubert } else if ((key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
856*a90b9d01SCy Schubert key_len == SHA384_MAC_LEN) ||
857*a90b9d01SCy Schubert wpa_key_mgmt_sha384(key_mgmt)) {
85885732ac8SCy Schubert struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
85985732ac8SCy Schubert
860e28a4053SRui Paulo os_memset(hdr, 0, sizeof(*hdr));
861e28a4053SRui Paulo pos += sizeof(*hdr);
862*a90b9d01SCy Schubert mic_control |= FTE_MIC_LEN_24 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
863*a90b9d01SCy Schubert WPA_PUT_LE16(hdr->mic_control, mic_control);
864e28a4053SRui Paulo if (anonce)
865e28a4053SRui Paulo os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
866e28a4053SRui Paulo if (snonce)
867e28a4053SRui Paulo os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
86885732ac8SCy Schubert } else {
86985732ac8SCy Schubert struct rsn_ftie *hdr = (struct rsn_ftie *) pos;
87085732ac8SCy Schubert
87185732ac8SCy Schubert os_memset(hdr, 0, sizeof(*hdr));
87285732ac8SCy Schubert pos += sizeof(*hdr);
873*a90b9d01SCy Schubert mic_control |= FTE_MIC_LEN_16 << FTE_MIC_CTRL_MIC_LEN_SHIFT;
874*a90b9d01SCy Schubert WPA_PUT_LE16(hdr->mic_control, mic_control);
87585732ac8SCy Schubert if (anonce)
87685732ac8SCy Schubert os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
87785732ac8SCy Schubert if (snonce)
87885732ac8SCy Schubert os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
87985732ac8SCy Schubert }
880e28a4053SRui Paulo
881e28a4053SRui Paulo /* Optional Parameters */
882e28a4053SRui Paulo *pos++ = FTIE_SUBELEM_R1KH_ID;
883e28a4053SRui Paulo *pos++ = FT_R1KH_ID_LEN;
884e28a4053SRui Paulo os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN);
885e28a4053SRui Paulo pos += FT_R1KH_ID_LEN;
886e28a4053SRui Paulo
887e28a4053SRui Paulo if (r0kh_id) {
888e28a4053SRui Paulo *pos++ = FTIE_SUBELEM_R0KH_ID;
889e28a4053SRui Paulo *pos++ = r0kh_id_len;
890e28a4053SRui Paulo os_memcpy(pos, r0kh_id, r0kh_id_len);
891e28a4053SRui Paulo pos += r0kh_id_len;
892e28a4053SRui Paulo }
893e28a4053SRui Paulo
894e28a4053SRui Paulo if (subelem) {
895e28a4053SRui Paulo os_memcpy(pos, subelem, subelem_len);
896e28a4053SRui Paulo pos += subelem_len;
897e28a4053SRui Paulo }
898e28a4053SRui Paulo
899e28a4053SRui Paulo *ielen = pos - buf - 2;
900e28a4053SRui Paulo
901e28a4053SRui Paulo return pos - buf;
902e28a4053SRui Paulo }
903e28a4053SRui Paulo
904e28a4053SRui Paulo
90585732ac8SCy Schubert /* A packet to be handled after seq response */
90685732ac8SCy Schubert struct ft_remote_item {
90785732ac8SCy Schubert struct dl_list list;
90885732ac8SCy Schubert
90985732ac8SCy Schubert u8 nonce[FT_RRB_NONCE_LEN];
91085732ac8SCy Schubert struct os_reltime nonce_ts;
91185732ac8SCy Schubert
91285732ac8SCy Schubert u8 src_addr[ETH_ALEN];
91385732ac8SCy Schubert u8 *enc;
91485732ac8SCy Schubert size_t enc_len;
91585732ac8SCy Schubert u8 *auth;
91685732ac8SCy Schubert size_t auth_len;
91785732ac8SCy Schubert int (*cb)(struct wpa_authenticator *wpa_auth,
91885732ac8SCy Schubert const u8 *src_addr,
91985732ac8SCy Schubert const u8 *enc, size_t enc_len,
92085732ac8SCy Schubert const u8 *auth, size_t auth_len,
92185732ac8SCy Schubert int no_defer);
92285732ac8SCy Schubert };
92385732ac8SCy Schubert
92485732ac8SCy Schubert
wpa_ft_rrb_seq_free(struct ft_remote_item * item)92585732ac8SCy Schubert static void wpa_ft_rrb_seq_free(struct ft_remote_item *item)
92685732ac8SCy Schubert {
92785732ac8SCy Schubert eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item);
92885732ac8SCy Schubert dl_list_del(&item->list);
92985732ac8SCy Schubert bin_clear_free(item->enc, item->enc_len);
93085732ac8SCy Schubert os_free(item->auth);
93185732ac8SCy Schubert os_free(item);
93285732ac8SCy Schubert }
93385732ac8SCy Schubert
93485732ac8SCy Schubert
wpa_ft_rrb_seq_flush(struct wpa_authenticator * wpa_auth,struct ft_remote_seq * rkh_seq,int cb)93585732ac8SCy Schubert static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth,
93685732ac8SCy Schubert struct ft_remote_seq *rkh_seq, int cb)
93785732ac8SCy Schubert {
93885732ac8SCy Schubert struct ft_remote_item *item, *n;
93985732ac8SCy Schubert
94085732ac8SCy Schubert dl_list_for_each_safe(item, n, &rkh_seq->rx.queue,
94185732ac8SCy Schubert struct ft_remote_item, list) {
94285732ac8SCy Schubert if (cb && item->cb)
94385732ac8SCy Schubert item->cb(wpa_auth, item->src_addr, item->enc,
94485732ac8SCy Schubert item->enc_len, item->auth, item->auth_len, 1);
94585732ac8SCy Schubert wpa_ft_rrb_seq_free(item);
94685732ac8SCy Schubert }
94785732ac8SCy Schubert }
94885732ac8SCy Schubert
94985732ac8SCy Schubert
wpa_ft_rrb_seq_timeout(void * eloop_ctx,void * timeout_ctx)95085732ac8SCy Schubert static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx)
95185732ac8SCy Schubert {
95285732ac8SCy Schubert struct ft_remote_item *item = timeout_ctx;
95385732ac8SCy Schubert
95485732ac8SCy Schubert wpa_ft_rrb_seq_free(item);
95585732ac8SCy Schubert }
95685732ac8SCy Schubert
95785732ac8SCy Schubert
95885732ac8SCy Schubert static int
wpa_ft_rrb_seq_req(struct wpa_authenticator * wpa_auth,struct ft_remote_seq * rkh_seq,const u8 * src_addr,const u8 * f_r0kh_id,size_t f_r0kh_id_len,const u8 * f_r1kh_id,const u8 * key,size_t key_len,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,int (* cb)(struct wpa_authenticator * wpa_auth,const u8 * src_addr,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,int no_defer))95985732ac8SCy Schubert wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
96085732ac8SCy Schubert struct ft_remote_seq *rkh_seq, const u8 *src_addr,
96185732ac8SCy Schubert const u8 *f_r0kh_id, size_t f_r0kh_id_len,
96285732ac8SCy Schubert const u8 *f_r1kh_id, const u8 *key, size_t key_len,
96385732ac8SCy Schubert const u8 *enc, size_t enc_len,
96485732ac8SCy Schubert const u8 *auth, size_t auth_len,
96585732ac8SCy Schubert int (*cb)(struct wpa_authenticator *wpa_auth,
96685732ac8SCy Schubert const u8 *src_addr,
96785732ac8SCy Schubert const u8 *enc, size_t enc_len,
96885732ac8SCy Schubert const u8 *auth, size_t auth_len,
96985732ac8SCy Schubert int no_defer))
97085732ac8SCy Schubert {
97185732ac8SCy Schubert struct ft_remote_item *item = NULL;
97285732ac8SCy Schubert u8 *packet = NULL;
97385732ac8SCy Schubert size_t packet_len;
97485732ac8SCy Schubert struct tlv_list seq_req_auth[] = {
97585732ac8SCy Schubert { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
97685732ac8SCy Schubert .data = NULL /* to be filled: item->nonce */ },
97785732ac8SCy Schubert { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
97885732ac8SCy Schubert .data = f_r0kh_id },
97985732ac8SCy Schubert { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
98085732ac8SCy Schubert .data = f_r1kh_id },
98185732ac8SCy Schubert { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
98285732ac8SCy Schubert };
98385732ac8SCy Schubert
98485732ac8SCy Schubert if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) {
98585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long");
98685732ac8SCy Schubert goto err;
98785732ac8SCy Schubert }
98885732ac8SCy Schubert
989c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "FT: Send sequence number request from " MACSTR
990c1d255d3SCy Schubert " to " MACSTR,
991c1d255d3SCy Schubert MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
99285732ac8SCy Schubert item = os_zalloc(sizeof(*item));
99385732ac8SCy Schubert if (!item)
99485732ac8SCy Schubert goto err;
99585732ac8SCy Schubert
99685732ac8SCy Schubert os_memcpy(item->src_addr, src_addr, ETH_ALEN);
99785732ac8SCy Schubert item->cb = cb;
99885732ac8SCy Schubert
99985732ac8SCy Schubert if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) {
100085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes");
100185732ac8SCy Schubert goto err;
100285732ac8SCy Schubert }
100385732ac8SCy Schubert
100485732ac8SCy Schubert if (os_get_reltime(&item->nonce_ts) < 0)
100585732ac8SCy Schubert goto err;
100685732ac8SCy Schubert
100785732ac8SCy Schubert if (enc && enc_len > 0) {
100885732ac8SCy Schubert item->enc = os_memdup(enc, enc_len);
100985732ac8SCy Schubert item->enc_len = enc_len;
101085732ac8SCy Schubert if (!item->enc)
101185732ac8SCy Schubert goto err;
101285732ac8SCy Schubert }
101385732ac8SCy Schubert
101485732ac8SCy Schubert if (auth && auth_len > 0) {
101585732ac8SCy Schubert item->auth = os_memdup(auth, auth_len);
101685732ac8SCy Schubert item->auth_len = auth_len;
101785732ac8SCy Schubert if (!item->auth)
101885732ac8SCy Schubert goto err;
101985732ac8SCy Schubert }
102085732ac8SCy Schubert
102185732ac8SCy Schubert eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout,
102285732ac8SCy Schubert wpa_auth, item);
102385732ac8SCy Schubert
102485732ac8SCy Schubert seq_req_auth[0].data = item->nonce;
102585732ac8SCy Schubert
102685732ac8SCy Schubert if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL,
102785732ac8SCy Schubert wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
102885732ac8SCy Schubert &packet, &packet_len) < 0) {
102985732ac8SCy Schubert item = NULL; /* some other seq resp might still accept this */
103085732ac8SCy Schubert goto err;
103185732ac8SCy Schubert }
103285732ac8SCy Schubert
103385732ac8SCy Schubert dl_list_add(&rkh_seq->rx.queue, &item->list);
103485732ac8SCy Schubert
103585732ac8SCy Schubert wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
103685732ac8SCy Schubert packet, packet_len);
103785732ac8SCy Schubert
103885732ac8SCy Schubert os_free(packet);
103985732ac8SCy Schubert
104085732ac8SCy Schubert return 0;
104185732ac8SCy Schubert err:
104285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request");
104385732ac8SCy Schubert if (item) {
104485732ac8SCy Schubert os_free(item->auth);
104585732ac8SCy Schubert bin_clear_free(item->enc, item->enc_len);
104685732ac8SCy Schubert os_free(item);
104785732ac8SCy Schubert }
104885732ac8SCy Schubert
104985732ac8SCy Schubert return -1;
105085732ac8SCy Schubert }
105185732ac8SCy Schubert
105285732ac8SCy Schubert
105385732ac8SCy Schubert #define FT_RRB_SEQ_OK 0
105485732ac8SCy Schubert #define FT_RRB_SEQ_DROP 1
105585732ac8SCy Schubert #define FT_RRB_SEQ_DEFER 2
105685732ac8SCy Schubert
105785732ac8SCy Schubert static int
wpa_ft_rrb_seq_chk(struct ft_remote_seq * rkh_seq,const u8 * src_addr,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,const char * msgtype,int no_defer)105885732ac8SCy Schubert wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr,
105985732ac8SCy Schubert const u8 *enc, size_t enc_len,
106085732ac8SCy Schubert const u8 *auth, size_t auth_len,
106185732ac8SCy Schubert const char *msgtype, int no_defer)
106285732ac8SCy Schubert {
106385732ac8SCy Schubert const u8 *f_seq;
106485732ac8SCy Schubert size_t f_seq_len;
106585732ac8SCy Schubert const struct ft_rrb_seq *msg_both;
106685732ac8SCy Schubert u32 msg_seq, msg_off, rkh_off;
106785732ac8SCy Schubert struct os_reltime now;
106885732ac8SCy Schubert unsigned int i;
106985732ac8SCy Schubert
107085732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
107185732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len);
107285732ac8SCy Schubert msg_both = (const struct ft_rrb_seq *) f_seq;
107385732ac8SCy Schubert
107485732ac8SCy Schubert if (rkh_seq->rx.num_last == 0) {
107585732ac8SCy Schubert /* first packet from remote */
107685732ac8SCy Schubert goto defer;
107785732ac8SCy Schubert }
107885732ac8SCy Schubert
107985732ac8SCy Schubert if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) {
108085732ac8SCy Schubert /* remote might have rebooted */
108185732ac8SCy Schubert goto defer;
108285732ac8SCy Schubert }
108385732ac8SCy Schubert
108485732ac8SCy Schubert if (os_get_reltime(&now) == 0) {
108585732ac8SCy Schubert u32 msg_ts_now_remote, msg_ts_off;
108685732ac8SCy Schubert struct os_reltime now_remote;
108785732ac8SCy Schubert
108885732ac8SCy Schubert os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote);
108985732ac8SCy Schubert msg_ts_now_remote = now_remote.sec;
109085732ac8SCy Schubert msg_ts_off = le_to_host32(msg_both->ts) -
109185732ac8SCy Schubert (msg_ts_now_remote - ftRRBseqTimeout);
109285732ac8SCy Schubert if (msg_ts_off > 2 * ftRRBseqTimeout)
109385732ac8SCy Schubert goto defer;
109485732ac8SCy Schubert }
109585732ac8SCy Schubert
109685732ac8SCy Schubert msg_seq = le_to_host32(msg_both->seq);
109785732ac8SCy Schubert rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
109885732ac8SCy Schubert msg_off = msg_seq - rkh_off;
109985732ac8SCy Schubert if (msg_off > 0xC0000000)
110085732ac8SCy Schubert goto out; /* too old message, drop it */
110185732ac8SCy Schubert
110285732ac8SCy Schubert if (msg_off <= 0x40000000) {
110385732ac8SCy Schubert for (i = 0; i < rkh_seq->rx.num_last; i++) {
110485732ac8SCy Schubert if (rkh_seq->rx.last[i] == msg_seq)
110585732ac8SCy Schubert goto out; /* duplicate message, drop it */
110685732ac8SCy Schubert }
110785732ac8SCy Schubert
110885732ac8SCy Schubert return FT_RRB_SEQ_OK;
110985732ac8SCy Schubert }
111085732ac8SCy Schubert
111185732ac8SCy Schubert defer:
111285732ac8SCy Schubert if (no_defer)
111385732ac8SCy Schubert goto out;
111485732ac8SCy Schubert
111585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from "
111685732ac8SCy Schubert MACSTR, msgtype, MAC2STR(src_addr));
111785732ac8SCy Schubert
111885732ac8SCy Schubert return FT_RRB_SEQ_DEFER;
111985732ac8SCy Schubert out:
112085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR,
112185732ac8SCy Schubert msgtype, MAC2STR(src_addr));
112285732ac8SCy Schubert
112385732ac8SCy Schubert return FT_RRB_SEQ_DROP;
112485732ac8SCy Schubert }
112585732ac8SCy Schubert
112685732ac8SCy Schubert
112785732ac8SCy Schubert static void
wpa_ft_rrb_seq_accept(struct wpa_authenticator * wpa_auth,struct ft_remote_seq * rkh_seq,const u8 * src_addr,const u8 * auth,size_t auth_len,const char * msgtype)112885732ac8SCy Schubert wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth,
112985732ac8SCy Schubert struct ft_remote_seq *rkh_seq, const u8 *src_addr,
113085732ac8SCy Schubert const u8 *auth, size_t auth_len,
113185732ac8SCy Schubert const char *msgtype)
113285732ac8SCy Schubert {
113385732ac8SCy Schubert const u8 *f_seq;
113485732ac8SCy Schubert size_t f_seq_len;
113585732ac8SCy Schubert const struct ft_rrb_seq *msg_both;
113685732ac8SCy Schubert u32 msg_seq, msg_off, min_off, rkh_off;
113785732ac8SCy Schubert int minidx = 0;
113885732ac8SCy Schubert unsigned int i;
113985732ac8SCy Schubert
114085732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
114185732ac8SCy Schubert msg_both = (const struct ft_rrb_seq *) f_seq;
114285732ac8SCy Schubert
114385732ac8SCy Schubert msg_seq = le_to_host32(msg_both->seq);
114485732ac8SCy Schubert
114585732ac8SCy Schubert if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) {
114685732ac8SCy Schubert rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq;
114785732ac8SCy Schubert rkh_seq->rx.num_last++;
114885732ac8SCy Schubert return;
114985732ac8SCy Schubert }
115085732ac8SCy Schubert
115185732ac8SCy Schubert rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
115285732ac8SCy Schubert for (i = 0; i < rkh_seq->rx.num_last; i++) {
115385732ac8SCy Schubert msg_off = rkh_seq->rx.last[i] - rkh_off;
115485732ac8SCy Schubert min_off = rkh_seq->rx.last[minidx] - rkh_off;
115585732ac8SCy Schubert if (msg_off < min_off && i != rkh_seq->rx.offsetidx)
115685732ac8SCy Schubert minidx = i;
115785732ac8SCy Schubert }
115885732ac8SCy Schubert rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq;
115985732ac8SCy Schubert rkh_seq->rx.offsetidx = minidx;
116085732ac8SCy Schubert
116185732ac8SCy Schubert return;
116285732ac8SCy Schubert out:
116385732ac8SCy Schubert /* RRB_GET_AUTH should never fail here as
116485732ac8SCy Schubert * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */
116585732ac8SCy Schubert wpa_printf(MSG_ERROR, "FT: %s() failed", __func__);
116685732ac8SCy Schubert }
116785732ac8SCy Schubert
116885732ac8SCy Schubert
wpa_ft_new_seq(struct ft_remote_seq * rkh_seq,struct ft_rrb_seq * f_seq)116985732ac8SCy Schubert static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq,
117085732ac8SCy Schubert struct ft_rrb_seq *f_seq)
117185732ac8SCy Schubert {
117285732ac8SCy Schubert struct os_reltime now;
117385732ac8SCy Schubert
117485732ac8SCy Schubert if (os_get_reltime(&now) < 0)
117585732ac8SCy Schubert return -1;
117685732ac8SCy Schubert
117785732ac8SCy Schubert if (!rkh_seq->tx.dom) {
117885732ac8SCy Schubert if (random_get_bytes((u8 *) &rkh_seq->tx.seq,
117985732ac8SCy Schubert sizeof(rkh_seq->tx.seq))) {
118085732ac8SCy Schubert wpa_printf(MSG_ERROR,
118185732ac8SCy Schubert "FT: Failed to get random data for sequence number initialization");
118285732ac8SCy Schubert rkh_seq->tx.seq = now.usec;
118385732ac8SCy Schubert }
118485732ac8SCy Schubert if (random_get_bytes((u8 *) &rkh_seq->tx.dom,
118585732ac8SCy Schubert sizeof(rkh_seq->tx.dom))) {
118685732ac8SCy Schubert wpa_printf(MSG_ERROR,
118785732ac8SCy Schubert "FT: Failed to get random data for sequence number initialization");
118885732ac8SCy Schubert rkh_seq->tx.dom = now.usec;
118985732ac8SCy Schubert }
119085732ac8SCy Schubert rkh_seq->tx.dom |= 1;
119185732ac8SCy Schubert }
119285732ac8SCy Schubert
119385732ac8SCy Schubert f_seq->dom = host_to_le32(rkh_seq->tx.dom);
119485732ac8SCy Schubert f_seq->seq = host_to_le32(rkh_seq->tx.seq);
119585732ac8SCy Schubert f_seq->ts = host_to_le32(now.sec);
119685732ac8SCy Schubert
119785732ac8SCy Schubert rkh_seq->tx.seq++;
119885732ac8SCy Schubert
119985732ac8SCy Schubert return 0;
120085732ac8SCy Schubert }
120185732ac8SCy Schubert
120285732ac8SCy Schubert
1203e28a4053SRui Paulo struct wpa_ft_pmk_r0_sa {
120485732ac8SCy Schubert struct dl_list list;
120585732ac8SCy Schubert u8 pmk_r0[PMK_LEN_MAX];
120685732ac8SCy Schubert size_t pmk_r0_len;
1207e28a4053SRui Paulo u8 pmk_r0_name[WPA_PMK_NAME_LEN];
1208e28a4053SRui Paulo u8 spa[ETH_ALEN];
1209e28a4053SRui Paulo int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
121085732ac8SCy Schubert struct vlan_description *vlan;
121185732ac8SCy Schubert os_time_t expiration; /* 0 for no expiration */
121285732ac8SCy Schubert u8 *identity;
121385732ac8SCy Schubert size_t identity_len;
121485732ac8SCy Schubert u8 *radius_cui;
121585732ac8SCy Schubert size_t radius_cui_len;
121685732ac8SCy Schubert os_time_t session_timeout; /* 0 for no expiration */
121785732ac8SCy Schubert /* TODO: radius_class, EAP type */
1218e28a4053SRui Paulo int pmk_r1_pushed;
1219e28a4053SRui Paulo };
1220e28a4053SRui Paulo
1221e28a4053SRui Paulo struct wpa_ft_pmk_r1_sa {
122285732ac8SCy Schubert struct dl_list list;
122385732ac8SCy Schubert u8 pmk_r1[PMK_LEN_MAX];
122485732ac8SCy Schubert size_t pmk_r1_len;
1225e28a4053SRui Paulo u8 pmk_r1_name[WPA_PMK_NAME_LEN];
1226e28a4053SRui Paulo u8 spa[ETH_ALEN];
1227e28a4053SRui Paulo int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
122885732ac8SCy Schubert struct vlan_description *vlan;
122985732ac8SCy Schubert u8 *identity;
123085732ac8SCy Schubert size_t identity_len;
123185732ac8SCy Schubert u8 *radius_cui;
123285732ac8SCy Schubert size_t radius_cui_len;
123385732ac8SCy Schubert os_time_t session_timeout; /* 0 for no expiration */
123485732ac8SCy Schubert /* TODO: radius_class, EAP type */
1235e28a4053SRui Paulo };
1236e28a4053SRui Paulo
1237e28a4053SRui Paulo struct wpa_ft_pmk_cache {
123885732ac8SCy Schubert struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */
123985732ac8SCy Schubert struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */
1240e28a4053SRui Paulo };
1241e28a4053SRui Paulo
124285732ac8SCy Schubert
124385732ac8SCy Schubert static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx);
124485732ac8SCy Schubert static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx);
124585732ac8SCy Schubert
124685732ac8SCy Schubert
wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa * r0)124785732ac8SCy Schubert static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0)
124885732ac8SCy Schubert {
124985732ac8SCy Schubert if (!r0)
125085732ac8SCy Schubert return;
125185732ac8SCy Schubert
125285732ac8SCy Schubert dl_list_del(&r0->list);
125385732ac8SCy Schubert eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
125485732ac8SCy Schubert
125585732ac8SCy Schubert os_memset(r0->pmk_r0, 0, PMK_LEN_MAX);
125685732ac8SCy Schubert os_free(r0->vlan);
125785732ac8SCy Schubert os_free(r0->identity);
125885732ac8SCy Schubert os_free(r0->radius_cui);
125985732ac8SCy Schubert os_free(r0);
126085732ac8SCy Schubert }
126185732ac8SCy Schubert
126285732ac8SCy Schubert
wpa_ft_expire_pmk_r0(void * eloop_ctx,void * timeout_ctx)126385732ac8SCy Schubert static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx)
126485732ac8SCy Schubert {
126585732ac8SCy Schubert struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx;
126685732ac8SCy Schubert struct os_reltime now;
126785732ac8SCy Schubert int expires_in;
126885732ac8SCy Schubert int session_timeout;
126985732ac8SCy Schubert
127085732ac8SCy Schubert os_get_reltime(&now);
127185732ac8SCy Schubert
127285732ac8SCy Schubert if (!r0)
127385732ac8SCy Schubert return;
127485732ac8SCy Schubert
127585732ac8SCy Schubert expires_in = r0->expiration - now.sec;
127685732ac8SCy Schubert session_timeout = r0->session_timeout - now.sec;
127785732ac8SCy Schubert /* conditions to remove from cache:
127885732ac8SCy Schubert * a) r0->expiration is set and hit
127985732ac8SCy Schubert * -or-
128085732ac8SCy Schubert * b) r0->session_timeout is set and hit
128185732ac8SCy Schubert */
128285732ac8SCy Schubert if ((!r0->expiration || expires_in > 0) &&
128385732ac8SCy Schubert (!r0->session_timeout || session_timeout > 0)) {
128485732ac8SCy Schubert wpa_printf(MSG_ERROR,
128585732ac8SCy Schubert "FT: %s() called for non-expired entry %p",
128685732ac8SCy Schubert __func__, r0);
128785732ac8SCy Schubert eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
128885732ac8SCy Schubert if (r0->expiration && expires_in > 0)
128985732ac8SCy Schubert eloop_register_timeout(expires_in + 1, 0,
129085732ac8SCy Schubert wpa_ft_expire_pmk_r0, r0, NULL);
129185732ac8SCy Schubert if (r0->session_timeout && session_timeout > 0)
129285732ac8SCy Schubert eloop_register_timeout(session_timeout + 1, 0,
129385732ac8SCy Schubert wpa_ft_expire_pmk_r0, r0, NULL);
129485732ac8SCy Schubert return;
129585732ac8SCy Schubert }
129685732ac8SCy Schubert
129785732ac8SCy Schubert wpa_ft_free_pmk_r0(r0);
129885732ac8SCy Schubert }
129985732ac8SCy Schubert
130085732ac8SCy Schubert
wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa * r1)130185732ac8SCy Schubert static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1)
130285732ac8SCy Schubert {
130385732ac8SCy Schubert if (!r1)
130485732ac8SCy Schubert return;
130585732ac8SCy Schubert
130685732ac8SCy Schubert dl_list_del(&r1->list);
130785732ac8SCy Schubert eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL);
130885732ac8SCy Schubert
130985732ac8SCy Schubert os_memset(r1->pmk_r1, 0, PMK_LEN_MAX);
131085732ac8SCy Schubert os_free(r1->vlan);
131185732ac8SCy Schubert os_free(r1->identity);
131285732ac8SCy Schubert os_free(r1->radius_cui);
131385732ac8SCy Schubert os_free(r1);
131485732ac8SCy Schubert }
131585732ac8SCy Schubert
131685732ac8SCy Schubert
wpa_ft_expire_pmk_r1(void * eloop_ctx,void * timeout_ctx)131785732ac8SCy Schubert static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx)
131885732ac8SCy Schubert {
131985732ac8SCy Schubert struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx;
132085732ac8SCy Schubert
132185732ac8SCy Schubert wpa_ft_free_pmk_r1(r1);
132285732ac8SCy Schubert }
132385732ac8SCy Schubert
132485732ac8SCy Schubert
wpa_ft_pmk_cache_init(void)1325e28a4053SRui Paulo struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
1326e28a4053SRui Paulo {
1327e28a4053SRui Paulo struct wpa_ft_pmk_cache *cache;
1328e28a4053SRui Paulo
1329e28a4053SRui Paulo cache = os_zalloc(sizeof(*cache));
133085732ac8SCy Schubert if (cache) {
133185732ac8SCy Schubert dl_list_init(&cache->pmk_r0);
133285732ac8SCy Schubert dl_list_init(&cache->pmk_r1);
133385732ac8SCy Schubert }
1334e28a4053SRui Paulo
1335e28a4053SRui Paulo return cache;
1336e28a4053SRui Paulo }
1337e28a4053SRui Paulo
1338e28a4053SRui Paulo
wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache * cache)1339e28a4053SRui Paulo void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
1340e28a4053SRui Paulo {
1341e28a4053SRui Paulo struct wpa_ft_pmk_r0_sa *r0, *r0prev;
1342e28a4053SRui Paulo struct wpa_ft_pmk_r1_sa *r1, *r1prev;
1343e28a4053SRui Paulo
134485732ac8SCy Schubert dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0,
134585732ac8SCy Schubert struct wpa_ft_pmk_r0_sa, list)
134685732ac8SCy Schubert wpa_ft_free_pmk_r0(r0);
1347e28a4053SRui Paulo
134885732ac8SCy Schubert dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1,
134985732ac8SCy Schubert struct wpa_ft_pmk_r1_sa, list)
135085732ac8SCy Schubert wpa_ft_free_pmk_r1(r1);
1351e28a4053SRui Paulo
1352e28a4053SRui Paulo os_free(cache);
1353e28a4053SRui Paulo }
1354e28a4053SRui Paulo
1355e28a4053SRui Paulo
wpa_ft_store_pmk_r0(struct wpa_authenticator * wpa_auth,const u8 * spa,const u8 * pmk_r0,size_t pmk_r0_len,const u8 * pmk_r0_name,int pairwise,const struct vlan_description * vlan,int expires_in,int session_timeout,const u8 * identity,size_t identity_len,const u8 * radius_cui,size_t radius_cui_len)1356e28a4053SRui Paulo static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
1357e28a4053SRui Paulo const u8 *spa, const u8 *pmk_r0,
135885732ac8SCy Schubert size_t pmk_r0_len,
135985732ac8SCy Schubert const u8 *pmk_r0_name, int pairwise,
136085732ac8SCy Schubert const struct vlan_description *vlan,
136185732ac8SCy Schubert int expires_in, int session_timeout,
136285732ac8SCy Schubert const u8 *identity, size_t identity_len,
136385732ac8SCy Schubert const u8 *radius_cui, size_t radius_cui_len)
1364e28a4053SRui Paulo {
1365e28a4053SRui Paulo struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
1366e28a4053SRui Paulo struct wpa_ft_pmk_r0_sa *r0;
136785732ac8SCy Schubert struct os_reltime now;
1368e28a4053SRui Paulo
136985732ac8SCy Schubert /* TODO: add limit on number of entries in cache */
137085732ac8SCy Schubert os_get_reltime(&now);
1371e28a4053SRui Paulo
1372e28a4053SRui Paulo r0 = os_zalloc(sizeof(*r0));
1373e28a4053SRui Paulo if (r0 == NULL)
1374e28a4053SRui Paulo return -1;
1375e28a4053SRui Paulo
137685732ac8SCy Schubert os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len);
137785732ac8SCy Schubert r0->pmk_r0_len = pmk_r0_len;
1378e28a4053SRui Paulo os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
1379e28a4053SRui Paulo os_memcpy(r0->spa, spa, ETH_ALEN);
1380e28a4053SRui Paulo r0->pairwise = pairwise;
138185732ac8SCy Schubert if (expires_in > 0)
138285732ac8SCy Schubert r0->expiration = now.sec + expires_in;
138385732ac8SCy Schubert if (vlan && vlan->notempty) {
138485732ac8SCy Schubert r0->vlan = os_zalloc(sizeof(*vlan));
138585732ac8SCy Schubert if (!r0->vlan) {
138685732ac8SCy Schubert bin_clear_free(r0, sizeof(*r0));
138785732ac8SCy Schubert return -1;
138885732ac8SCy Schubert }
138985732ac8SCy Schubert *r0->vlan = *vlan;
139085732ac8SCy Schubert }
139185732ac8SCy Schubert if (identity) {
139285732ac8SCy Schubert r0->identity = os_malloc(identity_len);
139385732ac8SCy Schubert if (r0->identity) {
139485732ac8SCy Schubert os_memcpy(r0->identity, identity, identity_len);
139585732ac8SCy Schubert r0->identity_len = identity_len;
139685732ac8SCy Schubert }
139785732ac8SCy Schubert }
139885732ac8SCy Schubert if (radius_cui) {
139985732ac8SCy Schubert r0->radius_cui = os_malloc(radius_cui_len);
140085732ac8SCy Schubert if (r0->radius_cui) {
140185732ac8SCy Schubert os_memcpy(r0->radius_cui, radius_cui, radius_cui_len);
140285732ac8SCy Schubert r0->radius_cui_len = radius_cui_len;
140385732ac8SCy Schubert }
140485732ac8SCy Schubert }
140585732ac8SCy Schubert if (session_timeout > 0)
140685732ac8SCy Schubert r0->session_timeout = now.sec + session_timeout;
1407e28a4053SRui Paulo
140885732ac8SCy Schubert dl_list_add(&cache->pmk_r0, &r0->list);
140985732ac8SCy Schubert if (expires_in > 0)
141085732ac8SCy Schubert eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0,
141185732ac8SCy Schubert r0, NULL);
141285732ac8SCy Schubert if (session_timeout > 0)
141385732ac8SCy Schubert eloop_register_timeout(session_timeout + 1, 0,
141485732ac8SCy Schubert wpa_ft_expire_pmk_r0, r0, NULL);
1415e28a4053SRui Paulo
1416e28a4053SRui Paulo return 0;
1417e28a4053SRui Paulo }
1418e28a4053SRui Paulo
1419e28a4053SRui Paulo
wpa_ft_fetch_pmk_r0(struct wpa_authenticator * wpa_auth,const u8 * spa,const u8 * pmk_r0_name,const struct wpa_ft_pmk_r0_sa ** r0_out)1420e28a4053SRui Paulo static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
1421e28a4053SRui Paulo const u8 *spa, const u8 *pmk_r0_name,
142285732ac8SCy Schubert const struct wpa_ft_pmk_r0_sa **r0_out)
1423e28a4053SRui Paulo {
1424e28a4053SRui Paulo struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
1425e28a4053SRui Paulo struct wpa_ft_pmk_r0_sa *r0;
142685732ac8SCy Schubert struct os_reltime now;
1427e28a4053SRui Paulo
142885732ac8SCy Schubert os_get_reltime(&now);
142985732ac8SCy Schubert dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
1430*a90b9d01SCy Schubert if (ether_addr_equal(r0->spa, spa) &&
14315b9c547cSRui Paulo os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
14325b9c547cSRui Paulo WPA_PMK_NAME_LEN) == 0) {
143385732ac8SCy Schubert *r0_out = r0;
1434e28a4053SRui Paulo return 0;
1435e28a4053SRui Paulo }
1436e28a4053SRui Paulo }
1437e28a4053SRui Paulo
143885732ac8SCy Schubert *r0_out = NULL;
1439e28a4053SRui Paulo return -1;
1440e28a4053SRui Paulo }
1441e28a4053SRui Paulo
1442e28a4053SRui Paulo
wpa_ft_store_pmk_r1(struct wpa_authenticator * wpa_auth,const u8 * spa,const u8 * pmk_r1,size_t pmk_r1_len,const u8 * pmk_r1_name,int pairwise,const struct vlan_description * vlan,int expires_in,int session_timeout,const u8 * identity,size_t identity_len,const u8 * radius_cui,size_t radius_cui_len)1443e28a4053SRui Paulo static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
1444e28a4053SRui Paulo const u8 *spa, const u8 *pmk_r1,
144585732ac8SCy Schubert size_t pmk_r1_len,
144685732ac8SCy Schubert const u8 *pmk_r1_name, int pairwise,
144785732ac8SCy Schubert const struct vlan_description *vlan,
144885732ac8SCy Schubert int expires_in, int session_timeout,
144985732ac8SCy Schubert const u8 *identity, size_t identity_len,
145085732ac8SCy Schubert const u8 *radius_cui, size_t radius_cui_len)
1451e28a4053SRui Paulo {
1452e28a4053SRui Paulo struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
145385732ac8SCy Schubert int max_expires_in = wpa_auth->conf.r1_max_key_lifetime;
1454e28a4053SRui Paulo struct wpa_ft_pmk_r1_sa *r1;
145585732ac8SCy Schubert struct os_reltime now;
1456e28a4053SRui Paulo
145785732ac8SCy Schubert /* TODO: limit on number of entries in cache */
145885732ac8SCy Schubert os_get_reltime(&now);
145985732ac8SCy Schubert
146085732ac8SCy Schubert if (max_expires_in && (max_expires_in < expires_in || expires_in == 0))
146185732ac8SCy Schubert expires_in = max_expires_in;
1462e28a4053SRui Paulo
1463e28a4053SRui Paulo r1 = os_zalloc(sizeof(*r1));
1464e28a4053SRui Paulo if (r1 == NULL)
1465e28a4053SRui Paulo return -1;
1466e28a4053SRui Paulo
146785732ac8SCy Schubert os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len);
146885732ac8SCy Schubert r1->pmk_r1_len = pmk_r1_len;
1469e28a4053SRui Paulo os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
1470e28a4053SRui Paulo os_memcpy(r1->spa, spa, ETH_ALEN);
1471e28a4053SRui Paulo r1->pairwise = pairwise;
147285732ac8SCy Schubert if (vlan && vlan->notempty) {
147385732ac8SCy Schubert r1->vlan = os_zalloc(sizeof(*vlan));
147485732ac8SCy Schubert if (!r1->vlan) {
147585732ac8SCy Schubert bin_clear_free(r1, sizeof(*r1));
147685732ac8SCy Schubert return -1;
147785732ac8SCy Schubert }
147885732ac8SCy Schubert *r1->vlan = *vlan;
147985732ac8SCy Schubert }
148085732ac8SCy Schubert if (identity) {
148185732ac8SCy Schubert r1->identity = os_malloc(identity_len);
148285732ac8SCy Schubert if (r1->identity) {
148385732ac8SCy Schubert os_memcpy(r1->identity, identity, identity_len);
148485732ac8SCy Schubert r1->identity_len = identity_len;
148585732ac8SCy Schubert }
148685732ac8SCy Schubert }
148785732ac8SCy Schubert if (radius_cui) {
148885732ac8SCy Schubert r1->radius_cui = os_malloc(radius_cui_len);
148985732ac8SCy Schubert if (r1->radius_cui) {
149085732ac8SCy Schubert os_memcpy(r1->radius_cui, radius_cui, radius_cui_len);
149185732ac8SCy Schubert r1->radius_cui_len = radius_cui_len;
149285732ac8SCy Schubert }
149385732ac8SCy Schubert }
149485732ac8SCy Schubert if (session_timeout > 0)
149585732ac8SCy Schubert r1->session_timeout = now.sec + session_timeout;
1496e28a4053SRui Paulo
149785732ac8SCy Schubert dl_list_add(&cache->pmk_r1, &r1->list);
149885732ac8SCy Schubert
149985732ac8SCy Schubert if (expires_in > 0)
150085732ac8SCy Schubert eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1,
150185732ac8SCy Schubert r1, NULL);
150285732ac8SCy Schubert if (session_timeout > 0)
150385732ac8SCy Schubert eloop_register_timeout(session_timeout + 1, 0,
150485732ac8SCy Schubert wpa_ft_expire_pmk_r1, r1, NULL);
1505e28a4053SRui Paulo
1506e28a4053SRui Paulo return 0;
1507e28a4053SRui Paulo }
1508e28a4053SRui Paulo
1509e28a4053SRui Paulo
wpa_ft_fetch_pmk_r1(struct wpa_authenticator * wpa_auth,const u8 * spa,const u8 * pmk_r1_name,u8 * pmk_r1,size_t * pmk_r1_len,int * pairwise,struct vlan_description * vlan,const u8 ** identity,size_t * identity_len,const u8 ** radius_cui,size_t * radius_cui_len,int * session_timeout)1510c1d255d3SCy Schubert int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
1511e28a4053SRui Paulo const u8 *spa, const u8 *pmk_r1_name,
151285732ac8SCy Schubert u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
151385732ac8SCy Schubert struct vlan_description *vlan,
151485732ac8SCy Schubert const u8 **identity, size_t *identity_len,
151585732ac8SCy Schubert const u8 **radius_cui, size_t *radius_cui_len,
151685732ac8SCy Schubert int *session_timeout)
1517e28a4053SRui Paulo {
1518e28a4053SRui Paulo struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
1519e28a4053SRui Paulo struct wpa_ft_pmk_r1_sa *r1;
152085732ac8SCy Schubert struct os_reltime now;
1521e28a4053SRui Paulo
152285732ac8SCy Schubert os_get_reltime(&now);
152385732ac8SCy Schubert
152485732ac8SCy Schubert dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
1525*a90b9d01SCy Schubert if (ether_addr_equal(r1->spa, spa) &&
15265b9c547cSRui Paulo os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
15275b9c547cSRui Paulo WPA_PMK_NAME_LEN) == 0) {
152885732ac8SCy Schubert os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
152985732ac8SCy Schubert *pmk_r1_len = r1->pmk_r1_len;
1530e28a4053SRui Paulo if (pairwise)
1531e28a4053SRui Paulo *pairwise = r1->pairwise;
153285732ac8SCy Schubert if (vlan && r1->vlan)
153385732ac8SCy Schubert *vlan = *r1->vlan;
153485732ac8SCy Schubert if (vlan && !r1->vlan)
153585732ac8SCy Schubert os_memset(vlan, 0, sizeof(*vlan));
153685732ac8SCy Schubert if (identity && identity_len) {
153785732ac8SCy Schubert *identity = r1->identity;
153885732ac8SCy Schubert *identity_len = r1->identity_len;
153985732ac8SCy Schubert }
154085732ac8SCy Schubert if (radius_cui && radius_cui_len) {
154185732ac8SCy Schubert *radius_cui = r1->radius_cui;
154285732ac8SCy Schubert *radius_cui_len = r1->radius_cui_len;
154385732ac8SCy Schubert }
154485732ac8SCy Schubert if (session_timeout && r1->session_timeout > now.sec)
154585732ac8SCy Schubert *session_timeout = r1->session_timeout -
154685732ac8SCy Schubert now.sec;
154785732ac8SCy Schubert else if (session_timeout && r1->session_timeout)
154885732ac8SCy Schubert *session_timeout = 1;
154985732ac8SCy Schubert else if (session_timeout)
155085732ac8SCy Schubert *session_timeout = 0;
1551e28a4053SRui Paulo return 0;
1552e28a4053SRui Paulo }
1553e28a4053SRui Paulo }
1554e28a4053SRui Paulo
1555e28a4053SRui Paulo return -1;
1556e28a4053SRui Paulo }
1557e28a4053SRui Paulo
1558e28a4053SRui Paulo
wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh * r0kh)155985732ac8SCy Schubert static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh)
156085732ac8SCy Schubert {
156185732ac8SCy Schubert if (r0kh->seq)
156285732ac8SCy Schubert return 0;
156385732ac8SCy Schubert
156485732ac8SCy Schubert r0kh->seq = os_zalloc(sizeof(*r0kh->seq));
156585732ac8SCy Schubert if (!r0kh->seq) {
156685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq");
156785732ac8SCy Schubert return -1;
156885732ac8SCy Schubert }
156985732ac8SCy Schubert
157085732ac8SCy Schubert dl_list_init(&r0kh->seq->rx.queue);
157185732ac8SCy Schubert
157285732ac8SCy Schubert return 0;
157385732ac8SCy Schubert }
157485732ac8SCy Schubert
157585732ac8SCy Schubert
wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator * wpa_auth,const u8 * f_r0kh_id,size_t f_r0kh_id_len,struct ft_remote_r0kh ** r0kh_out,struct ft_remote_r0kh ** r0kh_wildcard)157685732ac8SCy Schubert static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
157785732ac8SCy Schubert const u8 *f_r0kh_id, size_t f_r0kh_id_len,
157885732ac8SCy Schubert struct ft_remote_r0kh **r0kh_out,
157985732ac8SCy Schubert struct ft_remote_r0kh **r0kh_wildcard)
158085732ac8SCy Schubert {
158185732ac8SCy Schubert struct ft_remote_r0kh *r0kh;
158285732ac8SCy Schubert
158385732ac8SCy Schubert *r0kh_wildcard = NULL;
158485732ac8SCy Schubert *r0kh_out = NULL;
158585732ac8SCy Schubert
158685732ac8SCy Schubert if (wpa_auth->conf.r0kh_list)
158785732ac8SCy Schubert r0kh = *wpa_auth->conf.r0kh_list;
158885732ac8SCy Schubert else
158985732ac8SCy Schubert r0kh = NULL;
159085732ac8SCy Schubert for (; r0kh; r0kh = r0kh->next) {
159185732ac8SCy Schubert if (r0kh->id_len == 1 && r0kh->id[0] == '*')
159285732ac8SCy Schubert *r0kh_wildcard = r0kh;
159385732ac8SCy Schubert if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len &&
159485732ac8SCy Schubert os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0)
159585732ac8SCy Schubert *r0kh_out = r0kh;
159685732ac8SCy Schubert }
159785732ac8SCy Schubert
159885732ac8SCy Schubert if (!*r0kh_out && !*r0kh_wildcard)
159985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
160085732ac8SCy Schubert
160185732ac8SCy Schubert if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
160285732ac8SCy Schubert *r0kh_out = NULL;
160385732ac8SCy Schubert }
160485732ac8SCy Schubert
160585732ac8SCy Schubert
wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh * r1kh)160685732ac8SCy Schubert static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh)
160785732ac8SCy Schubert {
160885732ac8SCy Schubert if (r1kh->seq)
160985732ac8SCy Schubert return 0;
161085732ac8SCy Schubert
161185732ac8SCy Schubert r1kh->seq = os_zalloc(sizeof(*r1kh->seq));
161285732ac8SCy Schubert if (!r1kh->seq) {
161385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq");
161485732ac8SCy Schubert return -1;
161585732ac8SCy Schubert }
161685732ac8SCy Schubert
161785732ac8SCy Schubert dl_list_init(&r1kh->seq->rx.queue);
161885732ac8SCy Schubert
161985732ac8SCy Schubert return 0;
162085732ac8SCy Schubert }
162185732ac8SCy Schubert
162285732ac8SCy Schubert
wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator * wpa_auth,const u8 * f_r1kh_id,struct ft_remote_r1kh ** r1kh_out,struct ft_remote_r1kh ** r1kh_wildcard)162385732ac8SCy Schubert static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
162485732ac8SCy Schubert const u8 *f_r1kh_id,
162585732ac8SCy Schubert struct ft_remote_r1kh **r1kh_out,
162685732ac8SCy Schubert struct ft_remote_r1kh **r1kh_wildcard)
162785732ac8SCy Schubert {
162885732ac8SCy Schubert struct ft_remote_r1kh *r1kh;
162985732ac8SCy Schubert
163085732ac8SCy Schubert *r1kh_wildcard = NULL;
163185732ac8SCy Schubert *r1kh_out = NULL;
163285732ac8SCy Schubert
163385732ac8SCy Schubert if (wpa_auth->conf.r1kh_list)
163485732ac8SCy Schubert r1kh = *wpa_auth->conf.r1kh_list;
163585732ac8SCy Schubert else
163685732ac8SCy Schubert r1kh = NULL;
163785732ac8SCy Schubert for (; r1kh; r1kh = r1kh->next) {
163885732ac8SCy Schubert if (is_zero_ether_addr(r1kh->addr) &&
163985732ac8SCy Schubert is_zero_ether_addr(r1kh->id))
164085732ac8SCy Schubert *r1kh_wildcard = r1kh;
164185732ac8SCy Schubert if (f_r1kh_id &&
164285732ac8SCy Schubert os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0)
164385732ac8SCy Schubert *r1kh_out = r1kh;
164485732ac8SCy Schubert }
164585732ac8SCy Schubert
164685732ac8SCy Schubert if (!*r1kh_out && !*r1kh_wildcard)
164785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
164885732ac8SCy Schubert
164985732ac8SCy Schubert if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
165085732ac8SCy Schubert *r1kh_out = NULL;
165185732ac8SCy Schubert }
165285732ac8SCy Schubert
165385732ac8SCy Schubert
wpa_ft_rrb_check_r0kh(struct wpa_authenticator * wpa_auth,const u8 * f_r0kh_id,size_t f_r0kh_id_len)165485732ac8SCy Schubert static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth,
165585732ac8SCy Schubert const u8 *f_r0kh_id, size_t f_r0kh_id_len)
165685732ac8SCy Schubert {
165785732ac8SCy Schubert if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len ||
165885732ac8SCy Schubert os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder,
165985732ac8SCy Schubert f_r0kh_id_len) != 0)
166085732ac8SCy Schubert return -1;
166185732ac8SCy Schubert
166285732ac8SCy Schubert return 0;
166385732ac8SCy Schubert }
166485732ac8SCy Schubert
166585732ac8SCy Schubert
wpa_ft_rrb_check_r1kh(struct wpa_authenticator * wpa_auth,const u8 * f_r1kh_id)166685732ac8SCy Schubert static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
166785732ac8SCy Schubert const u8 *f_r1kh_id)
166885732ac8SCy Schubert {
166985732ac8SCy Schubert if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder,
167085732ac8SCy Schubert FT_R1KH_ID_LEN) != 0)
167185732ac8SCy Schubert return -1;
167285732ac8SCy Schubert
167385732ac8SCy Schubert return 0;
167485732ac8SCy Schubert }
167585732ac8SCy Schubert
167685732ac8SCy Schubert
wpa_ft_rrb_del_r0kh(void * eloop_ctx,void * timeout_ctx)167785732ac8SCy Schubert static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx)
167885732ac8SCy Schubert {
167985732ac8SCy Schubert struct wpa_authenticator *wpa_auth = eloop_ctx;
168085732ac8SCy Schubert struct ft_remote_r0kh *r0kh, *prev = NULL;
168185732ac8SCy Schubert
168285732ac8SCy Schubert if (!wpa_auth->conf.r0kh_list)
168385732ac8SCy Schubert return;
168485732ac8SCy Schubert
168585732ac8SCy Schubert for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
168685732ac8SCy Schubert if (r0kh == timeout_ctx)
168785732ac8SCy Schubert break;
168885732ac8SCy Schubert prev = r0kh;
168985732ac8SCy Schubert }
169085732ac8SCy Schubert if (!r0kh)
169185732ac8SCy Schubert return;
169285732ac8SCy Schubert if (prev)
169385732ac8SCy Schubert prev->next = r0kh->next;
169485732ac8SCy Schubert else
169585732ac8SCy Schubert *wpa_auth->conf.r0kh_list = r0kh->next;
169685732ac8SCy Schubert if (r0kh->seq)
169785732ac8SCy Schubert wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
169885732ac8SCy Schubert os_free(r0kh->seq);
169985732ac8SCy Schubert os_free(r0kh);
170085732ac8SCy Schubert }
170185732ac8SCy Schubert
170285732ac8SCy Schubert
wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator * wpa_auth,struct ft_remote_r0kh * r0kh,int timeout)170385732ac8SCy Schubert static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth,
170485732ac8SCy Schubert struct ft_remote_r0kh *r0kh, int timeout)
170585732ac8SCy Schubert {
170685732ac8SCy Schubert if (timeout > 0)
170785732ac8SCy Schubert eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
170885732ac8SCy Schubert wpa_auth, r0kh);
170985732ac8SCy Schubert }
171085732ac8SCy Schubert
171185732ac8SCy Schubert
wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator * wpa_auth,struct ft_remote_r0kh * r0kh,int timeout)171285732ac8SCy Schubert static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth,
171385732ac8SCy Schubert struct ft_remote_r0kh *r0kh, int timeout)
171485732ac8SCy Schubert {
171585732ac8SCy Schubert eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh);
171685732ac8SCy Schubert
171785732ac8SCy Schubert if (timeout > 0)
171885732ac8SCy Schubert eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
171985732ac8SCy Schubert wpa_auth, r0kh);
172085732ac8SCy Schubert }
172185732ac8SCy Schubert
172285732ac8SCy Schubert
172385732ac8SCy Schubert static struct ft_remote_r0kh *
wpa_ft_rrb_add_r0kh(struct wpa_authenticator * wpa_auth,struct ft_remote_r0kh * r0kh_wildcard,const u8 * src_addr,const u8 * r0kh_id,size_t id_len,int timeout)172485732ac8SCy Schubert wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth,
172585732ac8SCy Schubert struct ft_remote_r0kh *r0kh_wildcard,
172685732ac8SCy Schubert const u8 *src_addr, const u8 *r0kh_id, size_t id_len,
172785732ac8SCy Schubert int timeout)
172885732ac8SCy Schubert {
172985732ac8SCy Schubert struct ft_remote_r0kh *r0kh;
173085732ac8SCy Schubert
173185732ac8SCy Schubert if (!wpa_auth->conf.r0kh_list)
173285732ac8SCy Schubert return NULL;
173385732ac8SCy Schubert
173485732ac8SCy Schubert r0kh = os_zalloc(sizeof(*r0kh));
173585732ac8SCy Schubert if (!r0kh)
173685732ac8SCy Schubert return NULL;
173785732ac8SCy Schubert
173885732ac8SCy Schubert if (src_addr)
173985732ac8SCy Schubert os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr));
174085732ac8SCy Schubert
174185732ac8SCy Schubert if (id_len > FT_R0KH_ID_MAX_LEN)
174285732ac8SCy Schubert id_len = FT_R0KH_ID_MAX_LEN;
174385732ac8SCy Schubert os_memcpy(r0kh->id, r0kh_id, id_len);
174485732ac8SCy Schubert r0kh->id_len = id_len;
174585732ac8SCy Schubert
174685732ac8SCy Schubert os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key));
174785732ac8SCy Schubert
174885732ac8SCy Schubert r0kh->next = *wpa_auth->conf.r0kh_list;
174985732ac8SCy Schubert *wpa_auth->conf.r0kh_list = r0kh;
175085732ac8SCy Schubert
175185732ac8SCy Schubert if (timeout > 0)
175285732ac8SCy Schubert eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
175385732ac8SCy Schubert wpa_auth, r0kh);
175485732ac8SCy Schubert
175585732ac8SCy Schubert if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0)
175685732ac8SCy Schubert return NULL;
175785732ac8SCy Schubert
175885732ac8SCy Schubert return r0kh;
175985732ac8SCy Schubert }
176085732ac8SCy Schubert
176185732ac8SCy Schubert
wpa_ft_rrb_del_r1kh(void * eloop_ctx,void * timeout_ctx)176285732ac8SCy Schubert static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx)
176385732ac8SCy Schubert {
176485732ac8SCy Schubert struct wpa_authenticator *wpa_auth = eloop_ctx;
176585732ac8SCy Schubert struct ft_remote_r1kh *r1kh, *prev = NULL;
176685732ac8SCy Schubert
176785732ac8SCy Schubert if (!wpa_auth->conf.r1kh_list)
176885732ac8SCy Schubert return;
176985732ac8SCy Schubert
177085732ac8SCy Schubert for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
177185732ac8SCy Schubert if (r1kh == timeout_ctx)
177285732ac8SCy Schubert break;
177385732ac8SCy Schubert prev = r1kh;
177485732ac8SCy Schubert }
177585732ac8SCy Schubert if (!r1kh)
177685732ac8SCy Schubert return;
177785732ac8SCy Schubert if (prev)
177885732ac8SCy Schubert prev->next = r1kh->next;
177985732ac8SCy Schubert else
178085732ac8SCy Schubert *wpa_auth->conf.r1kh_list = r1kh->next;
178185732ac8SCy Schubert if (r1kh->seq)
178285732ac8SCy Schubert wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
178385732ac8SCy Schubert os_free(r1kh->seq);
178485732ac8SCy Schubert os_free(r1kh);
178585732ac8SCy Schubert }
178685732ac8SCy Schubert
178785732ac8SCy Schubert
wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator * wpa_auth,struct ft_remote_r1kh * r1kh,int timeout)178885732ac8SCy Schubert static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth,
178985732ac8SCy Schubert struct ft_remote_r1kh *r1kh, int timeout)
179085732ac8SCy Schubert {
179185732ac8SCy Schubert if (timeout > 0)
179285732ac8SCy Schubert eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
179385732ac8SCy Schubert wpa_auth, r1kh);
179485732ac8SCy Schubert }
179585732ac8SCy Schubert
179685732ac8SCy Schubert
179785732ac8SCy Schubert static struct ft_remote_r1kh *
wpa_ft_rrb_add_r1kh(struct wpa_authenticator * wpa_auth,struct ft_remote_r1kh * r1kh_wildcard,const u8 * src_addr,const u8 * r1kh_id,int timeout)179885732ac8SCy Schubert wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth,
179985732ac8SCy Schubert struct ft_remote_r1kh *r1kh_wildcard,
180085732ac8SCy Schubert const u8 *src_addr, const u8 *r1kh_id, int timeout)
180185732ac8SCy Schubert {
180285732ac8SCy Schubert struct ft_remote_r1kh *r1kh;
180385732ac8SCy Schubert
180485732ac8SCy Schubert if (!wpa_auth->conf.r1kh_list)
180585732ac8SCy Schubert return NULL;
180685732ac8SCy Schubert
180785732ac8SCy Schubert r1kh = os_zalloc(sizeof(*r1kh));
180885732ac8SCy Schubert if (!r1kh)
180985732ac8SCy Schubert return NULL;
181085732ac8SCy Schubert
181185732ac8SCy Schubert os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr));
181285732ac8SCy Schubert os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id));
181385732ac8SCy Schubert os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key));
181485732ac8SCy Schubert r1kh->next = *wpa_auth->conf.r1kh_list;
181585732ac8SCy Schubert *wpa_auth->conf.r1kh_list = r1kh;
181685732ac8SCy Schubert
181785732ac8SCy Schubert if (timeout > 0)
181885732ac8SCy Schubert eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
181985732ac8SCy Schubert wpa_auth, r1kh);
182085732ac8SCy Schubert
182185732ac8SCy Schubert if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
182285732ac8SCy Schubert return NULL;
182385732ac8SCy Schubert
182485732ac8SCy Schubert return r1kh;
182585732ac8SCy Schubert }
182685732ac8SCy Schubert
182785732ac8SCy Schubert
wpa_ft_sta_deinit(struct wpa_state_machine * sm)182885732ac8SCy Schubert void wpa_ft_sta_deinit(struct wpa_state_machine *sm)
182985732ac8SCy Schubert {
183085732ac8SCy Schubert eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
183185732ac8SCy Schubert }
183285732ac8SCy Schubert
183385732ac8SCy Schubert
wpa_ft_deinit_seq(struct wpa_authenticator * wpa_auth)183485732ac8SCy Schubert static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
183585732ac8SCy Schubert {
183685732ac8SCy Schubert struct ft_remote_r0kh *r0kh;
183785732ac8SCy Schubert struct ft_remote_r1kh *r1kh;
183885732ac8SCy Schubert
183985732ac8SCy Schubert eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX);
184085732ac8SCy Schubert
184185732ac8SCy Schubert if (wpa_auth->conf.r0kh_list)
184285732ac8SCy Schubert r0kh = *wpa_auth->conf.r0kh_list;
184385732ac8SCy Schubert else
184485732ac8SCy Schubert r0kh = NULL;
184585732ac8SCy Schubert for (; r0kh; r0kh = r0kh->next) {
184685732ac8SCy Schubert if (!r0kh->seq)
184785732ac8SCy Schubert continue;
184885732ac8SCy Schubert wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
184985732ac8SCy Schubert os_free(r0kh->seq);
185085732ac8SCy Schubert r0kh->seq = NULL;
185185732ac8SCy Schubert }
185285732ac8SCy Schubert
185385732ac8SCy Schubert if (wpa_auth->conf.r1kh_list)
185485732ac8SCy Schubert r1kh = *wpa_auth->conf.r1kh_list;
185585732ac8SCy Schubert else
185685732ac8SCy Schubert r1kh = NULL;
185785732ac8SCy Schubert for (; r1kh; r1kh = r1kh->next) {
185885732ac8SCy Schubert if (!r1kh->seq)
185985732ac8SCy Schubert continue;
186085732ac8SCy Schubert wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
186185732ac8SCy Schubert os_free(r1kh->seq);
186285732ac8SCy Schubert r1kh->seq = NULL;
186385732ac8SCy Schubert }
186485732ac8SCy Schubert }
186585732ac8SCy Schubert
186685732ac8SCy Schubert
wpa_ft_deinit_rkh_tmp(struct wpa_authenticator * wpa_auth)186785732ac8SCy Schubert static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth)
186885732ac8SCy Schubert {
186985732ac8SCy Schubert struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL;
187085732ac8SCy Schubert struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL;
187185732ac8SCy Schubert
187285732ac8SCy Schubert if (wpa_auth->conf.r0kh_list)
187385732ac8SCy Schubert r0kh = *wpa_auth->conf.r0kh_list;
187485732ac8SCy Schubert else
187585732ac8SCy Schubert r0kh = NULL;
187685732ac8SCy Schubert while (r0kh) {
187785732ac8SCy Schubert r0kh_next = r0kh->next;
187885732ac8SCy Schubert if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth,
187985732ac8SCy Schubert r0kh) > 0) {
188085732ac8SCy Schubert if (r0kh_prev)
188185732ac8SCy Schubert r0kh_prev->next = r0kh_next;
188285732ac8SCy Schubert else
188385732ac8SCy Schubert *wpa_auth->conf.r0kh_list = r0kh_next;
188485732ac8SCy Schubert os_free(r0kh);
188585732ac8SCy Schubert } else {
188685732ac8SCy Schubert r0kh_prev = r0kh;
188785732ac8SCy Schubert }
188885732ac8SCy Schubert r0kh = r0kh_next;
188985732ac8SCy Schubert }
189085732ac8SCy Schubert
189185732ac8SCy Schubert if (wpa_auth->conf.r1kh_list)
189285732ac8SCy Schubert r1kh = *wpa_auth->conf.r1kh_list;
189385732ac8SCy Schubert else
189485732ac8SCy Schubert r1kh = NULL;
189585732ac8SCy Schubert while (r1kh) {
189685732ac8SCy Schubert r1kh_next = r1kh->next;
189785732ac8SCy Schubert if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth,
189885732ac8SCy Schubert r1kh) > 0) {
189985732ac8SCy Schubert if (r1kh_prev)
190085732ac8SCy Schubert r1kh_prev->next = r1kh_next;
190185732ac8SCy Schubert else
190285732ac8SCy Schubert *wpa_auth->conf.r1kh_list = r1kh_next;
190385732ac8SCy Schubert os_free(r1kh);
190485732ac8SCy Schubert } else {
190585732ac8SCy Schubert r1kh_prev = r1kh;
190685732ac8SCy Schubert }
190785732ac8SCy Schubert r1kh = r1kh_next;
190885732ac8SCy Schubert }
190985732ac8SCy Schubert }
191085732ac8SCy Schubert
191185732ac8SCy Schubert
wpa_ft_deinit(struct wpa_authenticator * wpa_auth)191285732ac8SCy Schubert void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
191385732ac8SCy Schubert {
191485732ac8SCy Schubert wpa_ft_deinit_seq(wpa_auth);
191585732ac8SCy Schubert wpa_ft_deinit_rkh_tmp(wpa_auth);
191685732ac8SCy Schubert }
191785732ac8SCy Schubert
191885732ac8SCy Schubert
wpa_ft_block_r0kh(struct wpa_authenticator * wpa_auth,const u8 * f_r0kh_id,size_t f_r0kh_id_len)191985732ac8SCy Schubert static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth,
192085732ac8SCy Schubert const u8 *f_r0kh_id, size_t f_r0kh_id_len)
192185732ac8SCy Schubert {
192285732ac8SCy Schubert struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
192385732ac8SCy Schubert
192485732ac8SCy Schubert if (!wpa_auth->conf.rkh_neg_timeout)
192585732ac8SCy Schubert return;
192685732ac8SCy Schubert
192785732ac8SCy Schubert wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
192885732ac8SCy Schubert &r0kh, &r0kh_wildcard);
192985732ac8SCy Schubert
193085732ac8SCy Schubert if (!r0kh_wildcard) {
193185732ac8SCy Schubert /* r0kh removed after neg_timeout and might need re-adding */
193285732ac8SCy Schubert return;
193385732ac8SCy Schubert }
193485732ac8SCy Schubert
1935c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: Temporarily block R0KH-ID",
193685732ac8SCy Schubert f_r0kh_id, f_r0kh_id_len);
193785732ac8SCy Schubert
193885732ac8SCy Schubert if (r0kh) {
193985732ac8SCy Schubert wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh,
194085732ac8SCy Schubert wpa_auth->conf.rkh_neg_timeout);
194185732ac8SCy Schubert os_memset(r0kh->addr, 0, ETH_ALEN);
194285732ac8SCy Schubert } else
194385732ac8SCy Schubert wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id,
194485732ac8SCy Schubert f_r0kh_id_len,
194585732ac8SCy Schubert wpa_auth->conf.rkh_neg_timeout);
194685732ac8SCy Schubert }
194785732ac8SCy Schubert
194885732ac8SCy Schubert
wpa_ft_expire_pull(void * eloop_ctx,void * timeout_ctx)194985732ac8SCy Schubert static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx)
195085732ac8SCy Schubert {
195185732ac8SCy Schubert struct wpa_state_machine *sm = eloop_ctx;
195285732ac8SCy Schubert
195385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR,
195485732ac8SCy Schubert MAC2STR(sm->addr));
195585732ac8SCy Schubert if (sm->ft_pending_pull_left_retries <= 0)
195685732ac8SCy Schubert wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len);
195785732ac8SCy Schubert
195885732ac8SCy Schubert /* cancel multiple timeouts */
195985732ac8SCy Schubert eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
196085732ac8SCy Schubert ft_finish_pull(sm);
196185732ac8SCy Schubert }
196285732ac8SCy Schubert
196385732ac8SCy Schubert
wpa_ft_pull_pmk_r1(struct wpa_state_machine * sm,const u8 * ies,size_t ies_len,const u8 * pmk_r0_name)19645b9c547cSRui Paulo static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
19655b9c547cSRui Paulo const u8 *ies, size_t ies_len,
19665b9c547cSRui Paulo const u8 *pmk_r0_name)
1967e28a4053SRui Paulo {
196885732ac8SCy Schubert struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
196985732ac8SCy Schubert u8 *packet = NULL;
197085732ac8SCy Schubert const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder;
197185732ac8SCy Schubert size_t packet_len, key_len;
197285732ac8SCy Schubert struct ft_rrb_seq f_seq;
197385732ac8SCy Schubert int tsecs, tusecs, first;
197485732ac8SCy Schubert struct wpabuf *ft_pending_req_ies;
197585732ac8SCy Schubert int r0kh_timeout;
197685732ac8SCy Schubert struct tlv_list req_enc[] = {
197785732ac8SCy Schubert { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
197885732ac8SCy Schubert .data = pmk_r0_name },
197985732ac8SCy Schubert { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
198085732ac8SCy Schubert .data = sm->addr },
198185732ac8SCy Schubert { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
198285732ac8SCy Schubert };
198385732ac8SCy Schubert struct tlv_list req_auth[] = {
198485732ac8SCy Schubert { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
198585732ac8SCy Schubert .data = sm->ft_pending_pull_nonce },
198685732ac8SCy Schubert { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
198785732ac8SCy Schubert .data = (u8 *) &f_seq },
198885732ac8SCy Schubert { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
198985732ac8SCy Schubert .data = sm->r0kh_id },
199085732ac8SCy Schubert { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
199185732ac8SCy Schubert .data = f_r1kh_id },
199285732ac8SCy Schubert { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
199385732ac8SCy Schubert };
1994e28a4053SRui Paulo
199585732ac8SCy Schubert if (sm->ft_pending_pull_left_retries <= 0)
199685732ac8SCy Schubert return -1;
199785732ac8SCy Schubert first = sm->ft_pending_pull_left_retries ==
199885732ac8SCy Schubert sm->wpa_auth->conf.rkh_pull_retries;
199985732ac8SCy Schubert sm->ft_pending_pull_left_retries--;
200085732ac8SCy Schubert
200185732ac8SCy Schubert wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
200285732ac8SCy Schubert &r0kh, &r0kh_wildcard);
200385732ac8SCy Schubert
200485732ac8SCy Schubert /* Keep r0kh sufficiently long in the list for seq num check */
200585732ac8SCy Schubert r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 +
200685732ac8SCy Schubert 1 + ftRRBseqTimeout;
200785732ac8SCy Schubert if (r0kh) {
200885732ac8SCy Schubert wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout);
200985732ac8SCy Schubert } else if (r0kh_wildcard) {
201085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
201185732ac8SCy Schubert /* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */
201285732ac8SCy Schubert r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard,
201385732ac8SCy Schubert r0kh_wildcard->addr,
201485732ac8SCy Schubert sm->r0kh_id, sm->r0kh_id_len,
201585732ac8SCy Schubert r0kh_timeout);
2016e28a4053SRui Paulo }
20175b9c547cSRui Paulo if (r0kh == NULL) {
20185b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
20195b9c547cSRui Paulo sm->r0kh_id, sm->r0kh_id_len);
2020e28a4053SRui Paulo return -1;
20215b9c547cSRui Paulo }
202285732ac8SCy Schubert if (is_zero_ether_addr(r0kh->addr)) {
2023c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is temporarily blocked",
202485732ac8SCy Schubert sm->r0kh_id, sm->r0kh_id_len);
202585732ac8SCy Schubert return -1;
202685732ac8SCy Schubert }
2027*a90b9d01SCy Schubert if (ether_addr_equal(r0kh->addr, sm->wpa_auth->addr)) {
202885732ac8SCy Schubert wpa_printf(MSG_DEBUG,
202985732ac8SCy Schubert "FT: R0KH-ID points to self - no matching key available");
203085732ac8SCy Schubert return -1;
203185732ac8SCy Schubert }
203285732ac8SCy Schubert
203385732ac8SCy Schubert key = r0kh->key;
203485732ac8SCy Schubert key_len = sizeof(r0kh->key);
2035e28a4053SRui Paulo
203685732ac8SCy Schubert if (r0kh->seq->rx.num_last == 0) {
203785732ac8SCy Schubert /* A sequence request will be sent out anyway when pull
203885732ac8SCy Schubert * response is received. Send it out now to avoid one RTT. */
203985732ac8SCy Schubert wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr,
204085732ac8SCy Schubert r0kh->id, r0kh->id_len, f_r1kh_id, key,
204185732ac8SCy Schubert key_len, NULL, 0, NULL, 0, NULL);
204285732ac8SCy Schubert }
2043e28a4053SRui Paulo
2044c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request from " MACSTR
2045c1d255d3SCy Schubert " to remote R0KH address " MACSTR,
2046c1d255d3SCy Schubert MAC2STR(sm->wpa_auth->addr), MAC2STR(r0kh->addr));
2047c1d255d3SCy Schubert
204885732ac8SCy Schubert if (first &&
204985732ac8SCy Schubert random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) {
2050e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
2051e28a4053SRui Paulo "nonce");
2052e28a4053SRui Paulo return -1;
2053e28a4053SRui Paulo }
2054e28a4053SRui Paulo
205585732ac8SCy Schubert if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) {
205685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
205785732ac8SCy Schubert return -1;
205885732ac8SCy Schubert }
205985732ac8SCy Schubert
206085732ac8SCy Schubert if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL,
206185732ac8SCy Schubert sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
206285732ac8SCy Schubert &packet, &packet_len) < 0)
2063e28a4053SRui Paulo return -1;
2064e28a4053SRui Paulo
206585732ac8SCy Schubert ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
20665b9c547cSRui Paulo wpabuf_free(sm->ft_pending_req_ies);
206785732ac8SCy Schubert sm->ft_pending_req_ies = ft_pending_req_ies;
206885732ac8SCy Schubert if (!sm->ft_pending_req_ies) {
206985732ac8SCy Schubert os_free(packet);
20705b9c547cSRui Paulo return -1;
207185732ac8SCy Schubert }
20725b9c547cSRui Paulo
207385732ac8SCy Schubert tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000;
207485732ac8SCy Schubert tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000;
207585732ac8SCy Schubert eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL);
207685732ac8SCy Schubert
207785732ac8SCy Schubert wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
207885732ac8SCy Schubert packet, packet_len);
207985732ac8SCy Schubert
208085732ac8SCy Schubert os_free(packet);
2081e28a4053SRui Paulo
2082e28a4053SRui Paulo return 0;
2083e28a4053SRui Paulo }
2084e28a4053SRui Paulo
2085e28a4053SRui Paulo
wpa_ft_store_pmk_fils(struct wpa_state_machine * sm,const u8 * pmk_r0,const u8 * pmk_r0_name)208685732ac8SCy Schubert int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm,
208785732ac8SCy Schubert const u8 *pmk_r0, const u8 *pmk_r0_name)
208885732ac8SCy Schubert {
208985732ac8SCy Schubert int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
209085732ac8SCy Schubert struct vlan_description vlan;
209185732ac8SCy Schubert const u8 *identity, *radius_cui;
209285732ac8SCy Schubert size_t identity_len, radius_cui_len;
209385732ac8SCy Schubert int session_timeout;
209485732ac8SCy Schubert size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
209585732ac8SCy Schubert SHA384_MAC_LEN : PMK_LEN;
209685732ac8SCy Schubert
209785732ac8SCy Schubert if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
209885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
209985732ac8SCy Schubert MAC2STR(sm->addr));
210085732ac8SCy Schubert return -1;
210185732ac8SCy Schubert }
210285732ac8SCy Schubert
210385732ac8SCy Schubert identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
210485732ac8SCy Schubert radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
210585732ac8SCy Schubert &radius_cui);
210685732ac8SCy Schubert session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
210785732ac8SCy Schubert
210885732ac8SCy Schubert return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
210985732ac8SCy Schubert pmk_r0_name, sm->pairwise, &vlan, expires_in,
211085732ac8SCy Schubert session_timeout, identity, identity_len,
211185732ac8SCy Schubert radius_cui, radius_cui_len);
211285732ac8SCy Schubert }
211385732ac8SCy Schubert
211485732ac8SCy Schubert
wpa_auth_derive_ptk_ft(struct wpa_state_machine * sm,struct wpa_ptk * ptk,u8 * pmk_r0,u8 * pmk_r1,u8 * pmk_r0_name,size_t * key_len,size_t kdk_len)2115*a90b9d01SCy Schubert int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
2116*a90b9d01SCy Schubert u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
2117*a90b9d01SCy Schubert size_t *key_len, size_t kdk_len)
2118e28a4053SRui Paulo {
2119*a90b9d01SCy Schubert size_t pmk_r0_len, pmk_r1_len;
2120e28a4053SRui Paulo u8 ptk_name[WPA_PMK_NAME_LEN];
2121e28a4053SRui Paulo const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
2122e28a4053SRui Paulo const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
2123e28a4053SRui Paulo size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len;
2124e28a4053SRui Paulo const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
2125e28a4053SRui Paulo const u8 *ssid = sm->wpa_auth->conf.ssid;
2126e28a4053SRui Paulo size_t ssid_len = sm->wpa_auth->conf.ssid_len;
2127206b73d0SCy Schubert const u8 *mpmk;
2128206b73d0SCy Schubert size_t mpmk_len;
2129e28a4053SRui Paulo
2130*a90b9d01SCy Schubert if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
2131*a90b9d01SCy Schubert (sm->xxkey_len == SHA256_MAC_LEN ||
2132*a90b9d01SCy Schubert sm->xxkey_len == SHA384_MAC_LEN ||
2133*a90b9d01SCy Schubert sm->xxkey_len == SHA512_MAC_LEN))
2134*a90b9d01SCy Schubert pmk_r0_len = sm->xxkey_len;
2135*a90b9d01SCy Schubert else if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
2136*a90b9d01SCy Schubert pmk_r0_len = SHA384_MAC_LEN;
2137*a90b9d01SCy Schubert else
2138*a90b9d01SCy Schubert pmk_r0_len = PMK_LEN;
2139*a90b9d01SCy Schubert *key_len = pmk_r1_len = pmk_r0_len;
2140*a90b9d01SCy Schubert
2141206b73d0SCy Schubert if (sm->xxkey_len > 0) {
2142206b73d0SCy Schubert mpmk = sm->xxkey;
2143206b73d0SCy Schubert mpmk_len = sm->xxkey_len;
2144206b73d0SCy Schubert } else if (sm->pmksa) {
2145206b73d0SCy Schubert mpmk = sm->pmksa->pmk;
2146206b73d0SCy Schubert mpmk_len = sm->pmksa->pmk_len;
2147206b73d0SCy Schubert } else {
2148e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
2149e28a4053SRui Paulo "derivation");
2150e28a4053SRui Paulo return -1;
2151e28a4053SRui Paulo }
2152e28a4053SRui Paulo
2153*a90b9d01SCy Schubert if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
2154*a90b9d01SCy Schubert r0kh, r0kh_len, sm->addr,
2155*a90b9d01SCy Schubert pmk_r0, pmk_r0_name,
2156*a90b9d01SCy Schubert sm->wpa_key_mgmt) < 0 ||
2157*a90b9d01SCy Schubert wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
2158*a90b9d01SCy Schubert pmk_r1, sm->pmk_r1_name) < 0)
2159*a90b9d01SCy Schubert return -1;
2160*a90b9d01SCy Schubert
2161*a90b9d01SCy Schubert return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
2162*a90b9d01SCy Schubert sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name,
2163*a90b9d01SCy Schubert ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise,
2164*a90b9d01SCy Schubert kdk_len);
2165*a90b9d01SCy Schubert }
2166*a90b9d01SCy Schubert
2167*a90b9d01SCy Schubert
wpa_auth_ft_store_keys(struct wpa_state_machine * sm,const u8 * pmk_r0,const u8 * pmk_r1,const u8 * pmk_r0_name,size_t key_len)2168*a90b9d01SCy Schubert void wpa_auth_ft_store_keys(struct wpa_state_machine *sm, const u8 *pmk_r0,
2169*a90b9d01SCy Schubert const u8 *pmk_r1, const u8 *pmk_r0_name,
2170*a90b9d01SCy Schubert size_t key_len)
2171*a90b9d01SCy Schubert {
2172*a90b9d01SCy Schubert int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
2173*a90b9d01SCy Schubert int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
2174*a90b9d01SCy Schubert struct vlan_description vlan;
2175*a90b9d01SCy Schubert const u8 *identity, *radius_cui;
2176*a90b9d01SCy Schubert size_t identity_len, radius_cui_len;
2177*a90b9d01SCy Schubert int session_timeout;
2178*a90b9d01SCy Schubert
2179*a90b9d01SCy Schubert if (psk_local && wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
2180*a90b9d01SCy Schubert return;
2181*a90b9d01SCy Schubert
218285732ac8SCy Schubert if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
218385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
218485732ac8SCy Schubert MAC2STR(sm->addr));
2185*a90b9d01SCy Schubert return;
218685732ac8SCy Schubert }
2187e28a4053SRui Paulo
218885732ac8SCy Schubert identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
218985732ac8SCy Schubert radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
219085732ac8SCy Schubert &radius_cui);
219185732ac8SCy Schubert session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
219285732ac8SCy Schubert
2193*a90b9d01SCy Schubert
2194*a90b9d01SCy Schubert wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, key_len,
219585732ac8SCy Schubert pmk_r0_name,
219685732ac8SCy Schubert sm->pairwise, &vlan, expires_in,
219785732ac8SCy Schubert session_timeout, identity, identity_len,
219885732ac8SCy Schubert radius_cui, radius_cui_len);
2199*a90b9d01SCy Schubert wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, key_len,
220085732ac8SCy Schubert sm->pmk_r1_name, sm->pairwise, &vlan,
220185732ac8SCy Schubert expires_in, session_timeout, identity,
220285732ac8SCy Schubert identity_len, radius_cui, radius_cui_len);
2203e28a4053SRui Paulo }
2204e28a4053SRui Paulo
2205e28a4053SRui Paulo
wpa_auth_get_seqnum(struct wpa_authenticator * wpa_auth,const u8 * addr,int idx,u8 * seq)2206e28a4053SRui Paulo static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
2207e28a4053SRui Paulo const u8 *addr, int idx, u8 *seq)
2208e28a4053SRui Paulo {
220985732ac8SCy Schubert if (wpa_auth->cb->get_seqnum == NULL)
2210e28a4053SRui Paulo return -1;
221185732ac8SCy Schubert return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
2212e28a4053SRui Paulo }
2213e28a4053SRui Paulo
2214e28a4053SRui Paulo
wpa_ft_gtk_subelem(struct wpa_state_machine * sm,size_t * len)2215e28a4053SRui Paulo static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
2216e28a4053SRui Paulo {
2217e28a4053SRui Paulo u8 *subelem;
2218c1d255d3SCy Schubert struct wpa_auth_config *conf = &sm->wpa_auth->conf;
2219e28a4053SRui Paulo struct wpa_group *gsm = sm->group;
2220e28a4053SRui Paulo size_t subelem_len, pad_len;
2221e28a4053SRui Paulo const u8 *key;
2222e28a4053SRui Paulo size_t key_len;
2223c1d255d3SCy Schubert u8 keybuf[WPA_GTK_MAX_LEN];
222485732ac8SCy Schubert const u8 *kek;
222585732ac8SCy Schubert size_t kek_len;
222685732ac8SCy Schubert
222785732ac8SCy Schubert if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
222885732ac8SCy Schubert kek = sm->PTK.kek2;
222985732ac8SCy Schubert kek_len = sm->PTK.kek2_len;
223085732ac8SCy Schubert } else {
223185732ac8SCy Schubert kek = sm->PTK.kek;
223285732ac8SCy Schubert kek_len = sm->PTK.kek_len;
223385732ac8SCy Schubert }
2234e28a4053SRui Paulo
2235e28a4053SRui Paulo key_len = gsm->GTK_len;
2236e28a4053SRui Paulo if (key_len > sizeof(keybuf))
2237e28a4053SRui Paulo return NULL;
2238e28a4053SRui Paulo
2239e28a4053SRui Paulo /*
2240e28a4053SRui Paulo * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
2241e28a4053SRui Paulo * than 16 bytes.
2242e28a4053SRui Paulo */
2243e28a4053SRui Paulo pad_len = key_len % 8;
2244e28a4053SRui Paulo if (pad_len)
2245e28a4053SRui Paulo pad_len = 8 - pad_len;
2246e28a4053SRui Paulo if (key_len + pad_len < 16)
2247e28a4053SRui Paulo pad_len += 8;
22485b9c547cSRui Paulo if (pad_len && key_len < sizeof(keybuf)) {
2249e28a4053SRui Paulo os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
2250c1d255d3SCy Schubert if (conf->disable_gtk ||
2251c1d255d3SCy Schubert sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
2252c1d255d3SCy Schubert /*
2253c1d255d3SCy Schubert * Provide unique random GTK to each STA to prevent use
2254c1d255d3SCy Schubert * of GTK in the BSS.
2255c1d255d3SCy Schubert */
2256c1d255d3SCy Schubert if (random_get_bytes(keybuf, key_len) < 0)
2257c1d255d3SCy Schubert return NULL;
2258c1d255d3SCy Schubert }
2259e28a4053SRui Paulo os_memset(keybuf + key_len, 0, pad_len);
2260e28a4053SRui Paulo keybuf[key_len] = 0xdd;
2261e28a4053SRui Paulo key_len += pad_len;
2262e28a4053SRui Paulo key = keybuf;
2263c1d255d3SCy Schubert } else if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
2264c1d255d3SCy Schubert /*
2265c1d255d3SCy Schubert * Provide unique random GTK to each STA to prevent use of GTK
2266c1d255d3SCy Schubert * in the BSS.
2267c1d255d3SCy Schubert */
2268c1d255d3SCy Schubert if (random_get_bytes(keybuf, key_len) < 0)
2269c1d255d3SCy Schubert return NULL;
2270c1d255d3SCy Schubert key = keybuf;
2271c1d255d3SCy Schubert } else {
2272e28a4053SRui Paulo key = gsm->GTK[gsm->GN - 1];
2273c1d255d3SCy Schubert }
2274e28a4053SRui Paulo
2275e28a4053SRui Paulo /*
2276e28a4053SRui Paulo * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
2277e28a4053SRui Paulo * Key[5..32].
2278e28a4053SRui Paulo */
2279e28a4053SRui Paulo subelem_len = 13 + key_len + 8;
2280e28a4053SRui Paulo subelem = os_zalloc(subelem_len);
2281e28a4053SRui Paulo if (subelem == NULL)
2282e28a4053SRui Paulo return NULL;
2283e28a4053SRui Paulo
2284e28a4053SRui Paulo subelem[0] = FTIE_SUBELEM_GTK;
2285e28a4053SRui Paulo subelem[1] = 11 + key_len + 8;
2286e28a4053SRui Paulo /* Key ID in B0-B1 of Key Info */
2287e28a4053SRui Paulo WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
2288e28a4053SRui Paulo subelem[4] = gsm->GTK_len;
2289e28a4053SRui Paulo wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
229085732ac8SCy Schubert if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) {
229185732ac8SCy Schubert wpa_printf(MSG_DEBUG,
229285732ac8SCy Schubert "FT: GTK subelem encryption failed: kek_len=%d",
229385732ac8SCy Schubert (int) kek_len);
2294*a90b9d01SCy Schubert forced_memzero(keybuf, sizeof(keybuf));
2295e28a4053SRui Paulo os_free(subelem);
2296e28a4053SRui Paulo return NULL;
2297e28a4053SRui Paulo }
2298e28a4053SRui Paulo
2299206b73d0SCy Schubert forced_memzero(keybuf, sizeof(keybuf));
2300e28a4053SRui Paulo *len = subelem_len;
2301e28a4053SRui Paulo return subelem;
2302e28a4053SRui Paulo }
2303e28a4053SRui Paulo
2304e28a4053SRui Paulo
wpa_ft_igtk_subelem(struct wpa_state_machine * sm,size_t * len)2305e28a4053SRui Paulo static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
2306e28a4053SRui Paulo {
2307e28a4053SRui Paulo u8 *subelem, *pos;
2308c1d255d3SCy Schubert struct wpa_auth_config *conf = &sm->wpa_auth->conf;
2309e28a4053SRui Paulo struct wpa_group *gsm = sm->group;
2310e28a4053SRui Paulo size_t subelem_len;
2311c1d255d3SCy Schubert const u8 *kek, *igtk;
231285732ac8SCy Schubert size_t kek_len;
231385732ac8SCy Schubert size_t igtk_len;
23144b72b91aSCy Schubert u8 stub_igtk[WPA_IGTK_MAX_LEN];
231585732ac8SCy Schubert
231685732ac8SCy Schubert if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
231785732ac8SCy Schubert kek = sm->PTK.kek2;
231885732ac8SCy Schubert kek_len = sm->PTK.kek2_len;
231985732ac8SCy Schubert } else {
232085732ac8SCy Schubert kek = sm->PTK.kek;
232185732ac8SCy Schubert kek_len = sm->PTK.kek_len;
232285732ac8SCy Schubert }
232385732ac8SCy Schubert
232485732ac8SCy Schubert igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
2325e28a4053SRui Paulo
2326e28a4053SRui Paulo /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
2327e28a4053SRui Paulo * Key[16+8] */
232885732ac8SCy Schubert subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8;
2329e28a4053SRui Paulo subelem = os_zalloc(subelem_len);
2330e28a4053SRui Paulo if (subelem == NULL)
2331e28a4053SRui Paulo return NULL;
2332e28a4053SRui Paulo
2333e28a4053SRui Paulo pos = subelem;
2334e28a4053SRui Paulo *pos++ = FTIE_SUBELEM_IGTK;
2335e28a4053SRui Paulo *pos++ = subelem_len - 2;
2336e28a4053SRui Paulo WPA_PUT_LE16(pos, gsm->GN_igtk);
2337e28a4053SRui Paulo pos += 2;
2338e28a4053SRui Paulo wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
2339e28a4053SRui Paulo pos += 6;
234085732ac8SCy Schubert *pos++ = igtk_len;
2341c1d255d3SCy Schubert igtk = gsm->IGTK[gsm->GN_igtk - 4];
2342c1d255d3SCy Schubert if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
2343c1d255d3SCy Schubert /*
2344c1d255d3SCy Schubert * Provide unique random IGTK to each STA to prevent use of
2345c1d255d3SCy Schubert * IGTK in the BSS.
2346c1d255d3SCy Schubert */
23474b72b91aSCy Schubert if (random_get_bytes(stub_igtk, igtk_len / 8) < 0) {
2348c1d255d3SCy Schubert os_free(subelem);
2349c1d255d3SCy Schubert return NULL;
2350c1d255d3SCy Schubert }
23514b72b91aSCy Schubert igtk = stub_igtk;
2352c1d255d3SCy Schubert }
2353c1d255d3SCy Schubert if (aes_wrap(kek, kek_len, igtk_len / 8, igtk, pos)) {
235485732ac8SCy Schubert wpa_printf(MSG_DEBUG,
235585732ac8SCy Schubert "FT: IGTK subelem encryption failed: kek_len=%d",
235685732ac8SCy Schubert (int) kek_len);
2357e28a4053SRui Paulo os_free(subelem);
2358e28a4053SRui Paulo return NULL;
2359e28a4053SRui Paulo }
2360e28a4053SRui Paulo
2361e28a4053SRui Paulo *len = subelem_len;
2362e28a4053SRui Paulo return subelem;
2363e28a4053SRui Paulo }
2364c1d255d3SCy Schubert
2365c1d255d3SCy Schubert
wpa_ft_bigtk_subelem(struct wpa_state_machine * sm,size_t * len)2366c1d255d3SCy Schubert static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
2367c1d255d3SCy Schubert {
2368c1d255d3SCy Schubert u8 *subelem, *pos;
2369*a90b9d01SCy Schubert struct wpa_authenticator *wpa_auth = sm->wpa_auth;
2370*a90b9d01SCy Schubert struct wpa_group *gsm = wpa_auth->group;
2371c1d255d3SCy Schubert size_t subelem_len;
2372c1d255d3SCy Schubert const u8 *kek, *bigtk;
2373c1d255d3SCy Schubert size_t kek_len;
2374c1d255d3SCy Schubert size_t bigtk_len;
23754b72b91aSCy Schubert u8 stub_bigtk[WPA_IGTK_MAX_LEN];
2376c1d255d3SCy Schubert
2377c1d255d3SCy Schubert if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
2378c1d255d3SCy Schubert kek = sm->PTK.kek2;
2379c1d255d3SCy Schubert kek_len = sm->PTK.kek2_len;
2380c1d255d3SCy Schubert } else {
2381c1d255d3SCy Schubert kek = sm->PTK.kek;
2382c1d255d3SCy Schubert kek_len = sm->PTK.kek_len;
2383c1d255d3SCy Schubert }
2384c1d255d3SCy Schubert
2385*a90b9d01SCy Schubert bigtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
2386c1d255d3SCy Schubert
2387c1d255d3SCy Schubert /* Sub-elem ID[1] | Length[1] | KeyID[2] | BIPN[6] | Key Length[1] |
2388c1d255d3SCy Schubert * Key[16+8] */
2389c1d255d3SCy Schubert subelem_len = 1 + 1 + 2 + 6 + 1 + bigtk_len + 8;
2390c1d255d3SCy Schubert subelem = os_zalloc(subelem_len);
2391c1d255d3SCy Schubert if (subelem == NULL)
2392c1d255d3SCy Schubert return NULL;
2393c1d255d3SCy Schubert
2394c1d255d3SCy Schubert pos = subelem;
2395c1d255d3SCy Schubert *pos++ = FTIE_SUBELEM_BIGTK;
2396c1d255d3SCy Schubert *pos++ = subelem_len - 2;
2397c1d255d3SCy Schubert WPA_PUT_LE16(pos, gsm->GN_bigtk);
2398c1d255d3SCy Schubert pos += 2;
2399*a90b9d01SCy Schubert wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, pos);
2400c1d255d3SCy Schubert pos += 6;
2401c1d255d3SCy Schubert *pos++ = bigtk_len;
2402*a90b9d01SCy Schubert bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
2403c1d255d3SCy Schubert if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
2404c1d255d3SCy Schubert /*
2405c1d255d3SCy Schubert * Provide unique random BIGTK to each OSEN STA to prevent use
2406c1d255d3SCy Schubert * of BIGTK in the BSS.
2407c1d255d3SCy Schubert */
24084b72b91aSCy Schubert if (random_get_bytes(stub_bigtk, bigtk_len / 8) < 0) {
2409c1d255d3SCy Schubert os_free(subelem);
2410c1d255d3SCy Schubert return NULL;
2411c1d255d3SCy Schubert }
24124b72b91aSCy Schubert bigtk = stub_bigtk;
2413c1d255d3SCy Schubert }
2414c1d255d3SCy Schubert if (aes_wrap(kek, kek_len, bigtk_len / 8, bigtk, pos)) {
2415c1d255d3SCy Schubert wpa_printf(MSG_DEBUG,
2416c1d255d3SCy Schubert "FT: BIGTK subelem encryption failed: kek_len=%d",
2417c1d255d3SCy Schubert (int) kek_len);
2418c1d255d3SCy Schubert os_free(subelem);
2419c1d255d3SCy Schubert return NULL;
2420c1d255d3SCy Schubert }
2421c1d255d3SCy Schubert
2422c1d255d3SCy Schubert *len = subelem_len;
2423c1d255d3SCy Schubert return subelem;
2424c1d255d3SCy Schubert }
2425e28a4053SRui Paulo
2426e28a4053SRui Paulo
wpa_ft_process_rdie(struct wpa_state_machine * sm,u8 * pos,u8 * end,u8 id,u8 descr_count,const u8 * ies,size_t ies_len)2427f05cddf9SRui Paulo static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
2428f05cddf9SRui Paulo u8 *pos, u8 *end, u8 id, u8 descr_count,
2429e28a4053SRui Paulo const u8 *ies, size_t ies_len)
2430e28a4053SRui Paulo {
2431e28a4053SRui Paulo struct ieee802_11_elems parse;
2432e28a4053SRui Paulo struct rsn_rdie *rdie;
2433e28a4053SRui Paulo
2434e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d",
2435e28a4053SRui Paulo id, descr_count);
2436e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)",
2437e28a4053SRui Paulo ies, ies_len);
2438e28a4053SRui Paulo
2439e28a4053SRui Paulo if (end - pos < (int) sizeof(*rdie)) {
2440e28a4053SRui Paulo wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE");
2441e28a4053SRui Paulo return pos;
2442e28a4053SRui Paulo }
2443e28a4053SRui Paulo
2444e28a4053SRui Paulo *pos++ = WLAN_EID_RIC_DATA;
2445e28a4053SRui Paulo *pos++ = sizeof(*rdie);
2446e28a4053SRui Paulo rdie = (struct rsn_rdie *) pos;
2447e28a4053SRui Paulo rdie->id = id;
2448e28a4053SRui Paulo rdie->descr_count = 0;
2449e28a4053SRui Paulo rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS);
2450e28a4053SRui Paulo pos += sizeof(*rdie);
2451e28a4053SRui Paulo
2452e28a4053SRui Paulo if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) ==
2453e28a4053SRui Paulo ParseFailed) {
2454e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs");
2455e28a4053SRui Paulo rdie->status_code =
2456e28a4053SRui Paulo host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
2457e28a4053SRui Paulo return pos;
2458e28a4053SRui Paulo }
2459e28a4053SRui Paulo
2460325151a3SRui Paulo if (parse.wmm_tspec) {
2461e28a4053SRui Paulo struct wmm_tspec_element *tspec;
2462e28a4053SRui Paulo
2463e28a4053SRui Paulo if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) {
2464e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE "
2465e28a4053SRui Paulo "(%d)", (int) parse.wmm_tspec_len);
2466e28a4053SRui Paulo rdie->status_code =
2467e28a4053SRui Paulo host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
2468e28a4053SRui Paulo return pos;
2469e28a4053SRui Paulo }
2470e28a4053SRui Paulo if (end - pos < (int) sizeof(*tspec)) {
2471e28a4053SRui Paulo wpa_printf(MSG_ERROR, "FT: Not enough room for "
2472e28a4053SRui Paulo "response TSPEC");
2473e28a4053SRui Paulo rdie->status_code =
2474e28a4053SRui Paulo host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
2475e28a4053SRui Paulo return pos;
2476e28a4053SRui Paulo }
2477e28a4053SRui Paulo tspec = (struct wmm_tspec_element *) pos;
2478e28a4053SRui Paulo os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
2479325151a3SRui Paulo }
2480325151a3SRui Paulo
2481325151a3SRui Paulo #ifdef NEED_AP_MLME
2482325151a3SRui Paulo if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
2483325151a3SRui Paulo int res;
2484325151a3SRui Paulo
2485325151a3SRui Paulo res = wmm_process_tspec((struct wmm_tspec_element *) pos);
2486e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res);
2487e28a4053SRui Paulo if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS)
2488e28a4053SRui Paulo rdie->status_code =
2489e28a4053SRui Paulo host_to_le16(WLAN_STATUS_INVALID_PARAMETERS);
2490e28a4053SRui Paulo else if (res == WMM_ADDTS_STATUS_REFUSED)
2491e28a4053SRui Paulo rdie->status_code =
2492e28a4053SRui Paulo host_to_le16(WLAN_STATUS_REQUEST_DECLINED);
2493e28a4053SRui Paulo else {
2494e28a4053SRui Paulo /* TSPEC accepted; include updated TSPEC in response */
2495e28a4053SRui Paulo rdie->descr_count = 1;
2496325151a3SRui Paulo pos += sizeof(struct wmm_tspec_element);
2497e28a4053SRui Paulo }
2498e28a4053SRui Paulo return pos;
2499e28a4053SRui Paulo }
2500e28a4053SRui Paulo #endif /* NEED_AP_MLME */
2501e28a4053SRui Paulo
2502f05cddf9SRui Paulo if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
2503f05cddf9SRui Paulo int res;
2504f05cddf9SRui Paulo
2505f05cddf9SRui Paulo res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
2506325151a3SRui Paulo sizeof(struct wmm_tspec_element));
2507f05cddf9SRui Paulo if (res >= 0) {
2508f05cddf9SRui Paulo if (res)
2509f05cddf9SRui Paulo rdie->status_code = host_to_le16(res);
2510f05cddf9SRui Paulo else {
2511f05cddf9SRui Paulo /* TSPEC accepted; include updated TSPEC in
2512f05cddf9SRui Paulo * response */
2513f05cddf9SRui Paulo rdie->descr_count = 1;
2514325151a3SRui Paulo pos += sizeof(struct wmm_tspec_element);
2515f05cddf9SRui Paulo }
2516f05cddf9SRui Paulo return pos;
2517f05cddf9SRui Paulo }
2518f05cddf9SRui Paulo }
2519f05cddf9SRui Paulo
2520e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
2521e28a4053SRui Paulo rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
2522e28a4053SRui Paulo return pos;
2523e28a4053SRui Paulo }
2524e28a4053SRui Paulo
2525e28a4053SRui Paulo
wpa_ft_process_ric(struct wpa_state_machine * sm,u8 * pos,u8 * end,const u8 * ric,size_t ric_len)2526f05cddf9SRui Paulo static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end,
2527f05cddf9SRui Paulo const u8 *ric, size_t ric_len)
2528e28a4053SRui Paulo {
2529e28a4053SRui Paulo const u8 *rpos, *start;
2530e28a4053SRui Paulo const struct rsn_rdie *rdie;
2531e28a4053SRui Paulo
2532e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len);
2533e28a4053SRui Paulo
2534e28a4053SRui Paulo rpos = ric;
2535e28a4053SRui Paulo while (rpos + sizeof(*rdie) < ric + ric_len) {
2536e28a4053SRui Paulo if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) ||
2537e28a4053SRui Paulo rpos + 2 + rpos[1] > ric + ric_len)
2538e28a4053SRui Paulo break;
2539e28a4053SRui Paulo rdie = (const struct rsn_rdie *) (rpos + 2);
2540e28a4053SRui Paulo rpos += 2 + rpos[1];
2541e28a4053SRui Paulo start = rpos;
2542e28a4053SRui Paulo
2543e28a4053SRui Paulo while (rpos + 2 <= ric + ric_len &&
2544e28a4053SRui Paulo rpos + 2 + rpos[1] <= ric + ric_len) {
2545e28a4053SRui Paulo if (rpos[0] == WLAN_EID_RIC_DATA)
2546e28a4053SRui Paulo break;
2547e28a4053SRui Paulo rpos += 2 + rpos[1];
2548e28a4053SRui Paulo }
2549f05cddf9SRui Paulo pos = wpa_ft_process_rdie(sm, pos, end, rdie->id,
2550e28a4053SRui Paulo rdie->descr_count,
2551e28a4053SRui Paulo start, rpos - start);
2552e28a4053SRui Paulo }
2553e28a4053SRui Paulo
2554e28a4053SRui Paulo return pos;
2555e28a4053SRui Paulo }
2556e28a4053SRui Paulo
2557e28a4053SRui Paulo
wpa_sm_write_assoc_resp_ies(struct wpa_state_machine * sm,u8 * pos,size_t max_len,int auth_alg,const u8 * req_ies,size_t req_ies_len,int omit_rsnxe)2558e28a4053SRui Paulo u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
2559e28a4053SRui Paulo size_t max_len, int auth_alg,
2560c1d255d3SCy Schubert const u8 *req_ies, size_t req_ies_len,
2561c1d255d3SCy Schubert int omit_rsnxe)
2562e28a4053SRui Paulo {
2563e28a4053SRui Paulo u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
256485732ac8SCy Schubert u8 *fte_mic, *elem_count;
2565e28a4053SRui Paulo size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
2566c1d255d3SCy Schubert u8 rsnxe_buf[10], *rsnxe = rsnxe_buf;
2567c1d255d3SCy Schubert size_t rsnxe_len;
2568c1d255d3SCy Schubert int rsnxe_used;
2569e28a4053SRui Paulo int res;
2570e28a4053SRui Paulo struct wpa_auth_config *conf;
2571e28a4053SRui Paulo struct wpa_ft_ies parse;
2572e28a4053SRui Paulo u8 *ric_start;
2573e28a4053SRui Paulo u8 *anonce, *snonce;
257485732ac8SCy Schubert const u8 *kck;
257585732ac8SCy Schubert size_t kck_len;
2576*a90b9d01SCy Schubert size_t key_len;
2577e28a4053SRui Paulo
2578e28a4053SRui Paulo if (sm == NULL)
2579e28a4053SRui Paulo return pos;
2580e28a4053SRui Paulo
2581e28a4053SRui Paulo conf = &sm->wpa_auth->conf;
2582e28a4053SRui Paulo
25835b9c547cSRui Paulo if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
2584e28a4053SRui Paulo return pos;
2585e28a4053SRui Paulo
2586e28a4053SRui Paulo end = pos + max_len;
2587e28a4053SRui Paulo
2588c1d255d3SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
2589c1d255d3SCy Schubert if (auth_alg == WLAN_AUTH_FT &&
2590c1d255d3SCy Schubert sm->wpa_auth->conf.rsne_override_ft_set) {
2591c1d255d3SCy Schubert wpa_printf(MSG_DEBUG,
2592c1d255d3SCy Schubert "TESTING: RSNE FT override for MIC calculation");
2593c1d255d3SCy Schubert rsnie = sm->wpa_auth->conf.rsne_override_ft;
2594c1d255d3SCy Schubert rsnie_len = sm->wpa_auth->conf.rsne_override_ft_len;
2595c1d255d3SCy Schubert if (end - pos < (long int) rsnie_len)
2596c1d255d3SCy Schubert return pos;
2597c1d255d3SCy Schubert os_memcpy(pos, rsnie, rsnie_len);
2598c1d255d3SCy Schubert rsnie = pos;
2599c1d255d3SCy Schubert pos += rsnie_len;
2600c1d255d3SCy Schubert if (rsnie_len > PMKID_LEN && sm->pmk_r1_name_valid) {
2601c1d255d3SCy Schubert int idx;
2602c1d255d3SCy Schubert
2603c1d255d3SCy Schubert /* Replace all 0xff PMKID with the valid PMKR1Name */
2604c1d255d3SCy Schubert for (idx = 0; idx < PMKID_LEN; idx++) {
2605c1d255d3SCy Schubert if (rsnie[rsnie_len - 1 - idx] != 0xff)
2606c1d255d3SCy Schubert break;
2607c1d255d3SCy Schubert }
2608c1d255d3SCy Schubert if (idx == PMKID_LEN)
2609c1d255d3SCy Schubert os_memcpy(&rsnie[rsnie_len - PMKID_LEN],
2610c1d255d3SCy Schubert sm->pmk_r1_name, WPA_PMK_NAME_LEN);
2611c1d255d3SCy Schubert }
2612c1d255d3SCy Schubert } else
2613c1d255d3SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
26144bc52338SCy Schubert if (auth_alg == WLAN_AUTH_FT ||
26154bc52338SCy Schubert ((auth_alg == WLAN_AUTH_FILS_SK ||
26164bc52338SCy Schubert auth_alg == WLAN_AUTH_FILS_SK_PFS ||
26174bc52338SCy Schubert auth_alg == WLAN_AUTH_FILS_PK) &&
26184bc52338SCy Schubert (sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
26194bc52338SCy Schubert WPA_KEY_MGMT_FT_FILS_SHA384)))) {
26204bc52338SCy Schubert if (!sm->pmk_r1_name_valid) {
26214bc52338SCy Schubert wpa_printf(MSG_ERROR,
26224bc52338SCy Schubert "FT: PMKR1Name is not valid for Assoc Resp RSNE");
26234bc52338SCy Schubert return NULL;
26244bc52338SCy Schubert }
26254bc52338SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE",
26264bc52338SCy Schubert sm->pmk_r1_name, WPA_PMK_NAME_LEN);
2627e28a4053SRui Paulo /*
2628e28a4053SRui Paulo * RSN (only present if this is a Reassociation Response and
26294bc52338SCy Schubert * part of a fast BSS transition; or if this is a
26304bc52338SCy Schubert * (Re)Association Response frame during an FT initial mobility
26314bc52338SCy Schubert * domain association using FILS)
2632e28a4053SRui Paulo */
2633e28a4053SRui Paulo res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
2634e28a4053SRui Paulo if (res < 0)
263585732ac8SCy Schubert return NULL;
2636e28a4053SRui Paulo rsnie = pos;
2637e28a4053SRui Paulo rsnie_len = res;
2638e28a4053SRui Paulo pos += res;
2639e28a4053SRui Paulo }
2640e28a4053SRui Paulo
2641e28a4053SRui Paulo /* Mobility Domain Information */
2642e28a4053SRui Paulo res = wpa_write_mdie(conf, pos, end - pos);
2643e28a4053SRui Paulo if (res < 0)
264485732ac8SCy Schubert return NULL;
2645e28a4053SRui Paulo mdie = pos;
2646e28a4053SRui Paulo mdie_len = res;
2647e28a4053SRui Paulo pos += res;
2648e28a4053SRui Paulo
2649e28a4053SRui Paulo /* Fast BSS Transition Information */
2650e28a4053SRui Paulo if (auth_alg == WLAN_AUTH_FT) {
2651e28a4053SRui Paulo subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
265285732ac8SCy Schubert if (!subelem) {
265385732ac8SCy Schubert wpa_printf(MSG_DEBUG,
265485732ac8SCy Schubert "FT: Failed to add GTK subelement");
265585732ac8SCy Schubert return NULL;
265685732ac8SCy Schubert }
2657e28a4053SRui Paulo r0kh_id = sm->r0kh_id;
2658e28a4053SRui Paulo r0kh_id_len = sm->r0kh_id_len;
2659e28a4053SRui Paulo anonce = sm->ANonce;
2660e28a4053SRui Paulo snonce = sm->SNonce;
2661e28a4053SRui Paulo if (sm->mgmt_frame_prot) {
2662e28a4053SRui Paulo u8 *igtk;
2663e28a4053SRui Paulo size_t igtk_len;
2664e28a4053SRui Paulo u8 *nbuf;
2665e28a4053SRui Paulo igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
2666e28a4053SRui Paulo if (igtk == NULL) {
266785732ac8SCy Schubert wpa_printf(MSG_DEBUG,
266885732ac8SCy Schubert "FT: Failed to add IGTK subelement");
2669e28a4053SRui Paulo os_free(subelem);
267085732ac8SCy Schubert return NULL;
2671e28a4053SRui Paulo }
2672e28a4053SRui Paulo nbuf = os_realloc(subelem, subelem_len + igtk_len);
2673e28a4053SRui Paulo if (nbuf == NULL) {
2674e28a4053SRui Paulo os_free(subelem);
2675e28a4053SRui Paulo os_free(igtk);
267685732ac8SCy Schubert return NULL;
2677e28a4053SRui Paulo }
2678e28a4053SRui Paulo subelem = nbuf;
2679e28a4053SRui Paulo os_memcpy(subelem + subelem_len, igtk, igtk_len);
2680e28a4053SRui Paulo subelem_len += igtk_len;
2681e28a4053SRui Paulo os_free(igtk);
2682e28a4053SRui Paulo }
2683c1d255d3SCy Schubert if (sm->mgmt_frame_prot && conf->beacon_prot) {
2684c1d255d3SCy Schubert u8 *bigtk;
2685c1d255d3SCy Schubert size_t bigtk_len;
2686c1d255d3SCy Schubert u8 *nbuf;
2687c1d255d3SCy Schubert
2688c1d255d3SCy Schubert bigtk = wpa_ft_bigtk_subelem(sm, &bigtk_len);
2689c1d255d3SCy Schubert if (!bigtk) {
2690c1d255d3SCy Schubert wpa_printf(MSG_DEBUG,
2691c1d255d3SCy Schubert "FT: Failed to add BIGTK subelement");
2692c1d255d3SCy Schubert os_free(subelem);
2693c1d255d3SCy Schubert return NULL;
2694c1d255d3SCy Schubert }
2695c1d255d3SCy Schubert nbuf = os_realloc(subelem, subelem_len + bigtk_len);
2696c1d255d3SCy Schubert if (!nbuf) {
2697c1d255d3SCy Schubert os_free(subelem);
2698c1d255d3SCy Schubert os_free(bigtk);
2699c1d255d3SCy Schubert return NULL;
2700c1d255d3SCy Schubert }
2701c1d255d3SCy Schubert subelem = nbuf;
2702c1d255d3SCy Schubert os_memcpy(subelem + subelem_len, bigtk, bigtk_len);
2703c1d255d3SCy Schubert subelem_len += bigtk_len;
2704c1d255d3SCy Schubert os_free(bigtk);
2705c1d255d3SCy Schubert }
27064bc52338SCy Schubert #ifdef CONFIG_OCV
27074bc52338SCy Schubert if (wpa_auth_uses_ocv(sm)) {
27084bc52338SCy Schubert struct wpa_channel_info ci;
27094bc52338SCy Schubert u8 *nbuf, *ocipos;
27104bc52338SCy Schubert
27114bc52338SCy Schubert if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
27124bc52338SCy Schubert wpa_printf(MSG_WARNING,
27134bc52338SCy Schubert "Failed to get channel info for OCI element");
27144bc52338SCy Schubert os_free(subelem);
27154bc52338SCy Schubert return NULL;
27164bc52338SCy Schubert }
2717c1d255d3SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
2718c1d255d3SCy Schubert if (conf->oci_freq_override_ft_assoc) {
2719c1d255d3SCy Schubert wpa_printf(MSG_INFO,
2720c1d255d3SCy Schubert "TEST: Override OCI frequency %d -> %u MHz",
2721c1d255d3SCy Schubert ci.frequency,
2722c1d255d3SCy Schubert conf->oci_freq_override_ft_assoc);
2723c1d255d3SCy Schubert ci.frequency = conf->oci_freq_override_ft_assoc;
2724c1d255d3SCy Schubert }
2725c1d255d3SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
27264bc52338SCy Schubert
27274bc52338SCy Schubert subelem_len += 2 + OCV_OCI_LEN;
27284bc52338SCy Schubert nbuf = os_realloc(subelem, subelem_len);
27294bc52338SCy Schubert if (!nbuf) {
27304bc52338SCy Schubert os_free(subelem);
27314bc52338SCy Schubert return NULL;
27324bc52338SCy Schubert }
27334bc52338SCy Schubert subelem = nbuf;
27344bc52338SCy Schubert
27354bc52338SCy Schubert ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN;
27364bc52338SCy Schubert *ocipos++ = FTIE_SUBELEM_OCI;
27374bc52338SCy Schubert *ocipos++ = OCV_OCI_LEN;
27384bc52338SCy Schubert if (ocv_insert_oci(&ci, &ocipos) < 0) {
27394bc52338SCy Schubert os_free(subelem);
27404bc52338SCy Schubert return NULL;
27414bc52338SCy Schubert }
27424bc52338SCy Schubert }
27434bc52338SCy Schubert #endif /* CONFIG_OCV */
2744e28a4053SRui Paulo } else {
2745e28a4053SRui Paulo r0kh_id = conf->r0_key_holder;
2746e28a4053SRui Paulo r0kh_id_len = conf->r0_key_holder_len;
2747e28a4053SRui Paulo anonce = NULL;
2748e28a4053SRui Paulo snonce = NULL;
2749e28a4053SRui Paulo }
2750c1d255d3SCy Schubert rsnxe_used = (auth_alg == WLAN_AUTH_FT) &&
2751*a90b9d01SCy Schubert (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
2752*a90b9d01SCy Schubert conf->sae_pwe == SAE_PWE_BOTH);
2753c1d255d3SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
2754c1d255d3SCy Schubert if (sm->wpa_auth->conf.ft_rsnxe_used) {
2755c1d255d3SCy Schubert rsnxe_used = sm->wpa_auth->conf.ft_rsnxe_used == 1;
2756c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "TESTING: FT: Force RSNXE Used %d",
2757c1d255d3SCy Schubert rsnxe_used);
2758c1d255d3SCy Schubert }
2759c1d255d3SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
2760*a90b9d01SCy Schubert key_len = sm->xxkey_len;
2761*a90b9d01SCy Schubert if (!key_len)
2762*a90b9d01SCy Schubert key_len = sm->pmk_r1_len;
2763*a90b9d01SCy Schubert if (!key_len && sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
2764*a90b9d01SCy Schubert sm->wpa_auth->cb->get_psk) {
2765*a90b9d01SCy Schubert size_t psk_len;
2766*a90b9d01SCy Schubert
2767*a90b9d01SCy Schubert if (sm->wpa_auth->cb->get_psk(sm->wpa_auth->cb_ctx,
2768*a90b9d01SCy Schubert sm->addr, sm->p2p_dev_addr,
2769*a90b9d01SCy Schubert NULL, &psk_len, NULL))
2770*a90b9d01SCy Schubert key_len = psk_len;
2771*a90b9d01SCy Schubert }
2772*a90b9d01SCy Schubert res = wpa_write_ftie(conf, sm->wpa_key_mgmt, key_len,
2773*a90b9d01SCy Schubert r0kh_id, r0kh_id_len,
277485732ac8SCy Schubert anonce, snonce, pos, end - pos,
2775c1d255d3SCy Schubert subelem, subelem_len, rsnxe_used);
2776e28a4053SRui Paulo os_free(subelem);
2777e28a4053SRui Paulo if (res < 0)
277885732ac8SCy Schubert return NULL;
2779e28a4053SRui Paulo ftie = pos;
2780e28a4053SRui Paulo ftie_len = res;
2781e28a4053SRui Paulo pos += res;
2782e28a4053SRui Paulo
2783*a90b9d01SCy Schubert if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
2784*a90b9d01SCy Schubert key_len == SHA512_MAC_LEN) {
2785*a90b9d01SCy Schubert struct rsn_ftie_sha512 *_ftie =
2786*a90b9d01SCy Schubert (struct rsn_ftie_sha512 *) (ftie + 2);
2787*a90b9d01SCy Schubert
2788*a90b9d01SCy Schubert fte_mic = _ftie->mic;
2789*a90b9d01SCy Schubert elem_count = &_ftie->mic_control[1];
2790*a90b9d01SCy Schubert } else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
2791*a90b9d01SCy Schubert key_len == SHA384_MAC_LEN) ||
2792*a90b9d01SCy Schubert wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
279385732ac8SCy Schubert struct rsn_ftie_sha384 *_ftie =
279485732ac8SCy Schubert (struct rsn_ftie_sha384 *) (ftie + 2);
279585732ac8SCy Schubert
279685732ac8SCy Schubert fte_mic = _ftie->mic;
279785732ac8SCy Schubert elem_count = &_ftie->mic_control[1];
279885732ac8SCy Schubert } else {
279985732ac8SCy Schubert struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2);
280085732ac8SCy Schubert
280185732ac8SCy Schubert fte_mic = _ftie->mic;
280285732ac8SCy Schubert elem_count = &_ftie->mic_control[1];
280385732ac8SCy Schubert }
2804e28a4053SRui Paulo if (auth_alg == WLAN_AUTH_FT)
280585732ac8SCy Schubert *elem_count = 3; /* Information element count */
2806e28a4053SRui Paulo
2807e28a4053SRui Paulo ric_start = pos;
2808*a90b9d01SCy Schubert if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse,
2809*a90b9d01SCy Schubert sm->wpa_key_mgmt, false) == 0 && parse.ric) {
2810f05cddf9SRui Paulo pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
2811f05cddf9SRui Paulo parse.ric_len);
2812e28a4053SRui Paulo if (auth_alg == WLAN_AUTH_FT)
281385732ac8SCy Schubert *elem_count +=
2814e28a4053SRui Paulo ieee802_11_ie_count(ric_start,
2815e28a4053SRui Paulo pos - ric_start);
2816e28a4053SRui Paulo }
2817e28a4053SRui Paulo if (ric_start == pos)
2818e28a4053SRui Paulo ric_start = NULL;
2819e28a4053SRui Paulo
2820c1d255d3SCy Schubert if (omit_rsnxe) {
2821c1d255d3SCy Schubert rsnxe_len = 0;
2822c1d255d3SCy Schubert } else {
2823c1d255d3SCy Schubert res = wpa_write_rsnxe(&sm->wpa_auth->conf, rsnxe,
2824c1d255d3SCy Schubert sizeof(rsnxe_buf));
2825*a90b9d01SCy Schubert if (res < 0) {
2826*a90b9d01SCy Schubert pos = NULL;
2827*a90b9d01SCy Schubert goto fail;
2828*a90b9d01SCy Schubert }
2829c1d255d3SCy Schubert rsnxe_len = res;
2830c1d255d3SCy Schubert }
2831c1d255d3SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
2832c1d255d3SCy Schubert if (auth_alg == WLAN_AUTH_FT &&
2833c1d255d3SCy Schubert sm->wpa_auth->conf.rsnxe_override_ft_set) {
2834c1d255d3SCy Schubert wpa_printf(MSG_DEBUG,
2835c1d255d3SCy Schubert "TESTING: RSNXE FT override for MIC calculation");
2836c1d255d3SCy Schubert rsnxe = sm->wpa_auth->conf.rsnxe_override_ft;
2837c1d255d3SCy Schubert rsnxe_len = sm->wpa_auth->conf.rsnxe_override_ft_len;
2838c1d255d3SCy Schubert }
2839c1d255d3SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
2840c1d255d3SCy Schubert if (auth_alg == WLAN_AUTH_FT && rsnxe_len)
2841c1d255d3SCy Schubert *elem_count += 1;
2842c1d255d3SCy Schubert
284385732ac8SCy Schubert if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
284485732ac8SCy Schubert kck = sm->PTK.kck2;
284585732ac8SCy Schubert kck_len = sm->PTK.kck2_len;
284685732ac8SCy Schubert } else {
284785732ac8SCy Schubert kck = sm->PTK.kck;
284885732ac8SCy Schubert kck_len = sm->PTK.kck_len;
284985732ac8SCy Schubert }
2850e28a4053SRui Paulo if (auth_alg == WLAN_AUTH_FT &&
2851*a90b9d01SCy Schubert wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
2852*a90b9d01SCy Schubert sm->addr, sm->wpa_auth->addr, 6,
2853e28a4053SRui Paulo mdie, mdie_len, ftie, ftie_len,
2854e28a4053SRui Paulo rsnie, rsnie_len,
2855e28a4053SRui Paulo ric_start, ric_start ? pos - ric_start : 0,
2856c1d255d3SCy Schubert rsnxe_len ? rsnxe : NULL, rsnxe_len,
2857*a90b9d01SCy Schubert NULL,
285885732ac8SCy Schubert fte_mic) < 0) {
2859e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
2860*a90b9d01SCy Schubert pos = NULL;
2861*a90b9d01SCy Schubert goto fail;
286285732ac8SCy Schubert }
2863e28a4053SRui Paulo
2864780fb4a2SCy Schubert os_free(sm->assoc_resp_ftie);
2865780fb4a2SCy Schubert sm->assoc_resp_ftie = os_malloc(ftie_len);
2866*a90b9d01SCy Schubert if (!sm->assoc_resp_ftie) {
2867*a90b9d01SCy Schubert pos = NULL;
2868*a90b9d01SCy Schubert goto fail;
2869*a90b9d01SCy Schubert }
2870780fb4a2SCy Schubert os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
2871780fb4a2SCy Schubert
2872*a90b9d01SCy Schubert fail:
2873*a90b9d01SCy Schubert wpa_ft_parse_ies_free(&parse);
2874e28a4053SRui Paulo return pos;
2875e28a4053SRui Paulo }
2876e28a4053SRui Paulo
2877e28a4053SRui Paulo
wpa_auth_set_key(struct wpa_authenticator * wpa_auth,int vlan_id,enum wpa_alg alg,const u8 * addr,int idx,u8 * key,size_t key_len,enum key_flag key_flag)2878e28a4053SRui Paulo static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
2879e28a4053SRui Paulo int vlan_id,
2880e28a4053SRui Paulo enum wpa_alg alg, const u8 *addr, int idx,
2881c1d255d3SCy Schubert u8 *key, size_t key_len,
2882c1d255d3SCy Schubert enum key_flag key_flag)
2883e28a4053SRui Paulo {
288485732ac8SCy Schubert if (wpa_auth->cb->set_key == NULL)
2885e28a4053SRui Paulo return -1;
288685732ac8SCy Schubert return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
2887c1d255d3SCy Schubert key, key_len, key_flag);
2888e28a4053SRui Paulo }
2889e28a4053SRui Paulo
2890e28a4053SRui Paulo
2891*a90b9d01SCy Schubert #ifdef CONFIG_PASN
wpa_auth_set_ltf_keyseed(struct wpa_authenticator * wpa_auth,const u8 * peer_addr,const u8 * ltf_keyseed,size_t ltf_keyseed_len)2892*a90b9d01SCy Schubert static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
2893*a90b9d01SCy Schubert const u8 *peer_addr,
2894*a90b9d01SCy Schubert const u8 *ltf_keyseed,
2895*a90b9d01SCy Schubert size_t ltf_keyseed_len)
2896*a90b9d01SCy Schubert {
2897*a90b9d01SCy Schubert if (!wpa_auth->cb->set_ltf_keyseed)
2898*a90b9d01SCy Schubert return -1;
2899*a90b9d01SCy Schubert return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
2900*a90b9d01SCy Schubert ltf_keyseed, ltf_keyseed_len);
2901*a90b9d01SCy Schubert }
2902*a90b9d01SCy Schubert #endif /* CONFIG_PASN */
2903*a90b9d01SCy Schubert
2904*a90b9d01SCy Schubert
wpa_auth_add_sta_ft(struct wpa_authenticator * wpa_auth,const u8 * addr)2905c1d255d3SCy Schubert static inline int wpa_auth_add_sta_ft(struct wpa_authenticator *wpa_auth,
2906c1d255d3SCy Schubert const u8 *addr)
2907c1d255d3SCy Schubert {
2908c1d255d3SCy Schubert if (!wpa_auth->cb->add_sta_ft)
2909c1d255d3SCy Schubert return -1;
2910c1d255d3SCy Schubert return wpa_auth->cb->add_sta_ft(wpa_auth->cb_ctx, addr);
2911c1d255d3SCy Schubert }
2912c1d255d3SCy Schubert
2913c1d255d3SCy Schubert
wpa_ft_install_ptk(struct wpa_state_machine * sm,int retry)2914c1d255d3SCy Schubert void wpa_ft_install_ptk(struct wpa_state_machine *sm, int retry)
2915e28a4053SRui Paulo {
2916e28a4053SRui Paulo enum wpa_alg alg;
2917e28a4053SRui Paulo int klen;
2918e28a4053SRui Paulo
2919e28a4053SRui Paulo /* MLME-SETKEYS.request(PTK) */
2920f05cddf9SRui Paulo alg = wpa_cipher_to_alg(sm->pairwise);
2921f05cddf9SRui Paulo klen = wpa_cipher_key_len(sm->pairwise);
2922f05cddf9SRui Paulo if (!wpa_cipher_valid_pairwise(sm->pairwise)) {
2923e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
2924e28a4053SRui Paulo "PTK configuration", sm->pairwise);
2925e28a4053SRui Paulo return;
2926e28a4053SRui Paulo }
2927e28a4053SRui Paulo
2928a2063804SGordon Tetlow if (sm->tk_already_set) {
2929a2063804SGordon Tetlow /* Must avoid TK reconfiguration to prevent clearing of TX/RX
2930a2063804SGordon Tetlow * PN in the driver */
2931a2063804SGordon Tetlow wpa_printf(MSG_DEBUG,
2932a2063804SGordon Tetlow "FT: Do not re-install same PTK to the driver");
2933a2063804SGordon Tetlow return;
2934a2063804SGordon Tetlow }
2935a2063804SGordon Tetlow
2936c1d255d3SCy Schubert if (!retry)
2937c1d255d3SCy Schubert wpa_auth_add_sta_ft(sm->wpa_auth, sm->addr);
2938c1d255d3SCy Schubert
2939e28a4053SRui Paulo /* FIX: add STA entry to kernel/driver here? The set_key will fail
2940e28a4053SRui Paulo * most likely without this.. At the moment, STA entry is added only
2941e28a4053SRui Paulo * after association has been completed. This function will be called
2942e28a4053SRui Paulo * again after association to get the PTK configured, but that could be
2943e28a4053SRui Paulo * optimized by adding the STA entry earlier.
2944e28a4053SRui Paulo */
2945c1d255d3SCy Schubert if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, sm->keyidx_active,
2946c1d255d3SCy Schubert sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
2947e28a4053SRui Paulo return;
2948e28a4053SRui Paulo
2949*a90b9d01SCy Schubert #ifdef CONFIG_PASN
2950*a90b9d01SCy Schubert if (sm->wpa_auth->conf.secure_ltf &&
2951*a90b9d01SCy Schubert ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
2952*a90b9d01SCy Schubert wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
2953*a90b9d01SCy Schubert sm->PTK.ltf_keyseed,
2954*a90b9d01SCy Schubert sm->PTK.ltf_keyseed_len)) {
2955*a90b9d01SCy Schubert wpa_printf(MSG_ERROR,
2956*a90b9d01SCy Schubert "FT: Failed to set LTF keyseed to driver");
2957*a90b9d01SCy Schubert return;
2958*a90b9d01SCy Schubert }
2959*a90b9d01SCy Schubert #endif /* CONFIG_PASN */
2960*a90b9d01SCy Schubert
2961e28a4053SRui Paulo /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
2962c1d255d3SCy Schubert sm->pairwise_set = true;
2963c1d255d3SCy Schubert sm->tk_already_set = true;
2964*a90b9d01SCy Schubert
2965*a90b9d01SCy Schubert wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
2966*a90b9d01SCy Schubert dot11RSNAConfigPMKLifetime, &sm->PTK);
2967e28a4053SRui Paulo }
2968e28a4053SRui Paulo
2969e28a4053SRui Paulo
297085732ac8SCy Schubert /* Derive PMK-R1 from PSK, check all available PSK */
wpa_ft_psk_pmk_r1(struct wpa_state_machine * sm,const u8 * req_pmk_r1_name,u8 * out_pmk_r1,int * out_pairwise,struct vlan_description * out_vlan,const u8 ** out_identity,size_t * out_identity_len,const u8 ** out_radius_cui,size_t * out_radius_cui_len,int * out_session_timeout)297185732ac8SCy Schubert static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
297285732ac8SCy Schubert const u8 *req_pmk_r1_name,
297385732ac8SCy Schubert u8 *out_pmk_r1, int *out_pairwise,
297485732ac8SCy Schubert struct vlan_description *out_vlan,
297585732ac8SCy Schubert const u8 **out_identity, size_t *out_identity_len,
297685732ac8SCy Schubert const u8 **out_radius_cui,
297785732ac8SCy Schubert size_t *out_radius_cui_len,
297885732ac8SCy Schubert int *out_session_timeout)
297985732ac8SCy Schubert {
298085732ac8SCy Schubert const u8 *pmk = NULL;
298185732ac8SCy Schubert u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
298285732ac8SCy Schubert u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
298385732ac8SCy Schubert struct wpa_authenticator *wpa_auth = sm->wpa_auth;
298485732ac8SCy Schubert const u8 *mdid = wpa_auth->conf.mobility_domain;
298585732ac8SCy Schubert const u8 *r0kh = sm->r0kh_id;
298685732ac8SCy Schubert size_t r0kh_len = sm->r0kh_id_len;
298785732ac8SCy Schubert const u8 *r1kh = wpa_auth->conf.r1_key_holder;
298885732ac8SCy Schubert const u8 *ssid = wpa_auth->conf.ssid;
298985732ac8SCy Schubert size_t ssid_len = wpa_auth->conf.ssid_len;
299085732ac8SCy Schubert int pairwise;
299185732ac8SCy Schubert
299285732ac8SCy Schubert pairwise = sm->pairwise;
299385732ac8SCy Schubert
299485732ac8SCy Schubert for (;;) {
299585732ac8SCy Schubert pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
299685732ac8SCy Schubert pmk);
299785732ac8SCy Schubert if (pmk == NULL)
299885732ac8SCy Schubert break;
299985732ac8SCy Schubert
300085732ac8SCy Schubert if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
300185732ac8SCy Schubert r0kh_len, sm->addr,
3002*a90b9d01SCy Schubert pmk_r0, pmk_r0_name,
3003*a90b9d01SCy Schubert WPA_KEY_MGMT_FT_PSK) < 0 ||
300485732ac8SCy Schubert wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
300585732ac8SCy Schubert sm->addr, pmk_r1, pmk_r1_name) < 0 ||
300685732ac8SCy Schubert os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
300785732ac8SCy Schubert WPA_PMK_NAME_LEN) != 0)
300885732ac8SCy Schubert continue;
300985732ac8SCy Schubert
301085732ac8SCy Schubert /* We found a PSK that matches the requested pmk_r1_name */
301185732ac8SCy Schubert wpa_printf(MSG_DEBUG,
301285732ac8SCy Schubert "FT: Found PSK to generate PMK-R1 locally");
301385732ac8SCy Schubert os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
301485732ac8SCy Schubert if (out_pairwise)
301585732ac8SCy Schubert *out_pairwise = pairwise;
30164bc52338SCy Schubert os_memcpy(sm->PMK, pmk, PMK_LEN);
30174bc52338SCy Schubert sm->pmk_len = PMK_LEN;
301885732ac8SCy Schubert if (out_vlan &&
301985732ac8SCy Schubert wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
302085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
302185732ac8SCy Schubert MACSTR, MAC2STR(sm->addr));
302285732ac8SCy Schubert return -1;
302385732ac8SCy Schubert }
302485732ac8SCy Schubert
302585732ac8SCy Schubert if (out_identity && out_identity_len) {
302685732ac8SCy Schubert *out_identity_len = wpa_ft_get_identity(
302785732ac8SCy Schubert sm->wpa_auth, sm->addr, out_identity);
302885732ac8SCy Schubert }
302985732ac8SCy Schubert
303085732ac8SCy Schubert if (out_radius_cui && out_radius_cui_len) {
303185732ac8SCy Schubert *out_radius_cui_len = wpa_ft_get_radius_cui(
303285732ac8SCy Schubert sm->wpa_auth, sm->addr, out_radius_cui);
303385732ac8SCy Schubert }
303485732ac8SCy Schubert
303585732ac8SCy Schubert if (out_session_timeout) {
303685732ac8SCy Schubert *out_session_timeout = wpa_ft_get_session_timeout(
303785732ac8SCy Schubert sm->wpa_auth, sm->addr);
303885732ac8SCy Schubert }
303985732ac8SCy Schubert
304085732ac8SCy Schubert return 0;
304185732ac8SCy Schubert }
304285732ac8SCy Schubert
304385732ac8SCy Schubert wpa_printf(MSG_DEBUG,
304485732ac8SCy Schubert "FT: Did not find PSK to generate PMK-R1 locally");
304585732ac8SCy Schubert return -1;
304685732ac8SCy Schubert }
304785732ac8SCy Schubert
304885732ac8SCy Schubert
304985732ac8SCy Schubert /* Detect the configuration the station asked for.
305085732ac8SCy Schubert * Required to detect FT-PSK and pairwise cipher.
305185732ac8SCy Schubert */
wpa_ft_set_key_mgmt(struct wpa_state_machine * sm,struct wpa_ft_ies * parse)305285732ac8SCy Schubert static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm,
305385732ac8SCy Schubert struct wpa_ft_ies *parse)
305485732ac8SCy Schubert {
305585732ac8SCy Schubert int key_mgmt, ciphers;
305685732ac8SCy Schubert
305785732ac8SCy Schubert if (sm->wpa_key_mgmt)
305885732ac8SCy Schubert return 0;
305985732ac8SCy Schubert
306085732ac8SCy Schubert key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt;
306185732ac8SCy Schubert if (!key_mgmt) {
306285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from "
306385732ac8SCy Schubert MACSTR, parse->key_mgmt, MAC2STR(sm->addr));
306485732ac8SCy Schubert return -1;
306585732ac8SCy Schubert }
306685732ac8SCy Schubert if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
306785732ac8SCy Schubert sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
306885732ac8SCy Schubert #ifdef CONFIG_SHA384
306985732ac8SCy Schubert else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
307085732ac8SCy Schubert sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
307185732ac8SCy Schubert #endif /* CONFIG_SHA384 */
307285732ac8SCy Schubert else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
307385732ac8SCy Schubert sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
307485732ac8SCy Schubert #ifdef CONFIG_FILS
307585732ac8SCy Schubert else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
307685732ac8SCy Schubert sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
307785732ac8SCy Schubert else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
307885732ac8SCy Schubert sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
307985732ac8SCy Schubert #endif /* CONFIG_FILS */
308085732ac8SCy Schubert ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
308185732ac8SCy Schubert if (!ciphers) {
308285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
308385732ac8SCy Schubert MACSTR,
308485732ac8SCy Schubert parse->pairwise_cipher, MAC2STR(sm->addr));
308585732ac8SCy Schubert return -1;
308685732ac8SCy Schubert }
308785732ac8SCy Schubert sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
308885732ac8SCy Schubert
308985732ac8SCy Schubert return 0;
309085732ac8SCy Schubert }
309185732ac8SCy Schubert
309285732ac8SCy Schubert
wpa_ft_local_derive_pmk_r1(struct wpa_authenticator * wpa_auth,struct wpa_state_machine * sm,const u8 * r0kh_id,size_t r0kh_id_len,const u8 * req_pmk_r0_name,u8 * out_pmk_r1_name,u8 * out_pmk_r1,int * out_pairwise,struct vlan_description * vlan,const u8 ** identity,size_t * identity_len,const u8 ** radius_cui,size_t * radius_cui_len,int * out_session_timeout,size_t * pmk_r1_len)309385732ac8SCy Schubert static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth,
309485732ac8SCy Schubert struct wpa_state_machine *sm,
309585732ac8SCy Schubert const u8 *r0kh_id, size_t r0kh_id_len,
309685732ac8SCy Schubert const u8 *req_pmk_r0_name,
3097*a90b9d01SCy Schubert u8 *out_pmk_r1_name,
309885732ac8SCy Schubert u8 *out_pmk_r1, int *out_pairwise,
309985732ac8SCy Schubert struct vlan_description *vlan,
310085732ac8SCy Schubert const u8 **identity, size_t *identity_len,
310185732ac8SCy Schubert const u8 **radius_cui,
310285732ac8SCy Schubert size_t *radius_cui_len,
3103*a90b9d01SCy Schubert int *out_session_timeout,
3104*a90b9d01SCy Schubert size_t *pmk_r1_len)
310585732ac8SCy Schubert {
310685732ac8SCy Schubert struct wpa_auth_config *conf = &wpa_auth->conf;
310785732ac8SCy Schubert const struct wpa_ft_pmk_r0_sa *r0;
310885732ac8SCy Schubert int expires_in = 0;
310985732ac8SCy Schubert int session_timeout = 0;
311085732ac8SCy Schubert struct os_reltime now;
311185732ac8SCy Schubert
311285732ac8SCy Schubert if (conf->r0_key_holder_len != r0kh_id_len ||
311385732ac8SCy Schubert os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) !=
311485732ac8SCy Schubert 0)
311585732ac8SCy Schubert return -1; /* not our R0KH-ID */
311685732ac8SCy Schubert
311785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration");
311885732ac8SCy Schubert if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) <
311985732ac8SCy Schubert 0)
312085732ac8SCy Schubert return -1; /* no matching PMKR0Name in local cache */
312185732ac8SCy Schubert
312285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache");
312385732ac8SCy Schubert
312485732ac8SCy Schubert if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name,
312585732ac8SCy Schubert conf->r1_key_holder,
3126*a90b9d01SCy Schubert sm->addr, out_pmk_r1, out_pmk_r1_name) < 0)
312785732ac8SCy Schubert return -1;
312885732ac8SCy Schubert
312985732ac8SCy Schubert os_get_reltime(&now);
313085732ac8SCy Schubert if (r0->expiration)
313185732ac8SCy Schubert expires_in = r0->expiration - now.sec;
313285732ac8SCy Schubert
313385732ac8SCy Schubert if (r0->session_timeout)
313485732ac8SCy Schubert session_timeout = r0->session_timeout - now.sec;
313585732ac8SCy Schubert
313685732ac8SCy Schubert wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len,
3137*a90b9d01SCy Schubert out_pmk_r1_name,
313885732ac8SCy Schubert sm->pairwise, r0->vlan, expires_in, session_timeout,
313985732ac8SCy Schubert r0->identity, r0->identity_len,
314085732ac8SCy Schubert r0->radius_cui, r0->radius_cui_len);
314185732ac8SCy Schubert
314285732ac8SCy Schubert *out_pairwise = sm->pairwise;
314385732ac8SCy Schubert if (vlan) {
314485732ac8SCy Schubert if (r0->vlan)
314585732ac8SCy Schubert *vlan = *r0->vlan;
314685732ac8SCy Schubert else
314785732ac8SCy Schubert os_memset(vlan, 0, sizeof(*vlan));
314885732ac8SCy Schubert }
314985732ac8SCy Schubert
315085732ac8SCy Schubert if (identity && identity_len) {
315185732ac8SCy Schubert *identity = r0->identity;
315285732ac8SCy Schubert *identity_len = r0->identity_len;
315385732ac8SCy Schubert }
315485732ac8SCy Schubert
315585732ac8SCy Schubert if (radius_cui && radius_cui_len) {
315685732ac8SCy Schubert *radius_cui = r0->radius_cui;
315785732ac8SCy Schubert *radius_cui_len = r0->radius_cui_len;
315885732ac8SCy Schubert }
315985732ac8SCy Schubert
316085732ac8SCy Schubert *out_session_timeout = session_timeout;
316185732ac8SCy Schubert
3162*a90b9d01SCy Schubert *pmk_r1_len = r0->pmk_r0_len;
3163*a90b9d01SCy Schubert
316485732ac8SCy Schubert return 0;
316585732ac8SCy Schubert }
316685732ac8SCy Schubert
316785732ac8SCy Schubert
wpa_ft_process_auth_req(struct wpa_state_machine * sm,const u8 * ies,size_t ies_len,u8 ** resp_ies,size_t * resp_ies_len)31685b9c547cSRui Paulo static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
3169e28a4053SRui Paulo const u8 *ies, size_t ies_len,
3170e28a4053SRui Paulo u8 **resp_ies, size_t *resp_ies_len)
3171e28a4053SRui Paulo {
3172e28a4053SRui Paulo struct rsn_mdie *mdie;
317385732ac8SCy Schubert u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN];
3174e28a4053SRui Paulo u8 ptk_name[WPA_PMK_NAME_LEN];
3175e28a4053SRui Paulo struct wpa_auth_config *conf;
3176e28a4053SRui Paulo struct wpa_ft_ies parse;
31775b9c547cSRui Paulo size_t buflen;
3178e28a4053SRui Paulo int ret;
3179e28a4053SRui Paulo u8 *pos, *end;
318085732ac8SCy Schubert int pairwise, session_timeout = 0;
318185732ac8SCy Schubert struct vlan_description vlan;
318285732ac8SCy Schubert const u8 *identity, *radius_cui;
318385732ac8SCy Schubert size_t identity_len = 0, radius_cui_len = 0;
3184*a90b9d01SCy Schubert size_t pmk_r1_len, kdk_len, len;
3185*a90b9d01SCy Schubert int retval = WLAN_STATUS_UNSPECIFIED_FAILURE;
3186e28a4053SRui Paulo
3187e28a4053SRui Paulo *resp_ies = NULL;
3188e28a4053SRui Paulo *resp_ies_len = 0;
3189e28a4053SRui Paulo
3190e28a4053SRui Paulo sm->pmk_r1_name_valid = 0;
3191e28a4053SRui Paulo conf = &sm->wpa_auth->conf;
3192e28a4053SRui Paulo
3193e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
3194e28a4053SRui Paulo ies, ies_len);
3195e28a4053SRui Paulo
3196*a90b9d01SCy Schubert if (wpa_ft_parse_ies(ies, ies_len, &parse, 0, false)) {
3197e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
3198e28a4053SRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
3199e28a4053SRui Paulo }
3200e28a4053SRui Paulo
3201e28a4053SRui Paulo mdie = (struct rsn_mdie *) parse.mdie;
3202e28a4053SRui Paulo if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
3203e28a4053SRui Paulo os_memcmp(mdie->mobility_domain,
3204e28a4053SRui Paulo sm->wpa_auth->conf.mobility_domain,
3205e28a4053SRui Paulo MOBILITY_DOMAIN_ID_LEN) != 0) {
3206e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
3207*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_MDIE;
3208*a90b9d01SCy Schubert goto out;
3209e28a4053SRui Paulo }
3210e28a4053SRui Paulo
3211*a90b9d01SCy Schubert if (!parse.ftie || parse.ftie_len < sizeof(struct rsn_ftie)) {
3212e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
3213*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3214*a90b9d01SCy Schubert goto out;
321585732ac8SCy Schubert }
3216e28a4053SRui Paulo
3217e28a4053SRui Paulo if (parse.r0kh_id == NULL) {
3218e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
3219*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3220*a90b9d01SCy Schubert goto out;
3221e28a4053SRui Paulo }
3222e28a4053SRui Paulo
3223e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID",
3224e28a4053SRui Paulo parse.r0kh_id, parse.r0kh_id_len);
3225e28a4053SRui Paulo os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
3226e28a4053SRui Paulo sm->r0kh_id_len = parse.r0kh_id_len;
3227e28a4053SRui Paulo
3228e28a4053SRui Paulo if (parse.rsn_pmkid == NULL) {
3229e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
3230*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_PMKID;
3231*a90b9d01SCy Schubert goto out;
3232e28a4053SRui Paulo }
3233e28a4053SRui Paulo
323485732ac8SCy Schubert if (wpa_ft_set_key_mgmt(sm, &parse) < 0)
3235*a90b9d01SCy Schubert goto out;
323685732ac8SCy Schubert
3237e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
3238e28a4053SRui Paulo parse.rsn_pmkid, WPA_PMK_NAME_LEN);
3239e28a4053SRui Paulo
324085732ac8SCy Schubert if (conf->ft_psk_generate_local &&
324185732ac8SCy Schubert wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
3242*a90b9d01SCy Schubert if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
3243*a90b9d01SCy Schubert sm->wpa_auth->conf.r1_key_holder,
3244*a90b9d01SCy Schubert sm->addr, pmk_r1_name, PMK_LEN) < 0)
3245*a90b9d01SCy Schubert goto out;
324685732ac8SCy Schubert if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
324785732ac8SCy Schubert &vlan, &identity, &identity_len,
324885732ac8SCy Schubert &radius_cui, &radius_cui_len,
3249*a90b9d01SCy Schubert &session_timeout) < 0) {
3250*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_PMKID;
3251*a90b9d01SCy Schubert goto out;
3252*a90b9d01SCy Schubert }
3253*a90b9d01SCy Schubert pmk_r1_len = PMK_LEN;
325485732ac8SCy Schubert wpa_printf(MSG_DEBUG,
325585732ac8SCy Schubert "FT: Generated PMK-R1 for FT-PSK locally");
3256*a90b9d01SCy Schubert goto pmk_r1_derived;
3257*a90b9d01SCy Schubert }
3258*a90b9d01SCy Schubert
3259*a90b9d01SCy Schubert /* Need to test all possible hash algorithms for FT-SAE-EXT-KEY since
3260*a90b9d01SCy Schubert * the key length is not yet known. For other AKMs, only the length
3261*a90b9d01SCy Schubert * identified by the AKM is used. */
3262*a90b9d01SCy Schubert for (len = SHA256_MAC_LEN; len <= SHA512_MAC_LEN; len += 16) {
3263*a90b9d01SCy Schubert if (parse.key_mgmt != WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
3264*a90b9d01SCy Schubert ((wpa_key_mgmt_sha384(parse.key_mgmt) &&
3265*a90b9d01SCy Schubert len != SHA384_MAC_LEN) ||
3266*a90b9d01SCy Schubert (!wpa_key_mgmt_sha384(parse.key_mgmt) &&
3267*a90b9d01SCy Schubert len != SHA256_MAC_LEN)))
3268*a90b9d01SCy Schubert continue;
3269*a90b9d01SCy Schubert if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
3270*a90b9d01SCy Schubert sm->wpa_auth->conf.r1_key_holder,
3271*a90b9d01SCy Schubert sm->addr, pmk_r1_name, len) < 0)
3272*a90b9d01SCy Schubert continue;
3273*a90b9d01SCy Schubert
3274*a90b9d01SCy Schubert if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
327585732ac8SCy Schubert pmk_r1, &pmk_r1_len, &pairwise, &vlan,
327685732ac8SCy Schubert &identity, &identity_len, &radius_cui,
3277*a90b9d01SCy Schubert &radius_cui_len,
3278*a90b9d01SCy Schubert &session_timeout) == 0) {
3279*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
3280*a90b9d01SCy Schubert "FT: Found PMKR1Name (using SHA%zu) from local cache",
3281*a90b9d01SCy Schubert pmk_r1_len * 8);
3282*a90b9d01SCy Schubert goto pmk_r1_derived;
3283*a90b9d01SCy Schubert }
3284*a90b9d01SCy Schubert }
3285*a90b9d01SCy Schubert
328685732ac8SCy Schubert wpa_printf(MSG_DEBUG,
328785732ac8SCy Schubert "FT: No PMK-R1 available in local cache for the requested PMKR1Name");
328885732ac8SCy Schubert if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
328985732ac8SCy Schubert parse.r0kh_id, parse.r0kh_id_len,
329085732ac8SCy Schubert parse.rsn_pmkid,
329185732ac8SCy Schubert pmk_r1_name, pmk_r1, &pairwise,
329285732ac8SCy Schubert &vlan, &identity, &identity_len,
329385732ac8SCy Schubert &radius_cui, &radius_cui_len,
3294*a90b9d01SCy Schubert &session_timeout, &pmk_r1_len) == 0) {
329585732ac8SCy Schubert wpa_printf(MSG_DEBUG,
329685732ac8SCy Schubert "FT: Generated PMK-R1 based on local PMK-R0");
329785732ac8SCy Schubert goto pmk_r1_derived;
329885732ac8SCy Schubert }
329985732ac8SCy Schubert
33005b9c547cSRui Paulo if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
330185732ac8SCy Schubert wpa_printf(MSG_DEBUG,
330285732ac8SCy Schubert "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
3303*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_PMKID;
3304*a90b9d01SCy Schubert goto out;
3305e28a4053SRui Paulo }
3306e28a4053SRui Paulo
3307*a90b9d01SCy Schubert retval = -1; /* Status pending */
3308*a90b9d01SCy Schubert goto out;
3309e28a4053SRui Paulo
331085732ac8SCy Schubert pmk_r1_derived:
331185732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
3312e28a4053SRui Paulo sm->pmk_r1_name_valid = 1;
3313e28a4053SRui Paulo os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
33144bc52338SCy Schubert os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len);
33154bc52338SCy Schubert sm->pmk_r1_len = pmk_r1_len;
3316e28a4053SRui Paulo
3317f05cddf9SRui Paulo if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
3318e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
3319e28a4053SRui Paulo "ANonce");
3320*a90b9d01SCy Schubert goto out;
3321*a90b9d01SCy Schubert }
3322*a90b9d01SCy Schubert
3323*a90b9d01SCy Schubert /* Now that we know the correct PMK-R1 length and as such, the length
3324*a90b9d01SCy Schubert * of the MIC field, fetch the SNonce. */
3325*a90b9d01SCy Schubert if (pmk_r1_len == SHA512_MAC_LEN) {
3326*a90b9d01SCy Schubert const struct rsn_ftie_sha512 *ftie;
3327*a90b9d01SCy Schubert
3328*a90b9d01SCy Schubert ftie = (const struct rsn_ftie_sha512 *) parse.ftie;
3329*a90b9d01SCy Schubert if (!ftie || parse.ftie_len < sizeof(*ftie)) {
3330*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
3331*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3332*a90b9d01SCy Schubert goto out;
3333*a90b9d01SCy Schubert }
3334*a90b9d01SCy Schubert
3335*a90b9d01SCy Schubert os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
3336*a90b9d01SCy Schubert } else if (pmk_r1_len == SHA384_MAC_LEN) {
3337*a90b9d01SCy Schubert const struct rsn_ftie_sha384 *ftie;
3338*a90b9d01SCy Schubert
3339*a90b9d01SCy Schubert ftie = (const struct rsn_ftie_sha384 *) parse.ftie;
3340*a90b9d01SCy Schubert if (!ftie || parse.ftie_len < sizeof(*ftie)) {
3341*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
3342*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3343*a90b9d01SCy Schubert goto out;
3344*a90b9d01SCy Schubert }
3345*a90b9d01SCy Schubert
3346*a90b9d01SCy Schubert os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
3347*a90b9d01SCy Schubert } else {
3348*a90b9d01SCy Schubert const struct rsn_ftie *ftie;
3349*a90b9d01SCy Schubert
3350*a90b9d01SCy Schubert ftie = (const struct rsn_ftie *) parse.ftie;
3351*a90b9d01SCy Schubert if (!ftie || parse.ftie_len < sizeof(*ftie)) {
3352*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
3353*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3354*a90b9d01SCy Schubert goto out;
3355*a90b9d01SCy Schubert }
3356*a90b9d01SCy Schubert
3357*a90b9d01SCy Schubert os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
3358e28a4053SRui Paulo }
3359e28a4053SRui Paulo
3360e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
3361e28a4053SRui Paulo sm->SNonce, WPA_NONCE_LEN);
3362e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
3363e28a4053SRui Paulo sm->ANonce, WPA_NONCE_LEN);
3364e28a4053SRui Paulo
3365c1d255d3SCy Schubert if (sm->wpa_auth->conf.force_kdk_derivation ||
3366c1d255d3SCy Schubert (sm->wpa_auth->conf.secure_ltf &&
3367c1d255d3SCy Schubert ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
3368c1d255d3SCy Schubert kdk_len = WPA_KDK_MAX_LEN;
3369c1d255d3SCy Schubert else
3370c1d255d3SCy Schubert kdk_len = 0;
3371c1d255d3SCy Schubert
337285732ac8SCy Schubert if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
337385732ac8SCy Schubert sm->addr, sm->wpa_auth->addr, pmk_r1_name,
3374*a90b9d01SCy Schubert &sm->PTK, ptk_name, parse.key_mgmt,
3375c1d255d3SCy Schubert pairwise, kdk_len) < 0)
3376*a90b9d01SCy Schubert goto out;
3377*a90b9d01SCy Schubert
3378*a90b9d01SCy Schubert #ifdef CONFIG_PASN
3379*a90b9d01SCy Schubert if (sm->wpa_auth->conf.secure_ltf &&
3380*a90b9d01SCy Schubert ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
3381*a90b9d01SCy Schubert wpa_ltf_keyseed(&sm->PTK, parse.key_mgmt, pairwise)) {
3382*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
3383*a90b9d01SCy Schubert goto out;
3384*a90b9d01SCy Schubert }
3385*a90b9d01SCy Schubert #endif /* CONFIG_PASN */
3386e28a4053SRui Paulo
3387e28a4053SRui Paulo sm->pairwise = pairwise;
3388c1d255d3SCy Schubert sm->PTK_valid = true;
3389c1d255d3SCy Schubert sm->tk_already_set = false;
3390c1d255d3SCy Schubert wpa_ft_install_ptk(sm, 0);
3391e28a4053SRui Paulo
339285732ac8SCy Schubert if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
339385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
3394*a90b9d01SCy Schubert goto out;
339585732ac8SCy Schubert }
339685732ac8SCy Schubert if (wpa_ft_set_identity(sm->wpa_auth, sm->addr,
339785732ac8SCy Schubert identity, identity_len) < 0 ||
339885732ac8SCy Schubert wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr,
339985732ac8SCy Schubert radius_cui, radius_cui_len) < 0) {
340085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI");
3401*a90b9d01SCy Schubert goto out;
340285732ac8SCy Schubert }
340385732ac8SCy Schubert wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout);
340485732ac8SCy Schubert
3405e28a4053SRui Paulo buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
3406e28a4053SRui Paulo 2 + FT_R1KH_ID_LEN + 200;
3407e28a4053SRui Paulo *resp_ies = os_zalloc(buflen);
340885732ac8SCy Schubert if (*resp_ies == NULL)
340985732ac8SCy Schubert goto fail;
3410e28a4053SRui Paulo
3411e28a4053SRui Paulo pos = *resp_ies;
3412e28a4053SRui Paulo end = *resp_ies + buflen;
3413e28a4053SRui Paulo
3414e28a4053SRui Paulo ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
341585732ac8SCy Schubert if (ret < 0)
341685732ac8SCy Schubert goto fail;
3417e28a4053SRui Paulo pos += ret;
3418e28a4053SRui Paulo
3419e28a4053SRui Paulo ret = wpa_write_mdie(conf, pos, end - pos);
342085732ac8SCy Schubert if (ret < 0)
342185732ac8SCy Schubert goto fail;
3422e28a4053SRui Paulo pos += ret;
3423e28a4053SRui Paulo
3424*a90b9d01SCy Schubert ret = wpa_write_ftie(conf, parse.key_mgmt, pmk_r1_len,
3425*a90b9d01SCy Schubert parse.r0kh_id, parse.r0kh_id_len,
3426c1d255d3SCy Schubert sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0,
3427c1d255d3SCy Schubert 0);
342885732ac8SCy Schubert if (ret < 0)
342985732ac8SCy Schubert goto fail;
3430e28a4053SRui Paulo pos += ret;
3431e28a4053SRui Paulo
3432e28a4053SRui Paulo *resp_ies_len = pos - *resp_ies;
3433e28a4053SRui Paulo
3434*a90b9d01SCy Schubert retval = WLAN_STATUS_SUCCESS;
3435*a90b9d01SCy Schubert goto out;
343685732ac8SCy Schubert fail:
343785732ac8SCy Schubert os_free(*resp_ies);
343885732ac8SCy Schubert *resp_ies = NULL;
3439*a90b9d01SCy Schubert out:
3440*a90b9d01SCy Schubert wpa_ft_parse_ies_free(&parse);
3441*a90b9d01SCy Schubert return retval;
3442e28a4053SRui Paulo }
3443e28a4053SRui Paulo
3444e28a4053SRui Paulo
wpa_ft_process_auth(struct wpa_state_machine * sm,u16 auth_transaction,const u8 * ies,size_t ies_len,void (* cb)(void * ctx,const u8 * dst,u16 auth_transaction,u16 status,const u8 * ies,size_t ies_len),void * ctx)3445*a90b9d01SCy Schubert void wpa_ft_process_auth(struct wpa_state_machine *sm,
3446e28a4053SRui Paulo u16 auth_transaction, const u8 *ies, size_t ies_len,
3447*a90b9d01SCy Schubert void (*cb)(void *ctx, const u8 *dst,
3448e28a4053SRui Paulo u16 auth_transaction, u16 status,
3449e28a4053SRui Paulo const u8 *ies, size_t ies_len),
3450e28a4053SRui Paulo void *ctx)
3451e28a4053SRui Paulo {
3452e28a4053SRui Paulo u16 status;
3453e28a4053SRui Paulo u8 *resp_ies;
3454e28a4053SRui Paulo size_t resp_ies_len;
34555b9c547cSRui Paulo int res;
3456e28a4053SRui Paulo
3457e28a4053SRui Paulo if (sm == NULL) {
3458e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
3459e28a4053SRui Paulo "WPA SM not available");
3460e28a4053SRui Paulo return;
3461e28a4053SRui Paulo }
3462e28a4053SRui Paulo
3463e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
3464e28a4053SRui Paulo " BSSID=" MACSTR " transaction=%d",
3465*a90b9d01SCy Schubert MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr),
3466*a90b9d01SCy Schubert auth_transaction);
34675b9c547cSRui Paulo sm->ft_pending_cb = cb;
34685b9c547cSRui Paulo sm->ft_pending_cb_ctx = ctx;
34695b9c547cSRui Paulo sm->ft_pending_auth_transaction = auth_transaction;
347085732ac8SCy Schubert sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
34715b9c547cSRui Paulo res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
3472e28a4053SRui Paulo &resp_ies_len);
34735b9c547cSRui Paulo if (res < 0) {
34745b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available");
34755b9c547cSRui Paulo return;
34765b9c547cSRui Paulo }
34775b9c547cSRui Paulo status = res;
3478e28a4053SRui Paulo
3479e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
3480206b73d0SCy Schubert " auth_transaction=%d status=%u (%s)",
3481206b73d0SCy Schubert MAC2STR(sm->addr), auth_transaction + 1, status,
3482206b73d0SCy Schubert status2str(status));
3483e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
3484*a90b9d01SCy Schubert cb(ctx, sm->addr, auth_transaction + 1, status, resp_ies, resp_ies_len);
3485e28a4053SRui Paulo os_free(resp_ies);
3486e28a4053SRui Paulo }
3487e28a4053SRui Paulo
3488e28a4053SRui Paulo
wpa_ft_validate_reassoc(struct wpa_state_machine * sm,const u8 * ies,size_t ies_len)3489c1d255d3SCy Schubert int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
3490e28a4053SRui Paulo size_t ies_len)
3491e28a4053SRui Paulo {
3492e28a4053SRui Paulo struct wpa_ft_ies parse;
3493e28a4053SRui Paulo struct rsn_mdie *mdie;
34945b9c547cSRui Paulo u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
3495*a90b9d01SCy Schubert size_t mic_len;
3496e28a4053SRui Paulo unsigned int count;
349785732ac8SCy Schubert const u8 *kck;
349885732ac8SCy Schubert size_t kck_len;
3499c1d255d3SCy Schubert struct wpa_auth_config *conf;
3500*a90b9d01SCy Schubert int retval = WLAN_STATUS_UNSPECIFIED_FAILURE;
3501e28a4053SRui Paulo
3502e28a4053SRui Paulo if (sm == NULL)
3503e28a4053SRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
3504e28a4053SRui Paulo
3505c1d255d3SCy Schubert conf = &sm->wpa_auth->conf;
350685732ac8SCy Schubert
3507e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
3508e28a4053SRui Paulo
3509*a90b9d01SCy Schubert if (wpa_ft_parse_ies(ies, ies_len, &parse, sm->wpa_key_mgmt,
3510*a90b9d01SCy Schubert false) < 0) {
3511e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
3512e28a4053SRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
3513e28a4053SRui Paulo }
3514e28a4053SRui Paulo
3515e28a4053SRui Paulo if (parse.rsn == NULL) {
3516e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req");
3517*a90b9d01SCy Schubert goto out;
3518e28a4053SRui Paulo }
3519e28a4053SRui Paulo
3520e28a4053SRui Paulo if (parse.rsn_pmkid == NULL) {
3521e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
3522*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_PMKID;
3523*a90b9d01SCy Schubert goto out;
3524e28a4053SRui Paulo }
3525e28a4053SRui Paulo
35265b9c547cSRui Paulo if (os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)
35275b9c547cSRui Paulo != 0) {
3528e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match "
3529e28a4053SRui Paulo "with the PMKR1Name derived from auth request");
3530*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_PMKID;
3531*a90b9d01SCy Schubert goto out;
3532e28a4053SRui Paulo }
3533e28a4053SRui Paulo
3534e28a4053SRui Paulo mdie = (struct rsn_mdie *) parse.mdie;
3535e28a4053SRui Paulo if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
3536c1d255d3SCy Schubert os_memcmp(mdie->mobility_domain, conf->mobility_domain,
3537e28a4053SRui Paulo MOBILITY_DOMAIN_ID_LEN) != 0) {
3538e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
3539*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_MDIE;
3540*a90b9d01SCy Schubert goto out;
3541e28a4053SRui Paulo }
3542e28a4053SRui Paulo
3543*a90b9d01SCy Schubert if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
3544*a90b9d01SCy Schubert sm->pmk_r1_len == SHA512_MAC_LEN)
3545*a90b9d01SCy Schubert mic_len = 32;
3546*a90b9d01SCy Schubert else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY &&
3547*a90b9d01SCy Schubert sm->pmk_r1_len == SHA384_MAC_LEN) ||
3548*a90b9d01SCy Schubert wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
3549*a90b9d01SCy Schubert mic_len = 24;
3550*a90b9d01SCy Schubert else
3551*a90b9d01SCy Schubert mic_len = 16;
355285732ac8SCy Schubert
3553*a90b9d01SCy Schubert if (!parse.ftie || !parse.fte_anonce || !parse.fte_snonce ||
3554*a90b9d01SCy Schubert parse.fte_mic_len != mic_len) {
3555*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
3556*a90b9d01SCy Schubert "FT: Invalid FTE (fte_mic_len=%zu mic_len=%zu)",
3557*a90b9d01SCy Schubert parse.fte_mic_len, mic_len);
3558*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3559*a90b9d01SCy Schubert goto out;
356085732ac8SCy Schubert }
356185732ac8SCy Schubert
3562*a90b9d01SCy Schubert if (os_memcmp(parse.fte_snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
356385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
356485732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
3565*a90b9d01SCy Schubert parse.fte_snonce, WPA_NONCE_LEN);
356685732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
356785732ac8SCy Schubert sm->SNonce, WPA_NONCE_LEN);
3568*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3569*a90b9d01SCy Schubert goto out;
357085732ac8SCy Schubert }
357185732ac8SCy Schubert
3572*a90b9d01SCy Schubert if (os_memcmp(parse.fte_anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
3573e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
3574e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
3575*a90b9d01SCy Schubert parse.fte_anonce, WPA_NONCE_LEN);
3576e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
3577e28a4053SRui Paulo sm->ANonce, WPA_NONCE_LEN);
3578*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3579*a90b9d01SCy Schubert goto out;
3580e28a4053SRui Paulo }
3581e28a4053SRui Paulo
3582e28a4053SRui Paulo if (parse.r0kh_id == NULL) {
3583e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
3584*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3585*a90b9d01SCy Schubert goto out;
3586e28a4053SRui Paulo }
3587e28a4053SRui Paulo
3588e28a4053SRui Paulo if (parse.r0kh_id_len != sm->r0kh_id_len ||
35895b9c547cSRui Paulo os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
35905b9c547cSRui Paulo {
3591e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
3592e28a4053SRui Paulo "the current R0KH-ID");
3593e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
3594e28a4053SRui Paulo parse.r0kh_id, parse.r0kh_id_len);
3595e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
3596e28a4053SRui Paulo sm->r0kh_id, sm->r0kh_id_len);
3597*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3598*a90b9d01SCy Schubert goto out;
3599e28a4053SRui Paulo }
3600e28a4053SRui Paulo
3601e28a4053SRui Paulo if (parse.r1kh_id == NULL) {
3602e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
3603*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3604*a90b9d01SCy Schubert goto out;
3605e28a4053SRui Paulo }
3606e28a4053SRui Paulo
3607c1d255d3SCy Schubert if (os_memcmp_const(parse.r1kh_id, conf->r1_key_holder,
3608e28a4053SRui Paulo FT_R1KH_ID_LEN) != 0) {
3609e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
3610e28a4053SRui Paulo "ReassocReq");
3611e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE",
3612e28a4053SRui Paulo parse.r1kh_id, FT_R1KH_ID_LEN);
3613e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
3614c1d255d3SCy Schubert conf->r1_key_holder, FT_R1KH_ID_LEN);
3615*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3616*a90b9d01SCy Schubert goto out;
3617e28a4053SRui Paulo }
3618e28a4053SRui Paulo
3619e28a4053SRui Paulo if (parse.rsn_pmkid == NULL ||
36205b9c547cSRui Paulo os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN))
36215b9c547cSRui Paulo {
3622e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
3623e28a4053SRui Paulo "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
3624*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_PMKID;
3625*a90b9d01SCy Schubert goto out;
3626e28a4053SRui Paulo }
3627e28a4053SRui Paulo
3628e28a4053SRui Paulo count = 3;
3629e28a4053SRui Paulo if (parse.ric)
3630f05cddf9SRui Paulo count += ieee802_11_ie_count(parse.ric, parse.ric_len);
3631c1d255d3SCy Schubert if (parse.rsnxe)
3632c1d255d3SCy Schubert count++;
3633*a90b9d01SCy Schubert if (parse.fte_elem_count != count) {
3634e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
3635e28a4053SRui Paulo "Control: received %u expected %u",
3636*a90b9d01SCy Schubert parse.fte_elem_count, count);
3637*a90b9d01SCy Schubert goto out;
3638e28a4053SRui Paulo }
3639e28a4053SRui Paulo
364085732ac8SCy Schubert if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
364185732ac8SCy Schubert kck = sm->PTK.kck2;
364285732ac8SCy Schubert kck_len = sm->PTK.kck2_len;
364385732ac8SCy Schubert } else {
364485732ac8SCy Schubert kck = sm->PTK.kck;
364585732ac8SCy Schubert kck_len = sm->PTK.kck_len;
364685732ac8SCy Schubert }
3647*a90b9d01SCy Schubert if (wpa_ft_mic(sm->wpa_key_mgmt, kck, kck_len,
3648*a90b9d01SCy Schubert sm->addr, sm->wpa_auth->addr, 5,
3649e28a4053SRui Paulo parse.mdie - 2, parse.mdie_len + 2,
3650e28a4053SRui Paulo parse.ftie - 2, parse.ftie_len + 2,
3651e28a4053SRui Paulo parse.rsn - 2, parse.rsn_len + 2,
3652e28a4053SRui Paulo parse.ric, parse.ric_len,
3653c1d255d3SCy Schubert parse.rsnxe ? parse.rsnxe - 2 : NULL,
3654c1d255d3SCy Schubert parse.rsnxe ? parse.rsnxe_len + 2 : 0,
3655*a90b9d01SCy Schubert NULL,
3656e28a4053SRui Paulo mic) < 0) {
3657e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
3658*a90b9d01SCy Schubert goto out;
3659e28a4053SRui Paulo }
3660e28a4053SRui Paulo
3661*a90b9d01SCy Schubert if (os_memcmp_const(mic, parse.fte_mic, mic_len) != 0) {
3662e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
3663f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
3664f05cddf9SRui Paulo MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
36655b9c547cSRui Paulo wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
3666*a90b9d01SCy Schubert parse.fte_mic, mic_len);
36675b9c547cSRui Paulo wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
3668f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
3669f05cddf9SRui Paulo parse.mdie - 2, parse.mdie_len + 2);
3670f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
3671f05cddf9SRui Paulo parse.ftie - 2, parse.ftie_len + 2);
3672f05cddf9SRui Paulo wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
3673f05cddf9SRui Paulo parse.rsn - 2, parse.rsn_len + 2);
3674c1d255d3SCy Schubert wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE",
3675c1d255d3SCy Schubert parse.rsnxe ? parse.rsnxe - 2 : NULL,
3676c1d255d3SCy Schubert parse.rsnxe ? parse.rsnxe_len + 2 : 0);
3677*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3678*a90b9d01SCy Schubert goto out;
3679e28a4053SRui Paulo }
3680e28a4053SRui Paulo
3681*a90b9d01SCy Schubert if (parse.fte_rsnxe_used &&
3682*a90b9d01SCy Schubert (conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
3683*a90b9d01SCy Schubert conf->sae_pwe == SAE_PWE_BOTH) &&
3684c1d255d3SCy Schubert !parse.rsnxe) {
3685c1d255d3SCy Schubert wpa_printf(MSG_INFO,
3686c1d255d3SCy Schubert "FT: FTE indicated that STA uses RSNXE, but RSNXE was not included");
3687*a90b9d01SCy Schubert retval = -1; /* discard request */
3688*a90b9d01SCy Schubert goto out;
3689c1d255d3SCy Schubert }
3690c1d255d3SCy Schubert
36914bc52338SCy Schubert #ifdef CONFIG_OCV
36924bc52338SCy Schubert if (wpa_auth_uses_ocv(sm)) {
36934bc52338SCy Schubert struct wpa_channel_info ci;
36944bc52338SCy Schubert int tx_chanwidth;
36954bc52338SCy Schubert int tx_seg1_idx;
3696c1d255d3SCy Schubert enum oci_verify_result res;
36974bc52338SCy Schubert
36984bc52338SCy Schubert if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
36994bc52338SCy Schubert wpa_printf(MSG_WARNING,
37004bc52338SCy Schubert "Failed to get channel info to validate received OCI in (Re)Assoc Request");
3701*a90b9d01SCy Schubert goto out;
37024bc52338SCy Schubert }
37034bc52338SCy Schubert
37044bc52338SCy Schubert if (get_sta_tx_parameters(sm,
37054bc52338SCy Schubert channel_width_to_int(ci.chanwidth),
37064bc52338SCy Schubert ci.seg1_idx, &tx_chanwidth,
37074bc52338SCy Schubert &tx_seg1_idx) < 0)
3708*a90b9d01SCy Schubert goto out;
37094bc52338SCy Schubert
3710c1d255d3SCy Schubert res = ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
3711c1d255d3SCy Schubert tx_chanwidth, tx_seg1_idx);
3712c1d255d3SCy Schubert if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
3713c1d255d3SCy Schubert /* Work around misbehaving STAs */
3714c1d255d3SCy Schubert wpa_printf(MSG_INFO,
3715c1d255d3SCy Schubert "Disable OCV with a STA that does not send OCI");
3716c1d255d3SCy Schubert wpa_auth_set_ocv(sm, 0);
3717c1d255d3SCy Schubert } else if (res != OCI_SUCCESS) {
3718c1d255d3SCy Schubert wpa_printf(MSG_WARNING, "OCV failed: %s", ocv_errorstr);
3719c1d255d3SCy Schubert if (sm->wpa_auth->conf.msg_ctx)
3720c1d255d3SCy Schubert wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
3721c1d255d3SCy Schubert OCV_FAILURE "addr=" MACSTR
3722c1d255d3SCy Schubert " frame=ft-reassoc-req error=%s",
3723c1d255d3SCy Schubert MAC2STR(sm->addr), ocv_errorstr);
3724*a90b9d01SCy Schubert retval = WLAN_STATUS_INVALID_FTIE;
3725*a90b9d01SCy Schubert goto out;
37264bc52338SCy Schubert }
37274bc52338SCy Schubert }
37284bc52338SCy Schubert #endif /* CONFIG_OCV */
37294bc52338SCy Schubert
3730*a90b9d01SCy Schubert retval = WLAN_STATUS_SUCCESS;
3731*a90b9d01SCy Schubert out:
3732*a90b9d01SCy Schubert wpa_ft_parse_ies_free(&parse);
3733*a90b9d01SCy Schubert return retval;
3734e28a4053SRui Paulo }
3735e28a4053SRui Paulo
3736e28a4053SRui Paulo
wpa_ft_action_rx(struct wpa_state_machine * sm,const u8 * data,size_t len)3737e28a4053SRui Paulo int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
3738e28a4053SRui Paulo {
3739e28a4053SRui Paulo const u8 *sta_addr, *target_ap;
3740e28a4053SRui Paulo const u8 *ies;
3741e28a4053SRui Paulo size_t ies_len;
3742e28a4053SRui Paulo u8 action;
3743e28a4053SRui Paulo struct ft_rrb_frame *frame;
3744e28a4053SRui Paulo
3745e28a4053SRui Paulo if (sm == NULL)
3746e28a4053SRui Paulo return -1;
3747e28a4053SRui Paulo
3748e28a4053SRui Paulo /*
3749e28a4053SRui Paulo * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
3750e28a4053SRui Paulo * FT Request action frame body[variable]
3751e28a4053SRui Paulo */
3752e28a4053SRui Paulo
3753e28a4053SRui Paulo if (len < 14) {
3754e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame "
3755e28a4053SRui Paulo "(len=%lu)", (unsigned long) len);
3756e28a4053SRui Paulo return -1;
3757e28a4053SRui Paulo }
3758e28a4053SRui Paulo
3759e28a4053SRui Paulo action = data[1];
3760e28a4053SRui Paulo sta_addr = data + 2;
3761e28a4053SRui Paulo target_ap = data + 8;
3762e28a4053SRui Paulo ies = data + 14;
3763e28a4053SRui Paulo ies_len = len - 14;
3764e28a4053SRui Paulo
3765e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR
3766e28a4053SRui Paulo " Target AP=" MACSTR " Action=%d)",
3767e28a4053SRui Paulo MAC2STR(sta_addr), MAC2STR(target_ap), action);
3768e28a4053SRui Paulo
3769*a90b9d01SCy Schubert if (!ether_addr_equal(sta_addr, sm->addr)) {
3770e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
3771e28a4053SRui Paulo "STA=" MACSTR " STA-Address=" MACSTR,
3772e28a4053SRui Paulo MAC2STR(sm->addr), MAC2STR(sta_addr));
3773e28a4053SRui Paulo return -1;
3774e28a4053SRui Paulo }
3775e28a4053SRui Paulo
3776e28a4053SRui Paulo /*
37774b72b91aSCy Schubert * Do some validity checking on the target AP address (not own and not
3778e28a4053SRui Paulo * broadcast. This could be extended to filter based on a list of known
3779e28a4053SRui Paulo * APs in the MD (if such a list were configured).
3780e28a4053SRui Paulo */
3781e28a4053SRui Paulo if ((target_ap[0] & 0x01) ||
3782*a90b9d01SCy Schubert ether_addr_equal(target_ap, sm->wpa_auth->addr)) {
3783e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
3784e28a4053SRui Paulo "frame");
3785e28a4053SRui Paulo return -1;
3786e28a4053SRui Paulo }
3787e28a4053SRui Paulo
3788e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
3789e28a4053SRui Paulo
379085732ac8SCy Schubert if (!sm->wpa_auth->conf.ft_over_ds) {
379185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject");
379285732ac8SCy Schubert return -1;
379385732ac8SCy Schubert }
379485732ac8SCy Schubert
3795e28a4053SRui Paulo /* RRB - Forward action frame to the target AP */
3796e28a4053SRui Paulo frame = os_malloc(sizeof(*frame) + len);
37975b9c547cSRui Paulo if (frame == NULL)
37985b9c547cSRui Paulo return -1;
3799e28a4053SRui Paulo frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
3800e28a4053SRui Paulo frame->packet_type = FT_PACKET_REQUEST;
3801e28a4053SRui Paulo frame->action_length = host_to_le16(len);
3802e28a4053SRui Paulo os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN);
3803e28a4053SRui Paulo os_memcpy(frame + 1, data, len);
3804e28a4053SRui Paulo
3805e28a4053SRui Paulo wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame,
3806e28a4053SRui Paulo sizeof(*frame) + len);
3807e28a4053SRui Paulo os_free(frame);
3808e28a4053SRui Paulo
3809e28a4053SRui Paulo return 0;
3810e28a4053SRui Paulo }
3811e28a4053SRui Paulo
3812e28a4053SRui Paulo
wpa_ft_rrb_rx_request_cb(void * ctx,const u8 * dst,u16 auth_transaction,u16 resp,const u8 * ies,size_t ies_len)3813*a90b9d01SCy Schubert static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst,
38145b9c547cSRui Paulo u16 auth_transaction, u16 resp,
38155b9c547cSRui Paulo const u8 *ies, size_t ies_len)
38165b9c547cSRui Paulo {
38175b9c547cSRui Paulo struct wpa_state_machine *sm = ctx;
38185b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
38195b9c547cSRui Paulo MAC2STR(sm->addr));
38205b9c547cSRui Paulo wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
38215b9c547cSRui Paulo WLAN_STATUS_SUCCESS, ies, ies_len);
38225b9c547cSRui Paulo }
38235b9c547cSRui Paulo
38245b9c547cSRui Paulo
wpa_ft_rrb_rx_request(struct wpa_authenticator * wpa_auth,const u8 * current_ap,const u8 * sta_addr,const u8 * body,size_t len)3825e28a4053SRui Paulo static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
3826e28a4053SRui Paulo const u8 *current_ap, const u8 *sta_addr,
3827e28a4053SRui Paulo const u8 *body, size_t len)
3828e28a4053SRui Paulo {
3829e28a4053SRui Paulo struct wpa_state_machine *sm;
3830e28a4053SRui Paulo u16 status;
38315b9c547cSRui Paulo u8 *resp_ies;
38325b9c547cSRui Paulo size_t resp_ies_len;
38335b9c547cSRui Paulo int res;
3834e28a4053SRui Paulo
3835e28a4053SRui Paulo sm = wpa_ft_add_sta(wpa_auth, sta_addr);
3836e28a4053SRui Paulo if (sm == NULL) {
3837e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on "
3838e28a4053SRui Paulo "RRB Request");
3839e28a4053SRui Paulo return -1;
3840e28a4053SRui Paulo }
3841e28a4053SRui Paulo
3842e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len);
3843e28a4053SRui Paulo
38445b9c547cSRui Paulo sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
38455b9c547cSRui Paulo sm->ft_pending_cb_ctx = sm;
38465b9c547cSRui Paulo os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
384785732ac8SCy Schubert sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
38485b9c547cSRui Paulo res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
3849e28a4053SRui Paulo &resp_ies_len);
38505b9c547cSRui Paulo if (res < 0) {
38515b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response");
38525b9c547cSRui Paulo return 0;
38535b9c547cSRui Paulo }
38545b9c547cSRui Paulo status = res;
38555b9c547cSRui Paulo
38565b9c547cSRui Paulo res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status,
38575b9c547cSRui Paulo resp_ies, resp_ies_len);
38585b9c547cSRui Paulo os_free(resp_ies);
38595b9c547cSRui Paulo return res;
38605b9c547cSRui Paulo }
38615b9c547cSRui Paulo
38625b9c547cSRui Paulo
wpa_ft_send_rrb_auth_resp(struct wpa_state_machine * sm,const u8 * current_ap,const u8 * sta_addr,u16 status,const u8 * resp_ies,size_t resp_ies_len)38635b9c547cSRui Paulo static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
38645b9c547cSRui Paulo const u8 *current_ap, const u8 *sta_addr,
38655b9c547cSRui Paulo u16 status, const u8 *resp_ies,
38665b9c547cSRui Paulo size_t resp_ies_len)
38675b9c547cSRui Paulo {
38685b9c547cSRui Paulo struct wpa_authenticator *wpa_auth = sm->wpa_auth;
38695b9c547cSRui Paulo size_t rlen;
38705b9c547cSRui Paulo struct ft_rrb_frame *frame;
38715b9c547cSRui Paulo u8 *pos;
3872e28a4053SRui Paulo
3873e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
3874206b73d0SCy Schubert " CurrentAP=" MACSTR " status=%u (%s)",
3875206b73d0SCy Schubert MAC2STR(sm->addr), MAC2STR(current_ap), status,
3876206b73d0SCy Schubert status2str(status));
3877e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
3878e28a4053SRui Paulo
3879e28a4053SRui Paulo /* RRB - Forward action frame response to the Current AP */
3880e28a4053SRui Paulo
3881e28a4053SRui Paulo /*
3882e28a4053SRui Paulo * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
3883e28a4053SRui Paulo * Status_Code[2] FT Request action frame body[variable]
3884e28a4053SRui Paulo */
3885e28a4053SRui Paulo rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
3886e28a4053SRui Paulo
3887e28a4053SRui Paulo frame = os_malloc(sizeof(*frame) + rlen);
38885b9c547cSRui Paulo if (frame == NULL)
38895b9c547cSRui Paulo return -1;
3890e28a4053SRui Paulo frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
3891e28a4053SRui Paulo frame->packet_type = FT_PACKET_RESPONSE;
3892e28a4053SRui Paulo frame->action_length = host_to_le16(rlen);
3893e28a4053SRui Paulo os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN);
3894e28a4053SRui Paulo pos = (u8 *) (frame + 1);
3895e28a4053SRui Paulo *pos++ = WLAN_ACTION_FT;
3896e28a4053SRui Paulo *pos++ = 2; /* Action: Response */
3897e28a4053SRui Paulo os_memcpy(pos, sta_addr, ETH_ALEN);
3898e28a4053SRui Paulo pos += ETH_ALEN;
3899e28a4053SRui Paulo os_memcpy(pos, wpa_auth->addr, ETH_ALEN);
3900e28a4053SRui Paulo pos += ETH_ALEN;
3901e28a4053SRui Paulo WPA_PUT_LE16(pos, status);
3902e28a4053SRui Paulo pos += 2;
39035b9c547cSRui Paulo if (resp_ies)
3904e28a4053SRui Paulo os_memcpy(pos, resp_ies, resp_ies_len);
3905e28a4053SRui Paulo
3906e28a4053SRui Paulo wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
3907e28a4053SRui Paulo sizeof(*frame) + rlen);
3908e28a4053SRui Paulo os_free(frame);
3909e28a4053SRui Paulo
3910e28a4053SRui Paulo return 0;
3911e28a4053SRui Paulo }
3912e28a4053SRui Paulo
3913e28a4053SRui Paulo
wpa_ft_rrb_build_r0(const u8 * key,const size_t key_len,const struct tlv_list * tlvs,const struct wpa_ft_pmk_r0_sa * pmk_r0,const u8 * r1kh_id,const u8 * s1kh_id,const struct tlv_list * tlv_auth,const u8 * src_addr,u8 type,u8 ** packet,size_t * packet_len)391485732ac8SCy Schubert static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
391585732ac8SCy Schubert const struct tlv_list *tlvs,
391685732ac8SCy Schubert const struct wpa_ft_pmk_r0_sa *pmk_r0,
391785732ac8SCy Schubert const u8 *r1kh_id, const u8 *s1kh_id,
391885732ac8SCy Schubert const struct tlv_list *tlv_auth,
391985732ac8SCy Schubert const u8 *src_addr, u8 type,
392085732ac8SCy Schubert u8 **packet, size_t *packet_len)
392185732ac8SCy Schubert {
392285732ac8SCy Schubert u8 pmk_r1[PMK_LEN_MAX];
392385732ac8SCy Schubert size_t pmk_r1_len = pmk_r0->pmk_r0_len;
392485732ac8SCy Schubert u8 pmk_r1_name[WPA_PMK_NAME_LEN];
392585732ac8SCy Schubert u8 f_pairwise[sizeof(le16)];
392685732ac8SCy Schubert u8 f_expires_in[sizeof(le16)];
392785732ac8SCy Schubert u8 f_session_timeout[sizeof(le32)];
392885732ac8SCy Schubert int expires_in;
392985732ac8SCy Schubert int session_timeout;
393085732ac8SCy Schubert struct os_reltime now;
393185732ac8SCy Schubert int ret;
393285732ac8SCy Schubert struct tlv_list sess_tlv[] = {
393385732ac8SCy Schubert { .type = FT_RRB_PMK_R1, .len = pmk_r1_len,
393485732ac8SCy Schubert .data = pmk_r1 },
393585732ac8SCy Schubert { .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
393685732ac8SCy Schubert .data = pmk_r1_name },
393785732ac8SCy Schubert { .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
393885732ac8SCy Schubert .data = f_pairwise },
393985732ac8SCy Schubert { .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in),
394085732ac8SCy Schubert .data = f_expires_in },
394185732ac8SCy Schubert { .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len,
394285732ac8SCy Schubert .data = pmk_r0->identity },
394385732ac8SCy Schubert { .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len,
394485732ac8SCy Schubert .data = pmk_r0->radius_cui },
394585732ac8SCy Schubert { .type = FT_RRB_SESSION_TIMEOUT,
394685732ac8SCy Schubert .len = sizeof(f_session_timeout),
394785732ac8SCy Schubert .data = f_session_timeout },
394885732ac8SCy Schubert { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
394985732ac8SCy Schubert };
395085732ac8SCy Schubert
3951c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 for peer AP");
395285732ac8SCy Schubert if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
395385732ac8SCy Schubert pmk_r0->pmk_r0_name, r1kh_id,
395485732ac8SCy Schubert s1kh_id, pmk_r1, pmk_r1_name) < 0)
395585732ac8SCy Schubert return -1;
395685732ac8SCy Schubert WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
395785732ac8SCy Schubert
395885732ac8SCy Schubert os_get_reltime(&now);
395985732ac8SCy Schubert if (pmk_r0->expiration > now.sec)
396085732ac8SCy Schubert expires_in = pmk_r0->expiration - now.sec;
396185732ac8SCy Schubert else if (pmk_r0->expiration)
396285732ac8SCy Schubert expires_in = 1;
396385732ac8SCy Schubert else
396485732ac8SCy Schubert expires_in = 0;
396585732ac8SCy Schubert WPA_PUT_LE16(f_expires_in, expires_in);
396685732ac8SCy Schubert
396785732ac8SCy Schubert if (pmk_r0->session_timeout > now.sec)
396885732ac8SCy Schubert session_timeout = pmk_r0->session_timeout - now.sec;
396985732ac8SCy Schubert else if (pmk_r0->session_timeout)
397085732ac8SCy Schubert session_timeout = 1;
397185732ac8SCy Schubert else
397285732ac8SCy Schubert session_timeout = 0;
397385732ac8SCy Schubert WPA_PUT_LE32(f_session_timeout, session_timeout);
397485732ac8SCy Schubert
397585732ac8SCy Schubert ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
397685732ac8SCy Schubert pmk_r0->vlan, src_addr, type,
397785732ac8SCy Schubert packet, packet_len);
397885732ac8SCy Schubert
3979206b73d0SCy Schubert forced_memzero(pmk_r1, sizeof(pmk_r1));
398085732ac8SCy Schubert
398185732ac8SCy Schubert return ret;
398285732ac8SCy Schubert }
398385732ac8SCy Schubert
398485732ac8SCy Schubert
wpa_ft_rrb_rx_pull(struct wpa_authenticator * wpa_auth,const u8 * src_addr,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,int no_defer)3985e28a4053SRui Paulo static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
3986e28a4053SRui Paulo const u8 *src_addr,
398785732ac8SCy Schubert const u8 *enc, size_t enc_len,
398885732ac8SCy Schubert const u8 *auth, size_t auth_len,
398985732ac8SCy Schubert int no_defer)
3990e28a4053SRui Paulo {
399185732ac8SCy Schubert const char *msgtype = "pull request";
399285732ac8SCy Schubert u8 *plain = NULL, *packet = NULL;
399385732ac8SCy Schubert size_t plain_len = 0, packet_len = 0;
399485732ac8SCy Schubert struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
399585732ac8SCy Schubert const u8 *key;
399685732ac8SCy Schubert size_t key_len;
399785732ac8SCy Schubert int seq_ret;
399885732ac8SCy Schubert const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
399985732ac8SCy Schubert size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
400085732ac8SCy Schubert size_t f_pmk_r0_name_len;
400185732ac8SCy Schubert const struct wpa_ft_pmk_r0_sa *r0;
400285732ac8SCy Schubert int ret;
400385732ac8SCy Schubert struct tlv_list resp[2];
400485732ac8SCy Schubert struct tlv_list resp_auth[5];
400585732ac8SCy Schubert struct ft_rrb_seq f_seq;
4006e28a4053SRui Paulo
4007e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
4008e28a4053SRui Paulo
400985732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
401085732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
4011e28a4053SRui Paulo
401285732ac8SCy Schubert if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) {
401385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch");
401485732ac8SCy Schubert goto out;
4015e28a4053SRui Paulo }
4016e28a4053SRui Paulo
401785732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
401885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
401985732ac8SCy Schubert
402085732ac8SCy Schubert wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
402185732ac8SCy Schubert if (r1kh) {
402285732ac8SCy Schubert key = r1kh->key;
402385732ac8SCy Schubert key_len = sizeof(r1kh->key);
402485732ac8SCy Schubert } else if (r1kh_wildcard) {
402585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID");
402685732ac8SCy Schubert key = r1kh_wildcard->key;
402785732ac8SCy Schubert key_len = sizeof(r1kh_wildcard->key);
402885732ac8SCy Schubert } else {
402985732ac8SCy Schubert goto out;
4030e28a4053SRui Paulo }
4031e28a4053SRui Paulo
403285732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
403385732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
4034e28a4053SRui Paulo
403585732ac8SCy Schubert seq_ret = FT_RRB_SEQ_DROP;
403685732ac8SCy Schubert if (r1kh)
403785732ac8SCy Schubert seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
403885732ac8SCy Schubert auth, auth_len, msgtype, no_defer);
403985732ac8SCy Schubert if (!no_defer && r1kh_wildcard &&
4040*a90b9d01SCy Schubert (!r1kh || !ether_addr_equal(r1kh->addr, src_addr))) {
404185732ac8SCy Schubert /* wildcard: r1kh-id unknown or changed addr -> do a seq req */
404285732ac8SCy Schubert seq_ret = FT_RRB_SEQ_DEFER;
4043e28a4053SRui Paulo }
4044e28a4053SRui Paulo
404585732ac8SCy Schubert if (seq_ret == FT_RRB_SEQ_DROP)
404685732ac8SCy Schubert goto out;
4047e28a4053SRui Paulo
404885732ac8SCy Schubert if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
404985732ac8SCy Schubert src_addr, FT_PACKET_R0KH_R1KH_PULL,
405085732ac8SCy Schubert &plain, &plain_len) < 0)
405185732ac8SCy Schubert goto out;
405285732ac8SCy Schubert
405385732ac8SCy Schubert if (!r1kh)
405485732ac8SCy Schubert r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr,
405585732ac8SCy Schubert f_r1kh_id,
405685732ac8SCy Schubert wpa_auth->conf.rkh_pos_timeout);
405785732ac8SCy Schubert if (!r1kh)
405885732ac8SCy Schubert goto out;
405985732ac8SCy Schubert
406085732ac8SCy Schubert if (seq_ret == FT_RRB_SEQ_DEFER) {
406185732ac8SCy Schubert wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
406285732ac8SCy Schubert f_r0kh_id_len, f_r1kh_id, key, key_len,
406385732ac8SCy Schubert enc, enc_len, auth, auth_len,
406485732ac8SCy Schubert &wpa_ft_rrb_rx_pull);
406585732ac8SCy Schubert goto out;
4066e28a4053SRui Paulo }
4067e28a4053SRui Paulo
406885732ac8SCy Schubert wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
406985732ac8SCy Schubert msgtype);
407085732ac8SCy Schubert wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
407185732ac8SCy Schubert wpa_auth->conf.rkh_pos_timeout);
4072e28a4053SRui Paulo
407385732ac8SCy Schubert RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN);
407485732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
407585732ac8SCy Schubert f_pmk_r0_name_len);
407685732ac8SCy Schubert
407785732ac8SCy Schubert RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
407885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
407985732ac8SCy Schubert
408085732ac8SCy Schubert if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
408185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
408285732ac8SCy Schubert goto out;
408385732ac8SCy Schubert }
408485732ac8SCy Schubert
4085c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull response from " MACSTR
4086c1d255d3SCy Schubert " to " MACSTR,
4087c1d255d3SCy Schubert MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
4088c1d255d3SCy Schubert
408985732ac8SCy Schubert resp[0].type = FT_RRB_S1KH_ID;
409085732ac8SCy Schubert resp[0].len = f_s1kh_id_len;
409185732ac8SCy Schubert resp[0].data = f_s1kh_id;
409285732ac8SCy Schubert resp[1].type = FT_RRB_LAST_EMPTY;
409385732ac8SCy Schubert resp[1].len = 0;
409485732ac8SCy Schubert resp[1].data = NULL;
409585732ac8SCy Schubert
409685732ac8SCy Schubert resp_auth[0].type = FT_RRB_NONCE;
409785732ac8SCy Schubert resp_auth[0].len = f_nonce_len;
409885732ac8SCy Schubert resp_auth[0].data = f_nonce;
409985732ac8SCy Schubert resp_auth[1].type = FT_RRB_SEQ;
410085732ac8SCy Schubert resp_auth[1].len = sizeof(f_seq);
410185732ac8SCy Schubert resp_auth[1].data = (u8 *) &f_seq;
410285732ac8SCy Schubert resp_auth[2].type = FT_RRB_R0KH_ID;
410385732ac8SCy Schubert resp_auth[2].len = f_r0kh_id_len;
410485732ac8SCy Schubert resp_auth[2].data = f_r0kh_id;
410585732ac8SCy Schubert resp_auth[3].type = FT_RRB_R1KH_ID;
410685732ac8SCy Schubert resp_auth[3].len = f_r1kh_id_len;
410785732ac8SCy Schubert resp_auth[3].data = f_r1kh_id;
410885732ac8SCy Schubert resp_auth[4].type = FT_RRB_LAST_EMPTY;
410985732ac8SCy Schubert resp_auth[4].len = 0;
411085732ac8SCy Schubert resp_auth[4].data = NULL;
411185732ac8SCy Schubert
411285732ac8SCy Schubert if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) {
411385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
411485732ac8SCy Schubert ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth,
411585732ac8SCy Schubert NULL, wpa_auth->addr,
411685732ac8SCy Schubert FT_PACKET_R0KH_R1KH_RESP,
411785732ac8SCy Schubert &packet, &packet_len);
411885732ac8SCy Schubert } else {
411985732ac8SCy Schubert ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id,
412085732ac8SCy Schubert f_s1kh_id, resp_auth, wpa_auth->addr,
412185732ac8SCy Schubert FT_PACKET_R0KH_R1KH_RESP,
412285732ac8SCy Schubert &packet, &packet_len);
412385732ac8SCy Schubert }
412485732ac8SCy Schubert
412585732ac8SCy Schubert if (!ret)
412685732ac8SCy Schubert wpa_ft_rrb_oui_send(wpa_auth, src_addr,
412785732ac8SCy Schubert FT_PACKET_R0KH_R1KH_RESP, packet,
412885732ac8SCy Schubert packet_len);
412985732ac8SCy Schubert
413085732ac8SCy Schubert out:
413185732ac8SCy Schubert os_free(plain);
413285732ac8SCy Schubert os_free(packet);
4133e28a4053SRui Paulo
4134e28a4053SRui Paulo return 0;
4135e28a4053SRui Paulo }
4136e28a4053SRui Paulo
4137e28a4053SRui Paulo
413885732ac8SCy Schubert /* @returns 0 on success
413985732ac8SCy Schubert * -1 on error
414085732ac8SCy Schubert * -2 if FR_RRB_PAIRWISE is missing
414185732ac8SCy Schubert */
wpa_ft_rrb_rx_r1(struct wpa_authenticator * wpa_auth,const u8 * src_addr,u8 type,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,const char * msgtype,u8 * s1kh_id_out,int (* cb)(struct wpa_authenticator * wpa_auth,const u8 * src_addr,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,int no_defer))414285732ac8SCy Schubert static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
414385732ac8SCy Schubert const u8 *src_addr, u8 type,
414485732ac8SCy Schubert const u8 *enc, size_t enc_len,
414585732ac8SCy Schubert const u8 *auth, size_t auth_len,
414685732ac8SCy Schubert const char *msgtype, u8 *s1kh_id_out,
414785732ac8SCy Schubert int (*cb)(struct wpa_authenticator *wpa_auth,
414885732ac8SCy Schubert const u8 *src_addr,
414985732ac8SCy Schubert const u8 *enc, size_t enc_len,
415085732ac8SCy Schubert const u8 *auth, size_t auth_len,
415185732ac8SCy Schubert int no_defer))
41525b9c547cSRui Paulo {
415385732ac8SCy Schubert u8 *plain = NULL;
415485732ac8SCy Schubert size_t plain_len = 0;
415585732ac8SCy Schubert struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
415685732ac8SCy Schubert const u8 *key;
415785732ac8SCy Schubert size_t key_len;
415885732ac8SCy Schubert int seq_ret;
415985732ac8SCy Schubert const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
416085732ac8SCy Schubert const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
416185732ac8SCy Schubert const u8 *f_expires_in;
416285732ac8SCy Schubert size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
416385732ac8SCy Schubert const u8 *f_identity, *f_radius_cui;
416485732ac8SCy Schubert const u8 *f_session_timeout;
416585732ac8SCy Schubert size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
416685732ac8SCy Schubert size_t f_expires_in_len;
416785732ac8SCy Schubert size_t f_identity_len, f_radius_cui_len;
416885732ac8SCy Schubert size_t f_session_timeout_len;
416985732ac8SCy Schubert int pairwise;
417085732ac8SCy Schubert int ret = -1;
417185732ac8SCy Schubert int expires_in;
417285732ac8SCy Schubert int session_timeout;
417385732ac8SCy Schubert struct vlan_description vlan;
417485732ac8SCy Schubert size_t pmk_r1_len;
417585732ac8SCy Schubert
417685732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
417785732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
417885732ac8SCy Schubert
417985732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
418085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
418185732ac8SCy Schubert
418285732ac8SCy Schubert if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) {
418385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch");
418485732ac8SCy Schubert goto out;
418585732ac8SCy Schubert }
418685732ac8SCy Schubert
418785732ac8SCy Schubert wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
418885732ac8SCy Schubert &r0kh_wildcard);
418985732ac8SCy Schubert if (r0kh) {
419085732ac8SCy Schubert key = r0kh->key;
419185732ac8SCy Schubert key_len = sizeof(r0kh->key);
419285732ac8SCy Schubert } else if (r0kh_wildcard) {
419385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
419485732ac8SCy Schubert key = r0kh_wildcard->key;
419585732ac8SCy Schubert key_len = sizeof(r0kh_wildcard->key);
419685732ac8SCy Schubert } else {
419785732ac8SCy Schubert goto out;
419885732ac8SCy Schubert }
419985732ac8SCy Schubert
420085732ac8SCy Schubert seq_ret = FT_RRB_SEQ_DROP;
420185732ac8SCy Schubert if (r0kh) {
420285732ac8SCy Schubert seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len,
420385732ac8SCy Schubert auth, auth_len, msgtype,
420485732ac8SCy Schubert cb ? 0 : 1);
420585732ac8SCy Schubert }
420685732ac8SCy Schubert if (cb && r0kh_wildcard &&
4207*a90b9d01SCy Schubert (!r0kh || !ether_addr_equal(r0kh->addr, src_addr))) {
420885732ac8SCy Schubert /* wildcard: r0kh-id unknown or changed addr -> do a seq req */
420985732ac8SCy Schubert seq_ret = FT_RRB_SEQ_DEFER;
421085732ac8SCy Schubert }
421185732ac8SCy Schubert
421285732ac8SCy Schubert if (seq_ret == FT_RRB_SEQ_DROP)
421385732ac8SCy Schubert goto out;
421485732ac8SCy Schubert
421585732ac8SCy Schubert if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
421685732ac8SCy Schubert src_addr, type, &plain, &plain_len) < 0)
421785732ac8SCy Schubert goto out;
421885732ac8SCy Schubert
421985732ac8SCy Schubert if (!r0kh)
422085732ac8SCy Schubert r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr,
422185732ac8SCy Schubert f_r0kh_id, f_r0kh_id_len,
422285732ac8SCy Schubert wpa_auth->conf.rkh_pos_timeout);
422385732ac8SCy Schubert if (!r0kh)
422485732ac8SCy Schubert goto out;
422585732ac8SCy Schubert
422685732ac8SCy Schubert if (seq_ret == FT_RRB_SEQ_DEFER) {
422785732ac8SCy Schubert wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
422885732ac8SCy Schubert f_r0kh_id_len, f_r1kh_id, key, key_len,
422985732ac8SCy Schubert enc, enc_len, auth, auth_len, cb);
423085732ac8SCy Schubert goto out;
423185732ac8SCy Schubert }
423285732ac8SCy Schubert
423385732ac8SCy Schubert wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
423485732ac8SCy Schubert msgtype);
423585732ac8SCy Schubert wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
423685732ac8SCy Schubert wpa_auth->conf.rkh_pos_timeout);
423785732ac8SCy Schubert
423885732ac8SCy Schubert RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
423985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
424085732ac8SCy Schubert
424185732ac8SCy Schubert if (s1kh_id_out)
424285732ac8SCy Schubert os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
424385732ac8SCy Schubert
424485732ac8SCy Schubert ret = -2;
424585732ac8SCy Schubert RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
424685732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len);
424785732ac8SCy Schubert
424885732ac8SCy Schubert ret = -1;
424985732ac8SCy Schubert RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
425085732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
425185732ac8SCy Schubert f_pmk_r1_name, WPA_PMK_NAME_LEN);
425285732ac8SCy Schubert
425385732ac8SCy Schubert pmk_r1_len = PMK_LEN;
425485732ac8SCy Schubert if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
425585732ac8SCy Schubert &f_pmk_r1) == 0 &&
4256*a90b9d01SCy Schubert (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN ||
4257*a90b9d01SCy Schubert f_pmk_r1_len == SHA512_MAC_LEN))
425885732ac8SCy Schubert pmk_r1_len = f_pmk_r1_len;
425985732ac8SCy Schubert RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
426085732ac8SCy Schubert wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
426185732ac8SCy Schubert
426285732ac8SCy Schubert pairwise = WPA_GET_LE16(f_pairwise);
426385732ac8SCy Schubert
426485732ac8SCy Schubert RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype,
426585732ac8SCy Schubert sizeof(le16));
426685732ac8SCy Schubert if (f_expires_in)
426785732ac8SCy Schubert expires_in = WPA_GET_LE16(f_expires_in);
426885732ac8SCy Schubert else
426985732ac8SCy Schubert expires_in = 0;
427085732ac8SCy Schubert
427185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype,
427285732ac8SCy Schubert expires_in);
427385732ac8SCy Schubert
427485732ac8SCy Schubert if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) {
427585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan");
427685732ac8SCy Schubert wpa_ft_rrb_dump(plain, plain_len);
427785732ac8SCy Schubert goto out;
427885732ac8SCy Schubert }
427985732ac8SCy Schubert
428085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: vlan %d%s",
428185732ac8SCy Schubert le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : "");
428285732ac8SCy Schubert
428385732ac8SCy Schubert RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1);
428485732ac8SCy Schubert if (f_identity)
428585732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity,
428685732ac8SCy Schubert f_identity_len);
428785732ac8SCy Schubert
428885732ac8SCy Schubert RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1);
428985732ac8SCy Schubert if (f_radius_cui)
429085732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui,
429185732ac8SCy Schubert f_radius_cui_len);
429285732ac8SCy Schubert
429385732ac8SCy Schubert RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype,
429485732ac8SCy Schubert sizeof(le32));
429585732ac8SCy Schubert if (f_session_timeout)
429685732ac8SCy Schubert session_timeout = WPA_GET_LE32(f_session_timeout);
429785732ac8SCy Schubert else
429885732ac8SCy Schubert session_timeout = 0;
429985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout);
430085732ac8SCy Schubert
430185732ac8SCy Schubert if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len,
430285732ac8SCy Schubert f_pmk_r1_name,
430385732ac8SCy Schubert pairwise, &vlan, expires_in, session_timeout,
430485732ac8SCy Schubert f_identity, f_identity_len, f_radius_cui,
430585732ac8SCy Schubert f_radius_cui_len) < 0)
430685732ac8SCy Schubert goto out;
430785732ac8SCy Schubert
430885732ac8SCy Schubert ret = 0;
430985732ac8SCy Schubert out:
4310206b73d0SCy Schubert bin_clear_free(plain, plain_len);
431185732ac8SCy Schubert
431285732ac8SCy Schubert return ret;
431385732ac8SCy Schubert
431485732ac8SCy Schubert }
431585732ac8SCy Schubert
431685732ac8SCy Schubert
ft_finish_pull(struct wpa_state_machine * sm)431785732ac8SCy Schubert static void ft_finish_pull(struct wpa_state_machine *sm)
431885732ac8SCy Schubert {
43195b9c547cSRui Paulo int res;
43205b9c547cSRui Paulo u8 *resp_ies;
43215b9c547cSRui Paulo size_t resp_ies_len;
43225b9c547cSRui Paulo u16 status;
43235b9c547cSRui Paulo
432485732ac8SCy Schubert if (!sm->ft_pending_cb || !sm->ft_pending_req_ies)
432585732ac8SCy Schubert return;
432685732ac8SCy Schubert
43275b9c547cSRui Paulo res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
43285b9c547cSRui Paulo wpabuf_len(sm->ft_pending_req_ies),
43295b9c547cSRui Paulo &resp_ies, &resp_ies_len);
433085732ac8SCy Schubert if (res < 0) {
433185732ac8SCy Schubert /* this loop is broken by ft_pending_pull_left_retries */
433285732ac8SCy Schubert wpa_printf(MSG_DEBUG,
433385732ac8SCy Schubert "FT: Callback postponed until response is available");
433485732ac8SCy Schubert return;
433585732ac8SCy Schubert }
43365b9c547cSRui Paulo wpabuf_free(sm->ft_pending_req_ies);
43375b9c547cSRui Paulo sm->ft_pending_req_ies = NULL;
43385b9c547cSRui Paulo status = res;
43395b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
43405b9c547cSRui Paulo " - status %u", MAC2STR(sm->addr), status);
43415b9c547cSRui Paulo
4342*a90b9d01SCy Schubert sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr,
43435b9c547cSRui Paulo sm->ft_pending_auth_transaction + 1, status,
43445b9c547cSRui Paulo resp_ies, resp_ies_len);
43455b9c547cSRui Paulo os_free(resp_ies);
43465b9c547cSRui Paulo }
43475b9c547cSRui Paulo
43485b9c547cSRui Paulo
434985732ac8SCy Schubert struct ft_get_sta_ctx {
435085732ac8SCy Schubert const u8 *nonce;
435185732ac8SCy Schubert const u8 *s1kh_id;
435285732ac8SCy Schubert struct wpa_state_machine *sm;
435385732ac8SCy Schubert };
435485732ac8SCy Schubert
435585732ac8SCy Schubert
ft_get_sta_cb(struct wpa_state_machine * sm,void * ctx)435685732ac8SCy Schubert static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
43575b9c547cSRui Paulo {
435885732ac8SCy Schubert struct ft_get_sta_ctx *info = ctx;
43595b9c547cSRui Paulo
436085732ac8SCy Schubert if ((info->s1kh_id &&
4361*a90b9d01SCy Schubert !ether_addr_equal(info->s1kh_id, sm->addr)) ||
436285732ac8SCy Schubert os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
436385732ac8SCy Schubert FT_RRB_NONCE_LEN) != 0 ||
436485732ac8SCy Schubert sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
43655b9c547cSRui Paulo return 0;
43665b9c547cSRui Paulo
436785732ac8SCy Schubert info->sm = sm;
436885732ac8SCy Schubert
43695b9c547cSRui Paulo return 1;
43705b9c547cSRui Paulo }
43715b9c547cSRui Paulo
43725b9c547cSRui Paulo
wpa_ft_rrb_rx_resp(struct wpa_authenticator * wpa_auth,const u8 * src_addr,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,int no_defer)4373e28a4053SRui Paulo static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
4374e28a4053SRui Paulo const u8 *src_addr,
437585732ac8SCy Schubert const u8 *enc, size_t enc_len,
437685732ac8SCy Schubert const u8 *auth, size_t auth_len,
437785732ac8SCy Schubert int no_defer)
4378e28a4053SRui Paulo {
437985732ac8SCy Schubert const char *msgtype = "pull response";
438085732ac8SCy Schubert int nak, ret = -1;
438185732ac8SCy Schubert struct ft_get_sta_ctx ctx;
438285732ac8SCy Schubert u8 s1kh_id[ETH_ALEN];
438385732ac8SCy Schubert const u8 *f_nonce;
438485732ac8SCy Schubert size_t f_nonce_len;
4385e28a4053SRui Paulo
4386e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
4387e28a4053SRui Paulo
438885732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN);
438985732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
4390e28a4053SRui Paulo
439185732ac8SCy Schubert os_memset(&ctx, 0, sizeof(ctx));
439285732ac8SCy Schubert ctx.nonce = f_nonce;
439385732ac8SCy Schubert if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
439485732ac8SCy Schubert /* nonce not found */
439585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Invalid nonce");
4396e28a4053SRui Paulo return -1;
4397e28a4053SRui Paulo }
4398e28a4053SRui Paulo
439985732ac8SCy Schubert ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
440085732ac8SCy Schubert enc, enc_len, auth, auth_len, msgtype, s1kh_id,
440185732ac8SCy Schubert no_defer ? NULL : &wpa_ft_rrb_rx_resp);
440285732ac8SCy Schubert if (ret == -2) {
440385732ac8SCy Schubert ret = 0;
440485732ac8SCy Schubert nak = 1;
440585732ac8SCy Schubert } else {
440685732ac8SCy Schubert nak = 0;
440785732ac8SCy Schubert }
440885732ac8SCy Schubert if (ret < 0)
4409e28a4053SRui Paulo return -1;
441085732ac8SCy Schubert
441185732ac8SCy Schubert ctx.s1kh_id = s1kh_id;
441285732ac8SCy Schubert if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
441385732ac8SCy Schubert wpa_printf(MSG_DEBUG,
441485732ac8SCy Schubert "FT: Response to a pending pull request for " MACSTR,
441585732ac8SCy Schubert MAC2STR(ctx.sm->addr));
441685732ac8SCy Schubert eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL);
441785732ac8SCy Schubert if (nak)
441885732ac8SCy Schubert ctx.sm->ft_pending_pull_left_retries = 0;
441985732ac8SCy Schubert ft_finish_pull(ctx.sm);
4420e28a4053SRui Paulo }
4421e28a4053SRui Paulo
442285732ac8SCy Schubert out:
442385732ac8SCy Schubert return ret;
4424e28a4053SRui Paulo }
4425e28a4053SRui Paulo
4426e28a4053SRui Paulo
wpa_ft_rrb_rx_push(struct wpa_authenticator * wpa_auth,const u8 * src_addr,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,int no_defer)4427e28a4053SRui Paulo static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
4428e28a4053SRui Paulo const u8 *src_addr,
442985732ac8SCy Schubert const u8 *enc, size_t enc_len,
443085732ac8SCy Schubert const u8 *auth, size_t auth_len, int no_defer)
4431e28a4053SRui Paulo {
443285732ac8SCy Schubert const char *msgtype = "push";
4433e28a4053SRui Paulo
4434e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
4435e28a4053SRui Paulo
443685732ac8SCy Schubert if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
443785732ac8SCy Schubert enc, enc_len, auth, auth_len, msgtype, NULL,
443885732ac8SCy Schubert no_defer ? NULL : wpa_ft_rrb_rx_push) < 0)
4439e28a4053SRui Paulo return -1;
4440e28a4053SRui Paulo
4441e28a4053SRui Paulo return 0;
4442e28a4053SRui Paulo }
4443e28a4053SRui Paulo
4444e28a4053SRui Paulo
wpa_ft_rrb_rx_seq(struct wpa_authenticator * wpa_auth,const u8 * src_addr,int type,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,struct ft_remote_seq ** rkh_seq,u8 ** key,size_t * key_len,struct ft_remote_r0kh ** r0kh_out,struct ft_remote_r1kh ** r1kh_out,struct ft_remote_r0kh ** r0kh_wildcard_out,struct ft_remote_r1kh ** r1kh_wildcard_out)444585732ac8SCy Schubert static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
444685732ac8SCy Schubert const u8 *src_addr, int type,
444785732ac8SCy Schubert const u8 *enc, size_t enc_len,
444885732ac8SCy Schubert const u8 *auth, size_t auth_len,
444985732ac8SCy Schubert struct ft_remote_seq **rkh_seq,
445085732ac8SCy Schubert u8 **key, size_t *key_len,
445185732ac8SCy Schubert struct ft_remote_r0kh **r0kh_out,
445285732ac8SCy Schubert struct ft_remote_r1kh **r1kh_out,
445385732ac8SCy Schubert struct ft_remote_r0kh **r0kh_wildcard_out,
445485732ac8SCy Schubert struct ft_remote_r1kh **r1kh_wildcard_out)
445585732ac8SCy Schubert {
445685732ac8SCy Schubert struct ft_remote_r0kh *r0kh = NULL;
445785732ac8SCy Schubert struct ft_remote_r1kh *r1kh = NULL;
445885732ac8SCy Schubert const u8 *f_r0kh_id, *f_r1kh_id;
445985732ac8SCy Schubert size_t f_r0kh_id_len, f_r1kh_id_len;
446085732ac8SCy Schubert int to_r0kh, to_r1kh;
446185732ac8SCy Schubert u8 *plain = NULL;
446285732ac8SCy Schubert size_t plain_len = 0;
446385732ac8SCy Schubert struct ft_remote_r0kh *r0kh_wildcard;
446485732ac8SCy Schubert struct ft_remote_r1kh *r1kh_wildcard;
446585732ac8SCy Schubert
446685732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
446785732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
446885732ac8SCy Schubert
446985732ac8SCy Schubert to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len);
447085732ac8SCy Schubert to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id);
447185732ac8SCy Schubert
447285732ac8SCy Schubert if (to_r0kh && to_r1kh) {
447385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID");
447485732ac8SCy Schubert goto out;
447585732ac8SCy Schubert }
447685732ac8SCy Schubert
447785732ac8SCy Schubert if (!to_r0kh && !to_r1kh) {
447885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID");
447985732ac8SCy Schubert goto out;
448085732ac8SCy Schubert }
448185732ac8SCy Schubert
448285732ac8SCy Schubert if (!to_r0kh) {
448385732ac8SCy Schubert wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
448485732ac8SCy Schubert &r0kh, &r0kh_wildcard);
448585732ac8SCy Schubert if (!r0kh_wildcard &&
4486*a90b9d01SCy Schubert (!r0kh || !ether_addr_equal(r0kh->addr, src_addr))) {
448785732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
448885732ac8SCy Schubert f_r0kh_id, f_r0kh_id_len);
448985732ac8SCy Schubert goto out;
449085732ac8SCy Schubert }
449185732ac8SCy Schubert if (r0kh) {
449285732ac8SCy Schubert *key = r0kh->key;
449385732ac8SCy Schubert *key_len = sizeof(r0kh->key);
449485732ac8SCy Schubert } else {
449585732ac8SCy Schubert *key = r0kh_wildcard->key;
449685732ac8SCy Schubert *key_len = sizeof(r0kh_wildcard->key);
449785732ac8SCy Schubert }
449885732ac8SCy Schubert }
449985732ac8SCy Schubert
450085732ac8SCy Schubert if (!to_r1kh) {
450185732ac8SCy Schubert wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
450285732ac8SCy Schubert &r1kh_wildcard);
450385732ac8SCy Schubert if (!r1kh_wildcard &&
4504*a90b9d01SCy Schubert (!r1kh || !ether_addr_equal(r1kh->addr, src_addr))) {
450585732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
450685732ac8SCy Schubert f_r1kh_id, FT_R1KH_ID_LEN);
450785732ac8SCy Schubert goto out;
450885732ac8SCy Schubert }
450985732ac8SCy Schubert if (r1kh) {
451085732ac8SCy Schubert *key = r1kh->key;
451185732ac8SCy Schubert *key_len = sizeof(r1kh->key);
451285732ac8SCy Schubert } else {
451385732ac8SCy Schubert *key = r1kh_wildcard->key;
451485732ac8SCy Schubert *key_len = sizeof(r1kh_wildcard->key);
451585732ac8SCy Schubert }
451685732ac8SCy Schubert }
451785732ac8SCy Schubert
451885732ac8SCy Schubert if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
451985732ac8SCy Schubert src_addr, type, &plain, &plain_len) < 0)
452085732ac8SCy Schubert goto out;
452185732ac8SCy Schubert
452285732ac8SCy Schubert os_free(plain);
452385732ac8SCy Schubert
452485732ac8SCy Schubert if (!to_r0kh) {
452585732ac8SCy Schubert if (!r0kh)
452685732ac8SCy Schubert r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard,
452785732ac8SCy Schubert src_addr, f_r0kh_id,
452885732ac8SCy Schubert f_r0kh_id_len,
452985732ac8SCy Schubert ftRRBseqTimeout);
453085732ac8SCy Schubert if (!r0kh)
453185732ac8SCy Schubert goto out;
453285732ac8SCy Schubert
453385732ac8SCy Schubert wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout);
453485732ac8SCy Schubert *rkh_seq = r0kh->seq;
453585732ac8SCy Schubert if (r0kh_out)
453685732ac8SCy Schubert *r0kh_out = r0kh;
453785732ac8SCy Schubert if (r0kh_wildcard_out)
453885732ac8SCy Schubert *r0kh_wildcard_out = r0kh_wildcard;
453985732ac8SCy Schubert }
454085732ac8SCy Schubert
454185732ac8SCy Schubert if (!to_r1kh) {
454285732ac8SCy Schubert if (!r1kh)
454385732ac8SCy Schubert r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard,
454485732ac8SCy Schubert src_addr, f_r1kh_id,
454585732ac8SCy Schubert ftRRBseqTimeout);
454685732ac8SCy Schubert if (!r1kh)
454785732ac8SCy Schubert goto out;
454885732ac8SCy Schubert
454985732ac8SCy Schubert wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout);
455085732ac8SCy Schubert *rkh_seq = r1kh->seq;
455185732ac8SCy Schubert if (r1kh_out)
455285732ac8SCy Schubert *r1kh_out = r1kh;
455385732ac8SCy Schubert if (r1kh_wildcard_out)
455485732ac8SCy Schubert *r1kh_wildcard_out = r1kh_wildcard;
455585732ac8SCy Schubert }
455685732ac8SCy Schubert
455785732ac8SCy Schubert return 0;
455885732ac8SCy Schubert out:
455985732ac8SCy Schubert return -1;
456085732ac8SCy Schubert }
456185732ac8SCy Schubert
456285732ac8SCy Schubert
wpa_ft_rrb_rx_seq_req(struct wpa_authenticator * wpa_auth,const u8 * src_addr,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,int no_defer)456385732ac8SCy Schubert static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
456485732ac8SCy Schubert const u8 *src_addr,
456585732ac8SCy Schubert const u8 *enc, size_t enc_len,
456685732ac8SCy Schubert const u8 *auth, size_t auth_len,
456785732ac8SCy Schubert int no_defer)
456885732ac8SCy Schubert {
456985732ac8SCy Schubert int ret = -1;
457085732ac8SCy Schubert struct ft_rrb_seq f_seq;
457185732ac8SCy Schubert const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id;
457285732ac8SCy Schubert size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len;
457385732ac8SCy Schubert struct ft_remote_seq *rkh_seq = NULL;
457485732ac8SCy Schubert u8 *packet = NULL, *key = NULL;
457585732ac8SCy Schubert size_t packet_len = 0, key_len = 0;
457685732ac8SCy Schubert struct tlv_list seq_resp_auth[5];
457785732ac8SCy Schubert
457885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Received sequence number request");
457985732ac8SCy Schubert
458085732ac8SCy Schubert if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
458185732ac8SCy Schubert enc, enc_len, auth, auth_len, &rkh_seq, &key,
458285732ac8SCy Schubert &key_len, NULL, NULL, NULL, NULL) < 0)
458385732ac8SCy Schubert goto out;
458485732ac8SCy Schubert
458585732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
458685732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len);
458785732ac8SCy Schubert
458885732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
458985732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
459085732ac8SCy Schubert
459185732ac8SCy Schubert if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) {
459285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
459385732ac8SCy Schubert goto out;
459485732ac8SCy Schubert }
459585732ac8SCy Schubert
4596c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "FT: Send sequence number response from " MACSTR
4597c1d255d3SCy Schubert " to " MACSTR,
4598c1d255d3SCy Schubert MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
4599c1d255d3SCy Schubert
460085732ac8SCy Schubert seq_resp_auth[0].type = FT_RRB_NONCE;
460185732ac8SCy Schubert seq_resp_auth[0].len = f_nonce_len;
460285732ac8SCy Schubert seq_resp_auth[0].data = f_nonce;
460385732ac8SCy Schubert seq_resp_auth[1].type = FT_RRB_SEQ;
460485732ac8SCy Schubert seq_resp_auth[1].len = sizeof(f_seq);
460585732ac8SCy Schubert seq_resp_auth[1].data = (u8 *) &f_seq;
460685732ac8SCy Schubert seq_resp_auth[2].type = FT_RRB_R0KH_ID;
460785732ac8SCy Schubert seq_resp_auth[2].len = f_r0kh_id_len;
460885732ac8SCy Schubert seq_resp_auth[2].data = f_r0kh_id;
460985732ac8SCy Schubert seq_resp_auth[3].type = FT_RRB_R1KH_ID;
461085732ac8SCy Schubert seq_resp_auth[3].len = FT_R1KH_ID_LEN;
461185732ac8SCy Schubert seq_resp_auth[3].data = f_r1kh_id;
461285732ac8SCy Schubert seq_resp_auth[4].type = FT_RRB_LAST_EMPTY;
461385732ac8SCy Schubert seq_resp_auth[4].len = 0;
461485732ac8SCy Schubert seq_resp_auth[4].data = NULL;
461585732ac8SCy Schubert
461685732ac8SCy Schubert if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL,
461785732ac8SCy Schubert wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
461885732ac8SCy Schubert &packet, &packet_len) < 0)
461985732ac8SCy Schubert goto out;
462085732ac8SCy Schubert
462185732ac8SCy Schubert wpa_ft_rrb_oui_send(wpa_auth, src_addr,
462285732ac8SCy Schubert FT_PACKET_R0KH_R1KH_SEQ_RESP, packet,
462385732ac8SCy Schubert packet_len);
462485732ac8SCy Schubert
462585732ac8SCy Schubert out:
462685732ac8SCy Schubert os_free(packet);
462785732ac8SCy Schubert
462885732ac8SCy Schubert return ret;
462985732ac8SCy Schubert }
463085732ac8SCy Schubert
463185732ac8SCy Schubert
wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator * wpa_auth,const u8 * src_addr,const u8 * enc,size_t enc_len,const u8 * auth,size_t auth_len,int no_defer)463285732ac8SCy Schubert static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
463385732ac8SCy Schubert const u8 *src_addr,
463485732ac8SCy Schubert const u8 *enc, size_t enc_len,
463585732ac8SCy Schubert const u8 *auth, size_t auth_len,
463685732ac8SCy Schubert int no_defer)
463785732ac8SCy Schubert {
463885732ac8SCy Schubert u8 *key = NULL;
463985732ac8SCy Schubert size_t key_len = 0;
464085732ac8SCy Schubert struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL;
464185732ac8SCy Schubert struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL;
464285732ac8SCy Schubert const u8 *f_nonce, *f_seq;
464385732ac8SCy Schubert size_t f_nonce_len, f_seq_len;
464485732ac8SCy Schubert struct ft_remote_seq *rkh_seq = NULL;
464585732ac8SCy Schubert struct ft_remote_item *item;
464685732ac8SCy Schubert struct os_reltime now, now_remote;
464785732ac8SCy Schubert int seq_ret, found;
464885732ac8SCy Schubert const struct ft_rrb_seq *msg_both;
464985732ac8SCy Schubert u32 msg_dom, msg_seq;
465085732ac8SCy Schubert
465185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Received sequence number response");
465285732ac8SCy Schubert
465385732ac8SCy Schubert if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
465485732ac8SCy Schubert enc, enc_len, auth, auth_len, &rkh_seq, &key,
465585732ac8SCy Schubert &key_len, &r0kh, &r1kh, &r0kh_wildcard,
465685732ac8SCy Schubert &r1kh_wildcard) < 0)
465785732ac8SCy Schubert goto out;
465885732ac8SCy Schubert
465985732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN);
466085732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce,
466185732ac8SCy Schubert f_nonce_len);
466285732ac8SCy Schubert
466385732ac8SCy Schubert found = 0;
466485732ac8SCy Schubert dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item,
466585732ac8SCy Schubert list) {
466685732ac8SCy Schubert if (os_memcmp_const(f_nonce, item->nonce,
466785732ac8SCy Schubert FT_RRB_NONCE_LEN) != 0 ||
466885732ac8SCy Schubert os_get_reltime(&now) < 0 ||
466985732ac8SCy Schubert os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout))
467085732ac8SCy Schubert continue;
467185732ac8SCy Schubert
467285732ac8SCy Schubert found = 1;
467385732ac8SCy Schubert break;
467485732ac8SCy Schubert }
467585732ac8SCy Schubert if (!found) {
467685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce");
467785732ac8SCy Schubert goto out;
467885732ac8SCy Schubert }
467985732ac8SCy Schubert
468085732ac8SCy Schubert if (r0kh) {
468185732ac8SCy Schubert wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
468285732ac8SCy Schubert wpa_auth->conf.rkh_pos_timeout);
468385732ac8SCy Schubert if (r0kh_wildcard)
468485732ac8SCy Schubert os_memcpy(r0kh->addr, src_addr, ETH_ALEN);
468585732ac8SCy Schubert }
468685732ac8SCy Schubert
468785732ac8SCy Schubert if (r1kh) {
468885732ac8SCy Schubert wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
468985732ac8SCy Schubert wpa_auth->conf.rkh_pos_timeout);
469085732ac8SCy Schubert if (r1kh_wildcard)
469185732ac8SCy Schubert os_memcpy(r1kh->addr, src_addr, ETH_ALEN);
469285732ac8SCy Schubert }
469385732ac8SCy Schubert
469485732ac8SCy Schubert seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth,
469585732ac8SCy Schubert auth_len, "seq response", 1);
469685732ac8SCy Schubert if (seq_ret == FT_RRB_SEQ_OK) {
469785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number");
469885732ac8SCy Schubert wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth,
469985732ac8SCy Schubert auth_len, "seq response");
470085732ac8SCy Schubert } else {
470185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number");
470285732ac8SCy Schubert
470385732ac8SCy Schubert RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response",
470485732ac8SCy Schubert sizeof(*msg_both));
470585732ac8SCy Schubert msg_both = (const struct ft_rrb_seq *) f_seq;
470685732ac8SCy Schubert
470785732ac8SCy Schubert msg_dom = le_to_host32(msg_both->dom);
470885732ac8SCy Schubert msg_seq = le_to_host32(msg_both->seq);
470985732ac8SCy Schubert now_remote.sec = le_to_host32(msg_both->ts);
471085732ac8SCy Schubert now_remote.usec = 0;
471185732ac8SCy Schubert
471285732ac8SCy Schubert rkh_seq->rx.num_last = 2;
471385732ac8SCy Schubert rkh_seq->rx.dom = msg_dom;
471485732ac8SCy Schubert rkh_seq->rx.offsetidx = 0;
471585732ac8SCy Schubert /* Accept some older, possibly cached packets as well */
471685732ac8SCy Schubert rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG -
471785732ac8SCy Schubert dl_list_len(&rkh_seq->rx.queue);
471885732ac8SCy Schubert rkh_seq->rx.last[1] = msg_seq;
471985732ac8SCy Schubert
472085732ac8SCy Schubert /* local time - offset = remote time
472185732ac8SCy Schubert * <=> local time - remote time = offset */
472285732ac8SCy Schubert os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset);
472385732ac8SCy Schubert }
472485732ac8SCy Schubert
472585732ac8SCy Schubert wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1);
472685732ac8SCy Schubert
472785732ac8SCy Schubert return 0;
472885732ac8SCy Schubert out:
472985732ac8SCy Schubert return -1;
473085732ac8SCy Schubert }
473185732ac8SCy Schubert
473285732ac8SCy Schubert
wpa_ft_rrb_rx(struct wpa_authenticator * wpa_auth,const u8 * src_addr,const u8 * data,size_t data_len)4733e28a4053SRui Paulo int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
4734e28a4053SRui Paulo const u8 *data, size_t data_len)
4735e28a4053SRui Paulo {
4736e28a4053SRui Paulo struct ft_rrb_frame *frame;
4737e28a4053SRui Paulo u16 alen;
4738e28a4053SRui Paulo const u8 *pos, *end, *start;
4739e28a4053SRui Paulo u8 action;
4740e28a4053SRui Paulo const u8 *sta_addr, *target_ap_addr;
4741e28a4053SRui Paulo
4742e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR,
4743e28a4053SRui Paulo MAC2STR(src_addr));
4744e28a4053SRui Paulo
4745e28a4053SRui Paulo if (data_len < sizeof(*frame)) {
4746e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)",
4747e28a4053SRui Paulo (unsigned long) data_len);
4748e28a4053SRui Paulo return -1;
4749e28a4053SRui Paulo }
4750e28a4053SRui Paulo
4751e28a4053SRui Paulo pos = data;
4752e28a4053SRui Paulo frame = (struct ft_rrb_frame *) pos;
4753e28a4053SRui Paulo pos += sizeof(*frame);
4754e28a4053SRui Paulo
4755e28a4053SRui Paulo alen = le_to_host16(frame->action_length);
4756e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d "
4757e28a4053SRui Paulo "action_length=%d ap_address=" MACSTR,
4758e28a4053SRui Paulo frame->frame_type, frame->packet_type, alen,
4759e28a4053SRui Paulo MAC2STR(frame->ap_address));
4760e28a4053SRui Paulo
4761e28a4053SRui Paulo if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) {
4762e28a4053SRui Paulo /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */
4763e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with "
4764e28a4053SRui Paulo "unrecognized type %d", frame->frame_type);
4765e28a4053SRui Paulo return -1;
4766e28a4053SRui Paulo }
4767e28a4053SRui Paulo
4768e28a4053SRui Paulo if (alen > data_len - sizeof(*frame)) {
4769e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action "
4770e28a4053SRui Paulo "frame");
4771e28a4053SRui Paulo return -1;
4772e28a4053SRui Paulo }
4773e28a4053SRui Paulo
4774e28a4053SRui Paulo wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
4775e28a4053SRui Paulo
4776e28a4053SRui Paulo if (alen < 1 + 1 + 2 * ETH_ALEN) {
4777e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough "
4778e28a4053SRui Paulo "room for Action Frame body); alen=%lu",
4779e28a4053SRui Paulo (unsigned long) alen);
4780e28a4053SRui Paulo return -1;
4781e28a4053SRui Paulo }
4782e28a4053SRui Paulo start = pos;
4783e28a4053SRui Paulo end = pos + alen;
4784e28a4053SRui Paulo
4785e28a4053SRui Paulo if (*pos != WLAN_ACTION_FT) {
4786e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category "
4787e28a4053SRui Paulo "%d", *pos);
4788e28a4053SRui Paulo return -1;
4789e28a4053SRui Paulo }
4790e28a4053SRui Paulo
4791e28a4053SRui Paulo pos++;
4792e28a4053SRui Paulo action = *pos++;
4793e28a4053SRui Paulo sta_addr = pos;
4794e28a4053SRui Paulo pos += ETH_ALEN;
4795e28a4053SRui Paulo target_ap_addr = pos;
4796e28a4053SRui Paulo pos += ETH_ALEN;
4797e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr="
4798e28a4053SRui Paulo MACSTR " target_ap_addr=" MACSTR,
4799e28a4053SRui Paulo action, MAC2STR(sta_addr), MAC2STR(target_ap_addr));
4800e28a4053SRui Paulo
4801e28a4053SRui Paulo if (frame->packet_type == FT_PACKET_REQUEST) {
4802e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request");
4803e28a4053SRui Paulo
4804e28a4053SRui Paulo if (action != 1) {
4805e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in "
4806e28a4053SRui Paulo "RRB Request", action);
4807e28a4053SRui Paulo return -1;
4808e28a4053SRui Paulo }
4809e28a4053SRui Paulo
4810*a90b9d01SCy Schubert if (!ether_addr_equal(target_ap_addr, wpa_auth->addr)) {
4811e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
4812e28a4053SRui Paulo "RRB Request does not match with own "
4813e28a4053SRui Paulo "address");
4814e28a4053SRui Paulo return -1;
4815e28a4053SRui Paulo }
4816e28a4053SRui Paulo
4817e28a4053SRui Paulo if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address,
4818e28a4053SRui Paulo sta_addr, pos, end - pos) < 0)
4819e28a4053SRui Paulo return -1;
4820e28a4053SRui Paulo } else if (frame->packet_type == FT_PACKET_RESPONSE) {
4821e28a4053SRui Paulo u16 status_code;
4822e28a4053SRui Paulo
4823e28a4053SRui Paulo if (end - pos < 2) {
4824e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Not enough room for status "
4825e28a4053SRui Paulo "code in RRB Response");
4826e28a4053SRui Paulo return -1;
4827e28a4053SRui Paulo }
4828e28a4053SRui Paulo status_code = WPA_GET_LE16(pos);
4829e28a4053SRui Paulo
4830e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response "
4831e28a4053SRui Paulo "(status_code=%d)", status_code);
4832e28a4053SRui Paulo
4833e28a4053SRui Paulo if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0)
4834e28a4053SRui Paulo return -1;
4835e28a4053SRui Paulo } else {
4836e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown "
4837e28a4053SRui Paulo "packet_type %d", frame->packet_type);
4838e28a4053SRui Paulo return -1;
4839e28a4053SRui Paulo }
4840e28a4053SRui Paulo
4841e28a4053SRui Paulo return 0;
4842e28a4053SRui Paulo }
4843e28a4053SRui Paulo
4844e28a4053SRui Paulo
wpa_ft_rrb_oui_rx(struct wpa_authenticator * wpa_auth,const u8 * src_addr,const u8 * dst_addr,u8 oui_suffix,const u8 * data,size_t data_len)484585732ac8SCy Schubert void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
484685732ac8SCy Schubert const u8 *dst_addr, u8 oui_suffix, const u8 *data,
484785732ac8SCy Schubert size_t data_len)
484885732ac8SCy Schubert {
484985732ac8SCy Schubert const u8 *auth, *enc;
485085732ac8SCy Schubert size_t alen, elen;
485185732ac8SCy Schubert int no_defer = 0;
485285732ac8SCy Schubert
4853c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "FT: RRB-OUI(" MACSTR
4854c1d255d3SCy Schubert ") received frame from remote AP "
4855c1d255d3SCy Schubert MACSTR " oui_suffix=%u dst=" MACSTR,
4856c1d255d3SCy Schubert MAC2STR(wpa_auth->addr), MAC2STR(src_addr), oui_suffix,
4857c1d255d3SCy Schubert MAC2STR(dst_addr));
48584bc52338SCy Schubert wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len);
485985732ac8SCy Schubert
486085732ac8SCy Schubert if (is_multicast_ether_addr(src_addr)) {
486185732ac8SCy Schubert wpa_printf(MSG_DEBUG,
486285732ac8SCy Schubert "FT: RRB-OUI received frame from multicast address "
486385732ac8SCy Schubert MACSTR, MAC2STR(src_addr));
486485732ac8SCy Schubert return;
486585732ac8SCy Schubert }
486685732ac8SCy Schubert
4867c1d255d3SCy Schubert if (is_multicast_ether_addr(dst_addr))
486885732ac8SCy Schubert no_defer = 1;
486985732ac8SCy Schubert
487085732ac8SCy Schubert if (data_len < sizeof(u16)) {
487185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
487285732ac8SCy Schubert return;
487385732ac8SCy Schubert }
487485732ac8SCy Schubert
487585732ac8SCy Schubert alen = WPA_GET_LE16(data);
487685732ac8SCy Schubert if (data_len < sizeof(u16) + alen) {
487785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
487885732ac8SCy Schubert return;
487985732ac8SCy Schubert }
488085732ac8SCy Schubert
488185732ac8SCy Schubert auth = data + sizeof(u16);
48824bc52338SCy Schubert wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen);
488385732ac8SCy Schubert enc = data + sizeof(u16) + alen;
488485732ac8SCy Schubert elen = data_len - sizeof(u16) - alen;
48854bc52338SCy Schubert wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen);
488685732ac8SCy Schubert
488785732ac8SCy Schubert switch (oui_suffix) {
488885732ac8SCy Schubert case FT_PACKET_R0KH_R1KH_PULL:
488985732ac8SCy Schubert wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
489085732ac8SCy Schubert no_defer);
489185732ac8SCy Schubert break;
489285732ac8SCy Schubert case FT_PACKET_R0KH_R1KH_RESP:
489385732ac8SCy Schubert wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
489485732ac8SCy Schubert no_defer);
489585732ac8SCy Schubert break;
489685732ac8SCy Schubert case FT_PACKET_R0KH_R1KH_PUSH:
489785732ac8SCy Schubert wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
489885732ac8SCy Schubert no_defer);
489985732ac8SCy Schubert break;
490085732ac8SCy Schubert case FT_PACKET_R0KH_R1KH_SEQ_REQ:
490185732ac8SCy Schubert wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
490285732ac8SCy Schubert no_defer);
490385732ac8SCy Schubert break;
490485732ac8SCy Schubert case FT_PACKET_R0KH_R1KH_SEQ_RESP:
490585732ac8SCy Schubert wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
490685732ac8SCy Schubert alen, no_defer);
490785732ac8SCy Schubert break;
490885732ac8SCy Schubert }
490985732ac8SCy Schubert }
491085732ac8SCy Schubert
491185732ac8SCy Schubert
wpa_ft_generate_pmk_r1(struct wpa_authenticator * wpa_auth,struct wpa_ft_pmk_r0_sa * pmk_r0,struct ft_remote_r1kh * r1kh,const u8 * s1kh_id)491285732ac8SCy Schubert static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
4913e28a4053SRui Paulo struct wpa_ft_pmk_r0_sa *pmk_r0,
4914e28a4053SRui Paulo struct ft_remote_r1kh *r1kh,
491585732ac8SCy Schubert const u8 *s1kh_id)
4916e28a4053SRui Paulo {
491785732ac8SCy Schubert u8 *packet;
491885732ac8SCy Schubert size_t packet_len;
491985732ac8SCy Schubert struct ft_rrb_seq f_seq;
492085732ac8SCy Schubert struct tlv_list push[] = {
492185732ac8SCy Schubert { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
492285732ac8SCy Schubert .data = s1kh_id },
492385732ac8SCy Schubert { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
492485732ac8SCy Schubert .data = pmk_r0->pmk_r0_name },
492585732ac8SCy Schubert { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
492685732ac8SCy Schubert };
492785732ac8SCy Schubert struct tlv_list push_auth[] = {
492885732ac8SCy Schubert { .type = FT_RRB_SEQ, .len = sizeof(f_seq),
492985732ac8SCy Schubert .data = (u8 *) &f_seq },
493085732ac8SCy Schubert { .type = FT_RRB_R0KH_ID,
493185732ac8SCy Schubert .len = wpa_auth->conf.r0_key_holder_len,
493285732ac8SCy Schubert .data = wpa_auth->conf.r0_key_holder },
493385732ac8SCy Schubert { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
493485732ac8SCy Schubert .data = r1kh->id },
493585732ac8SCy Schubert { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
493685732ac8SCy Schubert };
4937e28a4053SRui Paulo
493885732ac8SCy Schubert if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
493985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
494085732ac8SCy Schubert return -1;
494185732ac8SCy Schubert }
4942e28a4053SRui Paulo
4943c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 push from " MACSTR
4944c1d255d3SCy Schubert " to remote R0KH address " MACSTR,
4945c1d255d3SCy Schubert MAC2STR(wpa_auth->addr), MAC2STR(r1kh->addr));
4946c1d255d3SCy Schubert
494785732ac8SCy Schubert if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
494885732ac8SCy Schubert r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
494985732ac8SCy Schubert FT_PACKET_R0KH_R1KH_PUSH,
495085732ac8SCy Schubert &packet, &packet_len) < 0)
495185732ac8SCy Schubert return -1;
4952e28a4053SRui Paulo
495385732ac8SCy Schubert wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH,
495485732ac8SCy Schubert packet, packet_len);
495585732ac8SCy Schubert
495685732ac8SCy Schubert os_free(packet);
495785732ac8SCy Schubert return 0;
4958e28a4053SRui Paulo }
4959e28a4053SRui Paulo
4960e28a4053SRui Paulo
wpa_ft_push_pmk_r1(struct wpa_authenticator * wpa_auth,const u8 * addr)4961e28a4053SRui Paulo void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
4962e28a4053SRui Paulo {
496385732ac8SCy Schubert struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
496485732ac8SCy Schubert struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL;
4965e28a4053SRui Paulo struct ft_remote_r1kh *r1kh;
4966e28a4053SRui Paulo
4967e28a4053SRui Paulo if (!wpa_auth->conf.pmk_r1_push)
4968e28a4053SRui Paulo return;
496985732ac8SCy Schubert if (!wpa_auth->conf.r1kh_list)
497085732ac8SCy Schubert return;
4971e28a4053SRui Paulo
497285732ac8SCy Schubert dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
4973*a90b9d01SCy Schubert if (ether_addr_equal(r0->spa, addr)) {
497485732ac8SCy Schubert r0found = r0;
4975e28a4053SRui Paulo break;
497685732ac8SCy Schubert }
4977e28a4053SRui Paulo }
4978e28a4053SRui Paulo
497985732ac8SCy Schubert r0 = r0found;
4980e28a4053SRui Paulo if (r0 == NULL || r0->pmk_r1_pushed)
4981e28a4053SRui Paulo return;
4982e28a4053SRui Paulo r0->pmk_r1_pushed = 1;
4983e28a4053SRui Paulo
4984e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
4985e28a4053SRui Paulo "for STA " MACSTR, MAC2STR(addr));
4986e28a4053SRui Paulo
498785732ac8SCy Schubert for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
498885732ac8SCy Schubert if (is_zero_ether_addr(r1kh->addr) ||
498985732ac8SCy Schubert is_zero_ether_addr(r1kh->id))
499085732ac8SCy Schubert continue;
499185732ac8SCy Schubert if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
499285732ac8SCy Schubert continue;
499385732ac8SCy Schubert wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
4994e28a4053SRui Paulo }
4995e28a4053SRui Paulo }
4996e28a4053SRui Paulo
499785732ac8SCy Schubert #endif /* CONFIG_IEEE80211R_AP */
4998