1 /* Copyright (C) 2009-2014, Martin Johansson <martin@fatbob.nu>
2    Copyright (C) 2005-2014, Thorvald Natvig <thorvald@natvig.com>
3 
4    All rights reserved.
5 
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9 
10    - Redistributions of source code must retain the above copyright notice,
11      this list of conditions and the following disclaimer.
12    - Redistributions in binary form must reproduce the above copyright notice,
13      this list of conditions and the following disclaimer in the documentation
14      and/or other materials provided with the distribution.
15    - Neither the name of the Developers nor the names of its contributors may
16      be used to endorse or promote products derived from this software without
17      specific prior written permission.
18 
19    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/utsname.h>
37 #include <fcntl.h>
38 #include <pwd.h>
39 #include <grp.h>
40 #include <signal.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #ifdef _POSIX_PRIORITY_SCHEDULING
45 #if (_POSIX_PRIORITY_SCHEDULING > 0)
46 #define POSIX_PRIORITY_SCHEDULING
47 #include <sched.h>
48 #endif
49 #endif
50 #include "server.h"
51 #include "ssl.h"
52 #include "channel.h"
53 #include "log.h"
54 #include "client.h"
55 #include "conf.h"
56 #include "version.h"
57 #include "config.h"
58 #include "sharedmemory.h"
59 #include "ban.h"
60 
61 char system_string[64], version_string[64];
62 int bindport;
63 int bindport6;
64 char *bindaddr;
65 char *bindaddr6;
66 
lockfile(const char * pidfile)67 void lockfile(const char *pidfile)
68 {
69 	int lfp, flags;
70 	char str[16];
71 
72 	/* Don't use O_TRUNC here -- we want to leave the PID file
73 	 * unmodified if we cannot lock it.
74 	 */
75 	lfp = open(pidfile, O_WRONLY|O_CREAT, 0640);
76 
77 	if (lfp < 0)
78 		Log_fatal("Cannot open PID-file %s for writing", pidfile);
79 
80 	/* Try to lock the file. */
81 	if (lockf(lfp, F_TLOCK, 0) < 0) {
82 		close(lfp);
83 
84 		if (errno == EACCES || errno == EAGAIN)
85 			Log_fatal("PID file is locked -- uMurmur already running?");
86 
87 		Log_fatal("Cannot lock PID file: %s", strerror(errno));
88 	}
89 
90 	/* Now that we locked the file, erase its contents. */
91 	if (ftruncate(lfp, 0) < 0) {
92 		close(lfp);
93 		Log_fatal("Cannot truncate PID file: %s", strerror(errno));
94 	}
95 
96 	snprintf(str,16,"%d\n", getpid());
97 	(void)write(lfp, str, strlen(str)); /* record pid to lockfile */
98 	Log_info("PID-file: %s", pidfile);
99 
100 	/* If uMurmur ever starts to fork()+exec(), we don't want it to
101 	 * leak the fd to the forked process though. Set the close-on-exec
102 	 * flag to prevent leakage.
103 	 */
104 	flags = fcntl(lfp, F_GETFD, 0);
105 	flags |= FD_CLOEXEC;
106 	fcntl(lfp, F_SETFD, (long) flags);
107 
108 	/* Don't close(lfp) here!
109 	 * We want the fd to remain opened so the lock is held until the
110 	 * process exits.
111 	 */
112 	lfp = -1;
113 }
114 
115 /* Drops privileges (if configured to do so). */
switch_user(void)116 static void switch_user(void)
117 {
118 	struct passwd *pwd;
119 	struct group *grp = NULL;
120 	const char *username, *groupname;
121 	gid_t gid;
122 
123 	username = getStrConf(USERNAME);
124 	groupname = getStrConf(GROUPNAME);
125 
126 	if (!*username) {
127 		/* It's an error to specify groupname
128 		 * but leave username empty.
129 		 */
130 		if (*groupname)
131 			Log_fatal("username missing");
132 
133 		/* Nothing to do. */
134 		return;
135 	}
136 
137 	pwd = getpwnam(username);
138 	if (!pwd)
139 		Log_fatal("Unknown user '%s'", username);
140 
141 	if (!*groupname)
142 		gid = pwd->pw_gid;
143 	else {
144 		grp = getgrnam(groupname);
145 
146 		if (!grp)
147 			Log_fatal("Unknown group '%s'", groupname);
148 
149 		gid = grp->gr_gid;
150 	}
151 
152 	if (initgroups(pwd->pw_name, gid))
153 		Log_fatal("initgroups() failed: %s", strerror(errno));
154 
155 	if (setgid(gid))
156 		Log_fatal("setgid() failed: %s", strerror(errno));
157 
158 	if (setuid(pwd->pw_uid))
159 		Log_fatal("setuid() failed: %s", strerror(errno));
160 
161 	if (!grp)
162 		grp = getgrgid(gid);
163 	if (!grp)
164 		Log_fatal("getgrgid() failed: %s", strerror(errno));
165 
166 	Log_info("Switch to user '%s' group '%s'", pwd->pw_name, grp->gr_name);
167 }
168 
signal_handler(int sig)169 void signal_handler(int sig)
170 {
171 	switch(sig) {
172 		case SIGHUP:
173 			Log_info("HUP signal received.");
174 			Log_reset();
175 			break;
176 		case SIGTERM:
177 			Log_info("TERM signal. Shutting down.");
178 			Server_shutdown();
179 			break;
180 	}
181 }
182 
daemonize()183 void daemonize()
184 {
185 	int i;
186 
187 	if (getppid() == 1)
188 		return; /* already a daemon */
189 	i = fork();
190 	if ( i < 0) {
191 		fprintf(stderr, "Fork error. Exiting\n");
192 		exit(1); /* fork error */
193 	}
194 	if ( i > 0)
195 		exit(0); /* parent exits */
196 
197 	/* child (daemon) continues */
198 	setsid(); /* obtain a new process group */
199 	for (i = getdtablesize(); i >= 0; --i)
200 		close(i); /* close all descriptors */
201 
202 #ifdef USE_GNUTLS
203 	 gnutls_global_init();
204 #endif
205 
206 	i = open("/dev/null",O_RDWR);
207 	(void)dup(i);
208 	(void)dup(i);
209 
210 	umask(027); /* set newly created file permissions */
211 	(void)chdir("/");
212 
213 }
214 
215 #ifdef POSIX_PRIORITY_SCHEDULING
setscheduler()216 void setscheduler()
217 {
218 	int rc;
219 	struct sched_param sp;
220 
221 	sp.sched_priority = sched_get_priority_min(SCHED_RR); /* Should suffice */
222 	Log_info("Setting SCHED_RR prio %d", sp.sched_priority);
223 	rc = sched_setscheduler(0, SCHED_RR, &sp);
224 	if (rc < 0)
225 		Log_warn("Failed to set scheduler: %s", strerror(errno));
226 }
227 #endif
228 
printhelp()229 void printhelp()
230 {
231 	printf("uMurmur version %s ('%s'). Mumble protocol %d.%d.%d\n", UMURMUR_VERSION,
232 		UMURMUR_CODENAME, PROTVER_MAJOR, PROTVER_MINOR, PROTVER_PATCH);
233 	printf("Usage: umurmurd [-d] [-r] [-h] [-p <pidfile>] [-t] [-c <conf file>] [-a <addr>] [-b <port>]\n");
234 	printf("       -d             - Do not daemonize - run in foreground.\n");
235 #ifdef POSIX_PRIORITY_SCHEDULING
236 	printf("       -r             - Run with realtime priority\n");
237 #endif
238 	printf("       -p <pidfile>   - Write PID to this file\n");
239 	printf("       -c <conf file> - Specify configuration file (default %s)\n", DEFAULT_CONFIG);
240 	printf("       -t             - Test config. Error message to stderr + non-zero exit code on error\n");
241 	printf("       -a <address>   - Bind to IP address\n");
242 	printf("       -A <address>   - Bind to IPv6 address\n");
243 	printf("       -b <port>      - Bind to port\n");
244 	printf("       -B <port>      - Bind to port (IPv6)\n");
245 	printf("       -h             - Print this help\n");
246 	exit(0);
247 }
248 
main(int argc,char ** argv)249 int main(int argc, char **argv)
250 {
251 	bool_t nodaemon = false;
252 #ifdef POSIX_PRIORITY_SCHEDULING
253 	bool_t realtime = false;
254 #endif
255 	bool_t testconfig = false;
256 	char *conffile = NULL, *pidfile = NULL;
257 	int c;
258 	struct utsname utsbuf;
259 
260 	/* Arguments */
261 #ifdef POSIX_PRIORITY_SCHEDULING
262 	while ((c = getopt(argc, argv, "drp:c:a:A:b:B:ht")) != EOF) {
263 #else
264 		while ((c = getopt(argc, argv, "dp:c:a:A:b:B:ht")) != EOF) {
265 #endif
266 			switch(c) {
267 				case 'c':
268 					conffile = optarg;
269 					break;
270 				case 'p':
271 					pidfile = optarg;
272 					break;
273 				case 'a':
274 					bindaddr = optarg;
275 					break;
276 				case 'A':
277 					bindaddr6 = optarg;
278 					break;
279 				case 'b':
280 					bindport = atoi(optarg);
281 					break;
282 				case 'B':
283 					bindport6 = atoi(optarg);
284 					break;
285 				case 'd':
286 					nodaemon = true;
287 					break;
288 				case 'h':
289 					printhelp();
290 					break;
291 				case 't':
292 					testconfig = true;
293 					break;
294 #ifdef POSIX_PRIORITY_SCHEDULING
295 				case 'r':
296 					realtime = true;
297 					break;
298 #endif
299 				default:
300 					fprintf(stderr, "Unrecognized option\n");
301 					printhelp();
302 					break;
303 			}
304 		}
305 
306 		if (testconfig) {
307 			if (!Conf_ok(conffile))
308 				exit(1);
309 			else
310 				exit(0);
311 		}
312 
313 		/* Initialize the config subsystem early;
314 		 * switch_user() will need to read some config variables as well as logging.
315 		 */
316 		Conf_init(conffile);
317 
318 		/* Logging to terminal if not daemonizing, otherwise to syslog or log file.
319 		*/
320 		if (!nodaemon) {
321 			daemonize();
322 			Log_init(false);
323 			if (pidfile != NULL)
324 				lockfile(pidfile);
325 #ifdef POSIX_PRIORITY_SCHEDULING
326 			/* Set the scheduling policy, has to be called after daemonizing
327 			 * but before we drop privileges */
328 			if (realtime)
329 				setscheduler();
330 #endif
331 
332 		}
333 		else Log_init(true);
334 
335 #ifdef POSIX_PRIORITY_SCHEDULING
336 		/* We still want to set scheduling policy if nodaemon is specified,
337 		 * but if we are daemonizing setscheduler() will be called above */
338 		if (nodaemon) {
339 			if (realtime)
340 				setscheduler();
341 		}
342 #endif
343 
344 		signal(SIGCHLD, SIG_IGN); /* ignore child */
345 		signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
346 		signal(SIGTTOU, SIG_IGN);
347 		signal(SIGTTIN, SIG_IGN);
348 		signal(SIGPIPE, SIG_IGN);
349 		signal(SIGHUP, signal_handler); /* catch hangup signal */
350 		signal(SIGTERM, signal_handler); /* catch kill signal */
351 
352 		/* Build system string */
353 		if (uname(&utsbuf) == 0) {
354 			snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
355 			snprintf(version_string, 64, "%s", utsbuf.release);
356 		}
357 		else {
358 			snprintf(system_string, 64, "unknown unknown");
359 			snprintf(version_string, 64, "unknown");
360 		}
361 
362 		/* Initializing */
363 		SSLi_init();
364 		Chan_init();
365 		Client_init();
366 		Ban_init();
367 
368 #ifdef USE_SHAREDMEMORY_API
369     Sharedmemory_init( bindport, bindport6 );
370 #endif
371 
372 		if(!nodaemon) {
373 			/* SSL and scheduling is setup, we can drop privileges now */
374 			switch_user();
375 
376 			/* Reopen log file. If user switch results in access denied, we catch
377 			 * it early.
378 			 */
379 			Log_reset();
380 		}
381 
382 		Server_run();
383 
384 #ifdef USE_SHAREDMEMORY_API
385     Sharedmemory_deinit();
386 #endif
387 
388 		Ban_deinit();
389 		SSLi_deinit();
390 		Chan_free();
391 		Log_free();
392 		Conf_deinit();
393 
394 		if (pidfile != NULL)
395 			unlink(pidfile);
396 
397 		return 0;
398 	}
399