cosmetics
[strongswan.git] / src / charon / sa / states / responder_init.c
1 /**
2 * @file responder_init.c
3 *
4 * @brief Implementation of responder_init_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2005 Jan Hutter, Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include "responder_init.h"
24
25 #include <daemon.h>
26 #include <sa/states/state.h>
27 #include <sa/states/ike_sa_init_responded.h>
28 #include <encoding/payloads/sa_payload.h>
29 #include <encoding/payloads/ke_payload.h>
30 #include <encoding/payloads/nonce_payload.h>
31 #include <encoding/payloads/notify_payload.h>
32 #include <crypto/diffie_hellman.h>
33
34
35 typedef struct private_responder_init_t private_responder_init_t;
36
37 /**
38 * Private data of a responder_init_t object.
39 *
40 */
41 struct private_responder_init_t {
42 /**
43 * Methods of the state_t interface.
44 */
45 responder_init_t public;
46
47 /**
48 * Assigned IKE_SA.
49 */
50 protected_ike_sa_t *ike_sa;
51
52 /**
53 * Diffie Hellman object used to compute shared secret.
54 */
55 diffie_hellman_t *diffie_hellman;
56
57 /**
58 * Diffie Hellman group number from selected IKE proposal.
59 */
60 u_int16_t dh_group_number;
61
62 /**
63 * Priority used to get matching dh_group number.
64 */
65 u_int16_t dh_group_priority;
66
67 /**
68 * Sent nonce value.
69 *
70 * This value is passed to the next state of type IKE_SA_INIT_RESPONDED.
71 */
72 chunk_t sent_nonce;
73
74 /**
75 * Received nonce value
76 *
77 * This value is passed to the next state of type IKE_SA_INIT_RESPONDED.
78 */
79 chunk_t received_nonce;
80
81 /**
82 * Selected proposal
83 */
84 proposal_t *proposal;
85
86 /**
87 * Logger used to log data .
88 *
89 * Is logger of ike_sa!
90 */
91 logger_t *logger;
92
93 /**
94 * Handles received SA payload and builds the SA payload for the response.
95 *
96 * @param this calling object
97 * @param sa_request The received SA payload
98 * @param response the SA payload is added to this response message_t object.
99 * @return
100 * - DESTROY_ME
101 * - SUCCESS
102 */
103 status_t (*build_sa_payload) (private_responder_init_t *this,sa_payload_t *sa_request, message_t *response);
104
105 /**
106 * Handles received KE payload and builds the KE payload for the response.
107 *
108 * @param this calling object
109 * @param ke_request The received KE payload
110 * @param response the KE payload is added to this response message_t object.
111 * - DESTROY_ME
112 * - SUCCESS
113 */
114 status_t (*build_ke_payload) (private_responder_init_t *this,ke_payload_t *ke_request, message_t *response);
115
116 /**
117 * Handles received NONCE payload and builds the NONCE payload for the response.
118 *
119 * @param this calling object
120 * @param nonce_request The received NONCE payload
121 * @param response the NONCE payload is added to this response message_t object.
122 * - DESTROY_ME
123 * - SUCCESS
124 */
125 status_t (*build_nonce_payload) (private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *response);
126
127 /**
128 * Sends a IKE_SA_INIT reply containing a notify payload.
129 *
130 * @param this calling object
131 * @param notify_payload notify_payload to process
132 */
133 status_t (*process_notify_payload) (private_responder_init_t *this, notify_payload_t *notify_payload);
134
135 /**
136 * Destroy function called internally of this class after change
137 * to state IKE_SA_INIT_RESPONDED succeeded.
138 *
139 * This destroy function does not destroy objects which were passed to the new state.
140 *
141 * @param this calling object
142 */
143 void (*destroy_after_state_change) (private_responder_init_t *this);
144
145 };
146
147 /**
148 * Implementation of state_t.process_message.
149 */
150 static status_t process_message(private_responder_init_t *this, message_t *message)
151 {
152 ike_sa_init_responded_t *next_state;
153 chunk_t ike_sa_init_response_data;
154 chunk_t ike_sa_init_request_data;
155 sa_payload_t *sa_request = NULL;
156 ke_payload_t *ke_request = NULL;
157 nonce_payload_t *nonce_request = NULL;
158 host_t *source, *destination;
159 connection_t *connection;
160 iterator_t *payloads;
161 message_t *response;
162 status_t status;
163
164 if (message->get_exchange_type(message) != IKE_SA_INIT)
165 {
166 this->logger->log(this->logger, ERROR | LEVEL1, "message of type %s not supported in state responder_init",
167 mapping_find(exchange_type_m,message->get_exchange_type(message)));
168 return DESTROY_ME;
169 }
170 if (!message->get_request(message))
171 {
172 this->logger->log(this->logger, ERROR | LEVEL1, "IKE_SA_INIT responses not allowed in state ike_sa_init_responded");
173 return DESTROY_ME;
174 }
175
176 /* this is the first message to process, so get host infos */
177 source = message->get_source(message);
178 destination = message->get_destination(message);
179
180 connection = charon->connections->get_connection_by_hosts(charon->connections, destination, source);
181 if (connection == NULL)
182 {
183 /* no configuration matches given hosts */
184 this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request does not match any available connection, deleting IKE_SA");
185 /* TODO: inform requestor */
186 return DESTROY_ME;
187 }
188 this->ike_sa->set_connection(this->ike_sa,connection);
189
190 /* parse incoming message */
191 status = message->parse_body(message, NULL, NULL);
192 if (status != SUCCESS)
193 {
194 if (status == NOT_SUPPORTED)
195 {
196 this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request contains unsupported payload with critical flag set, "
197 "deleting IKE_SA");
198 this->ike_sa->send_notify(this->ike_sa, IKE_SA_INIT, UNSUPPORTED_CRITICAL_PAYLOAD, CHUNK_INITIALIZER);
199 }
200 else
201 {
202 this->logger->log(this->logger, AUDIT, "unable to parse IKE_SA_INIT request, deleting IKE_SA");
203 }
204 return DESTROY_ME;
205 }
206
207 payloads = message->get_payload_iterator(message);
208 while (payloads->has_next(payloads))
209 {
210 payload_t *payload;
211
212 payloads->current(payloads, (void**)&payload);
213
214 switch (payload->get_type(payload))
215 {
216 case SECURITY_ASSOCIATION:
217 {
218 sa_request = (sa_payload_t*)payload;
219 break;
220 }
221 case KEY_EXCHANGE:
222 {
223 ke_request = (ke_payload_t*)payload;
224 break;
225 }
226 case NONCE:
227 {
228 nonce_request = (nonce_payload_t*)payload;
229 break;
230 }
231 case NOTIFY:
232 {
233 notify_payload_t *notify_payload = (notify_payload_t *) payload;
234 status = this->process_notify_payload(this, notify_payload);
235 if (status != SUCCESS)
236 {
237 payloads->destroy(payloads);
238 return status;
239 }
240 }
241 default:
242 {
243 this->logger->log(this->logger, ERROR|LEVEL1, "ignoring payload %s (%d)",
244 mapping_find(payload_type_m, payload->get_type(payload)), payload->get_type(payload));
245 break;
246 }
247 }
248 }
249 payloads->destroy(payloads);
250
251 /* check if we have all payloads */
252 if (!(sa_request && ke_request && nonce_request))
253 {
254 this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain all required payloads. deleting IKE_SA");
255 return DESTROY_ME;
256 }
257
258 this->ike_sa->build_message(this->ike_sa, IKE_SA_INIT, FALSE, &response);
259
260 status = this->build_sa_payload(this, sa_request, response);
261 if (status != SUCCESS)
262 {
263 response->destroy(response);
264 return status;
265 }
266
267 status = this->build_ke_payload(this, ke_request, response);
268 if (status != SUCCESS)
269 {
270 response->destroy(response);
271 return status;
272 }
273
274 status = this->build_nonce_payload(this, nonce_request, response);
275 if (status != SUCCESS)
276 {
277 response->destroy(response);
278 return status;
279 }
280
281 /* derive all the keys used in the IKE_SA */
282 status = this->ike_sa->build_transforms(this->ike_sa, this->proposal, this->diffie_hellman, this->received_nonce, this->sent_nonce);
283 if (status != SUCCESS)
284 {
285 this->logger->log(this->logger, AUDIT, "transform objects could not be created from selected proposal, deleting IKE_SA");
286 return DESTROY_ME;
287 }
288
289 /* message can now be sent (must not be destroyed) */
290 status = this->ike_sa->send_response(this->ike_sa, response);
291 if (status != SUCCESS)
292 {
293 this->logger->log(this->logger, AUDIT, "unable to send IKE_SA_INIT response, deleting IKE_SA");
294 response->destroy(response);
295 return DESTROY_ME;
296 }
297
298 /* state can now be changed */
299 this->logger->log(this->logger, CONTROL|LEVEL2, "create next state object of type IKE_SA_INIT_RESPONDED");
300
301 response = this->ike_sa->get_last_responded_message(this->ike_sa);
302 ike_sa_init_response_data = response->get_packet_data(response);
303 ike_sa_init_request_data = message->get_packet_data(message);
304
305 next_state = ike_sa_init_responded_create(this->ike_sa, this->received_nonce, this->sent_nonce,ike_sa_init_request_data,
306 ike_sa_init_response_data);
307
308 /* state can now be changed */
309 this->ike_sa->set_new_state(this->ike_sa, (state_t *) next_state);
310 this->destroy_after_state_change(this);
311
312 return SUCCESS;
313 }
314
315 /**
316 * Implementation of private_initiator_init_t.build_sa_payload.
317 */
318 static status_t build_sa_payload(private_responder_init_t *this,sa_payload_t *sa_request, message_t *response)
319 {
320 proposal_t *proposal;
321 linked_list_t *proposal_list;
322 connection_t *connection;
323 sa_payload_t* sa_payload;
324 algorithm_t *algo;
325
326 connection = this->ike_sa->get_connection(this->ike_sa);
327
328 this->logger->log(this->logger, CONTROL | LEVEL2, "process received SA payload");
329
330 /* get the list of suggested proposals */
331 proposal_list = sa_request->get_proposals (sa_request);
332
333 /* select proposal */
334 this->proposal = connection->select_proposal(connection, proposal_list);
335 while(proposal_list->remove_last(proposal_list, (void**)&proposal) == SUCCESS)
336 {
337 proposal->destroy(proposal);
338 }
339 proposal_list->destroy(proposal_list);
340 if (this->proposal == NULL)
341 {
342 this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain any acceptable proposals, deleting IKE_SA");
343 this->ike_sa->send_notify(this->ike_sa, IKE_SA_INIT, NO_PROPOSAL_CHOSEN, CHUNK_INITIALIZER);
344 return DESTROY_ME;
345 }
346 /* get selected DH group to force policy, this is very restrictive!? */
347 this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, &algo);
348 this->dh_group_number = algo->algorithm;
349
350 this->logger->log(this->logger, CONTROL | LEVEL2, "SA Payload processed");
351
352 this->logger->log(this->logger, CONTROL|LEVEL2, "building SA payload");
353 sa_payload = sa_payload_create_from_proposal(this->proposal);
354 this->logger->log(this->logger, CONTROL|LEVEL2, "add SA payload to message");
355 response->add_payload(response,(payload_t *) sa_payload);
356
357 return SUCCESS;
358 }
359
360 /**
361 * Implementation of private_initiator_init_t.build_ke_payload.
362 */
363 static status_t build_ke_payload(private_responder_init_t *this,ke_payload_t *ke_request, message_t *response)
364 {
365 diffie_hellman_group_t group;
366 ke_payload_t *ke_payload;
367 diffie_hellman_t *dh;
368 chunk_t key_data;
369
370 this->logger->log(this->logger, CONTROL | LEVEL2, "process received KE payload");
371 group = ke_request->get_dh_group_number(ke_request);
372
373 if (group == MODP_NONE)
374 {
375 this->logger->log(this->logger, AUDIT, "no Diffie-Hellman group to select, deleting IKE_SA");
376 return DESTROY_ME;
377 }
378
379 if (this->dh_group_number != group)
380 {
381 u_int16_t accepted_group;
382 chunk_t accepted_group_chunk;
383 /* group not same as selected one
384 * Maybe key exchange payload is before SA payload */
385 this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain an acceptable Diffie-Hellman group, deleting IKE_SA");
386
387 accepted_group = htons(this->dh_group_number);
388 accepted_group_chunk.ptr = (u_int8_t*) &(accepted_group);
389 accepted_group_chunk.len = 2;
390 this->ike_sa->send_notify(this->ike_sa,IKE_SA_INIT,INVALID_KE_PAYLOAD,accepted_group_chunk);
391 return DESTROY_ME;
392 }
393
394 /* create diffie hellman object to handle DH exchange */
395 dh = diffie_hellman_create(group);
396 if (dh == NULL)
397 {
398 this->logger->log(this->logger, AUDIT, "could not generate DH object with group %d, deleting IKE_SA",
399 mapping_find(diffie_hellman_group_m,group) );
400 return DESTROY_ME;
401 }
402 this->logger->log(this->logger, CONTROL | LEVEL2, "set other DH public value");
403
404 dh->set_other_public_value(dh, ke_request->get_key_exchange_data(ke_request));
405
406 this->diffie_hellman = dh;
407
408 this->logger->log(this->logger, CONTROL | LEVEL2, "KE payload processed.");
409
410 this->logger->log(this->logger, CONTROL|LEVEL2, "building KE payload");
411 this->diffie_hellman->get_my_public_value(this->diffie_hellman,&key_data);
412
413 ke_payload = ke_payload_create();
414 ke_payload->set_key_exchange_data(ke_payload,key_data);
415 ke_payload->set_dh_group_number(ke_payload, this->dh_group_number);
416 chunk_free(&key_data);
417
418 this->logger->log(this->logger, CONTROL|LEVEL2, "add KE payload to message");
419 response->add_payload(response,(payload_t *) ke_payload);
420
421 return SUCCESS;
422 }
423
424 /**
425 * Implementation of private_responder_init_t.build_nonce_payload.
426 */
427 static status_t build_nonce_payload(private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *response)
428 {
429 nonce_payload_t *nonce_payload;
430 randomizer_t *randomizer;
431 status_t status;
432
433 this->logger->log(this->logger, CONTROL | LEVEL2, "process received NONCE payload");
434 free(this->received_nonce.ptr);
435 this->received_nonce = CHUNK_INITIALIZER;
436
437 this->logger->log(this->logger, CONTROL | LEVEL2, "get NONCE value and store it");
438 this->received_nonce = nonce_request->get_nonce(nonce_request);
439
440 this->logger->log(this->logger, CONTROL | LEVEL2, "create new NONCE value.");
441
442 randomizer = this->ike_sa->get_randomizer(this->ike_sa);
443 status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE, &(this->sent_nonce));
444 if (status != SUCCESS)
445 {
446 return status;
447 }
448
449 this->logger->log(this->logger, CONTROL|LEVEL2, "building NONCE payload");
450 nonce_payload = nonce_payload_create();
451 nonce_payload->set_nonce(nonce_payload, this->sent_nonce);
452
453 this->logger->log(this->logger, CONTROL|LEVEL2, "add NONCE payload to message");
454 response->add_payload(response,(payload_t *) nonce_payload);
455
456 return SUCCESS;
457 }
458
459 /**
460 * Implementation of private_responder_init_t.process_notify_payload.
461 */
462 static status_t process_notify_payload(private_responder_init_t *this, notify_payload_t *notify_payload)
463 {
464 notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload);
465
466 this->logger->log(this->logger, CONTROL|LEVEL1, "process notify type %s",
467 mapping_find(notify_message_type_m, notify_message_type));
468
469 if (notify_payload->get_protocol_id(notify_payload) != PROTO_IKE)
470 {
471 this->logger->log(this->logger, ERROR | LEVEL1, "notify reply not for IKE protocol.");
472 return FAILED;
473 }
474 switch (notify_message_type)
475 {
476 default:
477 {
478 this->logger->log(this->logger, CONTROL, "IKE_SA_INIT request contained a notify (%d), ignored.",
479 notify_message_type);
480 return SUCCESS;
481 }
482 }
483 }
484
485 /**
486 * Implementation of state_t.get_state.
487 */
488 static ike_sa_state_t get_state(private_responder_init_t *this)
489 {
490 return RESPONDER_INIT;
491 }
492
493 /**
494 * Implementation of state_t.destroy.
495 */
496 static void destroy(private_responder_init_t *this)
497 {
498 this->logger->log(this->logger, CONTROL | LEVEL1, "going to destroy responder init state object");
499
500 this->logger->log(this->logger, CONTROL | LEVEL2, "destroy nonces");
501 chunk_free(&(this->sent_nonce));
502 chunk_free(&(this->received_nonce));
503
504 if (this->diffie_hellman != NULL)
505 {
506 this->logger->log(this->logger, CONTROL | LEVEL2, "destroy diffie_hellman_t hellman object");
507 this->diffie_hellman->destroy(this->diffie_hellman);
508 }
509 if (this->proposal)
510 {
511 this->proposal->destroy(this->proposal);
512 }
513 this->logger->log(this->logger, CONTROL | LEVEL2, "destroy object");
514 free(this);
515 }
516
517 /**
518 * Implementation of private_responder_init_t.destroy_after_state_change
519 */
520 static void destroy_after_state_change (private_responder_init_t *this)
521 {
522 this->logger->log(this->logger, CONTROL | LEVEL1, "going to destroy responder_init_t state object");
523
524 /* destroy diffie hellman object */
525 if (this->diffie_hellman != NULL)
526 {
527 this->logger->log(this->logger, CONTROL | LEVEL2, "destroy diffie_hellman_t object");
528 this->diffie_hellman->destroy(this->diffie_hellman);
529 }
530 if (this->proposal)
531 {
532 this->proposal->destroy(this->proposal);
533 }
534
535 this->logger->log(this->logger, CONTROL | LEVEL2, "destroy object");
536 free(this);
537 }
538
539 /*
540 * Described in header.
541 */
542 responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa)
543 {
544 private_responder_init_t *this = malloc_thing(private_responder_init_t);
545
546 /* interface functions */
547 this->public.state_interface.process_message = (status_t (*) (state_t *,message_t *)) process_message;
548 this->public.state_interface.get_state = (ike_sa_state_t (*) (state_t *)) get_state;
549 this->public.state_interface.destroy = (void (*) (state_t *)) destroy;
550
551 /* private functions */
552 this->build_sa_payload = build_sa_payload;
553 this->build_ke_payload = build_ke_payload;
554 this->build_nonce_payload = build_nonce_payload;
555 this->destroy_after_state_change = destroy_after_state_change;
556 this->process_notify_payload = process_notify_payload;
557
558 /* private data */
559 this->ike_sa = ike_sa;
560 this->logger = logger_manager->get_logger(logger_manager, IKE_SA);
561 this->sent_nonce = CHUNK_INITIALIZER;
562 this->received_nonce = CHUNK_INITIALIZER;
563 this->dh_group_number = MODP_NONE;
564 this->diffie_hellman = NULL;
565 this->proposal = NULL;
566
567 return &(this->public);
568 }