📄 ikev2.c
字号:
/* * IKEv2 initiator (RFC 4306) for EAP-IKEV2 * Copyright (c) 2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */#include "includes.h"#include "common.h"#include "dh_groups.h"#include "ikev2.h"static int ikev2_process_idr(struct ikev2_initiator_data *data, const u8 *idr, size_t idr_len);void ikev2_initiator_deinit(struct ikev2_initiator_data *data){ ikev2_free_keys(&data->keys); wpabuf_free(data->r_dh_public); wpabuf_free(data->i_dh_private); os_free(data->IDi); os_free(data->IDr); os_free(data->shared_secret); wpabuf_free(data->i_sign_msg); wpabuf_free(data->r_sign_msg); os_free(data->key_pad);}static int ikev2_derive_keys(struct ikev2_initiator_data *data){ u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN]; size_t buf_len, pad_len; struct wpabuf *shared; const struct ikev2_integ_alg *integ; const struct ikev2_prf_alg *prf; const struct ikev2_encr_alg *encr; int ret; const u8 *addr[2]; size_t len[2]; /* RFC 4306, Sect. 2.14 */ integ = ikev2_get_integ(data->proposal.integ); prf = ikev2_get_prf(data->proposal.prf); encr = ikev2_get_encr(data->proposal.encr); if (integ == NULL || prf == NULL || encr == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal"); return -1; } shared = dh_derive_shared(data->r_dh_public, data->i_dh_private, data->dh); if (shared == NULL) return -1; /* Construct Ni | Nr | SPIi | SPIr */ buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN; buf = os_malloc(buf_len); if (buf == NULL) { wpabuf_free(shared); return -1; } pos = buf; os_memcpy(pos, data->i_nonce, data->i_nonce_len); pos += data->i_nonce_len; os_memcpy(pos, data->r_nonce, data->r_nonce_len); pos += data->r_nonce_len; os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN); pos += IKEV2_SPI_LEN; os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN); /* SKEYSEED = prf(Ni | Nr, g^ir) */ /* Use zero-padding per RFC 4306, Sect. 2.14 */ pad_len = data->dh->prime_len - wpabuf_len(shared); pad = os_zalloc(pad_len ? pad_len : 1); if (pad == NULL) { wpabuf_free(shared); os_free(buf); return -1; } addr[0] = pad; len[0] = pad_len; addr[1] = wpabuf_head(shared); len[1] = wpabuf_len(shared); if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len, 2, addr, len, skeyseed) < 0) { wpabuf_free(shared); os_free(buf); os_free(pad); return -1; } os_free(pad); wpabuf_free(shared); /* DH parameters are not needed anymore, so free them */ wpabuf_free(data->r_dh_public); data->r_dh_public = NULL; wpabuf_free(data->i_dh_private); data->i_dh_private = NULL; wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED", skeyseed, prf->hash_len); ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len, &data->keys); os_free(buf); return ret;}static int ikev2_parse_transform(struct ikev2_initiator_data *data, struct ikev2_proposal_data *prop, const u8 *pos, const u8 *end){ int transform_len; const struct ikev2_transform *t; u16 transform_id; const u8 *tend; if (end - pos < (int) sizeof(*t)) { wpa_printf(MSG_INFO, "IKEV2: Too short transform"); return -1; } t = (const struct ikev2_transform *) pos; transform_len = WPA_GET_BE16(t->transform_length); if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", transform_len); return -1; } tend = pos + transform_len; transform_id = WPA_GET_BE16(t->transform_id); wpa_printf(MSG_DEBUG, "IKEV2: Transform:"); wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Transform Length: %d " "Transform Type: %d Transform ID: %d", t->type, transform_len, t->transform_type, transform_id); if (t->type != 0 && t->type != 3) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type"); return -1; } pos = (const u8 *) (t + 1); if (pos < tend) { wpa_hexdump(MSG_DEBUG, "IKEV2: Transform Attributes", pos, tend - pos); } switch (t->transform_type) { case IKEV2_TRANSFORM_ENCR: if (ikev2_get_encr(transform_id) && transform_id == data->proposal.encr) { if (transform_id == ENCR_AES_CBC) { if (tend - pos != 4) { wpa_printf(MSG_DEBUG, "IKEV2: No " "Transform Attr for AES"); break; } if (WPA_GET_BE16(pos) != 0x800e) { wpa_printf(MSG_DEBUG, "IKEV2: Not a " "Key Size attribute for " "AES"); break; } if (WPA_GET_BE16(pos + 2) != 128) { wpa_printf(MSG_DEBUG, "IKEV2: " "Unsupported AES key size " "%d bits", WPA_GET_BE16(pos + 2)); break; } } prop->encr = transform_id; } break; case IKEV2_TRANSFORM_PRF: if (ikev2_get_prf(transform_id) && transform_id == data->proposal.prf) prop->prf = transform_id; break; case IKEV2_TRANSFORM_INTEG: if (ikev2_get_integ(transform_id) && transform_id == data->proposal.integ) prop->integ = transform_id; break; case IKEV2_TRANSFORM_DH: if (dh_groups_get(transform_id) && transform_id == data->proposal.dh) prop->dh = transform_id; break; } return transform_len;}static int ikev2_parse_proposal(struct ikev2_initiator_data *data, struct ikev2_proposal_data *prop, const u8 *pos, const u8 *end){ const u8 *pend, *ppos; int proposal_len, i; const struct ikev2_proposal *p; if (end - pos < (int) sizeof(*p)) { wpa_printf(MSG_INFO, "IKEV2: Too short proposal"); return -1; } p = (const struct ikev2_proposal *) pos; proposal_len = WPA_GET_BE16(p->proposal_length); if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", proposal_len); return -1; } wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d", p->proposal_num); wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Proposal Length: %d " " Protocol ID: %d", p->type, proposal_len, p->protocol_id); wpa_printf(MSG_DEBUG, "IKEV2: SPI Size: %d Transforms: %d", p->spi_size, p->num_transforms); if (p->type != 0 && p->type != 2) { wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type"); return -1; } if (p->protocol_id != IKEV2_PROTOCOL_IKE) { wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID " "(only IKE allowed for EAP-IKEv2)"); return -1; } if (p->proposal_num != prop->proposal_num) { if (p->proposal_num == prop->proposal_num + 1) prop->proposal_num = p->proposal_num; else { wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #"); return -1; } } ppos = (const u8 *) (p + 1); pend = pos + proposal_len; if (ppos + p->spi_size > pend) { wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " "in proposal"); return -1; } if (p->spi_size) { wpa_hexdump(MSG_DEBUG, "IKEV2: SPI", ppos, p->spi_size); ppos += p->spi_size; } /* * For initial IKE_SA negotiation, SPI Size MUST be zero; for * subsequent negotiations, it must be 8 for IKE. We only support * initial case for now. */ if (p->spi_size != 0) { wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size"); return -1; } if (p->num_transforms == 0) { wpa_printf(MSG_INFO, "IKEV2: At least one transform required"); return -1; } for (i = 0; i < (int) p->num_transforms; i++) { int tlen = ikev2_parse_transform(data, prop, ppos, pend); if (tlen < 0) return -1; ppos += tlen; } if (ppos != pend) { wpa_printf(MSG_INFO, "IKEV2: Unexpected data after " "transforms"); return -1; } return proposal_len;}static int ikev2_process_sar1(struct ikev2_initiator_data *data, const u8 *sar1, size_t sar1_len){ struct ikev2_proposal_data prop; const u8 *pos, *end; int found = 0; /* Security Association Payloads: <Proposals> */ if (sar1 == NULL) { wpa_printf(MSG_INFO, "IKEV2: SAr1 not received"); return -1; } os_memset(&prop, 0, sizeof(prop)); prop.proposal_num = 1; pos = sar1; end = sar1 + sar1_len; while (pos < end) { int plen; prop.integ = -1; prop.prf = -1; prop.encr = -1; prop.dh = -1; plen = ikev2_parse_proposal(data, &prop, pos, end); if (plen < 0) return -1; if (!found && prop.integ != -1 && prop.prf != -1 && prop.encr != -1 && prop.dh != -1) { found = 1; } pos += plen; /* Only one proposal expected in SAr */ break; } if (pos != end) { wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposal"); return -1; } if (!found) { wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found"); return -1; } wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d " "INTEG:%d D-H:%d", data->proposal.proposal_num, data->proposal.encr, data->proposal.prf, data->proposal.integ, data->proposal.dh); return 0;}static int ikev2_process_ker(struct ikev2_initiator_data *data, const u8 *ker, size_t ker_len){ u16 group; /* * Key Exchange Payload: * DH Group # (16 bits) * RESERVED (16 bits) * Key Exchange Data (Diffie-Hellman public value) */ if (ker == NULL) { wpa_printf(MSG_INFO, "IKEV2: KEr not received"); return -1; } if (ker_len < 4 + 96) { wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload"); return -1; } group = WPA_GET_BE16(ker); wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u", group); if (group != data->proposal.dh) { wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u does not match " "with the selected proposal (%u)", group, data->proposal.dh); return -1; } if (data->dh == NULL) { wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group"); return -1; } /* RFC 4306, Section 3.4: * The length of DH public value MUST be equal to the lenght of the * prime modulus. */ if (ker_len - 4 != data->dh->prime_len) { wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length " "%ld (expected %ld)", (long) (ker_len - 4), (long) data->dh->prime_len); return -1; } wpabuf_free(data->r_dh_public); data->r_dh_public = wpabuf_alloc_copy(ker + 4, ker_len - 4); if (data->r_dh_public == NULL) return -1; wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEr Diffie-Hellman Public Value", data->r_dh_public); return 0;}static int ikev2_process_nr(struct ikev2_initiator_data *data, const u8 *nr, size_t nr_len){ if (nr == NULL) { wpa_printf(MSG_INFO, "IKEV2: Nr not received"); return -1; } if (nr_len < IKEV2_NONCE_MIN_LEN || nr_len > IKEV2_NONCE_MAX_LEN) { wpa_printf(MSG_INFO, "IKEV2: Invalid Nr length %ld", (long) nr_len); return -1; } data->r_nonce_len = nr_len; os_memcpy(data->r_nonce, nr, nr_len); wpa_hexdump(MSG_MSGDUMP, "IKEV2: Nr", data->r_nonce, data->r_nonce_len); return 0;}static int ikev2_process_sa_init_encr(struct ikev2_initiator_data *data, const struct ikev2_hdr *hdr, const u8 *encrypted, size_t encrypted_len, u8 next_payload){ u8 *decrypted; size_t decrypted_len; struct ikev2_payloads pl; int ret = 0; decrypted = ikev2_decrypt_payload(data->proposal.encr, data->proposal.integ, &data->keys, 0, hdr, encrypted, encrypted_len, &decrypted_len); if (decrypted == NULL) return -1; wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); if (ikev2_parse_payloads(&pl, next_payload, decrypted, decrypted + decrypted_len) < 0) { wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " "payloads"); return -1; } if (pl.idr) ret = ikev2_process_idr(data, pl.idr, pl.idr_len); os_free(decrypted); return ret;}static int ikev2_process_sa_init(struct ikev2_initiator_data *data, const struct ikev2_hdr *hdr, struct ikev2_payloads *pl){ if (ikev2_process_sar1(data, pl->sa, pl->sa_len) < 0 || ikev2_process_ker(data, pl->ke, pl->ke_len) < 0 || ikev2_process_nr(data, pl->nonce, pl->nonce_len) < 0) return -1; os_memcpy(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN); if (ikev2_derive_keys(data) < 0) return -1; if (pl->encrypted) { wpa_printf(MSG_DEBUG, "IKEV2: Encrypted payload in SA_INIT - " "try to get IDr from it"); if (ikev2_process_sa_init_encr(data, hdr, pl->encrypted, pl->encrypted_len, pl->encr_next_payload) < 0) { wpa_printf(MSG_INFO, "IKEV2: Failed to process " "encrypted payload"); return -1; } } data->state = SA_AUTH; return 0;}static int ikev2_process_idr(struct ikev2_initiator_data *data, const u8 *idr, size_t idr_len){ u8 id_type; if (idr == NULL) { wpa_printf(MSG_INFO, "IKEV2: No IDr received"); return -1; } if (idr_len < 4) { wpa_printf(MSG_INFO, "IKEV2: Too short IDr payload"); return -1; } id_type = idr[0]; idr += 4; idr_len -= 4; wpa_printf(MSG_DEBUG, "IKEV2: IDr ID Type %d", id_type); wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDr", idr, idr_len); if (data->IDr) { if (id_type != data->IDr_type || idr_len != data->IDr_len || os_memcmp(idr, data->IDr, idr_len) != 0) { wpa_printf(MSG_INFO, "IKEV2: IDr differs from the one " "received earlier"); wpa_printf(MSG_DEBUG, "IKEV2: Previous IDr ID Type %d", id_type); wpa_hexdump_ascii(MSG_DEBUG, "Previous IKEV2: IDr", data->IDr, data->IDr_len); return -1; } os_free(data->IDr); } data->IDr = os_malloc(idr_len); if (data->IDr == NULL) return -1; os_memcpy(data->IDr, idr, idr_len); data->IDr_len = idr_len; data->IDr_type = id_type; return 0;}static int ikev2_process_cert(struct ikev2_initiator_data *data, const u8 *cert, size_t cert_len){ u8 cert_encoding; if (cert == NULL) { if (data->peer_auth == PEER_AUTH_CERT) { wpa_printf(MSG_INFO, "IKEV2: No Certificate received"); return -1; } return 0; } if (cert_len < 1) { wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field"); return -1; } cert_encoding = cert[0]; cert++; cert_len--; wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding); wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len); /* TODO: validate certificate */ return 0;}static int ikev2_process_auth_cert(struct ikev2_initiator_data *data, u8 method, const u8 *auth, size_t auth_len){ if (method != AUTH_RSA_SIGN) { wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " "method %d", method); return -1; } /* TODO: validate AUTH */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -