16b0ed856137a4344da464517a51f18ff28a14b0
[strongswan.git] / src / charon-nm / charon-nm.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <stdio.h>
17 #include <syslog.h>
18 #include <signal.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include <pwd.h>
22 #include <grp.h>
23 #ifdef HAVE_PRCTL
24 #include <sys/prctl.h>
25 #endif
26
27 #include <hydra.h>
28 #include <daemon.h>
29
30 #include <library.h>
31 #include <utils/backtrace.h>
32 #include <threading/thread.h>
33
34 #include <nm/nm_backend.h>
35
36 /**
37 * Hook in library for debugging messages
38 */
39 extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
40
41 /**
42 * Simple logging hook for library logs, using syslog output
43 */
44 static void dbg_syslog(debug_t group, level_t level, char *fmt, ...)
45 {
46 if (level <= 1)
47 {
48 char buffer[8192], groupstr[4];
49 va_list args;
50
51 va_start(args, fmt);
52 /* write in memory buffer first */
53 vsnprintf(buffer, sizeof(buffer), fmt, args);
54 /* cache group name */
55 snprintf(groupstr, sizeof(groupstr), "%N", debug_names, group);
56 syslog(LOG_DAEMON|LOG_INFO, "00[%s] %s", groupstr, buffer);
57 va_end(args);
58 }
59 }
60
61 /**
62 * Run the daemon and handle unix signals
63 */
64 static void run()
65 {
66 sigset_t set;
67
68 /* handle SIGINT and SIGTERM in this handler */
69 sigemptyset(&set);
70 sigaddset(&set, SIGINT);
71 sigaddset(&set, SIGTERM);
72 sigprocmask(SIG_BLOCK, &set, NULL);
73
74 while (TRUE)
75 {
76 int sig;
77 int error;
78
79 error = sigwait(&set, &sig);
80 if (error)
81 {
82 DBG1(DBG_DMN, "error %d while waiting for a signal", error);
83 return;
84 }
85 switch (sig)
86 {
87 case SIGINT:
88 {
89 DBG1(DBG_DMN, "signal of type SIGINT received. Shutting down");
90 charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
91 return;
92 }
93 case SIGTERM:
94 {
95 DBG1(DBG_DMN, "signal of type SIGTERM received. Shutting down");
96 charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
97 return;
98 }
99 default:
100 {
101 DBG1(DBG_DMN, "unknown signal %d received. Ignored", sig);
102 break;
103 }
104 }
105 }
106 }
107
108 /**
109 * Handle SIGSEGV/SIGILL signals raised by threads
110 */
111 static void segv_handler(int signal)
112 {
113 backtrace_t *backtrace;
114
115 DBG1(DBG_DMN, "thread %u received %d", thread_current_id(), signal);
116 backtrace = backtrace_create(2);
117 backtrace->log(backtrace, stderr, TRUE);
118 backtrace->destroy(backtrace);
119
120 DBG1(DBG_DMN, "killing ourself, received critical signal");
121 abort();
122 }
123
124 /**
125 * Initialize logging to syslog
126 */
127 static void initialize_logger()
128 {
129 sys_logger_t *sys_logger;
130 debug_t group;
131 level_t def;
132
133 sys_logger = sys_logger_create(LOG_DAEMON, FALSE);
134 def = lib->settings->get_int(lib->settings,
135 "charon-nm.syslog.default", 1);
136 for (group = 0; group < DBG_MAX; group++)
137 {
138 sys_logger->set_level(sys_logger, group,
139 lib->settings->get_int(lib->settings, "charon-nm.syslog.%N", def,
140 debug_lower_names, group));
141 }
142 charon->sys_loggers->insert_last(charon->sys_loggers, sys_logger);
143 charon->bus->add_logger(charon->bus, &sys_logger->logger);
144 }
145
146 /**
147 * Lookup UID and GID
148 */
149 static bool lookup_uid_gid()
150 {
151 #ifdef IPSEC_USER
152 {
153 char buf[1024];
154 struct passwd passwd, *pwp;
155
156 if (getpwnam_r(IPSEC_USER, &passwd, buf, sizeof(buf), &pwp) != 0 ||
157 pwp == NULL)
158 {
159 DBG1(DBG_DMN, "resolving user '"IPSEC_USER"' failed");
160 return FALSE;
161 }
162 charon->uid = pwp->pw_uid;
163 }
164 #endif
165 #ifdef IPSEC_GROUP
166 {
167 char buf[1024];
168 struct group group, *grp;
169
170 if (getgrnam_r(IPSEC_GROUP, &group, buf, sizeof(buf), &grp) != 0 ||
171 grp == NULL)
172 {
173 DBG1(DBG_DMN, "resolving group '"IPSEC_GROUP"' failed");
174 return FALSE;
175 }
176 charon->gid = grp->gr_gid;
177 }
178 #endif
179 return TRUE;
180 }
181
182 /**
183 * Drop process capabilities
184 */
185 static bool drop_capabilities()
186 {
187 #ifdef HAVE_PRCTL
188 prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
189 #endif
190
191 if (setgid(charon->gid) != 0)
192 {
193 DBG1(DBG_DMN, "change to unprivileged group failed");
194 return FALSE;
195 }
196 if (setuid(charon->uid) != 0)
197 {
198 DBG1(DBG_DMN, "change to unprivileged user failed");
199 return FALSE;
200 }
201 if (!charon->drop_capabilities(charon))
202 {
203 DBG1(DBG_DMN, "unable to drop daemon capabilities");
204 return FALSE;
205 }
206 return TRUE;
207 }
208
209 /**
210 * Main function, starts NetworkManager backend.
211 */
212 int main(int argc, char *argv[])
213 {
214 struct sigaction action;
215 int status = SS_RC_INITIALIZATION_FAILED;
216
217 /* logging for library during initialization, as we have no bus yet */
218 dbg = dbg_syslog;
219
220 /* initialize library */
221 if (!library_init(NULL))
222 {
223 library_deinit();
224 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
225 }
226
227 if (lib->integrity &&
228 !lib->integrity->check_file(lib->integrity, "charon-nm", argv[0]))
229 {
230 dbg_syslog(DBG_DMN, 1, "integrity check of charon-nm failed");
231 library_deinit();
232 exit(SS_RC_DAEMON_INTEGRITY);
233 }
234
235 if (!libhydra_init("charon-nm"))
236 {
237 dbg_syslog(DBG_DMN, 1, "initialization failed - aborting charon-nm");
238 libhydra_deinit();
239 library_deinit();
240 exit(SS_RC_INITIALIZATION_FAILED);
241 }
242
243 if (!libcharon_init())
244 {
245 dbg_syslog(DBG_DMN, 1, "initialization failed - aborting charon-nm");
246 goto deinit;
247 }
248
249 if (!lookup_uid_gid())
250 {
251 dbg_syslog(DBG_DMN, 1, "invalid uid/gid - aborting charon-nm");
252 goto deinit;
253 }
254
255 initialize_logger();
256
257 DBG1(DBG_DMN, "Starting charon NetworkManager backend (strongSwan "VERSION")");
258 if (lib->integrity)
259 {
260 DBG1(DBG_DMN, "integrity tests enabled:");
261 DBG1(DBG_DMN, "lib 'libstrongswan': passed file and segment integrity tests");
262 DBG1(DBG_DMN, "lib 'libhydra': passed file and segment integrity tests");
263 DBG1(DBG_DMN, "lib 'libcharon': passed file and segment integrity tests");
264 DBG1(DBG_DMN, "daemon 'charon-nm': passed file integrity test");
265 }
266
267 /* initialize daemon */
268 if (!charon->initialize(charon,
269 lib->settings->get_str(lib->settings, "charon-nm.load", PLUGINS)))
270 {
271 DBG1(DBG_DMN, "initialization failed - aborting charon-nm");
272 goto deinit;
273 }
274
275 /* load NM backend */
276 if (!nm_backend_init())
277 {
278 DBG1(DBG_DMN, "failed to initialize NetworkManager backend - aborting charon-nm");
279 goto deinit_nm;
280 }
281
282 if (!drop_capabilities())
283 {
284 DBG1(DBG_DMN, "capability dropping failed - aborting charon-nm");
285 goto deinit_nm;
286 }
287
288 /* add handler for SEGV and ILL,
289 * INT and TERM are handled by sigwait() in run() */
290 action.sa_handler = segv_handler;
291 action.sa_flags = 0;
292 sigemptyset(&action.sa_mask);
293 sigaddset(&action.sa_mask, SIGINT);
294 sigaddset(&action.sa_mask, SIGTERM);
295 sigaction(SIGSEGV, &action, NULL);
296 sigaction(SIGILL, &action, NULL);
297 sigaction(SIGBUS, &action, NULL);
298 action.sa_handler = SIG_IGN;
299 sigaction(SIGPIPE, &action, NULL);
300
301 pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL);
302
303 /* start daemon (i.e. the threads in the thread-pool) */
304 charon->start(charon);
305
306 /* main thread goes to run loop */
307 run();
308
309 status = 0;
310
311 deinit_nm:
312 nm_backend_deinit();
313 deinit:
314 libcharon_deinit();
315 libhydra_deinit();
316 library_deinit();
317 return status;
318 }
319