Annotation of embedaddon/strongswan/src/libcharon/plugins/ha/ha_ike.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2008 Martin Willi
! 3: * HSR Hochschule fuer Technik Rapperswil
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify it
! 6: * under the terms of the GNU General Public License as published by the
! 7: * Free Software Foundation; either version 2 of the License, or (at your
! 8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 9: *
! 10: * This program is distributed in the hope that it will be useful, but
! 11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 13: * for more details.
! 14: */
! 15:
! 16: #include "ha_ike.h"
! 17:
! 18: #include <sa/ikev2/keymat_v2.h>
! 19: #include <sa/ikev1/keymat_v1.h>
! 20:
! 21: typedef struct private_ha_ike_t private_ha_ike_t;
! 22:
! 23: /**
! 24: * Private data of an ha_ike_t object.
! 25: */
! 26: struct private_ha_ike_t {
! 27:
! 28: /**
! 29: * Public ha_ike_t interface.
! 30: */
! 31: ha_ike_t public;
! 32:
! 33: /**
! 34: * socket we use for syncing
! 35: */
! 36: ha_socket_t *socket;
! 37:
! 38: /**
! 39: * tunnel securing sync messages
! 40: */
! 41: ha_tunnel_t *tunnel;
! 42:
! 43: /**
! 44: * message cache
! 45: */
! 46: ha_cache_t *cache;
! 47: };
! 48:
! 49: /**
! 50: * Return condition if it is set on ike_sa
! 51: */
! 52: static ike_condition_t copy_condition(ike_sa_t *ike_sa, ike_condition_t cond)
! 53: {
! 54: if (ike_sa->has_condition(ike_sa, cond))
! 55: {
! 56: return cond;
! 57: }
! 58: return 0;
! 59: }
! 60:
! 61: /**
! 62: * Return extension if it is supported by peers IKE_SA
! 63: */
! 64: static ike_extension_t copy_extension(ike_sa_t *ike_sa, ike_extension_t ext)
! 65: {
! 66: if (ike_sa->supports_extension(ike_sa, ext))
! 67: {
! 68: return ext;
! 69: }
! 70: return 0;
! 71: }
! 72:
! 73: METHOD(listener_t, ike_keys, bool,
! 74: private_ha_ike_t *this, ike_sa_t *ike_sa, diffie_hellman_t *dh,
! 75: chunk_t dh_other, chunk_t nonce_i, chunk_t nonce_r, ike_sa_t *rekey,
! 76: shared_key_t *shared, auth_method_t method)
! 77: {
! 78: ha_message_t *m;
! 79: chunk_t secret;
! 80: proposal_t *proposal;
! 81: uint16_t alg, len;
! 82:
! 83: if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa))
! 84: { /* do not sync SA between nodes */
! 85: return TRUE;
! 86: }
! 87: if (!dh->get_shared_secret(dh, &secret))
! 88: {
! 89: return TRUE;
! 90: }
! 91:
! 92: m = ha_message_create(HA_IKE_ADD);
! 93: m->add_attribute(m, HA_IKE_VERSION, ike_sa->get_version(ike_sa));
! 94: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
! 95:
! 96: if (rekey && rekey->get_version(rekey) == IKEV2)
! 97: {
! 98: chunk_t skd;
! 99: keymat_v2_t *keymat;
! 100:
! 101: keymat = (keymat_v2_t*)rekey->get_keymat(rekey);
! 102: m->add_attribute(m, HA_IKE_REKEY_ID, rekey->get_id(rekey));
! 103: m->add_attribute(m, HA_ALG_OLD_PRF, keymat->get_skd(keymat, &skd));
! 104: m->add_attribute(m, HA_OLD_SKD, skd);
! 105: }
! 106:
! 107: proposal = ike_sa->get_proposal(ike_sa);
! 108: if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &len))
! 109: {
! 110: m->add_attribute(m, HA_ALG_ENCR, alg);
! 111: if (len)
! 112: {
! 113: m->add_attribute(m, HA_ALG_ENCR_LEN, len);
! 114: }
! 115: }
! 116: if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL))
! 117: {
! 118: m->add_attribute(m, HA_ALG_INTEG, alg);
! 119: }
! 120: if (proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
! 121: {
! 122: m->add_attribute(m, HA_ALG_PRF, alg);
! 123: }
! 124: if (proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, &alg, NULL))
! 125: {
! 126: m->add_attribute(m, HA_ALG_DH, alg);
! 127: }
! 128: m->add_attribute(m, HA_NONCE_I, nonce_i);
! 129: m->add_attribute(m, HA_NONCE_R, nonce_r);
! 130: m->add_attribute(m, HA_SECRET, secret);
! 131: chunk_clear(&secret);
! 132: if (ike_sa->get_version(ike_sa) == IKEV1)
! 133: {
! 134: if (dh->get_my_public_value(dh, &secret))
! 135: {
! 136: m->add_attribute(m, HA_LOCAL_DH, secret);
! 137: chunk_free(&secret);
! 138: }
! 139: m->add_attribute(m, HA_REMOTE_DH, dh_other);
! 140: if (shared)
! 141: {
! 142: m->add_attribute(m, HA_PSK, shared->get_key(shared));
! 143: }
! 144: else
! 145: {
! 146: m->add_attribute(m, HA_AUTH_METHOD, method);
! 147: }
! 148: }
! 149: m->add_attribute(m, HA_REMOTE_ADDR, ike_sa->get_other_host(ike_sa));
! 150:
! 151: this->socket->push(this->socket, m);
! 152: this->cache->cache(this->cache, ike_sa, m);
! 153:
! 154: return TRUE;
! 155: }
! 156:
! 157: METHOD(listener_t, ike_updown, bool,
! 158: private_ha_ike_t *this, ike_sa_t *ike_sa, bool up)
! 159: {
! 160: ha_message_t *m;
! 161:
! 162: if (ike_sa->get_state(ike_sa) == IKE_PASSIVE)
! 163: { /* only sync active IKE_SAs */
! 164: return TRUE;
! 165: }
! 166: if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa))
! 167: { /* do not sync SA between nodes */
! 168: return TRUE;
! 169: }
! 170:
! 171: if (up)
! 172: {
! 173: enumerator_t *enumerator;
! 174: peer_cfg_t *peer_cfg;
! 175: uint32_t extension, condition;
! 176: host_t *addr;
! 177: ike_sa_id_t *id;
! 178: identification_t *eap_id;
! 179:
! 180: peer_cfg = ike_sa->get_peer_cfg(ike_sa);
! 181:
! 182: condition = copy_condition(ike_sa, COND_NAT_ANY)
! 183: | copy_condition(ike_sa, COND_NAT_HERE)
! 184: | copy_condition(ike_sa, COND_NAT_THERE)
! 185: | copy_condition(ike_sa, COND_NAT_FAKE)
! 186: | copy_condition(ike_sa, COND_EAP_AUTHENTICATED)
! 187: | copy_condition(ike_sa, COND_CERTREQ_SEEN)
! 188: | copy_condition(ike_sa, COND_ORIGINAL_INITIATOR)
! 189: | copy_condition(ike_sa, COND_STALE)
! 190: | copy_condition(ike_sa, COND_INIT_CONTACT_SEEN)
! 191: | copy_condition(ike_sa, COND_XAUTH_AUTHENTICATED);
! 192:
! 193: extension = copy_extension(ike_sa, EXT_NATT)
! 194: | copy_extension(ike_sa, EXT_MOBIKE)
! 195: | copy_extension(ike_sa, EXT_HASH_AND_URL)
! 196: | copy_extension(ike_sa, EXT_MULTIPLE_AUTH)
! 197: | copy_extension(ike_sa, EXT_STRONGSWAN)
! 198: | copy_extension(ike_sa, EXT_EAP_ONLY_AUTHENTICATION)
! 199: | copy_extension(ike_sa, EXT_MS_WINDOWS)
! 200: | copy_extension(ike_sa, EXT_XAUTH)
! 201: | copy_extension(ike_sa, EXT_DPD);
! 202:
! 203: id = ike_sa->get_id(ike_sa);
! 204:
! 205: m = ha_message_create(HA_IKE_UPDATE);
! 206: m->add_attribute(m, HA_IKE_ID, id);
! 207: m->add_attribute(m, HA_LOCAL_ID, ike_sa->get_my_id(ike_sa));
! 208: m->add_attribute(m, HA_REMOTE_ID, ike_sa->get_other_id(ike_sa));
! 209: eap_id = ike_sa->get_other_eap_id(ike_sa);
! 210: if (!eap_id->equals(eap_id, ike_sa->get_other_id(ike_sa)))
! 211: {
! 212: m->add_attribute(m, HA_REMOTE_EAP_ID, eap_id);
! 213: }
! 214: m->add_attribute(m, HA_LOCAL_ADDR, ike_sa->get_my_host(ike_sa));
! 215: m->add_attribute(m, HA_REMOTE_ADDR, ike_sa->get_other_host(ike_sa));
! 216: m->add_attribute(m, HA_CONDITIONS, condition);
! 217: m->add_attribute(m, HA_EXTENSIONS, extension);
! 218: m->add_attribute(m, HA_CONFIG_NAME, peer_cfg->get_name(peer_cfg));
! 219: enumerator = ike_sa->create_peer_address_enumerator(ike_sa);
! 220: while (enumerator->enumerate(enumerator, (void**)&addr))
! 221: {
! 222: m->add_attribute(m, HA_PEER_ADDR, addr);
! 223: }
! 224: enumerator->destroy(enumerator);
! 225: }
! 226: else
! 227: {
! 228: m = ha_message_create(HA_IKE_DELETE);
! 229: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
! 230: }
! 231: this->socket->push(this->socket, m);
! 232: this->cache->cache(this->cache, ike_sa, m);
! 233: return TRUE;
! 234: }
! 235:
! 236: METHOD(listener_t, ike_rekey, bool,
! 237: private_ha_ike_t *this, ike_sa_t *old, ike_sa_t *new)
! 238: {
! 239: ike_updown(this, old, FALSE);
! 240: ike_updown(this, new, TRUE);
! 241: return TRUE;
! 242: }
! 243:
! 244: METHOD(listener_t, alert, bool,
! 245: private_ha_ike_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
! 246: {
! 247: switch (alert)
! 248: {
! 249: case ALERT_HALF_OPEN_TIMEOUT:
! 250: ike_updown(this, ike_sa, FALSE);
! 251: break;
! 252: default:
! 253: break;
! 254: }
! 255: return TRUE;
! 256: }
! 257:
! 258: METHOD(listener_t, ike_state_change, bool,
! 259: private_ha_ike_t *this, ike_sa_t *ike_sa, ike_sa_state_t new)
! 260: {
! 261: /* delete any remaining cache entry if IKE_SA gets destroyed */
! 262: if (new == IKE_DESTROYING)
! 263: {
! 264: this->cache->delete(this->cache, ike_sa);
! 265: }
! 266: return TRUE;
! 267: }
! 268:
! 269: /**
! 270: * Send a virtual IP sync message for remote VIPs
! 271: */
! 272: static void sync_vips(private_ha_ike_t *this, ike_sa_t *ike_sa)
! 273: {
! 274: ha_message_t *m = NULL;
! 275: enumerator_t *enumerator;
! 276: host_t *vip;
! 277:
! 278: enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
! 279: while (enumerator->enumerate(enumerator, &vip))
! 280: {
! 281: if (!m)
! 282: {
! 283: m = ha_message_create(HA_IKE_UPDATE);
! 284: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
! 285: }
! 286: m->add_attribute(m, HA_REMOTE_VIP, vip);
! 287: }
! 288: enumerator->destroy(enumerator);
! 289:
! 290: if (m)
! 291: {
! 292: this->socket->push(this->socket, m);
! 293: this->cache->cache(this->cache, ike_sa, m);
! 294: }
! 295: }
! 296:
! 297: METHOD(listener_t, message_hook, bool,
! 298: private_ha_ike_t *this, ike_sa_t *ike_sa, message_t *message,
! 299: bool incoming, bool plain)
! 300: {
! 301: if (this->tunnel && this->tunnel->is_sa(this->tunnel, ike_sa))
! 302: { /* do not sync SA between nodes */
! 303: return TRUE;
! 304: }
! 305:
! 306: if (plain && ike_sa->get_version(ike_sa) == IKEV2)
! 307: {
! 308: if (message->get_exchange_type(message) != IKE_SA_INIT &&
! 309: message->get_request(message))
! 310: { /* we sync on requests, but skip it on IKE_SA_INIT */
! 311: ha_message_t *m;
! 312:
! 313: if (incoming)
! 314: {
! 315: m = ha_message_create(HA_IKE_MID_RESPONDER);
! 316: }
! 317: else
! 318: {
! 319: m = ha_message_create(HA_IKE_MID_INITIATOR);
! 320: }
! 321: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
! 322: m->add_attribute(m, HA_MID, message->get_message_id(message) + 1);
! 323: this->socket->push(this->socket, m);
! 324: this->cache->cache(this->cache, ike_sa, m);
! 325: }
! 326: if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
! 327: message->get_exchange_type(message) == IKE_AUTH &&
! 328: !message->get_request(message))
! 329: { /* After IKE_SA has been established, sync peers virtual IP.
! 330: * We cannot sync it in the state_change hook, it is installed later.
! 331: * TODO: where to sync local VIP? */
! 332: sync_vips(this, ike_sa);
! 333: }
! 334: }
! 335: if (ike_sa->get_version(ike_sa) == IKEV1)
! 336: {
! 337: ha_message_t *m;
! 338: keymat_v1_t *keymat;
! 339: chunk_t iv;
! 340:
! 341: /* we need the last block (or expected next IV) of Phase 1, which gets
! 342: * updated after successful en-/decryption depending on direction */
! 343: if (incoming == plain)
! 344: {
! 345: if (message->get_message_id(message) == 0)
! 346: {
! 347: keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
! 348: if (keymat->get_iv(keymat, 0, &iv))
! 349: {
! 350: m = ha_message_create(HA_IKE_IV);
! 351: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
! 352: m->add_attribute(m, HA_IV, iv);
! 353: this->socket->push(this->socket, m);
! 354: this->cache->cache(this->cache, ike_sa, m);
! 355: }
! 356: }
! 357: }
! 358: if (!plain && !incoming &&
! 359: message->get_exchange_type(message) == TRANSACTION)
! 360: {
! 361: sync_vips(this, ike_sa);
! 362: }
! 363: }
! 364: if (plain && ike_sa->get_version(ike_sa) == IKEV1 &&
! 365: message->get_exchange_type(message) == INFORMATIONAL_V1)
! 366: {
! 367: ha_message_t *m;
! 368: notify_payload_t *notify;
! 369: chunk_t data;
! 370: uint32_t seq;
! 371:
! 372: notify = message->get_notify(message, DPD_R_U_THERE);
! 373: if (notify)
! 374: {
! 375: data = notify->get_notification_data(notify);
! 376: if (data.len == 4)
! 377: {
! 378: seq = untoh32(data.ptr);
! 379: if (incoming)
! 380: {
! 381: m = ha_message_create(HA_IKE_MID_RESPONDER);
! 382: }
! 383: else
! 384: {
! 385: m = ha_message_create(HA_IKE_MID_INITIATOR);
! 386: }
! 387: m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
! 388: m->add_attribute(m, HA_MID, seq + 1);
! 389: this->socket->push(this->socket, m);
! 390: this->cache->cache(this->cache, ike_sa, m);
! 391: }
! 392: }
! 393: }
! 394: return TRUE;
! 395: }
! 396:
! 397: METHOD(ha_ike_t, destroy, void,
! 398: private_ha_ike_t *this)
! 399: {
! 400: free(this);
! 401: }
! 402:
! 403: /**
! 404: * See header
! 405: */
! 406: ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel,
! 407: ha_cache_t *cache)
! 408: {
! 409: private_ha_ike_t *this;
! 410:
! 411: INIT(this,
! 412: .public = {
! 413: .listener = {
! 414: .alert = _alert,
! 415: .ike_keys = _ike_keys,
! 416: .ike_updown = _ike_updown,
! 417: .ike_rekey = _ike_rekey,
! 418: .ike_state_change = _ike_state_change,
! 419: .message = _message_hook,
! 420: },
! 421: .destroy = _destroy,
! 422: },
! 423: .socket = socket,
! 424: .tunnel = tunnel,
! 425: .cache = cache,
! 426: );
! 427:
! 428: return &this->public;
! 429: }
! 430:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>