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