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