- using random SPIs now, but without collision checks
[strongswan.git] / src / charon / charon / threads / stroke_interface.c
1 /**
2 * @file stroke.c
3 *
4 * @brief Implementation of stroke_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2006 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 <stdlib.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <sys/fcntl.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 #include <errno.h>
32 #include <pthread.h>
33
34 #include "stroke_interface.h"
35
36 #include <stroke.h>
37 #include <types.h>
38 #include <daemon.h>
39 #include <crypto/x509.h>
40 #include <queues/jobs/initiate_ike_sa_job.h>
41
42
43 struct sockaddr_un socket_addr = { AF_UNIX, STROKE_SOCKET};
44
45
46 typedef struct private_stroke_t private_stroke_t;
47
48 /**
49 * Private data of an stroke_t object.
50 */
51 struct private_stroke_t {
52
53 /**
54 * Public part of stroke_t object.
55 */
56 stroke_t public;
57
58 /**
59 * Assigned logger_t object in charon.
60 */
61 logger_t *logger;
62
63 /**
64 * Logger which logs to stroke
65 */
66 logger_t *stroke_logger;
67
68 /**
69 * Unix socket to listen for strokes
70 */
71 int socket;
72
73 /**
74 * Thread which reads from the socket
75 */
76 pthread_t assigned_thread;
77
78 /**
79 * Read from the socket and handle stroke messages
80 */
81 void (*stroke_receive) (private_stroke_t *this);
82 };
83
84 /**
85 * Helper function which corrects the string pointers
86 * in a stroke_msg_t. Strings in a stroke_msg sent over "wire"
87 * contains RELATIVE addresses (relative to the beginning of the
88 * stroke_msg). They must be corrected if they reach our address
89 * space...
90 */
91 static void pop_string(stroke_msg_t *msg, char **string)
92 {
93 /* check for sanity of string pointer and string */
94 if (*string == NULL)
95 {
96 *string = "";
97 }
98 else if (string < (char**)msg ||
99 string > (char**)msg + sizeof(stroke_msg_t) ||
100 *string < (char*)msg->buffer - (u_int)msg ||
101 *string > (char*)(u_int)msg->length)
102 {
103 *string = "(invalid char* in stroke msg)";
104 }
105 else
106 {
107 *string = (char*)msg + (u_int)*string;
108 }
109 }
110
111 /**
112 * Add a connection to the configuration list
113 */
114 static void stroke_add_conn(private_stroke_t *this, stroke_msg_t *msg)
115 {
116 connection_t *connection;
117 policy_t *policy;
118 identification_t *my_id, *other_id;
119 host_t *my_host, *other_host, *my_subnet, *other_subnet;
120 proposal_t *proposal;
121 traffic_selector_t *my_ts, *other_ts;
122 x509_t *cert;
123
124 pop_string(msg, &msg->add_conn.name);
125 pop_string(msg, &msg->add_conn.me.address);
126 pop_string(msg, &msg->add_conn.other.address);
127 pop_string(msg, &msg->add_conn.me.id);
128 pop_string(msg, &msg->add_conn.other.id);
129 pop_string(msg, &msg->add_conn.me.cert);
130 pop_string(msg, &msg->add_conn.other.cert);
131 pop_string(msg, &msg->add_conn.me.subnet);
132 pop_string(msg, &msg->add_conn.other.subnet);
133
134 this->logger->log(this->logger, CONTROL, "received stroke: add connection \"%s\"", msg->add_conn.name);
135
136 my_host = host_create(AF_INET, msg->add_conn.me.address, 500);
137 if (my_host == NULL)
138 {
139 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid host: %s", msg->add_conn.me.address);
140 return;
141 }
142 other_host = host_create(AF_INET, msg->add_conn.other.address, 500);
143 if (other_host == NULL)
144 {
145 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid host: %s", msg->add_conn.other.address);
146 my_host->destroy(my_host);
147 return;
148 }
149 my_id = identification_create_from_string(*msg->add_conn.me.id ?
150 msg->add_conn.me.id : msg->add_conn.me.address);
151 if (my_id == NULL)
152 {
153 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid id: %s", msg->add_conn.me.id);
154 my_host->destroy(my_host);
155 other_host->destroy(other_host);
156 return;
157 }
158 other_id = identification_create_from_string(*msg->add_conn.other.id ?
159 msg->add_conn.other.id : msg->add_conn.other.address);
160 if (other_id == NULL)
161 {
162 my_host->destroy(my_host);
163 other_host->destroy(other_host);
164 my_id->destroy(my_id);
165 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid id: %s", msg->add_conn.other.id);
166 return;
167 }
168
169 my_subnet = host_create(AF_INET, *msg->add_conn.me.subnet ? msg->add_conn.me.subnet : msg->add_conn.me.address, 500);
170 if (my_subnet == NULL)
171 {
172 my_host->destroy(my_host);
173 other_host->destroy(other_host);
174 my_id->destroy(my_id);
175 other_id->destroy(other_id);
176 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid subnet: %s", msg->add_conn.me.subnet);
177 return;
178 }
179
180 other_subnet = host_create(AF_INET, *msg->add_conn.other.subnet ? msg->add_conn.other.subnet : msg->add_conn.other.address, 500);
181 if (other_subnet == NULL)
182 {
183 my_host->destroy(my_host);
184 other_host->destroy(other_host);
185 my_id->destroy(my_id);
186 other_id->destroy(other_id);
187 my_subnet->destroy(my_subnet);
188 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid subnet: %s", msg->add_conn.me.subnet);
189 return;
190 }
191
192 my_ts = traffic_selector_create_from_subnet(my_subnet, *msg->add_conn.me.subnet ? msg->add_conn.me.subnet_mask : 32);
193 my_subnet->destroy(my_subnet);
194 other_ts = traffic_selector_create_from_subnet(other_subnet, *msg->add_conn.other.subnet ? msg->add_conn.other.subnet_mask : 32);
195 other_subnet->destroy(other_subnet);
196
197 if (charon->socket->is_listening_on(charon->socket, other_host))
198 {
199 this->stroke_logger->log(this->stroke_logger, CONTROL|LEVEL1, "left is other host, switching");
200
201 host_t *tmp_host = my_host;
202 identification_t *tmp_id = my_id;
203 traffic_selector_t *tmp_ts = my_ts;
204 char *tmp_cert = msg->add_conn.me.cert;
205
206 my_host = other_host;
207 other_host = tmp_host;
208 my_id = other_id;
209 other_id = tmp_id;
210 my_ts = other_ts;
211 other_ts = tmp_ts;
212 msg->add_conn.me.cert = msg->add_conn.other.cert;
213 msg->add_conn.other.cert = tmp_cert;
214 }
215 else if (charon->socket->is_listening_on(charon->socket, my_host))
216 {
217 this->stroke_logger->log(this->stroke_logger, CONTROL|LEVEL1, "left is own host, not switching");
218 }
219 else
220 {
221 this->stroke_logger->log(this->stroke_logger, ERROR, "left nor right host is our, aborting");
222
223 my_host->destroy(my_host);
224 other_host->destroy(other_host);
225 my_id->destroy(my_id);
226 other_id->destroy(other_id);
227 my_ts->destroy(my_ts);
228 other_ts->destroy(other_ts);
229 return;
230 }
231
232 if (msg->add_conn.me.cert)
233 {
234 char file[128];
235 snprintf(file, sizeof(file), "%s/%s", CERTIFICATE_DIR, msg->add_conn.me.cert);
236 cert = x509_create_from_file(file);
237 if (cert)
238 {
239 my_id->destroy(my_id);
240 my_id = cert->get_subject(cert);
241 my_id = my_id->clone(my_id);
242 cert->destroy(cert);
243 this->logger->log(this->logger, CONTROL,
244 "valid certificate with ID \"%s\"",
245 my_id->get_string(my_id));
246 }
247 }
248 if (msg->add_conn.other.cert)
249 {
250 char file[128];
251 snprintf(file, sizeof(file), "%s/%s", CERTIFICATE_DIR, msg->add_conn.other.cert);
252 cert = x509_create_from_file(file);
253 if (cert)
254 {
255 other_id->destroy(other_id);
256 other_id = cert->get_subject(cert);
257 other_id = other_id->clone(other_id);
258 cert->destroy(cert);
259 this->logger->log(this->logger, CONTROL,
260 "valid certificate with ID \"%s\"",
261 other_id->get_string(other_id));
262 }
263 }
264
265 connection = connection_create(msg->add_conn.name,
266 my_host, other_host,
267 my_id->clone(my_id), other_id->clone(other_id),
268 RSA_DIGITAL_SIGNATURE);
269 proposal = proposal_create(1);
270 proposal->add_algorithm(proposal, PROTO_IKE, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16);
271 proposal->add_algorithm(proposal, PROTO_IKE, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0);
272 proposal->add_algorithm(proposal, PROTO_IKE, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0);
273 proposal->add_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA1, 0);
274 proposal->add_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 0);
275 proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0);
276 proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_1536_BIT, 0);
277 proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0);
278 proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_4096_BIT, 0);
279 proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_8192_BIT, 0);
280 connection->add_proposal(connection, proposal);
281 /* add to global connection list */
282 charon->connections->add_connection(charon->connections, connection);
283
284 policy = policy_create(my_id, other_id);
285 proposal = proposal_create(1);
286 proposal->add_algorithm(proposal, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16);
287 proposal->add_algorithm(proposal, PROTO_ESP, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0);
288 proposal->add_algorithm(proposal, PROTO_ESP, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0);
289 policy->add_proposal(policy, proposal);
290 policy->add_my_traffic_selector(policy, my_ts);
291 policy->add_other_traffic_selector(policy, other_ts);
292 /* add to global policy list */
293 charon->policies->add_policy(charon->policies, policy);
294
295 this->stroke_logger->log(this->stroke_logger, CONTROL|LEVEL1, "connection \"%s\" added", msg->add_conn.name);
296 }
297
298 /**
299 * initiate a connection by name
300 */
301 static void stroke_initiate(private_stroke_t *this, stroke_msg_t *msg)
302 {
303 initiate_ike_sa_job_t *job;
304 connection_t *connection;
305
306 pop_string(msg, &(msg->initiate.name));
307 this->logger->log(this->logger, CONTROL, "received stroke: initiate \"%s\"", msg->initiate.name);
308 connection = charon->connections->get_connection_by_name(charon->connections, msg->initiate.name);
309 if (connection == NULL)
310 {
311 this->stroke_logger->log(this->stroke_logger, ERROR, "no connection named \"%s\"", msg->initiate.name);
312 }
313 else
314 {
315 job = initiate_ike_sa_job_create(connection);
316 charon->job_queue->add(charon->job_queue, (job_t*)job);
317 }
318 }
319
320 /**
321 * terminate a connection by name
322 */
323 static void stroke_terminate(private_stroke_t *this, stroke_msg_t *msg)
324 {
325 linked_list_t *ike_sas;
326 iterator_t *iterator;
327 int instances = 0;
328
329 pop_string(msg, &(msg->terminate.name));
330 this->logger->log(this->logger, CONTROL, "received stroke: terminate \"%s\"", msg->terminate.name);
331
332 ike_sas = charon->ike_sa_manager->get_ike_sa_list_by_name(charon->ike_sa_manager, msg->terminate.name);
333
334 iterator = ike_sas->create_iterator(ike_sas, TRUE);
335 while (iterator->has_next(iterator))
336 {
337 ike_sa_id_t *ike_sa_id;
338 iterator->current(iterator, (void**)&ike_sa_id);
339 charon->ike_sa_manager->delete(charon->ike_sa_manager, ike_sa_id);
340 ike_sa_id->destroy(ike_sa_id);
341 instances++;
342 }
343 iterator->destroy(iterator);
344 ike_sas->destroy(ike_sas);
345 this->stroke_logger->log(this->stroke_logger, CONTROL, "terminated %d instances of %s", instances, msg->terminate.name);
346 }
347
348 /**
349 * show status of (established) connections
350 */
351 static void stroke_status(private_stroke_t *this, stroke_msg_t *msg)
352 {
353 if (msg->status.name)
354 {
355 pop_string(msg, &(msg->status.name));
356 }
357 charon->ike_sa_manager->log_status(charon->ike_sa_manager, this->stroke_logger, msg->status.name);
358 }
359
360 logger_context_t get_context(char *context)
361 {
362 if (strcasecmp(context, "ALL") == 0) return ALL_LOGGERS;
363 else if (strcasecmp(context, "PARSR") == 0) return PARSER;
364 else if (strcasecmp(context, "GNRAT") == 0) return GENERATOR;
365 else if (strcasecmp(context, "IKESA") == 0) return IKE_SA;
366 else if (strcasecmp(context, "SAMGR") == 0) return IKE_SA_MANAGER;
367 else if (strcasecmp(context, "CHDSA") == 0) return CHILD_SA;
368 else if (strcasecmp(context, "MESSG") == 0) return MESSAGE;
369 else if (strcasecmp(context, "TPOOL") == 0) return THREAD_POOL;
370 else if (strcasecmp(context, "WORKR") == 0) return WORKER;
371 else if (strcasecmp(context, "SCHED") == 0) return SCHEDULER;
372 else if (strcasecmp(context, "SENDR") == 0) return SENDER;
373 else if (strcasecmp(context, "RECVR") == 0) return RECEIVER;
374 else if (strcasecmp(context, "SOCKT") == 0) return SOCKET;
375 else if (strcasecmp(context, "TESTR") == 0) return TESTER;
376 else if (strcasecmp(context, "DAEMN") == 0) return DAEMON;
377 else if (strcasecmp(context, "CONFG") == 0) return CONFIG;
378 else if (strcasecmp(context, "ENCPL") == 0) return ENCRYPTION_PAYLOAD;
379 else if (strcasecmp(context, "PAYLD") == 0) return PAYLOAD;
380 else return -2;
381 }
382
383 /**
384 * set the type of logged messages in a context
385 */
386 static void stroke_logtype(private_stroke_t *this, stroke_msg_t *msg)
387 {
388 pop_string(msg, &(msg->logtype.context));
389 pop_string(msg, &(msg->logtype.type));
390
391 this->logger->log(this->logger, CONTROL, "received stroke: logtype for %s", msg->logtype.context);
392
393 log_level_t level;
394 logger_context_t context = get_context(msg->logtype.context);
395 if (context == -2)
396 {
397 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid context (%s)!", msg->logtype.context);
398 return;
399 }
400
401 if (strcasecmp(msg->logtype.type, "CONTROL") == 0) level = CONTROL;
402 else if (strcasecmp(msg->logtype.type, "ERROR") == 0) level = ERROR;
403 else if (strcasecmp(msg->logtype.type, "AUDIT") == 0) level = AUDIT;
404 else if (strcasecmp(msg->logtype.type, "RAW") == 0) level = RAW;
405 else if (strcasecmp(msg->logtype.type, "PRIVATE") == 0) level = PRIVATE;
406 else
407 {
408 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid type (%s)!", msg->logtype.type);
409 return;
410 }
411
412 if (msg->logtype.enable)
413 {
414 logger_manager->enable_log_level(logger_manager, context, level);
415 }
416 else
417 {
418 logger_manager->disable_log_level(logger_manager, context, level);
419 }
420 }
421
422 /**
423 * set the verbosity of a logger
424 */
425 static void stroke_loglevel(private_stroke_t *this, stroke_msg_t *msg)
426 {
427 pop_string(msg, &(msg->loglevel.context));
428
429 this->logger->log(this->logger, CONTROL, "received stroke: loglevel for %s", msg->loglevel.context);
430
431 log_level_t level;
432 logger_context_t context = get_context(msg->loglevel.context);
433
434 if (context == -2)
435 {
436 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid context (%s)!", msg->loglevel.context);
437 return;
438 }
439
440 if (msg->loglevel.level == 0)
441 {
442 level = LEVEL0;
443 }
444 else if (msg->loglevel.level == 1)
445 {
446 level = LEVEL1;
447 }
448 else if (msg->loglevel.level == 2)
449 {
450 level = LEVEL2;
451 }
452 else if (msg->loglevel.level == 3)
453 {
454 level = LEVEL3;
455 }
456 else
457 {
458 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid level (%d)!", msg->loglevel.level);
459 return;
460 }
461
462 logger_manager->enable_log_level(logger_manager, context, level);
463 }
464
465 /**
466 * Implementation of private_stroke_t.stroke_receive.
467 */
468 static void stroke_receive(private_stroke_t *this)
469 {
470 stroke_msg_t *msg;
471 u_int16_t msg_length;
472 struct sockaddr_un strokeaddr;
473 int strokeaddrlen = sizeof(strokeaddr);
474 ssize_t bytes_read;
475 int strokefd;
476 FILE *strokefile;
477 int oldstate;
478
479 /* disable cancellation by default */
480 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
481
482 while (1)
483 {
484 /* wait for connections, but allow thread to terminate */
485 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
486 strokefd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen);
487 pthread_setcancelstate(oldstate, NULL);
488
489 if (strokefd < 0)
490 {
491 this->logger->log(this->logger, ERROR, "accepting stroke connection failed: %s", strerror(errno));
492 continue;
493 }
494
495 /* peek the length */
496 bytes_read = recv(strokefd, &msg_length, sizeof(msg_length), MSG_PEEK);
497 if (bytes_read != sizeof(msg_length))
498 {
499 this->logger->log(this->logger, ERROR, "reading lenght of stroke message failed");
500 close(strokefd);
501 continue;
502 }
503
504 /* read message */
505 msg = malloc(msg_length);
506 bytes_read = recv(strokefd, msg, msg_length, 0);
507 if (bytes_read != msg_length)
508 {
509 this->logger->log(this->logger, ERROR, "reading stroke message failed: %s");
510 close(strokefd);
511 continue;
512 }
513
514 strokefile = fdopen(dup(strokefd), "w");
515 if (strokefile == NULL)
516 {
517 this->logger->log(this->logger, ERROR, "opening stroke output channel failed:", strerror(errno));
518 close(strokefd);
519 free(msg);
520 continue;
521 }
522
523 /* TODO: BAD: does not work for starter, charon crashes!! FIX this */
524 this->stroke_logger = logger_create("-", CONTROL|ERROR, FALSE, strokefile);
525
526 this->logger->log_bytes(this->logger, RAW, "stroke message", (void*)msg, msg_length);
527
528 switch (msg->type)
529 {
530 case STR_INITIATE:
531 {
532 stroke_initiate(this, msg);
533 break;
534 }
535 case STR_TERMINATE:
536 {
537 stroke_terminate(this, msg);
538 break;
539 }
540 case STR_STATUS:
541 {
542 stroke_status(this, msg);
543 break;
544 }
545 case STR_STATUS_ALL:
546 {
547 this->stroke_logger->enable_level(this->stroke_logger, LEVEL1);
548 stroke_status(this, msg);
549 break;
550 }
551 case STR_ADD_CONN:
552 {
553 stroke_add_conn(this, msg);
554 break;
555 }
556 case STR_LOGTYPE:
557 {
558 stroke_logtype(this, msg);
559 break;
560 }
561 case STR_LOGLEVEL:
562 {
563 stroke_loglevel(this, msg);
564 break;
565 }
566 default:
567 this->logger->log(this->logger, ERROR, "received invalid stroke");
568 }
569 this->stroke_logger->destroy(this->stroke_logger);
570 fclose(strokefile);
571 close(strokefd);
572 free(msg);
573 }
574 }
575
576 /**
577 * Implementation of stroke_t.destroy.
578 */
579 static void destroy(private_stroke_t *this)
580 {
581
582 pthread_cancel(this->assigned_thread);
583 pthread_join(this->assigned_thread, NULL);
584
585 close(this->socket);
586 unlink(socket_addr.sun_path);
587 free(this);
588 }
589
590
591 /*
592 * Described in header-file
593 */
594 stroke_t *stroke_create()
595 {
596 private_stroke_t *this = malloc_thing(private_stroke_t);
597 mode_t old;
598
599 /* public functions */
600 this->public.destroy = (void (*)(stroke_t*))destroy;
601
602 /* private functions */
603 this->stroke_receive = stroke_receive;
604
605 this->logger = logger_manager->get_logger(logger_manager, CONFIG);
606
607 /* set up unix socket */
608 this->socket = socket(AF_UNIX, SOCK_STREAM, 0);
609 if (this->socket == -1)
610 {
611 this->logger->log(this->logger, ERROR, "could not create whack socket");
612 free(this);
613 return NULL;
614 }
615
616 old = umask(~S_IRWXU);
617 if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
618 {
619 this->logger->log(this->logger, ERROR, "could not bind stroke socket: %s", strerror(errno));
620 close(this->socket);
621 free(this);
622 return NULL;
623 }
624 umask(old);
625
626 if (listen(this->socket, 0) < 0)
627 {
628 this->logger->log(this->logger, ERROR, "could not listen on stroke socket: %s", strerror(errno));
629 close(this->socket);
630 unlink(socket_addr.sun_path);
631 free(this);
632 return NULL;
633 }
634
635 /* start a thread reading from the socket */
636 if (pthread_create(&(this->assigned_thread), NULL, (void*(*)(void*))this->stroke_receive, this) != 0)
637 {
638 this->logger->log(this->logger, ERROR, "Could not spawn stroke thread");
639 close(this->socket);
640 unlink(socket_addr.sun_path);
641 free(this);
642 return NULL;
643 }
644
645 return (&this->public);
646 }