Merge from vendor branch TNFTP:
[dragonfly.git] / contrib / bind-9.3 / bin / named / unix / os.c
1 /*
2  * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-2002  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: os.c,v 1.46.2.4.8.24 2006/02/03 23:51:37 marka Exp $ */
19
20 #include <config.h>
21 #include <stdarg.h>
22
23 #include <sys/types.h>  /* dev_t FreeBSD 2.1 */
24 #include <sys/stat.h>
25
26 #include <ctype.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <grp.h>                /* Required for initgroups() on IRIX. */
30 #include <pwd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <signal.h>
34 #include <syslog.h>
35 #ifdef HAVE_TZSET
36 #include <time.h>
37 #endif
38 #include <unistd.h>
39
40 #include <isc/buffer.h>
41 #include <isc/file.h>
42 #include <isc/print.h>
43 #include <isc/result.h>
44 #include <isc/strerror.h>
45 #include <isc/string.h>
46
47 #include <named/main.h>
48 #include <named/os.h>
49 #ifdef HAVE_LIBSCF
50 #include <named/ns_smf_globals.h>
51 #endif
52
53 static char *pidfile = NULL;
54 static int devnullfd = -1;
55
56 #ifndef ISC_FACILITY
57 #define ISC_FACILITY LOG_DAEMON
58 #endif
59
60 /*
61  * If there's no <linux/capability.h>, we don't care about <sys/prctl.h>
62  */
63 #ifndef HAVE_LINUX_CAPABILITY_H
64 #undef HAVE_SYS_PRCTL_H
65 #endif
66
67 /*
68  * Linux defines:
69  *      (T) HAVE_LINUXTHREADS
70  *      (C) HAVE_LINUX_CAPABILITY_H
71  *      (P) HAVE_SYS_PRCTL_H
72  * The possible cases are:
73  *      none:   setuid() normally
74  *      T:      no setuid()
75  *      C:      setuid() normally, drop caps (keep CAP_SETUID)
76  *      T+C:    no setuid(), drop caps (don't keep CAP_SETUID)
77  *      T+C+P:  setuid() early, drop caps (keep CAP_SETUID)
78  *      C+P:    setuid() normally, drop caps (keep CAP_SETUID)
79  *      P:      not possible
80  *      T+P:    not possible
81  *
82  * if (C)
83  *      caps = BIND_SERVICE + CHROOT + SETGID
84  *      if ((T && C && P) || !T)
85  *              caps += SETUID
86  *      endif
87  *      capset(caps)
88  * endif
89  * if (T && C && P && -u)
90  *      setuid()
91  * else if (T && -u)
92  *      fail
93  * --> start threads
94  * if (!T && -u)
95  *      setuid()
96  * if (C && (P || !-u))
97  *      caps = BIND_SERVICE
98  *      capset(caps)
99  * endif
100  *
101  * It will be nice when Linux threads work properly with setuid().
102  */
103
104 #ifdef HAVE_LINUXTHREADS
105 static pid_t mainpid = 0;
106 #endif
107
108 static struct passwd *runas_pw = NULL;
109 static isc_boolean_t done_setuid = ISC_FALSE;
110 static int dfd[2] = { -1, -1 };
111
112 #ifdef HAVE_LINUX_CAPABILITY_H
113
114 static isc_boolean_t non_root = ISC_FALSE;
115 static isc_boolean_t non_root_caps = ISC_FALSE;
116
117 /*
118  * We define _LINUX_FS_H to prevent it from being included.  We don't need
119  * anything from it, and the files it includes cause warnings with 2.2
120  * kernels, and compilation failures (due to conflicts between <linux/string.h>
121  * and <string.h>) on 2.3 kernels.
122  */
123 #define _LINUX_FS_H
124
125 #include <sys/syscall.h>        /* Required for syscall(). */
126 #include <linux/capability.h>   /* Required for _LINUX_CAPABILITY_VERSION. */
127
128 #ifdef HAVE_SYS_PRCTL_H
129 #include <sys/prctl.h>          /* Required for prctl(). */
130
131 /*
132  * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it
133  * here.  This allows setuid() to work on systems running a new enough
134  * kernel but with /usr/include/linux pointing to "standard" kernel
135  * headers.
136  */
137 #ifndef PR_SET_KEEPCAPS
138 #define PR_SET_KEEPCAPS 8
139 #endif
140
141 #endif /* HAVE_SYS_PRCTL_H */
142
143 #ifndef SYS_capset
144 #ifndef __NR_capset
145 #include <asm/unistd.h> /* Slackware 4.0 needs this. */
146 #endif
147 #define SYS_capset __NR_capset
148 #endif
149
150 static void
151 linux_setcaps(unsigned int caps) {
152         struct __user_cap_header_struct caphead;
153         struct __user_cap_data_struct cap;
154         char strbuf[ISC_STRERRORSIZE];
155
156         if ((getuid() != 0 && !non_root_caps) || non_root)
157                 return;
158
159         memset(&caphead, 0, sizeof(caphead));
160         caphead.version = _LINUX_CAPABILITY_VERSION;
161         caphead.pid = 0;
162         memset(&cap, 0, sizeof(cap));
163         cap.effective = caps;
164         cap.permitted = caps;
165         cap.inheritable = 0;
166         if (syscall(SYS_capset, &caphead, &cap) < 0) {
167                 isc__strerror(errno, strbuf, sizeof(strbuf));
168                 ns_main_earlyfatal("capset failed: %s:"
169                                    " please ensure that the capset kernel"
170                                    " module is loaded.  see insmod(8)",
171                                    strbuf);
172         }
173 }
174
175 static void
176 linux_initialprivs(void) {
177         unsigned int caps;
178
179         /*
180          * We don't need most privileges, so we drop them right away.
181          * Later on linux_minprivs() will be called, which will drop our
182          * capabilities to the minimum needed to run the server.
183          */
184
185         caps = 0;
186
187         /*
188          * We need to be able to bind() to privileged ports, notably port 53!
189          */
190         caps |= (1 << CAP_NET_BIND_SERVICE);
191
192         /*
193          * We need chroot() initially too.
194          */
195         caps |= (1 << CAP_SYS_CHROOT);
196
197 #if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
198         /*
199          * We can setuid() only if either the kernel supports keeping
200          * capabilities after setuid() (which we don't know until we've
201          * tried) or we're not using threads.  If either of these is
202          * true, we want the setuid capability.
203          */
204         caps |= (1 << CAP_SETUID);
205 #endif
206
207         /*
208          * Since we call initgroups, we need this.
209          */
210         caps |= (1 << CAP_SETGID);
211
212         /*
213          * Without this, we run into problems reading a configuration file
214          * owned by a non-root user and non-world-readable on startup.
215          */
216         caps |= (1 << CAP_DAC_READ_SEARCH);
217
218         /*
219          * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
220          *      clear it would work right given the way linuxthreads work.
221          * XXXDCL But since we need to be able to set the maximum number
222          * of files, the stack size, data size, and core dump size to
223          * support named.conf options, this is now being added to test.
224          */
225         caps |= (1 << CAP_SYS_RESOURCE);
226
227         linux_setcaps(caps);
228 }
229
230 static void
231 linux_minprivs(void) {
232         unsigned int caps;
233
234         /*
235          * Drop all privileges except the ability to bind() to privileged
236          * ports.
237          *
238          * It's important that we drop CAP_SYS_CHROOT.  If we didn't, it
239          * chroot() could be used to escape from the chrooted area.
240          */
241
242         caps = 0;
243         caps |= (1 << CAP_NET_BIND_SERVICE);
244
245         /*
246          * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
247          *      clear it would work right given the way linuxthreads work.
248          * XXXDCL But since we need to be able to set the maximum number
249          * of files, the stack size, data size, and core dump size to
250          * support named.conf options, this is now being added to test.
251          */
252         caps |= (1 << CAP_SYS_RESOURCE);
253
254         linux_setcaps(caps);
255 }
256
257 #ifdef HAVE_SYS_PRCTL_H
258 static void
259 linux_keepcaps(void) {
260         char strbuf[ISC_STRERRORSIZE];
261         /*
262          * Ask the kernel to allow us to keep our capabilities after we
263          * setuid().
264          */
265
266         if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
267                 if (errno != EINVAL) {
268                         isc__strerror(errno, strbuf, sizeof(strbuf));
269                         ns_main_earlyfatal("prctl() failed: %s", strbuf);
270                 }
271         } else {
272                 non_root_caps = ISC_TRUE;
273                 if (getuid() != 0)
274                         non_root = ISC_TRUE;
275         }
276 }
277 #endif
278
279 #endif  /* HAVE_LINUX_CAPABILITY_H */
280
281
282 static void
283 setup_syslog(const char *progname) {
284         int options;
285
286         options = LOG_PID;
287 #ifdef LOG_NDELAY
288         options |= LOG_NDELAY;
289 #endif
290         openlog(isc_file_basename(progname), options, ISC_FACILITY);
291 }
292
293 void
294 ns_os_init(const char *progname) {
295         setup_syslog(progname);
296 #ifdef HAVE_LINUX_CAPABILITY_H
297         linux_initialprivs();
298 #endif
299 #ifdef HAVE_LINUXTHREADS
300         mainpid = getpid();
301 #endif
302 #ifdef SIGXFSZ
303         signal(SIGXFSZ, SIG_IGN);
304 #endif
305 }
306
307 void
308 ns_os_daemonize(void) {
309         pid_t pid;
310         char strbuf[ISC_STRERRORSIZE];
311
312         if (pipe(dfd) == -1) {
313                 isc__strerror(errno, strbuf, sizeof(strbuf));
314                 ns_main_earlyfatal("pipe(): %s", strbuf);
315         }
316
317         pid = fork();
318         if (pid == -1) {
319                 isc__strerror(errno, strbuf, sizeof(strbuf));
320                 ns_main_earlyfatal("fork(): %s", strbuf);
321         }
322         if (pid != 0) {
323                 int n;
324                 /*
325                  * Wait for the child to finish loading for the first time.
326                  * This would be so much simpler if fork() worked once we
327                  * were multi-threaded.
328                  */
329                 (void)close(dfd[1]);
330                 do {
331                         char buf;
332                         n = read(dfd[0], &buf, 1);
333                         if (n == 1)
334                                 _exit(0);
335                 } while (n == -1 && errno == EINTR);
336                 _exit(1);
337         }
338         (void)close(dfd[0]);
339
340         /*
341          * We're the child.
342          */
343
344 #ifdef HAVE_LINUXTHREADS
345         mainpid = getpid();
346 #endif
347
348         if (setsid() == -1) {
349                 isc__strerror(errno, strbuf, sizeof(strbuf));
350                 ns_main_earlyfatal("setsid(): %s", strbuf);
351         }
352
353         /*
354          * Try to set stdin, stdout, and stderr to /dev/null, but press
355          * on even if it fails.
356          *
357          * XXXMLG The close() calls here are unneeded on all but NetBSD, but
358          * are harmless to include everywhere.  dup2() is supposed to close
359          * the FD if it is in use, but unproven-pthreads-0.16 is broken
360          * and will end up closing the wrong FD.  This will be fixed eventually,
361          * and these calls will be removed.
362          */
363         if (devnullfd != -1) {
364                 if (devnullfd != STDIN_FILENO) {
365                         (void)close(STDIN_FILENO);
366                         (void)dup2(devnullfd, STDIN_FILENO);
367                 }
368                 if (devnullfd != STDOUT_FILENO) {
369                         (void)close(STDOUT_FILENO);
370                         (void)dup2(devnullfd, STDOUT_FILENO);
371                 }
372                 if (devnullfd != STDERR_FILENO) {
373                         (void)close(STDERR_FILENO);
374                         (void)dup2(devnullfd, STDERR_FILENO);
375                 }
376         }
377 }
378
379 void
380 ns_os_started(void) {
381         char buf = 0;
382
383         /*
384          * Signal to the parent that we stated successfully.
385          */
386         if (dfd[0] != -1 && dfd[1] != -1) {
387                 write(dfd[1], &buf, 1);
388                 close(dfd[1]);
389                 dfd[0] = dfd[1] = -1;
390         }
391 }
392
393 void
394 ns_os_opendevnull(void) {
395         devnullfd = open("/dev/null", O_RDWR, 0);
396 }
397
398 void
399 ns_os_closedevnull(void) {
400         if (devnullfd != STDIN_FILENO &&
401             devnullfd != STDOUT_FILENO &&
402             devnullfd != STDERR_FILENO) {
403                 close(devnullfd);
404                 devnullfd = -1;
405         }
406 }
407
408 static isc_boolean_t
409 all_digits(const char *s) {
410         if (*s == '\0')
411                 return (ISC_FALSE);
412         while (*s != '\0') {
413                 if (!isdigit((*s)&0xff))
414                         return (ISC_FALSE);
415                 s++;
416         }
417         return (ISC_TRUE);
418 }
419
420 void
421 ns_os_chroot(const char *root) {
422         char strbuf[ISC_STRERRORSIZE];
423 #ifdef HAVE_LIBSCF
424         ns_smf_chroot = 0;
425 #endif
426         if (root != NULL) {
427                 if (chroot(root) < 0) {
428                         isc__strerror(errno, strbuf, sizeof(strbuf));
429                         ns_main_earlyfatal("chroot(): %s", strbuf);
430                 }
431                 if (chdir("/") < 0) {
432                         isc__strerror(errno, strbuf, sizeof(strbuf));
433                         ns_main_earlyfatal("chdir(/): %s", strbuf);
434                 }
435 #ifdef HAVE_LIBSCF
436                 /* Set ns_smf_chroot flag on successful chroot. */
437                 ns_smf_chroot = 1;
438 #endif
439         }
440 }
441
442 void
443 ns_os_inituserinfo(const char *username) {
444         char strbuf[ISC_STRERRORSIZE];
445         if (username == NULL)
446                 return;
447
448         if (all_digits(username))
449                 runas_pw = getpwuid((uid_t)atoi(username));
450         else
451                 runas_pw = getpwnam(username);
452         endpwent();
453
454         if (runas_pw == NULL)
455                 ns_main_earlyfatal("user '%s' unknown", username);
456
457         if (getuid() == 0) {
458                 if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
459                         isc__strerror(errno, strbuf, sizeof(strbuf));
460                         ns_main_earlyfatal("initgroups(): %s", strbuf);
461                 }
462         }
463
464 }
465
466 void
467 ns_os_changeuser(void) {
468         char strbuf[ISC_STRERRORSIZE];
469         if (runas_pw == NULL || done_setuid)
470                 return;
471
472         done_setuid = ISC_TRUE;
473
474 #ifdef HAVE_LINUXTHREADS
475 #ifdef HAVE_LINUX_CAPABILITY_H
476         if (!non_root_caps)
477                 ns_main_earlyfatal("-u with Linux threads not supported: "
478                                    "requires kernel support for "
479                                    "prctl(PR_SET_KEEPCAPS)");
480 #else
481         ns_main_earlyfatal("-u with Linux threads not supported: "
482                            "no capabilities support or capabilities "
483                            "disabled at build time");
484 #endif
485 #endif
486
487         if (setgid(runas_pw->pw_gid) < 0) {
488                 isc__strerror(errno, strbuf, sizeof(strbuf));
489                 ns_main_earlyfatal("setgid(): %s", strbuf);
490         }
491
492         if (setuid(runas_pw->pw_uid) < 0) {
493                 isc__strerror(errno, strbuf, sizeof(strbuf));
494                 ns_main_earlyfatal("setuid(): %s", strbuf);
495         }
496
497 #if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
498         linux_minprivs();
499 #endif
500 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
501         /*
502          * Restore the ability of named to drop core after the setuid()
503          * call has disabled it.
504          */
505         prctl(PR_SET_DUMPABLE,1,0,0,0);
506 #endif
507 }
508
509 void
510 ns_os_minprivs(void) {
511 #ifdef HAVE_SYS_PRCTL_H
512         linux_keepcaps();
513 #endif
514
515 #ifdef HAVE_LINUXTHREADS
516         ns_os_changeuser(); /* Call setuid() before threads are started */
517 #endif
518
519 #if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
520         linux_minprivs();
521 #endif
522 }
523
524 static int
525 safe_open(const char *filename, isc_boolean_t append) {
526         int fd;
527         struct stat sb;
528
529         if (stat(filename, &sb) == -1) {
530                 if (errno != ENOENT)
531                         return (-1);
532         } else if ((sb.st_mode & S_IFREG) == 0) {
533                 errno = EOPNOTSUPP;
534                 return (-1);
535         }
536
537         if (append)
538                 fd = open(filename, O_WRONLY|O_CREAT|O_APPEND,
539                           S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
540         else {
541                 (void)unlink(filename);
542                 fd = open(filename, O_WRONLY|O_CREAT|O_EXCL,
543                           S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
544         }
545         return (fd);
546 }
547
548 static void
549 cleanup_pidfile(void) {
550         if (pidfile != NULL) {
551                 (void)unlink(pidfile);
552                 free(pidfile);
553         }
554         pidfile = NULL;
555 }
556
557 void
558 ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
559         int fd;
560         FILE *lockfile;
561         size_t len;
562         pid_t pid;
563         char strbuf[ISC_STRERRORSIZE];
564         void (*report)(const char *, ...);
565
566         /*
567          * The caller must ensure any required synchronization.
568          */
569
570         report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
571
572         cleanup_pidfile();
573
574         if (filename == NULL)
575                 return;
576
577         len = strlen(filename);
578         pidfile = malloc(len + 1);
579         if (pidfile == NULL) {
580                 isc__strerror(errno, strbuf, sizeof(strbuf));
581                 (*report)("couldn't malloc '%s': %s", filename, strbuf);
582                 return;
583         }
584         /* This is safe. */
585         strcpy(pidfile, filename);
586
587         fd = safe_open(filename, ISC_FALSE);
588         if (fd < 0) {
589                 isc__strerror(errno, strbuf, sizeof(strbuf));
590                 (*report)("couldn't open pid file '%s': %s", filename, strbuf);
591                 free(pidfile);
592                 pidfile = NULL;
593                 return;
594         }
595         lockfile = fdopen(fd, "w");
596         if (lockfile == NULL) {
597                 isc__strerror(errno, strbuf, sizeof(strbuf));
598                 (*report)("could not fdopen() pid file '%s': %s",
599                           filename, strbuf);
600                 (void)close(fd);
601                 cleanup_pidfile();
602                 return;
603         }
604 #ifdef HAVE_LINUXTHREADS
605         pid = mainpid;
606 #else
607         pid = getpid();
608 #endif
609         if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
610                 (*report)("fprintf() to pid file '%s' failed", filename);
611                 (void)fclose(lockfile);
612                 cleanup_pidfile();
613                 return;
614         }
615         if (fflush(lockfile) == EOF) {
616                 (*report)("fflush() to pid file '%s' failed", filename);
617                 (void)fclose(lockfile);
618                 cleanup_pidfile();
619                 return;
620         }
621         (void)fclose(lockfile);
622 }
623
624 void
625 ns_os_shutdown(void) {
626         closelog();
627         cleanup_pidfile();
628 }
629
630 isc_result_t
631 ns_os_gethostname(char *buf, size_t len) {
632         int n;
633
634         n = gethostname(buf, len);
635         return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
636 }
637
638 static char *
639 next_token(char **stringp, const char *delim) {
640         char *res;
641
642         do {
643                 res = strsep(stringp, delim);
644                 if (res == NULL)
645                         break;
646         } while (*res == '\0');
647         return (res);
648 }
649
650 void
651 ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
652         char *input, *ptr;
653         unsigned int n;
654         pid_t pid;
655
656         input = command;
657
658         /* Skip the command name. */
659         ptr = next_token(&input, " \t");
660         if (ptr == NULL)
661                 return;
662
663         ptr = next_token(&input, " \t");
664         if (ptr == NULL)
665                 return;
666         
667         if (strcmp(ptr, "-p") != 0)
668                 return;
669
670 #ifdef HAVE_LINUXTHREADS
671         pid = mainpid;
672 #else
673         pid = getpid();
674 #endif
675
676         n = snprintf((char *)isc_buffer_used(text),
677                      isc_buffer_availablelength(text),
678                      "pid: %ld", (long)pid);
679         /* Only send a message if it is complete. */
680         if (n < isc_buffer_availablelength(text))
681                 isc_buffer_add(text, n);
682 }
683
684 void
685 ns_os_tzset(void) {
686 #ifdef HAVE_TZSET
687         tzset();
688 #endif
689 }