Implemented first two exchanges of Main Mode as initiator
[strongswan.git] / src / libcharon / sa / tasks / main_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 "main_mode.h"
17
18 #include <string.h>
19
20 #include <daemon.h>
21 #include <crypto/diffie_hellman.h>
22 #include <encoding/payloads/sa_payload.h>
23 #include <encoding/payloads/ke_payload.h>
24 #include <encoding/payloads/nonce_payload.h>
25
26 typedef struct private_main_mode_t private_main_mode_t;
27
28 /**
29 * Private members of a main_mode_t task.
30 */
31 struct private_main_mode_t {
32
33 /**
34 * Public methods and task_t interface.
35 */
36 main_mode_t public;
37
38 /**
39 * Assigned IKE_SA.
40 */
41 ike_sa_t *ike_sa;
42
43 /**
44 * Are we the initiator?
45 */
46 bool initiator;
47
48 /**
49 * IKE config to establish
50 */
51 ike_cfg_t *ike_cfg;
52
53 /**
54 * selected IKE proposal
55 */
56 proposal_t *proposal;
57
58 /**
59 * DH exchange
60 */
61 diffie_hellman_t *dh;
62
63 /**
64 * Received public DH value from peer
65 */
66 chunk_t dh_value;
67
68 /**
69 * Initiators nonce
70 */
71 chunk_t nonce_i;
72
73 /**
74 * Responder nonce
75 */
76 chunk_t nonce_r;
77
78 /** states of main mode */
79 enum {
80 MM_INIT,
81 MM_SA,
82 MM_KE,
83 MM_ID,
84 } state;
85 };
86
87 METHOD(task_t, build_i, status_t,
88 private_main_mode_t *this, message_t *message)
89 {
90 switch (this->state)
91 {
92 case MM_INIT:
93 {
94 sa_payload_t *sa_payload;
95 linked_list_t *proposals;
96
97 this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
98 DBG0(DBG_IKE, "initiating IKE_SA %s[%d] to %H",
99 this->ike_sa->get_name(this->ike_sa),
100 this->ike_sa->get_unique_id(this->ike_sa),
101 this->ike_sa->get_other_host(this->ike_sa));
102 this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
103
104 proposals = this->ike_cfg->get_proposals(this->ike_cfg);
105
106 sa_payload = sa_payload_create_from_proposal_list(
107 SECURITY_ASSOCIATION_V1, proposals);
108 proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
109
110 message->add_payload(message, &sa_payload->payload_interface);
111
112 this->state = MM_SA;
113 return NEED_MORE;
114 }
115 case MM_SA:
116 {
117 ke_payload_t *ke_payload;
118 nonce_payload_t *nonce_payload;
119 u_int16_t group;
120 rng_t *rng;
121
122 if (!this->proposal->get_algorithm(this->proposal,
123 DIFFIE_HELLMAN_GROUP, &group, NULL))
124 {
125 DBG1(DBG_IKE, "DH group selection failed");
126 return FAILED;
127 }
128 this->dh = lib->crypto->create_dh(lib->crypto, group);
129 if (!this->dh)
130 {
131 DBG1(DBG_IKE, "negotiated DH group not supported");
132 return FAILED;
133 }
134 ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1,
135 this->dh);
136 message->add_payload(message, &ke_payload->payload_interface);
137
138 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
139 if (!rng)
140 {
141 DBG1(DBG_IKE, "no RNG found to create nonce");
142 return FAILED;
143 }
144 /* TODO-IKEv1: nonce size? */
145 rng->allocate_bytes(rng, 20, &this->nonce_i);
146 rng->destroy(rng);
147
148 nonce_payload = nonce_payload_create(NONCE_V1);
149 nonce_payload->set_nonce(nonce_payload, this->nonce_i);
150 message->add_payload(message, &nonce_payload->payload_interface);
151
152 this->state = MM_KE;
153 return NEED_MORE;
154 }
155 default:
156 return FAILED;
157 }
158 }
159
160 METHOD(task_t, process_r, status_t,
161 private_main_mode_t *this, message_t *message)
162 {
163 switch (this->state)
164 {
165 case MM_INIT:
166 {
167
168 linked_list_t *list;
169 sa_payload_t *sa_payload;
170
171 this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
172 DBG0(DBG_IKE, "%H is initiating a Main Mode",
173 message->get_source(message));
174 this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
175
176 sa_payload = (sa_payload_t*)message->get_payload(message,
177 SECURITY_ASSOCIATION_V1);
178 if (!sa_payload)
179 {
180 DBG1(DBG_IKE, "SA payload missing");
181 return FAILED;
182 }
183 list = sa_payload->get_proposals(sa_payload);
184 this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
185 list, FALSE);
186 list->destroy_offset(list, offsetof(proposal_t, destroy));
187 if (!this->proposal)
188 {
189 DBG1(DBG_IKE, "no proposal found");
190 return FAILED;
191 }
192 this->state = MM_SA;
193 return NEED_MORE;
194 }
195 case MM_SA:
196 {
197 ke_payload_t *ke_payload;
198 nonce_payload_t *nonce_payload;
199 u_int16_t group;
200
201 ke_payload = (ke_payload_t*)message->get_payload(message,
202 KEY_EXCHANGE_V1);
203 if (!ke_payload)
204 {
205 DBG1(DBG_IKE, "KE payload missing");
206 return FAILED;
207 }
208 this->dh_value = ke_payload->get_key_exchange_data(ke_payload);
209 this->dh_value = chunk_clone(this->dh_value);
210
211 if (!this->proposal->get_algorithm(this->proposal,
212 DIFFIE_HELLMAN_GROUP, &group, NULL))
213 {
214 DBG1(DBG_IKE, "DH group selection failed");
215 return FAILED;
216 }
217 this->dh = lib->crypto->create_dh(lib->crypto, group);
218 if (!this->dh)
219 {
220 DBG1(DBG_IKE, "negotiated DH group not supported");
221 return FAILED;
222 }
223 this->dh->set_other_public_value(this->dh, this->dh_value);
224
225
226 nonce_payload = (nonce_payload_t*)message->get_payload(message,
227 NONCE_V1);
228 if (!nonce_payload)
229 {
230 DBG1(DBG_IKE, "Nonce payload missing");
231 return FAILED;
232 }
233 this->nonce_i = nonce_payload->get_nonce(nonce_payload);
234 /* TODO-IKEv1: verify nonce length */
235
236 this->state = MM_KE;
237 return NEED_MORE;
238 }
239 default:
240 return FAILED;
241 }
242 }
243
244 METHOD(task_t, build_r, status_t,
245 private_main_mode_t *this, message_t *message)
246 {
247 switch (this->state)
248 {
249 case MM_SA:
250 {
251 sa_payload_t *sa_payload;
252
253 sa_payload = sa_payload_create_from_proposal(SECURITY_ASSOCIATION_V1,
254 this->proposal);
255 message->add_payload(message, &sa_payload->payload_interface);
256 return NEED_MORE;
257 }
258 case MM_KE:
259 {
260 ke_payload_t *ke_payload;
261 nonce_payload_t *nonce_payload;
262 rng_t *rng;
263
264 ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1,
265 this->dh);
266 message->add_payload(message, &ke_payload->payload_interface);
267
268 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
269 if (!rng)
270 {
271 DBG1(DBG_IKE, "no RNG found to create nonce");
272 return FAILED;
273 }
274 /* TODO-IKEv1: nonce size? */
275 rng->allocate_bytes(rng, 20, &this->nonce_r);
276 rng->destroy(rng);
277
278 nonce_payload = nonce_payload_create(NONCE_V1);
279 nonce_payload->set_nonce(nonce_payload, this->nonce_r);
280 message->add_payload(message, &nonce_payload->payload_interface);
281 return NEED_MORE;
282 }
283 default:
284 return FAILED;
285 }
286 }
287
288 METHOD(task_t, process_i, status_t,
289 private_main_mode_t *this, message_t *message)
290 {
291 switch (this->state)
292 {
293 case MM_SA:
294 {
295 linked_list_t *list;
296 sa_payload_t *sa_payload;
297
298 sa_payload = (sa_payload_t*)message->get_payload(message,
299 SECURITY_ASSOCIATION_V1);
300 if (!sa_payload)
301 {
302 DBG1(DBG_IKE, "SA payload missing");
303 return FAILED;
304 }
305 list = sa_payload->get_proposals(sa_payload);
306 this->proposal = this->ike_cfg->select_proposal(this->ike_cfg,
307 list, FALSE);
308 list->destroy_offset(list, offsetof(proposal_t, destroy));
309 if (!this->proposal)
310 {
311 DBG1(DBG_IKE, "no proposal found");
312 return FAILED;
313 }
314 return NEED_MORE;
315 }
316 case MM_KE:
317 {
318 ke_payload_t *ke_payload;
319 nonce_payload_t *nonce_payload;
320
321 ke_payload = (ke_payload_t*)message->get_payload(message,
322 KEY_EXCHANGE_V1);
323 if (!ke_payload)
324 {
325 DBG1(DBG_IKE, "KE payload missing");
326 return FAILED;
327 }
328 this->dh_value = ke_payload->get_key_exchange_data(ke_payload);
329 this->dh_value = chunk_clone(this->dh_value);
330 this->dh->set_other_public_value(this->dh, this->dh_value);
331
332 nonce_payload = (nonce_payload_t*)message->get_payload(message,
333 NONCE_V1);
334 if (!nonce_payload)
335 {
336 DBG1(DBG_IKE, "Nonce payload missing");
337 return FAILED;
338 }
339 this->nonce_r = nonce_payload->get_nonce(nonce_payload);
340 /* TODO-IKEv1: verify nonce length */
341
342 return NEED_MORE;
343 }
344 default:
345 return FAILED;
346 }
347 }
348
349 METHOD(task_t, get_type, task_type_t,
350 private_main_mode_t *this)
351 {
352 return MAIN_MODE;
353 }
354
355 METHOD(task_t, migrate, void,
356 private_main_mode_t *this, ike_sa_t *ike_sa)
357 {
358 this->ike_sa = ike_sa;
359 }
360
361 METHOD(task_t, destroy, void,
362 private_main_mode_t *this)
363 {
364 DESTROY_IF(this->proposal);
365 DESTROY_IF(this->dh);
366 free(this->dh_value.ptr);
367 free(this->nonce_i.ptr);
368 free(this->nonce_r.ptr);
369 free(this);
370 }
371
372 /*
373 * Described in header.
374 */
375 main_mode_t *main_mode_create(ike_sa_t *ike_sa, bool initiator)
376 {
377 private_main_mode_t *this;
378
379 INIT(this,
380 .public = {
381 .task = {
382 .get_type = _get_type,
383 .migrate = _migrate,
384 .destroy = _destroy,
385 },
386 },
387 .ike_sa = ike_sa,
388 .initiator = initiator,
389 .state = MM_INIT,
390 );
391
392 if (initiator)
393 {
394 this->public.task.build = _build_i;
395 this->public.task.process = _process_i;
396 }
397 else
398 {
399 this->public.task.build = _build_r;
400 this->public.task.process = _process_r;
401 }
402
403 return &this->public;
404 }