task-manager-v1: Clear retransmit alert on request retransmit
[strongswan.git] / src / libcharon / sa / ikev1 / iv_manager.c
1 /*
2 * Copyright (C) 2011-2016 Tobias Brunner
3 * HSR Hochschule fuer Technik Rapperswil
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 "iv_manager.h"
17
18 #include <library.h>
19 #include <collections/linked_list.h>
20
21 /**
22 * Max. number of IVs/QMs to track.
23 */
24 #define MAX_EXCHANGES_DEFAULT 3
25
26 typedef struct private_iv_manager_t private_iv_manager_t;
27 typedef struct iv_data_t iv_data_t;
28 typedef struct qm_data_t qm_data_t;
29
30 /**
31 * Data stored for IVs.
32 */
33 struct iv_data_t {
34 /**
35 * message ID
36 */
37 uint32_t mid;
38
39 /**
40 * current IV
41 */
42 chunk_t iv;
43
44 /**
45 * last block of encrypted message
46 */
47 chunk_t last_block;
48 };
49
50 /**
51 * Private data of a iv_manager_t object.
52 */
53 struct private_iv_manager_t {
54 /**
55 * Implement public interface.
56 */
57 iv_manager_t public;
58
59 /**
60 * Phase 1 IV.
61 */
62 iv_data_t phase1_iv;
63
64 /**
65 * Keep track of IVs for exchanges after phase 1. We store only a limited
66 * number of IVs in an MRU sort of way. Stores iv_data_t objects.
67 */
68 linked_list_t *ivs;
69
70 /**
71 * Keep track of Nonces during Quick Mode exchanges. Only a limited number
72 * of QMs are tracked at the same time. Stores qm_data_t objects.
73 */
74 linked_list_t *qms;
75
76 /**
77 * Max. number of IVs/Quick Modes to track.
78 */
79 int max_exchanges;
80
81 /**
82 * Hasher used for IV generation.
83 */
84 hasher_t *hasher;
85
86 /*
87 * Encryption algorithm the block size.
88 */
89 size_t block_size;
90 };
91
92 /**
93 * Data stored for Quick Mode exchanges.
94 */
95 struct qm_data_t {
96 /**
97 * Message ID.
98 */
99 uint32_t mid;
100
101 /**
102 * Ni_b (Nonce from first message).
103 */
104 chunk_t n_i;
105
106 /**
107 * Nr_b (Nonce from second message).
108 */
109 chunk_t n_r;
110 };
111
112 /**
113 * Destroy an iv_data_t object.
114 */
115 static void iv_data_destroy(iv_data_t *this)
116 {
117 chunk_free(&this->last_block);
118 chunk_free(&this->iv);
119 free(this);
120 }
121
122 /**
123 * Destroy a qm_data_t object.
124 */
125 static void qm_data_destroy(qm_data_t *this)
126 {
127 chunk_free(&this->n_i);
128 chunk_free(&this->n_r);
129 free(this);
130 }
131
132 /**
133 * Generate an IV.
134 */
135 static bool generate_iv(private_iv_manager_t *this, iv_data_t *iv)
136 {
137 if (iv->mid == 0 || iv->iv.ptr)
138 { /* use last block of previous encrypted message */
139 chunk_free(&iv->iv);
140 iv->iv = iv->last_block;
141 iv->last_block = chunk_empty;
142 }
143 else
144 {
145 /* initial phase 2 IV = hash(last_phase1_block | mid) */
146 uint32_t net;;
147 chunk_t data;
148
149 net = htonl(iv->mid);
150 data = chunk_cata("cc", this->phase1_iv.iv, chunk_from_thing(net));
151 if (!this->hasher->allocate_hash(this->hasher, data, &iv->iv))
152 {
153 return FALSE;
154 }
155 if (iv->iv.len > this->block_size)
156 {
157 iv->iv.len = this->block_size;
158 }
159 }
160 DBG4(DBG_IKE, "next IV for MID %u %B", iv->mid, &iv->iv);
161 return TRUE;
162 }
163
164 /**
165 * Try to find an IV for the given message ID, if not found, generate it.
166 */
167 static iv_data_t *lookup_iv(private_iv_manager_t *this, uint32_t mid)
168 {
169 enumerator_t *enumerator;
170 iv_data_t *iv, *found = NULL;
171
172 if (mid == 0)
173 {
174 return &this->phase1_iv;
175 }
176
177 enumerator = this->ivs->create_enumerator(this->ivs);
178 while (enumerator->enumerate(enumerator, &iv))
179 {
180 if (iv->mid == mid)
181 { /* IV gets moved to the front of the list */
182 this->ivs->remove_at(this->ivs, enumerator);
183 found = iv;
184 break;
185 }
186 }
187 enumerator->destroy(enumerator);
188 if (!found)
189 {
190 INIT(found,
191 .mid = mid,
192 );
193 if (!generate_iv(this, found))
194 {
195 iv_data_destroy(found);
196 return NULL;
197 }
198 }
199 this->ivs->insert_first(this->ivs, found);
200 /* remove least recently used IV if maximum reached */
201 if (this->ivs->get_count(this->ivs) > this->max_exchanges &&
202 this->ivs->remove_last(this->ivs, (void**)&iv) == SUCCESS)
203 {
204 iv_data_destroy(iv);
205 }
206 return found;
207 }
208
209 METHOD(iv_manager_t, init_iv_chain, bool,
210 private_iv_manager_t *this, chunk_t data, hasher_t *hasher,
211 size_t block_size)
212 {
213 this->hasher = hasher;
214 this->block_size = block_size;
215
216 if (!this->hasher->allocate_hash(this->hasher, data, &this->phase1_iv.iv))
217 {
218 return FALSE;
219 }
220 if (this->phase1_iv.iv.len > this->block_size)
221 {
222 this->phase1_iv.iv.len = this->block_size;
223 }
224 DBG4(DBG_IKE, "initial IV %B", &this->phase1_iv.iv);
225 return TRUE;
226 }
227
228 METHOD(iv_manager_t, get_iv, bool,
229 private_iv_manager_t *this, uint32_t mid, chunk_t *out)
230 {
231 iv_data_t *iv;
232
233 iv = lookup_iv(this, mid);
234 if (iv)
235 {
236 *out = iv->iv;
237 return TRUE;
238 }
239 return FALSE;
240 }
241
242 METHOD(iv_manager_t, update_iv, bool,
243 private_iv_manager_t *this, uint32_t mid, chunk_t last_block)
244 {
245 iv_data_t *iv = lookup_iv(this, mid);
246 if (iv)
247 { /* update last block */
248 chunk_free(&iv->last_block);
249 iv->last_block = chunk_clone(last_block);
250 return TRUE;
251 }
252 return FALSE;
253 }
254
255 METHOD(iv_manager_t, confirm_iv, bool,
256 private_iv_manager_t *this, uint32_t mid)
257 {
258 iv_data_t *iv = lookup_iv(this, mid);
259 if (iv)
260 {
261 return generate_iv(this, iv);
262 }
263 return FALSE;
264 }
265
266 METHOD(iv_manager_t, lookup_quick_mode, void,
267 private_iv_manager_t *this, uint32_t mid, chunk_t **n_i, chunk_t **n_r)
268 {
269 enumerator_t *enumerator;
270 qm_data_t *qm, *found = NULL;
271
272 enumerator = this->qms->create_enumerator(this->qms);
273 while (enumerator->enumerate(enumerator, &qm))
274 {
275 if (qm->mid == mid)
276 { /* state gets moved to the front of the list */
277 this->qms->remove_at(this->qms, enumerator);
278 found = qm;
279 break;
280 }
281 }
282 enumerator->destroy(enumerator);
283 if (!found)
284 {
285 INIT(found,
286 .mid = mid,
287 );
288 }
289
290 *n_i = &found->n_i;
291 *n_r = &found->n_r;
292
293 this->qms->insert_first(this->qms, found);
294 /* remove least recently used state if maximum reached */
295 if (this->qms->get_count(this->qms) > this->max_exchanges &&
296 this->qms->remove_last(this->qms, (void**)&qm) == SUCCESS)
297 {
298 qm_data_destroy(qm);
299 }
300 }
301
302 METHOD(iv_manager_t, remove_quick_mode, void,
303 private_iv_manager_t *this, uint32_t mid)
304 {
305 enumerator_t *enumerator;
306 qm_data_t *qm;
307
308 enumerator = this->qms->create_enumerator(this->qms);
309 while (enumerator->enumerate(enumerator, &qm))
310 {
311 if (qm->mid == mid)
312 {
313 this->qms->remove_at(this->qms, enumerator);
314 qm_data_destroy(qm);
315 break;
316 }
317 }
318 enumerator->destroy(enumerator);
319 }
320
321 METHOD(iv_manager_t, destroy, void,
322 private_iv_manager_t *this)
323 {
324 chunk_free(&this->phase1_iv.iv);
325 chunk_free(&this->phase1_iv.last_block);
326 this->ivs->destroy_function(this->ivs, (void*)iv_data_destroy);
327 this->qms->destroy_function(this->qms, (void*)qm_data_destroy);
328 free(this);
329 }
330
331 iv_manager_t *iv_manager_create(int max_exchanges)
332 {
333 private_iv_manager_t *this;
334
335 INIT(this,
336 .public = {
337 .init_iv_chain = _init_iv_chain,
338 .get_iv = _get_iv,
339 .update_iv = _update_iv,
340 .confirm_iv = _confirm_iv,
341 .lookup_quick_mode = _lookup_quick_mode,
342 .remove_quick_mode = _remove_quick_mode,
343 .destroy = _destroy,
344 },
345 .ivs = linked_list_create(),
346 .qms = linked_list_create(),
347 .max_exchanges = max_exchanges,
348 );
349
350 if (!this->max_exchanges)
351 {
352 this->max_exchanges = lib->settings->get_int(lib->settings,
353 "%s.max_ikev1_exchanges", MAX_EXCHANGES_DEFAULT, lib->ns);
354 }
355 return &this->public;
356 }