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); 136337817Scy if (transform_len < (int) sizeof(*t) || transform_len > end - pos) { 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); 224337817Scy if (proposal_len < (int) sizeof(*p) || proposal_len > end - pos) { 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; 259337817Scy if (p->spi_size > pend - ppos) { 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 } 547346981Scy data->IDr = os_memdup(idr, idr_len); 548189251Ssam if (data->IDr == NULL) 549189251Ssam return -1; 550189251Ssam data->IDr_len = idr_len; 551189251Ssam data->IDr_type = id_type; 552189251Ssam 553189251Ssam return 0; 554189251Ssam} 555189251Ssam 556189251Ssam 557189251Ssamstatic int ikev2_process_cert(struct ikev2_initiator_data *data, 558189251Ssam const u8 *cert, size_t cert_len) 559189251Ssam{ 560189251Ssam u8 cert_encoding; 561189251Ssam 562189251Ssam if (cert == NULL) { 563189251Ssam if (data->peer_auth == PEER_AUTH_CERT) { 564189251Ssam wpa_printf(MSG_INFO, "IKEV2: No Certificate received"); 565189251Ssam return -1; 566189251Ssam } 567189251Ssam return 0; 568189251Ssam } 569189251Ssam 570189251Ssam if (cert_len < 1) { 571189251Ssam wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field"); 572189251Ssam return -1; 573189251Ssam } 574189251Ssam 575189251Ssam cert_encoding = cert[0]; 576189251Ssam cert++; 577189251Ssam cert_len--; 578189251Ssam 579189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding); 580189251Ssam wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len); 581189251Ssam 582189251Ssam /* TODO: validate certificate */ 583189251Ssam 584189251Ssam return 0; 585189251Ssam} 586189251Ssam 587189251Ssam 588189251Ssamstatic int ikev2_process_auth_cert(struct ikev2_initiator_data *data, 589189251Ssam u8 method, const u8 *auth, size_t auth_len) 590189251Ssam{ 591189251Ssam if (method != AUTH_RSA_SIGN) { 592189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " 593189251Ssam "method %d", method); 594189251Ssam return -1; 595189251Ssam } 596189251Ssam 597189251Ssam /* TODO: validate AUTH */ 598189251Ssam return 0; 599189251Ssam} 600189251Ssam 601189251Ssam 602189251Ssamstatic int ikev2_process_auth_secret(struct ikev2_initiator_data *data, 603189251Ssam u8 method, const u8 *auth, 604189251Ssam size_t auth_len) 605189251Ssam{ 606189251Ssam u8 auth_data[IKEV2_MAX_HASH_LEN]; 607189251Ssam const struct ikev2_prf_alg *prf; 608189251Ssam 609189251Ssam if (method != AUTH_SHARED_KEY_MIC) { 610189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication " 611189251Ssam "method %d", method); 612189251Ssam return -1; 613189251Ssam } 614189251Ssam 615189251Ssam /* msg | Ni | prf(SK_pr,IDr') */ 616189251Ssam if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg, 617189251Ssam data->IDr, data->IDr_len, data->IDr_type, 618189251Ssam &data->keys, 0, data->shared_secret, 619189251Ssam data->shared_secret_len, 620189251Ssam data->i_nonce, data->i_nonce_len, 621189251Ssam data->key_pad, data->key_pad_len, 622189251Ssam auth_data) < 0) { 623189251Ssam wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); 624189251Ssam return -1; 625189251Ssam } 626189251Ssam 627189251Ssam wpabuf_free(data->r_sign_msg); 628189251Ssam data->r_sign_msg = NULL; 629189251Ssam 630189251Ssam prf = ikev2_get_prf(data->proposal.prf); 631189251Ssam if (prf == NULL) 632189251Ssam return -1; 633189251Ssam 634189251Ssam if (auth_len != prf->hash_len || 635281806Srpaulo os_memcmp_const(auth, auth_data, auth_len) != 0) { 636189251Ssam wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data"); 637189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data", 638189251Ssam auth, auth_len); 639189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data", 640189251Ssam auth_data, prf->hash_len); 641189251Ssam return -1; 642189251Ssam } 643189251Ssam 644189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Peer authenticated successfully " 645189251Ssam "using shared keys"); 646189251Ssam 647189251Ssam return 0; 648189251Ssam} 649189251Ssam 650189251Ssam 651189251Ssamstatic int ikev2_process_auth(struct ikev2_initiator_data *data, 652189251Ssam const u8 *auth, size_t auth_len) 653189251Ssam{ 654189251Ssam u8 auth_method; 655189251Ssam 656189251Ssam if (auth == NULL) { 657189251Ssam wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload"); 658189251Ssam return -1; 659189251Ssam } 660189251Ssam 661189251Ssam if (auth_len < 4) { 662189251Ssam wpa_printf(MSG_INFO, "IKEV2: Too short Authentication " 663189251Ssam "Payload"); 664189251Ssam return -1; 665189251Ssam } 666189251Ssam 667189251Ssam auth_method = auth[0]; 668189251Ssam auth += 4; 669189251Ssam auth_len -= 4; 670189251Ssam 671189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method); 672189251Ssam wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len); 673189251Ssam 674189251Ssam switch (data->peer_auth) { 675189251Ssam case PEER_AUTH_CERT: 676189251Ssam return ikev2_process_auth_cert(data, auth_method, auth, 677189251Ssam auth_len); 678189251Ssam case PEER_AUTH_SECRET: 679189251Ssam return ikev2_process_auth_secret(data, auth_method, auth, 680189251Ssam auth_len); 681189251Ssam } 682189251Ssam 683189251Ssam return -1; 684189251Ssam} 685189251Ssam 686189251Ssam 687189251Ssamstatic int ikev2_process_sa_auth_decrypted(struct ikev2_initiator_data *data, 688189251Ssam u8 next_payload, 689189251Ssam u8 *payload, size_t payload_len) 690189251Ssam{ 691189251Ssam struct ikev2_payloads pl; 692189251Ssam 693189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads"); 694189251Ssam 695189251Ssam if (ikev2_parse_payloads(&pl, next_payload, payload, payload + 696189251Ssam payload_len) < 0) { 697189251Ssam wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted " 698189251Ssam "payloads"); 699189251Ssam return -1; 700189251Ssam } 701189251Ssam 702189251Ssam if (ikev2_process_idr(data, pl.idr, pl.idr_len) < 0 || 703189251Ssam ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 || 704189251Ssam ikev2_process_auth(data, pl.auth, pl.auth_len) < 0) 705189251Ssam return -1; 706189251Ssam 707189251Ssam return 0; 708189251Ssam} 709189251Ssam 710189251Ssam 711189251Ssamstatic int ikev2_process_sa_auth(struct ikev2_initiator_data *data, 712189251Ssam const struct ikev2_hdr *hdr, 713189251Ssam struct ikev2_payloads *pl) 714189251Ssam{ 715189251Ssam u8 *decrypted; 716189251Ssam size_t decrypted_len; 717189251Ssam int ret; 718189251Ssam 719189251Ssam decrypted = ikev2_decrypt_payload(data->proposal.encr, 720189251Ssam data->proposal.integ, 721189251Ssam &data->keys, 0, hdr, pl->encrypted, 722189251Ssam pl->encrypted_len, &decrypted_len); 723189251Ssam if (decrypted == NULL) 724189251Ssam return -1; 725189251Ssam 726189251Ssam ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload, 727189251Ssam decrypted, decrypted_len); 728189251Ssam os_free(decrypted); 729189251Ssam 730189251Ssam if (ret == 0 && !data->unknown_user) { 731189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Authentication completed"); 732189251Ssam data->state = IKEV2_DONE; 733189251Ssam } 734189251Ssam 735189251Ssam return ret; 736189251Ssam} 737189251Ssam 738189251Ssam 739189251Ssamstatic int ikev2_validate_rx_state(struct ikev2_initiator_data *data, 740189251Ssam u8 exchange_type, u32 message_id) 741189251Ssam{ 742189251Ssam switch (data->state) { 743189251Ssam case SA_INIT: 744189251Ssam /* Expect to receive IKE_SA_INIT: HDR, SAr, KEr, Nr, [CERTREQ], 745189251Ssam * [SK{IDr}] */ 746189251Ssam if (exchange_type != IKE_SA_INIT) { 747189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " 748189251Ssam "%u in SA_INIT state", exchange_type); 749189251Ssam return -1; 750189251Ssam } 751189251Ssam if (message_id != 0) { 752189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " 753189251Ssam "in SA_INIT state", message_id); 754189251Ssam return -1; 755189251Ssam } 756189251Ssam break; 757189251Ssam case SA_AUTH: 758189251Ssam /* Expect to receive IKE_SA_AUTH: 759189251Ssam * HDR, SK {IDr, [CERT,] [CERTREQ,] [NFID,] AUTH} 760189251Ssam */ 761189251Ssam if (exchange_type != IKE_SA_AUTH) { 762189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " 763189251Ssam "%u in SA_AUTH state", exchange_type); 764189251Ssam return -1; 765189251Ssam } 766189251Ssam if (message_id != 1) { 767189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " 768189251Ssam "in SA_AUTH state", message_id); 769189251Ssam return -1; 770189251Ssam } 771189251Ssam break; 772189251Ssam case CHILD_SA: 773189251Ssam if (exchange_type != CREATE_CHILD_SA) { 774189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type " 775189251Ssam "%u in CHILD_SA state", exchange_type); 776189251Ssam return -1; 777189251Ssam } 778189251Ssam if (message_id != 2) { 779189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u " 780189251Ssam "in CHILD_SA state", message_id); 781189251Ssam return -1; 782189251Ssam } 783189251Ssam break; 784189251Ssam case IKEV2_DONE: 785189251Ssam return -1; 786189251Ssam } 787189251Ssam 788189251Ssam return 0; 789189251Ssam} 790189251Ssam 791189251Ssam 792189251Ssamint ikev2_initiator_process(struct ikev2_initiator_data *data, 793189251Ssam const struct wpabuf *buf) 794189251Ssam{ 795189251Ssam const struct ikev2_hdr *hdr; 796189251Ssam u32 length, message_id; 797189251Ssam const u8 *pos, *end; 798189251Ssam struct ikev2_payloads pl; 799189251Ssam 800189251Ssam wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)", 801189251Ssam (unsigned long) wpabuf_len(buf)); 802189251Ssam 803189251Ssam if (wpabuf_len(buf) < sizeof(*hdr)) { 804189251Ssam wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR"); 805189251Ssam return -1; 806189251Ssam } 807189251Ssam 808189251Ssam hdr = (const struct ikev2_hdr *) wpabuf_head(buf); 809189251Ssam end = wpabuf_head_u8(buf) + wpabuf_len(buf); 810189251Ssam message_id = WPA_GET_BE32(hdr->message_id); 811189251Ssam length = WPA_GET_BE32(hdr->length); 812189251Ssam 813189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", 814189251Ssam hdr->i_spi, IKEV2_SPI_LEN); 815189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", 816189251Ssam hdr->r_spi, IKEV2_SPI_LEN); 817189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Version: 0x%x " 818189251Ssam "Exchange Type: %u", 819189251Ssam hdr->next_payload, hdr->version, hdr->exchange_type); 820189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Message ID: %u Length: %u", 821189251Ssam message_id, length); 822189251Ssam 823189251Ssam if (hdr->version != IKEV2_VERSION) { 824189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x " 825189251Ssam "(expected 0x%x)", hdr->version, IKEV2_VERSION); 826189251Ssam return -1; 827189251Ssam } 828189251Ssam 829189251Ssam if (length != wpabuf_len(buf)) { 830189251Ssam wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != " 831189251Ssam "RX: %lu)", (unsigned long) length, 832189251Ssam (unsigned long) wpabuf_len(buf)); 833189251Ssam return -1; 834189251Ssam } 835189251Ssam 836189251Ssam if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0) 837189251Ssam return -1; 838189251Ssam 839189251Ssam if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) != 840189251Ssam IKEV2_HDR_RESPONSE) { 841189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x", 842189251Ssam hdr->flags); 843189251Ssam return -1; 844189251Ssam } 845189251Ssam 846189251Ssam if (data->state != SA_INIT) { 847189251Ssam if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) { 848189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " 849189251Ssam "Initiator's SPI"); 850189251Ssam return -1; 851189251Ssam } 852189251Ssam if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) { 853189251Ssam wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA " 854189251Ssam "Responder's SPI"); 855189251Ssam return -1; 856189251Ssam } 857189251Ssam } 858189251Ssam 859189251Ssam pos = (const u8 *) (hdr + 1); 860189251Ssam if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0) 861189251Ssam return -1; 862189251Ssam 863189251Ssam switch (data->state) { 864189251Ssam case SA_INIT: 865189251Ssam if (ikev2_process_sa_init(data, hdr, &pl) < 0) 866189251Ssam return -1; 867189251Ssam wpabuf_free(data->r_sign_msg); 868189251Ssam data->r_sign_msg = wpabuf_dup(buf); 869189251Ssam break; 870189251Ssam case SA_AUTH: 871189251Ssam if (ikev2_process_sa_auth(data, hdr, &pl) < 0) 872189251Ssam return -1; 873189251Ssam break; 874189251Ssam case CHILD_SA: 875189251Ssam case IKEV2_DONE: 876189251Ssam break; 877189251Ssam } 878189251Ssam 879189251Ssam return 0; 880189251Ssam} 881189251Ssam 882189251Ssam 883189251Ssamstatic void ikev2_build_hdr(struct ikev2_initiator_data *data, 884189251Ssam struct wpabuf *msg, u8 exchange_type, 885189251Ssam u8 next_payload, u32 message_id) 886189251Ssam{ 887189251Ssam struct ikev2_hdr *hdr; 888189251Ssam 889189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR"); 890189251Ssam 891189251Ssam /* HDR - RFC 4306, Sect. 3.1 */ 892189251Ssam hdr = wpabuf_put(msg, sizeof(*hdr)); 893189251Ssam os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN); 894189251Ssam os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN); 895189251Ssam hdr->next_payload = next_payload; 896189251Ssam hdr->version = IKEV2_VERSION; 897189251Ssam hdr->exchange_type = exchange_type; 898189251Ssam hdr->flags = IKEV2_HDR_INITIATOR; 899189251Ssam WPA_PUT_BE32(hdr->message_id, message_id); 900189251Ssam} 901189251Ssam 902189251Ssam 903189251Ssamstatic int ikev2_build_sai(struct ikev2_initiator_data *data, 904189251Ssam struct wpabuf *msg, u8 next_payload) 905189251Ssam{ 906189251Ssam struct ikev2_payload_hdr *phdr; 907189251Ssam size_t plen; 908189251Ssam struct ikev2_proposal *p; 909189251Ssam struct ikev2_transform *t; 910189251Ssam 911189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding SAi payload"); 912189251Ssam 913189251Ssam /* SAi1 - RFC 4306, Sect. 2.7 and 3.3 */ 914189251Ssam phdr = wpabuf_put(msg, sizeof(*phdr)); 915189251Ssam phdr->next_payload = next_payload; 916189251Ssam phdr->flags = 0; 917189251Ssam 918189251Ssam /* TODO: support for multiple proposals */ 919189251Ssam p = wpabuf_put(msg, sizeof(*p)); 920189251Ssam p->proposal_num = data->proposal.proposal_num; 921189251Ssam p->protocol_id = IKEV2_PROTOCOL_IKE; 922189251Ssam p->num_transforms = 4; 923189251Ssam 924189251Ssam t = wpabuf_put(msg, sizeof(*t)); 925189251Ssam t->type = 3; 926189251Ssam t->transform_type = IKEV2_TRANSFORM_ENCR; 927189251Ssam WPA_PUT_BE16(t->transform_id, data->proposal.encr); 928189251Ssam if (data->proposal.encr == ENCR_AES_CBC) { 929189251Ssam /* Transform Attribute: Key Len = 128 bits */ 930189251Ssam wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */ 931189251Ssam wpabuf_put_be16(msg, 128); /* 128-bit key */ 932189251Ssam } 933189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t; 934189251Ssam WPA_PUT_BE16(t->transform_length, plen); 935189251Ssam 936189251Ssam t = wpabuf_put(msg, sizeof(*t)); 937189251Ssam t->type = 3; 938189251Ssam WPA_PUT_BE16(t->transform_length, sizeof(*t)); 939189251Ssam t->transform_type = IKEV2_TRANSFORM_PRF; 940189251Ssam WPA_PUT_BE16(t->transform_id, data->proposal.prf); 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_INTEG; 946189251Ssam WPA_PUT_BE16(t->transform_id, data->proposal.integ); 947189251Ssam 948189251Ssam t = wpabuf_put(msg, sizeof(*t)); 949189251Ssam WPA_PUT_BE16(t->transform_length, sizeof(*t)); 950189251Ssam t->transform_type = IKEV2_TRANSFORM_DH; 951189251Ssam WPA_PUT_BE16(t->transform_id, data->proposal.dh); 952189251Ssam 953189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p; 954189251Ssam WPA_PUT_BE16(p->proposal_length, plen); 955189251Ssam 956189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; 957189251Ssam WPA_PUT_BE16(phdr->payload_length, plen); 958189251Ssam 959189251Ssam return 0; 960189251Ssam} 961189251Ssam 962189251Ssam 963189251Ssamstatic int ikev2_build_kei(struct ikev2_initiator_data *data, 964189251Ssam struct wpabuf *msg, u8 next_payload) 965189251Ssam{ 966189251Ssam struct ikev2_payload_hdr *phdr; 967189251Ssam size_t plen; 968189251Ssam struct wpabuf *pv; 969189251Ssam 970189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding KEi payload"); 971189251Ssam 972189251Ssam data->dh = dh_groups_get(data->proposal.dh); 973189251Ssam pv = dh_init(data->dh, &data->i_dh_private); 974189251Ssam if (pv == NULL) { 975189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH"); 976189251Ssam return -1; 977189251Ssam } 978189251Ssam 979189251Ssam /* KEi - RFC 4306, Sect. 3.4 */ 980189251Ssam phdr = wpabuf_put(msg, sizeof(*phdr)); 981189251Ssam phdr->next_payload = next_payload; 982189251Ssam phdr->flags = 0; 983189251Ssam 984189251Ssam wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */ 985189251Ssam wpabuf_put(msg, 2); /* RESERVED */ 986189251Ssam /* 987189251Ssam * RFC 4306, Sect. 3.4: possible zero padding for public value to 988189251Ssam * match the length of the prime. 989189251Ssam */ 990189251Ssam wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv)); 991189251Ssam wpabuf_put_buf(msg, pv); 992281806Srpaulo wpabuf_free(pv); 993189251Ssam 994189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; 995189251Ssam WPA_PUT_BE16(phdr->payload_length, plen); 996189251Ssam return 0; 997189251Ssam} 998189251Ssam 999189251Ssam 1000189251Ssamstatic int ikev2_build_ni(struct ikev2_initiator_data *data, 1001189251Ssam struct wpabuf *msg, u8 next_payload) 1002189251Ssam{ 1003189251Ssam struct ikev2_payload_hdr *phdr; 1004189251Ssam size_t plen; 1005189251Ssam 1006189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding Ni payload"); 1007189251Ssam 1008189251Ssam /* Ni - RFC 4306, Sect. 3.9 */ 1009189251Ssam phdr = wpabuf_put(msg, sizeof(*phdr)); 1010189251Ssam phdr->next_payload = next_payload; 1011189251Ssam phdr->flags = 0; 1012189251Ssam wpabuf_put_data(msg, data->i_nonce, data->i_nonce_len); 1013189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; 1014189251Ssam WPA_PUT_BE16(phdr->payload_length, plen); 1015189251Ssam return 0; 1016189251Ssam} 1017189251Ssam 1018189251Ssam 1019189251Ssamstatic int ikev2_build_idi(struct ikev2_initiator_data *data, 1020189251Ssam struct wpabuf *msg, u8 next_payload) 1021189251Ssam{ 1022189251Ssam struct ikev2_payload_hdr *phdr; 1023189251Ssam size_t plen; 1024189251Ssam 1025189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding IDi payload"); 1026189251Ssam 1027189251Ssam if (data->IDi == NULL) { 1028189251Ssam wpa_printf(MSG_INFO, "IKEV2: No IDi available"); 1029189251Ssam return -1; 1030189251Ssam } 1031189251Ssam 1032189251Ssam /* IDi - RFC 4306, Sect. 3.5 */ 1033189251Ssam phdr = wpabuf_put(msg, sizeof(*phdr)); 1034189251Ssam phdr->next_payload = next_payload; 1035189251Ssam phdr->flags = 0; 1036189251Ssam wpabuf_put_u8(msg, ID_KEY_ID); 1037189251Ssam wpabuf_put(msg, 3); /* RESERVED */ 1038189251Ssam wpabuf_put_data(msg, data->IDi, data->IDi_len); 1039189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; 1040189251Ssam WPA_PUT_BE16(phdr->payload_length, plen); 1041189251Ssam return 0; 1042189251Ssam} 1043189251Ssam 1044189251Ssam 1045189251Ssamstatic int ikev2_build_auth(struct ikev2_initiator_data *data, 1046189251Ssam struct wpabuf *msg, u8 next_payload) 1047189251Ssam{ 1048189251Ssam struct ikev2_payload_hdr *phdr; 1049189251Ssam size_t plen; 1050189251Ssam const struct ikev2_prf_alg *prf; 1051189251Ssam 1052189251Ssam wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload"); 1053189251Ssam 1054189251Ssam prf = ikev2_get_prf(data->proposal.prf); 1055189251Ssam if (prf == NULL) 1056189251Ssam return -1; 1057189251Ssam 1058189251Ssam /* Authentication - RFC 4306, Sect. 3.8 */ 1059189251Ssam phdr = wpabuf_put(msg, sizeof(*phdr)); 1060189251Ssam phdr->next_payload = next_payload; 1061189251Ssam phdr->flags = 0; 1062189251Ssam wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC); 1063189251Ssam wpabuf_put(msg, 3); /* RESERVED */ 1064189251Ssam 1065189251Ssam /* msg | Nr | prf(SK_pi,IDi') */ 1066189251Ssam if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg, 1067189251Ssam data->IDi, data->IDi_len, ID_KEY_ID, 1068189251Ssam &data->keys, 1, data->shared_secret, 1069189251Ssam data->shared_secret_len, 1070189251Ssam data->r_nonce, data->r_nonce_len, 1071189251Ssam data->key_pad, data->key_pad_len, 1072189251Ssam wpabuf_put(msg, prf->hash_len)) < 0) { 1073189251Ssam wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data"); 1074189251Ssam return -1; 1075189251Ssam } 1076189251Ssam wpabuf_free(data->i_sign_msg); 1077189251Ssam data->i_sign_msg = NULL; 1078189251Ssam 1079189251Ssam plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr; 1080189251Ssam WPA_PUT_BE16(phdr->payload_length, plen); 1081189251Ssam return 0; 1082189251Ssam} 1083189251Ssam 1084189251Ssam 1085189251Ssamstatic struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data) 1086189251Ssam{ 1087189251Ssam struct wpabuf *msg; 1088189251Ssam 1089189251Ssam /* build IKE_SA_INIT: HDR, SAi, KEi, Ni */ 1090189251Ssam 1091189251Ssam if (os_get_random(data->i_spi, IKEV2_SPI_LEN)) 1092189251Ssam return NULL; 1093189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI", 1094189251Ssam data->i_spi, IKEV2_SPI_LEN); 1095189251Ssam 1096189251Ssam data->i_nonce_len = IKEV2_NONCE_MIN_LEN; 1097252726Srpaulo if (random_get_bytes(data->i_nonce, data->i_nonce_len)) 1098189251Ssam return NULL; 1099189251Ssam wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len); 1100189251Ssam 1101189251Ssam msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000); 1102189251Ssam if (msg == NULL) 1103189251Ssam return NULL; 1104189251Ssam 1105189251Ssam ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0); 1106189251Ssam if (ikev2_build_sai(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) || 1107189251Ssam ikev2_build_kei(data, msg, IKEV2_PAYLOAD_NONCE) || 1108189251Ssam ikev2_build_ni(data, msg, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) { 1109189251Ssam wpabuf_free(msg); 1110189251Ssam return NULL; 1111189251Ssam } 1112189251Ssam 1113189251Ssam ikev2_update_hdr(msg); 1114189251Ssam 1115189251Ssam wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg); 1116189251Ssam 1117189251Ssam wpabuf_free(data->i_sign_msg); 1118189251Ssam data->i_sign_msg = wpabuf_dup(msg); 1119189251Ssam 1120189251Ssam return msg; 1121189251Ssam} 1122189251Ssam 1123189251Ssam 1124189251Ssamstatic struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data) 1125189251Ssam{ 1126189251Ssam struct wpabuf *msg, *plain; 1127189251Ssam const u8 *secret; 1128189251Ssam size_t secret_len; 1129189251Ssam 1130189251Ssam secret = data->get_shared_secret(data->cb_ctx, data->IDr, 1131189251Ssam data->IDr_len, &secret_len); 1132189251Ssam if (secret == NULL) { 1133189251Ssam wpa_printf(MSG_INFO, "IKEV2: Could not get shared secret - " 1134189251Ssam "use fake value"); 1135189251Ssam /* RFC 5106, Sect. 7: 1136189251Ssam * Use a random key to fake AUTH generation in order to prevent 1137189251Ssam * probing of user identities. 1138189251Ssam */ 1139189251Ssam data->unknown_user = 1; 1140189251Ssam os_free(data->shared_secret); 1141189251Ssam data->shared_secret = os_malloc(16); 1142189251Ssam if (data->shared_secret == NULL) 1143189251Ssam return NULL; 1144189251Ssam data->shared_secret_len = 16; 1145252726Srpaulo if (random_get_bytes(data->shared_secret, 16)) 1146189251Ssam return NULL; 1147189251Ssam } else { 1148189251Ssam os_free(data->shared_secret); 1149346981Scy data->shared_secret = os_memdup(secret, secret_len); 1150189251Ssam if (data->shared_secret == NULL) 1151189251Ssam return NULL; 1152189251Ssam data->shared_secret_len = secret_len; 1153189251Ssam } 1154189251Ssam 1155189251Ssam /* build IKE_SA_AUTH: HDR, SK {IDi, [CERT,] [CERTREQ,] AUTH} */ 1156189251Ssam 1157189251Ssam msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000); 1158189251Ssam if (msg == NULL) 1159189251Ssam return NULL; 1160189251Ssam ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1); 1161189251Ssam 1162189251Ssam plain = wpabuf_alloc(data->IDr_len + 1000); 1163189251Ssam if (plain == NULL) { 1164189251Ssam wpabuf_free(msg); 1165189251Ssam return NULL; 1166189251Ssam } 1167189251Ssam 1168189251Ssam if (ikev2_build_idi(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) || 1169189251Ssam ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) || 1170189251Ssam ikev2_build_encrypted(data->proposal.encr, data->proposal.integ, 1171189251Ssam &data->keys, 1, msg, plain, 1172189251Ssam IKEV2_PAYLOAD_IDi)) { 1173189251Ssam wpabuf_free(plain); 1174189251Ssam wpabuf_free(msg); 1175189251Ssam return NULL; 1176189251Ssam } 1177189251Ssam wpabuf_free(plain); 1178189251Ssam 1179189251Ssam wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg); 1180189251Ssam 1181189251Ssam return msg; 1182189251Ssam} 1183189251Ssam 1184189251Ssam 1185189251Ssamstruct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data) 1186189251Ssam{ 1187189251Ssam switch (data->state) { 1188189251Ssam case SA_INIT: 1189189251Ssam return ikev2_build_sa_init(data); 1190189251Ssam case SA_AUTH: 1191189251Ssam return ikev2_build_sa_auth(data); 1192189251Ssam case CHILD_SA: 1193189251Ssam return NULL; 1194189251Ssam case IKEV2_DONE: 1195189251Ssam return NULL; 1196189251Ssam } 1197189251Ssam return NULL; 1198189251Ssam} 1199