ikev2.c revision 281806
1189251Ssam/* 2189251Ssam * IKEv2 initiator (RFC 4306) for EAP-IKEV2 3189251Ssam * Copyright (c) 2007, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12214734Srpaulo#include "crypto/dh_groups.h" 13252726Srpaulo#include "crypto/random.h" 14189251Ssam#include "ikev2.h" 15189251Ssam 16189251Ssam 17189251Ssamstatic int ikev2_process_idr(struct ikev2_initiator_data *data, 18189251Ssam const u8 *idr, size_t idr_len); 19189251Ssam 20189251Ssam 21189251Ssamvoid ikev2_initiator_deinit(struct ikev2_initiator_data *data) 22189251Ssam{ 23189251Ssam ikev2_free_keys(&data->keys); 24189251Ssam wpabuf_free(data->r_dh_public); 25189251Ssam wpabuf_free(data->i_dh_private); 26189251Ssam os_free(data->IDi); 27189251Ssam os_free(data->IDr); 28189251Ssam os_free(data->shared_secret); 29189251Ssam wpabuf_free(data->i_sign_msg); 30189251Ssam wpabuf_free(data->r_sign_msg); 31189251Ssam os_free(data->key_pad); 32189251Ssam} 33189251Ssam 34189251Ssam 35189251Ssamstatic int ikev2_derive_keys(struct ikev2_initiator_data *data) 36189251Ssam{ 37189251Ssam u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN]; 38189251Ssam size_t buf_len, pad_len; 39189251Ssam struct wpabuf *shared; 40189251Ssam const struct ikev2_integ_alg *integ; 41189251Ssam const struct ikev2_prf_alg *prf; 42189251Ssam const struct ikev2_encr_alg *encr; 43189251Ssam int ret; 44189251Ssam const u8 *addr[2]; 45189251Ssam size_t len[2]; 46189251Ssam 47189251Ssam /* RFC 4306, Sect. 2.14 */ 48189251Ssam 49189251Ssam integ = ikev2_get_integ(data->proposal.integ); 50189251Ssam prf = ikev2_get_prf(data->proposal.prf); 51189251Ssam encr = ikev2_get_encr(data->proposal.encr); 52189251Ssam if (integ == NULL || prf == NULL || encr == NULL) { 53189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal"); 54189251Ssam return -1; 55189251Ssam } 56189251Ssam 57189251Ssam shared = dh_derive_shared(data->r_dh_public, data->i_dh_private, 58189251Ssam data->dh); 59189251Ssam if (shared == NULL) 60189251Ssam return -1; 61189251Ssam 62189251Ssam /* Construct Ni | Nr | SPIi | SPIr */ 63189251Ssam 64189251Ssam buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN; 65189251Ssam buf = os_malloc(buf_len); 66189251Ssam if (buf == NULL) { 67189251Ssam wpabuf_free(shared); 68189251Ssam return -1; 69189251Ssam } 70189251Ssam 71189251Ssam pos = buf; 72189251Ssam os_memcpy(pos, data->i_nonce, data->i_nonce_len); 73189251Ssam pos += data->i_nonce_len; 74189251Ssam os_memcpy(pos, data->r_nonce, data->r_nonce_len); 75189251Ssam pos += data->r_nonce_len; 76189251Ssam os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN); 77189251Ssam pos += IKEV2_SPI_LEN; 78189251Ssam os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN); 79189251Ssam 80189251Ssam /* SKEYSEED = prf(Ni | Nr, g^ir) */ 81189251Ssam 82189251Ssam /* Use zero-padding per RFC 4306, Sect. 2.14 */ 83189251Ssam pad_len = data->dh->prime_len - wpabuf_len(shared); 84189251Ssam pad = os_zalloc(pad_len ? pad_len : 1); 85189251Ssam if (pad == NULL) { 86189251Ssam wpabuf_free(shared); 87189251Ssam os_free(buf); 88189251Ssam return -1; 89189251Ssam } 90189251Ssam addr[0] = pad; 91189251Ssam len[0] = pad_len; 92189251Ssam addr[1] = wpabuf_head(shared); 93189251Ssam len[1] = wpabuf_len(shared); 94189251Ssam if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len, 95189251Ssam 2, addr, len, skeyseed) < 0) { 96189251Ssam wpabuf_free(shared); 97189251Ssam os_free(buf); 98189251Ssam os_free(pad); 99189251Ssam return -1; 100189251Ssam } 101189251Ssam os_free(pad); 102189251Ssam wpabuf_free(shared); 103189251Ssam 104189251Ssam /* DH parameters are not needed anymore, so free them */ 105189251Ssam wpabuf_free(data->r_dh_public); 106189251Ssam data->r_dh_public = NULL; 107189251Ssam wpabuf_free(data->i_dh_private); 108189251Ssam data->i_dh_private = NULL; 109189251Ssam 110189251Ssam wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED", 111189251Ssam skeyseed, prf->hash_len); 112189251Ssam 113189251Ssam ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len, 114189251Ssam &data->keys); 115189251Ssam os_free(buf); 116189251Ssam return ret; 117189251Ssam} 118189251Ssam 119189251Ssam 120189251Ssamstatic int ikev2_parse_transform(struct ikev2_initiator_data *data, 121189251Ssam struct ikev2_proposal_data *prop, 122189251Ssam const u8 *pos, const u8 *end) 123189251Ssam{ 124189251Ssam int transform_len; 125189251Ssam const struct ikev2_transform *t; 126189251Ssam u16 transform_id; 127189251Ssam const u8 *tend; 128189251Ssam 129189251Ssam if (end - pos < (int) sizeof(*t)) { 130189251Ssam wpa_printf(MSG_INFO, "IKEV2: Too short transform"); 131189251Ssam return -1; 132189251Ssam } 133189251Ssam 134189251Ssam t = (const struct ikev2_transform *) pos; 135189251Ssam transform_len = WPA_GET_BE16(t->transform_length); 136189251Ssam if (transform_len < (int) sizeof(*t) || pos + transform_len > end) { 137189251Ssam wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d", 138189251Ssam transform_len); 139189251Ssam return -1; 140189251Ssam } 141189251Ssam tend = pos + transform_len; 142189251Ssam 143189251Ssam transform_id = WPA_GET_BE16(t->transform_id); 144189251Ssam 145189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Transform:"); 146189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Transform Length: %d " 147189251Ssam "Transform Type: %d Transform ID: %d", 148189251Ssam t->type, transform_len, t->transform_type, transform_id); 149189251Ssam 150189251Ssam if (t->type != 0 && t->type != 3) { 151189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type"); 152189251Ssam return -1; 153189251Ssam } 154189251Ssam 155189251Ssam pos = (const u8 *) (t + 1); 156189251Ssam if (pos < tend) { 157189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: Transform Attributes", 158189251Ssam pos, tend - pos); 159189251Ssam } 160189251Ssam 161189251Ssam switch (t->transform_type) { 162189251Ssam case IKEV2_TRANSFORM_ENCR: 163189251Ssam if (ikev2_get_encr(transform_id) && 164189251Ssam transform_id == data->proposal.encr) { 165189251Ssam if (transform_id == ENCR_AES_CBC) { 166189251Ssam if (tend - pos != 4) { 167189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: No " 168189251Ssam "Transform Attr for AES"); 169189251Ssam break; 170189251Ssam } 171189251Ssam if (WPA_GET_BE16(pos) != 0x800e) { 172189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Not a " 173189251Ssam "Key Size attribute for " 174189251Ssam "AES"); 175189251Ssam break; 176189251Ssam } 177189251Ssam if (WPA_GET_BE16(pos + 2) != 128) { 178189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: " 179189251Ssam "Unsupported AES key size " 180189251Ssam "%d bits", 181189251Ssam WPA_GET_BE16(pos + 2)); 182189251Ssam break; 183189251Ssam } 184189251Ssam } 185189251Ssam prop->encr = transform_id; 186189251Ssam } 187189251Ssam break; 188189251Ssam case IKEV2_TRANSFORM_PRF: 189189251Ssam if (ikev2_get_prf(transform_id) && 190189251Ssam transform_id == data->proposal.prf) 191189251Ssam prop->prf = transform_id; 192189251Ssam break; 193189251Ssam case IKEV2_TRANSFORM_INTEG: 194189251Ssam if (ikev2_get_integ(transform_id) && 195189251Ssam transform_id == data->proposal.integ) 196189251Ssam prop->integ = transform_id; 197189251Ssam break; 198189251Ssam case IKEV2_TRANSFORM_DH: 199189251Ssam if (dh_groups_get(transform_id) && 200189251Ssam transform_id == data->proposal.dh) 201189251Ssam prop->dh = transform_id; 202189251Ssam break; 203189251Ssam } 204189251Ssam 205189251Ssam return transform_len; 206189251Ssam} 207189251Ssam 208189251Ssam 209189251Ssamstatic int ikev2_parse_proposal(struct ikev2_initiator_data *data, 210189251Ssam struct ikev2_proposal_data *prop, 211189251Ssam const u8 *pos, const u8 *end) 212189251Ssam{ 213189251Ssam const u8 *pend, *ppos; 214189251Ssam int proposal_len, i; 215189251Ssam const struct ikev2_proposal *p; 216189251Ssam 217189251Ssam if (end - pos < (int) sizeof(*p)) { 218189251Ssam wpa_printf(MSG_INFO, "IKEV2: Too short proposal"); 219189251Ssam return -1; 220189251Ssam } 221189251Ssam 222189251Ssam p = (const struct ikev2_proposal *) pos; 223189251Ssam proposal_len = WPA_GET_BE16(p->proposal_length); 224189251Ssam if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { 225189251Ssam wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", 226189251Ssam proposal_len); 227189251Ssam return -1; 228189251Ssam } 229189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d", 230189251Ssam p->proposal_num); 231189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Type: %d Proposal Length: %d " 232189251Ssam " Protocol ID: %d", 233189251Ssam p->type, proposal_len, p->protocol_id); 234189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: SPI Size: %d Transforms: %d", 235189251Ssam p->spi_size, p->num_transforms); 236189251Ssam 237189251Ssam if (p->type != 0 && p->type != 2) { 238189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type"); 239189251Ssam return -1; 240189251Ssam } 241189251Ssam 242189251Ssam if (p->protocol_id != IKEV2_PROTOCOL_IKE) { 243189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID " 244189251Ssam "(only IKE allowed for EAP-IKEv2)"); 245189251Ssam return -1; 246189251Ssam } 247189251Ssam 248189251Ssam if (p->proposal_num != prop->proposal_num) { 249189251Ssam if (p->proposal_num == prop->proposal_num + 1) 250189251Ssam prop->proposal_num = p->proposal_num; 251189251Ssam else { 252189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #"); 253189251Ssam return -1; 254189251Ssam } 255189251Ssam } 256189251Ssam 257189251Ssam ppos = (const u8 *) (p + 1); 258189251Ssam pend = pos + proposal_len; 259189251Ssam if (ppos + p->spi_size > pend) { 260189251Ssam wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI " 261189251Ssam "in proposal"); 262189251Ssam return -1; 263189251Ssam } 264189251Ssam if (p->spi_size) { 265189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: SPI", 266189251Ssam ppos, p->spi_size); 267189251Ssam ppos += p->spi_size; 268189251Ssam } 269189251Ssam 270189251Ssam /* 271189251Ssam * For initial IKE_SA negotiation, SPI Size MUST be zero; for 272189251Ssam * subsequent negotiations, it must be 8 for IKE. We only support 273189251Ssam * initial case for now. 274189251Ssam */ 275189251Ssam if (p->spi_size != 0) { 276189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size"); 277189251Ssam return -1; 278189251Ssam } 279189251Ssam 280189251Ssam if (p->num_transforms == 0) { 281189251Ssam wpa_printf(MSG_INFO, "IKEV2: At least one transform required"); 282189251Ssam return -1; 283189251Ssam } 284189251Ssam 285189251Ssam for (i = 0; i < (int) p->num_transforms; i++) { 286189251Ssam int tlen = ikev2_parse_transform(data, prop, ppos, pend); 287189251Ssam if (tlen < 0) 288189251Ssam return -1; 289189251Ssam ppos += tlen; 290189251Ssam } 291189251Ssam 292189251Ssam if (ppos != pend) { 293189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected data after " 294189251Ssam "transforms"); 295189251Ssam return -1; 296189251Ssam } 297189251Ssam 298189251Ssam return proposal_len; 299189251Ssam} 300189251Ssam 301189251Ssam 302189251Ssamstatic int ikev2_process_sar1(struct ikev2_initiator_data *data, 303189251Ssam const u8 *sar1, size_t sar1_len) 304189251Ssam{ 305189251Ssam struct ikev2_proposal_data prop; 306189251Ssam const u8 *pos, *end; 307189251Ssam int found = 0; 308189251Ssam 309189251Ssam /* Security Association Payloads: <Proposals> */ 310189251Ssam 311189251Ssam if (sar1 == NULL) { 312189251Ssam wpa_printf(MSG_INFO, "IKEV2: SAr1 not received"); 313189251Ssam return -1; 314189251Ssam } 315189251Ssam 316189251Ssam os_memset(&prop, 0, sizeof(prop)); 317189251Ssam prop.proposal_num = 1; 318189251Ssam 319189251Ssam pos = sar1; 320189251Ssam end = sar1 + sar1_len; 321189251Ssam 322189251Ssam while (pos < end) { 323189251Ssam int plen; 324189251Ssam 325189251Ssam prop.integ = -1; 326189251Ssam prop.prf = -1; 327189251Ssam prop.encr = -1; 328189251Ssam prop.dh = -1; 329189251Ssam plen = ikev2_parse_proposal(data, &prop, pos, end); 330189251Ssam if (plen < 0) 331189251Ssam return -1; 332189251Ssam 333189251Ssam if (!found && prop.integ != -1 && prop.prf != -1 && 334189251Ssam prop.encr != -1 && prop.dh != -1) { 335189251Ssam found = 1; 336189251Ssam } 337189251Ssam 338189251Ssam pos += plen; 339189251Ssam 340189251Ssam /* Only one proposal expected in SAr */ 341189251Ssam break; 342189251Ssam } 343189251Ssam 344189251Ssam if (pos != end) { 345189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposal"); 346189251Ssam return -1; 347189251Ssam } 348189251Ssam 349189251Ssam if (!found) { 350189251Ssam wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found"); 351189251Ssam return -1; 352189251Ssam } 353189251Ssam 354189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d " 355189251Ssam "INTEG:%d D-H:%d", data->proposal.proposal_num, 356189251Ssam data->proposal.encr, data->proposal.prf, 357189251Ssam data->proposal.integ, data->proposal.dh); 358189251Ssam 359189251Ssam return 0; 360189251Ssam} 361189251Ssam 362189251Ssam 363189251Ssamstatic int ikev2_process_ker(struct ikev2_initiator_data *data, 364189251Ssam const u8 *ker, size_t ker_len) 365189251Ssam{ 366189251Ssam u16 group; 367189251Ssam 368189251Ssam /* 369189251Ssam * Key Exchange Payload: 370189251Ssam * DH Group # (16 bits) 371189251Ssam * RESERVED (16 bits) 372189251Ssam * Key Exchange Data (Diffie-Hellman public value) 373189251Ssam */ 374189251Ssam 375189251Ssam if (ker == NULL) { 376189251Ssam wpa_printf(MSG_INFO, "IKEV2: KEr not received"); 377189251Ssam return -1; 378189251Ssam } 379189251Ssam 380189251Ssam if (ker_len < 4 + 96) { 381189251Ssam wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload"); 382189251Ssam return -1; 383189251Ssam } 384189251Ssam 385189251Ssam group = WPA_GET_BE16(ker); 386189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u", group); 387189251Ssam 388189251Ssam if (group != data->proposal.dh) { 389189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u does not match " 390189251Ssam "with the selected proposal (%u)", 391189251Ssam group, data->proposal.dh); 392189251Ssam return -1; 393189251Ssam } 394189251Ssam 395189251Ssam if (data->dh == NULL) { 396189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group"); 397189251Ssam return -1; 398189251Ssam } 399189251Ssam 400189251Ssam /* RFC 4306, Section 3.4: 401252726Srpaulo * The length of DH public value MUST be equal to the length of the 402189251Ssam * prime modulus. 403189251Ssam */ 404189251Ssam if (ker_len - 4 != data->dh->prime_len) { 405189251Ssam wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length " 406189251Ssam "%ld (expected %ld)", 407189251Ssam (long) (ker_len - 4), (long) data->dh->prime_len); 408189251Ssam return -1; 409189251Ssam } 410189251Ssam 411189251Ssam wpabuf_free(data->r_dh_public); 412189251Ssam data->r_dh_public = wpabuf_alloc_copy(ker + 4, ker_len - 4); 413189251Ssam if (data->r_dh_public == NULL) 414189251Ssam return -1; 415189251Ssam 416189251Ssam wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEr Diffie-Hellman Public Value", 417189251Ssam data->r_dh_public); 418189251Ssam 419189251Ssam return 0; 420189251Ssam} 421189251Ssam 422189251Ssam 423189251Ssamstatic int ikev2_process_nr(struct ikev2_initiator_data *data, 424189251Ssam const u8 *nr, size_t nr_len) 425189251Ssam{ 426189251Ssam if (nr == NULL) { 427189251Ssam wpa_printf(MSG_INFO, "IKEV2: Nr not received"); 428189251Ssam return -1; 429189251Ssam } 430189251Ssam 431189251Ssam if (nr_len < IKEV2_NONCE_MIN_LEN || nr_len > IKEV2_NONCE_MAX_LEN) { 432189251Ssam wpa_printf(MSG_INFO, "IKEV2: Invalid Nr length %ld", 433189251Ssam (long) nr_len); 434189251Ssam return -1; 435189251Ssam } 436189251Ssam 437189251Ssam data->r_nonce_len = nr_len; 438189251Ssam os_memcpy(data->r_nonce, nr, nr_len); 439189251Ssam wpa_hexdump(MSG_MSGDUMP, "IKEV2: Nr", 440189251Ssam data->r_nonce, data->r_nonce_len); 441189251Ssam 442189251Ssam return 0; 443189251Ssam} 444189251Ssam 445189251Ssam 446189251Ssamstatic int ikev2_process_sa_init_encr(struct ikev2_initiator_data *data, 447189251Ssam const struct ikev2_hdr *hdr, 448189251Ssam const u8 *encrypted, 449189251Ssam size_t encrypted_len, u8 next_payload) 450189251Ssam{ 451189251Ssam u8 *decrypted; 452189251Ssam size_t decrypted_len; 453189251Ssam struct ikev2_payloads pl; 454189251Ssam int ret = 0; 455189251Ssam 456189251Ssam decrypted = ikev2_decrypt_payload(data->proposal.encr, 457189251Ssam data->proposal.integ, &data->keys, 0, 458189251Ssam hdr, encrypted, encrypted_len, 459189251Ssam &decrypted_len); 460189251Ssam if (decrypted == NULL) 461189251Ssam return -1; 462189251Ssam 463189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); 464189251Ssam 465189251Ssam if (ikev2_parse_payloads(&pl, next_payload, decrypted, 466189251Ssam decrypted + decrypted_len) < 0) { 467189251Ssam wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " 468189251Ssam "payloads"); 469189251Ssam return -1; 470189251Ssam } 471189251Ssam 472189251Ssam if (pl.idr) 473189251Ssam ret = ikev2_process_idr(data, pl.idr, pl.idr_len); 474189251Ssam 475189251Ssam os_free(decrypted); 476189251Ssam 477189251Ssam return ret; 478189251Ssam} 479189251Ssam 480189251Ssam 481189251Ssamstatic int ikev2_process_sa_init(struct ikev2_initiator_data *data, 482189251Ssam const struct ikev2_hdr *hdr, 483189251Ssam struct ikev2_payloads *pl) 484189251Ssam{ 485189251Ssam if (ikev2_process_sar1(data, pl->sa, pl->sa_len) < 0 || 486189251Ssam ikev2_process_ker(data, pl->ke, pl->ke_len) < 0 || 487189251Ssam ikev2_process_nr(data, pl->nonce, pl->nonce_len) < 0) 488189251Ssam return -1; 489189251Ssam 490189251Ssam os_memcpy(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN); 491189251Ssam 492189251Ssam if (ikev2_derive_keys(data) < 0) 493189251Ssam return -1; 494189251Ssam 495189251Ssam if (pl->encrypted) { 496189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Encrypted payload in SA_INIT - " 497189251Ssam "try to get IDr from it"); 498189251Ssam if (ikev2_process_sa_init_encr(data, hdr, pl->encrypted, 499189251Ssam pl->encrypted_len, 500189251Ssam pl->encr_next_payload) < 0) { 501189251Ssam wpa_printf(MSG_INFO, "IKEV2: Failed to process " 502189251Ssam "encrypted payload"); 503189251Ssam return -1; 504189251Ssam } 505189251Ssam } 506189251Ssam 507189251Ssam data->state = SA_AUTH; 508189251Ssam 509189251Ssam return 0; 510189251Ssam} 511189251Ssam 512189251Ssam 513189251Ssamstatic int ikev2_process_idr(struct ikev2_initiator_data *data, 514189251Ssam const u8 *idr, size_t idr_len) 515189251Ssam{ 516189251Ssam u8 id_type; 517189251Ssam 518189251Ssam if (idr == NULL) { 519189251Ssam wpa_printf(MSG_INFO, "IKEV2: No IDr received"); 520189251Ssam return -1; 521189251Ssam } 522189251Ssam 523189251Ssam if (idr_len < 4) { 524189251Ssam wpa_printf(MSG_INFO, "IKEV2: Too short IDr payload"); 525189251Ssam return -1; 526189251Ssam } 527189251Ssam 528189251Ssam id_type = idr[0]; 529189251Ssam idr += 4; 530189251Ssam idr_len -= 4; 531189251Ssam 532189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: IDr ID Type %d", id_type); 533189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDr", idr, idr_len); 534189251Ssam if (data->IDr) { 535189251Ssam if (id_type != data->IDr_type || idr_len != data->IDr_len || 536189251Ssam os_memcmp(idr, data->IDr, idr_len) != 0) { 537189251Ssam wpa_printf(MSG_INFO, "IKEV2: IDr differs from the one " 538189251Ssam "received earlier"); 539189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Previous IDr ID Type %d", 540189251Ssam id_type); 541189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "Previous IKEV2: IDr", 542189251Ssam data->IDr, data->IDr_len); 543189251Ssam return -1; 544189251Ssam } 545189251Ssam os_free(data->IDr); 546189251Ssam } 547189251Ssam data->IDr = os_malloc(idr_len); 548189251Ssam if (data->IDr == NULL) 549189251Ssam return -1; 550189251Ssam os_memcpy(data->IDr, idr, idr_len); 551189251Ssam data->IDr_len = idr_len; 552189251Ssam data->IDr_type = id_type; 553189251Ssam 554189251Ssam return 0; 555189251Ssam} 556189251Ssam 557189251Ssam 558189251Ssamstatic int ikev2_process_cert(struct ikev2_initiator_data *data, 559189251Ssam const u8 *cert, size_t cert_len) 560189251Ssam{ 561189251Ssam u8 cert_encoding; 562189251Ssam 563189251Ssam if (cert == NULL) { 564189251Ssam if (data->peer_auth == PEER_AUTH_CERT) { 565189251Ssam wpa_printf(MSG_INFO, "IKEV2: No Certificate received"); 566189251Ssam return -1; 567189251Ssam } 568189251Ssam return 0; 569189251Ssam } 570189251Ssam 571189251Ssam if (cert_len < 1) { 572189251Ssam wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field"); 573189251Ssam return -1; 574189251Ssam } 575189251Ssam 576189251Ssam cert_encoding = cert[0]; 577189251Ssam cert++; 578189251Ssam cert_len--; 579189251Ssam 580189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding); 581189251Ssam wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len); 582189251Ssam 583189251Ssam /* TODO: validate certificate */ 584189251Ssam 585189251Ssam return 0; 586189251Ssam} 587189251Ssam 588189251Ssam 589189251Ssamstatic int ikev2_process_auth_cert(struct ikev2_initiator_data *data, 590189251Ssam u8 method, const u8 *auth, size_t auth_len) 591189251Ssam{ 592189251Ssam if (method != AUTH_RSA_SIGN) { 593189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " 594189251Ssam "method %d", method); 595189251Ssam return -1; 596189251Ssam } 597189251Ssam 598189251Ssam /* TODO: validate AUTH */ 599189251Ssam return 0; 600189251Ssam} 601189251Ssam 602189251Ssam 603189251Ssamstatic int ikev2_process_auth_secret(struct ikev2_initiator_data *data, 604189251Ssam u8 method, const u8 *auth, 605189251Ssam size_t auth_len) 606189251Ssam{ 607189251Ssam u8 auth_data[IKEV2_MAX_HASH_LEN]; 608189251Ssam const struct ikev2_prf_alg *prf; 609189251Ssam 610189251Ssam if (method != AUTH_SHARED_KEY_MIC) { 611189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " 612189251Ssam "method %d", method); 613189251Ssam return -1; 614189251Ssam } 615189251Ssam 616189251Ssam /* msg | Ni | prf(SK_pr,IDr') */ 617189251Ssam if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg, 618189251Ssam data->IDr, data->IDr_len, data->IDr_type, 619189251Ssam &data->keys, 0, data->shared_secret, 620189251Ssam data->shared_secret_len, 621189251Ssam data->i_nonce, data->i_nonce_len, 622189251Ssam data->key_pad, data->key_pad_len, 623189251Ssam auth_data) < 0) { 624189251Ssam wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); 625189251Ssam return -1; 626189251Ssam } 627189251Ssam 628189251Ssam wpabuf_free(data->r_sign_msg); 629189251Ssam data->r_sign_msg = NULL; 630189251Ssam 631189251Ssam prf = ikev2_get_prf(data->proposal.prf); 632189251Ssam if (prf == NULL) 633189251Ssam return -1; 634189251Ssam 635189251Ssam if (auth_len != prf->hash_len || 636281806Srpaulo os_memcmp_const(auth, auth_data, auth_len) != 0) { 637189251Ssam wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data"); 638189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data", 639189251Ssam auth, auth_len); 640189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data", 641189251Ssam auth_data, prf->hash_len); 642189251Ssam return -1; 643189251Ssam } 644189251Ssam 645189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Peer authenticated successfully " 646189251Ssam "using shared keys"); 647189251Ssam 648189251Ssam return 0; 649189251Ssam} 650189251Ssam 651189251Ssam 652189251Ssamstatic int ikev2_process_auth(struct ikev2_initiator_data *data, 653189251Ssam const u8 *auth, size_t auth_len) 654189251Ssam{ 655189251Ssam u8 auth_method; 656189251Ssam 657189251Ssam if (auth == NULL) { 658189251Ssam wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload"); 659189251Ssam return -1; 660189251Ssam } 661189251Ssam 662189251Ssam if (auth_len < 4) { 663189251Ssam wpa_printf(MSG_INFO, "IKEV2: Too short Authentication " 664189251Ssam "Payload"); 665189251Ssam return -1; 666189251Ssam } 667189251Ssam 668189251Ssam auth_method = auth[0]; 669189251Ssam auth += 4; 670189251Ssam auth_len -= 4; 671189251Ssam 672189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method); 673189251Ssam wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len); 674189251Ssam 675189251Ssam switch (data->peer_auth) { 676189251Ssam case PEER_AUTH_CERT: 677189251Ssam return ikev2_process_auth_cert(data, auth_method, auth, 678189251Ssam auth_len); 679189251Ssam case PEER_AUTH_SECRET: 680189251Ssam return ikev2_process_auth_secret(data, auth_method, auth, 681189251Ssam auth_len); 682189251Ssam } 683189251Ssam 684189251Ssam return -1; 685189251Ssam} 686189251Ssam 687189251Ssam 688189251Ssamstatic int ikev2_process_sa_auth_decrypted(struct ikev2_initiator_data *data, 689189251Ssam u8 next_payload, 690189251Ssam u8 *payload, size_t payload_len) 691189251Ssam{ 692189251Ssam struct ikev2_payloads pl; 693189251Ssam 694189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); 695189251Ssam 696189251Ssam if (ikev2_parse_payloads(&pl, next_payload, payload, payload + 697189251Ssam payload_len) < 0) { 698189251Ssam wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " 699189251Ssam "payloads"); 700189251Ssam return -1; 701189251Ssam } 702189251Ssam 703189251Ssam if (ikev2_process_idr(data, pl.idr, pl.idr_len) < 0 || 704189251Ssam ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 || 705189251Ssam ikev2_process_auth(data, pl.auth, pl.auth_len) < 0) 706189251Ssam return -1; 707189251Ssam 708189251Ssam return 0; 709189251Ssam} 710189251Ssam 711189251Ssam 712189251Ssamstatic int ikev2_process_sa_auth(struct ikev2_initiator_data *data, 713189251Ssam const struct ikev2_hdr *hdr, 714189251Ssam struct ikev2_payloads *pl) 715189251Ssam{ 716189251Ssam u8 *decrypted; 717189251Ssam size_t decrypted_len; 718189251Ssam int ret; 719189251Ssam 720189251Ssam decrypted = ikev2_decrypt_payload(data->proposal.encr, 721189251Ssam data->proposal.integ, 722189251Ssam &data->keys, 0, hdr, pl->encrypted, 723189251Ssam pl->encrypted_len, &decrypted_len); 724189251Ssam if (decrypted == NULL) 725189251Ssam return -1; 726189251Ssam 727189251Ssam ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload, 728189251Ssam decrypted, decrypted_len); 729189251Ssam os_free(decrypted); 730189251Ssam 731189251Ssam if (ret == 0 && !data->unknown_user) { 732189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Authentication completed"); 733189251Ssam data->state = IKEV2_DONE; 734189251Ssam } 735189251Ssam 736189251Ssam return ret; 737189251Ssam} 738189251Ssam 739189251Ssam 740189251Ssamstatic int ikev2_validate_rx_state(struct ikev2_initiator_data *data, 741189251Ssam u8 exchange_type, u32 message_id) 742189251Ssam{ 743189251Ssam switch (data->state) { 744189251Ssam case SA_INIT: 745189251Ssam /* Expect to receive IKE_SA_INIT: HDR, SAr, KEr, Nr, [CERTREQ], 746189251Ssam * [SK{IDr}] */ 747189251Ssam if (exchange_type != IKE_SA_INIT) { 748189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " 749189251Ssam "%u in SA_INIT state", exchange_type); 750189251Ssam return -1; 751189251Ssam } 752189251Ssam if (message_id != 0) { 753189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " 754189251Ssam "in SA_INIT state", message_id); 755189251Ssam return -1; 756189251Ssam } 757189251Ssam break; 758189251Ssam case SA_AUTH: 759189251Ssam /* Expect to receive IKE_SA_AUTH: 760189251Ssam * HDR, SK {IDr, [CERT,] [CERTREQ,] [NFID,] AUTH} 761189251Ssam */ 762189251Ssam if (exchange_type != IKE_SA_AUTH) { 763189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " 764189251Ssam "%u in SA_AUTH state", exchange_type); 765189251Ssam return -1; 766189251Ssam } 767189251Ssam if (message_id != 1) { 768189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " 769189251Ssam "in SA_AUTH state", message_id); 770189251Ssam return -1; 771189251Ssam } 772189251Ssam break; 773189251Ssam case CHILD_SA: 774189251Ssam if (exchange_type != CREATE_CHILD_SA) { 775189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " 776189251Ssam "%u in CHILD_SA state", exchange_type); 777189251Ssam return -1; 778189251Ssam } 779189251Ssam if (message_id != 2) { 780189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " 781189251Ssam "in CHILD_SA state", message_id); 782189251Ssam return -1; 783189251Ssam } 784189251Ssam break; 785189251Ssam case IKEV2_DONE: 786189251Ssam return -1; 787189251Ssam } 788189251Ssam 789189251Ssam return 0; 790189251Ssam} 791189251Ssam 792189251Ssam 793189251Ssamint ikev2_initiator_process(struct ikev2_initiator_data *data, 794189251Ssam const struct wpabuf *buf) 795189251Ssam{ 796189251Ssam const struct ikev2_hdr *hdr; 797189251Ssam u32 length, message_id; 798189251Ssam const u8 *pos, *end; 799189251Ssam struct ikev2_payloads pl; 800189251Ssam 801189251Ssam wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)", 802189251Ssam (unsigned long) wpabuf_len(buf)); 803189251Ssam 804189251Ssam if (wpabuf_len(buf) < sizeof(*hdr)) { 805189251Ssam wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR"); 806189251Ssam return -1; 807189251Ssam } 808189251Ssam 809189251Ssam hdr = (const struct ikev2_hdr *) wpabuf_head(buf); 810189251Ssam end = wpabuf_head_u8(buf) + wpabuf_len(buf); 811189251Ssam message_id = WPA_GET_BE32(hdr->message_id); 812189251Ssam length = WPA_GET_BE32(hdr->length); 813189251Ssam 814189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", 815189251Ssam hdr->i_spi, IKEV2_SPI_LEN); 816189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", 817189251Ssam hdr->r_spi, IKEV2_SPI_LEN); 818189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x " 819189251Ssam "Exchange Type: %u", 820189251Ssam hdr->next_payload, hdr->version, hdr->exchange_type); 821189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u", 822189251Ssam message_id, length); 823189251Ssam 824189251Ssam if (hdr->version != IKEV2_VERSION) { 825189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x " 826189251Ssam "(expected 0x%x)", hdr->version, IKEV2_VERSION); 827189251Ssam return -1; 828189251Ssam } 829189251Ssam 830189251Ssam if (length != wpabuf_len(buf)) { 831189251Ssam wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != " 832189251Ssam "RX: %lu)", (unsigned long) length, 833189251Ssam (unsigned long) wpabuf_len(buf)); 834189251Ssam return -1; 835189251Ssam } 836189251Ssam 837189251Ssam if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0) 838189251Ssam return -1; 839189251Ssam 840189251Ssam if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) != 841189251Ssam IKEV2_HDR_RESPONSE) { 842189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x", 843189251Ssam hdr->flags); 844189251Ssam return -1; 845189251Ssam } 846189251Ssam 847189251Ssam if (data->state != SA_INIT) { 848189251Ssam if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) { 849189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " 850189251Ssam "Initiator's SPI"); 851189251Ssam return -1; 852189251Ssam } 853189251Ssam if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) { 854189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " 855189251Ssam "Responder's SPI"); 856189251Ssam return -1; 857189251Ssam } 858189251Ssam } 859189251Ssam 860189251Ssam pos = (const u8 *) (hdr + 1); 861189251Ssam if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0) 862189251Ssam return -1; 863189251Ssam 864189251Ssam switch (data->state) { 865189251Ssam case SA_INIT: 866189251Ssam if (ikev2_process_sa_init(data, hdr, &pl) < 0) 867189251Ssam return -1; 868189251Ssam wpabuf_free(data->r_sign_msg); 869189251Ssam data->r_sign_msg = wpabuf_dup(buf); 870189251Ssam break; 871189251Ssam case SA_AUTH: 872189251Ssam if (ikev2_process_sa_auth(data, hdr, &pl) < 0) 873189251Ssam return -1; 874189251Ssam break; 875189251Ssam case CHILD_SA: 876189251Ssam case IKEV2_DONE: 877189251Ssam break; 878189251Ssam } 879189251Ssam 880189251Ssam return 0; 881189251Ssam} 882189251Ssam 883189251Ssam 884189251Ssamstatic void ikev2_build_hdr(struct ikev2_initiator_data *data, 885189251Ssam struct wpabuf *msg, u8 exchange_type, 886189251Ssam u8 next_payload, u32 message_id) 887189251Ssam{ 888189251Ssam struct ikev2_hdr *hdr; 889189251Ssam 890189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR"); 891189251Ssam 892189251Ssam /* HDR - RFC 4306, Sect. 3.1 */ 893189251Ssam hdr = wpabuf_put(msg, sizeof(*hdr)); 894189251Ssam os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN); 895189251Ssam os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN); 896189251Ssam hdr->next_payload = next_payload; 897189251Ssam hdr->version = IKEV2_VERSION; 898189251Ssam hdr->exchange_type = exchange_type; 899189251Ssam hdr->flags = IKEV2_HDR_INITIATOR; 900189251Ssam WPA_PUT_BE32(hdr->message_id, message_id); 901189251Ssam} 902189251Ssam 903189251Ssam 904189251Ssamstatic int ikev2_build_sai(struct ikev2_initiator_data *data, 905189251Ssam struct wpabuf *msg, u8 next_payload) 906189251Ssam{ 907189251Ssam struct ikev2_payload_hdr *phdr; 908189251Ssam size_t plen; 909189251Ssam struct ikev2_proposal *p; 910189251Ssam struct ikev2_transform *t; 911189251Ssam 912189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding SAi payload"); 913189251Ssam 914189251Ssam /* SAi1 - RFC 4306, Sect. 2.7 and 3.3 */ 915189251Ssam phdr = wpabuf_put(msg, sizeof(*phdr)); 916189251Ssam phdr->next_payload = next_payload; 917189251Ssam phdr->flags = 0; 918189251Ssam 919189251Ssam /* TODO: support for multiple proposals */ 920189251Ssam p = wpabuf_put(msg, sizeof(*p)); 921189251Ssam p->proposal_num = data->proposal.proposal_num; 922189251Ssam p->protocol_id = IKEV2_PROTOCOL_IKE; 923189251Ssam p->num_transforms = 4; 924189251Ssam 925189251Ssam t = wpabuf_put(msg, sizeof(*t)); 926189251Ssam t->type = 3; 927189251Ssam t->transform_type = IKEV2_TRANSFORM_ENCR; 928189251Ssam WPA_PUT_BE16(t->transform_id, data->proposal.encr); 929189251Ssam if (data->proposal.encr == ENCR_AES_CBC) { 930189251Ssam /* Transform Attribute: Key Len = 128 bits */ 931189251Ssam wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */ 932189251Ssam wpabuf_put_be16(msg, 128); /* 128-bit key */ 933189251Ssam } 934189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t; 935189251Ssam WPA_PUT_BE16(t->transform_length, plen); 936189251Ssam 937189251Ssam t = wpabuf_put(msg, sizeof(*t)); 938189251Ssam t->type = 3; 939189251Ssam WPA_PUT_BE16(t->transform_length, sizeof(*t)); 940189251Ssam t->transform_type = IKEV2_TRANSFORM_PRF; 941189251Ssam WPA_PUT_BE16(t->transform_id, data->proposal.prf); 942189251Ssam 943189251Ssam t = wpabuf_put(msg, sizeof(*t)); 944189251Ssam t->type = 3; 945189251Ssam WPA_PUT_BE16(t->transform_length, sizeof(*t)); 946189251Ssam t->transform_type = IKEV2_TRANSFORM_INTEG; 947189251Ssam WPA_PUT_BE16(t->transform_id, data->proposal.integ); 948189251Ssam 949189251Ssam t = wpabuf_put(msg, sizeof(*t)); 950189251Ssam WPA_PUT_BE16(t->transform_length, sizeof(*t)); 951189251Ssam t->transform_type = IKEV2_TRANSFORM_DH; 952189251Ssam WPA_PUT_BE16(t->transform_id, data->proposal.dh); 953189251Ssam 954189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p; 955189251Ssam WPA_PUT_BE16(p->proposal_length, plen); 956189251Ssam 957189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; 958189251Ssam WPA_PUT_BE16(phdr->payload_length, plen); 959189251Ssam 960189251Ssam return 0; 961189251Ssam} 962189251Ssam 963189251Ssam 964189251Ssamstatic int ikev2_build_kei(struct ikev2_initiator_data *data, 965189251Ssam struct wpabuf *msg, u8 next_payload) 966189251Ssam{ 967189251Ssam struct ikev2_payload_hdr *phdr; 968189251Ssam size_t plen; 969189251Ssam struct wpabuf *pv; 970189251Ssam 971189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding KEi payload"); 972189251Ssam 973189251Ssam data->dh = dh_groups_get(data->proposal.dh); 974189251Ssam pv = dh_init(data->dh, &data->i_dh_private); 975189251Ssam if (pv == NULL) { 976189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH"); 977189251Ssam return -1; 978189251Ssam } 979189251Ssam 980189251Ssam /* KEi - RFC 4306, Sect. 3.4 */ 981189251Ssam phdr = wpabuf_put(msg, sizeof(*phdr)); 982189251Ssam phdr->next_payload = next_payload; 983189251Ssam phdr->flags = 0; 984189251Ssam 985189251Ssam wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */ 986189251Ssam wpabuf_put(msg, 2); /* RESERVED */ 987189251Ssam /* 988189251Ssam * RFC 4306, Sect. 3.4: possible zero padding for public value to 989189251Ssam * match the length of the prime. 990189251Ssam */ 991189251Ssam wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); 992189251Ssam wpabuf_put_buf(msg, pv); 993281806Srpaulo wpabuf_free(pv); 994189251Ssam 995189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; 996189251Ssam WPA_PUT_BE16(phdr->payload_length, plen); 997189251Ssam return 0; 998189251Ssam} 999189251Ssam 1000189251Ssam 1001189251Ssamstatic int ikev2_build_ni(struct ikev2_initiator_data *data, 1002189251Ssam struct wpabuf *msg, u8 next_payload) 1003189251Ssam{ 1004189251Ssam struct ikev2_payload_hdr *phdr; 1005189251Ssam size_t plen; 1006189251Ssam 1007189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding Ni payload"); 1008189251Ssam 1009189251Ssam /* Ni - RFC 4306, Sect. 3.9 */ 1010189251Ssam phdr = wpabuf_put(msg, sizeof(*phdr)); 1011189251Ssam phdr->next_payload = next_payload; 1012189251Ssam phdr->flags = 0; 1013189251Ssam wpabuf_put_data(msg, data->i_nonce, data->i_nonce_len); 1014189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; 1015189251Ssam WPA_PUT_BE16(phdr->payload_length, plen); 1016189251Ssam return 0; 1017189251Ssam} 1018189251Ssam 1019189251Ssam 1020189251Ssamstatic int ikev2_build_idi(struct ikev2_initiator_data *data, 1021189251Ssam struct wpabuf *msg, u8 next_payload) 1022189251Ssam{ 1023189251Ssam struct ikev2_payload_hdr *phdr; 1024189251Ssam size_t plen; 1025189251Ssam 1026189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding IDi payload"); 1027189251Ssam 1028189251Ssam if (data->IDi == NULL) { 1029189251Ssam wpa_printf(MSG_INFO, "IKEV2: No IDi available"); 1030189251Ssam return -1; 1031189251Ssam } 1032189251Ssam 1033189251Ssam /* IDi - RFC 4306, Sect. 3.5 */ 1034189251Ssam phdr = wpabuf_put(msg, sizeof(*phdr)); 1035189251Ssam phdr->next_payload = next_payload; 1036189251Ssam phdr->flags = 0; 1037189251Ssam wpabuf_put_u8(msg, ID_KEY_ID); 1038189251Ssam wpabuf_put(msg, 3); /* RESERVED */ 1039189251Ssam wpabuf_put_data(msg, data->IDi, data->IDi_len); 1040189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; 1041189251Ssam WPA_PUT_BE16(phdr->payload_length, plen); 1042189251Ssam return 0; 1043189251Ssam} 1044189251Ssam 1045189251Ssam 1046189251Ssamstatic int ikev2_build_auth(struct ikev2_initiator_data *data, 1047189251Ssam struct wpabuf *msg, u8 next_payload) 1048189251Ssam{ 1049189251Ssam struct ikev2_payload_hdr *phdr; 1050189251Ssam size_t plen; 1051189251Ssam const struct ikev2_prf_alg *prf; 1052189251Ssam 1053189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload"); 1054189251Ssam 1055189251Ssam prf = ikev2_get_prf(data->proposal.prf); 1056189251Ssam if (prf == NULL) 1057189251Ssam return -1; 1058189251Ssam 1059189251Ssam /* Authentication - RFC 4306, Sect. 3.8 */ 1060189251Ssam phdr = wpabuf_put(msg, sizeof(*phdr)); 1061189251Ssam phdr->next_payload = next_payload; 1062189251Ssam phdr->flags = 0; 1063189251Ssam wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC); 1064189251Ssam wpabuf_put(msg, 3); /* RESERVED */ 1065189251Ssam 1066189251Ssam /* msg | Nr | prf(SK_pi,IDi') */ 1067189251Ssam if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg, 1068189251Ssam data->IDi, data->IDi_len, ID_KEY_ID, 1069189251Ssam &data->keys, 1, data->shared_secret, 1070189251Ssam data->shared_secret_len, 1071189251Ssam data->r_nonce, data->r_nonce_len, 1072189251Ssam data->key_pad, data->key_pad_len, 1073189251Ssam wpabuf_put(msg, prf->hash_len)) < 0) { 1074189251Ssam wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); 1075189251Ssam return -1; 1076189251Ssam } 1077189251Ssam wpabuf_free(data->i_sign_msg); 1078189251Ssam data->i_sign_msg = NULL; 1079189251Ssam 1080189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; 1081189251Ssam WPA_PUT_BE16(phdr->payload_length, plen); 1082189251Ssam return 0; 1083189251Ssam} 1084189251Ssam 1085189251Ssam 1086189251Ssamstatic struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data) 1087189251Ssam{ 1088189251Ssam struct wpabuf *msg; 1089189251Ssam 1090189251Ssam /* build IKE_SA_INIT: HDR, SAi, KEi, Ni */ 1091189251Ssam 1092189251Ssam if (os_get_random(data->i_spi, IKEV2_SPI_LEN)) 1093189251Ssam return NULL; 1094189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", 1095189251Ssam data->i_spi, IKEV2_SPI_LEN); 1096189251Ssam 1097189251Ssam data->i_nonce_len = IKEV2_NONCE_MIN_LEN; 1098252726Srpaulo if (random_get_bytes(data->i_nonce, data->i_nonce_len)) 1099189251Ssam return NULL; 1100189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len); 1101189251Ssam 1102189251Ssam msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000); 1103189251Ssam if (msg == NULL) 1104189251Ssam return NULL; 1105189251Ssam 1106189251Ssam ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0); 1107189251Ssam if (ikev2_build_sai(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) || 1108189251Ssam ikev2_build_kei(data, msg, IKEV2_PAYLOAD_NONCE) || 1109189251Ssam ikev2_build_ni(data, msg, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { 1110189251Ssam wpabuf_free(msg); 1111189251Ssam return NULL; 1112189251Ssam } 1113189251Ssam 1114189251Ssam ikev2_update_hdr(msg); 1115189251Ssam 1116189251Ssam wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg); 1117189251Ssam 1118189251Ssam wpabuf_free(data->i_sign_msg); 1119189251Ssam data->i_sign_msg = wpabuf_dup(msg); 1120189251Ssam 1121189251Ssam return msg; 1122189251Ssam} 1123189251Ssam 1124189251Ssam 1125189251Ssamstatic struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data) 1126189251Ssam{ 1127189251Ssam struct wpabuf *msg, *plain; 1128189251Ssam const u8 *secret; 1129189251Ssam size_t secret_len; 1130189251Ssam 1131189251Ssam secret = data->get_shared_secret(data->cb_ctx, data->IDr, 1132189251Ssam data->IDr_len, &secret_len); 1133189251Ssam if (secret == NULL) { 1134189251Ssam wpa_printf(MSG_INFO, "IKEV2: Could not get shared secret - " 1135189251Ssam "use fake value"); 1136189251Ssam /* RFC 5106, Sect. 7: 1137189251Ssam * Use a random key to fake AUTH generation in order to prevent 1138189251Ssam * probing of user identities. 1139189251Ssam */ 1140189251Ssam data->unknown_user = 1; 1141189251Ssam os_free(data->shared_secret); 1142189251Ssam data->shared_secret = os_malloc(16); 1143189251Ssam if (data->shared_secret == NULL) 1144189251Ssam return NULL; 1145189251Ssam data->shared_secret_len = 16; 1146252726Srpaulo if (random_get_bytes(data->shared_secret, 16)) 1147189251Ssam return NULL; 1148189251Ssam } else { 1149189251Ssam os_free(data->shared_secret); 1150189251Ssam data->shared_secret = os_malloc(secret_len); 1151189251Ssam if (data->shared_secret == NULL) 1152189251Ssam return NULL; 1153189251Ssam os_memcpy(data->shared_secret, secret, secret_len); 1154189251Ssam data->shared_secret_len = secret_len; 1155189251Ssam } 1156189251Ssam 1157189251Ssam /* build IKE_SA_AUTH: HDR, SK {IDi, [CERT,] [CERTREQ,] AUTH} */ 1158189251Ssam 1159189251Ssam msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000); 1160189251Ssam if (msg == NULL) 1161189251Ssam return NULL; 1162189251Ssam ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1); 1163189251Ssam 1164189251Ssam plain = wpabuf_alloc(data->IDr_len + 1000); 1165189251Ssam if (plain == NULL) { 1166189251Ssam wpabuf_free(msg); 1167189251Ssam return NULL; 1168189251Ssam } 1169189251Ssam 1170189251Ssam if (ikev2_build_idi(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) || 1171189251Ssam ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || 1172189251Ssam ikev2_build_encrypted(data->proposal.encr, data->proposal.integ, 1173189251Ssam &data->keys, 1, msg, plain, 1174189251Ssam IKEV2_PAYLOAD_IDi)) { 1175189251Ssam wpabuf_free(plain); 1176189251Ssam wpabuf_free(msg); 1177189251Ssam return NULL; 1178189251Ssam } 1179189251Ssam wpabuf_free(plain); 1180189251Ssam 1181189251Ssam wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg); 1182189251Ssam 1183189251Ssam return msg; 1184189251Ssam} 1185189251Ssam 1186189251Ssam 1187189251Ssamstruct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data) 1188189251Ssam{ 1189189251Ssam switch (data->state) { 1190189251Ssam case SA_INIT: 1191189251Ssam return ikev2_build_sa_init(data); 1192189251Ssam case SA_AUTH: 1193189251Ssam return ikev2_build_sa_auth(data); 1194189251Ssam case CHILD_SA: 1195189251Ssam return NULL; 1196189251Ssam case IKEV2_DONE: 1197189251Ssam return NULL; 1198189251Ssam } 1199189251Ssam return NULL; 1200189251Ssam} 1201