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.
This commit is contained in:
parent
fd1e4a384a
commit
6e214b2616
4
.github/workflows/ci-alpine-linux.yml
vendored
4
.github/workflows/ci-alpine-linux.yml
vendored
@ -15,7 +15,9 @@ jobs:
|
|||||||
meson \
|
meson \
|
||||||
pkgconf \
|
pkgconf \
|
||||||
linux-pam \
|
linux-pam \
|
||||||
linux-pam-dev
|
linux-pam-dev \
|
||||||
|
libcap \
|
||||||
|
libcap-dev
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- run: meson setup builddir/
|
- run: meson setup builddir/
|
||||||
|
4
.github/workflows/ci-ubuntu.yml
vendored
4
.github/workflows/ci-ubuntu.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: sudo apt-get update -q
|
- 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/
|
- run: meson setup builddir/
|
||||||
env:
|
env:
|
||||||
CC: gcc
|
CC: gcc
|
||||||
@ -23,7 +23,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: sudo apt-get update -q
|
- 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/
|
- run: meson setup builddir/
|
||||||
env:
|
env:
|
||||||
CC: clang
|
CC: clang
|
||||||
|
@ -34,6 +34,7 @@ DESTDIR=/tmp/openrc-image
|
|||||||
MKBASHCOMP=no
|
MKBASHCOMP=no
|
||||||
MKNET=no
|
MKNET=no
|
||||||
MKPAM=pam
|
MKPAM=pam
|
||||||
|
MKCAP=yes
|
||||||
MKPREFIX=yes
|
MKPREFIX=yes
|
||||||
MKPKGCONFIG=no
|
MKPKGCONFIG=no
|
||||||
MKSELINUX=yes
|
MKSELINUX=yes
|
||||||
|
@ -161,6 +161,9 @@ Cmd must be an absolute pathname, but relative to the path optionally given with
|
|||||||
.Fl r , -chroot .
|
.Fl r , -chroot .
|
||||||
This process must be prepared to accept input on stdin and be able to
|
This process must be prepared to accept input on stdin and be able to
|
||||||
log it or send it to another location.
|
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
|
.It Fl w , -wait Ar milliseconds
|
||||||
Wait
|
Wait
|
||||||
.Ar milliseconds
|
.Ar milliseconds
|
||||||
|
@ -158,6 +158,9 @@ The logfile can also be a named pipe.
|
|||||||
The same thing as
|
The same thing as
|
||||||
.Fl 1 , -stdout
|
.Fl 1 , -stdout
|
||||||
but with the standard error output.
|
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
|
.El
|
||||||
.Sh ENVIRONMENT
|
.Sh ENVIRONMENT
|
||||||
.Va SSD_IONICELEVEL
|
.Va SSD_IONICELEVEL
|
||||||
|
@ -57,6 +57,13 @@ if not pam_dep.found() and get_option('pam')
|
|||||||
error('Pam was requested but could not be located')
|
error('Pam was requested but could not be located')
|
||||||
endif
|
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')
|
option_pkg_prefix = get_option('pkg_prefix')
|
||||||
if option_pkg_prefix == ''
|
if option_pkg_prefix == ''
|
||||||
if os == 'Dragonfly' or os == 'FreeBSD'
|
if os == 'Dragonfly' or os == 'FreeBSD'
|
||||||
|
@ -14,6 +14,8 @@ option('os', type : 'combo',
|
|||||||
description : 'Operating System (autodetected if not specified)')
|
description : 'Operating System (autodetected if not specified)')
|
||||||
option('pam', type : 'boolean',
|
option('pam', type : 'boolean',
|
||||||
description : 'enable PAM support')
|
description : 'enable PAM support')
|
||||||
|
option('capabilities', type : 'feature', value: 'auto',
|
||||||
|
description : 'enable capabilities support')
|
||||||
option('pkg_prefix', type : 'string',
|
option('pkg_prefix', type : 'string',
|
||||||
description : 'default location where packages are installed')
|
description : 'default location where packages are installed')
|
||||||
option('pkgconfig', type : 'boolean',
|
option('pkgconfig', type : 'boolean',
|
||||||
|
@ -27,6 +27,12 @@ endif
|
|||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq (${MKCAP},yes)
|
||||||
|
CPPFLAGS+= -DHAVE_CAP
|
||||||
|
LIBCAP?= -lcap
|
||||||
|
LDADD += $(LIBCAP)
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq (${MKAUDIT},yes)
|
ifeq (${MKAUDIT},yes)
|
||||||
LIBAUDIT?= -laudit
|
LIBAUDIT?= -laudit
|
||||||
CPPFLAGS+= -DHAVE_AUDIT
|
CPPFLAGS+= -DHAVE_AUDIT
|
||||||
|
@ -187,6 +187,21 @@ with
|
|||||||
|
|
||||||
* command_user="user:group"
|
* 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
|
Finally, if your daemon always forks into the background but fails to
|
||||||
create a PID file, then your only option is to use
|
create a PID file, then your only option is to use
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ ssd_start()
|
|||||||
${error_log+--stderr} $error_log \
|
${error_log+--stderr} $error_log \
|
||||||
${output_logger_arg} \
|
${output_logger_arg} \
|
||||||
${error_logger_arg} \
|
${error_logger_arg} \
|
||||||
|
${capabilities+--capabilities} "$capabilities" \
|
||||||
${procname:+--name} $procname \
|
${procname:+--name} $procname \
|
||||||
${pidfile:+--pidfile} $pidfile \
|
${pidfile:+--pidfile} $pidfile \
|
||||||
${command_user+--user} $command_user \
|
${command_user+--user} $command_user \
|
||||||
|
@ -36,6 +36,7 @@ supervise_start()
|
|||||||
${respawn_period:+--respawn-period} $respawn_period \
|
${respawn_period:+--respawn-period} $respawn_period \
|
||||||
${healthcheck_delay:+--healthcheck-delay} $healthcheck_delay \
|
${healthcheck_delay:+--healthcheck-delay} $healthcheck_delay \
|
||||||
${healthcheck_timer:+--healthcheck-timer} $healthcheck_timer \
|
${healthcheck_timer:+--healthcheck-timer} $healthcheck_timer \
|
||||||
|
${capabilities+--capabilities} "$capabilities" \
|
||||||
${command_user+--user} $command_user \
|
${command_user+--user} $command_user \
|
||||||
${umask+--umask} $umask \
|
${umask+--umask} $umask \
|
||||||
${supervise_daemon_args:-${start_stop_daemon_args}} \
|
${supervise_daemon_args:-${start_stop_daemon_args}} \
|
||||||
|
@ -93,9 +93,9 @@ executable('runscript',
|
|||||||
executable('start-stop-daemon',
|
executable('start-stop-daemon',
|
||||||
['start-stop-daemon.c', 'rc-pipes.c', rc_misc_c, rc_schedules_c,
|
['start-stop-daemon.c', 'rc-pipes.c', rc_misc_c, rc_schedules_c,
|
||||||
rc_selinux_c, usage_c, version_h],
|
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],
|
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],
|
include_directories: [incdir, einfo_incdir, rc_incdir],
|
||||||
install: true,
|
install: true,
|
||||||
install_dir: sbindir)
|
install_dir: sbindir)
|
||||||
@ -103,9 +103,9 @@ executable('start-stop-daemon',
|
|||||||
executable('supervise-daemon',
|
executable('supervise-daemon',
|
||||||
['supervise-daemon.c', rc_misc_c, rc_plugin_c, rc_schedules_c,
|
['supervise-daemon.c', rc_misc_c, rc_plugin_c, rc_schedules_c,
|
||||||
usage_c, version_h],
|
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],
|
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],
|
include_directories: [incdir, einfo_incdir, rc_incdir],
|
||||||
install: true,
|
install: true,
|
||||||
install_dir: sbindir)
|
install_dir: sbindir)
|
||||||
|
@ -55,6 +55,10 @@
|
|||||||
static struct pam_conv conv = { NULL, NULL};
|
static struct pam_conv conv = { NULL, NULL};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_CAP
|
||||||
|
#include <sys/capability.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "einfo.h"
|
#include "einfo.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "rc.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:" \
|
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;
|
getoptstring_COMMON;
|
||||||
const struct option longopts[] = {
|
const struct option longopts[] = {
|
||||||
|
{ "capabilities", 1, NULL, 0x100},
|
||||||
{ "ionice", 1, NULL, 'I'},
|
{ "ionice", 1, NULL, 'I'},
|
||||||
{ "stop", 0, NULL, 'K'},
|
{ "stop", 0, NULL, 'K'},
|
||||||
{ "nicelevel", 1, NULL, 'N'},
|
{ "nicelevel", 1, NULL, 'N'},
|
||||||
@ -101,6 +106,7 @@ const struct option longopts[] = {
|
|||||||
longopts_COMMON
|
longopts_COMMON
|
||||||
};
|
};
|
||||||
const char * const longopts_help[] = {
|
const char * const longopts_help[] = {
|
||||||
|
"Set the inheritable, ambient and bounding capabilities",
|
||||||
"Set an ionice class:data when starting",
|
"Set an ionice class:data when starting",
|
||||||
"Stop daemon",
|
"Stop daemon",
|
||||||
"Set a nicelevel when starting",
|
"Set a nicelevel when starting",
|
||||||
@ -307,6 +313,9 @@ int main(int argc, char **argv)
|
|||||||
mode_t numask = 022;
|
mode_t numask = 022;
|
||||||
char **margv;
|
char **margv;
|
||||||
unsigned int start_wait = 0;
|
unsigned int start_wait = 0;
|
||||||
|
#ifdef HAVE_CAP
|
||||||
|
cap_iab_t cap_iab = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
applet = basename_c(argv[0]);
|
applet = basename_c(argv[0]);
|
||||||
atexit(cleanup);
|
atexit(cleanup);
|
||||||
@ -353,6 +362,16 @@ int main(int argc, char **argv)
|
|||||||
while ((opt = getopt_long(argc, argv, getoptstring, longopts,
|
while ((opt = getopt_long(argc, argv, getoptstring, longopts,
|
||||||
(int *) 0)) != -1)
|
(int *) 0)) != -1)
|
||||||
switch (opt) {
|
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 */
|
case 'I': /* --ionice */
|
||||||
if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0)
|
if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0)
|
||||||
eerrorx("%s: invalid ionice `%s'",
|
eerrorx("%s: invalid ionice `%s'",
|
||||||
@ -850,13 +869,29 @@ int main(int argc, char **argv)
|
|||||||
if (changeuser && initgroups(changeuser, gid))
|
if (changeuser && initgroups(changeuser, gid))
|
||||||
eerrorx("%s: initgroups (%s, %d)",
|
eerrorx("%s: initgroups (%s, %d)",
|
||||||
applet, changeuser, gid);
|
applet, changeuser, gid);
|
||||||
|
#ifdef HAVE_CAP
|
||||||
|
if (uid && cap_setuid(uid))
|
||||||
|
#else
|
||||||
if (uid && setuid(uid))
|
if (uid && setuid(uid))
|
||||||
|
#endif
|
||||||
eerrorx ("%s: unable to set userid to %d",
|
eerrorx ("%s: unable to set userid to %d",
|
||||||
applet, uid);
|
applet, uid);
|
||||||
|
|
||||||
/* Close any fd's to the passwd database */
|
/* Close any fd's to the passwd database */
|
||||||
endpwent();
|
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
|
#ifdef TIOCNOTTY
|
||||||
ioctl(tty_fd, TIOCNOTTY, 0);
|
ioctl(tty_fd, TIOCNOTTY, 0);
|
||||||
close(tty_fd);
|
close(tty_fd);
|
||||||
|
@ -57,6 +57,10 @@
|
|||||||
static struct pam_conv conv = { NULL, NULL};
|
static struct pam_conv conv = { NULL, NULL};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_CAP
|
||||||
|
#include <sys/capability.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "einfo.h"
|
#include "einfo.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "rc.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[] = {
|
const struct option longopts[] = {
|
||||||
{ "healthcheck-timer", 1, NULL, 'a'},
|
{ "healthcheck-timer", 1, NULL, 'a'},
|
||||||
{ "healthcheck-delay", 1, NULL, 'A'},
|
{ "healthcheck-delay", 1, NULL, 'A'},
|
||||||
|
{ "capabilities", 1, NULL, 0x100},
|
||||||
{ "respawn-delay", 1, NULL, 'D'},
|
{ "respawn-delay", 1, NULL, 'D'},
|
||||||
{ "chdir", 1, NULL, 'd'},
|
{ "chdir", 1, NULL, 'd'},
|
||||||
{ "env", 1, NULL, 'e'},
|
{ "env", 1, NULL, 'e'},
|
||||||
@ -98,6 +103,7 @@ const struct option longopts[] = {
|
|||||||
const char * const longopts_help[] = {
|
const char * const longopts_help[] = {
|
||||||
"set an initial health check delay",
|
"set an initial health check delay",
|
||||||
"set a health check timer",
|
"set a health check timer",
|
||||||
|
"Set the inheritable, ambient and bounding capabilities",
|
||||||
"Set a respawn delay",
|
"Set a respawn delay",
|
||||||
"Change the PWD",
|
"Change the PWD",
|
||||||
"Set an environment string",
|
"Set an environment string",
|
||||||
@ -152,6 +158,9 @@ static int fifo_fd = 0;
|
|||||||
static char *pidfile = NULL;
|
static char *pidfile = NULL;
|
||||||
static char *svcname = NULL;
|
static char *svcname = NULL;
|
||||||
static bool verbose = false;
|
static bool verbose = false;
|
||||||
|
#ifdef HAVE_CAP
|
||||||
|
static cap_iab_t cap_iab = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
extern char **environ;
|
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);
|
eerrorx("%s: unable to set groupid to %d", applet, gid);
|
||||||
if (changeuser && initgroups(changeuser, gid))
|
if (changeuser && initgroups(changeuser, gid))
|
||||||
eerrorx("%s: initgroups (%s, %d)", applet, changeuser, gid);
|
eerrorx("%s: initgroups (%s, %d)", applet, changeuser, gid);
|
||||||
|
#ifdef HAVE_CAP
|
||||||
|
if (uid && cap_setuid(uid))
|
||||||
|
#else
|
||||||
if (uid && setuid(uid))
|
if (uid && setuid(uid))
|
||||||
|
#endif
|
||||||
eerrorx ("%s: unable to set userid to %d", applet, uid);
|
eerrorx ("%s: unable to set userid to %d", applet, uid);
|
||||||
|
|
||||||
/* Close any fd's to the passwd database */
|
/* Close any fd's to the passwd database */
|
||||||
endpwent();
|
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 */
|
/* remove the controlling tty */
|
||||||
#ifdef TIOCNOTTY
|
#ifdef TIOCNOTTY
|
||||||
ioctl(tty_fd, TIOCNOTTY, 0);
|
ioctl(tty_fd, TIOCNOTTY, 0);
|
||||||
@ -797,6 +822,16 @@ int main(int argc, char **argv)
|
|||||||
eerrorx("%s: invalid health check delay %s", applet, optarg);
|
eerrorx("%s: invalid health check delay %s", applet, optarg);
|
||||||
break;
|
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 */
|
case 'D': /* --respawn-delay time */
|
||||||
n = sscanf(optarg, "%d", &respawn_delay);
|
n = sscanf(optarg, "%d", &respawn_delay);
|
||||||
if (n != 1 || respawn_delay < 1)
|
if (n != 1 || respawn_delay < 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user