Do user switch after SSL keys are initialized
[umurmur.git] / src / main.c
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
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). */
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
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
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
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
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
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                 }
326                 else Log_init(true);
327
328                 signal(SIGCHLD, SIG_IGN); /* ignore child */
329                 signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
330                 signal(SIGTTOU, SIG_IGN);
331                 signal(SIGTTIN, SIG_IGN);
332                 signal(SIGPIPE, SIG_IGN);
333                 signal(SIGHUP, signal_handler); /* catch hangup signal */
334                 signal(SIGTERM, signal_handler); /* catch kill signal */
335
336                 /* Build system string */
337                 if (uname(&utsbuf) == 0) {
338                         snprintf(system_string, 64, "%s %s", utsbuf.sysname, utsbuf.machine);
339                         snprintf(version_string, 64, "%s", utsbuf.release);
340                 }
341                 else {
342                         snprintf(system_string, 64, "unknown unknown");
343                         snprintf(version_string, 64, "unknown");
344                 }
345
346                 /* Initializing */
347                 SSLi_init();
348                 Chan_init();
349                 Client_init();
350                 Ban_init();
351
352 #ifdef USE_SHAREDMEMORY_API
353     Sharedmemory_init( bindport, bindport6 );
354 #endif
355
356 #ifdef POSIX_PRIORITY_SCHEDULING
357                 if (realtime)
358                         setscheduler();
359 #endif
360
361                 switch_user();
362                 /* Reopen log file. If user switch results in access denied, we catch
363                  * it early.
364                  */
365                 Log_reset();
366
367                 Server_run();
368
369 #ifdef USE_SHAREDMEMORY_API
370     Sharedmemory_deinit();
371 #endif
372
373                 Ban_deinit();
374                 SSLi_deinit();
375                 Chan_free();
376                 Log_free();
377                 Conf_deinit();
378
379                 if (pidfile != NULL)
380                         unlink(pidfile);
381
382                 return 0;
383         }