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