Annotation of embedaddon/strongswan/src/libcharon/sa/ikev2/tasks/ike_init.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2008-2019 Tobias Brunner
! 3: * Copyright (C) 2005-2008 Martin Willi
! 4: * Copyright (C) 2005 Jan Hutter
! 5: * HSR Hochschule fuer Technik Rapperswil
! 6: *
! 7: * This program is free software; you can redistribute it and/or modify it
! 8: * under the terms of the GNU General Public License as published by the
! 9: * Free Software Foundation; either version 2 of the License, or (at your
! 10: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 11: *
! 12: * This program is distributed in the hope that it will be useful, but
! 13: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 14: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 15: * for more details.
! 16: */
! 17:
! 18: #include "ike_init.h"
! 19:
! 20: #include <string.h>
! 21:
! 22: #include <daemon.h>
! 23: #include <bio/bio_reader.h>
! 24: #include <bio/bio_writer.h>
! 25: #include <sa/ikev2/keymat_v2.h>
! 26: #include <crypto/diffie_hellman.h>
! 27: #include <crypto/hashers/hash_algorithm_set.h>
! 28: #include <encoding/payloads/sa_payload.h>
! 29: #include <encoding/payloads/ke_payload.h>
! 30: #include <encoding/payloads/nonce_payload.h>
! 31:
! 32: /** maximum retries to do with cookies/other dh groups */
! 33: #define MAX_RETRIES 5
! 34:
! 35: typedef struct private_ike_init_t private_ike_init_t;
! 36:
! 37: /**
! 38: * Private members of a ike_init_t task.
! 39: */
! 40: struct private_ike_init_t {
! 41:
! 42: /**
! 43: * Public methods and task_t interface.
! 44: */
! 45: ike_init_t public;
! 46:
! 47: /**
! 48: * Assigned IKE_SA.
! 49: */
! 50: ike_sa_t *ike_sa;
! 51:
! 52: /**
! 53: * Are we the initiator?
! 54: */
! 55: bool initiator;
! 56:
! 57: /**
! 58: * diffie hellman group to use
! 59: */
! 60: diffie_hellman_group_t dh_group;
! 61:
! 62: /**
! 63: * diffie hellman key exchange
! 64: */
! 65: diffie_hellman_t *dh;
! 66:
! 67: /**
! 68: * Applying DH public value failed?
! 69: */
! 70: bool dh_failed;
! 71:
! 72: /**
! 73: * Keymat derivation (from IKE_SA)
! 74: */
! 75: keymat_v2_t *keymat;
! 76:
! 77: /**
! 78: * nonce chosen by us
! 79: */
! 80: chunk_t my_nonce;
! 81:
! 82: /**
! 83: * nonce chosen by peer
! 84: */
! 85: chunk_t other_nonce;
! 86:
! 87: /**
! 88: * nonce generator
! 89: */
! 90: nonce_gen_t *nonceg;
! 91:
! 92: /**
! 93: * Negotiated proposal used for IKE_SA
! 94: */
! 95: proposal_t *proposal;
! 96:
! 97: /**
! 98: * Old IKE_SA which gets rekeyed
! 99: */
! 100: ike_sa_t *old_sa;
! 101:
! 102: /**
! 103: * cookie received from responder
! 104: */
! 105: chunk_t cookie;
! 106:
! 107: /**
! 108: * retries done so far after failure (cookie or bad dh group)
! 109: */
! 110: u_int retry;
! 111:
! 112: /**
! 113: * Whether to use Signature Authentication as per RFC 7427
! 114: */
! 115: bool signature_authentication;
! 116:
! 117: /**
! 118: * Whether to follow IKEv2 redirects as per RFC 5685
! 119: */
! 120: bool follow_redirects;
! 121: };
! 122:
! 123: /**
! 124: * Allocate our own nonce value
! 125: */
! 126: static bool generate_nonce(private_ike_init_t *this)
! 127: {
! 128: if (!this->nonceg)
! 129: {
! 130: DBG1(DBG_IKE, "no nonce generator found to create nonce");
! 131: return FALSE;
! 132: }
! 133: if (!this->nonceg->allocate_nonce(this->nonceg, NONCE_SIZE,
! 134: &this->my_nonce))
! 135: {
! 136: DBG1(DBG_IKE, "nonce allocation failed");
! 137: return FALSE;
! 138: }
! 139: return TRUE;
! 140: }
! 141:
! 142: /**
! 143: * Notify the peer about the hash algorithms we support or expect,
! 144: * as per RFC 7427
! 145: */
! 146: static void send_supported_hash_algorithms(private_ike_init_t *this,
! 147: message_t *message)
! 148: {
! 149: hash_algorithm_set_t *algos;
! 150: enumerator_t *enumerator, *rounds;
! 151: bio_writer_t *writer;
! 152: hash_algorithm_t hash;
! 153: peer_cfg_t *peer;
! 154: auth_cfg_t *auth;
! 155: auth_rule_t rule;
! 156: signature_params_t *config;
! 157: int written;
! 158: size_t len = BUF_LEN;
! 159: char buf[len];
! 160: char *pos = buf;
! 161: char *plugin_name;
! 162:
! 163: algos = hash_algorithm_set_create();
! 164: peer = this->ike_sa->get_peer_cfg(this->ike_sa);
! 165: if (peer)
! 166: {
! 167: rounds = peer->create_auth_cfg_enumerator(peer, FALSE);
! 168: while (rounds->enumerate(rounds, &auth))
! 169: {
! 170: enumerator = auth->create_enumerator(auth);
! 171: while (enumerator->enumerate(enumerator, &rule, &config))
! 172: {
! 173: if (rule == AUTH_RULE_IKE_SIGNATURE_SCHEME)
! 174: {
! 175: hash = hasher_from_signature_scheme(config->scheme,
! 176: config->params);
! 177: if (hasher_algorithm_for_ikev2(hash))
! 178: {
! 179: algos->add(algos, hash);
! 180: }
! 181: }
! 182: }
! 183: enumerator->destroy(enumerator);
! 184: }
! 185: rounds->destroy(rounds);
! 186: }
! 187:
! 188: if (!algos->count(algos))
! 189: {
! 190: enumerator = lib->crypto->create_hasher_enumerator(lib->crypto);
! 191: while (enumerator->enumerate(enumerator, &hash, &plugin_name))
! 192: {
! 193: if (hasher_algorithm_for_ikev2(hash))
! 194: {
! 195: algos->add(algos, hash);
! 196: }
! 197: }
! 198: enumerator->destroy(enumerator);
! 199: }
! 200:
! 201: if (algos->count(algos))
! 202: {
! 203: writer = bio_writer_create(0);
! 204: enumerator = algos->create_enumerator(algos);
! 205: while (enumerator->enumerate(enumerator, &hash))
! 206: {
! 207: writer->write_uint16(writer, hash);
! 208:
! 209: /* generate debug output */
! 210: written = snprintf(pos, len, " %N", hash_algorithm_short_names,
! 211: hash);
! 212: if (written > 0 && written < len)
! 213: {
! 214: pos += written;
! 215: len -= written;
! 216: }
! 217: }
! 218: enumerator->destroy(enumerator);
! 219: message->add_notify(message, FALSE, SIGNATURE_HASH_ALGORITHMS,
! 220: writer->get_buf(writer));
! 221: writer->destroy(writer);
! 222:
! 223: *pos = '\0';
! 224: DBG2(DBG_CFG, "sending supported signature hash algorithms:%s", buf);
! 225: }
! 226: algos->destroy(algos);
! 227: }
! 228:
! 229: /**
! 230: * Store algorithms supported by other peer
! 231: */
! 232: static void handle_supported_hash_algorithms(private_ike_init_t *this,
! 233: notify_payload_t *notify)
! 234: {
! 235: bio_reader_t *reader;
! 236: uint16_t algo;
! 237: int written;
! 238: size_t len = BUF_LEN;
! 239: char buf[len];
! 240: char *pos = buf;
! 241: bool added = FALSE;
! 242:
! 243: reader = bio_reader_create(notify->get_notification_data(notify));
! 244: while (reader->remaining(reader) >= 2 && reader->read_uint16(reader, &algo))
! 245: {
! 246: if (hasher_algorithm_for_ikev2(algo))
! 247: {
! 248: this->keymat->add_hash_algorithm(this->keymat, algo);
! 249: added = TRUE;
! 250:
! 251: /* generate debug output */
! 252: written = snprintf(pos, len, " %N", hash_algorithm_short_names,
! 253: algo);
! 254: if (written > 0 && written < len)
! 255: {
! 256: pos += written;
! 257: len -= written;
! 258: }
! 259: }
! 260: }
! 261: reader->destroy(reader);
! 262:
! 263: *pos = '\0';
! 264: DBG2(DBG_CFG, "received supported signature hash algorithms:%s", buf);
! 265:
! 266: if (added)
! 267: {
! 268: this->ike_sa->enable_extension(this->ike_sa, EXT_SIGNATURE_AUTH);
! 269: }
! 270: }
! 271:
! 272: /**
! 273: * Check whether to send a USE_PPK notify
! 274: */
! 275: static bool send_use_ppk(private_ike_init_t *this)
! 276: {
! 277: peer_cfg_t *peer;
! 278: enumerator_t *keys;
! 279: shared_key_t *key;
! 280: bool use_ppk = FALSE;
! 281:
! 282: if (this->initiator)
! 283: {
! 284: peer = this->ike_sa->get_peer_cfg(this->ike_sa);
! 285: if (peer->get_ppk_id(peer))
! 286: {
! 287: use_ppk = TRUE;
! 288: }
! 289: }
! 290: else if (this->ike_sa->supports_extension(this->ike_sa, EXT_PPK))
! 291: {
! 292: /* check if we have at least one PPK available */
! 293: keys = lib->credmgr->create_shared_enumerator(lib->credmgr, SHARED_PPK,
! 294: NULL, NULL);
! 295: if (keys->enumerate(keys, &key, NULL, NULL))
! 296: {
! 297: use_ppk = TRUE;
! 298: }
! 299: keys->destroy(keys);
! 300: }
! 301: return use_ppk;
! 302: }
! 303:
! 304: /**
! 305: * build the payloads for the message
! 306: */
! 307: static bool build_payloads(private_ike_init_t *this, message_t *message)
! 308: {
! 309: sa_payload_t *sa_payload;
! 310: ke_payload_t *ke_payload;
! 311: nonce_payload_t *nonce_payload;
! 312: linked_list_t *proposal_list, *other_dh_groups;
! 313: ike_sa_id_t *id;
! 314: proposal_t *proposal;
! 315: enumerator_t *enumerator;
! 316: ike_cfg_t *ike_cfg;
! 317:
! 318: id = this->ike_sa->get_id(this->ike_sa);
! 319:
! 320: ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
! 321:
! 322: if (this->initiator)
! 323: {
! 324: proposal_list = ike_cfg->get_proposals(ike_cfg);
! 325: other_dh_groups = linked_list_create();
! 326: enumerator = proposal_list->create_enumerator(proposal_list);
! 327: while (enumerator->enumerate(enumerator, (void**)&proposal))
! 328: {
! 329: /* include SPI of new IKE_SA when we are rekeying */
! 330: if (this->old_sa)
! 331: {
! 332: proposal->set_spi(proposal, id->get_initiator_spi(id));
! 333: }
! 334: /* move the selected DH group to the front of the proposal */
! 335: if (!proposal->promote_dh_group(proposal, this->dh_group))
! 336: { /* the proposal does not include the group, move to the back */
! 337: proposal_list->remove_at(proposal_list, enumerator);
! 338: other_dh_groups->insert_last(other_dh_groups, proposal);
! 339: }
! 340: }
! 341: enumerator->destroy(enumerator);
! 342: /* add proposals that don't contain the selected group */
! 343: enumerator = other_dh_groups->create_enumerator(other_dh_groups);
! 344: while (enumerator->enumerate(enumerator, (void**)&proposal))
! 345: { /* no need to remove from the list as we destroy it anyway*/
! 346: proposal_list->insert_last(proposal_list, proposal);
! 347: }
! 348: enumerator->destroy(enumerator);
! 349: other_dh_groups->destroy(other_dh_groups);
! 350:
! 351: sa_payload = sa_payload_create_from_proposals_v2(proposal_list);
! 352: proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
! 353: }
! 354: else
! 355: {
! 356: if (this->old_sa)
! 357: {
! 358: /* include SPI of new IKE_SA when we are rekeying */
! 359: this->proposal->set_spi(this->proposal, id->get_responder_spi(id));
! 360: }
! 361: sa_payload = sa_payload_create_from_proposal_v2(this->proposal);
! 362: }
! 363: message->add_payload(message, (payload_t*)sa_payload);
! 364:
! 365: ke_payload = ke_payload_create_from_diffie_hellman(PLV2_KEY_EXCHANGE,
! 366: this->dh);
! 367: if (!ke_payload)
! 368: {
! 369: DBG1(DBG_IKE, "creating KE payload failed");
! 370: return FALSE;
! 371: }
! 372: nonce_payload = nonce_payload_create(PLV2_NONCE);
! 373: nonce_payload->set_nonce(nonce_payload, this->my_nonce);
! 374:
! 375: if (this->old_sa)
! 376: { /* payload order differs if we are rekeying */
! 377: message->add_payload(message, (payload_t*)nonce_payload);
! 378: message->add_payload(message, (payload_t*)ke_payload);
! 379: }
! 380: else
! 381: {
! 382: message->add_payload(message, (payload_t*)ke_payload);
! 383: message->add_payload(message, (payload_t*)nonce_payload);
! 384: }
! 385:
! 386: /* negotiate fragmentation if we are not rekeying */
! 387: if (!this->old_sa &&
! 388: ike_cfg->fragmentation(ike_cfg) != FRAGMENTATION_NO)
! 389: {
! 390: if (this->initiator ||
! 391: this->ike_sa->supports_extension(this->ike_sa,
! 392: EXT_IKE_FRAGMENTATION))
! 393: {
! 394: message->add_notify(message, FALSE, FRAGMENTATION_SUPPORTED,
! 395: chunk_empty);
! 396: }
! 397: }
! 398: /* submit supported hash algorithms for signature authentication */
! 399: if (!this->old_sa && this->signature_authentication)
! 400: {
! 401: if (this->initiator ||
! 402: this->ike_sa->supports_extension(this->ike_sa,
! 403: EXT_SIGNATURE_AUTH))
! 404: {
! 405: send_supported_hash_algorithms(this, message);
! 406: }
! 407: }
! 408: /* notify other peer if we support redirection */
! 409: if (!this->old_sa && this->initiator && this->follow_redirects)
! 410: {
! 411: identification_t *gateway;
! 412: host_t *from;
! 413: chunk_t data;
! 414:
! 415: from = this->ike_sa->get_redirected_from(this->ike_sa);
! 416: if (from)
! 417: {
! 418: gateway = identification_create_from_sockaddr(
! 419: from->get_sockaddr(from));
! 420: data = redirect_data_create(gateway, chunk_empty);
! 421: message->add_notify(message, FALSE, REDIRECTED_FROM, data);
! 422: chunk_free(&data);
! 423: gateway->destroy(gateway);
! 424: }
! 425: else
! 426: {
! 427: message->add_notify(message, FALSE, REDIRECT_SUPPORTED,
! 428: chunk_empty);
! 429: }
! 430: }
! 431: /* notify the peer if we want to use/support PPK */
! 432: if (!this->old_sa && send_use_ppk(this))
! 433: {
! 434: message->add_notify(message, FALSE, USE_PPK, chunk_empty);
! 435: }
! 436: /* notify the peer if we accept childless IKE_SAs */
! 437: if (!this->old_sa && !this->initiator &&
! 438: ike_cfg->childless(ike_cfg) != CHILDLESS_NEVER)
! 439: {
! 440: message->add_notify(message, FALSE, CHILDLESS_IKEV2_SUPPORTED,
! 441: chunk_empty);
! 442: }
! 443: return TRUE;
! 444: }
! 445:
! 446: /**
! 447: * Process the SA payload and select a proposal
! 448: */
! 449: static void process_sa_payload(private_ike_init_t *this, message_t *message,
! 450: sa_payload_t *sa_payload)
! 451: {
! 452: ike_cfg_t *ike_cfg, *cfg, *alt_cfg = NULL;
! 453: enumerator_t *enumerator;
! 454: linked_list_t *proposal_list;
! 455: host_t *me, *other;
! 456: proposal_selection_flag_t flags = 0;
! 457:
! 458: ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
! 459:
! 460: proposal_list = sa_payload->get_proposals(sa_payload);
! 461: if (!this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN) &&
! 462: !lib->settings->get_bool(lib->settings, "%s.accept_private_algs",
! 463: FALSE, lib->ns))
! 464: {
! 465: flags |= PROPOSAL_SKIP_PRIVATE;
! 466: }
! 467: if (!lib->settings->get_bool(lib->settings,
! 468: "%s.prefer_configured_proposals", TRUE, lib->ns))
! 469: {
! 470: flags |= PROPOSAL_PREFER_SUPPLIED;
! 471: }
! 472: this->proposal = ike_cfg->select_proposal(ike_cfg, proposal_list, flags);
! 473: if (!this->proposal)
! 474: {
! 475: if (!this->initiator && !this->old_sa)
! 476: {
! 477: me = message->get_destination(message);
! 478: other = message->get_source(message);
! 479: enumerator = charon->backends->create_ike_cfg_enumerator(
! 480: charon->backends, me, other, IKEV2);
! 481: while (enumerator->enumerate(enumerator, &cfg))
! 482: {
! 483: if (ike_cfg == cfg)
! 484: { /* already tried and failed */
! 485: continue;
! 486: }
! 487: DBG1(DBG_IKE, "no matching proposal found, trying alternative "
! 488: "config");
! 489: this->proposal = cfg->select_proposal(cfg, proposal_list,
! 490: flags);
! 491: if (this->proposal)
! 492: {
! 493: alt_cfg = cfg->get_ref(cfg);
! 494: break;
! 495: }
! 496: }
! 497: enumerator->destroy(enumerator);
! 498: }
! 499: if (alt_cfg)
! 500: {
! 501: this->ike_sa->set_ike_cfg(this->ike_sa, alt_cfg);
! 502: alt_cfg->destroy(alt_cfg);
! 503: }
! 504: else
! 505: {
! 506: charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE,
! 507: proposal_list);
! 508: }
! 509: }
! 510: proposal_list->destroy_offset(proposal_list,
! 511: offsetof(proposal_t, destroy));
! 512: }
! 513:
! 514: /**
! 515: * Read payloads from message
! 516: */
! 517: static void process_payloads(private_ike_init_t *this, message_t *message)
! 518: {
! 519: enumerator_t *enumerator;
! 520: payload_t *payload;
! 521: ke_payload_t *ke_payload = NULL;
! 522:
! 523: enumerator = message->create_payload_enumerator(message);
! 524: while (enumerator->enumerate(enumerator, &payload))
! 525: {
! 526: switch (payload->get_type(payload))
! 527: {
! 528: case PLV2_SECURITY_ASSOCIATION:
! 529: {
! 530: process_sa_payload(this, message, (sa_payload_t*)payload);
! 531: break;
! 532: }
! 533: case PLV2_KEY_EXCHANGE:
! 534: {
! 535: ke_payload = (ke_payload_t*)payload;
! 536:
! 537: this->dh_group = ke_payload->get_dh_group_number(ke_payload);
! 538: break;
! 539: }
! 540: case PLV2_NONCE:
! 541: {
! 542: nonce_payload_t *nonce_payload = (nonce_payload_t*)payload;
! 543:
! 544: this->other_nonce = nonce_payload->get_nonce(nonce_payload);
! 545: break;
! 546: }
! 547: case PLV2_NOTIFY:
! 548: {
! 549: notify_payload_t *notify = (notify_payload_t*)payload;
! 550:
! 551: switch (notify->get_notify_type(notify))
! 552: {
! 553: case FRAGMENTATION_SUPPORTED:
! 554: this->ike_sa->enable_extension(this->ike_sa,
! 555: EXT_IKE_FRAGMENTATION);
! 556: break;
! 557: case SIGNATURE_HASH_ALGORITHMS:
! 558: if (this->signature_authentication)
! 559: {
! 560: handle_supported_hash_algorithms(this, notify);
! 561: }
! 562: break;
! 563: case USE_PPK:
! 564: if (!this->old_sa)
! 565: {
! 566: this->ike_sa->enable_extension(this->ike_sa,
! 567: EXT_PPK);
! 568: }
! 569: break;
! 570: case REDIRECTED_FROM:
! 571: {
! 572: identification_t *gateway;
! 573: chunk_t data;
! 574:
! 575: data = notify->get_notification_data(notify);
! 576: gateway = redirect_data_parse(data, NULL);
! 577: if (!gateway)
! 578: {
! 579: DBG1(DBG_IKE, "received invalid REDIRECTED_FROM "
! 580: "notify, ignored");
! 581: break;
! 582: }
! 583: DBG1(DBG_IKE, "client got redirected from %Y", gateway);
! 584: gateway->destroy(gateway);
! 585: /* fall-through */
! 586: }
! 587: case REDIRECT_SUPPORTED:
! 588: if (!this->old_sa)
! 589: {
! 590: this->ike_sa->enable_extension(this->ike_sa,
! 591: EXT_IKE_REDIRECTION);
! 592: }
! 593: break;
! 594: case CHILDLESS_IKEV2_SUPPORTED:
! 595: if (this->initiator && !this->old_sa)
! 596: {
! 597: this->ike_sa->enable_extension(this->ike_sa,
! 598: EXT_IKE_CHILDLESS);
! 599: }
! 600: break;
! 601: default:
! 602: /* other notifies are handled elsewhere */
! 603: break;
! 604: }
! 605:
! 606: }
! 607: default:
! 608: break;
! 609: }
! 610: }
! 611: enumerator->destroy(enumerator);
! 612:
! 613: if (this->proposal)
! 614: {
! 615: this->ike_sa->set_proposal(this->ike_sa, this->proposal);
! 616: }
! 617:
! 618: if (ke_payload && this->proposal &&
! 619: this->proposal->has_dh_group(this->proposal, this->dh_group))
! 620: {
! 621: if (!this->initiator)
! 622: {
! 623: this->dh = this->keymat->keymat.create_dh(
! 624: &this->keymat->keymat, this->dh_group);
! 625: }
! 626: else if (this->dh)
! 627: {
! 628: this->dh_failed = this->dh->get_dh_group(this->dh) != this->dh_group;
! 629: }
! 630: if (this->dh && !this->dh_failed)
! 631: {
! 632: this->dh_failed = !this->dh->set_other_public_value(this->dh,
! 633: ke_payload->get_key_exchange_data(ke_payload));
! 634: }
! 635: }
! 636: }
! 637:
! 638: METHOD(task_t, build_i, status_t,
! 639: private_ike_init_t *this, message_t *message)
! 640: {
! 641: ike_cfg_t *ike_cfg;
! 642:
! 643: ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
! 644:
! 645: DBG0(DBG_IKE, "initiating IKE_SA %s[%d] to %H",
! 646: this->ike_sa->get_name(this->ike_sa),
! 647: this->ike_sa->get_unique_id(this->ike_sa),
! 648: this->ike_sa->get_other_host(this->ike_sa));
! 649: this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
! 650:
! 651: if (this->retry >= MAX_RETRIES)
! 652: {
! 653: DBG1(DBG_IKE, "giving up after %d retries", MAX_RETRIES);
! 654: return FAILED;
! 655: }
! 656:
! 657: /* if we are retrying after an INVALID_KE_PAYLOAD we already have one */
! 658: if (!this->dh)
! 659: {
! 660: if (this->old_sa && lib->settings->get_bool(lib->settings,
! 661: "%s.prefer_previous_dh_group", TRUE, lib->ns))
! 662: { /* reuse the DH group we used for the old IKE_SA when rekeying */
! 663: proposal_t *proposal;
! 664: uint16_t dh_group;
! 665:
! 666: proposal = this->old_sa->get_proposal(this->old_sa);
! 667: if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP,
! 668: &dh_group, NULL))
! 669: {
! 670: this->dh_group = dh_group;
! 671: }
! 672: else
! 673: { /* this shouldn't happen, but let's be safe */
! 674: this->dh_group = ike_cfg->get_dh_group(ike_cfg);
! 675: }
! 676: }
! 677: else
! 678: {
! 679: this->dh_group = ike_cfg->get_dh_group(ike_cfg);
! 680: }
! 681: this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
! 682: this->dh_group);
! 683: if (!this->dh)
! 684: {
! 685: DBG1(DBG_IKE, "configured DH group %N not supported",
! 686: diffie_hellman_group_names, this->dh_group);
! 687: return FAILED;
! 688: }
! 689: }
! 690: else if (this->dh->get_dh_group(this->dh) != this->dh_group)
! 691: { /* reset DH instance if group changed (INVALID_KE_PAYLOAD) */
! 692: this->dh->destroy(this->dh);
! 693: this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
! 694: this->dh_group);
! 695: if (!this->dh)
! 696: {
! 697: DBG1(DBG_IKE, "requested DH group %N not supported",
! 698: diffie_hellman_group_names, this->dh_group);
! 699: return FAILED;
! 700: }
! 701: }
! 702:
! 703: /* generate nonce only when we are trying the first time */
! 704: if (this->my_nonce.ptr == NULL)
! 705: {
! 706: if (!generate_nonce(this))
! 707: {
! 708: return FAILED;
! 709: }
! 710: }
! 711:
! 712: if (this->cookie.ptr)
! 713: {
! 714: message->add_notify(message, FALSE, COOKIE, this->cookie);
! 715: }
! 716:
! 717: if (!build_payloads(this, message))
! 718: {
! 719: return FAILED;
! 720: }
! 721:
! 722: #ifdef ME
! 723: {
! 724: chunk_t connect_id = this->ike_sa->get_connect_id(this->ike_sa);
! 725: if (connect_id.ptr)
! 726: {
! 727: message->add_notify(message, FALSE, ME_CONNECTID, connect_id);
! 728: }
! 729: }
! 730: #endif /* ME */
! 731:
! 732: return NEED_MORE;
! 733: }
! 734:
! 735: METHOD(task_t, process_r, status_t,
! 736: private_ike_init_t *this, message_t *message)
! 737: {
! 738: DBG0(DBG_IKE, "%H is initiating an IKE_SA", message->get_source(message));
! 739: this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
! 740:
! 741: if (!generate_nonce(this))
! 742: {
! 743: return FAILED;
! 744: }
! 745:
! 746: #ifdef ME
! 747: {
! 748: notify_payload_t *notify = message->get_notify(message, ME_CONNECTID);
! 749: if (notify)
! 750: {
! 751: chunk_t connect_id = notify->get_notification_data(notify);
! 752: DBG2(DBG_IKE, "received ME_CONNECTID %#B", &connect_id);
! 753: charon->connect_manager->stop_checks(charon->connect_manager,
! 754: connect_id);
! 755: }
! 756: }
! 757: #endif /* ME */
! 758:
! 759: process_payloads(this, message);
! 760:
! 761: return NEED_MORE;
! 762: }
! 763:
! 764: /**
! 765: * Derive the keymat for the IKE_SA
! 766: */
! 767: static bool derive_keys(private_ike_init_t *this,
! 768: chunk_t nonce_i, chunk_t nonce_r)
! 769: {
! 770: keymat_v2_t *old_keymat;
! 771: pseudo_random_function_t prf_alg = PRF_UNDEFINED;
! 772: chunk_t skd = chunk_empty;
! 773: ike_sa_id_t *id;
! 774:
! 775: id = this->ike_sa->get_id(this->ike_sa);
! 776: if (this->old_sa)
! 777: {
! 778: /* rekeying: Include old SKd, use old PRF, apply SPI */
! 779: old_keymat = (keymat_v2_t*)this->old_sa->get_keymat(this->old_sa);
! 780: prf_alg = old_keymat->get_skd(old_keymat, &skd);
! 781: if (this->initiator)
! 782: {
! 783: id->set_responder_spi(id, this->proposal->get_spi(this->proposal));
! 784: }
! 785: else
! 786: {
! 787: id->set_initiator_spi(id, this->proposal->get_spi(this->proposal));
! 788: }
! 789: }
! 790: if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, this->dh,
! 791: nonce_i, nonce_r, id, prf_alg, skd))
! 792: {
! 793: return FALSE;
! 794: }
! 795: charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, chunk_empty,
! 796: nonce_i, nonce_r, this->old_sa, NULL, AUTH_NONE);
! 797: return TRUE;
! 798: }
! 799:
! 800: METHOD(task_t, build_r, status_t,
! 801: private_ike_init_t *this, message_t *message)
! 802: {
! 803: identification_t *gateway;
! 804:
! 805: /* check if we have everything we need */
! 806: if (this->proposal == NULL ||
! 807: this->other_nonce.len == 0 || this->my_nonce.len == 0)
! 808: {
! 809: DBG1(DBG_IKE, "received proposals unacceptable");
! 810: message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
! 811: return FAILED;
! 812: }
! 813:
! 814: /* check if we'd have to redirect the client */
! 815: if (!this->old_sa &&
! 816: this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) &&
! 817: charon->redirect->redirect_on_init(charon->redirect, this->ike_sa,
! 818: &gateway))
! 819: {
! 820: chunk_t data;
! 821:
! 822: DBG1(DBG_IKE, "redirecting peer to %Y", gateway);
! 823: data = redirect_data_create(gateway, this->other_nonce);
! 824: message->add_notify(message, TRUE, REDIRECT, data);
! 825: gateway->destroy(gateway);
! 826: chunk_free(&data);
! 827: return FAILED;
! 828: }
! 829:
! 830: if (this->dh == NULL ||
! 831: !this->proposal->has_dh_group(this->proposal, this->dh_group))
! 832: {
! 833: uint16_t group;
! 834:
! 835: if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP,
! 836: &group, NULL))
! 837: {
! 838: DBG1(DBG_IKE, "DH group %N unacceptable, requesting %N",
! 839: diffie_hellman_group_names, this->dh_group,
! 840: diffie_hellman_group_names, group);
! 841: this->dh_group = group;
! 842: group = htons(group);
! 843: message->add_notify(message, FALSE, INVALID_KE_PAYLOAD,
! 844: chunk_from_thing(group));
! 845: }
! 846: else
! 847: {
! 848: DBG1(DBG_IKE, "no acceptable proposal found");
! 849: message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
! 850: }
! 851: return FAILED;
! 852: }
! 853:
! 854: if (this->dh_failed)
! 855: {
! 856: DBG1(DBG_IKE, "applying DH public value failed");
! 857: message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
! 858: return FAILED;
! 859: }
! 860:
! 861: if (!derive_keys(this, this->other_nonce, this->my_nonce))
! 862: {
! 863: DBG1(DBG_IKE, "key derivation failed");
! 864: message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
! 865: return FAILED;
! 866: }
! 867: if (!build_payloads(this, message))
! 868: {
! 869: message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
! 870: return FAILED;
! 871: }
! 872: return SUCCESS;
! 873: }
! 874:
! 875: /**
! 876: * Raise alerts for received notify errors
! 877: */
! 878: static void raise_alerts(private_ike_init_t *this, notify_type_t type)
! 879: {
! 880: ike_cfg_t *ike_cfg;
! 881: linked_list_t *list;
! 882:
! 883: switch (type)
! 884: {
! 885: case NO_PROPOSAL_CHOSEN:
! 886: ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
! 887: list = ike_cfg->get_proposals(ike_cfg);
! 888: charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE, list);
! 889: list->destroy_offset(list, offsetof(proposal_t, destroy));
! 890: break;
! 891: default:
! 892: break;
! 893: }
! 894: }
! 895:
! 896: METHOD(task_t, pre_process_i, status_t,
! 897: private_ike_init_t *this, message_t *message)
! 898: {
! 899: enumerator_t *enumerator;
! 900: payload_t *payload;
! 901:
! 902: /* check for erroneous notifies */
! 903: enumerator = message->create_payload_enumerator(message);
! 904: while (enumerator->enumerate(enumerator, &payload))
! 905: {
! 906: if (payload->get_type(payload) == PLV2_NOTIFY)
! 907: {
! 908: notify_payload_t *notify = (notify_payload_t*)payload;
! 909: notify_type_t type = notify->get_notify_type(notify);
! 910:
! 911: switch (type)
! 912: {
! 913: case COOKIE:
! 914: {
! 915: chunk_t cookie;
! 916:
! 917: cookie = notify->get_notification_data(notify);
! 918: if (chunk_equals(cookie, this->cookie))
! 919: {
! 920: DBG1(DBG_IKE, "ignore response with duplicate COOKIE "
! 921: "notify");
! 922: enumerator->destroy(enumerator);
! 923: return FAILED;
! 924: }
! 925: break;
! 926: }
! 927: case REDIRECT:
! 928: {
! 929: identification_t *gateway;
! 930: chunk_t data, nonce = chunk_empty;
! 931: status_t status = SUCCESS;
! 932:
! 933: if (this->old_sa)
! 934: {
! 935: break;
! 936: }
! 937: data = notify->get_notification_data(notify);
! 938: gateway = redirect_data_parse(data, &nonce);
! 939: if (!gateway || !chunk_equals(nonce, this->my_nonce))
! 940: {
! 941: DBG1(DBG_IKE, "received invalid REDIRECT notify");
! 942: status = FAILED;
! 943: }
! 944: DESTROY_IF(gateway);
! 945: chunk_free(&nonce);
! 946: enumerator->destroy(enumerator);
! 947: return status;
! 948: }
! 949: default:
! 950: break;
! 951: }
! 952: }
! 953: }
! 954: enumerator->destroy(enumerator);
! 955: return SUCCESS;
! 956: }
! 957:
! 958: METHOD(task_t, process_i, status_t,
! 959: private_ike_init_t *this, message_t *message)
! 960: {
! 961: enumerator_t *enumerator;
! 962: payload_t *payload;
! 963:
! 964: /* check for erroneous notifies */
! 965: enumerator = message->create_payload_enumerator(message);
! 966: while (enumerator->enumerate(enumerator, &payload))
! 967: {
! 968: if (payload->get_type(payload) == PLV2_NOTIFY)
! 969: {
! 970: notify_payload_t *notify = (notify_payload_t*)payload;
! 971: notify_type_t type = notify->get_notify_type(notify);
! 972:
! 973: switch (type)
! 974: {
! 975: case INVALID_KE_PAYLOAD:
! 976: {
! 977: chunk_t data;
! 978: diffie_hellman_group_t bad_group;
! 979:
! 980: bad_group = this->dh_group;
! 981: data = notify->get_notification_data(notify);
! 982: this->dh_group = ntohs(*((uint16_t*)data.ptr));
! 983: DBG1(DBG_IKE, "peer didn't accept DH group %N, "
! 984: "it requested %N", diffie_hellman_group_names,
! 985: bad_group, diffie_hellman_group_names, this->dh_group);
! 986:
! 987: if (this->old_sa == NULL)
! 988: { /* reset the IKE_SA if we are not rekeying */
! 989: this->ike_sa->reset(this->ike_sa, FALSE);
! 990: }
! 991:
! 992: enumerator->destroy(enumerator);
! 993: this->retry++;
! 994: return NEED_MORE;
! 995: }
! 996: case NAT_DETECTION_SOURCE_IP:
! 997: case NAT_DETECTION_DESTINATION_IP:
! 998: /* skip, handled in ike_natd_t */
! 999: break;
! 1000: case MULTIPLE_AUTH_SUPPORTED:
! 1001: /* handled in ike_auth_t */
! 1002: break;
! 1003: case COOKIE:
! 1004: {
! 1005: chunk_free(&this->cookie);
! 1006: this->cookie = chunk_clone(notify->get_notification_data(notify));
! 1007: this->ike_sa->reset(this->ike_sa, FALSE);
! 1008: enumerator->destroy(enumerator);
! 1009: DBG2(DBG_IKE, "received %N notify", notify_type_names, type);
! 1010: this->retry++;
! 1011: return NEED_MORE;
! 1012: }
! 1013: case REDIRECT:
! 1014: {
! 1015: identification_t *gateway;
! 1016: chunk_t data, nonce = chunk_empty;
! 1017: status_t status = FAILED;
! 1018:
! 1019: if (this->old_sa)
! 1020: {
! 1021: DBG1(DBG_IKE, "received REDIRECT notify during rekeying"
! 1022: ", ignored");
! 1023: break;
! 1024: }
! 1025: data = notify->get_notification_data(notify);
! 1026: gateway = redirect_data_parse(data, &nonce);
! 1027: if (this->ike_sa->handle_redirect(this->ike_sa, gateway))
! 1028: {
! 1029: status = NEED_MORE;
! 1030: }
! 1031: DESTROY_IF(gateway);
! 1032: chunk_free(&nonce);
! 1033: enumerator->destroy(enumerator);
! 1034: return status;
! 1035: }
! 1036: default:
! 1037: {
! 1038: if (type <= 16383)
! 1039: {
! 1040: DBG1(DBG_IKE, "received %N notify error",
! 1041: notify_type_names, type);
! 1042: enumerator->destroy(enumerator);
! 1043: raise_alerts(this, type);
! 1044: return FAILED;
! 1045: }
! 1046: DBG2(DBG_IKE, "received %N notify",
! 1047: notify_type_names, type);
! 1048: break;
! 1049: }
! 1050: }
! 1051: }
! 1052: }
! 1053: enumerator->destroy(enumerator);
! 1054:
! 1055: process_payloads(this, message);
! 1056:
! 1057: /* check if we have everything */
! 1058: if (this->proposal == NULL ||
! 1059: this->other_nonce.len == 0 || this->my_nonce.len == 0)
! 1060: {
! 1061: DBG1(DBG_IKE, "peers proposal selection invalid");
! 1062: return FAILED;
! 1063: }
! 1064:
! 1065: if (this->dh == NULL ||
! 1066: !this->proposal->has_dh_group(this->proposal, this->dh_group))
! 1067: {
! 1068: DBG1(DBG_IKE, "peer DH group selection invalid");
! 1069: return FAILED;
! 1070: }
! 1071:
! 1072: if (this->dh_failed)
! 1073: {
! 1074: DBG1(DBG_IKE, "applying DH public value failed");
! 1075: return FAILED;
! 1076: }
! 1077:
! 1078: if (!derive_keys(this, this->my_nonce, this->other_nonce))
! 1079: {
! 1080: DBG1(DBG_IKE, "key derivation failed");
! 1081: return FAILED;
! 1082: }
! 1083: return SUCCESS;
! 1084: }
! 1085:
! 1086: METHOD(task_t, get_type, task_type_t,
! 1087: private_ike_init_t *this)
! 1088: {
! 1089: return TASK_IKE_INIT;
! 1090: }
! 1091:
! 1092: METHOD(task_t, migrate, void,
! 1093: private_ike_init_t *this, ike_sa_t *ike_sa)
! 1094: {
! 1095: DESTROY_IF(this->proposal);
! 1096: chunk_free(&this->other_nonce);
! 1097:
! 1098: this->ike_sa = ike_sa;
! 1099: this->keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa);
! 1100: this->proposal = NULL;
! 1101: this->dh_failed = FALSE;
! 1102: }
! 1103:
! 1104: METHOD(task_t, destroy, void,
! 1105: private_ike_init_t *this)
! 1106: {
! 1107: DESTROY_IF(this->dh);
! 1108: DESTROY_IF(this->proposal);
! 1109: DESTROY_IF(this->nonceg);
! 1110: chunk_free(&this->my_nonce);
! 1111: chunk_free(&this->other_nonce);
! 1112: chunk_free(&this->cookie);
! 1113: free(this);
! 1114: }
! 1115:
! 1116: METHOD(ike_init_t, get_lower_nonce, chunk_t,
! 1117: private_ike_init_t *this)
! 1118: {
! 1119: if (memcmp(this->my_nonce.ptr, this->other_nonce.ptr,
! 1120: min(this->my_nonce.len, this->other_nonce.len)) < 0)
! 1121: {
! 1122: return this->my_nonce;
! 1123: }
! 1124: else
! 1125: {
! 1126: return this->other_nonce;
! 1127: }
! 1128: }
! 1129:
! 1130: /*
! 1131: * Described in header.
! 1132: */
! 1133: ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
! 1134: {
! 1135: private_ike_init_t *this;
! 1136:
! 1137: INIT(this,
! 1138: .public = {
! 1139: .task = {
! 1140: .get_type = _get_type,
! 1141: .migrate = _migrate,
! 1142: .destroy = _destroy,
! 1143: },
! 1144: .get_lower_nonce = _get_lower_nonce,
! 1145: },
! 1146: .ike_sa = ike_sa,
! 1147: .initiator = initiator,
! 1148: .dh_group = MODP_NONE,
! 1149: .keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa),
! 1150: .old_sa = old_sa,
! 1151: .signature_authentication = lib->settings->get_bool(lib->settings,
! 1152: "%s.signature_authentication", TRUE, lib->ns),
! 1153: .follow_redirects = lib->settings->get_bool(lib->settings,
! 1154: "%s.follow_redirects", TRUE, lib->ns),
! 1155: );
! 1156: this->nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
! 1157:
! 1158: if (initiator)
! 1159: {
! 1160: this->public.task.build = _build_i;
! 1161: this->public.task.process = _process_i;
! 1162: this->public.task.pre_process = _pre_process_i;
! 1163: }
! 1164: else
! 1165: {
! 1166: this->public.task.build = _build_r;
! 1167: this->public.task.process = _process_r;
! 1168: }
! 1169: return &this->public;
! 1170: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>