1
0
mirror of https://github.com/systemd/systemd synced 2025-11-19 08:44:44 +01:00

Compare commits

..

No commits in common. "1835ce2f045815f70849c29426c0c9c0f1a5af9c" and "8eefd0f4debc0bcfeea89dd39c43e3318f3f7ae7" have entirely different histories.

25 changed files with 449 additions and 313 deletions

View File

@ -30,6 +30,7 @@ PACKAGES=(
libarchive-dev
libblkid-dev
libbpf-dev
libcap-dev
libcurl4-gnutls-dev
libfdisk-dev
libfido2-dev

1
README
View File

@ -212,6 +212,7 @@ REQUIREMENTS:
glibc >= 2.31
libxcrypt or glibc (<= 2.38 built with --enable-crypt)
libcap
libmount >= 2.30 (from util-linux)
(util-linux *must* be built without --enable-libmount-support-mtab)
libseccomp >= 2.3.1 (optional)

4
TODO
View File

@ -1640,6 +1640,10 @@ Features:
work for ECDSA keys since their signatures contain a random component, but
will work for RSA and Ed25519 keys.
* drop dependency on libcap, replace by direct syscalls based on
CapabilityQuintet we already have. (This likely allows us to drop libcap
dep in the base OS image)
* userdbd: implement an additional varlink service socket that provides the
host user db in restricted form, then allow this to be bind mounted into
sandboxed environments that want the host database in minimal form. All

View File

@ -20,6 +20,8 @@ If such modularity is required that goes beyond what we support in the configure
For example: if all you want is the tmpfiles tool, then build systemd normally, and list only /usr/bin/systemd-tmpfiles in the .spec file for your RPM package.
This is simple to do, allows you to pick exactly what you need, but requires a larger number of build dependencies (but not runtime dependencies).
2. If you want to reduce the build time dependencies (though only dbus is needed as build time deps) and you know the specific component you are interested in doesn't need it, then create a dummy .pc file for that dependency (i.e. basically empty), and configure systemd with PKG_CONFIG_PATH set to the path of these dummy .pc files. Then, build only the few bits you need with "make foobar", where foobar is the file you need.
2. If you want to reduce the build time dependencies (though only dbus and libcap are needed as build time deps) and you know the specific component you are interested in doesn't need it, then create a dummy .pc file for that dependency (i.e. basically empty), and configure systemd with PKG_CONFIG_PATH set to the path of these dummy .pc files. Then, build only the few bits you need with "make foobar", where foobar is the file you need.
We are open to merging patches for the build system that make more "fringe" components of systemd optional. However, please be aware that in order to keep the complexity of our build system small and its readability high, and to make our lives easier, we will not accept patches that make the minimal core components optional, i.e. systemd itself, journald and udevd.
Note that the .pc file trick mentioned above currently doesn't work for libcap, since libcap doesn't provide a .pc file. We invite you to go ahead and post a patch to libcap upstream to get this corrected. We'll happily change our build system to look for that .pc file then. (a .pc file has been sent to upstream by Bryan Kadzban).

View File

@ -225,7 +225,7 @@
<constant>esp</constant>, <constant>xbootldr</constant>, <constant>tmp</constant>,
<constant>var</constant>.</para>
<xi:include href="system-only.xml" xpointer="singular"/>
<xi:include href="system-or-user-ns-mountfsd.xml" xpointer="singular"/>
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
</varlistentry>

View File

@ -692,6 +692,7 @@ conf.set('GPERF_LEN_TYPE', gperf_len_type,
foreach header : [
'crypt.h',
'sys/capability.h',
]
if not cc.has_header(header)
@ -1000,6 +1001,7 @@ threads = dependency('threads')
librt = cc.find_library('rt')
libm = cc.find_library('m')
libdl = cc.find_library('dl')
libcap = dependency('libcap')
# On some architectures, libatomic is required. But on some installations,
# it is found, but actual linking fails. So let's try to use it opportunistically.
@ -2139,7 +2141,8 @@ if static_libsystemd != 'false'
install_tag: 'libsystemd',
install_dir : libdir,
pic : static_libsystemd_pic,
dependencies : [libdl,
dependencies : [libcap,
libdl,
libgcrypt_cflags,
liblz4_cflags,
libm,

View File

@ -20,7 +20,7 @@ static const struct capability_name* lookup_capability(register const char *str,
const char* capability_to_name(int id) {
if (id < 0)
return NULL;
if ((unsigned) id >= capability_list_length())
if (id >= capability_list_length())
return NULL;
return capability_names[id];
@ -70,8 +70,8 @@ int capability_from_name(const char *name) {
* value larger than the last known capability. This is different from cap_last_cap() which returns the
* highest supported capability. Hence with everyone agreeing on the same capabilities list, this function
* will return one higher than cap_last_cap(). */
unsigned capability_list_length(void) {
return MIN((unsigned) ELEMENTSOF(capability_names), (unsigned) (CAP_LIMIT + 1));
int capability_list_length(void) {
return MIN((int) ELEMENTSOF(capability_names), CAP_LIMIT + 1);
}
int capability_set_to_string(uint64_t set, char **ret) {

View File

@ -13,7 +13,7 @@ const char* capability_to_string(int id, char buf[static CAPABILITY_TO_STRING_MA
#define CAPABILITY_TO_STRING(id) capability_to_string(id, (char[CAPABILITY_TO_STRING_MAX]) {})
int capability_from_name(const char *name);
unsigned capability_list_length(void);
int capability_list_length(void);
int capability_set_to_string(uint64_t set, char **ret);
int capability_set_to_string_negative(uint64_t set, char **ret);

View File

@ -4,67 +4,35 @@
#include <stdatomic.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "alloc-util.h"
#include "bitfield.h"
#include "capability-list.h"
#include "capability-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
#include "logarithm.h"
#include "parse-util.h"
#include "pidref.h"
#include "process-util.h"
#include "stat-util.h"
#include "user-util.h"
int capability_get(CapabilityQuintet *ret) {
assert(ret);
int have_effective_cap(int value) {
_cleanup_cap_free_ cap_t cap = NULL;
cap_flag_value_t fv = CAP_CLEAR; /* To avoid false-positive use-of-uninitialized-value error reported
* by fuzzers. */
struct __user_cap_header_struct hdr = {
.version = _LINUX_CAPABILITY_VERSION_3,
.pid = getpid_cached(),
};
assert_cc(_LINUX_CAPABILITY_U32S_3 == 2);
struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3];
if (syscall(SYS_capget, &hdr, data) < 0)
cap = cap_get_proc();
if (!cap)
return -errno;
*ret = (CapabilityQuintet) {
.effective = (uint64_t) data[0].effective | ((uint64_t) data[1].effective << 32),
.bounding = UINT64_MAX,
.inheritable = (uint64_t) data[0].inheritable | ((uint64_t) data[1].inheritable << 32),
.permitted = (uint64_t) data[0].permitted | ((uint64_t) data[1].permitted << 32),
.ambient = UINT64_MAX,
};
return 0;
}
if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
return -errno;
static int capability_apply(const CapabilityQuintet *q) {
assert(q);
struct __user_cap_header_struct hdr = {
.version = _LINUX_CAPABILITY_VERSION_3,
.pid = getpid_cached(),
};
struct __user_cap_data_struct data[_LINUX_CAPABILITY_U32S_3] = {
{
.effective = (uint32_t) (q->effective & UINT32_MAX),
.inheritable = (uint32_t) (q->inheritable & UINT32_MAX),
.permitted = (uint32_t) (q->permitted & UINT32_MAX),
},
{
.effective = (uint32_t) (q->effective >> 32),
.inheritable = (uint32_t) (q->inheritable >> 32),
.permitted = (uint32_t) (q->permitted >> 32),
},
};
return RET_NERRNO(syscall(SYS_capset, &hdr, data));
return fv == CAP_SET;
}
unsigned cap_last_cap(void) {
@ -121,33 +89,25 @@ unsigned cap_last_cap(void) {
return c;
}
int have_effective_cap(unsigned cap) {
CapabilityQuintet q;
int r;
int capability_update_inherited_set(cap_t caps, uint64_t set) {
/* Add capabilities in the set to the inherited caps, drops capabilities not in the set.
* Do not apply them yet. */
assert(cap <= CAP_LIMIT);
for (unsigned i = 0; i <= cap_last_cap(); i++) {
cap_flag_value_t flag = set & (UINT64_C(1) << i) ? CAP_SET : CAP_CLEAR;
cap_value_t v;
r = capability_get(&q);
if (r < 0)
return r;
v = (cap_value_t) i;
return BIT_SET(q.effective, cap);
if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, flag) < 0)
return -errno;
}
int have_inheritable_cap(unsigned cap) {
CapabilityQuintet q;
int r;
assert(cap <= CAP_LIMIT);
r = capability_get(&q);
if (r < 0)
return r;
return BIT_SET(q.inheritable, cap);
return 0;
}
int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
_cleanup_cap_free_ cap_t caps = NULL;
int r;
/* Remove capabilities requested in ambient set, but not in the bounding set */
@ -165,20 +125,19 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
/* Add the capabilities to the ambient set (an possibly also the inheritable set) */
if (also_inherit) {
CapabilityQuintet q;
caps = cap_get_proc();
if (!caps)
return -errno;
r = capability_get(&q);
r = capability_update_inherited_set(caps, set);
if (r < 0)
return r;
return -errno;
q.inheritable = set;
r = capability_apply(&q);
if (r < 0)
return r;
if (cap_set_proc(caps) < 0)
return -errno;
}
for (unsigned i = 0; i <= cap_last_cap(); i++)
for (unsigned i = 0; i <= cap_last_cap(); i++) {
if (BIT_SET(set, i)) {
/* Add the capability to the ambient set. */
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0)
@ -192,85 +151,107 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, i, 0, 0) < 0)
return -errno;
}
}
return 0;
}
int capability_gain_cap_setpcap(void) {
CapabilityQuintet q;
int r;
int capability_gain_cap_setpcap(cap_t *ret_before_caps) {
_cleanup_cap_free_ cap_t caps = NULL;
cap_flag_value_t fv;
caps = cap_get_proc();
if (!caps)
return -errno;
r = capability_get(&q);
if (r < 0)
return r;
if (cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
return -errno;
if (BIT_SET(q.effective, CAP_SETPCAP))
return 1; /* We already have capability. */
if (fv != CAP_SET) {
_cleanup_cap_free_ cap_t temp_cap = NULL;
static const cap_value_t v = CAP_SETPCAP;
SET_BIT(q.effective, CAP_SETPCAP);
temp_cap = cap_dup(caps);
if (!temp_cap)
return -errno;
r = capability_apply(&q);
if (r < 0) {
/* If we didn't manage to acquire the CAP_SETPCAP bit, we continue anyway, after all this
* just means we'll fail later, when we actually intend to drop some capabilities or try to
* set securebits. */
log_debug_errno(r, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m");
return 0;
if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0)
return -errno;
if (cap_set_proc(temp_cap) < 0)
log_debug_errno(errno, "Can't acquire effective CAP_SETPCAP bit, ignoring: %m");
/* If we didn't manage to acquire the CAP_SETPCAP bit, we continue anyway, after all this just means
* we'll fail later, when we actually intend to drop some capabilities or try to set securebits. */
}
if (ret_before_caps)
/* Return the capabilities as they have been before setting CAP_SETPCAP */
*ret_before_caps = TAKE_PTR(caps);
return 1; /* acquired */
return 0;
}
int capability_bounding_set_drop(uint64_t keep, bool right_now) {
int k, r;
_cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL;
int r;
/* If we are run as PID 1 we will lack CAP_SETPCAP by default in the effective set (yes, the kernel
* drops that when executing init!), so get it back temporarily so that we can call PR_CAPBSET_DROP. */
/* If we are run as PID 1 we will lack CAP_SETPCAP by default
* in the effective set (yes, the kernel drops that when
* executing init!), so get it back temporarily so that we can
* call PR_CAPBSET_DROP. */
CapabilityQuintet q;
r = capability_get(&q);
r = capability_gain_cap_setpcap(&before_cap);
if (r < 0)
return r;
CapabilityQuintet saved = q;
r = capability_gain_cap_setpcap();
if (r < 0)
return r;
after_cap = cap_dup(before_cap);
if (!after_cap)
return -errno;
for (unsigned i = 0; i <= cap_last_cap(); i++) {
if (BIT_SET(keep, i))
cap_value_t v;
if ((keep & (UINT64_C(1) << i)))
continue;
/* Drop it from the bounding set */
if (prctl(PR_CAPBSET_DROP, i) < 0) {
r = -errno;
/* If dropping the capability failed, let's see if we didn't have it in the first
* place. If so, continue anyway, as dropping a capability we didn't have in the
* first place doesn't really matter anyway. */
/* If dropping the capability failed, let's see if we didn't have it in the first place. If so,
* continue anyway, as dropping a capability we didn't have in the first place doesn't really
* matter anyway. */
if (prctl(PR_CAPBSET_READ, i) != 0)
goto finish;
}
v = (cap_value_t) i;
/* Also drop it from the inheritable set, so that anything we exec() loses the capability for
* good. */
CLEAR_BIT(q.inheritable, i);
/* Also drop it from the inheritable set, so
* that anything we exec() loses the
* capability for good. */
if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) {
r = -errno;
goto finish;
}
/* If we shall apply this right now drop it also from our own capability sets. */
/* If we shall apply this right now drop it
* also from our own capability sets. */
if (right_now) {
CLEAR_BIT(q.effective, i);
CLEAR_BIT(q.permitted, i);
if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 ||
cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) {
r = -errno;
goto finish;
}
}
}
r = 0;
finish:
k = capability_apply(&q);
if (k < 0)
if (cap_set_proc(after_cap) < 0) {
/* If there are no actual changes anyway then let's ignore this error. */
if (!capability_quintet_equal(&q, &saved))
return k;
if (cap_compare(before_cap, after_cap) != 0)
r = -errno;
}
return r;
}
@ -326,7 +307,7 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
if (setresgid(gid, gid, gid) < 0)
return log_error_errno(errno, "Failed to change group ID: %m");
r = maybe_setgroups(/* size= */ 0, /* list= */ NULL);
r = maybe_setgroups(0, NULL);
if (r < 0)
return log_error_errno(r, "Failed to drop auxiliary groups list: %m");
@ -344,54 +325,64 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
/* Drop all caps from the bounding set (as well as the inheritable/permitted/effective sets), except
* the ones we want to keep */
r = capability_bounding_set_drop(keep_capabilities, /* right_now= */ true);
r = capability_bounding_set_drop(keep_capabilities, true);
if (r < 0)
return log_error_errno(r, "Failed to drop capabilities: %m");
/* Now upgrade the permitted caps we still kept to effective caps */
if (keep_capabilities != 0) {
CapabilityQuintet q = {
.effective = keep_capabilities,
.permitted = keep_capabilities,
};
cap_value_t bits[log2u64(keep_capabilities) + 1];
_cleanup_cap_free_ cap_t d = NULL;
unsigned i, j = 0;
r = capability_apply(&q);
if (r < 0)
return log_error_errno(r, "Failed to increase capabilities: %m");
d = cap_init();
if (!d)
return log_oom();
for (i = 0; i < ELEMENTSOF(bits); i++)
if (keep_capabilities & (1ULL << i))
bits[j++] = i;
/* use enough bits */
assert(i == 64 || (keep_capabilities >> i) == 0);
/* don't use too many bits */
assert(keep_capabilities & (UINT64_C(1) << (i - 1)));
if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 ||
cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0)
return log_error_errno(errno, "Failed to enable capabilities bits: %m");
if (cap_set_proc(d) < 0)
return log_error_errno(errno, "Failed to increase capabilities: %m");
}
return 0;
}
static int change_capability(unsigned cap, bool b) {
CapabilityQuintet q;
int r;
static int change_capability(cap_value_t cv, cap_flag_value_t flag) {
_cleanup_cap_free_ cap_t tmp_cap = NULL;
assert(cap <= CAP_LIMIT);
tmp_cap = cap_get_proc();
if (!tmp_cap)
return -errno;
r = capability_get(&q);
if (r < 0)
return r;
if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, flag) < 0) ||
(cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, flag) < 0) ||
(cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, flag) < 0))
return -errno;
if (b) {
SET_BIT(q.effective, cap);
SET_BIT(q.permitted, cap);
SET_BIT(q.inheritable, cap);
} else {
CLEAR_BIT(q.effective, cap);
CLEAR_BIT(q.permitted, cap);
CLEAR_BIT(q.inheritable, cap);
if (cap_set_proc(tmp_cap) < 0)
return -errno;
return 0;
}
return capability_apply(&q);
int drop_capability(cap_value_t cv) {
return change_capability(cv, CAP_CLEAR);
}
int drop_capability(unsigned cap) {
return change_capability(cap, false);
}
int keep_capability(unsigned cap) {
return change_capability(cap, true);
int keep_capability(cap_value_t cv) {
return change_capability(cv, CAP_SET);
}
bool capability_quintet_mangle(CapabilityQuintet *q) {
@ -423,73 +414,161 @@ bool capability_quintet_mangle(CapabilityQuintet *q) {
}
int capability_quintet_enforce(const CapabilityQuintet *q) {
CapabilityQuintet c;
bool modified = false;
_cleanup_cap_free_ cap_t c = NULL, modified = NULL;
int r;
if (q->ambient != CAP_MASK_UNSET ||
q->inheritable != CAP_MASK_UNSET ||
q->permitted != CAP_MASK_UNSET ||
q->effective != CAP_MASK_UNSET) {
r = capability_get(&c);
if (r < 0)
return r;
}
if (q->ambient != CAP_MASK_UNSET) {
bool changed = false;
c = cap_get_proc();
if (!c)
return -errno;
/* In order to raise the ambient caps set we first need to raise the matching
* inheritable + permitted cap */
if (!FLAGS_SET(c.permitted, q->ambient) ||
!FLAGS_SET(c.inheritable, q->ambient)) {
for (unsigned i = 0; i <= cap_last_cap(); i++) {
uint64_t m = UINT64_C(1) << i;
cap_value_t cv = (cap_value_t) i;
cap_flag_value_t old_value_inheritable, old_value_permitted;
c.permitted |= q->ambient;
c.inheritable |= q->ambient;
if ((q->ambient & m) == 0)
continue;
r = capability_apply(&c);
if (r < 0)
return r;
if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value_inheritable) < 0)
return -errno;
if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value_permitted) < 0)
return -errno;
if (old_value_inheritable == CAP_SET && old_value_permitted == CAP_SET)
continue;
if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, CAP_SET) < 0)
return -errno;
if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, CAP_SET) < 0)
return -errno;
changed = true;
}
r = capability_ambient_set_apply(q->ambient, /* also_inherit= */ false);
if (changed)
if (cap_set_proc(c) < 0)
return -errno;
r = capability_ambient_set_apply(q->ambient, false);
if (r < 0)
return r;
}
if (q->inheritable != CAP_MASK_UNSET || q->permitted != CAP_MASK_UNSET || q->effective != CAP_MASK_UNSET) {
if (!FLAGS_SET(c.effective, q->effective) ||
!FLAGS_SET(c.permitted, q->permitted) ||
!FLAGS_SET(c.inheritable, q->inheritable)) {
bool changed = false;
c.effective |= q->effective;
c.permitted |= q->permitted;
c.inheritable |= q->inheritable;
if (!c) {
c = cap_get_proc();
if (!c)
return -errno;
}
for (unsigned i = 0; i <= cap_last_cap(); i++) {
uint64_t m = UINT64_C(1) << i;
cap_value_t cv = (cap_value_t) i;
if (q->inheritable != CAP_MASK_UNSET) {
cap_flag_value_t old_value, new_value;
if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value) < 0) {
if (errno == EINVAL) /* If the kernel knows more caps than this
* version of libcap, then this will return
* EINVAL. In that case, simply ignore it,
* pretend it doesn't exist. */
continue;
return -errno;
}
new_value = (q->inheritable & m) ? CAP_SET : CAP_CLEAR;
if (old_value != new_value) {
changed = true;
if (cap_set_flag(c, CAP_INHERITABLE, 1, &cv, new_value) < 0)
return -errno;
}
}
if (q->permitted != CAP_MASK_UNSET) {
cap_flag_value_t old_value, new_value;
if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value) < 0) {
if (errno == EINVAL)
continue;
return -errno;
}
new_value = (q->permitted & m) ? CAP_SET : CAP_CLEAR;
if (old_value != new_value) {
changed = true;
if (cap_set_flag(c, CAP_PERMITTED, 1, &cv, new_value) < 0)
return -errno;
}
}
if (q->effective != CAP_MASK_UNSET) {
cap_flag_value_t old_value, new_value;
if (cap_get_flag(c, cv, CAP_EFFECTIVE, &old_value) < 0) {
if (errno == EINVAL)
continue;
return -errno;
}
new_value = (q->effective & m) ? CAP_SET : CAP_CLEAR;
if (old_value != new_value) {
changed = true;
if (cap_set_flag(c, CAP_EFFECTIVE, 1, &cv, new_value) < 0)
return -errno;
}
}
}
if (changed) {
/* In order to change the bounding caps, we need to keep CAP_SETPCAP for a bit
* longer. Let's add it to our list hence for now. */
if (q->bounding != CAP_MASK_UNSET) {
cap_value_t cv = CAP_SETPCAP;
modified = cap_dup(c);
if (!modified)
return -ENOMEM;
if (cap_set_flag(modified, CAP_PERMITTED, 1, &cv, CAP_SET) < 0)
return -errno;
if (cap_set_flag(modified, CAP_EFFECTIVE, 1, &cv, CAP_SET) < 0)
return -errno;
if (cap_compare(modified, c) == 0) {
/* No change? then drop this nonsense again */
cap_free(modified);
modified = NULL;
}
}
/* Now, let's enforce the caps for the first time. Note that this is where we acquire
* caps in any of the sets we currently don't have. We have to do this before
* dropping the bounding caps below, since at that point we can never acquire new
* caps in inherited/permitted/effective anymore, but only lose them.
*
* In order to change the bounding caps, we need to keep CAP_SETPCAP for a bit
* longer. Let's add it to our list hence for now. */
if (q->bounding != CAP_MASK_UNSET &&
(!BIT_SET(c.effective, CAP_SETPCAP) || !BIT_SET(c.permitted, CAP_SETPCAP))) {
CapabilityQuintet tmp = c;
SET_BIT(c.effective, CAP_SETPCAP);
SET_BIT(c.permitted, CAP_SETPCAP);
modified = true;
r = capability_apply(&tmp);
} else
r = capability_apply(&c);
if (r < 0)
return r;
* caps in inherited/permitted/effective anymore, but only lose them. */
if (cap_set_proc(modified ?: c) < 0)
return -errno;
}
}
if (q->bounding != CAP_MASK_UNSET) {
r = capability_bounding_set_drop(q->bounding, /* right_now= */ false);
r = capability_bounding_set_drop(q->bounding, false);
if (r < 0)
return r;
}
@ -498,11 +577,9 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
* we have already set only in the CAP_SETPCAP bit, which we needed for dropping the bounding
* bits. This call only undoes bits and doesn't acquire any which means the bounding caps don't
* matter. */
if (modified) {
r = capability_apply(&c);
if (r < 0)
return r;
}
if (modified)
if (cap_set_proc(c) < 0)
return -errno;
return 0;
}

View File

@ -1,13 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <linux/capability.h> /* IWYU pragma: export */
#include <sys/capability.h> /* IWYU pragma: export */
#include "basic-forward.h"
/* Special marker used when storing a capabilities mask as "unset". This would need to be updated as soon as
* Linux learns more than 63 caps. */
#define CAP_MASK_UNSET UINT64_MAX
assert_cc(CAP_LAST_CAP < 64);
/* All possible capabilities bits on */
#define CAP_MASK_ALL UINT64_C(0x7fffffffffffffff)
@ -15,7 +16,35 @@
/* The largest capability we can deal with, given we want to be able to store cap masks in uint64_t but still
* be able to use UINT64_MAX as indicator for "not set". The latter makes capability 63 unavailable. */
#define CAP_LIMIT 62
assert_cc(CAP_LAST_CAP <= CAP_LIMIT);
static inline bool capability_is_set(uint64_t v) {
return v != CAP_MASK_UNSET;
}
unsigned cap_last_cap(void);
int have_effective_cap(int value);
int capability_gain_cap_setpcap(cap_t *ret_before_caps);
int capability_bounding_set_drop(uint64_t keep, bool right_now);
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_capability(cap_value_t cv);
int keep_capability(cap_value_t cv);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(cap_t, cap_free, NULL);
#define _cleanup_cap_free_ _cleanup_(cap_freep)
static inline uint64_t all_capabilities(void) {
return UINT64_MAX >> (63 - cap_last_cap());
}
static inline bool cap_test_all(uint64_t caps) {
return FLAGS_SET(caps, all_capabilities());
}
/* Identical to linux/capability.h's CAP_TO_MASK(), but uses an unsigned 1U instead of a signed 1 for shifting left, in
* order to avoid complaints about shifting a signed int left by 31 bits, which would make it negative. */
@ -30,42 +59,7 @@ typedef struct CapabilityQuintet {
uint64_t ambient;
} CapabilityQuintet;
#define CAPABILITY_QUINTET_NULL \
(const CapabilityQuintet) { \
CAP_MASK_UNSET, \
CAP_MASK_UNSET, \
CAP_MASK_UNSET, \
CAP_MASK_UNSET, \
CAP_MASK_UNSET, \
}
static inline bool capability_is_set(uint64_t v) {
return v != CAP_MASK_UNSET;
}
int capability_get(CapabilityQuintet *ret);
unsigned cap_last_cap(void);
int have_effective_cap(unsigned cap);
int have_inheritable_cap(unsigned cap);
int capability_gain_cap_setpcap(void);
int capability_bounding_set_drop(uint64_t keep, bool right_now);
int capability_bounding_set_drop_usermode(uint64_t keep);
int capability_ambient_set_apply(uint64_t set, bool also_inherit);
int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
int drop_capability(unsigned cap);
int keep_capability(unsigned cap);
static inline uint64_t all_capabilities(void) {
return UINT64_MAX >> (63 - cap_last_cap());
}
static inline bool cap_test_all(uint64_t caps) {
return FLAGS_SET(caps, all_capabilities());
}
#define CAPABILITY_QUINTET_NULL (const CapabilityQuintet) { CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET }
static inline bool capability_quintet_is_set(const CapabilityQuintet *q) {
return capability_is_set(q->effective) ||
@ -83,14 +77,6 @@ static inline bool capability_quintet_is_fully_set(const CapabilityQuintet *q) {
capability_is_set(q->ambient);
}
static inline bool capability_quintet_equal(const CapabilityQuintet *a, const CapabilityQuintet *b) {
return a->effective == b->effective &&
a->bounding == b->bounding &&
a->inheritable == b->inheritable &&
a->permitted == b->permitted &&
a->ambient == b->ambient;
}
/* Mangles the specified caps quintet taking the current bounding set into account:
* drops all caps from all five sets if our bounding set doesn't allow them.
* Returns true if the quintet was modified. */

View File

@ -202,7 +202,8 @@ libbasic_static = static_library(
fundamental_sources,
include_directories : basic_includes,
implicit_include_directories : false,
dependencies : [libdl,
dependencies : [libcap,
libdl,
libgcrypt_cflags,
liblz4_cflags,
libm,

View File

@ -6165,7 +6165,7 @@ int exec_invoke(
*
* Hence there is no security impact to raise it in the effective set before execve
*/
r = capability_gain_cap_setpcap();
r = capability_gain_cap_setpcap(/* ret_before_caps = */ NULL);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return log_error_errno(r, "Failed to gain CAP_SETPCAP for setting secure bits");

View File

@ -18,3 +18,4 @@ URL: {{PROJECT_URL}}
Version: {{PROJECT_VERSION}}
Libs: -L${libdir} -lsystemd
Cflags: -I${includedir}
Requires.private: libcap

View File

@ -18,3 +18,4 @@ Version: {{PROJECT_VERSION}}
Libs: -L${libdir} -ludev
Libs.private: -lrt -pthread
Cflags: -I${includedir}
Requires.private: libcap

View File

@ -449,9 +449,7 @@ static int vl_method_mount_image(
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
(p.verity_sharing ? DISSECT_IMAGE_VERITY_SHARE : 0) |
/* Maybe the image is a bare filesystem. Note that this requires privileges, as it is
* classified by the policy as an 'unprotected' image and will be refused otherwise. */
DISSECT_IMAGE_NO_PARTITION_TABLE |
(p.verity_data_fd_idx != UINT_MAX ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0) |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
/* Let's see if we have acquired the privilege to mount untrusted images already */

View File

@ -480,7 +480,7 @@ static int parse_capability_spec(const char *spec, uint64_t *ret_mask) {
break;
if (streq(t, "help")) {
for (unsigned i = 0; i < capability_list_length(); i++) {
for (int i = 0; i < capability_list_length(); i++) {
const char *name;
name = capability_to_name(i);

View File

@ -8,17 +8,17 @@
#
# Minimum rpm version supported: 4.14.0
%transfiletriggerin -P 900900 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}}/ /etc/systemd/system/
%transfiletriggerin -P 900900 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
-- This script will run after any package is initially installed or
-- upgraded. We care about the case where a package is initially
-- installed, because other cases are covered by the *un scriptlets,
-- so sometimes we will reload needlessly.
assert(rpm.execute("{{SYSTEMD_UPDATE_HELPER_PATH}}", "system-reload-restart"))
%transfiletriggerin -P 900899 -p <lua> -- {{USER_DATA_UNIT_DIR}}/ /etc/systemd/user/
%transfiletriggerin -P 900899 -p <lua> -- {{USER_DATA_UNIT_DIR}} /etc/systemd/user
assert(rpm.execute("{{SYSTEMD_UPDATE_HELPER_PATH}}", "user-reload-restart"))
%transfiletriggerpostun -P 1000100 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}}/ /etc/systemd/system/
%transfiletriggerpostun -P 1000100 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
-- On removal, we need to run daemon-reload after any units have been
-- removed.
-- On upgrade, we need to run daemon-reload after any new unit files
@ -26,53 +26,53 @@ assert(rpm.execute("{{SYSTEMD_UPDATE_HELPER_PATH}}", "user-reload-restart"))
-- executed.
assert(rpm.execute("{{SYSTEMD_UPDATE_HELPER_PATH}}", "system-reload"))
%transfiletriggerpostun -P 1000100 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}}/ /etc/systemd/system/
%transfiletriggerpostun -P 1000100 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
-- Execute daemon-reload in user managers.
assert(rpm.execute("{{SYSTEMD_UPDATE_HELPER_PATH}}", "user-reload"))
%transfiletriggerpostun -P 10000 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}}/ /etc/systemd/system/
%transfiletriggerpostun -P 10000 -p <lua> -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
-- We restart remaining system services that should be restarted here.
assert(rpm.execute("{{SYSTEMD_UPDATE_HELPER_PATH}}", "system-restart"))
%transfiletriggerpostun -P 9999 -p <lua> -- {{USER_DATA_UNIT_DIR}}/ /etc/systemd/user/
%transfiletriggerpostun -P 9999 -p <lua> -- {{USER_DATA_UNIT_DIR}} /etc/systemd/user
-- We restart remaining user services that should be restarted here.
assert(rpm.execute("{{SYSTEMD_UPDATE_HELPER_PATH}}", "user-restart"))
%transfiletriggerin -P 1000700 -p <lua> -- {{SYSUSERS_DIR}}/
%transfiletriggerin -P 1000700 -p <lua> -- {{SYSUSERS_DIR}}
-- This script will process files installed in {{SYSUSERS_DIR}} to create
-- specified users automatically. The priority is set such that it
-- will run before the tmpfiles file trigger.
assert(rpm.execute("systemd-sysusers"))
%transfiletriggerin -P 1000700 udev -p <lua> -- {{UDEV_HWDB_DIR}}/
%transfiletriggerin -P 1000700 udev -p <lua> -- {{UDEV_HWDB_DIR}}
-- This script will automatically invoke hwdb update if files have been
-- installed or updated in {{UDEV_HWDB_DIR}}.
assert(rpm.execute("systemd-hwdb", "update"))
%transfiletriggerin -P 1000700 -p <lua> -- {{SYSTEMD_CATALOG_DIR}}/
%transfiletriggerin -P 1000700 -p <lua> -- {{SYSTEMD_CATALOG_DIR}}
-- This script will automatically invoke journal catalog update if files
-- have been installed or updated in {{SYSTEMD_CATALOG_DIR}}.
assert(rpm.execute("journalctl", "--update-catalog"))
%transfiletriggerin -P 1000700 -p <lua> -- {{BINFMT_DIR}}/
%transfiletriggerin -P 1000700 -p <lua> -- {{BINFMT_DIR}}
-- This script will automatically apply binfmt rules if files have been
-- installed or updated in {{BINFMT_DIR}}.
if posix.access("/run/systemd/system") then
assert(rpm.execute("{{LIBEXECDIR}}/systemd-binfmt"))
end
%transfiletriggerin -P 1000600 -p <lua> -- {{TMPFILES_DIR}}/
%transfiletriggerin -P 1000600 -p <lua> -- {{TMPFILES_DIR}}
-- This script will process files installed in {{TMPFILES_DIR}} to create
-- tmpfiles automatically. The priority is set such that it will run
-- after the sysusers file trigger, but before any other triggers.
assert(rpm.execute("systemd-tmpfiles", "--create"))
%transfiletriggerin -P 1000600 udev -p <lua> -- {{UDEV_RULES_DIR}}/
%transfiletriggerin -P 1000600 udev -p <lua> -- {{UDEV_RULES_DIR}}
-- This script will automatically update udev with new rules if files
-- have been installed or updated in {{UDEV_RULES_DIR}}.
assert(rpm.execute("{{SYSTEMD_UPDATE_HELPER_PATH}}", "mark-reload-system-units", "systemd-udevd.service"))
%transfiletriggerin -P 1000500 -p <lua> -- {{SYSCTL_DIR}}/
%transfiletriggerin -P 1000500 -p <lua> -- {{SYSCTL_DIR}}
-- This script will automatically apply sysctl rules if files have been
-- installed or updated in {{SYSCTL_DIR}}.
if posix.access("/run/systemd/system") then

View File

@ -9,17 +9,17 @@
#
# Minimum rpm version supported: 4.14.0
%transfiletriggerin -P 900900 -- {{SYSTEM_DATA_UNIT_DIR}}/ /etc/systemd/system/
%transfiletriggerin -P 900900 -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
# This script will run after any package is initially installed or
# upgraded. We care about the case where a package is initially
# installed, because other cases are covered by the *un scriptlets,
# so sometimes we will reload needlessly.
{{SYSTEMD_UPDATE_HELPER_PATH}} system-reload-restart || :
%transfiletriggerin -P 900899 -- {{USER_DATA_UNIT_DIR}}/ /etc/systemd/user/
%transfiletriggerin -P 900899 -- {{USER_DATA_UNIT_DIR}} /etc/systemd/user
{{SYSTEMD_UPDATE_HELPER_PATH}} user-reload-restart || :
%transfiletriggerpostun -P 1000100 -- {{SYSTEM_DATA_UNIT_DIR}}/ /etc/systemd/system/
%transfiletriggerpostun -P 1000100 -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
# On removal, we need to run daemon-reload after any units have been
# removed.
# On upgrade, we need to run daemon-reload after any new unit files
@ -27,35 +27,35 @@
# executed.
{{SYSTEMD_UPDATE_HELPER_PATH}} system-reload || :
%transfiletriggerpostun -P 1000099 -- {{USER_DATA_UNIT_DIR}}/ /etc/systemd/user/
%transfiletriggerpostun -P 1000099 -- {{USER_DATA_UNIT_DIR}} /etc/systemd/user
# Execute daemon-reload in user managers.
{{SYSTEMD_UPDATE_HELPER_PATH}} user-reload || :
%transfiletriggerpostun -P 10000 -- {{SYSTEM_DATA_UNIT_DIR}}/ /etc/systemd/system/
%transfiletriggerpostun -P 10000 -- {{SYSTEM_DATA_UNIT_DIR}} /etc/systemd/system
# We restart remaining system services that should be restarted here.
{{SYSTEMD_UPDATE_HELPER_PATH}} system-restart || :
%transfiletriggerpostun -P 9999 -- {{USER_DATA_UNIT_DIR}}/ /etc/systemd/user/
%transfiletriggerpostun -P 9999 -- {{USER_DATA_UNIT_DIR}} /etc/systemd/user
# We restart remaining user services that should be restarted here.
{{SYSTEMD_UPDATE_HELPER_PATH}} user-restart || :
%transfiletriggerin -P 1000700 -- {{SYSUSERS_DIR}}/
%transfiletriggerin -P 1000700 -- {{SYSUSERS_DIR}}
# This script will process files installed in {{SYSUSERS_DIR}} to create
# specified users automatically. The priority is set such that it
# will run before the tmpfiles file trigger.
systemd-sysusers || :
%transfiletriggerin -P 1000700 udev -- {{UDEV_HWDB_DIR}}/
%transfiletriggerin -P 1000700 udev -- {{UDEV_HWDB_DIR}}
# This script will automatically invoke hwdb update if files have been
# installed or updated in {{UDEV_HWDB_DIR}}.
systemd-hwdb update || :
%transfiletriggerin -P 1000700 -- {{SYSTEMD_CATALOG_DIR}}/
%transfiletriggerin -P 1000700 -- {{SYSTEMD_CATALOG_DIR}}
# This script will automatically invoke journal catalog update if files
# have been installed or updated in {{SYSTEMD_CATALOG_DIR}}.
journalctl --update-catalog || :
%transfiletriggerin -P 1000700 -- {{BINFMT_DIR}}/
%transfiletriggerin -P 1000700 -- {{BINFMT_DIR}}
# This script will automatically apply binfmt rules if files have been
# installed or updated in {{BINFMT_DIR}}.
if test -d "/run/systemd/system"; then
@ -64,7 +64,7 @@ if test -d "/run/systemd/system"; then
{{LIBEXECDIR}}/systemd-binfmt || :
fi
%transfiletriggerin -P 1000600 -- {{TMPFILES_DIR}}/
%transfiletriggerin -P 1000600 -- {{TMPFILES_DIR}}
# This script will process files installed in {{TMPFILES_DIR}} to create
# tmpfiles automatically. The priority is set such that it will run
# after the sysusers file trigger, but before any other triggers.
@ -72,12 +72,12 @@ if test -d "/run/systemd/system"; then
systemd-tmpfiles --create || :
fi
%transfiletriggerin -P 1000600 udev -- {{UDEV_RULES_DIR}}/
%transfiletriggerin -P 1000600 udev -- {{UDEV_RULES_DIR}}
# This script will automatically update udev with new rules if files
# have been installed or updated in {{UDEV_RULES_DIR}}.
{{SYSTEMD_UPDATE_HELPER_PATH}} mark-reload-system-units systemd-udevd.service || :
%transfiletriggerin -P 1000500 -- {{SYSCTL_DIR}}/
%transfiletriggerin -P 1000500 -- {{SYSCTL_DIR}}
# This script will automatically apply sysctl rules if files have been
# installed or updated in {{SYSCTL_DIR}}.
if test -d "/run/systemd/system"; then

View File

@ -361,6 +361,7 @@ libshared_deps = [threads,
libacl_cflags,
libaudit_cflags,
libblkid_cflags,
libcap,
libcrypt,
libdl,
libgcrypt_cflags,

View File

@ -56,6 +56,7 @@ executables += [
'extract' : systemctl_extract_sources,
'link_with' : systemctl_link_with,
'dependencies' : [
libcap,
liblz4_cflags,
libxz_cflags,
libzstd_cflags,

View File

@ -69,8 +69,6 @@ simple_tests += files(
'test-bus-unit-util.c',
'test-bus-util.c',
'test-calendarspec.c',
'test-capability-list.c',
'test-capability-util.c',
'test-cgroup-setup.c',
'test-cgroup-util.c',
'test-chase.c',
@ -257,6 +255,14 @@ executables += [
'sources' : files('test-btrfs-physical-offset.c'),
'type' : 'manual',
},
test_template + {
'sources' : files('test-capability-list.c'),
'dependencies' : libcap,
},
test_template + {
'sources' : files('test-capability-util.c'),
'dependencies' : libcap,
},
test_template + {
'sources' : files('test-chase-manual.c'),
'type' : 'manual',

View File

@ -11,6 +11,11 @@
#include "strv.h"
#include "tests.h"
static inline void cap_free_charpp(char **p) {
if (*p)
cap_free(*p);
}
/* verify the capability parser */
TEST(cap_list) {
assert_se(!capability_to_name(-1));
@ -23,12 +28,12 @@ TEST(cap_list) {
ASSERT_STREQ(CAPABILITY_TO_STRING(62), "0x3e");
assert_se(!CAPABILITY_TO_STRING(64));
for (unsigned i = 0; i < capability_list_length(); i++) {
for (int i = 0; i < capability_list_length(); i++) {
const char *n;
ASSERT_NOT_NULL(n = capability_to_name(i));
ASSERT_OK_EQ(capability_from_name(n), (int) i);
printf("%s = %u\n", n, i);
assert_se(n = capability_to_name(i));
assert_se(capability_from_name(n) == i);
printf("%s = %i\n", n, i);
ASSERT_STREQ(CAPABILITY_TO_STRING(i), n);
}
@ -43,6 +48,25 @@ TEST(cap_list) {
assert_se(capability_from_name("63") == -EINVAL);
assert_se(capability_from_name("64") == -EINVAL);
assert_se(capability_from_name("-1") == -EINVAL);
for (int i = 0; i < capability_list_length(); i++) {
_cleanup_(cap_free_charpp) char *a = NULL;
const char *b;
unsigned u;
assert_se(a = cap_to_name(i));
/* quit the loop as soon as libcap starts returning
* numeric ids, formatted as strings */
if (safe_atou(a, &u) >= 0)
break;
assert_se(b = capability_to_name(i));
printf("%s vs. %s\n", a, b);
assert_se(strcasecmp(a, b) == 0);
}
}
static void test_capability_set_one(uint64_t c, const char *t) {
@ -126,9 +150,9 @@ static void test_capability_set_to_string_invalid(uint64_t invalid_cap_set) {
TEST(capability_set_to_string) {
test_capability_set_to_string_invalid(0);
/* once the kernel supports 62 (CAP_LIMIT) caps, there are no 'invalid' numbers
/* once the kernel supports 62 caps, there are no 'invalid' numbers
* for us to test with */
if (cap_last_cap() < CAP_LIMIT)
if (cap_last_cap() < 62)
test_capability_set_to_string_invalid(all_capabilities() + 1);
}

View File

@ -13,7 +13,6 @@
#define TEST_CAPABILITY_C
#include "alloc-util.h"
#include "capability-list.h"
#include "capability-util.h"
#include "errno-util.h"
#include "fd-util.h"
@ -85,15 +84,18 @@ static void fork_test(void (*test_func)(void)) {
}
static void show_capabilities(void) {
_cleanup_free_ char *e = NULL, *p = NULL, *i = NULL;
CapabilityQuintet q;
cap_t caps;
char *text;
ASSERT_OK(capability_get(&q));
ASSERT_OK(capability_set_to_string(q.effective, &e));
ASSERT_OK(capability_set_to_string(q.permitted, &p));
ASSERT_OK(capability_set_to_string(q.inheritable, &i));
caps = cap_get_proc();
assert_se(caps);
log_info("Capabilities:e=%s p=%s, i=%s", e, p, i);
text = cap_to_text(caps, NULL);
assert_se(text);
log_info("Capabilities:%s", text);
cap_free(caps);
cap_free(text);
}
static int setup_tests(bool *run_ambient) {
@ -181,18 +183,50 @@ static void test_have_effective_cap(void) {
assert_se(have_effective_cap(CAP_CHOWN) == 0);
}
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);
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_se(fv == CAP_SET);
cap_free(caps);
}
static void test_apply_ambient_caps(void) {
ASSERT_OK_EQ_ERRNO(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0), 0);
cap_t caps;
uint64_t set = 0;
cap_flag_value_t fv;
ASSERT_OK(capability_ambient_set_apply(UINT64_C(1) << CAP_CHOWN, true));
ASSERT_OK_POSITIVE(have_inheritable_cap(CAP_CHOWN));
assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
ASSERT_OK_EQ_ERRNO(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0), 1);
set = (UINT64_C(1) << CAP_CHOWN);
ASSERT_OK(capability_ambient_set_apply(0, true));
ASSERT_OK_ZERO(have_inheritable_cap(CAP_CHOWN));
assert_se(!capability_ambient_set_apply(set, true));
ASSERT_OK_EQ_ERRNO(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0), 0);
caps = cap_get_proc();
assert_se(caps);
assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
assert_se(fv == CAP_SET);
cap_free(caps);
assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 1);
assert_se(!capability_ambient_set_apply(0, true));
caps = cap_get_proc();
assert_se(caps);
assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
assert_se(fv == CAP_CLEAR);
cap_free(caps);
assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
}
static void test_ensure_cap_64_bit(void) {
@ -207,9 +241,11 @@ static void test_ensure_cap_64_bit(void) {
ASSERT_OK(safe_atolu(content, &p));
/* If caps don't fit into 64-bit anymore, we have a problem, fail the test. Moreover, we use
* UINT64_MAX as unset, hence it must be smaller than or equals to 62 (CAP_LIMIT). */
assert_se(p <= CAP_LIMIT);
/* If caps don't fit into 64-bit anymore, we have a problem, fail the test. */
assert_se(p <= 63);
/* Also check for the header definition */
assert_cc(CAP_LAST_CAP <= 63);
}
static void test_capability_get_ambient(void) {
@ -302,6 +338,8 @@ int main(int argc, char *argv[]) {
if (!userns_has_single_user())
test_drop_privileges();
test_update_inherited_set();
if (!userns_has_single_user())
fork_test(test_have_effective_cap);

View File

@ -93,15 +93,6 @@ if [ "$VERITY_SIG_SUPPORTED" -eq 1 ]; then
mv /tmp/app0.roothash.p7s.bak /tmp/app0.roothash.p7s
fi
# Bare squashfs without any verity or signature also should be rejected, even if we ask to trust it
(! systemd-run -M testuser@ --user --pipe --wait \
--property ExtensionImages=/tmp/app1.raw \
true)
(! systemd-run -M testuser@ --user --pipe --wait \
--property ExtensionImages=/tmp/app1.raw \
--property ExtensionImagePolicy=root=verity+signed+unprotected+absent:usr=verity+signed+unprotected+absent \
true)
# Install key in keychain
mkdir -p /run/verity.d
cp /tmp/test-50-unpriv-cert.crt /run/verity.d/

View File

@ -42,11 +42,11 @@ else
apt-get update
apt-get install -y gperf m4 gettext python3-pip \
libmount-dev \
libcap-dev libmount-dev \
pkg-config wget python3-jinja2 zipmerge zstd
if [[ "$ARCHITECTURE" == i386 ]]; then
apt-get install -y pkg-config:i386 libmount-dev:i386
apt-get install -y pkg-config:i386 libcap-dev:i386 libmount-dev:i386
fi
pip3 install -r .github/workflows/requirements.txt --require-hashes