]>
Commit | Line | Data |
---|---|---|
73ed38e7 | 1 | /* |
0ca0fa71 | 2 | * Copyright (C) 2006-2018 Tobias Brunner |
e2d9f27c | 3 | * Copyright (C) 2005-2014 Martin Willi |
73ed38e7 MW |
4 | * Copyright (C) 2006 Daniel Roethlisberger |
5 | * Copyright (C) 2005 Jan Hutter | |
1b671669 | 6 | * HSR Hochschule fuer Technik Rapperswil |
e2d9f27c | 7 | * Copyright (C) 2014 revosec AG |
73ed38e7 MW |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2 of the License, or (at your | |
12 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 | * for more details. | |
18 | */ | |
19 | ||
20 | #include <signal.h> | |
21 | #include <stdio.h> | |
22 | #include <pthread.h> | |
23 | #include <sys/stat.h> | |
24 | #include <sys/types.h> | |
25 | #include <sys/utsname.h> | |
26 | #include <unistd.h> | |
27 | #include <errno.h> | |
e2d9f27c MW |
28 | |
29 | /* won't make sense from our logging hook */ | |
30 | #define SD_JOURNAL_SUPPRESS_LOCATION | |
73ed38e7 | 31 | #include <systemd/sd-daemon.h> |
e2d9f27c | 32 | #include <systemd/sd-journal.h> |
73ed38e7 | 33 | |
73ed38e7 MW |
34 | #include <daemon.h> |
35 | ||
36 | #include <library.h> | |
37 | #include <utils/backtrace.h> | |
38 | #include <threading/thread.h> | |
e2d9f27c | 39 | #include <threading/rwlock.h> |
73ed38e7 | 40 | |
f3c83322 TB |
41 | /** |
42 | * Default user and group | |
43 | */ | |
44 | #ifndef IPSEC_USER | |
45 | #define IPSEC_USER NULL | |
46 | #endif | |
47 | ||
48 | #ifndef IPSEC_GROUP | |
49 | #define IPSEC_GROUP NULL | |
50 | #endif | |
51 | ||
73ed38e7 MW |
52 | /** |
53 | * hook in library for debugging messages | |
54 | */ | |
55 | extern void (*dbg) (debug_t group, level_t level, char *fmt, ...); | |
56 | ||
57 | /** | |
58 | * Logging hook for library logs, using stderr output | |
59 | */ | |
60 | static void dbg_stderr(debug_t group, level_t level, char *fmt, ...) | |
61 | { | |
62 | va_list args; | |
63 | ||
64 | if (level <= 1) | |
65 | { | |
66 | va_start(args, fmt); | |
67 | fprintf(stderr, "00[%N] ", debug_names, group); | |
68 | vfprintf(stderr, fmt, args); | |
69 | fprintf(stderr, "\n"); | |
70 | va_end(args); | |
71 | } | |
72 | } | |
73 | ||
e2d9f27c MW |
74 | typedef struct journal_logger_t journal_logger_t; |
75 | ||
76 | /** | |
77 | * Logger implementation using systemd-journal | |
78 | */ | |
79 | struct journal_logger_t { | |
80 | ||
81 | /** | |
0ca0fa71 | 82 | * Public interface |
e2d9f27c | 83 | */ |
0ca0fa71 | 84 | custom_logger_t public; |
e2d9f27c MW |
85 | |
86 | /** | |
87 | * Configured loglevels | |
88 | */ | |
89 | level_t levels[DBG_MAX]; | |
90 | ||
91 | /** | |
92 | * Lock for levels | |
93 | */ | |
94 | rwlock_t *lock; | |
95 | }; | |
96 | ||
97 | METHOD(logger_t, vlog, void, | |
98 | journal_logger_t *this, debug_t group, level_t level, int thread, | |
99 | ike_sa_t *ike_sa, const char *fmt, va_list args) | |
100 | { | |
101 | char buf[4096], *msg = buf; | |
102 | ssize_t len; | |
103 | va_list copy; | |
104 | ||
105 | va_copy(copy, args); | |
106 | len = vsnprintf(msg, sizeof(buf), fmt, copy); | |
107 | va_end(copy); | |
108 | ||
109 | if (len >= sizeof(buf)) | |
110 | { | |
111 | len++; | |
112 | msg = malloc(len); | |
113 | va_copy(copy, args); | |
114 | len = vsnprintf(msg, len, fmt, copy); | |
115 | va_end(copy); | |
116 | } | |
117 | if (len > 0) | |
118 | { | |
119 | char unique[64] = "", name[256] = ""; | |
120 | int priority; | |
121 | ||
122 | if (ike_sa) | |
123 | { | |
124 | snprintf(unique, sizeof(unique), "IKE_SA_UNIQUE_ID=%u", | |
125 | ike_sa->get_unique_id(ike_sa)); | |
126 | if (ike_sa->get_peer_cfg(ike_sa)) | |
127 | { | |
128 | snprintf(name, sizeof(name), "IKE_SA_NAME=%s", | |
129 | ike_sa->get_name(ike_sa)); | |
130 | } | |
131 | } | |
132 | switch (level) | |
133 | { | |
134 | case LEVEL_AUDIT: | |
135 | priority = LOG_NOTICE; | |
136 | break; | |
137 | case LEVEL_CTRL: | |
138 | priority = LOG_INFO; | |
139 | break; | |
140 | default: | |
141 | priority = LOG_DEBUG; | |
142 | break; | |
143 | } | |
144 | sd_journal_send( | |
145 | "MESSAGE=%s", msg, | |
146 | "MESSAGE_ID=57d2708c-d607-43bd-8c39-66bf%.8x", | |
147 | chunk_hash_static(chunk_from_str((char*)fmt)), | |
148 | "PRIORITY=%d", priority, | |
149 | "GROUP=%N", debug_names, group, | |
150 | "LEVEL=%d", level, | |
151 | "THREAD=%d", thread, | |
152 | unique[0] ? unique : NULL, | |
153 | name[0] ? name : NULL, | |
154 | NULL); | |
155 | } | |
156 | if (msg != buf) | |
157 | { | |
158 | free(msg); | |
159 | } | |
160 | } | |
161 | ||
162 | METHOD(logger_t, get_level, level_t, | |
163 | journal_logger_t *this, debug_t group) | |
164 | { | |
165 | level_t level; | |
166 | ||
167 | this->lock->read_lock(this->lock); | |
168 | level = this->levels[group]; | |
169 | this->lock->unlock(this->lock); | |
170 | ||
171 | return level; | |
172 | } | |
173 | ||
0ca0fa71 TB |
174 | METHOD(custom_logger_t, set_level, void, |
175 | journal_logger_t *this, debug_t group, level_t level) | |
e2d9f27c | 176 | { |
e2d9f27c | 177 | this->lock->write_lock(this->lock); |
0ca0fa71 | 178 | this->levels[group] = level; |
e2d9f27c | 179 | this->lock->unlock(this->lock); |
0ca0fa71 | 180 | } |
e2d9f27c | 181 | |
0ca0fa71 TB |
182 | METHOD(custom_logger_t, logger_destroy, void, |
183 | journal_logger_t *this) | |
184 | { | |
185 | this->lock->destroy(this->lock); | |
186 | free(this); | |
e2d9f27c MW |
187 | } |
188 | ||
0ca0fa71 | 189 | static custom_logger_t *journal_logger_create(const char *name) |
e2d9f27c MW |
190 | { |
191 | journal_logger_t *this; | |
192 | ||
0ca0fa71 TB |
193 | INIT(this, |
194 | .public = { | |
e2d9f27c MW |
195 | .logger = { |
196 | .vlog = _vlog, | |
197 | .get_level = _get_level, | |
198 | }, | |
0ca0fa71 TB |
199 | .set_level = _set_level, |
200 | .destroy = _logger_destroy, | |
201 | }, | |
202 | .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), | |
203 | ); | |
204 | return &this->public; | |
e2d9f27c MW |
205 | } |
206 | ||
73ed38e7 MW |
207 | /** |
208 | * Run the daemon and handle unix signals | |
209 | */ | |
210 | static int run() | |
211 | { | |
212 | sigset_t set; | |
213 | ||
214 | sigemptyset(&set); | |
ff22d53b | 215 | sigaddset(&set, SIGHUP); |
73ed38e7 MW |
216 | sigaddset(&set, SIGTERM); |
217 | sigprocmask(SIG_BLOCK, &set, NULL); | |
218 | ||
219 | sd_notify(0, "READY=1\n"); | |
220 | ||
221 | while (TRUE) | |
222 | { | |
85814809 | 223 | int sig; |
73ed38e7 | 224 | |
85814809 TB |
225 | sig = sigwaitinfo(&set, NULL); |
226 | if (sig == -1) | |
73ed38e7 | 227 | { |
88b85e02 TB |
228 | if (errno == EINTR) |
229 | { /* ignore signals we didn't wait for */ | |
230 | continue; | |
231 | } | |
31956501 | 232 | DBG1(DBG_DMN, "waiting for signal failed: %s", strerror(errno)); |
73ed38e7 MW |
233 | return SS_RC_INITIALIZATION_FAILED; |
234 | } | |
235 | switch (sig) | |
236 | { | |
ff22d53b TB |
237 | case SIGHUP: |
238 | { | |
239 | DBG1(DBG_DMN, "signal of type SIGHUP received. Reloading " | |
240 | "configuration"); | |
241 | if (lib->settings->load_files(lib->settings, lib->conf, FALSE)) | |
242 | { | |
9665686b | 243 | charon->load_loggers(charon); |
ff22d53b TB |
244 | lib->plugins->reload(lib->plugins, NULL); |
245 | } | |
246 | else | |
247 | { | |
248 | DBG1(DBG_DMN, "reloading config failed, keeping old"); | |
249 | } | |
250 | break; | |
251 | } | |
73ed38e7 MW |
252 | case SIGTERM: |
253 | { | |
254 | DBG1(DBG_DMN, "SIGTERM received, shutting down"); | |
255 | charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig); | |
256 | return 0; | |
257 | } | |
73ed38e7 MW |
258 | } |
259 | } | |
260 | } | |
261 | ||
262 | /** | |
263 | * lookup UID and GID | |
264 | */ | |
265 | static bool lookup_uid_gid() | |
266 | { | |
9c3c41f2 | 267 | char *name; |
f3c83322 TB |
268 | |
269 | name = lib->settings->get_str(lib->settings, "%s.user", IPSEC_USER, | |
270 | lib->ns); | |
271 | if (name && !lib->caps->resolve_uid(lib->caps, name)) | |
73ed38e7 MW |
272 | { |
273 | return FALSE; | |
274 | } | |
f3c83322 TB |
275 | name = lib->settings->get_str(lib->settings, "%s.group", IPSEC_GROUP, |
276 | lib->ns); | |
277 | if (name && !lib->caps->resolve_gid(lib->caps, name)) | |
73ed38e7 MW |
278 | { |
279 | return FALSE; | |
280 | } | |
73ed38e7 MW |
281 | return TRUE; |
282 | } | |
283 | ||
284 | /** | |
285 | * Handle SIGSEGV/SIGILL signals raised by threads | |
286 | */ | |
287 | static void segv_handler(int signal) | |
288 | { | |
289 | backtrace_t *backtrace; | |
290 | ||
291 | DBG1(DBG_DMN, "thread %u received %d", thread_current_id(), signal); | |
292 | backtrace = backtrace_create(2); | |
293 | backtrace->log(backtrace, NULL, TRUE); | |
294 | backtrace->log(backtrace, stderr, TRUE); | |
295 | backtrace->destroy(backtrace); | |
296 | ||
297 | DBG1(DBG_DMN, "killing ourself, received critical signal"); | |
298 | abort(); | |
299 | } | |
300 | ||
bc4e689d TB |
301 | /** |
302 | * Add namespace alias | |
303 | */ | |
304 | static void __attribute__ ((constructor))register_namespace() | |
305 | { | |
306 | /* inherit settings from charon */ | |
307 | library_add_namespace("charon"); | |
308 | } | |
309 | ||
0ca0fa71 TB |
310 | /** |
311 | * Register journal logger | |
312 | */ | |
313 | static void __attribute__ ((constructor))register_logger() | |
314 | { | |
315 | register_custom_logger("journal", journal_logger_create); | |
316 | } | |
317 | ||
73ed38e7 MW |
318 | /** |
319 | * Main function, starts the daemon. | |
320 | */ | |
321 | int main(int argc, char *argv[]) | |
322 | { | |
323 | struct sigaction action; | |
324 | struct utsname utsname; | |
cbe9e575 | 325 | int status = SS_RC_INITIALIZATION_FAILED; |
73ed38e7 MW |
326 | |
327 | dbg = dbg_stderr; | |
328 | ||
329 | if (uname(&utsname) != 0) | |
330 | { | |
331 | memset(&utsname, 0, sizeof(utsname)); | |
332 | } | |
333 | ||
334 | sd_notifyf(0, "STATUS=Starting charon-systemd, strongSwan %s, %s %s, %s", | |
335 | VERSION, utsname.sysname, utsname.release, utsname.machine); | |
336 | ||
337 | atexit(library_deinit); | |
338 | if (!library_init(NULL, "charon-systemd")) | |
339 | { | |
340 | sd_notifyf(0, "STATUS=libstrongswan initialization failed"); | |
341 | return SS_RC_INITIALIZATION_FAILED; | |
342 | } | |
343 | if (lib->integrity && | |
344 | !lib->integrity->check_file(lib->integrity, "charon-systemd", argv[0])) | |
345 | { | |
346 | sd_notifyf(0, "STATUS=integrity check of charon-systemd failed"); | |
347 | return SS_RC_INITIALIZATION_FAILED; | |
348 | } | |
73ed38e7 MW |
349 | if (!libcharon_init()) |
350 | { | |
351 | sd_notifyf(0, "STATUS=libcharon initialization failed"); | |
cbe9e575 | 352 | goto error; |
73ed38e7 MW |
353 | } |
354 | if (!lookup_uid_gid()) | |
355 | { | |
245979ab | 356 | sd_notifyf(0, "STATUS=unknown uid/gid"); |
cbe9e575 | 357 | goto error; |
73ed38e7 | 358 | } |
0ca0fa71 TB |
359 | /* we registered the journal logger as custom logger, which gets its |
360 | * settings from <ns>.customlog.journal, let it fallback to <ns>.journal */ | |
361 | lib->settings->add_fallback(lib->settings, "%s.customlog.journal", | |
362 | "%s.journal", lib->ns); | |
363 | /* load the journal logger by default */ | |
364 | lib->settings->set_default_str(lib->settings, "%s.journal.default", "1", | |
365 | lib->ns); | |
73ed38e7 | 366 | |
0ca0fa71 | 367 | charon->load_loggers(charon); |
e2d9f27c | 368 | |
d2f4345b TB |
369 | if (!charon->initialize(charon, |
370 | lib->settings->get_str(lib->settings, "%s.load", PLUGINS, lib->ns))) | |
73ed38e7 MW |
371 | { |
372 | sd_notifyf(0, "STATUS=charon initialization failed"); | |
cbe9e575 | 373 | goto error; |
73ed38e7 MW |
374 | } |
375 | lib->plugins->status(lib->plugins, LEVEL_CTRL); | |
376 | ||
377 | if (!lib->caps->drop(lib->caps)) | |
378 | { | |
379 | sd_notifyf(0, "STATUS=dropping capabilities failed"); | |
cbe9e575 | 380 | goto error; |
73ed38e7 MW |
381 | } |
382 | ||
383 | /* add handler for SEGV and ILL, | |
85814809 | 384 | * INT, TERM and HUP are handled by sigwaitinfo() in run() */ |
73ed38e7 MW |
385 | action.sa_handler = segv_handler; |
386 | action.sa_flags = 0; | |
387 | sigemptyset(&action.sa_mask); | |
388 | sigaddset(&action.sa_mask, SIGINT); | |
389 | sigaddset(&action.sa_mask, SIGTERM); | |
390 | sigaddset(&action.sa_mask, SIGHUP); | |
391 | sigaction(SIGSEGV, &action, NULL); | |
392 | sigaction(SIGILL, &action, NULL); | |
393 | sigaction(SIGBUS, &action, NULL); | |
394 | action.sa_handler = SIG_IGN; | |
395 | sigaction(SIGPIPE, &action, NULL); | |
396 | ||
397 | pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL); | |
398 | ||
399 | charon->start(charon); | |
400 | ||
401 | sd_notifyf(0, "STATUS=charon-systemd running, strongSwan %s, %s %s, %s", | |
402 | VERSION, utsname.sysname, utsname.release, utsname.machine); | |
403 | ||
cbe9e575 TB |
404 | status = run(); |
405 | ||
406 | error: | |
407 | libcharon_deinit(); | |
408 | return status; | |
73ed38e7 | 409 | } |