Implemented aggressive mode using Phase 1 helper class
[strongswan.git] / src / libcharon / sa / ikev1 / tasks / aggressive_mode.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
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 "aggressive_mode.h"
17
18 #include <string.h>
19
20 #include <daemon.h>
21 #include <sa/ikev1/phase1.h>
22 #include <encoding/payloads/sa_payload.h>
23 #include <encoding/payloads/id_payload.h>
24 #include <encoding/payloads/hash_payload.h>
25 #include <sa/ikev1/tasks/xauth.h>
26 #include <sa/ikev1/tasks/mode_config.h>
27 #include <sa/ikev1/tasks/informational.h>
28 #include <sa/ikev1/tasks/isakmp_delete.h>
29 #include <processing/jobs/adopt_children_job.h>
30
31 typedef struct private_aggressive_mode_t private_aggressive_mode_t;
32
33 /**
34 * Private members of a aggressive_mode_t task.
35 */
36 struct private_aggressive_mode_t {
37
38 /**
39 * Public methods and task_t interface.
40 */
41 aggressive_mode_t public;
42
43 /**
44 * Assigned IKE_SA.
45 */
46 ike_sa_t *ike_sa;
47
48 /**
49 * Are we the initiator?
50 */
51 bool initiator;
52
53 /**
54 * Common phase 1 helper class
55 */
56 phase1_t *ph1;
57
58 /**
59 * IKE config to establish
60 */
61 ike_cfg_t *ike_cfg;
62
63 /**
64 * Peer config to use
65 */
66 peer_cfg_t *peer_cfg;
67
68 /**
69 * selected IKE proposal
70 */
71 proposal_t *proposal;
72
73 /**
74 * Negotiated SA lifetime
75 */
76 u_int32_t lifetime;
77
78 /**
79 * Negotiated authentication method
80 */
81 auth_method_t method;
82
83 /**
84 * Encoded ID payload, without fixed header
85 */
86 chunk_t id_data;
87
88 /** states of aggressive mode */
89 enum {
90 AM_INIT,
91 AM_AUTH,
92 } state;
93 };
94
95 /**
96 * Set IKE_SA to established state
97 */
98 static void establish(private_aggressive_mode_t *this)
99 {
100 DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
101 this->ike_sa->get_name(this->ike_sa),
102 this->ike_sa->get_unique_id(this->ike_sa),
103 this->ike_sa->get_my_host(this->ike_sa),
104 this->ike_sa->get_my_id(this->ike_sa),
105 this->ike_sa->get_other_host(this->ike_sa),
106 this->ike_sa->get_other_id(this->ike_sa));
107
108 this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
109 charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
110 }
111
112 /**
113 * Check for notify errors, return TRUE if error found
114 */
115 static bool has_notify_errors(private_aggressive_mode_t *this, message_t *message)
116 {
117 enumerator_t *enumerator;
118 payload_t *payload;
119 bool err = FALSE;
120
121 enumerator = message->create_payload_enumerator(message);
122 while (enumerator->enumerate(enumerator, &payload))
123 {
124 if (payload->get_type(payload) == NOTIFY_V1)
125 {
126 notify_payload_t *notify;
127 notify_type_t type;
128
129 notify = (notify_payload_t*)payload;
130 type = notify->get_notify_type(notify);
131 if (type < 16384)
132 {
133 DBG1(DBG_IKE, "received %N error notify",
134 notify_type_names, type);
135 err = TRUE;
136 }
137 else
138 {
139 DBG1(DBG_IKE, "received %N notify", notify_type_names, type);
140 }
141 }
142 }
143 enumerator->destroy(enumerator);
144
145 return err;
146 }
147
148 /**
149 * Queue a task sending a notify in an INFORMATIONAL exchange
150 */
151 static status_t send_notify(private_aggressive_mode_t *this, notify_type_t type)
152 {
153 notify_payload_t *notify;
154 ike_sa_id_t *ike_sa_id;
155 u_int64_t spi_i, spi_r;
156 chunk_t spi;
157
158 notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1,
159 PROTO_IKE, type);
160 ike_sa_id = this->ike_sa->get_id(this->ike_sa);
161 spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
162 spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
163 spi = chunk_cata("cc", chunk_from_thing(spi_i), chunk_from_thing(spi_r));
164 notify->set_spi_data(notify, spi);
165
166 this->ike_sa->queue_task(this->ike_sa,
167 (task_t*)informational_create(this->ike_sa, notify));
168 /* cancel all active/passive tasks in favour of informational */
169 return ALREADY_DONE;
170 }
171
172 /**
173 * Queue a delete task if authentication failed as initiator
174 */
175 static status_t send_delete(private_aggressive_mode_t *this)
176 {
177 this->ike_sa->queue_task(this->ike_sa,
178 (task_t*)isakmp_delete_create(this->ike_sa, TRUE));
179 /* cancel all active tasks in favour of informational */
180 return ALREADY_DONE;
181 }
182
183 METHOD(task_t, build_i, status_t,
184 private_aggressive_mode_t *this, message_t *message)
185 {
186 switch (this->state)
187 {
188 case AM_INIT:
189 {
190 sa_payload_t *sa_payload;
191 id_payload_t *id_payload;
192 linked_list_t *proposals;
193 identification_t *id;
194 packet_t *packet;
195 u_int16_t group;
196
197 DBG0(DBG_IKE, "initiating aggressive mode IKE_SA %s[%d] to %H",
198 this->ike_sa->get_name(this->ike_sa),
199 this->ike_sa->get_unique_id(this->ike_sa),
200 this->ike_sa->get_other_host(this->ike_sa));
201 this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
202
203 this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
204 this->peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
205 this->peer_cfg->get_ref(this->peer_cfg);
206
207 this->method = this->ph1->get_auth_method(this->ph1, this->peer_cfg);
208 if (this->method == AUTH_NONE)
209 {
210 DBG1(DBG_CFG, "configuration uses unsupported authentication");
211 return FAILED;
212 }
213 this->lifetime = this->peer_cfg->get_reauth_time(this->peer_cfg,
214 FALSE);
215 if (!this->lifetime)
216 { /* fall back to rekey time of no rekey time configured */
217 this->lifetime = this->peer_cfg->get_rekey_time(this->peer_cfg,
218 FALSE);
219 }
220 this->lifetime += this->peer_cfg->get_over_time(this->peer_cfg);
221 proposals = this->ike_cfg->get_proposals(this->ike_cfg);
222 sa_payload = sa_payload_create_from_proposals_v1(proposals,
223 this->lifetime, 0, this->method, MODE_NONE, FALSE);
224 proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
225
226 message->add_payload(message, &sa_payload->payload_interface);
227
228 group = this->ike_cfg->get_dh_group(this->ike_cfg);
229 if (group == MODP_NONE)
230 {
231 DBG1(DBG_IKE, "DH group selection failed");
232 return FAILED;
233 }
234 if (!this->ph1->create_dh(this->ph1, group))
235 {
236 DBG1(DBG_IKE, "DH group %N not supported",
237 diffie_hellman_group_names, group);
238 return FAILED;
239 }
240 if (!this->ph1->add_nonce_ke(this->ph1, message))
241 {
242 return FAILED;
243 }
244 id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE);
245 if (!id)
246 {
247 DBG1(DBG_CFG, "own identity not known");
248 return FAILED;
249 }
250 this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
251 id_payload = id_payload_create_from_identification(ID_V1, id);
252 this->id_data = id_payload->get_encoded(id_payload);
253 message->add_payload(message, &id_payload->payload_interface);
254
255 /* pregenerate message to store SA payload */
256 if (this->ike_sa->generate_message(this->ike_sa, message,
257 &packet) != SUCCESS)
258 {
259 DBG1(DBG_IKE, "pregenerating SA payload failed");
260 return FAILED;
261 }
262 packet->destroy(packet);
263 if (!this->ph1->save_sa_payload(this->ph1, message))
264 {
265 DBG1(DBG_IKE, "SA payload invalid");
266 return FAILED;
267 }
268 this->state = AM_AUTH;
269 return NEED_MORE;
270 }
271 case AM_AUTH:
272 {
273 if (!this->ph1->build_auth(this->ph1, this->method, message,
274 this->id_data))
275 {
276 this->id_data = chunk_empty;
277 return send_notify(this, AUTHENTICATION_FAILED);
278 }
279 this->id_data = chunk_empty;
280
281 if (this->peer_cfg->get_virtual_ip(this->peer_cfg))
282 {
283 this->ike_sa->queue_task(this->ike_sa,
284 (task_t*)mode_config_create(this->ike_sa, TRUE));
285 }
286
287 switch (this->method)
288 {
289 case AUTH_XAUTH_INIT_PSK:
290 case AUTH_XAUTH_INIT_RSA:
291 case AUTH_HYBRID_INIT_RSA:
292 /* wait for XAUTH request */
293 return SUCCESS;
294 case AUTH_XAUTH_RESP_PSK:
295 case AUTH_XAUTH_RESP_RSA:
296 case AUTH_HYBRID_RESP_RSA:
297 /* TODO-IKEv1: not yet */
298 return FAILED;
299 default:
300 establish(this);
301 return SUCCESS;
302 }
303 }
304 default:
305 return FAILED;
306 }
307 }
308
309 METHOD(task_t, process_r, status_t,
310 private_aggressive_mode_t *this, message_t *message)
311 {
312 switch (this->state)
313 {
314 case AM_INIT:
315 {
316 sa_payload_t *sa_payload;
317 id_payload_t *id_payload;
318 identification_t *id;
319 linked_list_t *list;
320 u_int16_t group;
321
322 this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
323 DBG0(DBG_IKE, "%H is initiating a Aggressive Mode IKE_SA",
324 message->get_source(message));
325 this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
326
327 this->ike_sa->update_hosts(this->ike_sa,
328 message->get_destination(message),
329 message->get_source(message), TRUE);
330
331 sa_payload = (sa_payload_t*)message->get_payload(message,
332 SECURITY_ASSOCIATION_V1);
333 if (!sa_payload)
334 {
335 DBG1(DBG_IKE, "SA payload missing");
336 return send_notify(this, INVALID_PAYLOAD_TYPE);
337 }
338 if (!this->ph1->save_sa_payload(this->ph1, message))
339 {
340 return send_notify(this, INVALID_PAYLOAD_TYPE);
341 }
342
343 list = sa_payload->get_proposals(sa_payload);
344 this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
345 list, FALSE);
346 list->destroy_offset(list, offsetof(proposal_t, destroy));
347 if (!this->proposal)
348 {
349 DBG1(DBG_IKE, "no proposal found");
350 return send_notify(this, NO_PROPOSAL_CHOSEN);
351 }
352
353 this->method = sa_payload->get_auth_method(sa_payload);
354 this->lifetime = sa_payload->get_lifetime(sa_payload);
355
356 if (!this->proposal->get_algorithm(this->proposal,
357 DIFFIE_HELLMAN_GROUP, &group, NULL))
358 {
359 DBG1(DBG_IKE, "DH group selection failed");
360 return send_notify(this, INVALID_KEY_INFORMATION);
361 }
362 if (!this->ph1->create_dh(this->ph1, group))
363 {
364 DBG1(DBG_IKE, "negotiated DH group not supported");
365 return send_notify(this, INVALID_KEY_INFORMATION);
366 }
367 if (!this->ph1->get_nonce_ke(this->ph1, message))
368 {
369 return send_notify(this, INVALID_PAYLOAD_TYPE);
370 }
371
372 id_payload = (id_payload_t*)message->get_payload(message, ID_V1);
373 if (!id_payload)
374 {
375 DBG1(DBG_IKE, "IDii payload missing");
376 return send_notify(this, INVALID_PAYLOAD_TYPE);
377 }
378
379 id = id_payload->get_identification(id_payload);
380 this->id_data = id_payload->get_encoded(id_payload);
381 this->ike_sa->set_other_id(this->ike_sa, id);
382 this->peer_cfg = this->ph1->select_config(this->ph1,
383 this->method, id);
384 if (!this->peer_cfg)
385 {
386 DBG1(DBG_IKE, "no peer config found");
387 return send_notify(this, AUTHENTICATION_FAILED);
388 }
389 this->ike_sa->set_peer_cfg(this->ike_sa, this->peer_cfg);
390
391 this->state = AM_AUTH;
392 if (has_notify_errors(this, message))
393 {
394 return FAILED;
395 }
396 return NEED_MORE;
397 }
398 case AM_AUTH:
399 {
400 if (!this->ph1->verify_auth(this->ph1, this->method, message,
401 this->id_data))
402 {
403 this->id_data = chunk_empty;
404 return send_delete(this);
405 }
406 this->id_data = chunk_empty;
407
408 switch (this->method)
409 {
410 case AUTH_XAUTH_INIT_PSK:
411 case AUTH_XAUTH_INIT_RSA:
412 case AUTH_HYBRID_INIT_RSA:
413 this->ike_sa->queue_task(this->ike_sa,
414 (task_t*)xauth_create(this->ike_sa, TRUE));
415 return SUCCESS;
416 case AUTH_XAUTH_RESP_PSK:
417 case AUTH_XAUTH_RESP_RSA:
418 case AUTH_HYBRID_RESP_RSA:
419 /* TODO-IKEv1: not yet supported */
420 return FAILED;
421 default:
422 establish(this);
423 lib->processor->queue_job(lib->processor, (job_t*)
424 adopt_children_job_create(
425 this->ike_sa->get_id(this->ike_sa)));
426 return SUCCESS;
427 }
428 }
429 default:
430 return FAILED;
431 }
432 }
433
434 METHOD(task_t, build_r, status_t,
435 private_aggressive_mode_t *this, message_t *message)
436 {
437 if (this->state == AM_AUTH)
438 {
439 sa_payload_t *sa_payload;
440 id_payload_t *id_payload;
441 identification_t *id;
442
443 sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
444 this->lifetime, 0, this->method, MODE_NONE, FALSE);
445 message->add_payload(message, &sa_payload->payload_interface);
446
447 if (!this->ph1->add_nonce_ke(this->ph1, message))
448 {
449 return send_notify(this, INVALID_KEY_INFORMATION);
450 }
451 if (!this->ph1->create_hasher(this->ph1, this->proposal))
452 {
453 return send_notify(this, NO_PROPOSAL_CHOSEN);
454 }
455 if (!this->ph1->derive_keys(this->ph1, this->peer_cfg, this->method,
456 this->proposal))
457 {
458 return send_notify(this, INVALID_KEY_INFORMATION);
459 }
460
461 id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE);
462 if (!id)
463 {
464 DBG1(DBG_CFG, "own identity not known");
465 return send_notify(this, INVALID_ID_INFORMATION);
466 }
467 this->ike_sa->set_my_id(this->ike_sa, id->clone(id));
468
469 id_payload = id_payload_create_from_identification(ID_V1, id);
470 message->add_payload(message, &id_payload->payload_interface);
471
472 if (!this->ph1->build_auth(this->ph1, this->method, message,
473 id_payload->get_encoded(id_payload)))
474 {
475 return send_notify(this, AUTHENTICATION_FAILED);
476 }
477 return NEED_MORE;
478 }
479 return FAILED;
480 }
481
482 METHOD(task_t, process_i, status_t,
483 private_aggressive_mode_t *this, message_t *message)
484 {
485 if (this->state == AM_AUTH)
486 {
487 auth_method_t method;
488 sa_payload_t *sa_payload;
489 id_payload_t *id_payload;
490 identification_t *id, *cid;
491 linked_list_t *list;
492 u_int32_t lifetime;
493
494 sa_payload = (sa_payload_t*)message->get_payload(message,
495 SECURITY_ASSOCIATION_V1);
496 if (!sa_payload)
497 {
498 DBG1(DBG_IKE, "SA payload missing");
499 return send_notify(this, INVALID_PAYLOAD_TYPE);
500 }
501 list = sa_payload->get_proposals(sa_payload);
502 this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
503 list, FALSE);
504 list->destroy_offset(list, offsetof(proposal_t, destroy));
505 if (!this->proposal)
506 {
507 DBG1(DBG_IKE, "no proposal found");
508 return send_notify(this, NO_PROPOSAL_CHOSEN);
509 }
510
511 lifetime = sa_payload->get_lifetime(sa_payload);
512 if (lifetime != this->lifetime)
513 {
514 DBG1(DBG_IKE, "received lifetime %us does not match configured "
515 "lifetime %us", lifetime, this->lifetime);
516 }
517 this->lifetime = lifetime;
518 method = sa_payload->get_auth_method(sa_payload);
519 if (method != this->method)
520 {
521 DBG1(DBG_IKE, "received %N authentication, but configured %N, "
522 "continue with configured", auth_method_names, method,
523 auth_method_names, this->method);
524 }
525 if (!this->ph1->get_nonce_ke(this->ph1, message))
526 {
527 return send_notify(this, INVALID_PAYLOAD_TYPE);
528 }
529 if (!this->ph1->create_hasher(this->ph1, this->proposal))
530 {
531 return send_notify(this, NO_PROPOSAL_CHOSEN);
532 }
533 if (!this->ph1->derive_keys(this->ph1, this->peer_cfg, this->method,
534 this->proposal))
535 {
536 return send_notify(this, INVALID_KEY_INFORMATION);
537 }
538
539 id_payload = (id_payload_t*)message->get_payload(message, ID_V1);
540 if (!id_payload)
541 {
542 DBG1(DBG_IKE, "IDir payload missing");
543 return send_delete(this);
544 }
545 id = id_payload->get_identification(id_payload);
546 cid = this->ph1->get_id(this->ph1, this->peer_cfg, FALSE);
547 if (cid && !id->matches(id, cid))
548 {
549 DBG1(DBG_IKE, "IDir '%Y' does not match to '%Y'", id, cid);
550 id->destroy(id);
551 return send_notify(this, INVALID_ID_INFORMATION);
552 }
553 this->ike_sa->set_other_id(this->ike_sa, id);
554
555 if (!this->ph1->verify_auth(this->ph1, this->method, message,
556 id_payload->get_encoded(id_payload)))
557 {
558 return send_notify(this, AUTHENTICATION_FAILED);
559 }
560 return NEED_MORE;
561 }
562 return FAILED;
563 }
564
565 METHOD(task_t, get_type, task_type_t,
566 private_aggressive_mode_t *this)
567 {
568 return TASK_AGGRESSIVE_MODE;
569 }
570
571 METHOD(task_t, migrate, void,
572 private_aggressive_mode_t *this, ike_sa_t *ike_sa)
573 {
574 DESTROY_IF(this->peer_cfg);
575 DESTROY_IF(this->proposal);
576 this->ph1->destroy(this->ph1);
577 chunk_free(&this->id_data);
578
579 this->ike_sa = ike_sa;
580 this->state = AM_INIT;
581 this->peer_cfg = NULL;
582 this->proposal = NULL;
583 this->ph1 = phase1_create(ike_sa, this->initiator);
584 }
585
586 METHOD(task_t, destroy, void,
587 private_aggressive_mode_t *this)
588 {
589 DESTROY_IF(this->peer_cfg);
590 DESTROY_IF(this->proposal);
591 this->ph1->destroy(this->ph1);
592 chunk_free(&this->id_data);
593 free(this);
594 }
595
596 /*
597 * Described in header.
598 */
599 aggressive_mode_t *aggressive_mode_create(ike_sa_t *ike_sa, bool initiator)
600 {
601 private_aggressive_mode_t *this;
602
603 INIT(this,
604 .public = {
605 .task = {
606 .get_type = _get_type,
607 .migrate = _migrate,
608 .destroy = _destroy,
609 },
610 },
611 .ike_sa = ike_sa,
612 .ph1 = phase1_create(ike_sa, initiator),
613 .initiator = initiator,
614 .state = AM_INIT,
615 );
616
617 if (initiator)
618 {
619 this->public.task.build = _build_i;
620 this->public.task.process = _process_i;
621 }
622 else
623 {
624 this->public.task.build = _build_r;
625 this->public.task.process = _process_r;
626 }
627
628 return &this->public;
629 }