Added a small libcharon wrapper intended to directly host the nm plugin.
[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 /**
35 * Hook in library for debugging messages
36 */
37 extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
38
39 /**
40 * Simple logging hook for library logs, using syslog output
41 */
42 static void dbg_syslog(debug_t group, level_t level, char *fmt, ...)
43 {
44 if (level <= 1)
45 {
46 char buffer[8192], groupstr[4];
47 va_list args;
48
49 va_start(args, fmt);
50 /* write in memory buffer first */
51 vsnprintf(buffer, sizeof(buffer), fmt, args);
52 /* cache group name */
53 snprintf(groupstr, sizeof(groupstr), "%N", debug_names, group);
54 syslog(LOG_DAEMON|LOG_INFO, "00[%s] %s", groupstr, buffer);
55 va_end(args);
56 }
57 }
58
59 /**
60 * Run the daemon and handle unix signals
61 */
62 static void run()
63 {
64 sigset_t set;
65
66 /* handle SIGINT and SIGTERM in this handler */
67 sigemptyset(&set);
68 sigaddset(&set, SIGINT);
69 sigaddset(&set, SIGTERM);
70 sigprocmask(SIG_BLOCK, &set, NULL);
71
72 while (TRUE)
73 {
74 int sig;
75 int error;
76
77 error = sigwait(&set, &sig);
78 if (error)
79 {
80 DBG1(DBG_DMN, "error %d while waiting for a signal", error);
81 return;
82 }
83 switch (sig)
84 {
85 case SIGINT:
86 {
87 DBG1(DBG_DMN, "signal of type SIGINT received. Shutting down");
88 charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
89 return;
90 }
91 case SIGTERM:
92 {
93 DBG1(DBG_DMN, "signal of type SIGTERM received. Shutting down");
94 charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL, sig);
95 return;
96 }
97 default:
98 {
99 DBG1(DBG_DMN, "unknown signal %d received. Ignored", sig);
100 break;
101 }
102 }
103 }
104 }
105
106 /**
107 * Handle SIGSEGV/SIGILL signals raised by threads
108 */
109 static void segv_handler(int signal)
110 {
111 backtrace_t *backtrace;
112
113 DBG1(DBG_DMN, "thread %u received %d", thread_current_id(), signal);
114 backtrace = backtrace_create(2);
115 backtrace->log(backtrace, stderr, TRUE);
116 backtrace->destroy(backtrace);
117
118 DBG1(DBG_DMN, "killing ourself, received critical signal");
119 abort();
120 }
121
122 /**
123 * Initialize logging to syslog
124 */
125 static void initialize_logger()
126 {
127 sys_logger_t *sys_logger;
128 debug_t group;
129 level_t def;
130
131 sys_logger = sys_logger_create(LOG_DAEMON, FALSE);
132 def = lib->settings->get_int(lib->settings,
133 "charon-nm.syslog.default", 1);
134 for (group = 0; group < DBG_MAX; group++)
135 {
136 sys_logger->set_level(sys_logger, group,
137 lib->settings->get_int(lib->settings, "charon-nm.syslog.%N", def,
138 debug_lower_names, group));
139 }
140 charon->sys_loggers->insert_last(charon->sys_loggers, sys_logger);
141 charon->bus->add_logger(charon->bus, &sys_logger->logger);
142 }
143
144 /**
145 * Lookup UID and GID
146 */
147 static bool lookup_uid_gid()
148 {
149 #ifdef IPSEC_USER
150 {
151 char buf[1024];
152 struct passwd passwd, *pwp;
153
154 if (getpwnam_r(IPSEC_USER, &passwd, buf, sizeof(buf), &pwp) != 0 ||
155 pwp == NULL)
156 {
157 DBG1(DBG_DMN, "resolving user '"IPSEC_USER"' failed");
158 return FALSE;
159 }
160 charon->uid = pwp->pw_uid;
161 }
162 #endif
163 #ifdef IPSEC_GROUP
164 {
165 char buf[1024];
166 struct group group, *grp;
167
168 if (getgrnam_r(IPSEC_GROUP, &group, buf, sizeof(buf), &grp) != 0 ||
169 grp == NULL)
170 {
171 DBG1(DBG_DMN, "resolving group '"IPSEC_GROUP"' failed");
172 return FALSE;
173 }
174 charon->gid = grp->gr_gid;
175 }
176 #endif
177 return TRUE;
178 }
179
180 /**
181 * Drop process capabilities
182 */
183 static bool drop_capabilities()
184 {
185 #ifdef HAVE_PRCTL
186 prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
187 #endif
188
189 if (setgid(charon->gid) != 0)
190 {
191 DBG1(DBG_DMN, "change to unprivileged group failed");
192 return FALSE;
193 }
194 if (setuid(charon->uid) != 0)
195 {
196 DBG1(DBG_DMN, "change to unprivileged user failed");
197 return FALSE;
198 }
199 if (!charon->drop_capabilities(charon))
200 {
201 DBG1(DBG_DMN, "unable to drop daemon capabilities");
202 return FALSE;
203 }
204 return TRUE;
205 }
206
207 /**
208 * Main function, starts NetworkManager backend.
209 */
210 int main(int argc, char *argv[])
211 {
212 struct sigaction action;
213 int status = SS_RC_INITIALIZATION_FAILED;
214
215 /* logging for library during initialization, as we have no bus yet */
216 dbg = dbg_syslog;
217
218 /* initialize library */
219 if (!library_init(NULL))
220 {
221 library_deinit();
222 exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
223 }
224
225 if (lib->integrity &&
226 !lib->integrity->check_file(lib->integrity, "charon-nm", argv[0]))
227 {
228 dbg_syslog(DBG_DMN, 1, "integrity check of charon-nm failed");
229 library_deinit();
230 exit(SS_RC_DAEMON_INTEGRITY);
231 }
232
233 if (!libhydra_init("charon-nm"))
234 {
235 dbg_syslog(DBG_DMN, 1, "initialization failed - aborting charon-nm");
236 libhydra_deinit();
237 library_deinit();
238 exit(SS_RC_INITIALIZATION_FAILED);
239 }
240
241 if (!libcharon_init())
242 {
243 dbg_syslog(DBG_DMN, 1, "initialization failed - aborting charon-nm");
244 goto deinit;
245 }
246
247 if (!lookup_uid_gid())
248 {
249 dbg_syslog(DBG_DMN, 1, "invalid uid/gid - aborting charon-nm");
250 goto deinit;
251 }
252
253 initialize_logger();
254
255 DBG1(DBG_DMN, "Starting charon NetworkManager backend (strongSwan "VERSION")");
256 if (lib->integrity)
257 {
258 DBG1(DBG_DMN, "integrity tests enabled:");
259 DBG1(DBG_DMN, "lib 'libstrongswan': passed file and segment integrity tests");
260 DBG1(DBG_DMN, "lib 'libhydra': passed file and segment integrity tests");
261 DBG1(DBG_DMN, "lib 'libcharon': passed file and segment integrity tests");
262 DBG1(DBG_DMN, "daemon 'charon-nm': passed file integrity test");
263 }
264
265 /* initialize daemon */
266 if (!charon->initialize(charon,
267 lib->settings->get_str(lib->settings, "charon-nm.load", PLUGINS)))
268 {
269 DBG1(DBG_DMN, "initialization failed - aborting charon-nm");
270 goto deinit;
271 }
272
273 if (!drop_capabilities())
274 {
275 DBG1(DBG_DMN, "capability dropping failed - aborting charon-nm");
276 goto deinit;
277 }
278
279 /* add handler for SEGV and ILL,
280 * INT and TERM are handled by sigwait() in run() */
281 action.sa_handler = segv_handler;
282 action.sa_flags = 0;
283 sigemptyset(&action.sa_mask);
284 sigaddset(&action.sa_mask, SIGINT);
285 sigaddset(&action.sa_mask, SIGTERM);
286 sigaction(SIGSEGV, &action, NULL);
287 sigaction(SIGILL, &action, NULL);
288 sigaction(SIGBUS, &action, NULL);
289 action.sa_handler = SIG_IGN;
290 sigaction(SIGPIPE, &action, NULL);
291
292 pthread_sigmask(SIG_SETMASK, &action.sa_mask, NULL);
293
294 /* start daemon (i.e. the threads in the thread-pool) */
295 charon->start(charon);
296
297 /* main thread goes to run loop */
298 run();
299
300 status = 0;
301
302 deinit:
303 libcharon_deinit();
304 libhydra_deinit();
305 library_deinit();
306 return status;
307 }
308