Merge from vendor branch NTPD:
[dragonfly.git] / contrib / bind-9.2.4rc7 / bin / named / unix / os.c
1 /*
2  * Copyright (C) 2004  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.9 2004/04/15 05:36:13 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/file.h>
41 #include <isc/print.h>
42 #include <isc/result.h>
43 #include <isc/strerror.h>
44 #include <isc/string.h>
45
46 #include <named/main.h>
47 #include <named/os.h>
48
49 static char *pidfile = NULL;
50 static int devnullfd = -1;
51
52 /*
53  * If there's no <linux/capability.h>, we don't care about <sys/prctl.h>
54  */
55 #ifndef HAVE_LINUX_CAPABILITY_H
56 #undef HAVE_SYS_PRCTL_H
57 #endif
58
59 /*
60  * Linux defines:
61  *      (T) HAVE_LINUXTHREADS
62  *      (C) HAVE_LINUX_CAPABILITY_H
63  *      (P) HAVE_SYS_PRCTL_H
64  * The possible cases are:
65  *      none:   setuid() normally
66  *      T:      no setuid()
67  *      C:      setuid() normally, drop caps (keep CAP_SETUID)
68  *      T+C:    no setuid(), drop caps (don't keep CAP_SETUID)
69  *      T+C+P:  setuid() early, drop caps (keep CAP_SETUID)
70  *      C+P:    setuid() normally, drop caps (keep CAP_SETUID)
71  *      P:      not possible
72  *      T+P:    not possible
73  *
74  * if (C)
75  *      caps = BIND_SERVICE + CHROOT + SETGID
76  *      if ((T && C && P) || !T)
77  *              caps += SETUID
78  *      endif
79  *      capset(caps)
80  * endif
81  * if (T && C && P && -u)
82  *      setuid()
83  * else if (T && -u)
84  *      fail
85  * --> start threads
86  * if (!T && -u)
87  *      setuid()
88  * if (C && (P || !-u))
89  *      caps = BIND_SERVICE
90  *      capset(caps)
91  * endif
92  *
93  * It will be nice when Linux threads work properly with setuid().
94  */
95
96 #ifdef HAVE_LINUXTHREADS
97 static pid_t mainpid = 0;
98 #endif
99
100 static struct passwd *runas_pw = NULL;
101 static isc_boolean_t done_setuid = ISC_FALSE;
102
103 #ifdef HAVE_LINUX_CAPABILITY_H
104
105 static isc_boolean_t non_root = ISC_FALSE;
106 static isc_boolean_t non_root_caps = ISC_FALSE;
107
108 /*
109  * We define _LINUX_FS_H to prevent it from being included.  We don't need
110  * anything from it, and the files it includes cause warnings with 2.2
111  * kernels, and compilation failures (due to conflicts between <linux/string.h>
112  * and <string.h>) on 2.3 kernels.
113  */
114 #define _LINUX_FS_H
115
116 #include <sys/syscall.h>        /* Required for syscall(). */
117 #include <linux/capability.h>   /* Required for _LINUX_CAPABILITY_VERSION. */
118
119 #ifdef HAVE_SYS_PRCTL_H
120 #include <sys/prctl.h>          /* Required for prctl(). */
121
122 /*
123  * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it
124  * here.  This allows setuid() to work on systems running a new enough
125  * kernel but with /usr/include/linux pointing to "standard" kernel
126  * headers.
127  */
128 #ifndef PR_SET_KEEPCAPS
129 #define PR_SET_KEEPCAPS 8
130 #endif
131
132 #endif /* HAVE_SYS_PRCTL_H */
133
134 #ifndef SYS_capset
135 #ifndef __NR_capset
136 #include <asm/unistd.h> /* Slackware 4.0 needs this. */
137 #endif
138 #define SYS_capset __NR_capset
139 #endif
140
141 static void
142 linux_setcaps(unsigned int caps) {
143         struct __user_cap_header_struct caphead;
144         struct __user_cap_data_struct cap;
145         char strbuf[ISC_STRERRORSIZE];
146
147         if ((getuid() != 0 && !non_root_caps) || non_root)
148                 return;
149
150         memset(&caphead, 0, sizeof caphead);
151         caphead.version = _LINUX_CAPABILITY_VERSION;
152         caphead.pid = 0;
153         memset(&cap, 0, sizeof cap);
154         cap.effective = caps;
155         cap.permitted = caps;
156         cap.inheritable = caps;
157         if (syscall(SYS_capset, &caphead, &cap) < 0) {
158                 isc__strerror(errno, strbuf, sizeof(strbuf));
159                 ns_main_earlyfatal("capset failed: %s", strbuf);
160         }
161 }
162
163 static void
164 linux_initialprivs(void) {
165         unsigned int caps;
166
167         /*
168          * We don't need most privileges, so we drop them right away.
169          * Later on linux_minprivs() will be called, which will drop our
170          * capabilities to the minimum needed to run the server.
171          */
172
173         caps = 0;
174
175         /*
176          * We need to be able to bind() to privileged ports, notably port 53!
177          */
178         caps |= (1 << CAP_NET_BIND_SERVICE);
179
180         /*
181          * We need chroot() initially too.
182          */
183         caps |= (1 << CAP_SYS_CHROOT);
184
185 #if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
186         /*
187          * We can setuid() only if either the kernel supports keeping
188          * capabilities after setuid() (which we don't know until we've
189          * tried) or we're not using threads.  If either of these is
190          * true, we want the setuid capability.
191          */
192         caps |= (1 << CAP_SETUID);
193 #endif
194
195         /*
196          * Since we call initgroups, we need this.
197          */
198         caps |= (1 << CAP_SETGID);
199
200         /*
201          * Without this, we run into problems reading a configuration file
202          * owned by a non-root user and non-world-readable on startup.
203          */
204         caps |= (1 << CAP_DAC_READ_SEARCH);
205
206         /*
207          * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
208          *      clear it would work right given the way linuxthreads work.
209          * XXXDCL But since we need to be able to set the maximum number
210          * of files, the stack size, data size, and core dump size to
211          * support named.conf options, this is now being added to test.
212          */
213         caps |= (1 << CAP_SYS_RESOURCE);
214
215         linux_setcaps(caps);
216 }
217
218 static void
219 linux_minprivs(void) {
220         unsigned int caps;
221
222         /*
223          * Drop all privileges except the ability to bind() to privileged
224          * ports.
225          *
226          * It's important that we drop CAP_SYS_CHROOT.  If we didn't, it
227          * chroot() could be used to escape from the chrooted area.
228          */
229
230         caps = 0;
231         caps |= (1 << CAP_NET_BIND_SERVICE);
232
233         /*
234          * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
235          *      clear it would work right given the way linuxthreads work.
236          * XXXDCL But since we need to be able to set the maximum number
237          * of files, the stack size, data size, and core dump size to
238          * support named.conf options, this is now being added to test.
239          */
240         caps |= (1 << CAP_SYS_RESOURCE);
241
242         linux_setcaps(caps);
243 }
244
245 #ifdef HAVE_SYS_PRCTL_H
246 static void
247 linux_keepcaps(void) {
248         char strbuf[ISC_STRERRORSIZE];
249         /*
250          * Ask the kernel to allow us to keep our capabilities after we
251          * setuid().
252          */
253
254         if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
255                 if (errno != EINVAL) {
256                         isc__strerror(errno, strbuf, sizeof(strbuf));
257                         ns_main_earlyfatal("prctl() failed: %s", strbuf);
258                 }
259         } else {
260                 non_root_caps = ISC_TRUE;
261                 if (getuid() != 0)
262                         non_root = ISC_TRUE;
263         }
264 }
265 #endif
266
267 #endif  /* HAVE_LINUX_CAPABILITY_H */
268
269
270 static void
271 setup_syslog(const char *progname) {
272         int options;
273
274         options = LOG_PID;
275 #ifdef LOG_NDELAY
276         options |= LOG_NDELAY;
277 #endif
278
279         openlog(isc_file_basename(progname), options, LOG_DAEMON);
280 }
281
282 void
283 ns_os_init(const char *progname) {
284         setup_syslog(progname);
285 #ifdef HAVE_LINUX_CAPABILITY_H
286         linux_initialprivs();
287 #endif
288 #ifdef HAVE_LINUXTHREADS
289         mainpid = getpid();
290 #endif
291 #ifdef SIGXFSZ
292         signal(SIGXFSZ, SIG_IGN);
293 #endif
294 }
295
296 void
297 ns_os_daemonize(void) {
298         pid_t pid;
299         char strbuf[ISC_STRERRORSIZE];
300
301         pid = fork();
302         if (pid == -1) {
303                 isc__strerror(errno, strbuf, sizeof(strbuf));
304                 ns_main_earlyfatal("fork(): %s", strbuf);
305         }
306         if (pid != 0)
307                 _exit(0);
308
309         /*
310          * We're the child.
311          */
312
313 #ifdef HAVE_LINUXTHREADS
314         mainpid = getpid();
315 #endif
316
317         if (setsid() == -1) {
318                 isc__strerror(errno, strbuf, sizeof(strbuf));
319                 ns_main_earlyfatal("setsid(): %s", strbuf);
320         }
321
322         /*
323          * Try to set stdin, stdout, and stderr to /dev/null, but press
324          * on even if it fails.
325          *
326          * XXXMLG The close() calls here are unneeded on all but NetBSD, but
327          * are harmless to include everywhere.  dup2() is supposed to close
328          * the FD if it is in use, but unproven-pthreads-0.16 is broken
329          * and will end up closing the wrong FD.  This will be fixed eventually,
330          * and these calls will be removed.
331          */
332         if (devnullfd != -1) {
333                 if (devnullfd != STDIN_FILENO) {
334                         (void)close(STDIN_FILENO);
335                         (void)dup2(devnullfd, STDIN_FILENO);
336                 }
337                 if (devnullfd != STDOUT_FILENO) {
338                         (void)close(STDOUT_FILENO);
339                         (void)dup2(devnullfd, STDOUT_FILENO);
340                 }
341                 if (devnullfd != STDERR_FILENO) {
342                         (void)close(STDERR_FILENO);
343                         (void)dup2(devnullfd, STDERR_FILENO);
344                 }
345         }
346 }
347
348 void
349 ns_os_opendevnull(void) {
350         devnullfd = open("/dev/null", O_RDWR, 0);
351 }
352
353 void
354 ns_os_closedevnull(void) {
355         if (devnullfd != STDIN_FILENO &&
356             devnullfd != STDOUT_FILENO &&
357             devnullfd != STDERR_FILENO) {
358                 close(devnullfd);
359                 devnullfd = -1;
360         }
361 }
362
363 static isc_boolean_t
364 all_digits(const char *s) {
365         if (*s == '\0')
366                 return (ISC_FALSE);
367         while (*s != '\0') {
368                 if (!isdigit((*s)&0xff))
369                         return (ISC_FALSE);
370                 s++;
371         }
372         return (ISC_TRUE);
373 }
374
375 void
376 ns_os_chroot(const char *root) {
377         char strbuf[ISC_STRERRORSIZE];
378         if (root != NULL) {
379                 if (chroot(root) < 0) {
380                         isc__strerror(errno, strbuf, sizeof(strbuf));
381                         ns_main_earlyfatal("chroot(): %s", strbuf);
382                 }
383                 if (chdir("/") < 0) {
384                         isc__strerror(errno, strbuf, sizeof(strbuf));
385                         ns_main_earlyfatal("chdir(/): %s", strbuf);
386                 }
387         }
388 }
389
390 void
391 ns_os_inituserinfo(const char *username) {
392         char strbuf[ISC_STRERRORSIZE];
393         if (username == NULL)
394                 return;
395
396         if (all_digits(username))
397                 runas_pw = getpwuid((uid_t)atoi(username));
398         else
399                 runas_pw = getpwnam(username);
400         endpwent();
401
402         if (runas_pw == NULL)
403                 ns_main_earlyfatal("user '%s' unknown", username);
404
405         if (getuid() == 0) {
406                 if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
407                         isc__strerror(errno, strbuf, sizeof(strbuf));
408                         ns_main_earlyfatal("initgroups(): %s", strbuf);
409                 }
410         }
411
412 }
413
414 void
415 ns_os_changeuser(void) {
416         char strbuf[ISC_STRERRORSIZE];
417         if (runas_pw == NULL || done_setuid)
418                 return;
419
420         done_setuid = ISC_TRUE;
421
422 #ifdef HAVE_LINUXTHREADS
423 #ifdef HAVE_LINUX_CAPABILITY_H
424         if (!non_root_caps)
425 #endif
426                 ns_main_earlyfatal(
427                    "-u not supported on Linux kernels older than "
428                    "2.3.99-pre3 or 2.2.18 when using threads");
429 #endif
430
431         if (setgid(runas_pw->pw_gid) < 0) {
432                 isc__strerror(errno, strbuf, sizeof(strbuf));
433                 ns_main_earlyfatal("setgid(): %s", strbuf);
434         }
435
436         if (setuid(runas_pw->pw_uid) < 0) {
437                 isc__strerror(errno, strbuf, sizeof(strbuf));
438                 ns_main_earlyfatal("setuid(): %s", strbuf);
439         }
440
441 #if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
442         linux_minprivs();
443 #endif
444 }
445
446 void
447 ns_os_minprivs(void) {
448 #ifdef HAVE_SYS_PRCTL_H
449         linux_keepcaps();
450 #endif
451
452 #ifdef HAVE_LINUXTHREADS
453         ns_os_changeuser(); /* Call setuid() before threads are started */
454 #endif
455
456 #if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
457         linux_minprivs();
458 #endif
459 }
460
461 static int
462 safe_open(const char *filename, isc_boolean_t append) {
463         int fd;
464         struct stat sb;
465
466         if (stat(filename, &sb) == -1) {
467                 if (errno != ENOENT)
468                         return (-1);
469         } else if ((sb.st_mode & S_IFREG) == 0) {
470                 errno = EOPNOTSUPP;
471                 return (-1);
472         }
473
474         if (append)
475                 fd = open(filename, O_WRONLY|O_CREAT|O_APPEND,
476                           S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
477         else {
478                 (void)unlink(filename);
479                 fd = open(filename, O_WRONLY|O_CREAT|O_EXCL,
480                           S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
481         }
482         return (fd);
483 }
484
485 static void
486 cleanup_pidfile(void) {
487         if (pidfile != NULL) {
488                 (void)unlink(pidfile);
489                 free(pidfile);
490         }
491         pidfile = NULL;
492 }
493
494 void
495 ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
496         int fd;
497         FILE *lockfile;
498         size_t len;
499         pid_t pid;
500         char strbuf[ISC_STRERRORSIZE];
501         void (*report)(const char *, ...);
502
503         /*
504          * The caller must ensure any required synchronization.
505          */
506
507         report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
508
509         cleanup_pidfile();
510
511         len = strlen(filename);
512         pidfile = malloc(len + 1);
513         if (pidfile == NULL) {
514                 isc__strerror(errno, strbuf, sizeof(strbuf));
515                 (*report)("couldn't malloc '%s': %s", filename, strbuf);
516                 return;
517         }
518         /* This is safe. */
519         strcpy(pidfile, filename);
520
521         fd = safe_open(filename, ISC_FALSE);
522         if (fd < 0) {
523                 isc__strerror(errno, strbuf, sizeof(strbuf));
524                 (*report)("couldn't open pid file '%s': %s", filename, strbuf);
525                 free(pidfile);
526                 pidfile = NULL;
527                 return;
528         }
529         lockfile = fdopen(fd, "w");
530         if (lockfile == NULL) {
531                 isc__strerror(errno, strbuf, sizeof(strbuf));
532                 (*report)("could not fdopen() pid file '%s': %s",
533                           filename, strbuf);
534                 (void)close(fd);
535                 cleanup_pidfile();
536                 return;
537         }
538 #ifdef HAVE_LINUXTHREADS
539         pid = mainpid;
540 #else
541         pid = getpid();
542 #endif
543         if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
544                 (*report)("fprintf() to pid file '%s' failed", filename);
545                 (void)fclose(lockfile);
546                 cleanup_pidfile();
547                 return;
548         }
549         if (fflush(lockfile) == EOF) {
550                 (*report)("fflush() to pid file '%s' failed", filename);
551                 (void)fclose(lockfile);
552                 cleanup_pidfile();
553                 return;
554         }
555         (void)fclose(lockfile);
556 }
557
558 void
559 ns_os_shutdown(void) {
560         closelog();
561         cleanup_pidfile();
562 }
563
564 void
565 ns_os_tzset(void) {
566 #ifdef HAVE_TZSET
567         tzset();
568 #endif
569 }