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