diff options
| author | LinkTed <link.ted@mailbox.org> | 2021-06-13 19:26:24 +0200 | 
|---|---|---|
| committer | Mike Frysinger <vapier@gmail.com> | 2021-12-23 17:29:10 -0500 | 
| commit | 6e214b261604c4ab1ffc244272443a587bb59927 (patch) | |
| tree | 618ab3e7bb84f80eff7a8c8e23c1700a2ca587be | |
| parent | fd1e4a384af44a8687b3a5369283f80f1cf29d84 (diff) | |
| download | openrc-6e214b261604c4ab1ffc244272443a587bb59927.tar.xz | |
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.yml | 4 | ||||
| -rw-r--r-- | .github/workflows/ci-ubuntu.yml | 4 | ||||
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | man/start-stop-daemon.8 | 3 | ||||
| -rw-r--r-- | man/supervise-daemon.8 | 3 | ||||
| -rw-r--r-- | meson.build | 7 | ||||
| -rw-r--r-- | meson_options.txt | 2 | ||||
| -rw-r--r-- | mk/os-Linux.mk | 6 | ||||
| -rw-r--r-- | service-script-guide.md | 15 | ||||
| -rw-r--r-- | sh/start-stop-daemon.sh | 1 | ||||
| -rw-r--r-- | sh/supervise-daemon.sh | 1 | ||||
| -rw-r--r-- | src/rc/meson.build | 8 | ||||
| -rw-r--r-- | src/rc/start-stop-daemon.c | 35 | ||||
| -rw-r--r-- | src/rc/supervise-daemon.c | 35 | 
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 @@ -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) | 
