6a86edbe986d20414557979df8e60be54136dcf6
[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 #define IKE_PORT 500
43 #define PATH_BUF 256
44
45 static bool strict = FALSE;
46
47 struct sockaddr_un socket_addr = { AF_UNIX, STROKE_SOCKET};
48
49
50 typedef struct private_stroke_t private_stroke_t;
51
52 /**
53 * Private data of an stroke_t object.
54 */
55 struct private_stroke_t {
56
57 /**
58 * Public part of stroke_t object.
59 */
60 stroke_t public;
61
62 /**
63 * Assigned logger_t object in charon.
64 */
65 logger_t *logger;
66
67 /**
68 * Logger which logs to stroke
69 */
70 logger_t *stroke_logger;
71
72 /**
73 * Unix socket to listen for strokes
74 */
75 int socket;
76
77 /**
78 * Thread which reads from the \ 1ocket
79 */
80 pthread_t assigned_thread;
81
82 /**
83 * Read from the socket and handle stroke messages
84 */
85 void (*stroke_receive) (private_stroke_t *this);
86 };
87
88 /**
89 * Helper function which corrects the string pointers
90 * in a stroke_msg_t. Strings in a stroke_msg sent over "wire"
91 * contains RELATIVE addresses (relative to the beginning of the
92 * stroke_msg). They must be corrected if they reach our address
93 * space...
94 */
95 static void pop_string(stroke_msg_t *msg, char **string)
96 {
97 if (*string == NULL)
98 return;
99
100 /* check for sanity of string pointer and string */
101 if (string < (char**)msg
102 || string > (char**)msg + sizeof(stroke_msg_t)
103 || (u_int)*string < (u_int)((char*)msg->buffer - (char*)msg)
104 || (u_int)*string > msg->length)
105 {
106 *string = "(invalid pointer in stroke msg)";
107 }
108 else
109 {
110 *string = (char*)msg + (u_int)*string;
111 }
112 }
113
114 /**
115 * Load end entitity certificate
116 */
117 static x509_t* load_end_certificate(const char *filename, identification_t **idp, logger_t *logger)
118 {
119 char path[PATH_BUF];
120 x509_t *cert;
121
122 if (*filename == '/')
123 {
124 /* absolute path name */
125 snprintf(path, sizeof(path), "%s", filename);
126 }
127 else
128 {
129 /* relative path name */
130 snprintf(path, sizeof(path), "%s/%s", CERTIFICATE_DIR, filename);
131 }
132
133 cert = x509_create_from_file(path, "end entity certificate");
134
135 if (cert)
136 {
137 identification_t *id = *idp;
138 identification_t *subject = cert->get_subject(cert);
139
140 err_t ugh = cert->is_valid(cert, NULL);
141
142 if (ugh != NULL)
143 {
144 logger->log(logger, ERROR, "warning: certificate %s", ugh);
145 }
146 if (!id->equals(id, subject) && !cert->equals_subjectAltName(cert, id))
147 {
148 id->destroy(id);
149 id = subject;
150 *idp = id->clone(id);
151 }
152 return charon->credentials->add_end_certificate(charon->credentials, cert);
153 }
154 return NULL;
155 }
156
157 /**
158 * Add a connection to the configuration list
159 */
160 static void stroke_add_conn(private_stroke_t *this, stroke_msg_t *msg)
161 {
162 connection_t *connection;
163 policy_t *policy;
164 identification_t *my_id, *other_id;
165 identification_t *my_ca = NULL;
166 identification_t *other_ca = NULL;
167 bool my_ca_same = FALSE;
168 bool other_ca_same =FALSE;
169 host_t *my_host, *other_host, *my_subnet, *other_subnet;
170 proposal_t *proposal;
171 traffic_selector_t *my_ts, *other_ts;
172
173 pop_string(msg, &msg->add_conn.name);
174 pop_string(msg, &msg->add_conn.me.address);
175 pop_string(msg, &msg->add_conn.other.address);
176 pop_string(msg, &msg->add_conn.me.subnet);
177 pop_string(msg, &msg->add_conn.other.subnet);
178 pop_string(msg, &msg->add_conn.me.id);
179 pop_string(msg, &msg->add_conn.other.id);
180 pop_string(msg, &msg->add_conn.me.cert);
181 pop_string(msg, &msg->add_conn.other.cert);
182 pop_string(msg, &msg->add_conn.me.ca);
183 pop_string(msg, &msg->add_conn.other.ca);
184 pop_string(msg, &msg->add_conn.algorithms.ike);
185 pop_string(msg, &msg->add_conn.algorithms.esp);
186
187 this->logger->log(this->logger, CONTROL, "received stroke: add connection \"%s\"", msg->add_conn.name);
188
189 my_host = msg->add_conn.me.address?
190 host_create(AF_INET, msg->add_conn.me.address, IKE_PORT) : NULL;
191 if (my_host == NULL)
192 {
193 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid host: %s", msg->add_conn.me.address);
194 return;
195 }
196
197 other_host = msg->add_conn.other.address ?
198 host_create(AF_INET, msg->add_conn.other.address, IKE_PORT) : NULL;
199 if (other_host == NULL)
200 {
201 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid host: %s", msg->add_conn.other.address);
202 my_host->destroy(my_host);
203 return;
204 }
205
206 if (charon->socket->is_listening_on(charon->socket, other_host))
207 {
208 stroke_end_t tmp_end;
209 host_t *tmp_host;
210
211 this->stroke_logger->log(this->stroke_logger, CONTROL|LEVEL1, "left is other host, swapping ends");
212
213 tmp_host = my_host;
214 my_host = other_host;
215 other_host = tmp_host;
216
217 tmp_end = msg->add_conn.me;
218 msg->add_conn.me = msg->add_conn.other;
219 msg->add_conn.other = tmp_end;
220 }
221 else if (!charon->socket->is_listening_on(charon->socket, my_host))
222 {
223 this->stroke_logger->log(this->stroke_logger, ERROR, "left nor right host is our side, aborting");
224
225 my_host->destroy(my_host);
226 other_host->destroy(other_host);
227 return;
228 }
229
230 my_id = identification_create_from_string(msg->add_conn.me.id ?
231 msg->add_conn.me.id : msg->add_conn.me.address);
232 if (my_id == NULL)
233 {
234 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid id: %s", msg->add_conn.me.id);
235 my_host->destroy(my_host);
236 other_host->destroy(other_host);
237 return;
238 }
239
240 other_id = identification_create_from_string(msg->add_conn.other.id ?
241 msg->add_conn.other.id : msg->add_conn.other.address);
242 if (other_id == NULL)
243 {
244 my_host->destroy(my_host);
245 other_host->destroy(other_host);
246 my_id->destroy(my_id);
247 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid id: %s", msg->add_conn.other.id);
248 return;
249 }
250
251 my_subnet = host_create(AF_INET, msg->add_conn.me.subnet ?
252 msg->add_conn.me.subnet : msg->add_conn.me.address, IKE_PORT);
253 if (my_subnet == NULL)
254 {
255 my_host->destroy(my_host);
256 other_host->destroy(other_host);
257 my_id->destroy(my_id);
258 other_id->destroy(other_id);
259 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid subnet: %s", msg->add_conn.me.subnet);
260 return;
261 }
262
263 other_subnet = host_create(AF_INET, msg->add_conn.other.subnet ?
264 msg->add_conn.other.subnet : msg->add_conn.other.address, IKE_PORT);
265 if (other_subnet == NULL)
266 {
267 my_host->destroy(my_host);
268 other_host->destroy(other_host);
269 my_id->destroy(my_id);
270 other_id->destroy(other_id);
271 my_subnet->destroy(my_subnet);
272 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid subnet: %s", msg->add_conn.me.subnet);
273 return;
274 }
275
276 my_ts = traffic_selector_create_from_subnet(my_subnet, msg->add_conn.me.subnet ?
277 msg->add_conn.me.subnet_mask : 32);
278 my_subnet->destroy(my_subnet);
279
280 other_ts = traffic_selector_create_from_subnet(other_subnet, msg->add_conn.other.subnet ?
281 msg->add_conn.other.subnet_mask : 32);
282 other_subnet->destroy(other_subnet);
283
284 if (msg->add_conn.me.ca)
285 {
286 if (streq(msg->add_conn.me.ca, "%same"))
287 {
288 my_ca_same = TRUE;
289 }
290 else
291 {
292 my_ca = identification_create_from_string(msg->add_conn.me.ca);
293 }
294 }
295 if (msg->add_conn.other.ca)
296 {
297 if (streq(msg->add_conn.other.ca, "%same"))
298 {
299 other_ca_same = TRUE;
300 }
301 else
302 {
303 other_ca = identification_create_from_string(msg->add_conn.other.ca);
304 }
305 }
306 if (msg->add_conn.me.cert)
307 {
308 x509_t *cert = load_end_certificate(msg->add_conn.me.cert, &my_id, this->stroke_logger);
309
310 if (my_ca == NULL && !my_ca_same && cert)
311 {
312 identification_t *issuer = cert->get_issuer(cert);
313
314 my_ca = issuer->clone(issuer);
315 }
316 }
317 if (msg->add_conn.other.cert)
318 {
319 x509_t *cert = load_end_certificate(msg->add_conn.other.cert, &other_id, this->stroke_logger);
320
321 if (other_ca == NULL && !other_ca_same && cert)
322 {
323 identification_t *issuer = cert->get_issuer(cert);
324
325 other_ca = issuer->clone(issuer);
326 }
327 }
328 if (other_ca_same && my_ca)
329 {
330 other_ca = my_ca->clone(my_ca);
331 }
332 else if (my_ca_same && other_ca)
333 {
334 my_ca = other_ca->clone(other_ca);
335 }
336 if (my_ca == NULL)
337 {
338 my_ca = identification_create_from_string("%any");
339 }
340 if (other_ca == NULL)
341 {
342 other_ca = identification_create_from_string("%any");
343 }
344 this->logger->log(this->logger, CONTROL|LEVEL1, " my ca: '%s'", my_ca->get_string(my_ca));
345 this->logger->log(this->logger, CONTROL|LEVEL1, " other ca:'%s'", other_ca->get_string(other_ca));
346
347 connection = connection_create(msg->add_conn.name, msg->add_conn.ikev2,
348 my_host, other_host,
349 RSA_DIGITAL_SIGNATURE);
350 if (msg->add_conn.algorithms.ike)
351 {
352 char *proposal_string;
353 char *strict = msg->add_conn.algorithms.ike + strlen(msg->add_conn.algorithms.ike) - 1;
354 if (*strict == '!')
355 {
356 *strict = '\0';
357 }
358 else
359 {
360 strict = NULL;
361 }
362 while ((proposal_string = strsep(&msg->add_conn.algorithms.ike, ",")))
363 {
364 proposal = proposal_create_from_string(PROTO_IKE, proposal_string);
365 if (proposal == NULL)
366 {
367 this->logger->log(this->logger, ERROR, "invalid IKE proposal string: %s", msg->add_conn.algorithms.esp);
368 my_id->destroy(my_id);
369 other_id->destroy(other_id);
370 my_ts->destroy(my_ts);
371 other_ts->destroy(other_ts);
372 my_ca->destroy(my_ca);
373 other_ca->destroy(other_ca);
374 connection->destroy(connection);
375 return;
376 }
377 connection->add_proposal(connection, proposal);
378 }
379 if (!strict)
380 {
381 proposal = proposal_create_default(PROTO_IKE);
382 connection->add_proposal(connection, proposal);
383 }
384 }
385 else
386 {
387 proposal = proposal_create_default(PROTO_IKE);
388 connection->add_proposal(connection, proposal);
389 }
390
391 policy = policy_create(msg->add_conn.name, my_id, other_id,
392 msg->add_conn.rekey.ipsec_lifetime,
393 msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin,
394 msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100);
395 policy->add_my_traffic_selector(policy, my_ts);
396 policy->add_other_traffic_selector(policy, other_ts);
397 policy->add_authorities(policy, my_ca, other_ca);
398
399 if (msg->add_conn.algorithms.esp)
400 {
401 char *proposal_string;
402 char *strict = msg->add_conn.algorithms.esp + strlen(msg->add_conn.algorithms.esp) - 1;
403 if (*strict == '!')
404 {
405 *strict = '\0';
406 }
407 else
408 {
409 strict = NULL;
410 }
411
412 while ((proposal_string = strsep(&msg->add_conn.algorithms.esp, ",")))
413 {
414 proposal = proposal_create_from_string(PROTO_ESP, proposal_string);
415 if (proposal == NULL)
416 {
417 this->logger->log(this->logger, ERROR,
418 "invalid ESP proposal string: %s", msg->add_conn.algorithms.esp);
419 policy->destroy(policy);
420 connection->destroy(connection);
421 return;
422 }
423 policy->add_proposal(policy, proposal);
424 }
425 if (!strict)
426 {
427 proposal = proposal_create_default(PROTO_ESP);
428 policy->add_proposal(policy, proposal);
429 }
430 }
431 else
432 {
433 proposal = proposal_create_default(PROTO_ESP);
434 policy->add_proposal(policy, proposal);
435 }
436
437 /* add to global connection list */
438 charon->connections->add_connection(charon->connections, connection);
439 this->logger->log(this->logger, CONTROL, "added connection \"%s\": %s[%s]...%s[%s]",
440 msg->add_conn.name,
441 my_host->get_address(my_host),
442 my_id->get_string(my_id),
443 other_host->get_address(other_host),
444 other_id->get_string(other_id));
445 /* add to global policy list */
446 charon->policies->add_policy(charon->policies, policy);
447 }
448
449 /**
450 * Delete a connection from the list
451 */
452 static void stroke_del_conn(private_stroke_t *this, stroke_msg_t *msg)
453 {
454 status_t status;
455
456 pop_string(msg, &(msg->del_conn.name));
457 this->logger->log(this->logger, CONTROL, "received stroke: delete \"%s\"", msg->del_conn.name);
458
459 status = charon->connections->delete_connection(charon->connections,
460 msg->del_conn.name);
461 charon->policies->delete_policy(charon->policies, msg->del_conn.name);
462 if (status == SUCCESS)
463 {
464 this->stroke_logger->log(this->stroke_logger, CONTROL,
465 "Deleted connection '%s'", msg->del_conn.name);
466 }
467 else
468 {
469 this->stroke_logger->log(this->stroke_logger, ERROR,
470 "No connection named '%s'", msg->del_conn.name);
471 }
472 }
473
474 /**
475 * initiate a connection by name
476 */
477 static void stroke_initiate(private_stroke_t *this, stroke_msg_t *msg)
478 {
479 initiate_ike_sa_job_t *job;
480 connection_t *connection;
481 linked_list_t *ike_sas;
482 ike_sa_id_t *ike_sa_id;
483
484 pop_string(msg, &(msg->initiate.name));
485 this->logger->log(this->logger, CONTROL, "received stroke: initiate \"%s\"", msg->initiate.name);
486 connection = charon->connections->get_connection_by_name(charon->connections, msg->initiate.name);
487 if (connection == NULL)
488 {
489 this->stroke_logger->log(this->stroke_logger, ERROR, "no connection named \"%s\"", msg->initiate.name);
490 }
491 /* only initiate if it is an IKEv2 connection, ignore IKEv1 */
492 else if (connection->is_ikev2(connection))
493 {
494 /* check for already set up IKE_SAs befor initiating */
495 ike_sas = charon->ike_sa_manager->get_ike_sa_list_by_name(charon->ike_sa_manager, msg->initiate.name);
496 if (ike_sas->get_count(ike_sas) == 0)
497 {
498 this->stroke_logger->log(this->stroke_logger, CONTROL,
499 "initiating connection \"%s\" (see log)...", msg->initiate.name);
500 job = initiate_ike_sa_job_create(connection);
501 charon->job_queue->add(charon->job_queue, (job_t*)job);
502 }
503 else
504 {
505 this->stroke_logger->log(this->stroke_logger, CONTROL,
506 "connection \"%s\" already up", msg->initiate.name);
507 }
508 while (ike_sas->remove_last(ike_sas, (void**)&ike_sa_id) == SUCCESS)
509 {
510 ike_sa_id->destroy(ike_sa_id);
511 }
512 ike_sas->destroy(ike_sas);
513 }
514 }
515
516 /**
517 * terminate a connection by name
518 */
519 static void stroke_terminate(private_stroke_t *this, stroke_msg_t *msg)
520 {
521 linked_list_t *ike_sas;
522 iterator_t *iterator;
523 int instances = 0;
524 connection_t *conn;
525
526 pop_string(msg, &(msg->terminate.name));
527 this->logger->log(this->logger, CONTROL, "received stroke: terminate \"%s\"", msg->terminate.name);
528
529 /* we have to do tricky tricks to give the most comprehensive output to the user.
530 * There are different cases:
531 * 1. Connection is available, but IKEv1:
532 * => just ignore it, let pluto print it
533 * 2. Connection is not available, but instances of a deleted connection template:
534 * => terminate them, and print their termination
535 * 3. Connection is not available, and and no instances are there:
536 * => show error about bad connection name
537 * 4. An IKEv2 connection is available, and may contain instances:
538 * => terminate and print, simple
539 */
540 conn = charon->connections->get_connection_by_name(charon->connections, msg->terminate.name);
541 if (conn == NULL || conn->is_ikev2(conn))
542 {
543 ike_sas = charon->ike_sa_manager->get_ike_sa_list_by_name(charon->ike_sa_manager, msg->terminate.name);
544
545 iterator = ike_sas->create_iterator(ike_sas, TRUE);
546 while (iterator->has_next(iterator))
547 {
548 ike_sa_id_t *ike_sa_id;
549 iterator->current(iterator, (void**)&ike_sa_id);
550 charon->ike_sa_manager->delete(charon->ike_sa_manager, ike_sa_id);
551 ike_sa_id->destroy(ike_sa_id);
552 instances++;
553 }
554 iterator->destroy(iterator);
555 ike_sas->destroy(ike_sas);
556 if (conn == NULL && instances == 0)
557 {
558 this->stroke_logger->log(this->stroke_logger, CONTROL,
559 "no connection named \"%s\"",
560 msg->terminate.name);
561 }
562 else
563 {
564 this->stroke_logger->log(this->stroke_logger, CONTROL,
565 "terminated %d instances of \"%s\"",
566 instances, msg->terminate.name);
567 }
568 }
569 if (conn)
570 {
571 conn->destroy(conn);
572 }
573 }
574
575 /**
576 * show status of (established) connections
577 */
578 static void stroke_status(private_stroke_t *this, stroke_msg_t *msg)
579 {
580 if (msg->status.name)
581 {
582 pop_string(msg, &(msg->status.name));
583 }
584 charon->connections->log_connections(charon->connections, this->stroke_logger, msg->status.name);
585 charon->ike_sa_manager->log_status(charon->ike_sa_manager, this->stroke_logger, msg->status.name);
586 }
587
588 /**
589 * list various information
590 */
591 static void stroke_list(private_stroke_t *this, stroke_msg_t *msg)
592 {
593 if (msg->list.flags & LIST_CERTS)
594 {
595 charon->credentials->log_certificates(charon->credentials, this->stroke_logger, msg->list.utc);
596 }
597 if (msg->list.flags & LIST_CACERTS)
598 {
599 charon->credentials->log_ca_certificates(charon->credentials, this->stroke_logger, msg->list.utc);
600 }
601 if (msg->list.flags & LIST_CRLS)
602 {
603 charon->credentials->log_crls(charon->credentials, this->stroke_logger, msg->list.utc);
604 }
605 }
606
607 /**
608 * reread various information
609 */
610 static void stroke_reread(private_stroke_t *this, stroke_msg_t *msg)
611 {
612 if (msg->reread.flags & REREAD_CACERTS)
613 {
614 charon->credentials->load_ca_certificates(charon->credentials, CA_CERTIFICATE_DIR);
615 }
616 if (msg->reread.flags & REREAD_CRLS)
617 {
618 charon->credentials->load_crls(charon->credentials, CRL_DIR);
619 }
620 }
621
622 logger_context_t get_context(char *context)
623 {
624 if (strcasecmp(context, "ALL") == 0) return ALL_LOGGERS;
625 else if (strcasecmp(context, "PARSR") == 0) return PARSER;
626 else if (strcasecmp(context, "GNRAT") == 0) return GENERATOR;
627 else if (strcasecmp(context, "IKESA") == 0) return IKE_SA;
628 else if (strcasecmp(context, "SAMGR") == 0) return IKE_SA_MANAGER;
629 else if (strcasecmp(context, "CHDSA") == 0) return CHILD_SA;
630 else if (strcasecmp(context, "MESSG") == 0) return MESSAGE;
631 else if (strcasecmp(context, "TPOOL") == 0) return THREAD_POOL;
632 else if (strcasecmp(context, "WORKR") == 0) return WORKER;
633 else if (strcasecmp(context, "SCHED") == 0) return SCHEDULER;
634 else if (strcasecmp(context, "SENDR") == 0) return SENDER;
635 else if (strcasecmp(context, "RECVR") == 0) return RECEIVER;
636 else if (strcasecmp(context, "SOCKT") == 0) return SOCKET;
637 else if (strcasecmp(context, "TESTR") == 0) return TESTER;
638 else if (strcasecmp(context, "DAEMN") == 0) return DAEMON;
639 else if (strcasecmp(context, "CONFG") == 0) return CONFIG;
640 else if (strcasecmp(context, "ENCPL") == 0) return ENCRYPTION_PAYLOAD;
641 else if (strcasecmp(context, "PAYLD") == 0) return PAYLOAD;
642 else if (strcasecmp(context, "XFRM") == 0) return XFRM;
643 else return -2;
644 }
645
646 /**
647 * set the type of logged messages in a context
648 */
649 static void stroke_logtype(private_stroke_t *this, stroke_msg_t *msg)
650 {
651 pop_string(msg, &(msg->logtype.context));
652 pop_string(msg, &(msg->logtype.type));
653
654 this->logger->log(this->logger, CONTROL, "received stroke: logtype for %s", msg->logtype.context);
655
656 log_level_t level;
657 logger_context_t context = get_context(msg->logtype.context);
658 if (context == -2)
659 {
660 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid context (%s)!", msg->logtype.context);
661 return;
662 }
663
664 if (strcasecmp(msg->logtype.type, "CONTROL") == 0)
665 level = CONTROL;
666 else if (strcasecmp(msg->logtype.type, "ERROR") == 0)
667 level = ERROR;
668 else if (strcasecmp(msg->logtype.type, "AUDIT") == 0)
669 level = AUDIT;
670 else if (strcasecmp(msg->logtype.type, "RAW") == 0)
671 level = RAW;
672 else if (strcasecmp(msg->logtype.type, "PRIVATE") == 0)
673 level = PRIVATE;
674 else
675 {
676 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid type (%s)!", msg->logtype.type);
677 return;
678 }
679
680 if (msg->logtype.enable)
681 {
682 logger_manager->enable_log_level(logger_manager, context, level);
683 }
684 else
685 {
686 logger_manager->disable_log_level(logger_manager, context, level);
687 }
688 }
689
690 /**
691 * set the verbosity of a logger
692 */
693 static void stroke_loglevel(private_stroke_t *this, stroke_msg_t *msg)
694 {
695 log_level_t level;
696 logger_context_t context;
697
698 pop_string(msg, &(msg->loglevel.context));
699 this->logger->log(this->logger, CONTROL, "received stroke: loglevel for %s", msg->loglevel.context);
700
701 context = get_context(msg->loglevel.context);
702 if (context == -2)
703 {
704 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid context (%s)!", msg->loglevel.context);
705 return;
706 }
707
708 if (msg->loglevel.level == 0)
709 level = LEVEL0;
710 else if (msg->loglevel.level == 1)
711 level = LEVEL1;
712 else if (msg->loglevel.level == 2)
713 level = LEVEL2;
714 else if (msg->loglevel.level == 3)
715 level = LEVEL3;
716 else
717 {
718 this->stroke_logger->log(this->stroke_logger, ERROR, "invalid level (%d)!", msg->loglevel.level);
719 return;
720 }
721
722 logger_manager->enable_log_level(logger_manager, context, level);
723 }
724
725 /**
726 * Implementation of private_stroke_t.stroke_receive.
727 */
728 static void stroke_receive(private_stroke_t *this)
729 {
730 stroke_msg_t *msg;
731 u_int16_t msg_length;
732 struct sockaddr_un strokeaddr;
733 int strokeaddrlen = sizeof(strokeaddr);
734 ssize_t bytes_read;
735 int strokefd;
736 FILE *strokefile;
737 int oldstate;
738
739 /* disable cancellation by default */
740 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
741
742 while (1)
743 {
744 /* wait for connections, but allow thread to terminate */
745 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
746 strokefd = accept(this->socket, (struct sockaddr *)&strokeaddr, &strokeaddrlen);
747 pthread_setcancelstate(oldstate, NULL);
748
749 if (strokefd < 0)
750 {
751 this->logger->log(this->logger, ERROR, "accepting stroke connection failed: %s", strerror(errno));
752 continue;
753 }
754
755 /* peek the length */
756 bytes_read = recv(strokefd, &msg_length, sizeof(msg_length), MSG_PEEK);
757 if (bytes_read != sizeof(msg_length))
758 {
759 this->logger->log(this->logger, ERROR, "reading lenght of stroke message failed");
760 close(strokefd);
761 continue;
762 }
763
764 /* read message */
765 msg = malloc(msg_length);
766 bytes_read = recv(strokefd, msg, msg_length, 0);
767 if (bytes_read != msg_length)
768 {
769 this->logger->log(this->logger, ERROR, "reading stroke message failed: %s");
770 close(strokefd);
771 continue;
772 }
773
774 strokefile = fdopen(dup(strokefd), "w");
775 if (strokefile == NULL)
776 {
777 this->logger->log(this->logger, ERROR, "opening stroke output channel failed:", strerror(errno));
778 close(strokefd);
779 free(msg);
780 continue;
781 }
782
783 /* setup a logger which writes status to the unix socket */
784 this->stroke_logger = logger_create("", CONTROL|ERROR, FALSE, strokefile);
785
786 this->logger->log_bytes(this->logger, RAW, "stroke message", (void*)msg, msg_length);
787
788 switch (msg->type)
789 {
790 case STR_INITIATE:
791 stroke_initiate(this, msg);
792 break;
793 case STR_TERMINATE:
794 stroke_terminate(this, msg);
795 break;
796 case STR_STATUS:
797 stroke_status(this, msg);
798 break;
799 case STR_STATUS_ALL:
800 this->stroke_logger->enable_level(this->stroke_logger, LEVEL1);
801 stroke_status(this, msg);
802 break;
803 case STR_ADD_CONN:
804 stroke_add_conn(this, msg);
805 break;
806 case STR_DEL_CONN:
807 stroke_del_conn(this, msg);
808 break;
809 case STR_LOGTYPE:
810 stroke_logtype(this, msg);
811 break;
812 case STR_LOGLEVEL:
813 stroke_loglevel(this, msg);
814 break;
815 case STR_LIST:
816 stroke_list(this, msg);
817 break;
818 case STR_REREAD:
819 stroke_reread(this, msg);
820 break;
821 default:
822 this->logger->log(this->logger, ERROR, "received invalid stroke");
823 }
824 this->stroke_logger->destroy(this->stroke_logger);
825 fclose(strokefile);
826 close(strokefd);
827 free(msg);
828 }
829 }
830
831 /**
832 * Implementation of stroke_t.destroy.
833 */
834 static void destroy(private_stroke_t *this)
835 {
836
837 pthread_cancel(this->assigned_thread);
838 pthread_join(this->assigned_thread, NULL);
839
840 close(this->socket);
841 unlink(socket_addr.sun_path);
842 free(this);
843 }
844
845
846 /*
847 * Described in header-file
848 */
849 stroke_t *stroke_create()
850 {
851 private_stroke_t *this = malloc_thing(private_stroke_t);
852 mode_t old;
853
854 /* public functions */
855 this->public.destroy = (void (*)(stroke_t*))destroy;
856
857 /* private functions */
858 this->stroke_receive = stroke_receive;
859
860 this->logger = logger_manager->get_logger(logger_manager, CONFIG);
861
862 /* set up unix socket */
863 this->socket = socket(AF_UNIX, SOCK_STREAM, 0);
864 if (this->socket == -1)
865 {
866 this->logger->log(this->logger, ERROR, "could not create whack socket");
867 free(this);
868 return NULL;
869 }
870
871 old = umask(~S_IRWXU);
872 if (bind(this->socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
873 {
874 this->logger->log(this->logger, ERROR, "could not bind stroke socket: %s", strerror(errno));
875 close(this->socket);
876 free(this);
877 return NULL;
878 }
879 umask(old);
880
881 if (listen(this->socket, 0) < 0)
882 {
883 this->logger->log(this->logger, ERROR, "could not listen on stroke socket: %s", strerror(errno));
884 close(this->socket);
885 unlink(socket_addr.sun_path);
886 free(this);
887 return NULL;
888 }
889
890 /* start a thread reading from the socket */
891 if (pthread_create(&(this->assigned_thread), NULL, (void*(*)(void*))this->stroke_receive, this) != 0)
892 {
893 this->logger->log(this->logger, ERROR, "Could not spawn stroke thread");
894 close(this->socket);
895 unlink(socket_addr.sun_path);
896 free(this);
897 return NULL;
898 }
899
900 return (&this->public);
901 }