Annotation of embedaddon/strongswan/src/libcharon/sa/ikev2/tasks/ike_rekey.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2015-2016 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_rekey.h"
! 19:
! 20: #include <daemon.h>
! 21: #include <encoding/payloads/notify_payload.h>
! 22: #include <sa/ikev2/tasks/ike_init.h>
! 23: #include <sa/ikev2/tasks/ike_delete.h>
! 24: #include <processing/jobs/delete_ike_sa_job.h>
! 25: #include <processing/jobs/rekey_ike_sa_job.h>
! 26: #include <processing/jobs/initiate_tasks_job.h>
! 27:
! 28:
! 29: typedef struct private_ike_rekey_t private_ike_rekey_t;
! 30:
! 31: /**
! 32: * Private members of a ike_rekey_t task.
! 33: */
! 34: struct private_ike_rekey_t {
! 35:
! 36: /**
! 37: * Public methods and task_t interface.
! 38: */
! 39: ike_rekey_t public;
! 40:
! 41: /**
! 42: * Assigned IKE_SA.
! 43: */
! 44: ike_sa_t *ike_sa;
! 45:
! 46: /**
! 47: * New IKE_SA which replaces the current one
! 48: */
! 49: ike_sa_t *new_sa;
! 50:
! 51: /**
! 52: * Are we the initiator?
! 53: */
! 54: bool initiator;
! 55:
! 56: /**
! 57: * the TASK_IKE_INIT task which is reused to simplify rekeying
! 58: */
! 59: ike_init_t *ike_init;
! 60:
! 61: /**
! 62: * IKE_DELETE task to delete the old IKE_SA after rekeying was successful
! 63: */
! 64: ike_delete_t *ike_delete;
! 65:
! 66: /**
! 67: * colliding task detected by the task manager
! 68: */
! 69: task_t *collision;
! 70:
! 71: /**
! 72: * TRUE if rekeying can't be handled temporarily
! 73: */
! 74: bool failed_temporarily;
! 75: };
! 76:
! 77: /**
! 78: * Schedule a retry if rekeying temporary failed
! 79: */
! 80: static void schedule_delayed_rekey(private_ike_rekey_t *this)
! 81: {
! 82: uint32_t retry;
! 83: job_t *job;
! 84:
! 85: retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
! 86: job = (job_t*)rekey_ike_sa_job_create(
! 87: this->ike_sa->get_id(this->ike_sa), FALSE);
! 88: DBG1(DBG_IKE, "IKE_SA rekeying failed, trying again in %d seconds", retry);
! 89: this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
! 90: lib->scheduler->schedule_job(lib->scheduler, job, retry);
! 91: }
! 92:
! 93: /**
! 94: * Check if an IKE_SA has any queued tasks, return initiation job
! 95: */
! 96: static job_t* check_queued_tasks(ike_sa_t *ike_sa)
! 97: {
! 98: enumerator_t *enumerator;
! 99: task_t *task;
! 100: job_t *job = NULL;
! 101:
! 102: enumerator = ike_sa->create_task_enumerator(ike_sa, TASK_QUEUE_QUEUED);
! 103: if (enumerator->enumerate(enumerator, &task))
! 104: {
! 105: job = (job_t*)initiate_tasks_job_create(ike_sa->get_id(ike_sa));
! 106: }
! 107: enumerator->destroy(enumerator);
! 108: return job;
! 109: }
! 110:
! 111: /**
! 112: * Establish the new replacement IKE_SA
! 113: */
! 114: static void establish_new(private_ike_rekey_t *this)
! 115: {
! 116: if (this->new_sa)
! 117: {
! 118: job_t *job;
! 119:
! 120: this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
! 121: DBG0(DBG_IKE, "IKE_SA %s[%d] rekeyed between %H[%Y]...%H[%Y]",
! 122: this->new_sa->get_name(this->new_sa),
! 123: this->new_sa->get_unique_id(this->new_sa),
! 124: this->ike_sa->get_my_host(this->ike_sa),
! 125: this->ike_sa->get_my_id(this->ike_sa),
! 126: this->ike_sa->get_other_host(this->ike_sa),
! 127: this->ike_sa->get_other_id(this->ike_sa));
! 128:
! 129: this->new_sa->inherit_post(this->new_sa, this->ike_sa);
! 130: charon->bus->ike_rekey(charon->bus, this->ike_sa, this->new_sa);
! 131: job = check_queued_tasks(this->new_sa);
! 132: /* don't queue job before checkin(), as the IKE_SA is not yet
! 133: * registered at the manager */
! 134: charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
! 135: if (job)
! 136: {
! 137: lib->processor->queue_job(lib->processor, job);
! 138: }
! 139: this->new_sa = NULL;
! 140: charon->bus->set_sa(charon->bus, this->ike_sa);
! 141:
! 142: this->ike_sa->set_state(this->ike_sa, IKE_REKEYED);
! 143: }
! 144: }
! 145:
! 146: METHOD(task_t, build_i_delete, status_t,
! 147: private_ike_rekey_t *this, message_t *message)
! 148: {
! 149: /* update exchange type to INFORMATIONAL for the delete */
! 150: message->set_exchange_type(message, INFORMATIONAL);
! 151:
! 152: return this->ike_delete->task.build(&this->ike_delete->task, message);
! 153: }
! 154:
! 155: METHOD(task_t, process_i_delete, status_t,
! 156: private_ike_rekey_t *this, message_t *message)
! 157: {
! 158: return this->ike_delete->task.process(&this->ike_delete->task, message);
! 159: }
! 160:
! 161: METHOD(task_t, build_i, status_t,
! 162: private_ike_rekey_t *this, message_t *message)
! 163: {
! 164: ike_version_t version;
! 165:
! 166: /* create new SA only on first try */
! 167: if (this->new_sa == NULL)
! 168: {
! 169: version = this->ike_sa->get_version(this->ike_sa);
! 170: this->new_sa = charon->ike_sa_manager->checkout_new(
! 171: charon->ike_sa_manager, version, TRUE);
! 172: if (!this->new_sa)
! 173: { /* shouldn't happen */
! 174: return FAILED;
! 175: }
! 176: this->new_sa->inherit_pre(this->new_sa, this->ike_sa);
! 177: this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa);
! 178: this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
! 179: }
! 180: this->ike_init->task.build(&this->ike_init->task, message);
! 181:
! 182: return NEED_MORE;
! 183: }
! 184:
! 185: /**
! 186: * Check if there are any half-open children
! 187: */
! 188: static bool have_half_open_children(private_ike_rekey_t *this)
! 189: {
! 190: enumerator_t *enumerator;
! 191: child_sa_t *child_sa;
! 192: task_t *task;
! 193:
! 194: enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
! 195: while (enumerator->enumerate(enumerator, (void**)&child_sa))
! 196: {
! 197: switch (child_sa->get_state(child_sa))
! 198: {
! 199: case CHILD_REKEYING:
! 200: case CHILD_RETRYING:
! 201: case CHILD_DELETING:
! 202: enumerator->destroy(enumerator);
! 203: return TRUE;
! 204: default:
! 205: break;
! 206: }
! 207: }
! 208: enumerator->destroy(enumerator);
! 209: enumerator = this->ike_sa->create_task_enumerator(this->ike_sa,
! 210: TASK_QUEUE_ACTIVE);
! 211: while (enumerator->enumerate(enumerator, (void**)&task))
! 212: {
! 213: if (task->get_type(task) == TASK_CHILD_CREATE)
! 214: {
! 215: enumerator->destroy(enumerator);
! 216: return TRUE;
! 217: }
! 218: }
! 219: enumerator->destroy(enumerator);
! 220: return FALSE;
! 221: }
! 222:
! 223: METHOD(task_t, process_r, status_t,
! 224: private_ike_rekey_t *this, message_t *message)
! 225: {
! 226: if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
! 227: {
! 228: DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting");
! 229: this->failed_temporarily = TRUE;
! 230: return NEED_MORE;
! 231: }
! 232: if (have_half_open_children(this))
! 233: {
! 234: DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open");
! 235: this->failed_temporarily = TRUE;
! 236: return NEED_MORE;
! 237: }
! 238:
! 239: this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
! 240: this->ike_sa->get_version(this->ike_sa), FALSE);
! 241: if (!this->new_sa)
! 242: { /* shouldn't happen */
! 243: return FAILED;
! 244: }
! 245: this->new_sa->inherit_pre(this->new_sa, this->ike_sa);
! 246: this->ike_init = ike_init_create(this->new_sa, FALSE, this->ike_sa);
! 247: this->ike_init->task.process(&this->ike_init->task, message);
! 248:
! 249: return NEED_MORE;
! 250: }
! 251:
! 252: METHOD(task_t, build_r, status_t,
! 253: private_ike_rekey_t *this, message_t *message)
! 254: {
! 255: if (this->failed_temporarily)
! 256: {
! 257: message->add_notify(message, TRUE, TEMPORARY_FAILURE, chunk_empty);
! 258: return SUCCESS;
! 259: }
! 260: if (this->new_sa == NULL)
! 261: {
! 262: /* IKE_SA/a CHILD_SA is in an unacceptable state, deny rekeying */
! 263: message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
! 264: return SUCCESS;
! 265: }
! 266: if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED)
! 267: {
! 268: this->ike_init->task.destroy(&this->ike_init->task);
! 269: this->ike_init = NULL;
! 270: charon->bus->set_sa(charon->bus, this->ike_sa);
! 271: return SUCCESS;
! 272: }
! 273: charon->bus->set_sa(charon->bus, this->ike_sa);
! 274:
! 275: if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING)
! 276: { /* in case of a collision we let the initiating task handle this */
! 277: establish_new(this);
! 278: /* make sure the IKE_SA is gone in case the peer fails to delete it */
! 279: lib->scheduler->schedule_job(lib->scheduler, (job_t*)
! 280: delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE),
! 281: 90);
! 282: }
! 283: return SUCCESS;
! 284: }
! 285:
! 286: /**
! 287: * Conclude any undetected rekey collision.
! 288: *
! 289: * If the peer does not detect the collision it will delete this IKE_SA.
! 290: * Depending on when our request reaches the peer and we receive the delete
! 291: * this may get called at different times.
! 292: *
! 293: * Returns TRUE if there was a collision, FALSE otherwise.
! 294: */
! 295: static bool conclude_undetected_collision(private_ike_rekey_t *this)
! 296: {
! 297: if (this->collision &&
! 298: this->collision->get_type(this->collision) == TASK_IKE_REKEY)
! 299: {
! 300: DBG1(DBG_IKE, "peer did not notice IKE_SA rekey collision, abort "
! 301: "active rekeying");
! 302: establish_new((private_ike_rekey_t*)this->collision);
! 303: return TRUE;
! 304: }
! 305: return FALSE;
! 306: }
! 307:
! 308: METHOD(task_t, process_i, status_t,
! 309: private_ike_rekey_t *this, message_t *message)
! 310: {
! 311: if (message->get_notify(message, NO_ADDITIONAL_SAS))
! 312: {
! 313: DBG1(DBG_IKE, "peer seems to not support IKE rekeying, "
! 314: "starting reauthentication");
! 315: this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
! 316: lib->processor->queue_job(lib->processor,
! 317: (job_t*)rekey_ike_sa_job_create(
! 318: this->ike_sa->get_id(this->ike_sa), TRUE));
! 319: return SUCCESS;
! 320: }
! 321:
! 322: switch (this->ike_init->task.process(&this->ike_init->task, message))
! 323: {
! 324: case FAILED:
! 325: /* rekeying failed, fallback to old SA */
! 326: if (!conclude_undetected_collision(this))
! 327: {
! 328: schedule_delayed_rekey(this);
! 329: }
! 330: return SUCCESS;
! 331: case NEED_MORE:
! 332: /* bad dh group, try again */
! 333: this->ike_init->task.migrate(&this->ike_init->task, this->new_sa);
! 334: return NEED_MORE;
! 335: default:
! 336: break;
! 337: }
! 338:
! 339: /* check for collisions */
! 340: if (this->collision &&
! 341: this->collision->get_type(this->collision) == TASK_IKE_REKEY)
! 342: {
! 343: private_ike_rekey_t *other = (private_ike_rekey_t*)this->collision;
! 344: host_t *host;
! 345: chunk_t this_nonce, other_nonce;
! 346:
! 347: this_nonce = this->ike_init->get_lower_nonce(this->ike_init);
! 348: other_nonce = other->ike_init->get_lower_nonce(other->ike_init);
! 349:
! 350: /* if we have the lower nonce, delete rekeyed SA. If not, delete
! 351: * the redundant. */
! 352: if (memcmp(this_nonce.ptr, other_nonce.ptr,
! 353: min(this_nonce.len, other_nonce.len)) < 0)
! 354: {
! 355: DBG1(DBG_IKE, "IKE_SA rekey collision lost, deleting redundant "
! 356: "IKE_SA %s[%d]", this->new_sa->get_name(this->new_sa),
! 357: this->new_sa->get_unique_id(this->new_sa));
! 358: /* apply host for a proper delete */
! 359: host = this->ike_sa->get_my_host(this->ike_sa);
! 360: this->new_sa->set_my_host(this->new_sa, host->clone(host));
! 361: host = this->ike_sa->get_other_host(this->ike_sa);
! 362: this->new_sa->set_other_host(this->new_sa, host->clone(host));
! 363: /* IKE_SAs in state IKE_REKEYED are silently deleted, so we use
! 364: * IKE_REKEYING */
! 365: this->new_sa->set_state(this->new_sa, IKE_REKEYING);
! 366: if (this->new_sa->delete(this->new_sa, FALSE) == DESTROY_ME)
! 367: {
! 368: this->new_sa->destroy(this->new_sa);
! 369: }
! 370: else
! 371: {
! 372: charon->ike_sa_manager->checkin(charon->ike_sa_manager,
! 373: this->new_sa);
! 374: }
! 375: charon->bus->set_sa(charon->bus, this->ike_sa);
! 376: this->new_sa = NULL;
! 377: establish_new(other);
! 378: return SUCCESS;
! 379: }
! 380: /* peer should delete this SA. Add a timeout just in case. */
! 381: job_t *job = (job_t*)delete_ike_sa_job_create(
! 382: other->new_sa->get_id(other->new_sa), TRUE);
! 383: lib->scheduler->schedule_job(lib->scheduler, job,
! 384: HALF_OPEN_IKE_SA_TIMEOUT);
! 385: DBG1(DBG_IKE, "IKE_SA rekey collision won, waiting for delete for "
! 386: "redundant IKE_SA %s[%d]", other->new_sa->get_name(other->new_sa),
! 387: other->new_sa->get_unique_id(other->new_sa));
! 388: other->new_sa->set_state(other->new_sa, IKE_REKEYED);
! 389: charon->ike_sa_manager->checkin(charon->ike_sa_manager, other->new_sa);
! 390: other->new_sa = NULL;
! 391: charon->bus->set_sa(charon->bus, this->ike_sa);
! 392: }
! 393:
! 394: establish_new(this);
! 395:
! 396: /* rekeying successful, delete the IKE_SA using a subtask */
! 397: this->ike_delete = ike_delete_create(this->ike_sa, TRUE);
! 398: this->public.task.build = _build_i_delete;
! 399: this->public.task.process = _process_i_delete;
! 400:
! 401: return NEED_MORE;
! 402: }
! 403:
! 404: METHOD(task_t, get_type, task_type_t,
! 405: private_ike_rekey_t *this)
! 406: {
! 407: return TASK_IKE_REKEY;
! 408: }
! 409:
! 410: METHOD(ike_rekey_t, did_collide, bool,
! 411: private_ike_rekey_t *this)
! 412: {
! 413: return this->collision &&
! 414: this->collision->get_type(this->collision) == TASK_IKE_REKEY;
! 415: }
! 416:
! 417: METHOD(ike_rekey_t, collide, void,
! 418: private_ike_rekey_t* this, task_t *other)
! 419: {
! 420: DBG1(DBG_IKE, "detected %N collision with %N", task_type_names,
! 421: TASK_IKE_REKEY, task_type_names, other->get_type(other));
! 422:
! 423: switch (other->get_type(other))
! 424: {
! 425: case TASK_IKE_DELETE:
! 426: conclude_undetected_collision(this);
! 427: other->destroy(other);
! 428: return;
! 429: case TASK_IKE_REKEY:
! 430: {
! 431: private_ike_rekey_t *rekey = (private_ike_rekey_t*)other;
! 432:
! 433: if (!rekey->ike_init)
! 434: {
! 435: DBG1(DBG_IKE, "colliding exchange did not result in an IKE_SA, "
! 436: "ignore");
! 437: other->destroy(other);
! 438: return;
! 439: }
! 440: break;
! 441: }
! 442: default:
! 443: break;
! 444: }
! 445: DESTROY_IF(this->collision);
! 446: this->collision = other;
! 447: }
! 448:
! 449: /**
! 450: * Cleanup the task
! 451: */
! 452: static void cleanup(private_ike_rekey_t *this)
! 453: {
! 454: ike_sa_t *cur_sa;
! 455:
! 456: if (this->ike_init)
! 457: {
! 458: this->ike_init->task.destroy(&this->ike_init->task);
! 459: }
! 460: if (this->ike_delete)
! 461: {
! 462: this->ike_delete->task.destroy(&this->ike_delete->task);
! 463: }
! 464: cur_sa = charon->bus->get_sa(charon->bus);
! 465: DESTROY_IF(this->new_sa);
! 466: charon->bus->set_sa(charon->bus, cur_sa);
! 467: DESTROY_IF(this->collision);
! 468: }
! 469:
! 470: METHOD(task_t, migrate, void,
! 471: private_ike_rekey_t *this, ike_sa_t *ike_sa)
! 472: {
! 473: cleanup(this);
! 474: this->collision = NULL;
! 475: this->ike_sa = ike_sa;
! 476: this->new_sa = NULL;
! 477: this->ike_init = NULL;
! 478: this->ike_delete = NULL;
! 479: }
! 480:
! 481: METHOD(task_t, destroy, void,
! 482: private_ike_rekey_t *this)
! 483: {
! 484: cleanup(this);
! 485: free(this);
! 486: }
! 487:
! 488: /*
! 489: * Described in header.
! 490: */
! 491: ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator)
! 492: {
! 493: private_ike_rekey_t *this;
! 494:
! 495: INIT(this,
! 496: .public = {
! 497: .task = {
! 498: .get_type = _get_type,
! 499: .build = _build_r,
! 500: .process = _process_r,
! 501: .migrate = _migrate,
! 502: .destroy = _destroy,
! 503: },
! 504: .did_collide = _did_collide,
! 505: .collide = _collide,
! 506: },
! 507: .ike_sa = ike_sa,
! 508: .initiator = initiator,
! 509: );
! 510: if (initiator)
! 511: {
! 512: this->public.task.build = _build_i;
! 513: this->public.task.process = _process_i;
! 514: }
! 515:
! 516: return &this->public;
! 517: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>