Generate payload to rebuild_auth, works with injected unknown payloads
[strongswan.git] / src / conftest / hooks / pretend_auth.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 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 "hook.h"
17
18 #include <encoding/payloads/nonce_payload.h>
19 #include <encoding/payloads/cert_payload.h>
20 #include <encoding/payloads/auth_payload.h>
21 #include <encoding/payloads/id_payload.h>
22 #include <encoding/payloads/sa_payload.h>
23 #include <encoding/payloads/ts_payload.h>
24
25 typedef struct private_pretend_auth_t private_pretend_auth_t;
26
27 /**
28 * Private data of an pretend_auth_t object.
29 */
30 struct private_pretend_auth_t {
31
32 /**
33 * Implements the hook_t interface.
34 */
35 hook_t hook;
36
37 /**
38 * remote peer identity
39 */
40 identification_t *id;
41
42 /**
43 * reserved bytes of ID payload
44 */
45 char reserved[3];
46
47 /**
48 * IKE_SA_INIT data for signature
49 */
50 chunk_t ike_init;
51
52 /**
53 * Nonce for signature
54 */
55 chunk_t nonce;
56
57 /**
58 * Selected CHILD_SA proposal
59 */
60 proposal_t *proposal;
61
62 /**
63 * List of initiators Traffic Selectors
64 */
65 linked_list_t *tsi;
66
67 /**
68 * List of responders Traffic Selectors
69 */
70 linked_list_t *tsr;
71 };
72
73 /**
74 * Process IKE_SA_INIT request message, outgoing
75 */
76 static void process_init_request(private_pretend_auth_t *this,
77 ike_sa_t *ike_sa, message_t *message)
78 {
79 nonce_payload_t *nonce;
80
81 nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
82 if (nonce)
83 {
84 free(this->nonce.ptr);
85 this->nonce = nonce->get_nonce(nonce);
86 }
87 }
88
89 /**
90 * Process IKE_AUTH request message, outgoing
91 */
92 static void process_auth_request(private_pretend_auth_t *this,
93 ike_sa_t *ike_sa, message_t *message)
94 {
95 id_payload_t *id;
96 sa_payload_t *sa;
97 ts_payload_t *tsi, *tsr;
98 linked_list_t *proposals;
99
100 id = (id_payload_t*)message->get_payload(message, ID_RESPONDER);
101 if (id)
102 {
103 this->id->destroy(this->id);
104 this->id = id->get_identification(id);
105 }
106 sa = (sa_payload_t*)message->get_payload(message, SECURITY_ASSOCIATION);
107 if (sa)
108 {
109 proposals = sa->get_proposals(sa);
110 proposals->remove_first(proposals, (void**)&this->proposal);
111 if (this->proposal)
112 {
113 this->proposal->set_spi(this->proposal, htonl(0x12345678));
114 }
115 proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
116 }
117 tsi = (ts_payload_t*)message->get_payload(message,
118 TRAFFIC_SELECTOR_INITIATOR);
119 if (tsi)
120 {
121 this->tsi = tsi->get_traffic_selectors(tsi);
122 }
123 tsr = (ts_payload_t*)message->get_payload(message,
124 TRAFFIC_SELECTOR_RESPONDER);
125 if (tsr)
126 {
127 this->tsr = tsr->get_traffic_selectors(tsr);
128 }
129
130 }
131
132 /**
133 * Process IKE_SA_INIT response message, incoming
134 */
135 static void process_init_response(private_pretend_auth_t *this,
136 ike_sa_t *ike_sa, message_t *message)
137 {
138 this->ike_init = message->get_packet_data(message);
139 }
140
141 /**
142 * Build CERT payloads
143 */
144 static void build_certs(private_pretend_auth_t *this,
145 ike_sa_t *ike_sa, message_t *message, auth_cfg_t *auth)
146 {
147 enumerator_t *enumerator;
148 cert_payload_t *payload;
149 certificate_t *cert;
150 auth_rule_t type;
151
152 /* get subject cert first, then issuing certificates */
153 cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
154 if (cert)
155 {
156 payload = cert_payload_create_from_cert(cert);
157 if (payload)
158 {
159 DBG1(DBG_IKE, "pretending end entity cert \"%Y\"",
160 cert->get_subject(cert));
161 message->add_payload(message, (payload_t*)payload);
162 }
163 }
164 enumerator = auth->create_enumerator(auth);
165 while (enumerator->enumerate(enumerator, &type, &cert))
166 {
167 if (type == AUTH_RULE_IM_CERT)
168 {
169 payload = cert_payload_create_from_cert(cert);
170 if (payload)
171 {
172 DBG1(DBG_IKE, "pretending issuer cert \"%Y\"",
173 cert->get_subject(cert));
174 message->add_payload(message, (payload_t*)payload);
175 }
176 }
177 }
178 enumerator->destroy(enumerator);
179 }
180
181 /**
182 * Build faked AUTH payload
183 */
184 static bool build_auth(private_pretend_auth_t *this,
185 ike_sa_t *ike_sa, message_t *message)
186 {
187 chunk_t octets, auth_data;
188 private_key_t *private;
189 auth_cfg_t *auth;
190 auth_payload_t *auth_payload;
191 auth_method_t auth_method;
192 signature_scheme_t scheme;
193 keymat_t *keymat;
194
195 auth = auth_cfg_create();
196 private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, this->id, auth);
197 build_certs(this, ike_sa, message, auth);
198 auth->destroy(auth);
199 if (private == NULL)
200 {
201 DBG1(DBG_CFG, "no private key found for '%Y' to pretend AUTH", this->id);
202 return FALSE;
203 }
204
205 switch (private->get_type(private))
206 {
207 case KEY_RSA:
208 scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
209 auth_method = AUTH_RSA;
210 break;
211 case KEY_ECDSA:
212 /* we try to deduct the signature scheme from the keysize */
213 switch (private->get_keysize(private))
214 {
215 case 256:
216 scheme = SIGN_ECDSA_256;
217 auth_method = AUTH_ECDSA_256;
218 break;
219 case 384:
220 scheme = SIGN_ECDSA_384;
221 auth_method = AUTH_ECDSA_384;
222 break;
223 case 521:
224 scheme = SIGN_ECDSA_521;
225 auth_method = AUTH_ECDSA_521;
226 break;
227 default:
228 DBG1(DBG_CFG, "%d bit ECDSA private key size not supported",
229 private->get_keysize(private));
230 return FALSE;
231 }
232 break;
233 default:
234 DBG1(DBG_CFG, "private key of type %N not supported",
235 key_type_names, private->get_type(private));
236 return FALSE;
237 }
238 keymat = ike_sa->get_keymat(ike_sa);
239 octets = keymat->get_auth_octets(keymat, TRUE, this->ike_init,
240 this->nonce, this->id, this->reserved);
241 if (!private->sign(private, scheme, octets, &auth_data))
242 {
243 chunk_free(&octets);
244 private->destroy(private);
245 return FALSE;
246 }
247 auth_payload = auth_payload_create();
248 auth_payload->set_auth_method(auth_payload, auth_method);
249 auth_payload->set_data(auth_payload, auth_data);
250 chunk_free(&auth_data);
251 chunk_free(&octets);
252 private->destroy(private);
253 message->add_payload(message, (payload_t*)auth_payload);
254 DBG1(DBG_CFG, "pretending AUTH payload for '%Y' with %N",
255 this->id, auth_method_names, auth_method);
256 return TRUE;
257 }
258
259 /**
260 * Process IKE_AUTH response message, incoming
261 */
262 static void process_auth_response(private_pretend_auth_t *this,
263 ike_sa_t *ike_sa, message_t *message)
264 {
265 enumerator_t *enumerator;
266 payload_t *payload;
267
268 /* check for, and remove AUTHENTICATION_FAILED notify */
269 enumerator = message->create_payload_enumerator(message);
270 while (enumerator->enumerate(enumerator, &payload))
271 {
272 notify_payload_t *notify = (notify_payload_t*)payload;
273
274 if (payload->get_type(payload) != NOTIFY ||
275 notify->get_notify_type(notify) != AUTHENTICATION_FAILED)
276 {
277 DBG1(DBG_CFG, "no %N notify found, disabling AUTH pretending",
278 notify_type_names, AUTHENTICATION_FAILED);
279 enumerator->destroy(enumerator);
280 return;
281 }
282 message->remove_payload_at(message, enumerator);
283 payload->destroy(payload);
284 }
285 enumerator->destroy(enumerator);
286
287 if (!build_auth(this, ike_sa, message))
288 {
289 message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
290 return;
291 }
292 message->add_payload(message, (payload_t*)
293 id_payload_create_from_identification(ID_RESPONDER, this->id));
294 if (this->proposal)
295 {
296 message->add_payload(message, (payload_t*)
297 sa_payload_create_from_proposal(this->proposal));
298 }
299 if (this->tsi)
300 {
301 message->add_payload(message, (payload_t*)
302 ts_payload_create_from_traffic_selectors(TRUE, this->tsi));
303 }
304 if (this->tsr)
305 {
306 message->add_payload(message, (payload_t*)
307 ts_payload_create_from_traffic_selectors(FALSE, this->tsr));
308 }
309 }
310
311 METHOD(listener_t, message, bool,
312 private_pretend_auth_t *this, ike_sa_t *ike_sa, message_t *message,
313 bool incoming)
314 {
315 if (incoming)
316 {
317 if (!message->get_request(message))
318 {
319 if (message->get_exchange_type(message) == IKE_SA_INIT)
320 {
321 process_init_response(this, ike_sa, message);
322 }
323 if (message->get_exchange_type(message) == IKE_AUTH &&
324 message->get_message_id(message) == 1)
325 {
326 process_auth_response(this, ike_sa, message);
327 }
328 }
329 }
330 else
331 {
332 if (message->get_request(message))
333 {
334 if (message->get_exchange_type(message) == IKE_SA_INIT)
335 {
336 process_init_request(this, ike_sa, message);
337 }
338 if (message->get_exchange_type(message) == IKE_AUTH &&
339 message->get_message_id(message) == 1)
340 {
341 process_auth_request(this, ike_sa, message);
342 }
343 }
344 }
345 return TRUE;
346 }
347
348 METHOD(hook_t, destroy, void,
349 private_pretend_auth_t *this)
350 {
351 if (this->tsi)
352 {
353 this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
354 }
355 if (this->tsr)
356 {
357 this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
358 }
359 DESTROY_IF(this->proposal);
360 this->id->destroy(this->id);
361 free(this->ike_init.ptr);
362 free(this->nonce.ptr);
363 free(this);
364 }
365
366 /**
367 * Create the IKE_AUTH fill hook
368 */
369 hook_t *pretend_auth_hook_create(char *name)
370 {
371 private_pretend_auth_t *this;
372
373 INIT(this,
374 .hook = {
375 .listener = {
376 .message = _message,
377 },
378 .destroy = _destroy,
379 },
380 .id = identification_create_from_string(
381 conftest->test->get_str(conftest->test,
382 "hooks.%s.peer", "%any", name)),
383 );
384
385 return &this->hook;
386 }