Commit 134ba0e0 authored by Bob Halley's avatar Bob Halley

Linux PR_SET_KEEPCAPS support

parent 8469ee37
41. [feature] Use PR_SET_KEEPCAPS on Linux 2.3.99-pre3 and later
to allow 'named' to run as a non-root user while
retaining the ability to bind() to privileged
ports.
40. [feature] Introduced new logging category "dnssec" and
logging module "dns/validator".
......
......@@ -220,13 +220,19 @@ Bug Reports and Mailing Lists
enhance security on most systems. The way chroot() is defined
allows a process with root privileges to escape the chroot jail.
The "-u" option is not currently useful on Linux. Linux threads
are actually processes sharing a common address space. An unfortunate
side effect of this is that some system calls, e.g. setuid() that
in a typical pthreads environment would affect all threads only affect
the calling thread/process on Linux. The good news is that BIND 9
uses the Linux kernel's capability mechanism to drop all root
powers except the ability to bind() to a privileged port.
The "-u" option is not currently useful on Linux kernels older
than 2.3.99-pre3. Linux threads are actually processes sharing a
common address space. An unfortunate side effect of this is that
some system calls, e.g. setuid() that in a typical pthreads
environment would affect all threads only affect the calling
thread/process on Linux. The good news is that BIND 9 uses the
Linux kernel's capability mechanism to drop all root powers except
the ability to bind() to a privileged port. 2.3.99-pre3 and later
kernels allow a process to say that its capabilities should be
retained after setuid(). If BIND 9 is compiled with 2.3.99-pre3 or
later kernel .h files, the "-u" option will cause the server to
run with the specified user id, but it will retain the capability
to bind() to privileged ports.
On systems with more than one CPU, the "-n" option should be used
to indicate how many CPUs there are.
......
......@@ -289,6 +289,16 @@ setup() {
ns_os_chroot(ns_g_chrootdir);
/*
* For operating systems which have a capability mechanism, now
* is the time to switch to minimal privs and change our user id.
* On traditional UNIX systems, this call will be a no-op, and we
* will change the user ID after reading the config file the first
* time. (We need to read the config file to know which possibly
* privileged ports to bind() to.)
*/
ns_os_minprivs(ns_g_username);
result = ns_log_init();
if (result != ISC_R_SUCCESS)
ns_main_earlyfatal("ns_log_init() failed: %s",
......
......@@ -32,6 +32,9 @@ ns_os_chroot(const char *root);
void
ns_os_changeuser(const char *username);
void
ns_os_minprivs(const char *username);
void
ns_os_writepidfile(const char *filename);
......
......@@ -40,6 +40,7 @@
static char *pidfile = NULL;
#ifdef HAVE_LINUXTHREADS
static pid_t mainpid = 0;
static isc_boolean_t non_root_caps = ISC_FALSE;
#endif
#ifdef HAVE_LINUX_CAPABILITY_H
......@@ -55,6 +56,10 @@ static pid_t mainpid = 0;
#include <sys/syscall.h>
#include <linux/capability.h>
#ifdef HAVE_LINUX_PRCTL_H
#include <sys/prctl.h>
#endif
#ifndef SYS_capset
#define SYS_capset __NR_capset
#endif
......@@ -64,7 +69,7 @@ linux_setcaps(unsigned int caps) {
struct __user_cap_header_struct caphead;
struct __user_cap_data_struct cap;
if (getuid() != 0)
if (getuid() != 0 && !non_root_caps)
return;
memset(&caphead, 0, sizeof caphead);
......@@ -83,17 +88,41 @@ linux_initialprivs(void) {
unsigned int caps;
/*
* Drop all privileges except the abilities to bind() to privileged
* ports and chroot().
* We don't need most privileges, so we drop them right away.
* Later on linux_minprivs() will be called, which will drop our
* capabilities to the minimum needed to run the server.
*/
caps = 0;
/*
* We need to be able to bind() to privileged ports, notably port 53!
*/
caps |= (1 << CAP_NET_BIND_SERVICE);
/*
* We need chroot() initially too.
*/
caps |= (1 << CAP_SYS_CHROOT);
#if defined(HAVE_LINUX_PRCTL_H) && defined(PR_SET_KEEPCAPS)
/*
* If the kernel supports keeping capabilities after setuid(), we
* also want the setuid and setgid capabilities.
*
* There's no point turning these on if we don't have PR_SET_KEEPCAPS,
* because changing user ids only works right with linuxthreads if
* we can do it early (before creating threads).
*/
caps |= (1 << CAP_SETGID);
caps |= (1 << CAP_SETUID);
#endif
/*
* XXX We might want to add CAP_SYS_RESOURCE, though it's not
* clear it would work right given the way linuxthreads work.
*/
linux_setcaps(caps);
}
......@@ -102,8 +131,11 @@ linux_minprivs(void) {
unsigned int caps;
/*
* Drop all privileges except the abilities to bind() to privileged
* Drop all privileges except the ability to bind() to privileged
* ports.
*
* It's important that we drop CAP_SYS_CHROOT. If we didn't, it
* chroot() could be used to escape from the chrooted area.
*/
caps = 0;
......@@ -112,6 +144,23 @@ linux_minprivs(void) {
linux_setcaps(caps);
}
#if defined(HAVE_LINUX_PRCTL_H) && defined(PR_SET_KEEPCAPS)
static void
linux_keepcaps(void) {
/*
* Ask the kernel to allow us to keep our capabilities after we
* setuid().
*/
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
if (errno != EINVAL)
ns_main_earlyfatal("prctl() failed: %s",
strerror(errno));
} else
non_root_caps = ISC_TRUE;
}
#endif
#endif /* HAVE_LINUX_CAPABILITY_H */
......@@ -196,13 +245,6 @@ ns_os_chroot(const char *root) {
if (chdir("/") < 0)
ns_main_earlyfatal("chdir(/): %s", strerror(errno));
}
#ifdef HAVE_LINUX_CAPABILITY_H
/*
* We must drop the chroot() capability, otherwise it could be used
* to escape.
*/
linux_minprivs();
#endif
}
void
......@@ -212,6 +254,10 @@ ns_os_changeuser(const char *username) {
if (username == NULL || getuid() != 0)
return;
if (!non_root_caps)
ns_main_earlyfatal(
"-u not supported on Linux kernels older than 2.3.99-pre3");
if (all_digits(username))
pw = getpwuid((uid_t)atoi(username));
else
......@@ -227,6 +273,21 @@ ns_os_changeuser(const char *username) {
ns_main_earlyfatal("setuid(): %s", strerror(errno));
}
void
ns_os_minprivs(const char *username) {
#ifdef HAVE_LINUX_CAPABILITY_H
#if defined(HAVE_LINUX_PRCTL_H) && defined(PR_SET_KEEPCAPS)
linux_keepcaps();
ns_os_changeuser(username);
#else
(void)username;
#endif
linux_minprivs();
#else
(void)username;
#endif /* HAVE_LINUX_CAPABILITY_H */
}
static int
safe_open(const char *filename) {
struct stat sb;
......
/* config.h.in. Generated automatically from configure.in by autoheader. */
/*
* Copyright (C) 1999 Internet Software Consortium.
* Copyright (C) 1999, 2000 Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
......@@ -66,6 +66,9 @@
/* Define if you have the <linux/capability.h> header file. */
#undef HAVE_LINUX_CAPABILITY_H
/* Define if you have the <linux/prctl.h> header file. */
#undef HAVE_LINUX_PRCTL_H
/* Define if you have the <netinet6/in6.h> header file. */
#undef HAVE_NETINET6_IN6_H
......@@ -78,9 +81,6 @@
/* Define if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define if you have the c_r library (-lc_r). */
#undef HAVE_LIBC_R
/* Define if you have the nsl library (-lnsl). */
#undef HAVE_LIBNSL
......
......@@ -3784,6 +3784,45 @@ else
fi
done
for ac_hdr in linux/prctl.h
do
ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
echo "configure:3786: checking for $ac_hdr" >&5
if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
cat > conftest.$ac_ext <<EOF
#line 3791 "configure"
#include "confdefs.h"
#include <$ac_hdr>
EOF
ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
{ (eval echo configure:3796: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
if test -z "$ac_err"; then
rm -rf conftest*
eval "ac_cv_header_$ac_safe=yes"
else
echo "$ac_err" >&5
echo "configure: failed program was:" >&5
cat conftest.$ac_ext >&5
rm -rf conftest*
eval "ac_cv_header_$ac_safe=no"
fi
rm -f conftest*
fi
if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
echo "$ac_t""yes" 1>&6
ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
cat >> confdefs.h <<EOF
#define $ac_tr_hdr 1
EOF
else
echo "$ac_t""no" 1>&6
fi
done
#
......
......@@ -13,7 +13,7 @@ dnl PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
dnl ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
dnl SOFTWARE.
AC_REVISION($Revision: 1.113 $)
AC_REVISION($Revision: 1.114 $)
AC_PREREQ(2.13)
......@@ -709,7 +709,7 @@ AC_SUBST(ISC_PLATFORM_LONGLONGEQUALLONG)
#
AC_CHECK_FUNC(chroot, AC_DEFINE(HAVE_CHROOT))
AC_CHECK_HEADERS(linux/capability.h)
AC_CHECK_HEADERS(linux/prctl.h)
#
# Substitutions
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment