]>
git.ipfire.org Git - thirdparty/hostap.git/blob - wpa_supplicant/mesh_rsn.c
2 * WPA Supplicant - Mesh RSN routines
3 * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved.
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
9 #include "utils/includes.h"
11 #include "utils/common.h"
12 #include "utils/eloop.h"
13 #include "crypto/sha256.h"
14 #include "crypto/random.h"
15 #include "crypto/aes.h"
16 #include "crypto/aes_siv.h"
17 #include "rsn_supp/wpa.h"
18 #include "ap/hostapd.h"
19 #include "ap/wpa_auth.h"
20 #include "ap/sta_info.h"
21 #include "wpa_supplicant_i.h"
23 #include "wpas_glue.h"
27 #define MESH_AUTH_TIMEOUT 10
28 #define MESH_AUTH_RETRY 3
30 void mesh_auth_timer(void *eloop_ctx
, void *user_data
)
32 struct wpa_supplicant
*wpa_s
= eloop_ctx
;
33 struct sta_info
*sta
= user_data
;
35 if (sta
->sae
->state
!= SAE_ACCEPTED
) {
36 wpa_printf(MSG_DEBUG
, "AUTH: Re-authenticate with " MACSTR
38 MAC2STR(sta
->addr
), sta
->sae_auth_retry
);
39 if (sta
->sae_auth_retry
< MESH_AUTH_RETRY
) {
40 mesh_rsn_auth_sae_sta(wpa_s
, sta
);
42 /* block the STA if exceeded the number of attempts */
43 sta
->plink_state
= PLINK_BLOCKED
;
44 sta
->sae
->state
= SAE_NOTHING
;
46 sta
->sae_auth_retry
++;
51 static void auth_logger(void *ctx
, const u8
*addr
, logger_level level
,
55 wpa_printf(MSG_DEBUG
, "AUTH: " MACSTR
" - %s",
58 wpa_printf(MSG_DEBUG
, "AUTH: %s", txt
);
62 static const u8
*auth_get_psk(void *ctx
, const u8
*addr
,
63 const u8
*p2p_dev_addr
, const u8
*prev_psk
)
65 struct mesh_rsn
*mesh_rsn
= ctx
;
66 struct hostapd_data
*hapd
= mesh_rsn
->wpa_s
->ifmsh
->bss
[0];
67 struct sta_info
*sta
= ap_get_sta(hapd
, addr
);
69 wpa_printf(MSG_DEBUG
, "AUTH: %s (addr=" MACSTR
" prev_psk=%p)",
70 __func__
, MAC2STR(addr
), prev_psk
);
72 if (sta
&& sta
->auth_alg
== WLAN_AUTH_SAE
) {
73 if (!sta
->sae
|| prev_psk
)
82 static int auth_set_key(void *ctx
, int vlan_id
, enum wpa_alg alg
,
83 const u8
*addr
, int idx
, u8
*key
, size_t key_len
)
85 struct mesh_rsn
*mesh_rsn
= ctx
;
88 os_memset(seq
, 0, sizeof(seq
));
91 wpa_printf(MSG_DEBUG
, "AUTH: %s(alg=%d addr=" MACSTR
93 __func__
, alg
, MAC2STR(addr
), idx
);
95 wpa_printf(MSG_DEBUG
, "AUTH: %s(alg=%d key_idx=%d)",
98 wpa_hexdump_key(MSG_DEBUG
, "AUTH: set_key - key", key
, key_len
);
100 return wpa_drv_set_key(mesh_rsn
->wpa_s
, alg
, addr
, idx
,
101 1, seq
, 6, key
, key_len
);
105 static int auth_start_ampe(void *ctx
, const u8
*addr
)
107 struct mesh_rsn
*mesh_rsn
= ctx
;
108 struct hostapd_data
*hapd
;
109 struct sta_info
*sta
;
111 if (mesh_rsn
->wpa_s
->current_ssid
->mode
!= WPAS_MODE_MESH
)
114 hapd
= mesh_rsn
->wpa_s
->ifmsh
->bss
[0];
115 sta
= ap_get_sta(hapd
, addr
);
117 eloop_cancel_timeout(mesh_auth_timer
, mesh_rsn
->wpa_s
, sta
);
119 mesh_mpm_auth_peer(mesh_rsn
->wpa_s
, addr
);
124 static int __mesh_rsn_auth_init(struct mesh_rsn
*rsn
, const u8
*addr
)
126 struct wpa_auth_config conf
;
127 struct wpa_auth_callbacks cb
;
130 wpa_printf(MSG_DEBUG
, "AUTH: Initializing group state machine");
132 os_memset(&conf
, 0, sizeof(conf
));
134 conf
.wpa_key_mgmt
= WPA_KEY_MGMT_SAE
;
135 conf
.wpa_pairwise
= WPA_CIPHER_CCMP
;
136 conf
.rsn_pairwise
= WPA_CIPHER_CCMP
;
137 conf
.wpa_group
= WPA_CIPHER_CCMP
;
138 conf
.eapol_version
= 0;
139 conf
.wpa_group_rekey
= -1;
141 os_memset(&cb
, 0, sizeof(cb
));
143 cb
.logger
= auth_logger
;
144 cb
.get_psk
= auth_get_psk
;
145 cb
.set_key
= auth_set_key
;
146 cb
.start_ampe
= auth_start_ampe
;
148 rsn
->auth
= wpa_init(addr
, &conf
, &cb
);
149 if (rsn
->auth
== NULL
) {
150 wpa_printf(MSG_DEBUG
, "AUTH: wpa_init() failed");
154 /* TODO: support rekeying */
155 if (random_get_bytes(rsn
->mgtk
, 16) < 0) {
156 wpa_deinit(rsn
->auth
);
161 wpa_drv_set_key(rsn
->wpa_s
, WPA_ALG_IGTK
, NULL
, 4, 1,
162 seq
, sizeof(seq
), rsn
->mgtk
, sizeof(rsn
->mgtk
));
164 /* group privacy / data frames */
165 wpa_drv_set_key(rsn
->wpa_s
, WPA_ALG_CCMP
, NULL
, 1, 1,
166 seq
, sizeof(seq
), rsn
->mgtk
, sizeof(rsn
->mgtk
));
172 static void mesh_rsn_deinit(struct mesh_rsn
*rsn
)
174 os_memset(rsn
->mgtk
, 0, sizeof(rsn
->mgtk
));
175 wpa_deinit(rsn
->auth
);
179 struct mesh_rsn
*mesh_rsn_auth_init(struct wpa_supplicant
*wpa_s
,
180 struct mesh_conf
*conf
)
182 struct mesh_rsn
*mesh_rsn
;
183 struct hostapd_data
*bss
= wpa_s
->ifmsh
->bss
[0];
187 mesh_rsn
= os_zalloc(sizeof(*mesh_rsn
));
188 if (mesh_rsn
== NULL
)
190 mesh_rsn
->wpa_s
= wpa_s
;
192 if (__mesh_rsn_auth_init(mesh_rsn
, wpa_s
->own_addr
) < 0) {
193 mesh_rsn_deinit(mesh_rsn
);
197 bss
->wpa_auth
= mesh_rsn
->auth
;
199 ie
= wpa_auth_get_wpa_ie(mesh_rsn
->auth
, &ie_len
);
200 conf
->ies
= (u8
*) ie
;
201 conf
->ie_len
= ie_len
;
203 wpa_supplicant_rsn_supp_set_config(wpa_s
, wpa_s
->current_ssid
);
209 static int index_within_array(const int *array
, int idx
)
213 for (i
= 0; i
< idx
; i
++) {
222 static int mesh_rsn_sae_group(struct wpa_supplicant
*wpa_s
,
223 struct sae_data
*sae
)
225 int *groups
= wpa_s
->ifmsh
->bss
[0]->conf
->sae_groups
;
227 /* Configuration may have changed, so validate current index */
228 if (!index_within_array(groups
, wpa_s
->mesh_rsn
->sae_group_index
))
232 int group
= groups
[wpa_s
->mesh_rsn
->sae_group_index
];
236 if (sae_set_group(sae
, group
) == 0) {
237 wpa_dbg(wpa_s
, MSG_DEBUG
, "SME: Selected SAE group %d",
241 wpa_s
->mesh_rsn
->sae_group_index
++;
249 mesh_rsn_build_sae_commit(struct wpa_supplicant
*wpa_s
,
250 struct wpa_ssid
*ssid
, struct sta_info
*sta
)
255 if (ssid
->passphrase
== NULL
) {
256 wpa_msg(wpa_s
, MSG_DEBUG
, "SAE: No password available");
260 if (mesh_rsn_sae_group(wpa_s
, sta
->sae
) < 0) {
261 wpa_msg(wpa_s
, MSG_DEBUG
, "SAE: Failed to select group");
265 if (sae_prepare_commit(wpa_s
->own_addr
, sta
->addr
,
266 (u8
*) ssid
->passphrase
,
267 os_strlen(ssid
->passphrase
), sta
->sae
) < 0) {
268 wpa_msg(wpa_s
, MSG_DEBUG
, "SAE: Could not pick PWE");
272 len
= wpa_s
->mesh_rsn
->sae_token
?
273 wpabuf_len(wpa_s
->mesh_rsn
->sae_token
) : 0;
274 buf
= wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN
+ len
);
278 sae_write_commit(sta
->sae
, buf
, wpa_s
->mesh_rsn
->sae_token
);
284 static void mesh_rsn_send_auth(struct wpa_supplicant
*wpa_s
,
285 const u8
*dst
, const u8
*src
,
286 u16 auth_transaction
, u16 resp
,
289 struct ieee80211_mgmt
*auth
;
291 size_t len
, ielen
= 0;
294 ielen
= wpabuf_len(data
);
295 len
= IEEE80211_HDRLEN
+ sizeof(auth
->u
.auth
) + ielen
;
296 buf
= os_zalloc(len
);
300 auth
= (struct ieee80211_mgmt
*) buf
;
301 auth
->frame_control
= IEEE80211_FC(WLAN_FC_TYPE_MGMT
,
303 os_memcpy(auth
->da
, dst
, ETH_ALEN
);
304 os_memcpy(auth
->sa
, src
, ETH_ALEN
);
305 os_memcpy(auth
->bssid
, src
, ETH_ALEN
);
307 auth
->u
.auth
.auth_alg
= host_to_le16(WLAN_AUTH_SAE
);
308 auth
->u
.auth
.auth_transaction
= host_to_le16(auth_transaction
);
309 auth
->u
.auth
.status_code
= host_to_le16(resp
);
312 os_memcpy(auth
->u
.auth
.variable
, wpabuf_head(data
), ielen
);
314 wpa_msg(wpa_s
, MSG_DEBUG
, "authentication frame: STA=" MACSTR
315 " auth_transaction=%d resp=%d (IE len=%lu)",
316 MAC2STR(dst
), auth_transaction
, resp
, (unsigned long) ielen
);
317 if (wpa_drv_send_mlme(wpa_s
, buf
, len
, 0) < 0)
318 perror("send_auth_reply: send");
324 /* initiate new SAE authentication with sta */
325 int mesh_rsn_auth_sae_sta(struct wpa_supplicant
*wpa_s
,
326 struct sta_info
*sta
)
328 struct wpa_ssid
*ssid
= wpa_s
->current_ssid
;
333 wpa_msg(wpa_s
, MSG_DEBUG
,
334 "AUTH: No current_ssid known to initiate new SAE");
339 sta
->sae
= os_zalloc(sizeof(*sta
->sae
));
340 if (sta
->sae
== NULL
)
344 buf
= mesh_rsn_build_sae_commit(wpa_s
, ssid
, sta
);
348 wpa_msg(wpa_s
, MSG_DEBUG
,
349 "AUTH: started authentication with SAE peer: " MACSTR
,
352 sta
->sae
->state
= SAE_COMMITTED
;
353 wpa_supplicant_set_state(wpa_s
, WPA_AUTHENTICATING
);
355 mesh_rsn_send_auth(wpa_s
, sta
->addr
, wpa_s
->own_addr
,
356 1, WLAN_STATUS_SUCCESS
, buf
);
358 rnd
= rand() % MESH_AUTH_TIMEOUT
;
359 eloop_register_timeout(MESH_AUTH_TIMEOUT
+ rnd
, 0, mesh_auth_timer
,
367 void mesh_rsn_get_pmkid(struct mesh_rsn
*rsn
, struct sta_info
*sta
, u8
*pmkid
)
369 /* don't expect wpa auth to cache the pmkid for now */
370 rsn_pmkid(sta
->sae
->pmk
, PMK_LEN
, rsn
->wpa_s
->own_addr
,
372 wpa_key_mgmt_sha256(wpa_auth_sta_key_mgmt(sta
->wpa_sm
)));
377 mesh_rsn_derive_aek(struct mesh_rsn
*rsn
, struct sta_info
*sta
)
379 u8
*myaddr
= rsn
->wpa_s
->own_addr
;
380 u8
*peer
= sta
->addr
;
381 u8
*addr1
= peer
, *addr2
= myaddr
;
382 u8 context
[AES_BLOCK_SIZE
];
385 RSN_SELECTOR_PUT(context
, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP
));
387 if (os_memcmp(myaddr
, peer
, ETH_ALEN
) < 0) {
391 os_memcpy(context
+ 4, addr1
, ETH_ALEN
);
392 os_memcpy(context
+ 10, addr2
, ETH_ALEN
);
394 sha256_prf(sta
->sae
->pmk
, sizeof(sta
->sae
->pmk
), "AEK Derivation",
395 context
, sizeof(context
), sta
->aek
, sizeof(sta
->aek
));
399 /* derive mesh temporal key from pmk */
400 int mesh_rsn_derive_mtk(struct wpa_supplicant
*wpa_s
, struct sta_info
*sta
)
404 u16 min_lid
, max_lid
;
405 size_t nonce_len
= sizeof(sta
->my_nonce
);
406 size_t lid_len
= sizeof(sta
->my_lid
);
407 u8
*myaddr
= wpa_s
->own_addr
;
408 u8
*peer
= sta
->addr
;
409 /* 2 nonces, 2 linkids, akm suite, 2 mac addrs */
410 u8 context
[64 + 4 + 4 + 12];
413 if (os_memcmp(sta
->my_nonce
, sta
->peer_nonce
, nonce_len
) < 0) {
415 max
= sta
->peer_nonce
;
417 min
= sta
->peer_nonce
;
420 os_memcpy(ptr
, min
, nonce_len
);
421 os_memcpy(ptr
+ nonce_len
, max
, nonce_len
);
422 ptr
+= 2 * nonce_len
;
424 if (sta
->my_lid
< sta
->peer_lid
) {
425 min_lid
= host_to_le16(sta
->my_lid
);
426 max_lid
= host_to_le16(sta
->peer_lid
);
428 min_lid
= host_to_le16(sta
->peer_lid
);
429 max_lid
= host_to_le16(sta
->my_lid
);
431 os_memcpy(ptr
, &min_lid
, lid_len
);
432 os_memcpy(ptr
+ lid_len
, &max_lid
, lid_len
);
436 RSN_SELECTOR_PUT(ptr
, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP
));
439 if (os_memcmp(myaddr
, peer
, ETH_ALEN
) < 0) {
446 os_memcpy(ptr
, min
, ETH_ALEN
);
447 os_memcpy(ptr
+ ETH_ALEN
, max
, ETH_ALEN
);
449 sha256_prf(sta
->sae
->pmk
, sizeof(sta
->sae
->pmk
),
450 "Temporal Key Derivation", context
, sizeof(context
),
451 sta
->mtk
, sizeof(sta
->mtk
));
456 void mesh_rsn_init_ampe_sta(struct wpa_supplicant
*wpa_s
, struct sta_info
*sta
)
458 if (random_get_bytes(sta
->my_nonce
, 32) < 0) {
459 wpa_printf(MSG_INFO
, "mesh: Failed to derive random nonce");
460 /* TODO: How to handle this more cleanly? */
462 os_memset(sta
->peer_nonce
, 0, 32);
463 mesh_rsn_derive_aek(wpa_s
->mesh_rsn
, sta
);
467 /* insert AMPE and encrypted MIC at @ie.
468 * @mesh_rsn: mesh RSN context
469 * @sta: STA we're sending to
470 * @cat: pointer to category code in frame header.
471 * @buf: wpabuf to add encrypted AMPE and MIC to.
473 int mesh_rsn_protect_frame(struct mesh_rsn
*rsn
, struct sta_info
*sta
,
474 const u8
*cat
, struct wpabuf
*buf
)
476 struct ieee80211_ampe_ie
*ampe
;
477 u8
const *ie
= wpabuf_head_u8(buf
) + wpabuf_len(buf
);
478 u8
*ampe_ie
= NULL
, *mic_ie
= NULL
, *mic_payload
;
479 const u8
*aad
[] = { rsn
->wpa_s
->own_addr
, sta
->addr
, cat
};
480 const size_t aad_len
[] = { ETH_ALEN
, ETH_ALEN
, ie
- cat
};
483 if (AES_BLOCK_SIZE
+ 2 + sizeof(*ampe
) + 2 > wpabuf_tailroom(buf
)) {
484 wpa_printf(MSG_ERROR
, "protect frame: buffer too small");
488 ampe_ie
= os_zalloc(2 + sizeof(*ampe
));
490 wpa_printf(MSG_ERROR
, "protect frame: out of memory");
494 mic_ie
= os_zalloc(2 + AES_BLOCK_SIZE
);
496 wpa_printf(MSG_ERROR
, "protect frame: out of memory");
502 ampe_ie
[0] = WLAN_EID_AMPE
;
503 ampe_ie
[1] = sizeof(*ampe
);
504 ampe
= (struct ieee80211_ampe_ie
*) (ampe_ie
+ 2);
506 RSN_SELECTOR_PUT(ampe
->selected_pairwise_suite
,
507 wpa_cipher_to_suite(WPA_PROTO_RSN
, WPA_CIPHER_CCMP
));
508 os_memcpy(ampe
->local_nonce
, sta
->my_nonce
, 32);
509 os_memcpy(ampe
->peer_nonce
, sta
->peer_nonce
, 32);
510 /* incomplete: see 13.5.4 */
511 /* TODO: static mgtk for now since we don't support rekeying! */
512 os_memcpy(ampe
->mgtk
, rsn
->mgtk
, 16);
513 /* TODO: Populate Key RSC */
514 /* expire in 13 decades or so */
515 os_memset(ampe
->key_expiration
, 0xff, 4);
518 mic_ie
[0] = WLAN_EID_MIC
;
519 mic_ie
[1] = AES_BLOCK_SIZE
;
520 wpabuf_put_data(buf
, mic_ie
, 2);
521 /* MIC field is output ciphertext */
523 /* encrypt after MIC */
524 mic_payload
= (u8
*) wpabuf_put(buf
, 2 + sizeof(*ampe
) +
527 if (aes_siv_encrypt(sta
->aek
, ampe_ie
, 2 + sizeof(*ampe
), 3,
528 aad
, aad_len
, mic_payload
)) {
529 wpa_printf(MSG_ERROR
, "protect frame: failed to encrypt");
542 int mesh_rsn_process_ampe(struct wpa_supplicant
*wpa_s
, struct sta_info
*sta
,
543 struct ieee802_11_elems
*elems
, const u8
*cat
,
544 const u8
*start
, size_t elems_len
)
547 struct ieee80211_ampe_ie
*ampe
;
548 u8 null_nonce
[32] = {};
551 u8
*ampe_buf
, *crypt
= NULL
;
553 const u8
*aad
[] = { sta
->addr
, wpa_s
->own_addr
, cat
};
554 const size_t aad_len
[] = { ETH_ALEN
, ETH_ALEN
,
555 (elems
->mic
- 2) - cat
};
557 if (!elems
->mic
|| elems
->mic_len
< AES_BLOCK_SIZE
) {
558 wpa_msg(wpa_s
, MSG_DEBUG
, "Mesh RSN: missing mic ie");
562 ampe_buf
= (u8
*) elems
->mic
+ elems
->mic_len
;
563 if ((int) elems_len
< ampe_buf
- start
)
566 crypt_len
= elems_len
- (elems
->mic
- start
);
568 wpa_msg(wpa_s
, MSG_DEBUG
, "Mesh RSN: missing ampe ie");
572 /* crypt is modified by siv_decrypt */
573 crypt
= os_zalloc(crypt_len
);
575 wpa_printf(MSG_ERROR
, "Mesh RSN: out of memory");
580 os_memcpy(crypt
, elems
->mic
, crypt_len
);
582 if (aes_siv_decrypt(sta
->aek
, crypt
, crypt_len
, 3,
583 aad
, aad_len
, ampe_buf
)) {
584 wpa_printf(MSG_ERROR
, "Mesh RSN: frame verification failed!");
589 ampe_eid
= *ampe_buf
++;
590 ampe_ie_len
= *ampe_buf
++;
592 if (ampe_eid
!= WLAN_EID_AMPE
||
593 ampe_ie_len
< sizeof(struct ieee80211_ampe_ie
)) {
594 wpa_msg(wpa_s
, MSG_DEBUG
, "Mesh RSN: invalid ampe ie");
599 ampe
= (struct ieee80211_ampe_ie
*) ampe_buf
;
600 if (os_memcmp(ampe
->peer_nonce
, null_nonce
, 32) != 0 &&
601 os_memcmp(ampe
->peer_nonce
, sta
->my_nonce
, 32) != 0) {
602 wpa_msg(wpa_s
, MSG_DEBUG
, "Mesh RSN: invalid peer nonce");
606 os_memcpy(sta
->peer_nonce
, ampe
->local_nonce
,
607 sizeof(ampe
->local_nonce
));
608 os_memcpy(sta
->mgtk
, ampe
->mgtk
, sizeof(ampe
->mgtk
));
610 /* todo parse mgtk expiration */