mirror of
https://github.com/systemd/systemd
synced 2024-10-15 12:34:37 +00:00
Merge pull request #2265 from ipuustin/ambient
capabilities: added support for ambient capabilities.
This commit is contained in:
commit
1f52a79d4e
|
@ -806,6 +806,35 @@
|
||||||
settings.</para></listitem>
|
settings.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>AmbientCapabilities=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Controls which capabilities to include in the
|
||||||
|
ambient capability set for the executed process. Takes a
|
||||||
|
whitespace-separated list of capability names as read by
|
||||||
|
<citerefentry project='mankier'><refentrytitle>cap_from_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
|
||||||
|
e.g. <constant>CAP_SYS_ADMIN</constant>,
|
||||||
|
<constant>CAP_DAC_OVERRIDE</constant>,
|
||||||
|
<constant>CAP_SYS_PTRACE</constant>. This option may appear more than
|
||||||
|
once in which case the ambient capability sets are merged.
|
||||||
|
If the list of capabilities is prefixed with <literal>~</literal>, all
|
||||||
|
but the listed capabilities will be included, the effect of the
|
||||||
|
assignment inverted. If the empty string is
|
||||||
|
assigned to this option, the ambient capability set is reset to
|
||||||
|
the empty capability set, and all prior settings have no effect.
|
||||||
|
If set to <literal>~</literal> (without any further argument), the
|
||||||
|
ambient capability set is reset to the full set of available
|
||||||
|
capabilities, also undoing any previous settings. Note that adding
|
||||||
|
capabilities to ambient capability set adds them to the process's
|
||||||
|
inherited capability set.
|
||||||
|
</para><para>
|
||||||
|
Ambient capability sets are useful if you want to execute a process
|
||||||
|
as a non-privileged user but still want to give it some capabilities.
|
||||||
|
Note that in this case option <constant>keep-caps</constant> is
|
||||||
|
automatically added to <varname>SecureBits=</varname> to retain the
|
||||||
|
capabilities over the user change.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>SecureBits=</varname></term>
|
<term><varname>SecureBits=</varname></term>
|
||||||
<listitem><para>Controls the secure bits set for the executed
|
<listitem><para>Controls the secure bits set for the executed
|
||||||
|
|
|
@ -96,7 +96,62 @@ unsigned long cap_last_cap(void) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
int capability_bounding_set_drop(uint64_t drop, bool right_now) {
|
int capability_update_inherited_set(cap_t caps, uint64_t set) {
|
||||||
|
unsigned long i;
|
||||||
|
|
||||||
|
/* Add capabilities in the set to the inherited caps. Do not apply
|
||||||
|
* them yet. */
|
||||||
|
|
||||||
|
for (i = 0; i < cap_last_cap(); i++) {
|
||||||
|
|
||||||
|
if (set & (UINT64_C(1) << i)) {
|
||||||
|
cap_value_t v;
|
||||||
|
|
||||||
|
v = (cap_value_t) i;
|
||||||
|
|
||||||
|
/* Make the capability inheritable. */
|
||||||
|
if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, CAP_SET) < 0)
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
|
||||||
|
unsigned long i;
|
||||||
|
_cleanup_cap_free_ cap_t caps = NULL;
|
||||||
|
|
||||||
|
/* Add the capabilities to the ambient set. */
|
||||||
|
|
||||||
|
if (also_inherit) {
|
||||||
|
int r;
|
||||||
|
caps = cap_get_proc();
|
||||||
|
if (!caps)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
r = capability_update_inherited_set(caps, set);
|
||||||
|
if (r < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
if (cap_set_proc(caps) < 0)
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < cap_last_cap(); i++) {
|
||||||
|
|
||||||
|
if (set & (UINT64_C(1) << i)) {
|
||||||
|
|
||||||
|
/* Add the capability to the ambient set. */
|
||||||
|
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0)
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int capability_bounding_set_drop(uint64_t keep, bool right_now) {
|
||||||
_cleanup_cap_free_ cap_t after_cap = NULL;
|
_cleanup_cap_free_ cap_t after_cap = NULL;
|
||||||
cap_flag_value_t fv;
|
cap_flag_value_t fv;
|
||||||
unsigned long i;
|
unsigned long i;
|
||||||
|
@ -137,7 +192,7 @@ int capability_bounding_set_drop(uint64_t drop, bool right_now) {
|
||||||
|
|
||||||
for (i = 0; i <= cap_last_cap(); i++) {
|
for (i = 0; i <= cap_last_cap(); i++) {
|
||||||
|
|
||||||
if (drop & ((uint64_t) 1ULL << (uint64_t) i)) {
|
if (!(keep & (UINT64_C(1) << i))) {
|
||||||
cap_value_t v;
|
cap_value_t v;
|
||||||
|
|
||||||
/* Drop it from the bounding set */
|
/* Drop it from the bounding set */
|
||||||
|
@ -176,7 +231,7 @@ finish:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int drop_from_file(const char *fn, uint64_t drop) {
|
static int drop_from_file(const char *fn, uint64_t keep) {
|
||||||
int r, k;
|
int r, k;
|
||||||
uint32_t hi, lo;
|
uint32_t hi, lo;
|
||||||
uint64_t current, after;
|
uint64_t current, after;
|
||||||
|
@ -196,7 +251,7 @@ static int drop_from_file(const char *fn, uint64_t drop) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
|
current = (uint64_t) lo | ((uint64_t) hi << 32ULL);
|
||||||
after = current & ~drop;
|
after = current & keep;
|
||||||
|
|
||||||
if (current == after)
|
if (current == after)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -213,14 +268,14 @@ static int drop_from_file(const char *fn, uint64_t drop) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
int capability_bounding_set_drop_usermode(uint64_t drop) {
|
int capability_bounding_set_drop_usermode(uint64_t keep) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", drop);
|
r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", drop);
|
r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
@ -257,7 +312,7 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
|
||||||
return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
|
return log_error_errno(errno, "Failed to disable keep capabilities flag: %m");
|
||||||
|
|
||||||
/* Drop all caps from the bounding set, except the ones we want */
|
/* Drop all caps from the bounding set, except the ones we want */
|
||||||
r = capability_bounding_set_drop(~keep_capabilities, true);
|
r = capability_bounding_set_drop(keep_capabilities, true);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to drop capabilities: %m");
|
return log_error_errno(r, "Failed to drop capabilities: %m");
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,15 @@
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#define CAP_ALL (uint64_t) -1
|
||||||
|
|
||||||
unsigned long cap_last_cap(void);
|
unsigned long cap_last_cap(void);
|
||||||
int have_effective_cap(int value);
|
int have_effective_cap(int value);
|
||||||
int capability_bounding_set_drop(uint64_t drop, bool right_now);
|
int capability_bounding_set_drop(uint64_t keep, bool right_now);
|
||||||
int capability_bounding_set_drop_usermode(uint64_t drop);
|
int capability_bounding_set_drop_usermode(uint64_t keep);
|
||||||
|
|
||||||
|
int capability_ambient_set_apply(uint64_t set, bool also_inherit);
|
||||||
|
int capability_update_inherited_set(cap_t caps, uint64_t ambient_set);
|
||||||
|
|
||||||
int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
|
int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
|
||||||
|
|
||||||
|
@ -46,3 +51,9 @@ static inline void cap_free_charpp(char **p) {
|
||||||
cap_free(*p);
|
cap_free(*p);
|
||||||
}
|
}
|
||||||
#define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp)
|
#define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp)
|
||||||
|
|
||||||
|
static inline bool cap_test_all(uint64_t caps) {
|
||||||
|
uint64_t m;
|
||||||
|
m = (UINT64_C(1) << (cap_last_cap() + 1)) - 1;
|
||||||
|
return (caps & m) == m;
|
||||||
|
}
|
||||||
|
|
|
@ -1129,3 +1129,19 @@ static inline key_serial_t request_key(const char *type, const char *description
|
||||||
#ifndef KEY_SPEC_USER_KEYRING
|
#ifndef KEY_SPEC_USER_KEYRING
|
||||||
#define KEY_SPEC_USER_KEYRING -4
|
#define KEY_SPEC_USER_KEYRING -4
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef PR_CAP_AMBIENT
|
||||||
|
#define PR_CAP_AMBIENT 47
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PR_CAP_AMBIENT_IS_SET
|
||||||
|
#define PR_CAP_AMBIENT_IS_SET 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PR_CAP_AMBIENT_RAISE
|
||||||
|
#define PR_CAP_AMBIENT_RAISE 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PR_CAP_AMBIENT_CLEAR_ALL
|
||||||
|
#define PR_CAP_AMBIENT_CLEAR_ALL 4
|
||||||
|
#endif
|
||||||
|
|
|
@ -293,9 +293,25 @@ static int property_get_capability_bounding_set(
|
||||||
assert(reply);
|
assert(reply);
|
||||||
assert(c);
|
assert(c);
|
||||||
|
|
||||||
/* We store this negated internally, to match the kernel, but
|
return sd_bus_message_append(reply, "t", c->capability_bounding_set);
|
||||||
* we expose it normalized. */
|
}
|
||||||
return sd_bus_message_append(reply, "t", ~c->capability_bounding_set_drop);
|
|
||||||
|
static int property_get_ambient_capabilities(
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
ExecContext *c = userdata;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
assert(reply);
|
||||||
|
assert(c);
|
||||||
|
|
||||||
|
return sd_bus_message_append(reply, "t", c->capability_ambient_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int property_get_capabilities(
|
static int property_get_capabilities(
|
||||||
|
@ -689,6 +705,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||||
SD_BUS_PROPERTY("Capabilities", "s", property_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("Capabilities", "s", property_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
SD_BUS_PROPERTY("AmbientCapabilities", "t", property_get_ambient_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
|
|
@ -737,12 +737,7 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
|
||||||
/* Sets (but doesn't lookup) the uid and make sure we keep the
|
/* Sets (but doesn't lookup) the uid and make sure we keep the
|
||||||
* capabilities while doing so. */
|
* capabilities while doing so. */
|
||||||
|
|
||||||
if (context->capabilities) {
|
if (context->capabilities || context->capability_ambient_set != 0) {
|
||||||
_cleanup_cap_free_ cap_t d = NULL;
|
|
||||||
static const cap_value_t bits[] = {
|
|
||||||
CAP_SETUID, /* Necessary so that we can run setresuid() below */
|
|
||||||
CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* First step: If we need to keep capabilities but
|
/* First step: If we need to keep capabilities but
|
||||||
* drop privileges we need to make sure we keep our
|
* drop privileges we need to make sure we keep our
|
||||||
|
@ -758,16 +753,24 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
|
||||||
/* Second step: set the capabilities. This will reduce
|
/* Second step: set the capabilities. This will reduce
|
||||||
* the capabilities to the minimum we need. */
|
* the capabilities to the minimum we need. */
|
||||||
|
|
||||||
d = cap_dup(context->capabilities);
|
if (context->capabilities) {
|
||||||
if (!d)
|
_cleanup_cap_free_ cap_t d = NULL;
|
||||||
return -errno;
|
static const cap_value_t bits[] = {
|
||||||
|
CAP_SETUID, /* Necessary so that we can run setresuid() below */
|
||||||
|
CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
|
||||||
|
};
|
||||||
|
|
||||||
if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
|
d = cap_dup(context->capabilities);
|
||||||
cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
|
if (!d)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
if (cap_set_proc(d) < 0)
|
if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
|
||||||
return -errno;
|
cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
if (cap_set_proc(d) < 0)
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Third step: actually set the uids */
|
/* Third step: actually set the uids */
|
||||||
|
@ -1856,6 +1859,8 @@ static int exec_child(
|
||||||
|
|
||||||
if (params->apply_permissions) {
|
if (params->apply_permissions) {
|
||||||
|
|
||||||
|
int secure_bits = context->secure_bits;
|
||||||
|
|
||||||
for (i = 0; i < _RLIMIT_MAX; i++) {
|
for (i = 0; i < _RLIMIT_MAX; i++) {
|
||||||
if (!context->rlimit[i])
|
if (!context->rlimit[i])
|
||||||
continue;
|
continue;
|
||||||
|
@ -1866,28 +1871,71 @@ static int exec_child(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context->capability_bounding_set_drop) {
|
if (!cap_test_all(context->capability_bounding_set)) {
|
||||||
r = capability_bounding_set_drop(context->capability_bounding_set_drop, false);
|
r = capability_bounding_set_drop(context->capability_bounding_set, false);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
*exit_status = EXIT_CAPABILITIES;
|
*exit_status = EXIT_CAPABILITIES;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This is done before enforce_user, but ambient set
|
||||||
|
* does not survive over setresuid() if keep_caps is not set. */
|
||||||
|
if (context->capability_ambient_set != 0) {
|
||||||
|
r = capability_ambient_set_apply(context->capability_ambient_set, true);
|
||||||
|
if (r < 0) {
|
||||||
|
*exit_status = EXIT_CAPABILITIES;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->capabilities) {
|
||||||
|
|
||||||
|
/* The capabilities in ambient set need to be also in the inherited
|
||||||
|
* set. If they aren't, trying to get them will fail. Add the ambient
|
||||||
|
* set inherited capabilities to the capability set in the context.
|
||||||
|
* This is needed because if capabilities are set (using "Capabilities="
|
||||||
|
* keyword), they will override whatever we set now. */
|
||||||
|
|
||||||
|
r = capability_update_inherited_set(context->capabilities, context->capability_ambient_set);
|
||||||
|
if (r < 0) {
|
||||||
|
*exit_status = EXIT_CAPABILITIES;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (context->user) {
|
if (context->user) {
|
||||||
r = enforce_user(context, uid);
|
r = enforce_user(context, uid);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
*exit_status = EXIT_USER;
|
*exit_status = EXIT_USER;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
if (context->capability_ambient_set != 0) {
|
||||||
|
|
||||||
|
/* Fix the ambient capabilities after user change. */
|
||||||
|
r = capability_ambient_set_apply(context->capability_ambient_set, false);
|
||||||
|
if (r < 0) {
|
||||||
|
*exit_status = EXIT_CAPABILITIES;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we were asked to change user and ambient capabilities
|
||||||
|
* were requested, we had to add keep-caps to the securebits
|
||||||
|
* so that we would maintain the inherited capability set
|
||||||
|
* through the setresuid(). Make sure that the bit is added
|
||||||
|
* also to the context secure_bits so that we don't try to
|
||||||
|
* drop the bit away next. */
|
||||||
|
|
||||||
|
secure_bits |= 1<<SECURE_KEEP_CAPS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PR_GET_SECUREBITS is not privileged, while
|
/* PR_GET_SECUREBITS is not privileged, while
|
||||||
* PR_SET_SECUREBITS is. So to suppress
|
* PR_SET_SECUREBITS is. So to suppress
|
||||||
* potential EPERMs we'll try not to call
|
* potential EPERMs we'll try not to call
|
||||||
* PR_SET_SECUREBITS unless necessary. */
|
* PR_SET_SECUREBITS unless necessary. */
|
||||||
if (prctl(PR_GET_SECUREBITS) != context->secure_bits)
|
if (prctl(PR_GET_SECUREBITS) != secure_bits)
|
||||||
if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
|
if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) {
|
||||||
*exit_status = EXIT_SECUREBITS;
|
*exit_status = EXIT_SECUREBITS;
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
@ -2114,6 +2162,7 @@ void exec_context_init(ExecContext *c) {
|
||||||
c->timer_slack_nsec = NSEC_INFINITY;
|
c->timer_slack_nsec = NSEC_INFINITY;
|
||||||
c->personality = PERSONALITY_INVALID;
|
c->personality = PERSONALITY_INVALID;
|
||||||
c->runtime_directory_mode = 0755;
|
c->runtime_directory_mode = 0755;
|
||||||
|
c->capability_bounding_set = CAP_ALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void exec_context_done(ExecContext *c) {
|
void exec_context_done(ExecContext *c) {
|
||||||
|
@ -2517,12 +2566,23 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
|
||||||
(c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
|
(c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
|
||||||
(c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
|
(c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
|
||||||
|
|
||||||
if (c->capability_bounding_set_drop) {
|
if (c->capability_bounding_set != CAP_ALL) {
|
||||||
unsigned long l;
|
unsigned long l;
|
||||||
fprintf(f, "%sCapabilityBoundingSet:", prefix);
|
fprintf(f, "%sCapabilityBoundingSet:", prefix);
|
||||||
|
|
||||||
for (l = 0; l <= cap_last_cap(); l++)
|
for (l = 0; l <= cap_last_cap(); l++)
|
||||||
if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l)))
|
if (c->capability_bounding_set & (UINT64_C(1) << l))
|
||||||
|
fprintf(f, " %s", strna(capability_to_name(l)));
|
||||||
|
|
||||||
|
fputs("\n", f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c->capability_ambient_set != 0) {
|
||||||
|
unsigned long l;
|
||||||
|
fprintf(f, "%sAmbientCapabilities:", prefix);
|
||||||
|
|
||||||
|
for (l = 0; l <= cap_last_cap(); l++)
|
||||||
|
if (c->capability_ambient_set & (UINT64_C(1) << l))
|
||||||
fprintf(f, " %s", strna(capability_to_name(l)));
|
fprintf(f, " %s", strna(capability_to_name(l)));
|
||||||
|
|
||||||
fputs("\n", f);
|
fputs("\n", f);
|
||||||
|
|
|
@ -155,7 +155,9 @@ struct ExecContext {
|
||||||
char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
|
char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
|
||||||
unsigned long mount_flags;
|
unsigned long mount_flags;
|
||||||
|
|
||||||
uint64_t capability_bounding_set_drop;
|
uint64_t capability_bounding_set;
|
||||||
|
|
||||||
|
uint64_t capability_ambient_set;
|
||||||
|
|
||||||
cap_t capabilities;
|
cap_t capabilities;
|
||||||
int secure_bits;
|
int secure_bits;
|
||||||
|
|
|
@ -47,7 +47,8 @@ $1.SyslogLevel, config_parse_log_level, 0,
|
||||||
$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix)
|
$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix)
|
||||||
$1.Capabilities, config_parse_exec_capabilities, 0, offsetof($1, exec_context)
|
$1.Capabilities, config_parse_exec_capabilities, 0, offsetof($1, exec_context)
|
||||||
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context)
|
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context)
|
||||||
$1.CapabilityBoundingSet, config_parse_bounding_set, 0, offsetof($1, exec_context.capability_bounding_set_drop)
|
$1.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof($1, exec_context.capability_bounding_set)
|
||||||
|
$1.AmbientCapabilities, config_parse_capability_set, 0, offsetof($1, exec_context.capability_ambient_set)
|
||||||
$1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec)
|
$1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec)
|
||||||
$1.NoNewPrivileges, config_parse_no_new_privileges, 0, offsetof($1, exec_context)
|
$1.NoNewPrivileges, config_parse_no_new_privileges, 0, offsetof($1, exec_context)
|
||||||
m4_ifdef(`HAVE_SECCOMP',
|
m4_ifdef(`HAVE_SECCOMP',
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "bus-internal.h"
|
#include "bus-internal.h"
|
||||||
#include "bus-util.h"
|
#include "bus-util.h"
|
||||||
#include "cap-list.h"
|
#include "cap-list.h"
|
||||||
|
#include "capability-util.h"
|
||||||
#include "cgroup.h"
|
#include "cgroup.h"
|
||||||
#include "conf-parser.h"
|
#include "conf-parser.h"
|
||||||
#include "cpu-set-util.h"
|
#include "cpu-set-util.h"
|
||||||
|
@ -1024,7 +1025,7 @@ int config_parse_exec_secure_bits(const char *unit,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int config_parse_bounding_set(
|
int config_parse_capability_set(
|
||||||
const char *unit,
|
const char *unit,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
unsigned line,
|
unsigned line,
|
||||||
|
@ -1036,8 +1037,8 @@ int config_parse_bounding_set(
|
||||||
void *data,
|
void *data,
|
||||||
void *userdata) {
|
void *userdata) {
|
||||||
|
|
||||||
uint64_t *capability_bounding_set_drop = data;
|
uint64_t *capability_set = data;
|
||||||
uint64_t capability_bounding_set, sum = 0;
|
uint64_t sum = 0, initial = 0;
|
||||||
bool invert = false;
|
bool invert = false;
|
||||||
const char *p;
|
const char *p;
|
||||||
|
|
||||||
|
@ -1051,10 +1052,9 @@ int config_parse_bounding_set(
|
||||||
rvalue++;
|
rvalue++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note that we store this inverted internally, since the
|
if (strcmp(lvalue, "CapabilityBoundingSet") == 0)
|
||||||
* kernel wants it like this. But we actually expose it
|
initial = CAP_ALL; /* initialized to all bits on */
|
||||||
* non-inverted everywhere to have a fully normalized
|
/* else "AmbientCapabilities" initialized to all bits off */
|
||||||
* interface. */
|
|
||||||
|
|
||||||
p = rvalue;
|
p = rvalue;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -1073,18 +1073,21 @@ int config_parse_bounding_set(
|
||||||
|
|
||||||
cap = capability_from_name(word);
|
cap = capability_from_name(word);
|
||||||
if (cap < 0) {
|
if (cap < 0) {
|
||||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding set, ignoring: %s", word);
|
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
sum |= ((uint64_t) UINT64_C(1)) << (uint64_t) cap;
|
sum |= ((uint64_t) UINT64_C(1)) << (uint64_t) cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
capability_bounding_set = invert ? ~sum : sum;
|
sum = invert ? ~sum : sum;
|
||||||
if (*capability_bounding_set_drop != 0 && capability_bounding_set != 0)
|
|
||||||
*capability_bounding_set_drop = ~(~*capability_bounding_set_drop | capability_bounding_set);
|
if (sum == 0 || *capability_set == initial)
|
||||||
|
/* "" or uninitialized data -> replace */
|
||||||
|
*capability_set = sum;
|
||||||
else
|
else
|
||||||
*capability_bounding_set_drop = ~capability_bounding_set;
|
/* previous data -> merge */
|
||||||
|
*capability_set |= sum;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -4002,7 +4005,7 @@ void unit_dump_config_items(FILE *f) {
|
||||||
{ config_parse_log_level, "LEVEL" },
|
{ config_parse_log_level, "LEVEL" },
|
||||||
{ config_parse_exec_capabilities, "CAPABILITIES" },
|
{ config_parse_exec_capabilities, "CAPABILITIES" },
|
||||||
{ config_parse_exec_secure_bits, "SECUREBITS" },
|
{ config_parse_exec_secure_bits, "SECUREBITS" },
|
||||||
{ config_parse_bounding_set, "BOUNDINGSET" },
|
{ config_parse_capability_set, "BOUNDINGSET" },
|
||||||
{ config_parse_limit, "LIMIT" },
|
{ config_parse_limit, "LIMIT" },
|
||||||
{ config_parse_unit_deps, "UNIT [...]" },
|
{ config_parse_unit_deps, "UNIT [...]" },
|
||||||
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
|
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
|
||||||
|
|
|
@ -56,7 +56,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit, const char *filename, uns
|
||||||
int config_parse_exec_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
int config_parse_exec_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
int config_parse_exec_capabilities(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
int config_parse_exec_capabilities(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
int config_parse_exec_secure_bits(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
int config_parse_exec_secure_bits(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
int config_parse_bounding_set(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
int config_parse_capability_set(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
int config_parse_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
int config_parse_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
int config_parse_bytes_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
int config_parse_bytes_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
int config_parse_sec_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
int config_parse_sec_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
|
|
|
@ -117,7 +117,7 @@ static usec_t arg_runtime_watchdog = 0;
|
||||||
static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
|
static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
|
||||||
static char **arg_default_environment = NULL;
|
static char **arg_default_environment = NULL;
|
||||||
static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
|
static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
|
||||||
static uint64_t arg_capability_bounding_set_drop = 0;
|
static uint64_t arg_capability_bounding_set = CAP_ALL;
|
||||||
static nsec_t arg_timer_slack_nsec = NSEC_INFINITY;
|
static nsec_t arg_timer_slack_nsec = NSEC_INFINITY;
|
||||||
static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
|
static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
|
||||||
static Set* arg_syscall_archs = NULL;
|
static Set* arg_syscall_archs = NULL;
|
||||||
|
@ -644,7 +644,7 @@ static int parse_config_file(void) {
|
||||||
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
|
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
|
||||||
{ "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
|
{ "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
|
||||||
{ "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
|
{ "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
|
||||||
{ "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop },
|
{ "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
|
||||||
#ifdef HAVE_SECCOMP
|
#ifdef HAVE_SECCOMP
|
||||||
{ "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
|
{ "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
|
||||||
#endif
|
#endif
|
||||||
|
@ -1631,14 +1631,14 @@ int main(int argc, char *argv[]) {
|
||||||
if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
|
if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
|
||||||
log_error_errno(errno, "Failed to adjust timer slack: %m");
|
log_error_errno(errno, "Failed to adjust timer slack: %m");
|
||||||
|
|
||||||
if (arg_capability_bounding_set_drop) {
|
if (!cap_test_all(arg_capability_bounding_set)) {
|
||||||
r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop);
|
r = capability_bounding_set_drop_usermode(arg_capability_bounding_set);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m");
|
log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m");
|
||||||
error_message = "Failed to drop capability bounding set of usermode helpers";
|
error_message = "Failed to drop capability bounding set of usermode helpers";
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true);
|
r = capability_bounding_set_drop(arg_capability_bounding_set, true);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_emergency_errno(r, "Failed to drop capability bounding set: %m");
|
log_emergency_errno(r, "Failed to drop capability bounding set: %m");
|
||||||
error_message = "Failed to drop capability bounding set";
|
error_message = "Failed to drop capability bounding set";
|
||||||
|
|
|
@ -3231,7 +3231,7 @@ int unit_patch_contexts(Unit *u) {
|
||||||
ec->no_new_privileges = true;
|
ec->no_new_privileges = true;
|
||||||
|
|
||||||
if (ec->private_devices)
|
if (ec->private_devices)
|
||||||
ec->capability_bounding_set_drop |= (uint64_t) 1ULL << (uint64_t) CAP_MKNOD;
|
ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_MKNOD);
|
||||||
}
|
}
|
||||||
|
|
||||||
cc = unit_get_cgroup_context(u);
|
cc = unit_get_cgroup_context(u);
|
||||||
|
|
|
@ -134,7 +134,7 @@ int import_fork_tar_x(const char *path, pid_t *ret) {
|
||||||
if (unshare(CLONE_NEWNET) < 0)
|
if (unshare(CLONE_NEWNET) < 0)
|
||||||
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
|
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
|
||||||
|
|
||||||
r = capability_bounding_set_drop(~retain, true);
|
r = capability_bounding_set_drop(retain, true);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
|
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ int import_fork_tar_c(const char *path, pid_t *ret) {
|
||||||
if (unshare(CLONE_NEWNET) < 0)
|
if (unshare(CLONE_NEWNET) < 0)
|
||||||
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
|
log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
|
||||||
|
|
||||||
r = capability_bounding_set_drop(~retain, true);
|
r = capability_bounding_set_drop(retain, true);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
|
log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
|
||||||
|
|
||||||
|
|
|
@ -1482,7 +1482,7 @@ static int setup_journal(const char *directory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int drop_capabilities(void) {
|
static int drop_capabilities(void) {
|
||||||
return capability_bounding_set_drop(~arg_retain, false);
|
return capability_bounding_set_drop(arg_retain, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int reset_audit_loginuid(void) {
|
static int reset_audit_loginuid(void) {
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <sys/capability.h>
|
#include <sys/capability.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -66,8 +67,9 @@ static void show_capabilities(void) {
|
||||||
cap_free(text);
|
cap_free(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int setup_tests(void) {
|
static int setup_tests(bool *run_ambient) {
|
||||||
struct passwd *nobody;
|
struct passwd *nobody;
|
||||||
|
int r;
|
||||||
|
|
||||||
nobody = getpwnam("nobody");
|
nobody = getpwnam("nobody");
|
||||||
if (!nobody) {
|
if (!nobody) {
|
||||||
|
@ -77,6 +79,18 @@ static int setup_tests(void) {
|
||||||
test_uid = nobody->pw_uid;
|
test_uid = nobody->pw_uid;
|
||||||
test_gid = nobody->pw_gid;
|
test_gid = nobody->pw_gid;
|
||||||
|
|
||||||
|
*run_ambient = false;
|
||||||
|
|
||||||
|
r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
|
||||||
|
|
||||||
|
/* There's support for PR_CAP_AMBIENT if the prctl() call
|
||||||
|
* succeeded or error code was something else than EINVAL. The
|
||||||
|
* EINVAL check should be good enough to rule out false
|
||||||
|
* positives. */
|
||||||
|
|
||||||
|
if (r >= 0 || errno != EINVAL)
|
||||||
|
*run_ambient = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +154,53 @@ static void test_have_effective_cap(void) {
|
||||||
assert_se(!have_effective_cap(CAP_CHOWN));
|
assert_se(!have_effective_cap(CAP_CHOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_update_inherited_set(void) {
|
||||||
|
cap_t caps;
|
||||||
|
uint64_t set = 0;
|
||||||
|
cap_flag_value_t fv;
|
||||||
|
|
||||||
|
caps = cap_get_proc();
|
||||||
|
assert_se(caps);
|
||||||
|
assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
|
||||||
|
assert(fv == CAP_CLEAR);
|
||||||
|
|
||||||
|
set = (UINT64_C(1) << CAP_CHOWN);
|
||||||
|
|
||||||
|
assert_se(!capability_update_inherited_set(caps, set));
|
||||||
|
assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
|
||||||
|
assert(fv == CAP_SET);
|
||||||
|
|
||||||
|
cap_free(caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_set_ambient_caps(void) {
|
||||||
|
cap_t caps;
|
||||||
|
uint64_t set = 0;
|
||||||
|
cap_flag_value_t fv;
|
||||||
|
|
||||||
|
caps = cap_get_proc();
|
||||||
|
assert_se(caps);
|
||||||
|
assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
|
||||||
|
assert(fv == CAP_CLEAR);
|
||||||
|
cap_free(caps);
|
||||||
|
|
||||||
|
assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
|
||||||
|
|
||||||
|
set = (UINT64_C(1) << CAP_CHOWN);
|
||||||
|
|
||||||
|
assert_se(!capability_ambient_set_apply(set, true));
|
||||||
|
|
||||||
|
caps = cap_get_proc();
|
||||||
|
assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
|
||||||
|
assert(fv == CAP_SET);
|
||||||
|
cap_free(caps);
|
||||||
|
|
||||||
|
assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
int r;
|
int r;
|
||||||
|
bool run_ambient;
|
||||||
|
|
||||||
log_parse_environment();
|
log_parse_environment();
|
||||||
log_open();
|
log_open();
|
||||||
|
@ -149,14 +208,19 @@ int main(int argc, char *argv[]) {
|
||||||
if (getuid() != 0)
|
if (getuid() != 0)
|
||||||
return EXIT_TEST_SKIP;
|
return EXIT_TEST_SKIP;
|
||||||
|
|
||||||
r = setup_tests();
|
r = setup_tests(&run_ambient);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -r;
|
return -r;
|
||||||
|
|
||||||
show_capabilities();
|
show_capabilities();
|
||||||
|
|
||||||
test_drop_privileges();
|
test_drop_privileges();
|
||||||
|
test_update_inherited_set();
|
||||||
|
|
||||||
fork_test(test_have_effective_cap);
|
fork_test(test_have_effective_cap);
|
||||||
|
|
||||||
|
if (run_ambient)
|
||||||
|
fork_test(test_set_ambient_caps);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
|
@ -224,6 +225,20 @@ static void test_exec_capabilityboundingset(Manager *m) {
|
||||||
test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
|
test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_exec_capabilityambientset(Manager *m) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* Check if the kernel has support for ambient capabilities. Run
|
||||||
|
* the tests only if that's the case. Clearing all ambient
|
||||||
|
* capabilities is fine, since we are expecting them to be unset
|
||||||
|
* in the first place for the tests. */
|
||||||
|
r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
|
||||||
|
if (r >= 0 || errno != EINVAL) {
|
||||||
|
test(m, "exec-capabilityambientset.service", 0, CLD_EXITED);
|
||||||
|
test(m, "exec-capabilityambientset-merge.service", 0, CLD_EXITED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void test_exec_privatenetwork(Manager *m) {
|
static void test_exec_privatenetwork(Manager *m) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -266,6 +281,7 @@ int main(int argc, char *argv[]) {
|
||||||
test_exec_umask,
|
test_exec_umask,
|
||||||
test_exec_runtimedirectory,
|
test_exec_runtimedirectory,
|
||||||
test_exec_capabilityboundingset,
|
test_exec_capabilityboundingset,
|
||||||
|
test_exec_capabilityambientset,
|
||||||
test_exec_oomscoreadjust,
|
test_exec_oomscoreadjust,
|
||||||
test_exec_ioschedulingclass,
|
test_exec_ioschedulingclass,
|
||||||
NULL,
|
NULL,
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "capability-util.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
|
@ -625,8 +626,8 @@ static uint64_t make_cap(int cap) {
|
||||||
return ((uint64_t) 1ULL << (uint64_t) cap);
|
return ((uint64_t) 1ULL << (uint64_t) cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_config_parse_bounding_set(void) {
|
static void test_config_parse_capability_set(void) {
|
||||||
/* int config_parse_bounding_set(
|
/* int config_parse_capability_set(
|
||||||
const char *unit,
|
const char *unit,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
unsigned line,
|
unsigned line,
|
||||||
|
@ -638,38 +639,38 @@ static void test_config_parse_bounding_set(void) {
|
||||||
void *data,
|
void *data,
|
||||||
void *userdata) */
|
void *userdata) */
|
||||||
int r;
|
int r;
|
||||||
uint64_t capability_bounding_set_drop = 0;
|
uint64_t capability_bounding_set = 0;
|
||||||
|
|
||||||
r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
|
r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
|
||||||
"CapabilityBoundingSet", 0, "CAP_NET_RAW",
|
"CapabilityBoundingSet", 0, "CAP_NET_RAW",
|
||||||
&capability_bounding_set_drop, NULL);
|
&capability_bounding_set, NULL);
|
||||||
assert_se(r >= 0);
|
assert_se(r >= 0);
|
||||||
assert_se(capability_bounding_set_drop == ~make_cap(CAP_NET_RAW));
|
assert_se(capability_bounding_set == make_cap(CAP_NET_RAW));
|
||||||
|
|
||||||
r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
|
r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
|
||||||
"CapabilityBoundingSet", 0, "CAP_NET_ADMIN",
|
"CapabilityBoundingSet", 0, "CAP_NET_ADMIN",
|
||||||
&capability_bounding_set_drop, NULL);
|
&capability_bounding_set, NULL);
|
||||||
assert_se(r >= 0);
|
assert_se(r >= 0);
|
||||||
assert_se(capability_bounding_set_drop == ~(make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
|
assert_se(capability_bounding_set == (make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
|
||||||
|
|
||||||
r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
|
r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
|
||||||
"CapabilityBoundingSet", 0, "",
|
"CapabilityBoundingSet", 0, "",
|
||||||
&capability_bounding_set_drop, NULL);
|
&capability_bounding_set, NULL);
|
||||||
assert_se(r >= 0);
|
assert_se(r >= 0);
|
||||||
assert_se(capability_bounding_set_drop == ~((uint64_t) 0ULL));
|
assert_se(capability_bounding_set == UINT64_C(0));
|
||||||
|
|
||||||
r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
|
r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
|
||||||
"CapabilityBoundingSet", 0, "~",
|
"CapabilityBoundingSet", 0, "~",
|
||||||
&capability_bounding_set_drop, NULL);
|
&capability_bounding_set, NULL);
|
||||||
assert_se(r >= 0);
|
assert_se(r >= 0);
|
||||||
assert_se(capability_bounding_set_drop == (uint64_t) 0ULL);
|
assert_se(cap_test_all(capability_bounding_set));
|
||||||
|
|
||||||
capability_bounding_set_drop = 0;
|
capability_bounding_set = 0;
|
||||||
r = config_parse_bounding_set(NULL, "fake", 1, "section", 1,
|
r = config_parse_capability_set(NULL, "fake", 1, "section", 1,
|
||||||
"CapabilityBoundingSet", 0, " 'CAP_NET_RAW' WAT_CAP??? CAP_NET_ADMIN CAP'_trailing_garbage",
|
"CapabilityBoundingSet", 0, " 'CAP_NET_RAW' WAT_CAP??? CAP_NET_ADMIN CAP'_trailing_garbage",
|
||||||
&capability_bounding_set_drop, NULL);
|
&capability_bounding_set, NULL);
|
||||||
assert_se(r >= 0);
|
assert_se(r >= 0);
|
||||||
assert_se(capability_bounding_set_drop == ~(make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
|
assert_se(capability_bounding_set == (make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_config_parse_rlimit(void) {
|
static void test_config_parse_rlimit(void) {
|
||||||
|
@ -829,7 +830,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
r = test_unit_file_get_set();
|
r = test_unit_file_get_set();
|
||||||
test_config_parse_exec();
|
test_config_parse_exec();
|
||||||
test_config_parse_bounding_set();
|
test_config_parse_capability_set();
|
||||||
test_config_parse_rlimit();
|
test_config_parse_rlimit();
|
||||||
test_config_parse_pass_environ();
|
test_config_parse_pass_environ();
|
||||||
test_load_env_file_1();
|
test_load_env_file_1();
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Test for AmbientCapabilities
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000003000"'
|
||||||
|
Type=oneshot
|
||||||
|
User=nobody
|
||||||
|
AmbientCapabilities=CAP_NET_ADMIN
|
||||||
|
AmbientCapabilities=CAP_NET_RAW
|
8
test/test-execute/exec-capabilityambientset.service
Normal file
8
test/test-execute/exec-capabilityambientset.service
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Test for AmbientCapabilities
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000003000"'
|
||||||
|
Type=oneshot
|
||||||
|
User=nobody
|
||||||
|
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
|
Loading…
Reference in a new issue