Implemented quick mode protocol handling, no CHILD_SA or HASH payloads yet
[strongswan.git] / src / libcharon / sa / tasks / quick_mode.c
1 /*
2 * Copyright (C) 2011 Martin Willi
3 * Copyright (C) 2011 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 "quick_mode.h"
17
18 #include <string.h>
19
20 #include <daemon.h>
21 #include <encoding/payloads/sa_payload.h>
22 #include <encoding/payloads/nonce_payload.h>
23 #include <encoding/payloads/id_payload.h>
24
25 typedef struct private_quick_mode_t private_quick_mode_t;
26
27 /**
28 * Private members of a quick_mode_t task.
29 */
30 struct private_quick_mode_t {
31
32 /**
33 * Public methods and task_t interface.
34 */
35 quick_mode_t public;
36
37 /**
38 * Assigned IKE_SA.
39 */
40 ike_sa_t *ike_sa;
41
42 /**
43 * Traffic selector of initiator
44 */
45 traffic_selector_t *tsi;
46
47 /**
48 * Traffic selector of responder
49 */
50 traffic_selector_t *tsr;
51
52 /**
53 * Initiators nonce
54 */
55 chunk_t nonce_i;
56
57 /**
58 * Responder nonce
59 */
60 chunk_t nonce_r;
61
62 /**
63 * selected CHILD_SA proposal
64 */
65 proposal_t *proposal;
66
67 /**
68 * Config of CHILD_SA to establish
69 */
70 child_cfg_t *config;
71
72 /**
73 * CHILD_SA we are about to establish
74 */
75 child_sa_t *child_sa;
76
77 /** states of quick mode */
78 enum {
79 QM_INIT,
80 QM_NEGOTIATED,
81 } state;
82 };
83
84 METHOD(task_t, build_i, status_t,
85 private_quick_mode_t *this, message_t *message)
86 {
87 switch (this->state)
88 {
89 case QM_INIT:
90 {
91 sa_payload_t *sa_payload;
92 nonce_payload_t *nonce_payload;
93 id_payload_t *id_payload;
94 traffic_selector_t *ts;
95 linked_list_t *list;
96 rng_t *rng;
97
98 list = this->config->get_proposals(this->config, TRUE);
99 sa_payload = sa_payload_create_from_proposal_list(
100 SECURITY_ASSOCIATION_V1, list);
101 list->destroy_offset(list, offsetof(proposal_t, destroy));
102 message->add_payload(message, &sa_payload->payload_interface);
103
104 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
105 if (!rng)
106 {
107 DBG1(DBG_IKE, "no RNG found to create nonce");
108 return FAILED;
109 }
110 rng->allocate_bytes(rng, NONCE_SIZE, &this->nonce_i);
111 rng->destroy(rng);
112 nonce_payload = nonce_payload_create(NONCE_V1);
113 nonce_payload->set_nonce(nonce_payload, this->nonce_i);
114 message->add_payload(message, &nonce_payload->payload_interface);
115
116 list = this->config->get_traffic_selectors(this->config, TRUE, NULL,
117 this->ike_sa->get_my_host(this->ike_sa));
118 if (list->get_first(list, (void**)&ts) != SUCCESS)
119 {
120 list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
121 DBG1(DBG_IKE, "traffic selector missing");
122 return FAILED;
123 }
124 id_payload = id_payload_create_from_ts(ts);
125 this->tsi = ts->clone(ts);
126 list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
127 message->add_payload(message, &id_payload->payload_interface);
128
129 list = this->config->get_traffic_selectors(this->config, FALSE, NULL,
130 this->ike_sa->get_other_host(this->ike_sa));
131 if (list->get_first(list, (void**)&ts) != SUCCESS)
132 {
133 list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
134 DBG1(DBG_IKE, "traffic selector missing");
135 return FAILED;
136 }
137 id_payload = id_payload_create_from_ts(ts);
138 this->tsr = ts->clone(ts);
139 list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
140 message->add_payload(message, &id_payload->payload_interface);
141
142 /* TODO-IKEv1: Add HASH(1) */
143
144 return NEED_MORE;
145 }
146 case QM_NEGOTIATED:
147 {
148 /* TODO-IKEv1: Send HASH(3) */
149 return SUCCESS;
150 }
151 default:
152 return FAILED;
153 }
154 }
155
156 METHOD(task_t, process_r, status_t,
157 private_quick_mode_t *this, message_t *message)
158 {
159 switch (this->state)
160 {
161 case QM_INIT:
162 {
163 sa_payload_t *sa_payload;
164 nonce_payload_t *nonce_payload;
165 id_payload_t *id_payload;
166 payload_t *payload;
167 linked_list_t *tsi, *tsr, *list;
168 peer_cfg_t *peer_cfg;
169 host_t *me, *other;
170 enumerator_t *enumerator;
171 bool first = TRUE;
172
173 enumerator = message->create_payload_enumerator(message);
174 while (enumerator->enumerate(enumerator, &payload))
175 {
176 if (payload->get_type(payload) == ID_V1)
177 {
178 id_payload = (id_payload_t*)payload;
179
180 if (first)
181 {
182 this->tsi = id_payload->get_ts(id_payload);
183 first = FALSE;
184 }
185 else
186 {
187 this->tsr = id_payload->get_ts(id_payload);
188 break;
189 }
190 }
191 }
192 enumerator->destroy(enumerator);
193
194 /* TODO-IKEv1: create host2host TS if ID payloads missing */
195
196 me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
197 if (!me)
198 {
199 me = this->ike_sa->get_my_host(this->ike_sa);
200 }
201 other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
202 if (!other)
203 {
204 other = this->ike_sa->get_other_host(this->ike_sa);
205 }
206 peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
207 tsi = linked_list_create();
208 tsr = linked_list_create();
209 tsi->insert_last(tsi, this->tsi);
210 tsr->insert_last(tsr, this->tsr);
211 this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi,
212 me, other);
213 tsi->destroy(tsi);
214 tsr->destroy(tsr);
215 if (!this->config)
216 {
217 DBG1(DBG_IKE, "no child config found");
218 return FAILED;
219 }
220
221 sa_payload = (sa_payload_t*)message->get_payload(message,
222 SECURITY_ASSOCIATION_V1);
223 if (!sa_payload)
224 {
225 DBG1(DBG_IKE, "sa payload missing");
226 return FAILED;
227 }
228 list = sa_payload->get_proposals(sa_payload);
229 this->proposal = this->config->select_proposal(this->config,
230 list, TRUE, FALSE);
231 list->destroy_offset(list, offsetof(proposal_t, destroy));
232 if (!this->proposal)
233 {
234 DBG1(DBG_IKE, "no matching proposal found");
235 return FAILED;
236 }
237
238 nonce_payload = (nonce_payload_t*)message->get_payload(message,
239 NONCE_V1);
240 if (!nonce_payload)
241 {
242 DBG1(DBG_IKE, "Nonce payload missing");
243 return FAILED;
244 }
245 this->nonce_i = nonce_payload->get_nonce(nonce_payload);
246
247 /* TODO-IKEv1: verify HASH(1) */
248
249 return NEED_MORE;
250 }
251 case QM_NEGOTIATED:
252 {
253 /* TODO-IKEv1: verify HASH(3) */
254
255 return SUCCESS;
256 }
257 default:
258 return FAILED;
259 }
260 }
261
262 METHOD(task_t, build_r, status_t,
263 private_quick_mode_t *this, message_t *message)
264 {
265 switch (this->state)
266 {
267 case QM_INIT:
268 {
269 sa_payload_t *sa_payload;
270 nonce_payload_t *nonce_payload;
271 id_payload_t *id_payload;
272 rng_t *rng;
273
274 sa_payload = sa_payload_create_from_proposal(
275 SECURITY_ASSOCIATION_V1, this->proposal);
276 message->add_payload(message, &sa_payload->payload_interface);
277
278 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
279 if (!rng)
280 {
281 DBG1(DBG_IKE, "no RNG found to create nonce");
282 return FAILED;
283 }
284 rng->allocate_bytes(rng, NONCE_SIZE, &this->nonce_r);
285 rng->destroy(rng);
286 nonce_payload = nonce_payload_create(NONCE_V1);
287 nonce_payload->set_nonce(nonce_payload, this->nonce_r);
288 message->add_payload(message, &nonce_payload->payload_interface);
289
290 id_payload = id_payload_create_from_ts(this->tsi);
291 message->add_payload(message, &id_payload->payload_interface);
292 id_payload = id_payload_create_from_ts(this->tsr);
293 message->add_payload(message, &id_payload->payload_interface);
294
295 /* TODO-IKEv1: add HASH(2) */
296
297 this->state = QM_NEGOTIATED;
298 return NEED_MORE;
299 }
300 default:
301 return FAILED;
302 }
303 }
304
305 METHOD(task_t, process_i, status_t,
306 private_quick_mode_t *this, message_t *message)
307 {
308 switch (this->state)
309 {
310 case QM_INIT:
311 {
312 sa_payload_t *sa_payload;
313 nonce_payload_t *nonce_payload;
314 id_payload_t *id_payload;
315 payload_t *payload;
316 traffic_selector_t *tsi = NULL, *tsr = NULL;
317 linked_list_t *list;
318 enumerator_t *enumerator;
319 bool first = TRUE;
320
321 enumerator = message->create_payload_enumerator(message);
322 while (enumerator->enumerate(enumerator, &payload))
323 {
324 if (payload->get_type(payload) == ID_V1)
325 {
326 id_payload = (id_payload_t*)payload;
327
328 if (first)
329 {
330 tsi = id_payload->get_ts(id_payload);
331 first = FALSE;
332 }
333 else
334 {
335 tsr = id_payload->get_ts(id_payload);
336 break;
337 }
338 }
339 }
340 enumerator->destroy(enumerator);
341
342 /* TODO-IKEv1: create host2host TS if ID payloads missing */
343
344 if (!tsr->is_contained_in(tsr, this->tsr) ||
345 !tsi->is_contained_in(tsi, this->tsi))
346 {
347 tsi->destroy(tsi);
348 tsr->destroy(tsr);
349 DBG1(DBG_IKE, "TS mismatch");
350 return FAILED;
351 }
352 this->tsi->destroy(this->tsi);
353 this->tsr->destroy(this->tsr);
354 this->tsi = tsi;
355 this->tsr = tsr;
356
357 sa_payload = (sa_payload_t*)message->get_payload(message,
358 SECURITY_ASSOCIATION_V1);
359 if (!sa_payload)
360 {
361 DBG1(DBG_IKE, "sa payload missing");
362 return FAILED;
363 }
364 list = sa_payload->get_proposals(sa_payload);
365 this->proposal = this->config->select_proposal(this->config,
366 list, TRUE, FALSE);
367 list->destroy_offset(list, offsetof(proposal_t, destroy));
368 if (!this->proposal)
369 {
370 DBG1(DBG_IKE, "no matching proposal found");
371 return FAILED;
372 }
373 nonce_payload = (nonce_payload_t*)message->get_payload(message,
374 NONCE_V1);
375 if (!nonce_payload)
376 {
377 DBG1(DBG_IKE, "Nonce payload missing");
378 return FAILED;
379 }
380 this->nonce_r = nonce_payload->get_nonce(nonce_payload);
381
382 /* TODO-IKEv1: verify HASH(2) */
383
384 this->state = QM_NEGOTIATED;
385 return NEED_MORE;
386 }
387 default:
388 return FAILED;
389 }
390 }
391
392 METHOD(task_t, get_type, task_type_t,
393 private_quick_mode_t *this)
394 {
395 return TASK_QUICK_MODE;
396 }
397
398 METHOD(task_t, migrate, void,
399 private_quick_mode_t *this, ike_sa_t *ike_sa)
400 {
401 this->ike_sa = ike_sa;
402 }
403
404 METHOD(task_t, destroy, void,
405 private_quick_mode_t *this)
406 {
407 chunk_free(&this->nonce_i);
408 chunk_free(&this->nonce_r);
409 DESTROY_IF(this->tsi);
410 DESTROY_IF(this->tsr);
411 DESTROY_IF(this->proposal);
412 DESTROY_IF(this->child_sa);
413 DESTROY_IF(this->config);
414 free(this);
415 }
416
417 /*
418 * Described in header.
419 */
420 quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
421 traffic_selector_t *tsi, traffic_selector_t *tsr)
422 {
423 private_quick_mode_t *this;
424
425 INIT(this,
426 .public = {
427 .task = {
428 .get_type = _get_type,
429 .migrate = _migrate,
430 .destroy = _destroy,
431 },
432 },
433 .ike_sa = ike_sa,
434 .config = config,
435 .state = QM_INIT,
436 );
437
438 if (config)
439 {
440 this->public.task.build = _build_i;
441 this->public.task.process = _process_i;
442 }
443 else
444 {
445 this->public.task.build = _build_r;
446 this->public.task.process = _process_r;
447 }
448
449 return &this->public;
450 }