]>
Commit | Line | Data |
---|---|---|
c7f5259c | 1 | /* |
35e49ffd | 2 | * Copyright (C) 2016-2018 Tobias Brunner |
c7f5259c TB |
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 "exchange_test_helper.h" | |
7e542486 | 17 | #include "mock_dh.h" |
dda5aab0 | 18 | #include "mock_ipsec.h" |
35e49ffd | 19 | #include "mock_net.h" |
dda5aab0 | 20 | #include "mock_nonce_gen.h" |
c7f5259c | 21 | |
5d10ef31 | 22 | #include <collections/array.h> |
c7f5259c TB |
23 | #include <credentials/sets/mem_cred.h> |
24 | ||
25 | typedef struct private_exchange_test_helper_t private_exchange_test_helper_t; | |
557e262f | 26 | typedef struct private_backend_t private_backend_t; |
c7f5259c TB |
27 | |
28 | /** | |
29 | * Private data | |
30 | */ | |
31 | struct private_exchange_test_helper_t { | |
32 | ||
33 | /** | |
34 | * Public interface | |
35 | */ | |
36 | exchange_test_helper_t public; | |
37 | ||
c7f5259c TB |
38 | /** |
39 | * Credentials | |
40 | */ | |
41 | mem_cred_t *creds; | |
29f1637b TB |
42 | |
43 | /** | |
44 | * IKE_SA SPI counter | |
45 | */ | |
46 | refcount_t ike_spi; | |
5d10ef31 TB |
47 | |
48 | /** | |
49 | * List of registered listeners | |
50 | */ | |
51 | array_t *listeners; | |
e0678a8c TB |
52 | |
53 | /** | |
54 | * Config backend | |
55 | */ | |
56 | private_backend_t *backend; | |
c7f5259c TB |
57 | }; |
58 | ||
557e262f TB |
59 | /** |
60 | * Custom backend_t implementation | |
61 | */ | |
62 | struct private_backend_t { | |
63 | ||
64 | /** | |
65 | * Public interface | |
66 | */ | |
67 | backend_t public; | |
68 | ||
69 | /** | |
70 | * Responder ike_cfg | |
71 | */ | |
72 | ike_cfg_t *ike_cfg; | |
73 | ||
74 | /** | |
75 | * Responder peer_cfg/child_cfg | |
76 | */ | |
77 | peer_cfg_t *peer_cfg; | |
78 | }; | |
79 | ||
29f1637b TB |
80 | CALLBACK(get_ike_spi, uint64_t, |
81 | private_exchange_test_helper_t *this) | |
82 | { | |
83 | return (uint64_t)ref_get(&this->ike_spi); | |
84 | } | |
85 | ||
c7f5259c TB |
86 | /* |
87 | * Described in header | |
88 | */ | |
89 | exchange_test_helper_t *exchange_test_helper; | |
90 | ||
557e262f | 91 | static ike_cfg_t *create_ike_cfg(bool initiator, exchange_test_sa_conf_t *conf) |
c7f5259c | 92 | { |
9486a2e5 TB |
93 | ike_cfg_create_t ike = { |
94 | .version = IKEV2, | |
95 | .local = "127.0.0.1", | |
96 | .local_port = IKEV2_UDP_PORT, | |
97 | .remote = "127.0.0.1", | |
98 | .remote_port = IKEV2_UDP_PORT, | |
99 | }; | |
c7f5259c | 100 | ike_cfg_t *ike_cfg; |
557e262f | 101 | char *proposal = NULL; |
c7f5259c | 102 | |
557e262f TB |
103 | if (conf) |
104 | { | |
1b19469c TB |
105 | ike.childless = initiator ? conf->initiator.childless |
106 | : conf->responder.childless; | |
557e262f TB |
107 | proposal = initiator ? conf->initiator.ike : conf->responder.ike; |
108 | } | |
1b19469c TB |
109 | |
110 | ike_cfg = ike_cfg_create(&ike); | |
557e262f TB |
111 | if (proposal) |
112 | { | |
113 | ike_cfg->add_proposal(ike_cfg, | |
114 | proposal_create_from_string(PROTO_IKE, proposal)); | |
115 | } | |
116 | else | |
117 | { | |
118 | ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); | |
119 | } | |
c7f5259c TB |
120 | return ike_cfg; |
121 | } | |
122 | ||
557e262f TB |
123 | static child_cfg_t *create_child_cfg(bool initiator, |
124 | exchange_test_sa_conf_t *conf) | |
c7f5259c TB |
125 | { |
126 | child_cfg_t *child_cfg; | |
127 | child_cfg_create_t child = { | |
128 | .mode = MODE_TUNNEL, | |
129 | }; | |
557e262f | 130 | char *proposal = NULL; |
c7f5259c TB |
131 | |
132 | child_cfg = child_cfg_create(initiator ? "init" : "resp", &child); | |
557e262f TB |
133 | if (conf) |
134 | { | |
135 | proposal = initiator ? conf->initiator.esp : conf->responder.esp; | |
136 | } | |
137 | if (proposal) | |
138 | { | |
139 | child_cfg->add_proposal(child_cfg, | |
140 | proposal_create_from_string(PROTO_ESP, proposal)); | |
141 | } | |
142 | else | |
143 | { | |
144 | child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP)); | |
145 | } | |
c7f5259c TB |
146 | child_cfg->add_traffic_selector(child_cfg, TRUE, |
147 | traffic_selector_create_dynamic(0, 0, 65535)); | |
148 | child_cfg->add_traffic_selector(child_cfg, FALSE, | |
149 | traffic_selector_create_dynamic(0, 0, 65535)); | |
150 | return child_cfg; | |
151 | } | |
152 | ||
153 | static void add_auth_cfg(peer_cfg_t *peer_cfg, bool initiator, bool local) | |
154 | { | |
155 | auth_cfg_t *auth; | |
156 | char *id = "init"; | |
157 | ||
158 | auth = auth_cfg_create(); | |
159 | auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); | |
160 | if (initiator ^ local) | |
161 | { | |
162 | id = "resp"; | |
163 | } | |
164 | auth->add(auth, AUTH_RULE_IDENTITY, identification_create_from_string(id)); | |
165 | peer_cfg->add_auth_cfg(peer_cfg, auth, local); | |
166 | } | |
167 | ||
557e262f TB |
168 | static peer_cfg_t *create_peer_cfg(bool initiator, |
169 | exchange_test_sa_conf_t *conf) | |
c7f5259c TB |
170 | { |
171 | peer_cfg_t *peer_cfg; | |
172 | peer_cfg_create_t peer = { | |
173 | .cert_policy = CERT_SEND_IF_ASKED, | |
174 | .unique = UNIQUE_REPLACE, | |
175 | .keyingtries = 1, | |
176 | }; | |
177 | ||
557e262f TB |
178 | peer_cfg = peer_cfg_create(initiator ? "init" : "resp", |
179 | create_ike_cfg(initiator, conf), &peer); | |
c7f5259c TB |
180 | add_auth_cfg(peer_cfg, initiator, TRUE); |
181 | add_auth_cfg(peer_cfg, initiator, FALSE); | |
c7f5259c TB |
182 | return peer_cfg; |
183 | } | |
184 | ||
185 | METHOD(backend_t, create_ike_cfg_enumerator, enumerator_t*, | |
557e262f | 186 | private_backend_t *this, host_t *me, host_t *other) |
c7f5259c | 187 | { |
557e262f | 188 | return enumerator_create_single(this->ike_cfg, NULL); |
c7f5259c TB |
189 | } |
190 | ||
191 | METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*, | |
557e262f | 192 | private_backend_t *this, identification_t *me, identification_t *other) |
c7f5259c | 193 | { |
557e262f | 194 | return enumerator_create_single(this->peer_cfg, NULL); |
c7f5259c TB |
195 | } |
196 | ||
e0678a8c TB |
197 | /** |
198 | * Sets the config objects provided by the backend | |
199 | */ | |
200 | static void set_config(private_backend_t *this, ike_cfg_t *ike, | |
201 | peer_cfg_t *peer) | |
202 | { | |
203 | DESTROY_IF(this->ike_cfg); | |
204 | this->ike_cfg = ike; | |
205 | DESTROY_IF(this->peer_cfg); | |
206 | this->peer_cfg = peer; | |
207 | } | |
208 | ||
b76c1dec | 209 | METHOD(exchange_test_helper_t, process_message, status_t, |
c7f5259c TB |
210 | private_exchange_test_helper_t *this, ike_sa_t *ike_sa, message_t *message) |
211 | { | |
40d9a4c8 TB |
212 | status_t status = FAILED; |
213 | ike_sa_id_t *id; | |
b76c1dec | 214 | |
c7f5259c TB |
215 | if (!message) |
216 | { | |
217 | message = this->public.sender->dequeue(this->public.sender); | |
218 | } | |
40d9a4c8 TB |
219 | id = message->get_ike_sa_id(message); |
220 | id = id->clone(id); | |
221 | id->switch_initiator(id); | |
222 | if (!id->get_responder_spi(id) || id->equals(id, ike_sa->get_id(ike_sa))) | |
223 | { | |
224 | charon->bus->set_sa(charon->bus, ike_sa); | |
225 | status = ike_sa->process_message(ike_sa, message); | |
226 | charon->bus->set_sa(charon->bus, NULL); | |
227 | } | |
c7f5259c | 228 | message->destroy(message); |
40d9a4c8 | 229 | id->destroy(id); |
b76c1dec | 230 | return status; |
c7f5259c TB |
231 | } |
232 | ||
e0678a8c | 233 | METHOD(exchange_test_helper_t, create_sa, child_cfg_t*, |
557e262f TB |
234 | private_exchange_test_helper_t *this, ike_sa_t **init, ike_sa_t **resp, |
235 | exchange_test_sa_conf_t *conf) | |
c7f5259c | 236 | { |
c7f5259c | 237 | peer_cfg_t *peer_cfg; |
557e262f | 238 | child_cfg_t *child_cfg; |
c7f5259c | 239 | |
e0678a8c TB |
240 | *init = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, |
241 | IKEV2, TRUE); | |
c7f5259c | 242 | |
e0678a8c TB |
243 | *resp = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, |
244 | IKEV2, FALSE); | |
245 | ||
246 | peer_cfg = create_peer_cfg(FALSE, conf); | |
247 | child_cfg = create_child_cfg(FALSE, conf); | |
248 | peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg)); | |
249 | child_cfg->destroy(child_cfg); | |
250 | set_config(this->backend, create_ike_cfg(FALSE, conf), peer_cfg); | |
c7f5259c | 251 | |
557e262f TB |
252 | peer_cfg = create_peer_cfg(TRUE, conf); |
253 | child_cfg = create_child_cfg(TRUE, conf); | |
254 | peer_cfg->add_child_cfg(peer_cfg, child_cfg->get_ref(child_cfg)); | |
e0678a8c | 255 | (*init)->set_peer_cfg(*init, peer_cfg); |
c7f5259c | 256 | peer_cfg->destroy(peer_cfg); |
e0678a8c TB |
257 | return child_cfg; |
258 | } | |
557e262f | 259 | |
e0678a8c TB |
260 | METHOD(exchange_test_helper_t, establish_sa, void, |
261 | private_exchange_test_helper_t *this, ike_sa_t **init, ike_sa_t **resp, | |
262 | exchange_test_sa_conf_t *conf) | |
263 | { | |
264 | ike_sa_id_t *id_i, *id_r; | |
265 | ike_sa_t *sa_i, *sa_r; | |
266 | child_cfg_t *child_i; | |
267 | ||
268 | child_i = create_sa(this, init, resp, conf); | |
269 | ||
270 | sa_i = *init; | |
271 | sa_r = *resp; | |
272 | ||
273 | id_i = sa_i->get_id(sa_i); | |
274 | id_r = sa_r->get_id(sa_r); | |
275 | ||
276 | call_ikesa(sa_i, initiate, child_i, 0, NULL, NULL); | |
557e262f | 277 | |
c7f5259c TB |
278 | /* IKE_SA_INIT --> */ |
279 | id_r->set_initiator_spi(id_r, id_i->get_initiator_spi(id_i)); | |
280 | process_message(this, sa_r, NULL); | |
281 | /* <-- IKE_SA_INIT */ | |
282 | id_i->set_responder_spi(id_i, id_r->get_responder_spi(id_r)); | |
283 | process_message(this, sa_i, NULL); | |
284 | /* IKE_AUTH --> */ | |
285 | process_message(this, sa_r, NULL); | |
286 | /* <-- IKE_AUTH */ | |
287 | process_message(this, sa_i, NULL); | |
288 | } | |
289 | ||
5d10ef31 TB |
290 | METHOD(exchange_test_helper_t, add_listener, void, |
291 | private_exchange_test_helper_t *this, listener_t *listener) | |
292 | { | |
293 | array_insert_create(&this->listeners, ARRAY_TAIL, listener); | |
294 | charon->bus->add_listener(charon->bus, listener); | |
295 | } | |
296 | ||
c7f5259c TB |
297 | /** |
298 | * Enable logging in charon as requested | |
299 | */ | |
300 | static void initialize_logging() | |
301 | { | |
302 | int level = LEVEL_SILENT; | |
303 | char *verbosity; | |
304 | ||
305 | verbosity = getenv("TESTS_VERBOSITY"); | |
306 | if (verbosity) | |
307 | { | |
308 | level = atoi(verbosity); | |
309 | } | |
310 | lib->settings->set_int(lib->settings, "%s.filelog.stderr.default", | |
311 | lib->settings->get_int(lib->settings, "%s.filelog.stderr.default", | |
312 | level, lib->ns), lib->ns); | |
313 | lib->settings->set_bool(lib->settings, "%s.filelog.stderr.ike_name", TRUE, | |
314 | lib->ns); | |
9665686b | 315 | charon->load_loggers(charon); |
c7f5259c TB |
316 | } |
317 | ||
dda5aab0 TB |
318 | /** |
319 | * Create a nonce generator with the first byte | |
320 | */ | |
321 | static nonce_gen_t *create_nonce_gen() | |
322 | { | |
323 | return mock_nonce_gen_create(exchange_test_helper->nonce_first_byte); | |
324 | } | |
325 | ||
c7f5259c TB |
326 | /* |
327 | * Described in header | |
328 | */ | |
329 | void exchange_test_helper_init(char *plugins) | |
330 | { | |
331 | private_exchange_test_helper_t *this; | |
e0678a8c | 332 | private_backend_t *backend; |
7e542486 TB |
333 | plugin_feature_t features[] = { |
334 | PLUGIN_REGISTER(DH, mock_dh_create), | |
335 | /* we only need to support a limited number of DH groups */ | |
336 | PLUGIN_PROVIDE(DH, MODP_2048_BIT), | |
337 | PLUGIN_PROVIDE(DH, MODP_3072_BIT), | |
338 | PLUGIN_PROVIDE(DH, ECP_256_BIT), | |
dda5aab0 TB |
339 | PLUGIN_REGISTER(NONCE_GEN, create_nonce_gen), |
340 | PLUGIN_PROVIDE(NONCE_GEN), | |
341 | PLUGIN_DEPENDS(RNG, RNG_WEAK), | |
7e542486 | 342 | }; |
c7f5259c | 343 | |
e0678a8c TB |
344 | INIT(backend, |
345 | .public = { | |
346 | .create_ike_cfg_enumerator = _create_ike_cfg_enumerator, | |
347 | .create_peer_cfg_enumerator = _create_peer_cfg_enumerator, | |
348 | .get_peer_cfg_by_name = (void*)return_null, | |
349 | }, | |
350 | ); | |
351 | ||
c7f5259c TB |
352 | INIT(this, |
353 | .public = { | |
354 | .sender = mock_sender_create(), | |
355 | .establish_sa = _establish_sa, | |
e0678a8c | 356 | .create_sa = _create_sa, |
c7f5259c | 357 | .process_message = _process_message, |
5d10ef31 | 358 | .add_listener = _add_listener, |
c7f5259c | 359 | }, |
c7f5259c | 360 | .creds = mem_cred_create(), |
e0678a8c | 361 | .backend = backend, |
c7f5259c TB |
362 | ); |
363 | ||
364 | initialize_logging(); | |
7e542486 TB |
365 | lib->plugins->add_static_features(lib->plugins, "exchange-test-helper", |
366 | features, countof(features), TRUE, NULL, NULL); | |
c7f5259c TB |
367 | /* the libcharon unit tests only load the libstrongswan plugins, unless |
368 | * TESTS_PLUGINS is defined */ | |
369 | charon->initialize(charon, plugins); | |
370 | lib->plugins->status(lib->plugins, LEVEL_CTRL); | |
7e542486 | 371 | |
c7f5259c TB |
372 | /* the original sender is not initialized because there is no socket */ |
373 | charon->sender = (sender_t*)this->public.sender; | |
374 | /* and there is no kernel plugin loaded | |
375 | * TODO: we'd have more control if we'd implement kernel_interface_t */ | |
376 | charon->kernel->add_ipsec_interface(charon->kernel, mock_ipsec_create); | |
35e49ffd | 377 | charon->kernel->add_net_interface(charon->kernel, mock_net_create); |
29f1637b TB |
378 | /* like SPIs for IPsec SAs, make IKE SPIs predictable */ |
379 | charon->ike_sa_manager->set_spi_cb(charon->ike_sa_manager, get_ike_spi, | |
380 | this); | |
c7f5259c | 381 | |
e0678a8c TB |
382 | charon->backends->add_backend(charon->backends, &backend->public); |
383 | ||
c7f5259c TB |
384 | lib->credmgr->add_set(lib->credmgr, &this->creds->set); |
385 | ||
386 | this->creds->add_shared(this->creds, | |
387 | shared_key_create(SHARED_IKE, chunk_clone(chunk_from_str("test"))), | |
388 | identification_create_from_string("%any"), NULL); | |
389 | ||
390 | exchange_test_helper = &this->public; | |
391 | } | |
392 | ||
393 | /* | |
394 | * Described in header | |
395 | */ | |
396 | void exchange_test_helper_deinit() | |
397 | { | |
398 | private_exchange_test_helper_t *this; | |
5d10ef31 | 399 | listener_t *listener; |
c7f5259c TB |
400 | |
401 | this = (private_exchange_test_helper_t*)exchange_test_helper; | |
402 | ||
5d10ef31 TB |
403 | while (array_remove(this->listeners, ARRAY_HEAD, &listener)) |
404 | { | |
405 | charon->bus->remove_listener(charon->bus, listener); | |
406 | } | |
e0678a8c TB |
407 | charon->backends->remove_backend(charon->backends, &this->backend->public); |
408 | set_config(this->backend, NULL, NULL); | |
409 | free(this->backend); | |
c7f5259c TB |
410 | lib->credmgr->remove_set(lib->credmgr, &this->creds->set); |
411 | this->creds->destroy(this->creds); | |
735bd4ca TB |
412 | /* flush SAs before destroying the sender (in case of test failures) */ |
413 | charon->ike_sa_manager->flush(charon->ike_sa_manager); | |
414 | /* charon won't destroy this as it didn't initialize the original sender */ | |
c7f5259c TB |
415 | charon->sender->destroy(charon->sender); |
416 | charon->sender = NULL; | |
5d10ef31 | 417 | array_destroy(this->listeners); |
c7f5259c TB |
418 | free(this); |
419 | } |