aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinkTed <link.ted@mailbox.org>2021-06-13 19:26:24 +0200
committerMike Frysinger <vapier@gmail.com>2021-12-23 17:29:10 -0500
commit6e214b261604c4ab1ffc244272443a587bb59927 (patch)
tree618ab3e7bb84f80eff7a8c8e23c1700a2ca587be
parentfd1e4a384af44a8687b3a5369283f80f1cf29d84 (diff)
capabilities: Add support for Linux capabilities(7)
This adds capabilities for start-stop-daemon by adding --capabilities option. As a result, the user can specify the inheritable, ambient and bounding set by define capabilities in the service script. This fixes #314.
-rw-r--r--.github/workflows/ci-alpine-linux.yml4
-rw-r--r--.github/workflows/ci-ubuntu.yml4
-rw-r--r--README.md1
-rw-r--r--man/start-stop-daemon.83
-rw-r--r--man/supervise-daemon.83
-rw-r--r--meson.build7
-rw-r--r--meson_options.txt2
-rw-r--r--mk/os-Linux.mk6
-rw-r--r--service-script-guide.md15
-rw-r--r--sh/start-stop-daemon.sh1
-rw-r--r--sh/supervise-daemon.sh1
-rw-r--r--src/rc/meson.build8
-rw-r--r--src/rc/start-stop-daemon.c35
-rw-r--r--src/rc/supervise-daemon.c35
14 files changed, 118 insertions, 7 deletions
diff --git a/.github/workflows/ci-alpine-linux.yml b/.github/workflows/ci-alpine-linux.yml
index f5f2faed..40920751 100644
--- a/.github/workflows/ci-alpine-linux.yml
+++ b/.github/workflows/ci-alpine-linux.yml
@@ -15,7 +15,9 @@ jobs:
meson \
pkgconf \
linux-pam \
- linux-pam-dev
+ linux-pam-dev \
+ libcap \
+ libcap-dev
- name: checkout
uses: actions/checkout@v2
- run: meson setup builddir/
diff --git a/.github/workflows/ci-ubuntu.yml b/.github/workflows/ci-ubuntu.yml
index 5e1860cf..5a5c9ed7 100644
--- a/.github/workflows/ci-ubuntu.yml
+++ b/.github/workflows/ci-ubuntu.yml
@@ -9,7 +9,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- run: sudo apt-get update -q
- - run: sudo apt-get install -q -y build-essential libpam-dev meson
+ - run: sudo apt-get install -q -y build-essential libpam-dev meson libcap-dev
- run: meson setup builddir/
env:
CC: gcc
@@ -23,7 +23,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- run: sudo apt-get update -q
- - run: sudo apt-get install -q -y build-essential clang libpam-dev meson
+ - run: sudo apt-get install -q -y build-essential clang libpam-dev meson libcap-dev
- run: meson setup builddir/
env:
CC: clang
diff --git a/README.md b/README.md
index 35ba6c12..f9825678 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ DESTDIR=/tmp/openrc-image
MKBASHCOMP=no
MKNET=no
MKPAM=pam
+MKCAP=yes
MKPREFIX=yes
MKPKGCONFIG=no
MKSELINUX=yes
diff --git a/man/start-stop-daemon.8 b/man/start-stop-daemon.8
index e5aa4684..990e9097 100644
--- a/man/start-stop-daemon.8
+++ b/man/start-stop-daemon.8
@@ -161,6 +161,9 @@ Cmd must be an absolute pathname, but relative to the path optionally given with
.Fl r , -chroot .
This process must be prepared to accept input on stdin and be able to
log it or send it to another location.
+.It Fl -capabilities Ar cap-list
+Start the daemon with the listed inheritable, ambient and bounding capabilities.
+The format is the same as in cap_iab(3).
.It Fl w , -wait Ar milliseconds
Wait
.Ar milliseconds
diff --git a/man/supervise-daemon.8 b/man/supervise-daemon.8
index 69e2ff3f..9ff6ff66 100644
--- a/man/supervise-daemon.8
+++ b/man/supervise-daemon.8
@@ -158,6 +158,9 @@ The logfile can also be a named pipe.
The same thing as
.Fl 1 , -stdout
but with the standard error output.
+.It Fl -capabilities Ar cap-list
+Start the daemon with the listed inheritable, ambient and bounding capabilities.
+The format is the same as in cap_iab(3).
.El
.Sh ENVIRONMENT
.Va SSD_IONICELEVEL
diff --git a/meson.build b/meson.build
index 1fb378c1..9b16d088 100644
--- a/meson.build
+++ b/meson.build
@@ -57,6 +57,13 @@ if not pam_dep.found() and get_option('pam')
error('Pam was requested but could not be located')
endif
+cap_dep = dependency('libcap', version: '>=2.33', required : get_option('capabilities'))
+if cap_dep.found()
+ cc_cap_flags = '-DHAVE_CAP'
+else
+ cc_cap_flags = []
+endif
+
option_pkg_prefix = get_option('pkg_prefix')
if option_pkg_prefix == ''
if os == 'Dragonfly' or os == 'FreeBSD'
diff --git a/meson_options.txt b/meson_options.txt
index e4e7c4df..2c74152e 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -14,6 +14,8 @@ option('os', type : 'combo',
description : 'Operating System (autodetected if not specified)')
option('pam', type : 'boolean',
description : 'enable PAM support')
+option('capabilities', type : 'feature', value: 'auto',
+ description : 'enable capabilities support')
option('pkg_prefix', type : 'string',
description : 'default location where packages are installed')
option('pkgconfig', type : 'boolean',
diff --git a/mk/os-Linux.mk b/mk/os-Linux.mk
index e83f8616..00521c80 100644
--- a/mk/os-Linux.mk
+++ b/mk/os-Linux.mk
@@ -27,6 +27,12 @@ endif
endif
+ifeq (${MKCAP},yes)
+CPPFLAGS+= -DHAVE_CAP
+LIBCAP?= -lcap
+LDADD += $(LIBCAP)
+endif
+
ifeq (${MKAUDIT},yes)
LIBAUDIT?= -laudit
CPPFLAGS+= -DHAVE_AUDIT
diff --git a/service-script-guide.md b/service-script-guide.md
index 38b9c55d..dffb61c6 100644
--- a/service-script-guide.md
+++ b/service-script-guide.md
@@ -187,6 +187,21 @@ with
* command_user="user:group"
+If your daemon should run with specific inheritable, ambient and
+bounding capabilities, then you can tell start-stop-daemon to launch
+it with
+
+ * capabilities="cap-list"
+
+The format is the same as in cap_iab(3). (Only on Linux)
+
+For example, to start the daemon with ambient and inheritable
+`cap_chown`, but without `cap_setpcap` in the bounding set, use
+the following value:
+```sh
+capabilities="^cap_chown,!cap_setpcap"
+```
+
Finally, if your daemon always forks into the background but fails to
create a PID file, then your only option is to use
diff --git a/sh/start-stop-daemon.sh b/sh/start-stop-daemon.sh
index 95e62dce..02a7cd84 100644
--- a/sh/start-stop-daemon.sh
+++ b/sh/start-stop-daemon.sh
@@ -53,6 +53,7 @@ ssd_start()
${error_log+--stderr} $error_log \
${output_logger_arg} \
${error_logger_arg} \
+ ${capabilities+--capabilities} "$capabilities" \
${procname:+--name} $procname \
${pidfile:+--pidfile} $pidfile \
${command_user+--user} $command_user \
diff --git a/sh/supervise-daemon.sh b/sh/supervise-daemon.sh
index a8541922..be4c9d71 100644
--- a/sh/supervise-daemon.sh
+++ b/sh/supervise-daemon.sh
@@ -36,6 +36,7 @@ supervise_start()
${respawn_period:+--respawn-period} $respawn_period \
${healthcheck_delay:+--healthcheck-delay} $healthcheck_delay \
${healthcheck_timer:+--healthcheck-timer} $healthcheck_timer \
+ ${capabilities+--capabilities} "$capabilities" \
${command_user+--user} $command_user \
${umask+--umask} $umask \
${supervise_daemon_args:-${start_stop_daemon_args}} \
diff --git a/src/rc/meson.build b/src/rc/meson.build
index e0603fd0..c76587c2 100644
--- a/src/rc/meson.build
+++ b/src/rc/meson.build
@@ -93,9 +93,9 @@ executable('runscript',
executable('start-stop-daemon',
['start-stop-daemon.c', 'rc-pipes.c', rc_misc_c, rc_schedules_c,
rc_selinux_c, usage_c, version_h],
- c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags],
+ c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_cap_flags, cc_selinux_flags],
link_with: [libeinfo, librc],
- dependencies: [audit_dep, dl_dep, pam_dep, pam_misc_dep, util_dep, selinux_dep, crypt_dep],
+ dependencies: [audit_dep, dl_dep, pam_dep, cap_dep, pam_misc_dep, util_dep, selinux_dep, crypt_dep],
include_directories: [incdir, einfo_incdir, rc_incdir],
install: true,
install_dir: sbindir)
@@ -103,9 +103,9 @@ executable('start-stop-daemon',
executable('supervise-daemon',
['supervise-daemon.c', rc_misc_c, rc_plugin_c, rc_schedules_c,
usage_c, version_h],
- c_args : [cc_branding_flags, cc_pam_flags, cc_selinux_flags],
+ c_args : [cc_branding_flags, cc_pam_flags, cc_cap_flags, cc_selinux_flags],
link_with: [libeinfo, librc],
- dependencies: [dl_dep, pam_dep, util_dep, selinux_dep],
+ dependencies: [dl_dep, pam_dep, cap_dep, util_dep, selinux_dep],
include_directories: [incdir, einfo_incdir, rc_incdir],
install: true,
install_dir: sbindir)
diff --git a/src/rc/start-stop-daemon.c b/src/rc/start-stop-daemon.c
index 62e579d1..4d89b0b6 100644
--- a/src/rc/start-stop-daemon.c
+++ b/src/rc/start-stop-daemon.c
@@ -55,6 +55,10 @@
static struct pam_conv conv = { NULL, NULL};
#endif
+#ifdef HAVE_CAP
+#include <sys/capability.h>
+#endif
+
#include "einfo.h"
#include "queue.h"
#include "rc.h"
@@ -69,6 +73,7 @@ const char *extraopts = NULL;
const char getoptstring[] = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:1:2:3:4:" \
getoptstring_COMMON;
const struct option longopts[] = {
+ { "capabilities", 1, NULL, 0x100},
{ "ionice", 1, NULL, 'I'},
{ "stop", 0, NULL, 'K'},
{ "nicelevel", 1, NULL, 'N'},
@@ -101,6 +106,7 @@ const struct option longopts[] = {
longopts_COMMON
};
const char * const longopts_help[] = {
+ "Set the inheritable, ambient and bounding capabilities",
"Set an ionice class:data when starting",
"Stop daemon",
"Set a nicelevel when starting",
@@ -307,6 +313,9 @@ int main(int argc, char **argv)
mode_t numask = 022;
char **margv;
unsigned int start_wait = 0;
+#ifdef HAVE_CAP
+ cap_iab_t cap_iab = NULL;
+#endif
applet = basename_c(argv[0]);
atexit(cleanup);
@@ -353,6 +362,16 @@ int main(int argc, char **argv)
while ((opt = getopt_long(argc, argv, getoptstring, longopts,
(int *) 0)) != -1)
switch (opt) {
+ case 0x100:
+#ifdef HAVE_CAP
+ cap_iab = cap_iab_from_text(optarg);
+ if (cap_iab == NULL)
+ eerrorx("Could not parse iab: %s", strerror(errno));
+#else
+ eerrorx("Capabilities support not enabled");
+#endif
+ break;
+
case 'I': /* --ionice */
if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0)
eerrorx("%s: invalid ionice `%s'",
@@ -850,13 +869,29 @@ int main(int argc, char **argv)
if (changeuser && initgroups(changeuser, gid))
eerrorx("%s: initgroups (%s, %d)",
applet, changeuser, gid);
+#ifdef HAVE_CAP
+ if (uid && cap_setuid(uid))
+#else
if (uid && setuid(uid))
+#endif
eerrorx ("%s: unable to set userid to %d",
applet, uid);
/* Close any fd's to the passwd database */
endpwent();
+#ifdef HAVE_CAP
+ if (cap_iab != NULL) {
+ i = cap_iab_set_proc(cap_iab);
+
+ if (cap_free(cap_iab) != 0)
+ eerrorx("Could not releasable memory: %s", strerror(errno));
+
+ if (i != 0)
+ eerrorx("Could not set iab: %s", strerror(errno));
+ }
+#endif
+
#ifdef TIOCNOTTY
ioctl(tty_fd, TIOCNOTTY, 0);
close(tty_fd);
diff --git a/src/rc/supervise-daemon.c b/src/rc/supervise-daemon.c
index ddadb677..135fc902 100644
--- a/src/rc/supervise-daemon.c
+++ b/src/rc/supervise-daemon.c
@@ -57,6 +57,10 @@
static struct pam_conv conv = { NULL, NULL};
#endif
+#ifdef HAVE_CAP
+#include <sys/capability.h>
+#endif
+
#include "einfo.h"
#include "queue.h"
#include "rc.h"
@@ -73,6 +77,7 @@ const char getoptstring[] = "A:a:D:d:e:g:H:I:Kk:m:N:p:R:r:s:Su:1:2:3" \
const struct option longopts[] = {
{ "healthcheck-timer", 1, NULL, 'a'},
{ "healthcheck-delay", 1, NULL, 'A'},
+ { "capabilities", 1, NULL, 0x100},
{ "respawn-delay", 1, NULL, 'D'},
{ "chdir", 1, NULL, 'd'},
{ "env", 1, NULL, 'e'},
@@ -98,6 +103,7 @@ const struct option longopts[] = {
const char * const longopts_help[] = {
"set an initial health check delay",
"set a health check timer",
+ "Set the inheritable, ambient and bounding capabilities",
"Set a respawn delay",
"Change the PWD",
"Set an environment string",
@@ -152,6 +158,9 @@ static int fifo_fd = 0;
static char *pidfile = NULL;
static char *svcname = NULL;
static bool verbose = false;
+#ifdef HAVE_CAP
+static cap_iab_t cap_iab = NULL;
+#endif
extern char **environ;
@@ -398,12 +407,28 @@ static void child_process(char *exec, char **argv)
eerrorx("%s: unable to set groupid to %d", applet, gid);
if (changeuser && initgroups(changeuser, gid))
eerrorx("%s: initgroups (%s, %d)", applet, changeuser, gid);
+#ifdef HAVE_CAP
+ if (uid && cap_setuid(uid))
+#else
if (uid && setuid(uid))
+#endif
eerrorx ("%s: unable to set userid to %d", applet, uid);
/* Close any fd's to the passwd database */
endpwent();
+#ifdef HAVE_CAP
+ if (cap_iab != NULL) {
+ i = cap_iab_set_proc(cap_iab);
+
+ if (cap_free(cap_iab) != 0)
+ eerrorx("Could not releasable memory: %s", strerror(errno));
+
+ if (i != 0)
+ eerrorx("Could not set iab: %s", strerror(errno));
+ }
+#endif
+
/* remove the controlling tty */
#ifdef TIOCNOTTY
ioctl(tty_fd, TIOCNOTTY, 0);
@@ -797,6 +822,16 @@ int main(int argc, char **argv)
eerrorx("%s: invalid health check delay %s", applet, optarg);
break;
+ case 0x100:
+#ifdef HAVE_CAP
+ cap_iab = cap_iab_from_text(optarg);
+ if (cap_iab == NULL)
+ eerrorx("Could not parse iab: %s", strerror(errno));
+#else
+ eerrorx("Capabilities support not enabled");
+#endif
+ break;
+
case 'D': /* --respawn-delay time */
n = sscanf(optarg, "%d", &respawn_delay);
if (n != 1 || respawn_delay < 1)