Use libcap library instead raw system calls

Adam Tkac atkac at redhat.com
Wed Feb 20 15:00:20 UTC 2008


Hi all,

I have problems with capset system call on latest linux kernels
(version 2.6.25 rc0 and newer). This syscall have been changed (not
sure what exactly but was changed) and named simply stops work (you
can see http://thread.gmane.org/gmane.linux.kernel/635219). I've
created patch which switch to libcap library (POSIX.1e capabilities 
implementation on Linux) instead use raw syscall. It can be also
benefit on other platforms whose are posix.1e compatible. I saw some
changes in CVS around this (change 2325.) but this doesn't solve
problem when you compile named on 2.6.25-rc1 kernel. Would it be
possible to include attached patch to main source? Or do you have any
reason why not include it?

Regards, Adam

PS: in final version return values from cap_set_flag should be checked

-- 
Adam Tkac, Red Hat, Inc.
-------------- next part --------------
diff -up bind-9.5.0b2/configure.in.libcap bind-9.5.0b2/configure.in
--- bind-9.5.0b2/configure.in.libcap	2008-02-20 14:23:42.000000000 +0100
+++ bind-9.5.0b2/configure.in	2008-02-20 14:47:11.000000000 +0100
@@ -1875,7 +1875,8 @@ AC_ARG_ENABLE(linux-caps,
 	[  --disable-linux-caps	disable linux capabilities])
 case "$enable_linux_caps" in
 	yes|'')
-		AC_CHECK_HEADERS(linux/capability.h)
+		AC_CHECK_HEADERS(sys/capability.h)
+		AC_CHECK_LIB(cap, cap_set_proc, LIBS="-lcap $LIBS")
 		;;
 	no)
 		;;
diff -up bind-9.5.0b2/bin/named/unix/os.c.libcap bind-9.5.0b2/bin/named/unix/os.c
--- bind-9.5.0b2/bin/named/unix/os.c.libcap	2008-02-20 14:24:31.000000000 +0100
+++ bind-9.5.0b2/bin/named/unix/os.c	2008-02-20 14:45:25.000000000 +0100
@@ -60,16 +60,16 @@ static int devnullfd = -1;
 #endif
 
 /*
- * If there's no <linux/capability.h>, we don't care about <sys/prctl.h>
+ * If there's no <sys/capability.h>, we don't care about <sys/prctl.h>
  */
-#ifndef HAVE_LINUX_CAPABILITY_H
+#ifndef HAVE_SYS_CAPABILITY_H
 #undef HAVE_SYS_PRCTL_H
 #endif
 
 /*
  * Linux defines:
  *	(T) HAVE_LINUXTHREADS
- *	(C) HAVE_LINUX_CAPABILITY_H
+ *	(C) HAVE_SYS_CAPABILITY_H
  *	(P) HAVE_SYS_PRCTL_H
  * The possible cases are:
  *	none:	setuid() normally
@@ -111,7 +111,7 @@ static struct passwd *runas_pw = NULL;
 static isc_boolean_t done_setuid = ISC_FALSE;
 static int dfd[2] = { -1, -1 };
 
-#ifdef HAVE_LINUX_CAPABILITY_H
+#ifdef HAVE_SYS_CAPABILITY_H
 
 static isc_boolean_t non_root = ISC_FALSE;
 static isc_boolean_t non_root_caps = ISC_FALSE;
@@ -125,7 +125,7 @@ static isc_boolean_t non_root_caps = ISC
 #define _LINUX_FS_H
 
 #include <sys/syscall.h>	/* Required for syscall(). */
-#include <linux/capability.h>	/* Required for _LINUX_CAPABILITY_VERSION. */
+#include <sys/capability.h>	/* Required for _LINUX_CAPABILITY_VERSION. */
 
 #ifdef HAVE_SYS_PRCTL_H
 #include <sys/prctl.h>		/* Required for prctl(). */
@@ -142,32 +142,16 @@ static isc_boolean_t non_root_caps = ISC
 
 #endif /* HAVE_SYS_PRCTL_H */
 
-#ifndef SYS_capset
-#ifndef __NR_capset
-#include <asm/unistd.h> /* Slackware 4.0 needs this. */
-#endif
-#define SYS_capset __NR_capset
-#endif
-
 static void
-linux_setcaps(unsigned int caps) {
-	struct __user_cap_header_struct caphead;
-	struct __user_cap_data_struct cap;
+linux_setcaps(cap_t caps) {
 	char strbuf[ISC_STRERRORSIZE];
 
 	if ((getuid() != 0 && !non_root_caps) || non_root)
 		return;
 
-	memset(&caphead, 0, sizeof(caphead));
-	caphead.version = _LINUX_CAPABILITY_VERSION;
-	caphead.pid = 0;
-	memset(&cap, 0, sizeof(cap));
-	cap.effective = caps;
-	cap.permitted = caps;
-	cap.inheritable = 0;
-	if (syscall(SYS_capset, &caphead, &cap) < 0) {
+	if (cap_set_proc(caps) < 0) {
 		isc__strerror(errno, strbuf, sizeof(strbuf));
-		ns_main_earlyfatal("capset failed: %s:"
+		ns_main_earlyfatal("cap_set_proc failed: %s:"
 				   " please ensure that the capset kernel"
 				   " module is loaded.  see insmod(8)",
 				   strbuf);
@@ -176,7 +160,9 @@ linux_setcaps(unsigned int caps) {
 
 static void
 linux_initialprivs(void) {
-	unsigned int caps;
+	cap_t caps;
+	cap_value_t capval;
+	char strbuf[ISC_STRERRORSIZE];
 
 	/*%
 	 * We don't need most privileges, so we drop them right away.
@@ -184,17 +170,26 @@ linux_initialprivs(void) {
 	 * capabilities to the minimum needed to run the server.
 	 */
 
-	caps = 0;
+	if ( (caps = cap_init()) == NULL) {
+		isc__strerror(errno, strbuf, sizeof (strbuf));
+		ns_main_earlyfatal("cap_init failed: %s", strbuf);
+	}
 
 	/*
 	 * We need to be able to bind() to privileged ports, notably port 53!
 	 */
-	caps |= (1 << CAP_NET_BIND_SERVICE);
+
+	capval = CAP_NET_BIND_SERVICE;
+	cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET);
+	cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET);
 
 	/*
 	 * We need chroot() initially too.
 	 */
-	caps |= (1 << CAP_SYS_CHROOT);
+
+	capval = CAP_SYS_CHROOT;
+	cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET);
+	cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET);
 
 #if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
 	/*
@@ -203,19 +198,25 @@ linux_initialprivs(void) {
 	 * tried) or we're not using threads.  If either of these is
 	 * true, we want the setuid capability.
 	 */
-	caps |= (1 << CAP_SETUID);
+	capval = CAP_SETUID;
+	cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET);
+	cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET);
 #endif
 
 	/*
 	 * Since we call initgroups, we need this.
 	 */
-	caps |= (1 << CAP_SETGID);
+	capval = CAP_SETGID;
+	cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET);
+	cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET);
 
 	/*
 	 * Without this, we run into problems reading a configuration file
 	 * owned by a non-root user and non-world-readable on startup.
 	 */
-	caps |= (1 << CAP_DAC_READ_SEARCH);
+	capval = CAP_DAC_READ_SEARCH;
+	cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET);
+	cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET);
 
 	/*
 	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
@@ -224,14 +225,18 @@ linux_initialprivs(void) {
 	 * of files, the stack size, data size, and core dump size to
 	 * support named.conf options, this is now being added to test.
 	 */
-	caps |= (1 << CAP_SYS_RESOURCE);
+	capval = CAP_SYS_RESOURCE;
+	cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET);
+	cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET);
 
 	linux_setcaps(caps);
 }
 
 static void
 linux_minprivs(void) {
-	unsigned int caps;
+	cap_t caps;
+	cap_value_t capval;
+	char strbuf[ISC_STRERRORSIZE];
 
 	/*%
 	 * Drop all privileges except the ability to bind() to privileged
@@ -241,8 +246,14 @@ linux_minprivs(void) {
 	 * chroot() could be used to escape from the chrooted area.
 	 */
 
-	caps = 0;
-	caps |= (1 << CAP_NET_BIND_SERVICE);
+	if ( (caps = cap_init()) == NULL) {
+		isc__strerror(errno, strbuf, sizeof (strbuf));
+		ns_main_earlyfatal("cap_init failed: %s", strbuf);
+	}
+
+	capval = CAP_NET_BIND_SERVICE;
+	cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET);
+	cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET);
 
 	/*
 	 * XXX  We might want to add CAP_SYS_RESOURCE, though it's not
@@ -251,7 +262,9 @@ linux_minprivs(void) {
 	 * of files, the stack size, data size, and core dump size to
 	 * support named.conf options, this is now being added to test.
 	 */
-	caps |= (1 << CAP_SYS_RESOURCE);
+	capval = CAP_SYS_RESOURCE;
+	cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET);
+	cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET);
 
 	linux_setcaps(caps);
 }
@@ -278,7 +291,7 @@ linux_keepcaps(void) {
 }
 #endif
 
-#endif	/* HAVE_LINUX_CAPABILITY_H */
+#endif	/* HAVE_SYS_CAPABILITY_H */
 
 
 static void
@@ -295,7 +308,7 @@ setup_syslog(const char *progname) {
 void
 ns_os_init(const char *progname) {
 	setup_syslog(progname);
-#ifdef HAVE_LINUX_CAPABILITY_H
+#ifdef HAVE_SYS_CAPABILITY_H
 	linux_initialprivs();
 #endif
 #ifdef HAVE_LINUXTHREADS
@@ -474,7 +487,7 @@ ns_os_changeuser(void) {
 	done_setuid = ISC_TRUE;
 
 #ifdef HAVE_LINUXTHREADS
-#ifdef HAVE_LINUX_CAPABILITY_H
+#ifdef HAVE_SYS_CAPABILITY_H
 	if (!non_root_caps)
 		ns_main_earlyfatal("-u with Linux threads not supported: "
 				   "requires kernel support for "
@@ -507,7 +520,7 @@ ns_os_changeuser(void) {
 				     strbuf);
 	}
 #endif
-#if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
+#if defined(HAVE_SYS_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
 	linux_minprivs();
 #endif
 }
@@ -522,7 +535,7 @@ ns_os_minprivs(void) {
 	ns_os_changeuser(); /* Call setuid() before threads are started */
 #endif
 
-#if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
+#if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
 	linux_minprivs();
 #endif
 }
diff -up bind-9.5.0b2/config.h.in.libcap bind-9.5.0b2/config.h.in
--- bind-9.5.0b2/config.h.in.libcap	2008-02-20 14:23:57.000000000 +0100
+++ bind-9.5.0b2/config.h.in	2008-02-20 14:24:17.000000000 +0100
@@ -196,8 +196,8 @@ int sigwait(const unsigned int *set, int
 /* Define if libxml2 was found */
 #undef HAVE_LIBXML2
 
-/* Define to 1 if you have the <linux/capability.h> header file. */
-#undef HAVE_LINUX_CAPABILITY_H
+/* Define to 1 if you have the <sys/capability.h> header file. */
+#undef HAVE_SYS_CAPABILITY_H
 
 /* Define to 1 if you have the <locale.h> header file. */
 #undef HAVE_LOCALE_H


More information about the bind-workers mailing list