4 * @brief Implementation of stroke_t.
9 * Copyright (C) 2006 Martin Willi
10 * Hochschule fuer Technik Rapperswil
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>.
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
24 #include <sys/types.h>
26 #include <sys/socket.h>
28 #include <sys/fcntl.h>
34 #include "stroke_interface.h"
39 #include <crypto/x509.h>
40 #include <queues/jobs/initiate_ike_sa_job.h>
43 struct sockaddr_un socket_addr
= { AF_UNIX
, STROKE_SOCKET
};
46 typedef struct private_stroke_t private_stroke_t
;
49 * Private data of an stroke_t object.
51 struct private_stroke_t
{
54 * Public part of stroke_t object.
59 * Assigned logger_t object in charon.
64 * Logger which logs to stroke
66 logger_t
*stroke_logger
;
69 * Unix socket to listen for strokes
74 * Thread which reads from the socket
76 pthread_t assigned_thread
;
79 * Read from the socket and handle stroke messages
81 void (*stroke_receive
) (private_stroke_t
*this);
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
91 static void pop_string(stroke_msg_t
*msg
, char **string
)
93 /* check for sanity of string pointer and string */
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
)
103 *string
= "(invalid char* in stroke msg)";
107 *string
= (char*)msg
+ (u_int
)*string
;
112 * Add a connection to the configuration list
114 static void stroke_add_conn(private_stroke_t
*this, stroke_msg_t
*msg
)
116 connection_t
*connection
;
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
;
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
);
134 this->logger
->log(this->logger
, CONTROL
, "received stroke: add connection \"%s\"", msg
->add_conn
.name
);
136 my_host
= host_create(AF_INET
, msg
->add_conn
.me
.address
, 500);
139 this->stroke_logger
->log(this->stroke_logger
, ERROR
, "invalid host: %s", msg
->add_conn
.me
.address
);
142 other_host
= host_create(AF_INET
, msg
->add_conn
.other
.address
, 500);
143 if (other_host
== NULL
)
145 this->stroke_logger
->log(this->stroke_logger
, ERROR
, "invalid host: %s", msg
->add_conn
.other
.address
);
146 my_host
->destroy(my_host
);
149 my_id
= identification_create_from_string(*msg
->add_conn
.me
.id
?
150 msg
->add_conn
.me
.id
: msg
->add_conn
.me
.address
);
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
);
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
)
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
);
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
)
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
);
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
)
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
);
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
);
197 if (charon
->socket
->is_listening_on(charon
->socket
, other_host
))
199 this->stroke_logger
->log(this->stroke_logger
, CONTROL
|LEVEL1
, "left is other host, switching");
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
;
206 my_host
= other_host
;
207 other_host
= tmp_host
;
212 msg
->add_conn
.me
.cert
= msg
->add_conn
.other
.cert
;
213 msg
->add_conn
.other
.cert
= tmp_cert
;
215 else if (charon
->socket
->is_listening_on(charon
->socket
, my_host
))
217 this->stroke_logger
->log(this->stroke_logger
, CONTROL
|LEVEL1
, "left is own host, not switching");
221 this->stroke_logger
->log(this->stroke_logger
, ERROR
, "left nor right host is our, aborting");
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
);
232 if (msg
->add_conn
.me
.cert
)
235 snprintf(file
, sizeof(file
), "%s/%s", CERTIFICATE_DIR
, msg
->add_conn
.me
.cert
);
236 cert
= x509_create_from_file(file
);
239 my_id
->destroy(my_id
);
240 my_id
= cert
->get_subject(cert
);
241 my_id
= my_id
->clone(my_id
);
243 this->logger
->log(this->logger
, CONTROL
,
244 "valid certificate with ID \"%s\"",
245 my_id
->get_string(my_id
));
248 if (msg
->add_conn
.other
.cert
)
251 snprintf(file
, sizeof(file
), "%s/%s", CERTIFICATE_DIR
, msg
->add_conn
.other
.cert
);
252 cert
= x509_create_from_file(file
);
255 other_id
->destroy(other_id
);
256 other_id
= cert
->get_subject(cert
);
257 other_id
= other_id
->clone(other_id
);
259 this->logger
->log(this->logger
, CONTROL
,
260 "valid certificate with ID \"%s\"",
261 other_id
->get_string(other_id
));
265 connection
= connection_create(msg
->add_conn
.name
,
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
);
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
);
295 this->stroke_logger
->log(this->stroke_logger
, CONTROL
|LEVEL1
, "connection \"%s\" added", msg
->add_conn
.name
);
299 * initiate a connection by name
301 static void stroke_initiate(private_stroke_t
*this, stroke_msg_t
*msg
)
303 initiate_ike_sa_job_t
*job
;
304 connection_t
*connection
;
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
)
311 this->stroke_logger
->log(this->stroke_logger
, ERROR
, "no connection named \"%s\"", msg
->initiate
.name
);
315 job
= initiate_ike_sa_job_create(connection
);
316 charon
->job_queue
->add(charon
->job_queue
, (job_t
*)job
);
321 * terminate a connection by name
323 static void stroke_terminate(private_stroke_t
*this, stroke_msg_t
*msg
)
325 linked_list_t
*ike_sas
;
326 iterator_t
*iterator
;
329 pop_string(msg
, &(msg
->terminate
.name
));
330 this->logger
->log(this->logger
, CONTROL
, "received stroke: terminate \"%s\"", msg
->terminate
.name
);
332 ike_sas
= charon
->ike_sa_manager
->get_ike_sa_list_by_name(charon
->ike_sa_manager
, msg
->terminate
.name
);
334 iterator
= ike_sas
->create_iterator(ike_sas
, TRUE
);
335 while (iterator
->has_next(iterator
))
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
);
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
);
349 * show status of (established) connections
351 static void stroke_status(private_stroke_t
*this, stroke_msg_t
*msg
)
353 if (msg
->status
.name
)
355 pop_string(msg
, &(msg
->status
.name
));
357 charon
->ike_sa_manager
->log_status(charon
->ike_sa_manager
, this->stroke_logger
, msg
->status
.name
);
360 logger_context_t
get_context(char *context
)
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
;
384 * set the type of logged messages in a context
386 static void stroke_logtype(private_stroke_t
*this, stroke_msg_t
*msg
)
388 pop_string(msg
, &(msg
->logtype
.context
));
389 pop_string(msg
, &(msg
->logtype
.type
));
391 this->logger
->log(this->logger
, CONTROL
, "received stroke: logtype for %s", msg
->logtype
.context
);
394 logger_context_t context
= get_context(msg
->logtype
.context
);
397 this->stroke_logger
->log(this->stroke_logger
, ERROR
, "invalid context (%s)!", msg
->logtype
.context
);
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
;
408 this->stroke_logger
->log(this->stroke_logger
, ERROR
, "invalid type (%s)!", msg
->logtype
.type
);
412 if (msg
->logtype
.enable
)
414 logger_manager
->enable_log_level(logger_manager
, context
, level
);
418 logger_manager
->disable_log_level(logger_manager
, context
, level
);
423 * set the verbosity of a logger
425 static void stroke_loglevel(private_stroke_t
*this, stroke_msg_t
*msg
)
427 pop_string(msg
, &(msg
->loglevel
.context
));
429 this->logger
->log(this->logger
, CONTROL
, "received stroke: loglevel for %s", msg
->loglevel
.context
);
432 logger_context_t context
= get_context(msg
->loglevel
.context
);
436 this->stroke_logger
->log(this->stroke_logger
, ERROR
, "invalid context (%s)!", msg
->loglevel
.context
);
440 if (msg
->loglevel
.level
== 0)
444 else if (msg
->loglevel
.level
== 1)
448 else if (msg
->loglevel
.level
== 2)
452 else if (msg
->loglevel
.level
== 3)
458 this->stroke_logger
->log(this->stroke_logger
, ERROR
, "invalid level (%d)!", msg
->loglevel
.level
);
462 logger_manager
->enable_log_level(logger_manager
, context
, level
);
466 * Implementation of private_stroke_t.stroke_receive.
468 static void stroke_receive(private_stroke_t
*this)
471 u_int16_t msg_length
;
472 struct sockaddr_un strokeaddr
;
473 int strokeaddrlen
= sizeof(strokeaddr
);
479 /* disable cancellation by default */
480 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
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
);
491 this->logger
->log(this->logger
, ERROR
, "accepting stroke connection failed: %s", strerror(errno
));
495 /* peek the length */
496 bytes_read
= recv(strokefd
, &msg_length
, sizeof(msg_length
), MSG_PEEK
);
497 if (bytes_read
!= sizeof(msg_length
))
499 this->logger
->log(this->logger
, ERROR
, "reading lenght of stroke message failed");
505 msg
= malloc(msg_length
);
506 bytes_read
= recv(strokefd
, msg
, msg_length
, 0);
507 if (bytes_read
!= msg_length
)
509 this->logger
->log(this->logger
, ERROR
, "reading stroke message failed: %s");
514 strokefile
= fdopen(dup(strokefd
), "w");
515 if (strokefile
== NULL
)
517 this->logger
->log(this->logger
, ERROR
, "opening stroke output channel failed:", strerror(errno
));
523 /* setup a logger which writes status to the unix socket */
524 this->stroke_logger
= logger_create("-", CONTROL
|ERROR
, FALSE
, strokefile
);
526 this->logger
->log_bytes(this->logger
, RAW
, "stroke message", (void*)msg
, msg_length
);
532 stroke_initiate(this, msg
);
537 stroke_terminate(this, msg
);
542 stroke_status(this, msg
);
547 this->stroke_logger
->enable_level(this->stroke_logger
, LEVEL1
);
548 stroke_status(this, msg
);
553 stroke_add_conn(this, msg
);
558 stroke_logtype(this, msg
);
563 stroke_loglevel(this, msg
);
567 this->logger
->log(this->logger
, ERROR
, "received invalid stroke");
569 this->stroke_logger
->destroy(this->stroke_logger
);
577 * Implementation of stroke_t.destroy.
579 static void destroy(private_stroke_t
*this)
582 pthread_cancel(this->assigned_thread
);
583 pthread_join(this->assigned_thread
, NULL
);
586 unlink(socket_addr
.sun_path
);
592 * Described in header-file
594 stroke_t
*stroke_create()
596 private_stroke_t
*this = malloc_thing(private_stroke_t
);
599 /* public functions */
600 this->public.destroy
= (void (*)(stroke_t
*))destroy
;
602 /* private functions */
603 this->stroke_receive
= stroke_receive
;
605 this->logger
= logger_manager
->get_logger(logger_manager
, CONFIG
);
607 /* set up unix socket */
608 this->socket
= socket(AF_UNIX
, SOCK_STREAM
, 0);
609 if (this->socket
== -1)
611 this->logger
->log(this->logger
, ERROR
, "could not create whack socket");
616 old
= umask(~S_IRWXU
);
617 if (bind(this->socket
, (struct sockaddr
*)&socket_addr
, sizeof(socket_addr
)) < 0)
619 this->logger
->log(this->logger
, ERROR
, "could not bind stroke socket: %s", strerror(errno
));
626 if (listen(this->socket
, 0) < 0)
628 this->logger
->log(this->logger
, ERROR
, "could not listen on stroke socket: %s", strerror(errno
));
630 unlink(socket_addr
.sun_path
);
635 /* start a thread reading from the socket */
636 if (pthread_create(&(this->assigned_thread
), NULL
, (void*(*)(void*))this->stroke_receive
, this) != 0)
638 this->logger
->log(this->logger
, ERROR
, "Could not spawn stroke thread");
640 unlink(socket_addr
.sun_path
);
645 return (&this->public);