Memory leak fixed.
[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 *config;
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 /* TODO-IKEv1: initiate mainmode */
91 return FAILED;
92 }
93
94 METHOD(task_t, process_r, status_t,
95 private_main_mode_t *this, message_t *message)
96 {
97 switch (this->state)
98 {
99 case MM_INIT:
100 {
101
102 linked_list_t *list;
103 sa_payload_t *sa_payload;
104
105 this->config = this->ike_sa->get_ike_cfg(this->ike_sa);
106 DBG0(DBG_IKE, "%H is initiating a Main Mode",
107 message->get_source(message));
108 this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
109
110 sa_payload = (sa_payload_t*)message->get_payload(message,
111 SECURITY_ASSOCIATION_V1);
112 if (!sa_payload)
113 {
114 DBG1(DBG_IKE, "SA payload missing");
115 return FAILED;
116 }
117 list = sa_payload->get_proposals(sa_payload);
118 this->proposal = this->config->select_proposal(this->config,
119 list, FALSE);
120 list->destroy_offset(list, offsetof(proposal_t, destroy));
121 if (!this->proposal)
122 {
123 DBG1(DBG_IKE, "no proposal found");
124 return FAILED;
125 }
126 this->state = MM_SA;
127 return NEED_MORE;
128 }
129 case MM_SA:
130 {
131 ke_payload_t *ke_payload;
132 nonce_payload_t *nonce_payload;
133 u_int16_t group;
134
135 ke_payload = (ke_payload_t*)message->get_payload(message,
136 KEY_EXCHANGE_V1);
137 if (!ke_payload)
138 {
139 DBG1(DBG_IKE, "KE payload missing");
140 return FAILED;
141 }
142 this->dh_value = ke_payload->get_key_exchange_data(ke_payload);
143 this->dh_value = chunk_clone(this->dh_value);
144
145 if (!this->proposal->get_algorithm(this->proposal,
146 DIFFIE_HELLMAN_GROUP, &group, NULL))
147 {
148 DBG1(DBG_IKE, "DH group selection failed");
149 return FAILED;
150 }
151 this->dh = lib->crypto->create_dh(lib->crypto, group);
152 if (!this->dh)
153 {
154 DBG1(DBG_IKE, "negotiated DH group not supported");
155 return FAILED;
156 }
157 this->dh->set_other_public_value(this->dh, this->dh_value);
158
159
160 nonce_payload = (nonce_payload_t*)message->get_payload(message,
161 NONCE_V1);
162 if (!nonce_payload)
163 {
164 DBG1(DBG_IKE, "Nonce payload missing");
165 return FAILED;
166 }
167 this->nonce_i = nonce_payload->get_nonce(nonce_payload);
168 /* TODO-IKEv1: verify nonce length */
169
170 this->state = MM_KE;
171 return NEED_MORE;
172 }
173 default:
174 return FAILED;
175 }
176 }
177
178 METHOD(task_t, build_r, status_t,
179 private_main_mode_t *this, message_t *message)
180 {
181 switch (this->state)
182 {
183 case MM_SA:
184 {
185 sa_payload_t *sa_payload;
186
187 sa_payload = sa_payload_create_from_proposal(SECURITY_ASSOCIATION_V1,
188 this->proposal);
189 message->add_payload(message, &sa_payload->payload_interface);
190 return NEED_MORE;
191 }
192 case MM_KE:
193 {
194 ke_payload_t *ke_payload;
195 nonce_payload_t *nonce_payload;
196 rng_t *rng;
197
198 ke_payload = ke_payload_create_from_diffie_hellman(KEY_EXCHANGE_V1,
199 this->dh);
200 message->add_payload(message, &ke_payload->payload_interface);
201
202 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
203 if (!rng)
204 {
205 DBG1(DBG_IKE, "no RNG found to create nonce");
206 return FAILED;
207 }
208 /* TODO-IKEv1: nonce size? */
209 rng->allocate_bytes(rng, 20, &this->nonce_r);
210 rng->destroy(rng);
211
212 nonce_payload = nonce_payload_create(NONCE_V1);
213 nonce_payload->set_nonce(nonce_payload, this->nonce_r);
214 message->add_payload(message, &nonce_payload->payload_interface);
215 return NEED_MORE;
216 }
217 default:
218 return FAILED;
219 }
220 }
221
222 METHOD(task_t, process_i, status_t,
223 private_main_mode_t *this, message_t *message)
224 {
225 /* TODO-IKEv1: process main mode as initiator */
226 return FAILED;
227 }
228
229 METHOD(task_t, get_type, task_type_t,
230 private_main_mode_t *this)
231 {
232 return MAIN_MODE;
233 }
234
235 METHOD(task_t, migrate, void,
236 private_main_mode_t *this, ike_sa_t *ike_sa)
237 {
238 this->ike_sa = ike_sa;
239 }
240
241 METHOD(task_t, destroy, void,
242 private_main_mode_t *this)
243 {
244 DESTROY_IF(this->proposal);
245 DESTROY_IF(this->dh);
246 free(this->dh_value.ptr);
247 free(this->nonce_i.ptr);
248 free(this->nonce_r.ptr);
249 free(this);
250 }
251
252 /*
253 * Described in header.
254 */
255 main_mode_t *main_mode_create(ike_sa_t *ike_sa, bool initiator)
256 {
257 private_main_mode_t *this;
258
259 INIT(this,
260 .public = {
261 .task = {
262 .get_type = _get_type,
263 .migrate = _migrate,
264 .destroy = _destroy,
265 },
266 },
267 .ike_sa = ike_sa,
268 .initiator = initiator,
269 .state = MM_INIT,
270 );
271
272 if (initiator)
273 {
274 this->public.task.build = _build_i;
275 this->public.task.process = _process_i;
276 }
277 else
278 {
279 this->public.task.build = _build_r;
280 this->public.task.process = _process_r;
281 }
282
283 return &this->public;
284 }