Package: dpkg
Version: 1.14.24

Capabilities allow the administrator to split "root" user's power into
a set of privileges. For instance, most network daemons start as root because
they need to bind a port < 1024 when they in fact just need the
CAP_NET_BIND_SERVICE capacity.

The patch below provides the ability to remove some capabilities.

As it seems difficult to assess the specific capabilities needed by each
daemon (w/o maintainers' help), the patch just gives the option to do
so, but does not change anything by default. It is hence 100% backward
compatible with current packages.

Nonetheless, the set of "default" capabilities used by most Debian
daemons could be limited: most of those do not need CAP_SYS_PTRACE,
CAP_NET_RAW, etc. In fact, most daemons would work just fine when
launched w/o the following capabilities:

 - cap_sys_module    - cap_mknod            - cap_sys_tty_config
 - cap_sys_ptrace    - cap_net_admin        - cap_ipc_owner
 - cap_net_raw       - cap_mac_admin        - cap_sys_resource
 - cap_sys_boot      - cap_sys_rawio        - cap_mac_override
 - cap_sys_chroot    - cap_kill             - cap_audit_control
 - cap_sys_time      - cap_linux_immutable
 - cap_sys_pacct     - cap_sys_admin

However, some daemons will require some tuning like OpenSSH which
requires cap_sys_chroot and cap_net_admin (if using layer 2 tunnel).

My patch (against git tree) is trivial but I have most probably screwed
up the autoconf/automake stuff :)

It introduces a new command line option (--dropcap/-D) which takes a list of
capabilities to be removed (separated by commas).

The capabilities requested for removal are suppressed by modifying the 
"capability
bouding set" and the "inherited capability set" in order to be sure that the
daemon will never get it (even by exec*() a SUID binary).

---
 configure.ac              |    2 +-
 m4/libs.m4                |    1 +
 man/start-stop-daemon.8   |   12 ++++++++
 utils/start-stop-daemon.c |   64 ++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 3d3b607..e11b462 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,7 +74,7 @@ fi
 # Checks for header files.
 AC_HEADER_STDC
 AC_CHECK_HEADERS([stddef.h error.h locale.h libintl.h kvm.h \
-                  sys/cdefs.h sys/syscall.h])
+                  sys/cdefs.h sys/syscall.h sys/capability.h])
 DPKG_CHECK_DEFINE(TIOCNOTTY, [sys/ioctl.h])
 
 # Checks for typedefs, structures, and compiler characteristics.
diff --git a/m4/libs.m4 b/m4/libs.m4
index 1b3cb24..3af9775 100644
--- a/m4/libs.m4
+++ b/m4/libs.m4
@@ -108,4 +108,5 @@ AC_CHECK_LIB([ihash], [ihash_create], 
[SSD_LIBS="${SSD_LIBS:+$SSD_LIBS }-lihash"
 AC_CHECK_LIB([ps], [proc_stat_list_create], [SSD_LIBS="${SSD_LIBS:+$SSD_LIBS 
}-lps"])
 AC_CHECK_LIB([shouldbeinlibc], [fmt_past_time], 
[SSD_LIBS="${SSD_LIBS:+$SSD_LIBS }-lshouldbeinlibc"])
 AC_CHECK_LIB([kvm], [kvm_openfiles], [SSD_LIBS="${SSD_LIBS:+$SSD_LIBS }-lkvm"])
+AC_CHECK_LIB([cap], [cap_from_name], [SSD_LIBS="${SSD_LIBS:+$SSD_LIBS }-lcap"])
 ])# DPKG_LIB_SSD
diff --git a/man/start-stop-daemon.8 b/man/start-stop-daemon.8
index 1e6c00f..4cfc1c7 100644
--- a/man/start-stop-daemon.8
+++ b/man/start-stop-daemon.8
@@ -196,6 +196,9 @@ Chdir and chroot to
 before starting the process. Please note that the pidfile is also written
 after the chroot.
 .TP
+.BR \-D ", " \-\-dropcap " \fIcapabilities1,capabilities2\fP"
+Drop theses capabilities separated by commas.
+.TP
 .BR \-d ", " \-\-chdir " \fIpath\fP"
 Chdir to
 .I path
@@ -290,6 +293,15 @@ Demonstration of a custom schedule for stopping \fBfood\fP:
 start\-stop\-daemon \-\-stop \-\-oknodo \-\-user food \-\-name food 
\-\-pidfile /var/run/food.pid \-\-retry=TERM/30/KILL/5
 .fi
 .PP
+Start the \fBfood\fP daemon without two \fBcapabilities\fP:
+.IP
+.nf
+start\-stop\-daemon \-\-dropcap CAP_NET_RAW,cap_sys_module \-\-start \-\-exec 
/usr/sbin/food
+.fi
+.PP
+.
+.SH SEE ALSO
+\fBcapabilities\fP(7),
 .
 .SH AUTHORS
 Marek Michalkiewicz <[email protected]> based on
diff --git a/utils/start-stop-daemon.c b/utils/start-stop-daemon.c
index 34ee022..5c35a89 100644
--- a/utils/start-stop-daemon.c
+++ b/utils/start-stop-daemon.c
@@ -59,6 +59,11 @@
 #include <limits.h>
 #endif
 
+#if HAVE_SYS_CAPABILITY_H
+# include <sys/prctl.h>
+# include <sys/capability.h>
+#endif
+
 #ifdef HAVE_KVM_H
 #include <kvm.h>
 #include <sys/sysctl.h>
@@ -156,6 +161,7 @@ static char what_stop[1024];
 static const char *progname = "";
 static int nicelevel = 0;
 static int umask_value = -1;
+static char *caplist = NULL;
 
 #define IOPRIO_CLASS_SHIFT 13
 #define IOPRIO_PRIO_VALUE(class, prio) (((class) << IOPRIO_CLASS_SHIFT) | 
(prio))
@@ -361,6 +367,7 @@ do_help(void)
 "  -s|--signal <signal>          signal to send (default TERM)\n"
 "  -a|--startas <pathname>       program to start (default is <executable>)\n"
 "  -r|--chroot <directory>       chroot to <directory> before starting\n"
+"  -D|--dropcap <capbilities>    drop theses capabilities\n"
 "  -d|--chdir <directory>        change to <directory> (default is /)\n"
 "  -N|--nicelevel <incr>         add incr to the process's nice level\n"
 "  -P|--procsched <policy[:prio]>\n"
@@ -667,6 +674,46 @@ parse_schedule(const char *schedule_str)
        }
 }
 
+#ifdef HAVE_SYS_CAPABILITY_H
+static void
+remove_capabilities(char *capstr) {
+       cap_value_t capval;
+       char *savedptr, *ptr;
+       cap_t caps;
+
+       caps = cap_get_proc();
+       if (caps == NULL) {
+               fatal("Unable to retrieve my capabilities");
+       }
+
+       ptr = strtok_r(capstr, ",", &savedptr);
+       while (ptr) {
+               if (cap_from_name(ptr, &capval) != 0) {
+                       errno = EINVAL;
+                       fatal("Unable to parse this capability : \"%s\"", ptr);
+               }
+
+               if (prctl(PR_CAPBSET_DROP, capval, 0, 0) != 0) {
+                       fatal("Unable to drop this capability: %s", ptr);
+               }
+
+               if (cap_set_flag(caps, CAP_INHERITABLE, 1, (cap_value_t 
*)&capval, CAP_CLEAR) != 0) {
+                       fatal("Unable to clear the capability %s", ptr);
+               }
+
+               ptr = strtok_r(NULL, ",", &savedptr);
+       }
+
+       if (cap_set_proc(caps) != 0) {
+               fatal("Unable to remove theses capabilities from the inherited 
set\n");
+       }
+
+       if (cap_free(caps) == -1) {
+               fatal("Cannot free the capability");
+       }
+}
+#endif
+
 static void
 parse_options(int argc, char * const *argv)
 {
@@ -685,6 +732,7 @@ parse_options(int argc, char * const *argv)
                { "user",         1, NULL, 'u'},
                { "group",        1, NULL, 'g'},
                { "chroot",       1, NULL, 'r'},
+               { "dropcap",      1, NULL, 'D'},
                { "verbose",      0, NULL, 'v'},
                { "exec",         1, NULL, 'x'},
                { "chuid",        1, NULL, 'c'},
@@ -707,7 +755,7 @@ parse_options(int argc, char * const *argv)
 
        for (;;) {
                c = getopt_long(argc, argv,
-                               "HKSVa:n:op:qr:s:tu:vx:c:N:P:I:k:bmR:g:d:",
+                               "HKSVa:n:op:qr:s:tu:vx:c:N:P:I:k:bmR:g:d:D:",
                                longopts, NULL);
                if (c == -1)
                        break;
@@ -767,6 +815,13 @@ parse_options(int argc, char * const *argv)
                case 'r':  /* --chroot /new/root */
                        changeroot = optarg;
                        break;
+               case 'D':  /* --dropcap cap_net_raw,cap_mac_admin */
+#ifdef HAVE_SYS_CAPABILITY_H
+                       caplist = optarg;
+#else
+                       badusage("Capabilities are not supported on your OS");
+#endif
+                       break;
                case 'N':  /* --nice */
                        nicelevel = atoi(optarg);
                        break;
@@ -1589,6 +1644,13 @@ main(int argc, char **argv)
                for (i = get_open_fd_max() - 1; i >= 3; --i)
                        close(i);
        }
+
+#ifdef HAVE_SYS_CAPABILITY_H
+       if (caplist) {
+               remove_capabilities(caplist);
+       }
+#endif
+
        execv(startas, argv);
        fatal("Unable to start %s: %s", startas, strerror(errno));
 }

-- 
Nicolas Bareil                                  http://chdir.org/~nico/
OpenPGP=0xAE4F7057 Fingerprint=34DB22091049FB2F33E6B71580F314DAAE4F7057



-- 
To UNSUBSCRIBE, email to [email protected]
with a subject of "unsubscribe". Trouble? Contact [email protected]

Reply via email to