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