]>
Commit | Line | Data |
---|---|---|
33e2620b TB |
1 | /* |
2 | * Copyright (C) 2016 Tobias Brunner | |
19ef2aec TB |
3 | * |
4 | * Copyright (C) secunet Security Networks AG | |
33e2620b TB |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
15 | */ | |
16 | ||
17 | #include "test_suite.h" | |
18 | ||
19 | #include <daemon.h> | |
20 | #include <tests/utils/exchange_test_helper.h> | |
21 | #include <tests/utils/exchange_test_asserts.h> | |
0a2cad40 | 22 | #include <tests/utils/job_asserts.h> |
33e2620b TB |
23 | #include <tests/utils/sa_asserts.h> |
24 | ||
25 | /** | |
26 | * Regular CHILD_SA deletion either initiated by the original initiator or | |
27 | * responder of the IKE_SA. | |
28 | */ | |
29 | START_TEST(test_regular) | |
30 | { | |
31 | ike_sa_t *a, *b; | |
32 | ||
33 | if (_i) | |
34 | { /* responder deletes the CHILD_SA (SPI 2) */ | |
35 | exchange_test_helper->establish_sa(exchange_test_helper, | |
557e262f | 36 | &b, &a, NULL); |
33e2620b TB |
37 | } |
38 | else | |
39 | { /* initiator deletes the CHILD_SA (SPI 1) */ | |
40 | exchange_test_helper->establish_sa(exchange_test_helper, | |
557e262f | 41 | &a, &b, NULL); |
33e2620b TB |
42 | } |
43 | assert_hook_not_called(child_updown); | |
5d97e5c3 | 44 | call_ikesa(a, delete_child_sa, PROTO_ESP, _i+1, FALSE); |
33e2620b TB |
45 | assert_child_sa_state(a, _i+1, CHILD_DELETING); |
46 | assert_hook(); | |
47 | ||
48 | /* INFORMATIONAL { D } --> */ | |
49 | assert_hook_updown(child_updown, FALSE); | |
50 | assert_single_payload(IN, PLV2_DELETE); | |
51 | exchange_test_helper->process_message(exchange_test_helper, b, NULL); | |
52 | assert_child_sa_count(b, 0); | |
53 | assert_hook(); | |
54 | ||
55 | /* <-- INFORMATIONAL { D } */ | |
56 | assert_hook_updown(child_updown, FALSE); | |
57 | assert_single_payload(IN, PLV2_DELETE); | |
58 | exchange_test_helper->process_message(exchange_test_helper, a, NULL); | |
59 | assert_child_sa_count(a, 0); | |
60 | assert_hook(); | |
61 | ||
5d97e5c3 TB |
62 | call_ikesa(a, destroy); |
63 | call_ikesa(b, destroy); | |
33e2620b TB |
64 | } |
65 | END_TEST | |
66 | ||
67 | /** | |
68 | * Both peers initiate the CHILD_SA deletion concurrently and should handle | |
69 | * the collision properly. | |
70 | */ | |
71 | START_TEST(test_collision) | |
72 | { | |
73 | ike_sa_t *a, *b; | |
74 | ||
75 | exchange_test_helper->establish_sa(exchange_test_helper, | |
557e262f | 76 | &a, &b, NULL); |
33e2620b TB |
77 | /* both peers delete the CHILD_SA concurrently */ |
78 | assert_hook_not_called(child_updown); | |
5d97e5c3 | 79 | call_ikesa(a, delete_child_sa, PROTO_ESP, 1, FALSE); |
33e2620b | 80 | assert_child_sa_state(a, 1, CHILD_DELETING); |
5d97e5c3 | 81 | call_ikesa(b, delete_child_sa, PROTO_ESP, 2, FALSE); |
33e2620b TB |
82 | assert_child_sa_state(b, 2, CHILD_DELETING); |
83 | assert_hook(); | |
84 | ||
85 | /* RFC 7296 says: | |
86 | * | |
87 | * Normally, the response in the INFORMATIONAL exchange will contain | |
88 | * Delete payloads for the paired SAs going in the other direction. | |
89 | * There is one exception. If, by chance, both ends of a set of SAs | |
90 | * independently decide to close them, each may send a Delete payload | |
91 | * and the two requests may cross in the network. If a node receives a | |
92 | * delete request for SAs for which it has already issued a delete | |
93 | * request, it MUST delete the outgoing SAs while processing the request | |
94 | * and the incoming SAs while processing the response. In that case, | |
95 | * the responses MUST NOT include Delete payloads for the deleted SAs, | |
96 | * since that would result in duplicate deletion and could in theory | |
97 | * delete the wrong SA. | |
98 | * | |
99 | * We don't handle SAs separately so we expect both are still installed, | |
100 | * but the INFORMATIONAL response should not contain a DELETE payload. | |
101 | */ | |
102 | ||
103 | /* INFORMATIONAL { D } --> */ | |
104 | assert_hook_not_called(child_updown); | |
105 | assert_single_payload(IN, PLV2_DELETE); | |
106 | exchange_test_helper->process_message(exchange_test_helper, b, NULL); | |
107 | assert_child_sa_state(b, 2, CHILD_DELETING); | |
108 | /* <-- INFORMATIONAL { D } */ | |
109 | assert_single_payload(IN, PLV2_DELETE); | |
110 | exchange_test_helper->process_message(exchange_test_helper, a, NULL); | |
111 | assert_child_sa_state(a, 1, CHILD_DELETING); | |
112 | assert_hook(); | |
113 | ||
114 | /* <-- INFORMATIONAL { } */ | |
115 | assert_hook_updown(child_updown, FALSE); | |
116 | assert_message_empty(IN); | |
117 | exchange_test_helper->process_message(exchange_test_helper, a, NULL); | |
118 | assert_child_sa_count(a, 0); | |
119 | assert_hook(); | |
120 | /* INFORMATIONAL { } --> */ | |
121 | assert_hook_updown(child_updown, FALSE); | |
122 | assert_message_empty(IN); | |
123 | exchange_test_helper->process_message(exchange_test_helper, b, NULL); | |
124 | assert_child_sa_count(b, 0); | |
125 | assert_hook(); | |
126 | ||
5d97e5c3 TB |
127 | call_ikesa(a, destroy); |
128 | call_ikesa(b, destroy); | |
33e2620b TB |
129 | } |
130 | END_TEST | |
131 | ||
32cfe1e0 TB |
132 | /** |
133 | * This is like the collision above but one of the DELETEs is dropped or delayed | |
134 | * so the other peer is not aware that there is a collision. | |
135 | */ | |
136 | START_TEST(test_collision_drop) | |
137 | { | |
138 | ike_sa_t *a, *b; | |
139 | message_t *msg; | |
140 | ||
141 | exchange_test_helper->establish_sa(exchange_test_helper, | |
557e262f | 142 | &a, &b, NULL); |
32cfe1e0 TB |
143 | /* both peers delete the CHILD_SA concurrently */ |
144 | assert_hook_not_called(child_updown); | |
145 | call_ikesa(a, delete_child_sa, PROTO_ESP, 1, FALSE); | |
146 | assert_child_sa_state(a, 1, CHILD_DELETING); | |
147 | call_ikesa(b, delete_child_sa, PROTO_ESP, 2, FALSE); | |
148 | assert_child_sa_state(b, 2, CHILD_DELETING); | |
149 | assert_hook(); | |
150 | ||
151 | /* INFORMATIONAL { D } --> */ | |
152 | assert_hook_not_called(child_updown); | |
153 | assert_single_payload(IN, PLV2_DELETE); | |
154 | exchange_test_helper->process_message(exchange_test_helper, b, NULL); | |
155 | assert_child_sa_state(b, 2, CHILD_DELETING); | |
156 | assert_hook(); | |
157 | ||
158 | /* drop/delay the responder's message */ | |
159 | msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); | |
160 | ||
161 | /* <-- INFORMATIONAL { } */ | |
162 | assert_hook_updown(child_updown, FALSE); | |
163 | assert_message_empty(IN); | |
164 | exchange_test_helper->process_message(exchange_test_helper, a, NULL); | |
165 | assert_child_sa_count(a, 0); | |
166 | assert_hook(); | |
167 | ||
168 | /* <-- INFORMATIONAL { D } (delayed/retransmitted) */ | |
169 | assert_hook_not_called(child_updown); | |
170 | assert_single_payload(IN, PLV2_DELETE); | |
171 | exchange_test_helper->process_message(exchange_test_helper, a, msg); | |
172 | assert_hook(); | |
173 | ||
174 | /* INFORMATIONAL { } --> */ | |
175 | assert_hook_updown(child_updown, FALSE); | |
176 | assert_message_empty(IN); | |
177 | exchange_test_helper->process_message(exchange_test_helper, b, NULL); | |
178 | assert_child_sa_count(b, 0); | |
179 | assert_hook(); | |
180 | ||
181 | call_ikesa(a, destroy); | |
182 | call_ikesa(b, destroy); | |
183 | } | |
184 | END_TEST | |
185 | ||
0a2cad40 TB |
186 | /** |
187 | * One of the hosts initiates a rekey of the IKE_SA of the CHILD_SA the other | |
188 | * peer is concurrently trying to delete. | |
189 | * | |
190 | * delete ----\ /---- rekey IKE | |
191 | * \-----/----> detect collision | |
192 | * detect collision <---------/ /---- delete | |
193 | * TEMP_FAIL ----\ / | |
194 | * \----/-----> | |
195 | * <--------/ | |
196 | */ | |
197 | START_TEST(test_collision_ike_rekey) | |
198 | { | |
199 | ike_sa_t *a, *b; | |
200 | uint32_t spi_a = _i+1; | |
201 | ||
202 | if (_i) | |
203 | { /* responder deletes the CHILD_SA (SPI 2) */ | |
204 | exchange_test_helper->establish_sa(exchange_test_helper, | |
205 | &b, &a, NULL); | |
206 | } | |
207 | else | |
208 | { /* initiator deletes the CHILD_SA (SPI 1) */ | |
209 | exchange_test_helper->establish_sa(exchange_test_helper, | |
210 | &a, &b, NULL); | |
211 | } | |
212 | call_ikesa(a, delete_child_sa, PROTO_ESP, spi_a, FALSE); | |
213 | assert_child_sa_state(a, spi_a, CHILD_DELETING); | |
214 | call_ikesa(b, rekey); | |
215 | assert_ike_sa_state(b, IKE_REKEYING); | |
216 | ||
217 | /* this should never get called as there is no successful rekeying */ | |
218 | assert_hook_not_called(ike_rekey); | |
219 | ||
220 | /* RFC 7296, 2.25.2: If a peer receives a request to delete a Child SA when | |
221 | * it is currently rekeying the IKE SA, it SHOULD reply as usual, with a | |
222 | * Delete payload. | |
223 | */ | |
224 | ||
225 | /* INFORMATIONAL { D } --> */ | |
226 | assert_hook_updown(child_updown, FALSE); | |
227 | assert_single_payload(OUT, PLV2_DELETE); | |
228 | exchange_test_helper->process_message(exchange_test_helper, b, NULL); | |
229 | assert_ike_sa_state(b, IKE_REKEYING); | |
230 | assert_child_sa_count(b, 0); | |
231 | assert_hook(); | |
232 | ||
233 | /* RFC 7296, 2.25.1: If a peer receives a request to rekey the IKE SA, and | |
234 | * it is currently, rekeying, or closing a Child SA of that IKE SA, it | |
235 | * SHOULD reply with TEMPORARY_FAILURE. | |
236 | */ | |
237 | ||
238 | /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ | |
239 | assert_single_notify(OUT, TEMPORARY_FAILURE); | |
240 | exchange_test_helper->process_message(exchange_test_helper, a, NULL); | |
241 | assert_child_sa_state(a, spi_a, CHILD_DELETING); | |
242 | ||
243 | /* <-- INFORMATIONAL { D } */ | |
244 | assert_hook_updown(child_updown, FALSE); | |
245 | exchange_test_helper->process_message(exchange_test_helper, a, NULL); | |
246 | assert_child_sa_count(a, 0); | |
247 | assert_hook(); | |
248 | ||
249 | /* CREATE_CHILD_SA { N(TEMP_FAIL) } --> */ | |
250 | /* we expect a job to retry the rekeying is scheduled */ | |
251 | assert_jobs_scheduled(1); | |
252 | exchange_test_helper->process_message(exchange_test_helper, b, NULL); | |
253 | assert_ike_sa_state(b, IKE_ESTABLISHED); | |
254 | assert_scheduler(); | |
255 | ||
256 | /* ike_rekey */ | |
257 | assert_hook(); | |
258 | ||
259 | call_ikesa(a, destroy); | |
260 | call_ikesa(b, destroy); | |
261 | } | |
262 | END_TEST | |
263 | ||
264 | /** | |
265 | * One of the hosts initiates a delete of the IKE_SA of the CHILD_SA the other | |
266 | * peer is concurrently trying to delete. | |
267 | * | |
268 | * delete ----\ /---- delete IKE | |
269 | * \-----/----> detect collision | |
270 | * <---------/ /---- delete | |
271 | * delete ----\ / | |
272 | * \----/-----> | |
273 | * sa already gone <--------/ | |
274 | */ | |
275 | START_TEST(test_collision_ike_delete) | |
276 | { | |
277 | ike_sa_t *a, *b; | |
278 | uint32_t spi_a = _i+1; | |
279 | message_t *msg; | |
280 | status_t s; | |
281 | ||
282 | if (_i) | |
283 | { /* responder rekeys the CHILD_SA (SPI 2) */ | |
284 | exchange_test_helper->establish_sa(exchange_test_helper, | |
285 | &b, &a, NULL); | |
286 | } | |
287 | else | |
288 | { /* initiator rekeys the CHILD_SA (SPI 1) */ | |
289 | exchange_test_helper->establish_sa(exchange_test_helper, | |
290 | &a, &b, NULL); | |
291 | } | |
292 | call_ikesa(a, delete_child_sa, PROTO_ESP, spi_a, FALSE); | |
293 | assert_child_sa_state(a, spi_a, CHILD_DELETING); | |
a79d5103 | 294 | call_ikesa(b, delete, FALSE); |
0a2cad40 TB |
295 | assert_ike_sa_state(b, IKE_DELETING); |
296 | ||
297 | /* RFC 7296, 2.25.2 does not explicitly state what the behavior SHOULD be if | |
298 | * a peer receives a request to delete a CHILD_SA when it is currently | |
299 | * closing the IKE SA. We expect a regular response. | |
300 | */ | |
301 | ||
302 | /* INFORMATIONAL { D } --> */ | |
303 | assert_hook_updown(child_updown, FALSE); | |
304 | assert_single_payload(OUT, PLV2_DELETE); | |
305 | exchange_test_helper->process_message(exchange_test_helper, b, NULL); | |
306 | assert_ike_sa_state(b, IKE_DELETING); | |
307 | assert_child_sa_count(b, 0); | |
308 | assert_hook(); | |
309 | ||
310 | /* RFC 7296, 2.25.1 does not explicitly state what the behavior SHOULD be if | |
311 | * a peer receives a request to close the IKE SA if it is currently deleting | |
312 | * a Child SA of that IKE SA. Let's just close the IKE_SA and forget the | |
313 | * delete. | |
314 | */ | |
315 | ||
316 | /* <-- INFORMATIONAL { D } */ | |
317 | assert_hook_updown(ike_updown, FALSE); | |
318 | assert_hook_updown(child_updown, FALSE); | |
319 | assert_message_empty(OUT); | |
320 | s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); | |
321 | ck_assert_int_eq(DESTROY_ME, s); | |
322 | call_ikesa(a, destroy); | |
323 | assert_hook(); | |
324 | assert_hook(); | |
325 | ||
326 | /* <-- INFORMATIONAL { D } */ | |
327 | /* the SA is already gone */ | |
328 | msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); | |
329 | msg->destroy(msg); | |
330 | ||
331 | /* INFORMATIONAL { } --> */ | |
332 | assert_hook_updown(ike_updown, FALSE); | |
333 | assert_hook_not_called(child_updown); | |
334 | s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); | |
335 | ck_assert_int_eq(DESTROY_ME, s); | |
336 | call_ikesa(b, destroy); | |
337 | assert_hook(); | |
338 | assert_hook(); | |
339 | } | |
340 | END_TEST | |
341 | ||
33e2620b TB |
342 | Suite *child_delete_suite_create() |
343 | { | |
344 | Suite *s; | |
345 | TCase *tc; | |
346 | ||
347 | s = suite_create("child delete"); | |
348 | ||
349 | tc = tcase_create("regular"); | |
350 | tcase_add_loop_test(tc, test_regular, 0, 2); | |
351 | suite_add_tcase(s, tc); | |
352 | ||
32cfe1e0 | 353 | tc = tcase_create("collisions"); |
33e2620b | 354 | tcase_add_test(tc, test_collision); |
32cfe1e0 | 355 | tcase_add_test(tc, test_collision_drop); |
33e2620b TB |
356 | suite_add_tcase(s, tc); |
357 | ||
0a2cad40 TB |
358 | tc = tcase_create("collisions ike rekey"); |
359 | tcase_add_loop_test(tc, test_collision_ike_rekey, 0, 2); | |
360 | suite_add_tcase(s, tc); | |
361 | ||
362 | tc = tcase_create("collisions ike delete"); | |
363 | tcase_add_loop_test(tc, test_collision_ike_delete, 0, 2); | |
364 | suite_add_tcase(s, tc); | |
365 | ||
33e2620b TB |
366 | return s; |
367 | } |