Convert FT RRB into new TLV based format. Encryption is unchanged. Signed-off-by: Michael Braun <michael-dev@xxxxxxxxxxxxx> --- src/ap/wpa_auth.h | 113 ++++---- src/ap/wpa_auth_ft.c | 736 ++++++++++++++++++++++++++++++++++++--------------- src/ap/wpa_auth_i.h | 2 +- 3 files changed, 567 insertions(+), 284 deletions(-) diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 0e5ad1a..83fc305 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -38,76 +38,63 @@ struct ft_rrb_frame { #define FT_PACKET_REQUEST 0 #define FT_PACKET_RESPONSE 1 /* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ -#define FT_PACKET_R0KH_R1KH_PULL 200 -#define FT_PACKET_R0KH_R1KH_RESP 201 -#define FT_PACKET_R0KH_R1KH_PUSH 202 - -#define FT_R0KH_R1KH_PULL_NONCE_LEN 16 -#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ - FT_R0KH_ID_MAX_LEN + 1 + \ - WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \ - ETH_ALEN) -#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8) - -struct ft_r0kh_r1kh_pull_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ - le16 data_length; /* little endian length of data (44) */ - u8 ap_address[ETH_ALEN]; +/* 200, 201 und 202 are reserved by old protocol version */ +#define FT_PACKET_R0KH_R1KH_PULL 203 +#define FT_PACKET_R0KH_R1KH_RESP 204 +#define FT_PACKET_R0KH_R1KH_PUSH 205 + + +/* new packet format */ + +/* packet layout + * | struct ft_rrb_frame | multiple of of struct ft_rrbv1_tlv | + * | padding for encryption (blocksize) | 8 byte extra for keywrap | + * where + * packet_type = FT_PACKET_R0KH_R1KH* + * action_length = size of all struct ft_rrbv1_tlv (without final padding) + * (aka plaintext length) + * encryption: covers everything after struct ft_rrb_frame + */ - u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; - u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; - u8 r0kh_id_len; - u8 pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 r1kh_id[FT_R1KH_ID_LEN]; - u8 s1kh_id[ETH_ALEN]; - u8 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */ - u8 key_wrap_extra[8]; -} STRUCT_PACKED; +#define FT_RRB_NONCE_LEN 16 -#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ - FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \ - WPA_PMK_NAME_LEN + 2) -#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8) -struct ft_r0kh_r1kh_resp_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ - le16 data_length; /* little endian length of data (78) */ - u8 ap_address[ETH_ALEN]; +#define FT_RRB_LAST_EMPTY 0 /* placeholder */ - u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */ - u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ - u8 s1kh_id[ETH_ALEN]; /* copied from pull */ - u8 pmk_r1[PMK_LEN]; - u8 pmk_r1_name[WPA_PMK_NAME_LEN]; - le16 pairwise; /* zero if no entry found */ - u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */ - u8 key_wrap_extra[8]; -} STRUCT_PACKED; +#define FT_RRB_NONCE 1 /* size FT_RRB_NONCE_LEN */ +#define FT_RRB_TIMESTAMP 2 /* le32 unix seconds */ -#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \ - WPA_PMK_NAME_LEN + PMK_LEN + \ - WPA_PMK_NAME_LEN + 2) -#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8) -struct ft_r0kh_r1kh_push_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ - le16 data_length; /* little endian length of data (82) */ - u8 ap_address[ETH_ALEN]; +#define FT_RRB_R0KH_ID 3 /* FT_R0KH_ID_MAX_LEN */ +#define FT_RRB_R1KH_ID 4 /* FT_R1KH_ID_LEN */ +#define FT_RRB_S1KH_ID 5 /* ETH_ALEN */ + +#define FT_RRB_PMK_R0_NAME 6 /* WPA_PMK_NAME_LEN */ +#define FT_RRB_PMK_R0 7 /* PMK_LEN */ +#define FT_RRB_PMK_R1_NAME 8 /* WPA_PMK_NAME_LEN */ +#define FT_RRB_PMK_R1 9 /* PMK_LEN */ - /* Encrypted with AES key-wrap */ - u8 timestamp[4]; /* current time in seconds since unix epoch, little - * endian */ - u8 r1kh_id[FT_R1KH_ID_LEN]; - u8 s1kh_id[ETH_ALEN]; - u8 pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 pmk_r1[PMK_LEN]; - u8 pmk_r1_name[WPA_PMK_NAME_LEN]; - le16 pairwise; - u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */ - u8 key_wrap_extra[8]; +#define FT_RRB_PAIRWISE 10 /* le16 */ + +struct ft_rrbv1_tlv { + le16 type; + le16 len; + /* followed by data of length len */ } STRUCT_PACKED; +/* session TLVs: + * required: [PMK_R1, PMK_R1_NAME, PAIRWISE] + * + * pull frame TLVs: + * required: NONCE, R0KH_ID, PMK_R0_NAME, R1KH_ID, S1KH_ID + * + * response frame TLVs: + * required: NONCE, R1KH_ID, S1KH_ID, + * optional: all session-TLVs + * + * push frame TLVs: + * required: TIMESTAMP, R1KH_ID, S1KH_ID, PMK_R0_NAME, + * session-TLVs + */ + #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 0c92d40..5abe412 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -32,6 +32,232 @@ static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx); static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx); +/** + * decrypt message + * @data full packet + * @kek encryption key + * @plain pointer to store pointer for plaintext + * (only action_length many bytes) + * needs to be freed by caller if not null + * will only be returned on success + * @return 0 on success, -1 on error + */ + +static int wpa_ft_rrb_decrypt(const u8 *kek, size_t kek_len, + const u8 *data, const size_t data_len, + u8 **plain, size_t *plain_size) +{ + struct ft_rrb_frame *f; + size_t alen, blen, clen; + const u8 *crypt; + + *plain = NULL; + + if (data_len < sizeof(*f)) + return -1; + + f = (struct ft_rrb_frame *) data; + + alen = le_to_host16(f->action_length); /* plaintext length */ + blen = alen + ((8 - (alen % 8)) % 8); /* round up to full block size */ + clen = blen + 8; /* extra space for keywrap */ + + /* ciphertext (packet) long enough for given plaintext length? */ + if (data_len - sizeof(*f) < clen) + return -1; + crypt = data + sizeof(*f); + + *plain = os_zalloc(blen); + if (!*plain) + return -1; + + if (!aes_unwrap(kek, kek_len, blen / 8, crypt, *plain)) { + *plain_size = alen; + return 0; + } + + os_free(*plain); + *plain = NULL; + + return -1; +} + + +/* get first tlv record in packet matching type + * @data (decrypted) packet + * @return 0 on success else -1 + */ +static int wpa_ft_rrb_get_tlv(const u8 *plain, const size_t plain_len, + int type, size_t *tlv_len, const u8 **tlv_data) +{ + struct ft_rrbv1_tlv *f; + size_t left; + le16 type16; + size_t len; + + left = plain_len; + type16 = host_to_le16(type); + + while (left >= sizeof(*f)) { + f = (struct ft_rrbv1_tlv *) plain; + + left -= sizeof(*f); + plain += sizeof(*f); + len = le_to_host16(f->len); + + if (left < len) + return -1; + + if (f->type == type16) { + *tlv_len = len; + *tlv_data = plain; + return 0; + } + + left -= len; + plain += len; + } + + return -1; +} + + +/** + * encrypt message + * @frame ft_rrb_frame + * @kek encryption key + * @plain pointer to store pointer for plaintext + * (only action_length many bytes) + * needs to be freed by caller if not null + * will only be returned on success + * @return 0 on success, -1 on error + */ + +struct tlv_list { + int type; + int len; + const u8 *data; +}; + +static inline size_t wpa_ft_tlv_len(const struct tlv_list *tlvs) +{ + size_t tlv_len = 0; + int i; + + if (!tlvs) + return 0; + + for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) { + tlv_len += sizeof(struct ft_rrbv1_tlv); + tlv_len += tlvs[i].len; + } + + return tlv_len; +} + +static inline size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start, + u8 *endpos) +{ + int i; + size_t tlv_len; + struct ft_rrbv1_tlv *hdr; + u8 *pos = start; + + if (!tlvs) + return 0; + + tlv_len = 0; + pos = start + tlv_len; + for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) { + tlv_len += sizeof(*hdr); + if (start + tlv_len > endpos) + return tlv_len; + hdr = (struct ft_rrbv1_tlv *) pos; + hdr->type = host_to_le16(tlvs[i].type); + hdr->len = host_to_le16(tlvs[i].len); + pos = start + tlv_len; + + tlv_len += tlvs[i].len; + if (start + tlv_len > endpos) + return tlv_len; + os_memcpy(pos, tlvs[i].data, tlvs[i].len); + pos = start + tlv_len; + } + + return tlv_len; +} + +static int wpa_ft_rrb_build(const u8 *kek, size_t kek_len, + const struct ft_rrb_frame *frame, + const struct tlv_list *tlvs1, + const struct tlv_list *tlvs2, + u8 **packet, size_t *packet_len) +{ + u8 *plain, *pos, *crypt, *endpos; + struct ft_rrb_frame *f2; + size_t tlv_len, pad_len; + + *packet = NULL; + *packet_len = 0; + + tlv_len = 0; + tlv_len += wpa_ft_tlv_len(tlvs1); + tlv_len += wpa_ft_tlv_len(tlvs2); + pad_len = (8 - (tlv_len % 8)) % 8; + + plain = os_zalloc(tlv_len + pad_len); + if (plain == NULL) + return -1; + + pos = plain; + endpos = plain + tlv_len; + pos += wpa_ft_tlv_lin(tlvs1, pos, endpos); + pos += wpa_ft_tlv_lin(tlvs2, pos, endpos); + + /* sanity check */ + if (pos != endpos) { + wpa_printf(MSG_ERROR, "FT: length error building RRB"); + os_free(plain); + return -1; + } + + *packet_len = sizeof(*frame) + tlv_len + pad_len + 8; + *packet = os_zalloc(*packet_len); + if (*packet == NULL) { + *packet_len = 0; + os_free(plain); + return -1; + } + + f2 = (struct ft_rrb_frame *) *packet; + os_memcpy(*packet, frame, sizeof(*frame)); + f2->action_length = host_to_le16(tlv_len); + + crypt = *packet + sizeof(*frame); + + if (!aes_wrap(kek, kek_len, (tlv_len + 7) / 8, plain, crypt)) { + os_free(plain); + return 0; + } + + os_free(plain); + os_free(*packet); + *packet = NULL; + return -1; +} + + +#define RRB_GET(type, field, txt, checklength) do { \ + if (wpa_ft_rrb_get_tlv(plain, plain_len, type, \ + &f_##field##_len, &f_##field) < 0 || \ + (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \ + wpa_printf(MSG_DEBUG, "FT: Missing " #field " in PMK-R1 " \ + "%s from " MACSTR, txt, MAC2STR(src_addr)); \ + goto out; \ + } \ +} while (0) + + static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { @@ -241,7 +467,7 @@ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0_name, - u8 *pmk_r0, int *pairwise) + const struct wpa_ft_pmk_r0_sa **r0_out) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; @@ -251,15 +477,14 @@ static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && os_memcmp_const(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) == 0) { - os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); - if (pairwise) - *pairwise = r0->pairwise; + *r0_out = r0; return 0; } r0 = r0->next; } + *r0_out = NULL; return -1; } @@ -391,7 +616,8 @@ static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx) static void wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth, struct ft_remote_r1kh *r1kh_wildcard, - const u8 *src_addr, u8 *r1kh_id, int timeout) + const u8 *src_addr, const u8 *r1kh_id, + int timeout) { struct ft_remote_r1kh *r1kh; @@ -454,7 +680,9 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, const u8 *pmk_r0_name) { struct ft_remote_r0kh *r0kh, *r0kh_wildcard = NULL; - struct ft_r0kh_r1kh_pull_frame frame, f; + struct ft_rrb_frame req_hdr; + u8 *packet = NULL; + size_t packet_len; int tsecs, tusecs, first; struct wpabuf *ft_pending_req_ies; @@ -494,36 +722,36 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " "address " MACSTR, MAC2STR(r0kh->addr)); - os_memset(&frame, 0, sizeof(frame)); - frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; - frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); - os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN); - - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - if (first) { - if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) { - wpa_printf(MSG_DEBUG, "FT: Failed to get random data " - "for nonce"); - return -1; - } - os_memcpy(sm->ft_pending_pull_nonce, f.nonce, - FT_R0KH_R1KH_PULL_NONCE_LEN); - } else - os_memcpy(f.nonce, sm->ft_pending_pull_nonce, - FT_R0KH_R1KH_PULL_NONCE_LEN); - os_memcpy(f.r0kh_id, sm->r0kh_id, FT_R0KH_ID_MAX_LEN); - f.r0kh_id_len = sm->r0kh_id_len; - os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); - os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); - os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN); - os_memset(f.pad, 0, sizeof(f.pad)); - - if (aes_wrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, - f.nonce, frame.nonce) < 0) + os_memset(&req_hdr, 0, sizeof(req_hdr)); + req_hdr.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + req_hdr.packet_type = FT_PACKET_R0KH_R1KH_PULL; + os_memcpy(req_hdr.ap_address, sm->wpa_auth->addr, ETH_ALEN); + + if (first && + random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " + "nonce"); + return -1; + } + + struct tlv_list req_tlv[] = { + { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, + .data = sm->ft_pending_pull_nonce }, + { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len, + .data = sm->r0kh_id }, + { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, + .data = pmk_r0_name }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = sm->wpa_auth->conf.r1_key_holder }, + { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, + .data = sm->addr }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (wpa_ft_rrb_build(r0kh->key, sizeof(r0kh->key), &req_hdr, + req_tlv, NULL, &packet, &packet_len) < 0) { return -1; + } ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); wpabuf_free(sm->ft_pending_req_ies); @@ -535,7 +763,10 @@ static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000; eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL); - wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); + wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, packet, packet_len); + + os_free(packet); + packet = NULL; return 0; } @@ -1587,23 +1818,65 @@ static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, } +static int wpa_ft_rrb_build_r0(const u8 *kek, size_t kek_len, + const struct ft_rrb_frame *frame, + const struct tlv_list *tlvs, + const struct wpa_ft_pmk_r0_sa *pmk_r0, + const u8 *r1kh_id, const u8 *s1kh_id, + u8 **packet, size_t *packet_len) +{ + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 f_pairwise[sizeof(le16)]; + int ret; + + if (!pmk_r0) + return wpa_ft_rrb_build(kek, kek_len, frame, tlvs, NULL, + packet, packet_len); + + wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh_id, + s1kh_id, pmk_r1, pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise); + + struct tlv_list sess_tlv[] = { + { .type = FT_RRB_PMK_R1, .len = sizeof(pmk_r1), + .data = pmk_r1 }, + { .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name), + .data = pmk_r1_name }, + { .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise), + .data = f_pairwise }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + ret = wpa_ft_rrb_build(kek, kek_len, frame, tlvs, sess_tlv, + packet, packet_len); + + os_memset(pmk_r1, 0, sizeof(pmk_r1)); + + return ret; + +} + static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { - struct ft_r0kh_r1kh_pull_frame f; - const u8 *crypt; - u8 *plain; + u8 *plain, *packet = NULL; + size_t plain_len, packet_len; struct ft_remote_r1kh *r1kh, *r1kh_wildcard = NULL; - struct ft_r0kh_r1kh_resp_frame resp, r; - u8 pmk_r0[PMK_LEN]; - int pairwise; - - wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); + struct ft_rrb_frame resp_hdr; + int ret; + const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name; + size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len; + size_t f_pmk_r0_name_len; + struct tlv_list *sess_tlv = NULL; + const struct wpa_ft_pmk_r0_sa *r0; - if (data_len < sizeof(f)) - return -1; + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull request"); + /* get key and decrypt */ if (!wpa_auth->conf.r1kh_list) return -1; r1kh = *wpa_auth->conf.r1kh_list; @@ -1626,71 +1899,68 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, return -1; } - crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, - crypt, plain) < 0) { + if (wpa_ft_rrb_decrypt(r1kh->key, sizeof(r1kh->key), + data, data_len, &plain, &plain_len) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "request from " MACSTR, MAC2STR(src_addr)); return -1; } - if (f.r0kh_id_len != wpa_auth->conf.r0_key_holder_len || - os_memcmp_const(f.r0kh_id, wpa_auth->conf.r0_key_holder, - f.r0kh_id_len) != 0) - return -1; + RRB_GET(FT_RRB_R0KH_ID, r0kh_id, "pull request", -1); + if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len || + os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder, + f_r0kh_id_len) != 0) + goto out; + RRB_GET(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", - f.nonce, sizeof(f.nonce)); + f_nonce, f_nonce_len); + + RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, "pull request", + WPA_PMK_NAME_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", - f.pmk_r0_name, WPA_PMK_NAME_LEN); + f_pmk_r0_name, f_pmk_r0_name_len); + + RRB_GET(FT_RRB_R1KH_ID, r1kh_id, "pull request", FT_R1KH_ID_LEN); + RRB_GET(FT_RRB_S1KH_ID, s1kh_id, "pull request", ETH_ALEN); wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + MACSTR, MAC2STR(f_r1kh_id), MAC2STR(f_s1kh_id)); if (r1kh == r1kh_wildcard && wpa_auth->conf.rkh_pos_timeout) wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr, - f.r1kh_id, wpa_auth->conf.rkh_pos_timeout); - - os_memset(&resp, 0, sizeof(resp)); - resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; - resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); - os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); - - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); - os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); - os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); - if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0, - &pairwise) < 0) { - wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " - "PMK-R1 pull"); - r.pairwise = 0; - } else { - wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, - r.pmk_r1, r.pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, - WPA_PMK_NAME_LEN); - r.pairwise = host_to_le16(pairwise); - } - os_memset(r.pad, 0, sizeof(r.pad)); - - if (aes_wrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, - r.nonce, resp.nonce) < 0) { - os_memset(pmk_r0, 0, PMK_LEN); - return -1; - } + f_r1kh_id, wpa_auth->conf.rkh_pos_timeout); - os_memset(pmk_r0, 0, PMK_LEN); + os_memset(&resp_hdr, 0, sizeof(resp_hdr)); + resp_hdr.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + resp_hdr.packet_type = FT_PACKET_R0KH_R1KH_RESP; + os_memcpy(resp_hdr.ap_address, wpa_auth->addr, ETH_ALEN); - wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); + struct tlv_list resp_tlv[] = { + { .type = FT_RRB_NONCE, .len = f_nonce_len, + .data = f_nonce }, + { .type = FT_RRB_R1KH_ID, .len = f_r1kh_id_len, + .data = f_r1kh_id }, + { .type = FT_RRB_S1KH_ID, .len = f_s1kh_id_len, + .data = f_s1kh_id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) + wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found for " + "PMK-R1 pull request"); + + ret = wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), &resp_hdr, + resp_tlv, r0, f_r1kh_id, f_s1kh_id, + &packet, &packet_len); + + if (!ret) + wpa_ft_rrb_send(wpa_auth, src_addr, packet, packet_len); + +out: + + os_free(plain); plain = NULL; + os_free(packet); packet = NULL; + os_free(sess_tlv); sess_tlv = NULL; return 0; } @@ -1730,20 +2000,21 @@ static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx) struct ft_pull_resp_cb_ctx { - struct ft_r0kh_r1kh_resp_frame *frame; + const u8 *s1kh_id; + const u8 *nonce; + int nak; struct ft_remote_r0kh *r0kh_wildcard; - u8 src_addr[ETH_ALEN]; + const u8 *src_addr; }; static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) { struct ft_pull_resp_cb_ctx *info = ctx; - struct ft_r0kh_r1kh_resp_frame *frame = info->frame; - if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0) + if (os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) return 0; - if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce, - FT_R0KH_R1KH_PULL_NONCE_LEN) != 0) + if (os_memcmp(info->nonce, sm->ft_pending_pull_nonce, + FT_RRB_NONCE_LEN) != 0) return 0; if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) return 0; @@ -1751,7 +2022,7 @@ static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for " MACSTR " - process from timeout", MAC2STR(sm->addr)); - if (frame->pairwise == 0) + if (info->nak) sm->ft_pending_pull_left_retries = 0; eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); eloop_cancel_timeout(ft_pull_resp_cb_finish, sm, NULL); @@ -1768,24 +2039,78 @@ static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) } +/* @returns 0 on success + * -1 on error + * -2 if FR_RRB_PAIRWISE is missing + */ +static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *plain, size_t plain_len, + const char *msgtype) +{ + const u8 *f_r1kh_id, *f_s1kh_id; + const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1; + size_t f_r1kh_id_len, f_s1kh_id_len; + size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len; + int pairwise; + int ret = -1; + + RRB_GET(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); + if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder, + FT_R1KH_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s did not use a matching " + "R1KH-ID", msgtype); + goto out; + } + + + RRB_GET(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); + RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s", msgtype); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR " S1KH-ID=" MACSTR, + MAC2STR(f_r1kh_id), MAC2STR(f_s1kh_id)); + + ret = -2; + RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16)); + + ret = -1; + RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN); + RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, PMK_LEN); + + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", + f_pmk_r1_name, WPA_PMK_NAME_LEN); + + pairwise = WPA_GET_LE16(f_pairwise); + + if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, f_pmk_r1_name, + pairwise) < 0) + return -1; + + ret = 0; + +out: + return ret; + +} + static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { - struct ft_r0kh_r1kh_resp_frame f; - const u8 *crypt; u8 *plain; + size_t plain_len; struct ft_remote_r0kh *r0kh, *r0kh_wildcard = NULL; - int pairwise, res; + int ret, nak; struct ft_pull_resp_cb_ctx ctx; + const u8 *f_nonce, *f_s1kh_id; + size_t f_nonce_len, f_s1kh_id_len; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); - if (data_len < sizeof(f)) - return -1; - if (!wpa_auth->conf.r0kh_list) return -1; + r0kh = *wpa_auth->conf.r0kh_list; while (r0kh) { if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) @@ -1805,59 +2130,50 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, return -1; } - crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, - crypt, plain) < 0) { + if (wpa_ft_rrb_decrypt(r0kh->key, sizeof(r0kh->key), + data, data_len, &plain, &plain_len) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "response from " MACSTR, MAC2STR(src_addr)); return -1; } - if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, - FT_R1KH_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " - "matching R1KH-ID"); - return -1; - } + RRB_GET(FT_RRB_NONCE, nonce, "pull response", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", f_nonce, f_nonce_len); - pairwise = le_to_host16(f.pairwise); - if (f.pairwise == 0) { - res = 0; - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", - f.nonce, sizeof(f.nonce)); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR - " S1KH-ID=" MACSTR " NACK", - MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); - } else { - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", - f.nonce, sizeof(f.nonce)); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR - " S1KH-ID=" MACSTR " pairwise=0x%x", - MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", - f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", - f.pmk_r1_name, WPA_PMK_NAME_LEN); + RRB_GET(FT_RRB_S1KH_ID, s1kh_id, "pull response", ETH_ALEN); - res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, - f.pmk_r1_name, pairwise); - } + ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, plain, plain_len, + "pull response"); + + if (ret == -1) + goto out; + + if (ret == -2) { + ret = 0; + nak = 1; + } else + nak = 0; wpa_printf(MSG_DEBUG, "FT: Look for pending pull request"); - ctx.frame = &f; - os_memcpy(ctx.src_addr, src_addr, ETH_ALEN); + os_memset(&ctx, 0, sizeof(ctx)); + + ctx.s1kh_id = f_s1kh_id; + ctx.nonce = f_nonce; + ctx.nak = nak; + ctx.src_addr = src_addr; ctx.r0kh_wildcard = (r0kh == r0kh_wildcard) ? r0kh_wildcard : NULL; wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &ctx); - os_memset(f.pmk_r1, 0, PMK_LEN); - return res ? 0 : -1; +out: + if (plain) { + os_memset(plain, 0, plain_len); + os_free(plain); + plain = NULL; + } + + return ret; } @@ -1865,19 +2181,17 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { - struct ft_r0kh_r1kh_push_frame f; - const u8 *crypt; + int ret = -1; u8 *plain; + size_t plain_len; struct ft_remote_r0kh *r0kh, *r0kh_wildcard = NULL; struct os_time now; os_time_t tsend; - int pairwise; + const u8 *f_timestamp; + size_t f_timestamp_len; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); - if (data_len < sizeof(f)) - return -1; - if (!wpa_auth->conf.r0kh_list) return -1; r0kh = *wpa_auth->conf.r0kh_list; @@ -1899,53 +2213,36 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, return -1; } - crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - crypt, plain) < 0) { + if (wpa_ft_rrb_decrypt(r0kh->key, sizeof(r0kh->key), + data, data_len, &plain, &plain_len) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " MACSTR, MAC2STR(src_addr)); return -1; } + RRB_GET(FT_RRB_TIMESTAMP, timestamp, "push", sizeof(le32)); os_get_time(&now); - tsend = WPA_GET_LE32(f.timestamp); + tsend = WPA_GET_LE32(f_timestamp); if ((now.sec > tsend && now.sec - tsend > 60) || (now.sec < tsend && tsend - now.sec > 60)) { wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " "timestamp: sender time %d own time %d\n", (int) tsend, (int) now.sec); - return -1; + goto out; } - if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, - FT_R1KH_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " - "R1KH-ID (received " MACSTR " own " MACSTR ")", - MAC2STR(f.r1kh_id), - MAC2STR(wpa_auth->conf.r1_key_holder)); - return -1; - } + if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, plain, plain_len, + "push") < 0) + goto out; - pairwise = le_to_host16(f.pairwise); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR " pairwise=0x%x", - MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", - f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", - f.pmk_r1_name, WPA_PMK_NAME_LEN); + ret = 0; +out: - wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, - pairwise); - os_memset(f.pmk_r1, 0, PMK_LEN); + os_memset(plain, 0, plain_len); + os_free(plain); + plain = NULL; - return 0; + return ret; } @@ -2077,44 +2374,43 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, static void 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, int pairwise) + const u8 *s1kh_id) { - struct ft_r0kh_r1kh_push_frame frame, f; + struct ft_rrb_frame push_hdr; struct os_time now; - const u8 *plain; - u8 *crypt; - - os_memset(&frame, 0, sizeof(frame)); - frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; - frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); - os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); - - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); - os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); - os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, - s1kh_id, f.pmk_r1, f.pmk_r1_name); - wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, - WPA_PMK_NAME_LEN); + u8 *packet; + size_t packet_len; + u8 f_timestamp[sizeof(le32)]; + + os_memset(&push_hdr, 0, sizeof(push_hdr)); + push_hdr.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + push_hdr.packet_type = FT_PACKET_R0KH_R1KH_PUSH; + os_memcpy(push_hdr.ap_address, wpa_auth->addr, ETH_ALEN); + os_get_time(&now); - WPA_PUT_LE32(f.timestamp, now.sec); - f.pairwise = host_to_le16(pairwise); - os_memset(f.pad, 0, sizeof(f.pad)); - plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - if (aes_wrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - plain, crypt) < 0) + WPA_PUT_LE32(f_timestamp, now.sec); + + struct tlv_list push_tlv[] = { + { .type = FT_RRB_TIMESTAMP, .len = sizeof(f_timestamp), + .data = f_timestamp }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = r1kh->id }, + { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, + .data = s1kh_id }, + { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, + .data = pmk_r0->pmk_r0_name }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), &push_hdr, + push_tlv, pmk_r0, r1kh->id, s1kh_id, + &packet, &packet_len) < 0) return; - wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); + wpa_ft_rrb_send(wpa_auth, r1kh->addr, packet, packet_len); + + os_free(packet); + packet = NULL; } @@ -2146,7 +2442,7 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) if (is_zero_ether_addr(r1kh->addr) || is_zero_ether_addr(r1kh->id)) continue; - wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise); + wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); } } diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index c634c32..eb5d03e 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -128,7 +128,7 @@ struct wpa_state_machine { const u8 *ies, size_t ies_len); void *ft_pending_cb_ctx; struct wpabuf *ft_pending_req_ies; - u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; + u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN]; u8 ft_pending_auth_transaction; u8 ft_pending_current_ap[ETH_ALEN]; int ft_pending_pull_left_retries; -- 2.1.4 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap