8a0d220973fa580c35a700629ae022eec3aa2070
[strongswan.git] / src / 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 }
244 }
245 if (msg->add_conn.other.cert)
246 {
247 char file[128];
248 snprintf(file, sizeof(file), "%s/%s", CERTIFICATE_DIR, msg->add_conn.other.cert);
249 cert = x509_create_from_file(file);
250 if (cert)
251 {
252 other_id->destroy(other_id);
253 other_id = cert->get_subject(cert);
254 other_id = other_id->clone(other_id);
255 cert->destroy(cert);
256 }
257 }
258
259 connection = connection_create(msg->add_conn.name,
260 my_host, other_host,
261 my_id->clone(my_id), other_id->clone(other_id),
262 RSA_DIGITAL_SIGNATURE);
263 proposal = proposal_create(1);
264 proposal->add_algorithm(proposal, PROTO_IKE, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16);
265 proposal->add_algorithm(proposal, PROTO_IKE, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0);
266 proposal->add_algorithm(proposal, PROTO_IKE, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0);
267 proposal->add_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_SHA1, 0);
268 proposal->add_algorithm(proposal, PROTO_IKE, PSEUDO_RANDOM_FUNCTION, PRF_HMAC_MD5, 0);
269 proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_2048_BIT, 0);
270 proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_1536_BIT, 0);
271 proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_1024_BIT, 0);
272 proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_4096_BIT, 0);
273 proposal->add_algorithm(proposal, PROTO_IKE, DIFFIE_HELLMAN_GROUP, MODP_8192_BIT, 0);
274 connection->add_proposal(connection, proposal);
275
276 /* add to global connection list */
277 charon->connections->add_connection(charon->connections, connection);
278 this->logger->log(this->logger, CONTROL, "added connection \"%s\": %s[%s]...%s[%s]",
279 msg->add_conn.name,
280 my_host->get_address(my_host),
281 my_id->get_string(my_id),
282 other_host->get_address(other_host),
283 other_id->get_string(other_id));
284
285 policy = policy_create(my_id, other_id);
286 proposal = proposal_create(1);
287 proposal->add_algorithm(proposal, PROTO_ESP, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16);
288 proposal->add_algorithm(proposal, PROTO_ESP, INTEGRITY_ALGORITHM, AUTH_HMAC_SHA1_96, 0);
289 proposal->add_algorithm(proposal, PROTO_ESP, INTEGRITY_ALGORITHM, AUTH_HMAC_MD5_96, 0);
290 policy->add_proposal(policy, proposal);
291 policy->add_my_traffic_selector(policy, my_ts);
292 policy->add_other_traffic_selector(policy, other_ts);
293
294 /* add to global policy list */
295 charon->policies->add_policy(charon->policies, policy);
296
297 }
298
299 /**
300 * initiate a connection by name
301 */
302 static void stroke_initiate(private_stroke_t *this, stroke_msg_t *msg)
303 {
304 initiate_ike_sa_job_t *job;
305 connection_t *connection;
306
307 pop_string(msg, &(msg->initiate.name));
308 this->logger->log(this->logger, CONTROL, "received stroke: initiate \"%s\"", msg->initiate.name);
309 connection = charon->connections->get_connection_by_name(charon->connections, msg->initiate.name);
310 if (connection == NULL)
311 {
312 this->stroke_logger->log(this->stroke_logger, ERROR, "no connection named \"%s\"", msg->initiate.name);
313 }
314 else
315 {
316 job = initiate_ike_sa_job_create(connection);
317 charon->job_queue->add(charon->job_queue, (job_t*)job);
318 }
319 }
320
321 /**
322 * terminate a connection by name
323 */
324 static void stroke_terminate(private_stroke_t *this, stroke_msg_t *msg)
325 {
326 linked_list_t *ike_sas;
327 iterator_t *iterator;
328 int instances = 0;
329
330 pop_string(msg, &(msg->terminate.name));
331 this->logger->log(this->logger, CONTROL, "received stroke: terminate \"%s\"", msg->terminate.name);
332
333 ike_sas = charon->ike_sa_manager->get_ike_sa_list_by_name(charon->ike_sa_manager, msg->terminate.name);
334
335 iterator = ike_sas->create_iterator(ike_sas, TRUE);
336 while (iterator->has_next(iterator))
337 {
338 ike_sa_id_t *ike_sa_id;
339 iterator->current(iterator, (void**)&ike_sa_id);
340 charon->ike_sa_manager->delete(charon->ike_sa_manager, ike_sa_id);
341 ike_sa_id->destroy(ike_sa_id);
342 instances++;
343 }
344 iterator->destroy(iterator);
345 ike_sas->destroy(ike_sas);
346 this->stroke_logger->log(this->stroke_logger, CONTROL, "terminated %d instances of %s", instances, msg->terminate.name);
347 }
348
349 /**
350 * show status of (established) connections
351 */
352 static void stroke_status(private_stroke_t *this, stroke_msg_t *msg)
353 {
354 if (msg->status.name)
355 {
356 pop_string(msg, &(msg->status.name));
357 }
358 charon->ike_sa_manager->log_status(charon->ike_sa_manager, this->stroke_logger, msg->status.name);
359 }
360
361 /**
362 * list various information
363 */
364 static void stroke_list(private_stroke_t *this, stroke_msg_t *msg, bool utc)
365 {
366 if (msg->type = STR_LIST_CERTS)
367 {
368 charon->credentials->log_certificates(charon->credentials, this->stroke_logger, utc);
369 }
370 }
371 logger_context_t get_context(char *context)
372 {
373 if (strcasecmp(context, "ALL") == 0) return ALL_LOGGERS;
374 else if (strcasecmp(context, "PARSR") == 0) return PARSER;
375 else if (strcasecmp(context, "GNRAT") == 0) return GENERATOR;
376 else if (strcasecmp(context, "IKESA") == 0) return IKE_SA;
377 else if (strcasecmp(context, "SAMGR") == 0) return IKE_SA_MANAGER;
378 else if (strcasecmp(context, "CHDSA") == 0) return CHILD_SA;
379 else if (strcasecmp(context, "MESSG") == 0) return MESSAGE;
380 else if (strcasecmp(context, "TPOOL") == 0) return THREAD_POOL;
381 else if (strcasecmp(context, "WORKR") == 0) return WORKER;
382 else if (strcasecmp(context, "SCHED") == 0) return SCHEDULER;
383 else if (strcasecmp(context, "SENDR") == 0) return SENDER;
384 else if (strcasecmp(context, "RECVR") == 0) return RECEIVER;
385 else if (strcasecmp(context, "SOCKT") == 0) return SOCKET;
386 else if (strcasecmp(context, "TESTR") == 0) return TESTER;
387 else if (strcasecmp(context, "DAEMN") == 0) return DAEMON;
388 else if (strcasecmp(context, "CONFG") == 0) return CONFIG;
389 else if (strcasecmp(context, "ENCPL") == 0) return ENCRYPTION_PAYLOAD;
390 else if (strcasecmp(context, "PAYLD") == 0) return PAYLOAD;
391 else return -2;
392 }
393
394 /**
395 * set the type of logged messages in a context
396 */
397 static void stroke_logtype(private_stroke_t *this, stroke_msg_t *msg)
398 {
399 pop_string(msg, &(msg->logtype.context));
400 pop_string(msg, &(msg->logtype.type));
401
402 this->logger->log(this->logger, CONTROL, "received stroke: logtype for %s", msg->logtype.context);
403
404 log_level_t level;
405 logger_context_t context = get_context(msg->logtype.context);
406 if (context == -2)
407 {
408 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid context (%s)!", msg->logtype.context);
409 return;
410 }
411
412 if (strcasecmp(msg->logtype.type, "CONTROL") == 0)
413 level = CONTROL;
414 else if (strcasecmp(msg->logtype.type, "ERROR") == 0)
415 level = ERROR;
416 else if (strcasecmp(msg->logtype.type, "AUDIT") == 0)
417 level = AUDIT;
418 else if (strcasecmp(msg->logtype.type, "RAW") == 0)
419 level = RAW;
420 else if (strcasecmp(msg->logtype.type, "PRIVATE") == 0)
421 level = PRIVATE;
422 else
423 {
424 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid type (%s)!", msg->logtype.type);
425 return;
426 }
427
428 if (msg->logtype.enable)
429 {
430 logger_manager->enable_log_level(logger_manager, context, level);
431 }
432 else
433 {
434 logger_manager->disable_log_level(logger_manager, context, level);
435 }
436 }
437
438 /**
439 * set the verbosity of a logger
440 */
441 static void stroke_loglevel(private_stroke_t *this, stroke_msg_t *msg)
442 {
443 log_level_t level;
444 logger_context_t context;
445
446 pop_string(msg, &(msg->loglevel.context));
447 this->logger->log(this->logger, CONTROL, "received stroke: loglevel for %s", msg->loglevel.context);
448
449 context = get_context(msg->loglevel.context);
450 if (context == -2)
451 {
452 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid context (%s)!", msg->loglevel.context);
453 return;
454 }
455
456 if (msg->loglevel.level == 0)
457 level = LEVEL0;
458 else if (msg->loglevel.level == 1)
459 level = LEVEL1;
460 else if (msg->loglevel.level == 2)
461 level = LEVEL2;
462 else if (msg->loglevel.level == 3)
463 level = LEVEL3;
464 else
465 {
466 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid level (%d)!", msg->loglevel.level);
467 return;
468 }
469
470 logger_manager->enable_log_level(logger_manager, context, level);
471 }
472
473 /**
474 * Implementation of private_stroke_t.stroke_receive.
475 */
476 static void stroke_receive(private_stroke_t *this)
477 {
478 stroke_msg_t *msg;
479 u_int16_t msg_length;
480 struct sockaddr_un strokeaddr;
481 int strokeaddrlen = sizeof(strokeaddr);
482 ssize_t bytes_read;
483 int strokefd;
484 FILE *strokefile;
485 int oldstate;
486
487 /* disable cancellation by default */
488 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
489
490 while (1)
491 {
492 /* wait for connections, but allow thread to terminate */
493 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
494 strokefd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen);
495 pthread_setcancelstate(oldstate, NULL);
496
497 if (strokefd < 0)
498 {
499 this->logger->log(this->logger, ERROR, "accepting stroke connection failed: %s", strerror(errno));
500 continue;
501 }
502
503 /* peek the length */
504 bytes_read = recv(strokefd, &msg_length, sizeof(msg_length), MSG_PEEK);
505 if (bytes_read != sizeof(msg_length))
506 {
507 this->logger->log(this->logger, ERROR, "reading lenght of stroke message failed");
508 close(strokefd);
509 continue;
510 }
511
512 /* read message */
513 msg = malloc(msg_length);
514 bytes_read = recv(strokefd, msg, msg_length, 0);
515 if (bytes_read != msg_length)
516 {
517 this->logger->log(this->logger, ERROR, "reading stroke message failed: %s");
518 close(strokefd);
519 continue;
520 }
521
522 strokefile = fdopen(dup(strokefd), "w");
523 if (strokefile == NULL)
524 {
525 this->logger->log(this->logger, ERROR, "opening stroke output channel failed:", strerror(errno));
526 close(strokefd);
527 free(msg);
528 continue;
529 }
530
531 /* setup a logger which writes status to the unix socket */
532 this->stroke_logger = logger_create("-", CONTROL|ERROR, FALSE, strokefile);
533
534 this->logger->log_bytes(this->logger, RAW, "stroke message", (void*)msg, msg_length);
535
536 switch (msg->type)
537 {
538 case STR_INITIATE:
539 stroke_initiate(this, msg);
540 break;
541 case STR_TERMINATE:
542 stroke_terminate(this, msg);
543 break;
544 case STR_STATUS:
545 stroke_status(this, msg);
546 break;
547 case STR_STATUS_ALL:
548 this->stroke_logger->enable_level(this->stroke_logger, LEVEL1);
549 stroke_status(this, msg);
550 break;
551 case STR_ADD_CONN:
552 stroke_add_conn(this, msg);
553 break;
554 case STR_LOGTYPE:
555 stroke_logtype(this, msg);
556 break;
557 case STR_LOGLEVEL:
558 stroke_loglevel(this, msg);
559 break;
560 case STR_LIST_CERTS:
561 stroke_list(this, msg, FALSE);
562 break;
563 default:
564 this->logger->log(this->logger, ERROR, "received invalid stroke");
565 }
566 this->stroke_logger->destroy(this->stroke_logger);
567 fclose(strokefile);
568 close(strokefd);
569 free(msg);
570 }
571 }
572
573 /**
574 * Implementation of stroke_t.destroy.
575 */
576 static void destroy(private_stroke_t *this)
577 {
578
579 pthread_cancel(this->assigned_thread);
580 pthread_join(this->assigned_thread, NULL);
581
582 close(this->socket);
583 unlink(socket_addr.sun_path);
584 free(this);
585 }
586
587
588 /*
589 * Described in header-file
590 */
591 stroke_t *stroke_create()
592 {
593 private_stroke_t *this = malloc_thing(private_stroke_t);
594 mode_t old;
595
596 /* public functions */
597 this->public.destroy = (void (*)(stroke_t*))destroy;
598
599 /* private functions */
600 this->stroke_receive = stroke_receive;
601
602 this->logger = logger_manager->get_logger(logger_manager, CONFIG);
603
604 /* set up unix socket */
605 this->socket = socket(AF_UNIX, SOCK_STREAM, 0);
606 if (this->socket == -1)
607 {
608 this->logger->log(this->logger, ERROR, "could not create whack socket");
609 free(this);
610 return NULL;
611 }
612
613 old = umask(~S_IRWXU);
614 if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
615 {
616 this->logger->log(this->logger, ERROR, "could not bind stroke socket: %s", strerror(errno));
617 close(this->socket);
618 free(this);
619 return NULL;
620 }
621 umask(old);
622
623 if (listen(this->socket, 0) < 0)
624 {
625 this->logger->log(this->logger, ERROR, "could not listen on stroke socket: %s", strerror(errno));
626 close(this->socket);
627 unlink(socket_addr.sun_path);
628 free(this);
629 return NULL;
630 }
631
632 /* start a thread reading from the socket */
633 if (pthread_create(&(this->assigned_thread), NULL, (void*(*)(void*))this->stroke_receive, this) != 0)
634 {
635 this->logger->log(this->logger, ERROR, "Could not spawn stroke thread");
636 close(this->socket);
637 unlink(socket_addr.sun_path);
638 free(this);
639 return NULL;
640 }
641
642 return (&this->public);
643 }