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