1 : /*
2 : * main.c - Unix Teredo server & relay implementation
3 : * command line handling and core functions
4 : * $Id: main.c 1938 2007-02-22 20:55:28Z remi $
5 : */
6 :
7 : /***********************************************************************
8 : * Copyright © 2004-2007 Rémi Denis-Courmont. *
9 : * This program is free software; you can redistribute and/or modify *
10 : * it under the terms of the GNU General Public License as published *
11 : * by the Free Software Foundation; version 2 of the license. *
12 : * *
13 : * This program is distributed in the hope that it will be useful, *
14 : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
16 : * See the GNU General Public License for more details. *
17 : * *
18 : * You should have received a copy of the GNU General Public License *
19 : * along with this program; if not, you can get it from: *
20 : * http://www.gnu.org/copyleft/gpl.html *
21 : ***********************************************************************/
22 :
23 : #ifdef HAVE_CONFIG_H
24 : # include <config.h>
25 : #endif
26 :
27 : #include <gettext.h>
28 : #include "binreloc.h"
29 :
30 : #include <stdio.h>
31 : #include <stdlib.h> /* strtoul(), clearenv() */
32 : #include <string.h> /* strerror() */
33 :
34 : #include <locale.h>
35 :
36 : #include <sys/types.h>
37 : #include <sys/time.h> /* for <sys/resource.h> */
38 : #include <sys/resource.h> /* getrlimit() */
39 : #include <sys/stat.h> /* fstat(), mkdir */
40 : #include <unistd.h>
41 : #include <errno.h> /* errno */
42 : #include <fcntl.h> /* O_RDONLY */
43 : #include <resolv.h> /* res_init() */
44 : #include <pthread.h>
45 : #ifdef HAVE_SYS_CAPABILITY_H
46 : # include <sys/capability.h>
47 : #endif
48 :
49 : #include <pwd.h> /* getpwnam() */
50 : #include <grp.h> /* setgroups() */
51 :
52 : #ifdef HAVE_GETOPT_H
53 : # include <getopt.h>
54 : #endif
55 :
56 : #include "miredo.h"
57 :
58 : /*
59 : * RETURN VALUES:
60 : * 0: ok
61 : * 1: I/O error
62 : * 2: command line syntax error
63 : */
64 :
65 : static int
66 : quick_usage (const char *path)
67 0 : {
68 0 : fprintf (stderr, _("Try \"%s -h | more\" for more information.\n"),
69 : path);
70 0 : return 2;
71 : }
72 :
73 :
74 : static int
75 : usage (const char *path)
76 0 : {
77 0 : printf (_(
78 : "Usage: %s [OPTIONS] [SERVER_NAME]\n"
79 : "Creates a Teredo tunneling interface for encapsulation of IPv6 over UDP.\n"
80 : "\n"
81 : " -c, --config specify an configuration file\n"
82 : " -f, --foreground run in the foreground\n"
83 : " -h, --help display this help and exit\n"
84 : " -p, --pidfile override the location of the PID file\n"
85 : " -u, --user override the user to set UID to\n"
86 : " -V, --version display program version and exit\n"), path);
87 0 : return 0;
88 : }
89 :
90 :
91 : extern int
92 : miredo_version (void)
93 0 : {
94 : #ifndef VERSION
95 : # define VERSION "unknown version"
96 : #endif
97 0 : printf (_("Miredo: Teredo IPv6 tunneling software %s (%s)\n"
98 : " built %s on %s (%s)\n"),
99 : VERSION, PACKAGE_HOST, __DATE__,
100 : PACKAGE_BUILD_HOSTNAME, PACKAGE_BUILD);
101 0 : printf (_("Configured with: %s\n"), PACKAGE_CONFIGURE_INVOCATION);
102 0 : puts (_("Written by Remi Denis-Courmont.\n"));
103 :
104 0 : printf (_("Copyright (C) 2004-%u Remi Denis-Courmont\n"
105 : "This is free software; see the source for copying conditions.\n"
106 : "There is NO warranty; not even for MERCHANTABILITY or\n"
107 : "FITNESS FOR A PARTICULAR PURPOSE.\n"), 2006);
108 0 : return 0;
109 : }
110 :
111 :
112 : static int
113 : error_dup (int opt, const char *already, const char *additionnal)
114 0 : {
115 0 : fprintf (stderr, _(
116 : "Duplicate parameter \"%s\" for option -%c\n"
117 : "would override previous value \"%s\".\n"),
118 : additionnal, opt, already);
119 0 : return 2;
120 : }
121 :
122 :
123 : #if 0
124 : static int
125 : error_qty (int opt, const char *qty)
126 : {
127 : fprintf (stderr, _(
128 : "Invalid number (or capacity exceeded) \"%s\" for option -%c\n"), qty, opt);
129 : return 2;
130 : }
131 : #endif
132 :
133 :
134 : static int
135 : error_extra (const char *extra)
136 0 : {
137 0 : fprintf (stderr, _("%s: unexpected extra parameter\n"), extra);
138 0 : return 2;
139 : }
140 :
141 :
142 : static int
143 : error_errno (const char *str)
144 0 : {
145 0 : fprintf (stderr, _("Error (%s): %s\n"), str, strerror (errno));
146 0 : return -1;
147 : }
148 :
149 :
150 : /**
151 : * Creates a Process-ID file.
152 : */
153 : #ifndef O_NOFOLLOW
154 : # define O_NOFOLLOW 0
155 : #endif
156 : static int
157 : open_pidfile (const char *path)
158 0 : {
159 : int fd;
160 :
161 0 : fd = open (path, O_WRONLY|O_CREAT|O_NOFOLLOW, 0644);
162 0 : if (fd != -1)
163 : {
164 : struct stat s;
165 :
166 0 : errno = 0;
167 : /* We only check the lock. The actual locking occurs
168 : * after (possibly) calling daemon(). */
169 0 : if ((fstat (fd, &s) == 0)
170 : && S_ISREG(s.st_mode)
171 : && (lockf (fd, F_TEST, 0) == 0))
172 0 : return fd;
173 :
174 0 : if (errno == 0) /* !S_ISREG */
175 0 : errno = EACCES;
176 :
177 0 : (void)close (fd);
178 : }
179 0 : return -1;
180 : }
181 :
182 :
183 : static int
184 : write_pid (int fd)
185 0 : {
186 : char buf[20]; // enough for > 2^64
187 :
188 : /* Actually lock the file */
189 0 : if (lockf (fd, F_TLOCK, 0))
190 0 : return -1;
191 :
192 0 : (void)snprintf (buf, sizeof (buf), "%d", (int)getpid ());
193 0 : buf[sizeof (buf) - 1] = '\0';
194 0 : size_t len = strlen (buf);
195 :
196 0 : if (ftruncate (fd, 0)
197 : || (write (fd, buf, len) != (ssize_t)len)
198 : || fdatasync (fd))
199 0 : return -1;
200 0 : return 0;
201 : }
202 :
203 :
204 : static int
205 : close_pidfile (int fd)
206 0 : {
207 0 : if (lockf (fd, F_ULOCK, 0))
208 0 : return -1;
209 0 : if (close (fd))
210 0 : return -1;
211 0 : return 0;
212 : }
213 :
214 :
215 : #ifdef MIREDO_DEFAULT_USERNAME
216 : static void
217 : setuid_notice (void)
218 0 : {
219 0 : fputs (_(
220 : "That is usually an indication that you are trying to start\n"
221 : "the program as an user with insufficient system privileges.\n"
222 : "This program should normally be started by root.\n"), stderr);
223 0 : }
224 : #endif
225 :
226 :
227 : static void *dummy_thread (void *data)
228 0 : {
229 0 : pause ();
230 0 : return data;
231 : }
232 :
233 : static int check_threading (void)
234 0 : {
235 : /* Hack to ensure that thread library is working:
236 : * - this avoids infinite crashes/restart loops on broken OSes
237 : * - this eases setting chroots (preloads libgcc_s on glibc)
238 : */
239 : pthread_t dummyth;
240 0 : errno = pthread_create (&dummyth, NULL, dummy_thread, NULL);
241 0 : if (errno)
242 0 : return error_errno ("pthread_create");
243 :
244 0 : pthread_cancel (dummyth);
245 0 : pthread_join (dummyth, NULL);
246 0 : return 0;
247 : }
248 :
249 :
250 : /**
251 : * Initialize daemon context.
252 : */
253 : static int
254 : init_security (const char *username)
255 0 : {
256 : int val;
257 :
258 : /* Sets sensible umask */
259 0 : (void)umask (022);
260 :
261 : /*
262 : * We close all file handles, except 0, 1 and 2.
263 : * This ensures that select() fd_set won't overflow.
264 : *
265 : * Those last 3 handles will be opened as /dev/null
266 : * by later daemon().
267 : */
268 0 : errno = 0;
269 0 : if (closefrom (3) && (errno != EBADF))
270 0 : return -1;
271 :
272 : /*
273 : * Make sure 0, 1 and 2 are open.
274 : */
275 0 : val = dup (2);
276 0 : if (val < 3)
277 0 : return -1;
278 0 : close (val);
279 :
280 : /* Initialize libc resolver:
281 : * - before clearenv() for LOCALDOMAIN
282 : * - before chroot() for /etc/resolv.conf
283 : * - after closefrom() just in case
284 : */
285 0 : (void)res_init ();
286 :
287 : /* Clears environment */
288 0 : (void)clearenv ();
289 :
290 0 : if (check_threading ())
291 0 : return -1;
292 :
293 : #ifdef MIREDO_DEFAULT_USERNAME
294 : /* Determines unpriviledged user */
295 0 : errno = 0;
296 0 : struct passwd *pw = getpwnam (username);
297 0 : if (pw == NULL)
298 : {
299 0 : fprintf (stderr, _("User \"%s\": %s\n"),
300 : username, errno ? strerror (errno)
301 : : _("User not found"));
302 0 : fprintf (stderr,
303 : _("Error: This program was asked to run in the\n"
304 : "security context of system user \"%s\", but it\n"
305 : "does not seem to exist on your system.\n"
306 : "\n"
307 : "Use command line option \"-u <username>\" to run\n"
308 : "this program in the security context of another\n"
309 : "user.\n"
310 : ), username);
311 0 : return -1;
312 : }
313 :
314 0 : if (pw->pw_uid == 0)
315 : {
316 0 : fputs (_("Error: This program is not supposed to keep root\n"
317 : "privileges. That is potentially very dangerous\n"
318 : "(all the more as it has never been externally audited).\n"),
319 : stderr);
320 0 : return -1;
321 : }
322 0 : unpriv_uid = pw->pw_uid;
323 :
324 : /* Ensure we have root privilege before initialization */
325 0 : if (seteuid (0))
326 : {
327 0 : fprintf (stderr, _("SetUID to root: %s\n"), strerror (errno));
328 0 : setuid_notice ();
329 0 : return -1;
330 : }
331 :
332 : /* Unpriviledged group */
333 0 : (void)setgid (pw->pw_gid);
334 0 : (void)initgroups (username, pw->pw_gid);
335 : #else
336 : (void)username;
337 : #endif /* MIREDO_DEFAULT_USERNAME */
338 :
339 :
340 : #ifdef HAVE_LIBCAP
341 : /* POSIX.1e capabilities support */
342 0 : cap_t s = cap_init ();
343 0 : if (s == NULL)
344 0 : return error_errno ("cap_init"); // unlikely
345 :
346 : static cap_value_t caps[] =
347 : {
348 : CAP_KILL, // required by the signal handler
349 : CAP_SETUID
350 : };
351 0 : cap_set_flag (s, CAP_PERMITTED, 3, caps, CAP_SET);
352 0 : cap_set_flag (s, CAP_EFFECTIVE, 3, caps, CAP_SET);
353 :
354 0 : if (miredo_chrootdir != NULL)
355 : {
356 : static cap_value_t chroot_cap[] = { CAP_SYS_CHROOT };
357 0 : cap_set_flag (s, CAP_PERMITTED, 1, chroot_cap, CAP_SET);
358 0 : cap_set_flag (s, CAP_EFFECTIVE, 1, chroot_cap, CAP_SET);
359 : }
360 :
361 0 : cap_set_flag (s, CAP_PERMITTED, miredo_capc,
362 : (cap_value_t *)miredo_capv, CAP_SET);
363 0 : cap_set_flag (s, CAP_EFFECTIVE, miredo_capc,
364 : (cap_value_t *)miredo_capv, CAP_SET);
365 :
366 0 : val = cap_set_proc (s);
367 0 : cap_free (s);
368 :
369 0 : if (val)
370 : {
371 0 : error_errno ("cap_set_proc");
372 0 : setuid_notice ();
373 0 : return -1;
374 : }
375 : #endif
376 :
377 0 : return 0;
378 : }
379 :
380 :
381 : static void init_locale (void)
382 0 : {
383 0 : (void)br_init (NULL);
384 0 : (void)setlocale (LC_ALL, "");
385 0 : char *path = br_find_locale_dir (LOCALEDIR);
386 : (void)bindtextdomain (PACKAGE, path);
387 0 : free (path);
388 : (void)textdomain (PACKAGE);
389 0 : }
390 :
391 :
392 : int miredo_main (int argc, char *argv[])
393 0 : {
394 0 : const char *username = NULL, *conffile = NULL, *servername = NULL,
395 0 : *pidfile = NULL, *chrootdir = NULL;
396 : struct
397 : {
398 : unsigned foreground:1; /* Run in the foreground */
399 : } flags;
400 :
401 : static const struct option opts[] =
402 : {
403 : { "conf", required_argument, NULL, 'c' },
404 : { "config", required_argument, NULL, 'c' },
405 : { "foreground", no_argument, NULL, 'f' },
406 : { "help", no_argument, NULL, 'h' },
407 : { "pidfile", required_argument, NULL, 'p' },
408 : { "chroot", required_argument, NULL, 't' },
409 : { "chrootdir", required_argument, NULL, 't' },
410 : { "user", required_argument, NULL, 'u' },
411 : { "username", required_argument, NULL, 'u' },
412 : { "version", no_argument, NULL, 'V' },
413 : { NULL, no_argument, NULL, '\0'}
414 : };
415 :
416 0 : init_locale ();
417 :
418 : #define ONETIME_SETTING( setting ) \
419 : if (setting != NULL) \
420 : return error_dup (c, optarg, setting); \
421 : else \
422 : setting = optarg;
423 :
424 0 : memset (&flags, 0, sizeof (flags));
425 :
426 : int c;
427 0 : while ((c = getopt_long (argc, argv, "c:fhp:t:u:V", opts, NULL)) != -1)
428 0 : switch (c)
429 : {
430 :
431 : case 'c':
432 0 : ONETIME_SETTING (conffile);
433 0 : break;
434 :
435 : case 'f':
436 0 : flags.foreground = 1;
437 0 : break;
438 :
439 : case 'h':
440 0 : return usage (argv[0]);
441 :
442 : case 'p':
443 0 : ONETIME_SETTING (pidfile);
444 0 : break;
445 :
446 : case 'u':
447 0 : ONETIME_SETTING (username);
448 0 : break;
449 :
450 : case 't':
451 0 : ONETIME_SETTING (chrootdir);
452 0 : break;
453 :
454 : case 'V':
455 0 : return miredo_version ();
456 :
457 : case '?':
458 : default:
459 0 : return quick_usage (argv[0]);
460 : }
461 :
462 0 : if (optind < argc)
463 0 : servername = argv[optind++];
464 :
465 0 : if (optind < argc)
466 0 : return error_extra (argv[optind]);
467 :
468 : #ifdef MIREDO_DEFAULT_USERNAME
469 0 : if (username == NULL)
470 0 : username = MIREDO_DEFAULT_USERNAME;
471 : #else
472 : if (username != NULL)
473 : return error_extra (username);
474 : #endif
475 :
476 : size_t str_len;
477 : char *path;
478 0 : if (conffile == NULL)
479 : {
480 0 : path = br_find_etc_dir (SYSCONFDIR);
481 0 : str_len = strlen (path) + strlen (miredo_name)
482 : + sizeof ("/miredo/.conf");
483 : }
484 : else
485 0 : str_len = 0;
486 :
487 0 : char conffile_buf[str_len];
488 0 : if (conffile == NULL)
489 : {
490 0 : snprintf (conffile_buf, str_len, "%s/miredo/%s.conf", path,
491 : miredo_name);
492 0 : free (path);
493 0 : conffile = conffile_buf;
494 : }
495 :
496 : /* Check if config file and chroot dir are present */
497 0 : if ((servername == NULL) && access (conffile, R_OK))
498 : {
499 0 : fprintf (stderr, _("Reading configuration from %s: %s\n"),
500 : conffile, strerror (errno));
501 0 : return 1;
502 : }
503 :
504 0 : if (chrootdir != NULL)
505 : {
506 : struct stat s;
507 0 : errno = 0;
508 :
509 0 : if (stat (chrootdir, &s) || !S_ISDIR(s.st_mode)
510 : || access (chrootdir, X_OK))
511 : {
512 0 : if (errno == 0)
513 0 : errno = ENOTDIR;
514 :
515 0 : error_errno (chrootdir);
516 0 : return 1;
517 : }
518 : }
519 0 : miredo_chrootdir = chrootdir;
520 :
521 0 : if (pidfile == NULL)
522 0 : str_len = sizeof (LOCALSTATEDIR"/run/" ".pid") + strlen (miredo_name);
523 : else
524 0 : str_len = 0;
525 :
526 0 : char pidfile_buf[str_len];
527 0 : if (pidfile == NULL)
528 : {
529 0 : snprintf (pidfile_buf, str_len, LOCALSTATEDIR"/run/%s.pid",
530 : miredo_name);
531 0 : pidfile = pidfile_buf;
532 : }
533 :
534 0 : if (init_security (username))
535 0 : return 1;
536 :
537 0 : if (miredo_diagnose ())
538 0 : return 1;
539 :
540 : /* Opens pidfile */
541 0 : int fd = open_pidfile (pidfile);
542 0 : if (fd == -1)
543 : {
544 0 : fprintf (stderr, _("Cannot create PID file %s:\n %s\n"),
545 : pidfile, strerror (errno));
546 0 : if (errno == EAGAIN)
547 0 : fprintf (stderr, "%s\n",
548 : _("Make sure another instance of the program is not "
549 : "already running."));
550 0 : return -1;
551 : }
552 :
553 : /* Detaches */
554 0 : if (!flags.foreground && daemon (0, 0))
555 : {
556 0 : fprintf (stderr, _("Error (%s): %s\n"), "daemon", strerror (errno));
557 0 : return -1;
558 : }
559 :
560 0 : if (write_pid (fd))
561 : {
562 0 : close (fd);
563 0 : return -1;
564 : }
565 :
566 : /*
567 : * Run
568 : */
569 0 : c = miredo (conffile, servername, fd);
570 :
571 0 : close_pidfile (fd);
572 0 : (void)unlink (pidfile);
573 :
574 0 : return c ? 1 : 0;
575 : }
576 :
|