1 /*
2 * miredo.c - Miredo common daemon functions
3 *
4 * See "Teredo: Tunneling IPv6 over UDP through NATs"
5 * for more information
6 */
7
8 /***********************************************************************
9 * Copyright © 2004-2007 Rémi Denis-Courmont. *
10 * This program is free software; you can redistribute and/or modify *
11 * it under the terms of the GNU General Public License as published *
12 * by the Free Software Foundation; version 2 of the license, or (at *
13 * your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
18 * See the GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, you can get it from: *
22 * http://www.gnu.org/copyleft/gpl.html *
23 ***********************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include <config.h>
27 #endif
28
29 #include <gettext.h>
30
31 #include <string.h> // memset(), strsignal()
32 #include <stdlib.h> // exit()
33 #include <inttypes.h>
34 #include <stdbool.h>
35 #include <stdarg.h>
36
37 #include <pthread.h> // pthread_sigmask()
38 #include <signal.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <syslog.h>
43 #include <unistd.h> // uid_t
44 #include <sys/wait.h> // waitpid()
45 #ifdef HAVE_SYS_CAPABILITY_H
46 # include <sys/capability.h>
47 #endif
48
49 #ifndef LOG_PERROR
50 # define LOG_PERROR 0
51 #endif
52
53 #include "miredo.h"
54 #include "conf.h"
55
56 uid_t unpriv_uid = 0;
57 const char *miredo_chrootdir = NULL;
58
59 extern int
drop_privileges(void)60 drop_privileges (void)
61 {
62 /*
63 * We could chroot earlier, but we do it know to keep compatibility with
64 * grsecurity Linux kernel patch that automatically removes capabilities
65 * when chrooted.
66 */
67 if ((miredo_chrootdir != NULL)
68 && (chroot (miredo_chrootdir) || chdir ("/")))
69 {
70 syslog (LOG_ALERT, _("Error (%s): %m"), "chroot");
71 return -1;
72 }
73
74 // Definitely drops privileges
75 if (setuid (unpriv_uid))
76 {
77 syslog (LOG_ALERT, _("Error (%s): %m"), "setuid");
78 return -1;
79 }
80
81 #ifdef HAVE_LIBCAP
82 cap_t s = cap_init ();
83 if (s != NULL)
84 {
85 cap_set_proc (s);
86 cap_free (s);
87 }
88 #endif
89 return 0;
90 }
91
92
93 /*
94 * Configuration and respawning stuff
95 */
logger(void * dummy,bool error,const char * fmt,va_list ap)96 static void logger (void *dummy, bool error, const char *fmt, va_list ap)
97 {
98 (void)dummy;
99
100 vsyslog (error ? LOG_ERR : LOG_WARNING, fmt, ap);
101 }
102
103
104 extern int
miredo(const char * confpath,const char * server_name,int pidfd)105 miredo (const char *confpath, const char *server_name, int pidfd)
106 {
107 sigset_t set, exit_set, reload_set;
108 int retval;
109 miredo_conf *cnf = miredo_conf_create (logger, NULL);
110
111 if (cnf == NULL)
112 return -1;
113
114 sigemptyset (&set);
115
116 /* Exit signals */
117 sigaddset (&set, SIGINT);
118 sigaddset (&set, SIGQUIT);
119 sigaddset (&set, SIGTERM);
120 exit_set = set;
121
122 /* Reload signal */
123 sigaddset (&set, SIGHUP);
124 reload_set = set;
125
126 /* No-op signal */
127 sigaddset (&set, SIGCHLD);
128
129 pthread_sigmask (SIG_BLOCK, &set, NULL);
130
131 openlog (miredo_name, LOG_PID | LOG_PERROR, LOG_DAEMON);
132
133 do
134 {
135 int facility = LOG_DAEMON;
136 retval = 1;
137
138 if (!miredo_conf_read_file (cnf, confpath))
139 syslog (LOG_WARNING, _("Loading configuration from %s failed"),
140 confpath);
141
142 miredo_conf_parse_syslog_facility (cnf, "SyslogFacility",
143 &facility);
144
145 closelog ();
146 openlog (miredo_name, LOG_PID | LOG_PERROR, facility);
147 syslog (LOG_INFO, _("Starting..."));
148
149 // Starts the main miredo process
150 pid_t pid = fork ();
151
152 switch (pid)
153 {
154 case -1:
155 syslog (LOG_ALERT, _("Error (%s): %m"), "fork");
156 continue;
157
158 case 0:
159 close (pidfd);
160 retval = miredo_run (cnf, server_name);
161 miredo_conf_destroy (cnf);
162 closelog ();
163 exit (-retval);
164 break;
165
166 default:
167 miredo_conf_clear (cnf, 0);
168 }
169
170 int status, signum;
171
172 for (;;)
173 {
174 while (sigwait (&set, &signum));
175
176 if (signum == SIGCHLD
177 && waitpid (pid, &status, WNOHANG) == pid)
178 break; /* child died */
179
180 if (sigismember (&exit_set, signum))
181 {
182 syslog (LOG_NOTICE, _("Exiting on signal %d (%s)"),
183 signum, strsignal (signum));
184 retval = 0;
185 }
186 else
187 if (sigismember (&reload_set, signum))
188 {
189 syslog (LOG_NOTICE,
190 _("Reloading configuration on signal %d (%s)"),
191 signum, strsignal (signum));
192 retval = 2;
193 }
194 else
195 continue;
196
197 // Tells children to exit */
198 kill (pid, SIGTERM);
199 // Waits until the miredo process terminates
200 while (waitpid (pid, &status, 0) != pid);
201 break;
202 }
203
204 // At this point, the child process is gone.
205 if (WIFEXITED (status))
206 {
207 status = WEXITSTATUS (status);
208 syslog (LOG_NOTICE, _("Child %d exited (code: %d)"),
209 (int)pid, status);
210 if (status)
211 retval = 1;
212 }
213 else
214 if (WIFSIGNALED (status))
215 {
216 status = WTERMSIG (status);
217 syslog (LOG_INFO, _("Child %d killed by signal %d (%s)"),
218 (int)pid, status, strsignal (status));
219 retval = 2;
220 sleep (1);
221 }
222 }
223 while (retval == 2);
224
225 miredo_conf_destroy (cnf);
226
227 syslog (LOG_INFO, gettext (retval
228 ? N_("Terminated with error(s).")
229 : N_("Terminated with no error.")));
230
231 closelog ();
232 return -retval;
233 }
234
235
236 int (*miredo_diagnose) (void);
237 int (*miredo_run) (miredo_conf *conf, const char *server);
238
239 const char *miredo_name;
240
241 # ifdef HAVE_LIBCAP
242 const cap_value_t *miredo_capv;
243 int miredo_capc;
244 # endif
245
246