sigwaitinfo() may fail with EINTR if interrupted by an unblocked signal not in the set
[strongswan.git] / src / charon-tkm / src / charon-tkm.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Reto Buerki
4 * Copyright (C) 2012 Adrian-Ken Rueegsegger
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #define _GNU_SOURCE
19
20 #include <stdio.h>
21 #include <syslog.h>
22 #include <signal.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <libgen.h>
27 #include <errno.h>
28
29 #include <hydra.h>
30 #include <daemon.h>
31 #include <library.h>
32 #include <utils/backtrace.h>
33 #include <threading/thread.h>
34 #include <sa/keymat.h>
35 #include <credentials/credential_manager.h>
36
37 #include "tkm.h"
38 #include "tkm_nonceg.h"
39 #include "tkm_diffie_hellman.h"
40 #include "tkm_keymat.h"
41 #include "tkm_listener.h"
42 #include "tkm_kernel_ipsec.h"
43 #include "tkm_public_key.h"
44 #include "tkm_cred.h"
45 #include "tkm_encoder.h"
46 #include "tkm_spi_generator.h"
47
48 /**
49 * TKM bus listener for IKE authorize events.
50 */
51 static tkm_listener_t *listener;
52
53 /**
54 * PID file, in which charon-tkm stores its process id
55 */
56 static char *pidfile_name = NULL;
57
58 /**
59 * Global reference to PID file (required to truncate, if undeletable)
60 */
61 static FILE *pidfile = NULL;
62
63 /**
64 * Hook in library for debugging messages
65 */
66 extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
67
68 /**
69 * Simple logging hook for library logs, using syslog output
70 */
71 static void dbg_syslog(debug_t group, level_t level, char *fmt, ...)
72 {
73 if (level <= 1)
74 {
75 char buffer[8192];
76 va_list args;
77
78 va_start(args, fmt);
79 /* write in memory buffer first */
80 vsnprintf(buffer, sizeof(buffer), fmt, args);
81 syslog(LOG_DAEMON|LOG_INFO, "00[%s] %s", debug_names->names[group],
82 buffer);
83 va_end(args);
84 }
85 }
86
87 /**
88 * Run the daemon and handle unix signals
89 */
90 static void run()
91 {
92 sigset_t set;
93
94 /* handle SIGINT and SIGTERM in this handler */
95 sigemptyset(&set);
96 sigaddset(&set, SIGINT);
97 sigaddset(&set, SIGTERM);
98 sigprocmask(SIG_BLOCK, &set, NULL);
99
100 while (TRUE)
101 {
102 int sig;
103
104 sig = sigwaitinfo(&set, NULL);
105 if (sig == -1)
106 {
107 if (errno == EINTR)
108 { /* ignore signals we didn't wait for */
109 continue;
110 }
111 DBG1(DBG_DMN, "waiting for signal failed: %s", strerror(errno));
112 return;
113 }
114 switch (sig)
115 {
116 case SIGINT:
117 {
118 DBG1(DBG_DMN, "signal of type SIGINT received. Shutting down");
119 charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
120 return;
121 }
122 case SIGTERM:
123 {
124 DBG1(DBG_DMN, "signal of type SIGTERM received. Shutting down");
125 charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
126 return;
127 }
128 }
129 }
130 }
131
132 /**
133 * Handle SIGSEGV/SIGILL signals raised by threads
134 */
135 static void segv_handler(int signal)
136 {
137 backtrace_t *backtrace;
138
139 DBG1(DBG_DMN, "thread %u received %d", thread_current_id(), signal);
140 backtrace = backtrace_create(2);
141 backtrace->log(backtrace, stderr, TRUE);
142 backtrace->destroy(backtrace);
143
144 DBG1(DBG_DMN, "killing ourself, received critical signal");
145 abort();
146 }
147
148 /**
149 * Lookup UID and GID
150 */
151 static bool lookup_uid_gid()
152 {
153 #ifdef IPSEC_USER
154 if (!lib->caps->resolve_uid(lib->caps, IPSEC_USER))
155 {
156 return FALSE;
157 }
158 #endif
159 #ifdef IPSEC_GROUP
160 if (!lib->caps->resolve_gid(lib->caps, IPSEC_GROUP))
161 {
162 return FALSE;
163 }
164 #endif
165 return TRUE;
166 }
167
168 /**
169 * Check/create PID file, return TRUE if already running
170 */
171 static bool check_pidfile()
172 {
173 struct stat stb;
174
175 if (stat(pidfile_name, &stb) == 0)
176 {
177 pidfile = fopen(pidfile_name, "r");
178 if (pidfile)
179 {
180 char buf[64];
181 pid_t pid = 0;
182
183 memset(buf, 0, sizeof(buf));
184 if (fread(buf, 1, sizeof(buf), pidfile))
185 {
186 buf[sizeof(buf) - 1] = '\0';
187 pid = atoi(buf);
188 }
189 fclose(pidfile);
190 if (pid && kill(pid, 0) == 0)
191 { /* such a process is running */
192 return TRUE;
193 }
194 }
195 DBG1(DBG_DMN, "removing pidfile '%s', process not running", pidfile_name);
196 unlink(pidfile_name);
197 }
198
199 /* create new pidfile */
200 pidfile = fopen(pidfile_name, "w");
201 if (pidfile)
202 {
203 ignore_result(fchown(fileno(pidfile),
204 lib->caps->get_uid(lib->caps),
205 lib->caps->get_gid(lib->caps)));
206 fprintf(pidfile, "%d\n", getpid());
207 fflush(pidfile);
208 }
209 return FALSE;
210 }
211
212 /**
213 * Delete/truncate the PID file
214 */
215 static void unlink_pidfile()
216 {
217 /* because unlinking the PID file may fail, we truncate it to ensure the
218 * daemon can be properly restarted. one probable cause for this is the
219 * combination of not running as root and the effective user lacking
220 * permissions on the parent dir(s) of the PID file */
221 if (pidfile)
222 {
223 ignore_result(ftruncate(fileno(pidfile), 0));
224 fclose(pidfile);
225 }
226 unlink(pidfile_name);
227 }
228 /**
229 * Main function, starts TKM backend.
230 */
231 int main(int argc, char *argv[])
232 {
233 char *dmn_name;
234 if (argc > 0 && strlen(argv[0]) > 0)
235 {
236 dmn_name = basename(argv[0]);
237 }
238 else
239 {
240 dmn_name = "charon-tkm";
241 }
242
243 /* TKM credential set */
244 tkm_cred_t *creds;
245
246 struct sigaction action;
247 int status = SS_RC_INITIALIZATION_FAILED;
248
249 /* logging for library during initialization, as we have no bus yet */
250 dbg = dbg_syslog;
251
252 /* initialize library */
253 if (!library_init(NULL, dmn_name))
254 {
255 library_deinit();
256 exit(status);
257 }
258
259 if (!libhydra_init())
260 {
261 dbg_syslog(DBG_DMN, 1, "initialization failed - aborting %s", dmn_name);
262 libhydra_deinit();
263 library_deinit();
264 exit(status);
265 }
266
267 if (!libcharon_init())
268 {
269 dbg_syslog(DBG_DMN, 1, "initialization failed - aborting %s", dmn_name);
270 goto deinit;
271 }
272
273 if (!lookup_uid_gid())
274 {
275 dbg_syslog(DBG_DMN, 1, "invalid uid/gid - aborting %s", dmn_name);
276 goto deinit;
277 }
278
279 /* the authorize hook currently does not support RFC 7427 signature auth */
280 lib->settings->set_bool(lib->settings, "%s.signature_authentication", FALSE,
281 dmn_name);
282
283 /* make sure we log to the DAEMON facility by default */
284 lib->settings->set_int(lib->settings, "%s.syslog.daemon.default",
285 lib->settings->get_int(lib->settings, "%s.syslog.daemon.default", 1,
286 dmn_name), dmn_name);
287 charon->load_loggers(charon, NULL, FALSE);
288
289 DBG1(DBG_DMN, "Starting charon with TKM backend (strongSwan "VERSION")");
290
291 /* register TKM specific plugins */
292 static plugin_feature_t features[] = {
293 PLUGIN_REGISTER(NONCE_GEN, tkm_nonceg_create),
294 PLUGIN_PROVIDE(NONCE_GEN),
295 PLUGIN_REGISTER(PUBKEY, tkm_public_key_load, TRUE),
296 PLUGIN_PROVIDE(PUBKEY, KEY_RSA),
297 PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA1),
298 PLUGIN_PROVIDE(PUBKEY_VERIFY, SIGN_RSA_EMSA_PKCS1_SHA256),
299 PLUGIN_CALLBACK(kernel_ipsec_register, tkm_kernel_ipsec_create),
300 PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
301 PLUGIN_CALLBACK(tkm_spi_generator_register, NULL),
302 PLUGIN_PROVIDE(CUSTOM, "tkm-spi-generator"),
303 PLUGIN_DEPENDS(CUSTOM, "libcharon-sa-managers"),
304 };
305 lib->plugins->add_static_features(lib->plugins, "tkm-backend", features,
306 countof(features), TRUE, NULL, NULL);
307
308 if (!register_dh_mapping())
309 {
310 DBG1(DBG_DMN, "no DH group mapping defined - aborting %s", dmn_name);
311 goto deinit;
312 }
313
314 /* register TKM keymat variant */
315 keymat_register_constructor(IKEV2, (keymat_constructor_t)tkm_keymat_create);
316
317 /* initialize daemon */
318 if (!charon->initialize(charon, PLUGINS))
319 {
320 DBG1(DBG_DMN, "initialization failed - aborting %s", dmn_name);
321 goto deinit;
322 }
323 lib->plugins->status(lib->plugins, LEVEL_CTRL);
324
325 /* set global pidfile name depending on daemon name */
326 if (asprintf(&pidfile_name, IPSEC_PIDDIR"/%s.pid", dmn_name) < 0)
327 {
328 DBG1(DBG_DMN, "unable to set pidfile name - aborting %s", dmn_name);
329 goto deinit;
330 };
331
332 if (check_pidfile())
333 {
334 DBG1(DBG_DMN, "%s already running (\"%s\" exists)", dmn_name,
335 pidfile_name);
336 goto deinit;
337 }
338
339 if (!lib->caps->drop(lib->caps))
340 {
341 DBG1(DBG_DMN, "capability dropping failed - aborting %s", dmn_name);
342 goto deinit;
343 }
344
345 /* initialize TKM client */
346 if (!tkm_init())
347 {
348 DBG1(DBG_DMN, "init of TKM client failed - aborting %s", dmn_name);
349 goto deinit;
350 }
351
352 /* register TKM authorization hook */
353 listener = tkm_listener_create();
354 charon->bus->add_listener(charon->bus, &listener->listener);
355
356 /* register TKM credential set */
357 creds = tkm_cred_create();
358 lib->credmgr->add_set(lib->credmgr, (credential_set_t*)creds);
359
360 /* register TKM credential encoder */
361 lib->encoding->add_encoder(lib->encoding, tkm_encoder_encode);
362
363 /* add handler for SEGV and ILL,
364 * INT and TERM are handled by sigwaitinfo() in run() */
365 action.sa_handler = segv_handler;
366 action.sa_flags = 0;
367 sigemptyset(&action.sa_mask);
368 sigaddset(&action.sa_mask, SIGINT);
369 sigaddset(&action.sa_mask, SIGTERM);
370 sigaction(SIGSEGV, &action, NULL);
371 sigaction(SIGILL, &action, NULL);
372 sigaction(SIGBUS, &action, NULL);
373 action.sa_handler = SIG_IGN;
374 sigaction(SIGPIPE, &action, NULL);
375
376 pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL);
377
378 /* start daemon (i.e. the threads in the thread-pool) */
379 charon->start(charon);
380
381 /* main thread goes to run loop */
382 run();
383
384 unlink_pidfile();
385 status = 0;
386 charon->bus->remove_listener(charon->bus, &listener->listener);
387 listener->destroy(listener);
388 creds->destroy(creds);
389 lib->encoding->remove_encoder(lib->encoding, tkm_encoder_encode);
390
391 deinit:
392 destroy_dh_mapping();
393 libcharon_deinit();
394 libhydra_deinit();
395 library_deinit();
396 tkm_deinit();
397 return status;
398 }