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

Compare commits

...

20 Commits

Author SHA1 Message Date
Daan De Meyer
1835ce2f04 rpm: Make sure we only match files in the directories in triggers
/usr/lib/systemd/system will match /usr/lib/systemd/systemd-networkd,
which is definitely not the intention.
2025-10-24 10:29:40 +09:00
Yu Watanabe
fe5625cbba
mountfsd: allow privileged users to mount bare unprotected filesystems (#39411)
Split from https://github.com/systemd/systemd/pull/39394 as that
requires deeper rework that will take more time
2025-10-24 09:40:52 +09:00
Yu Watanabe
f4072a9da2
Drop libcap dependency (#39425) 2025-10-24 09:40:05 +09:00
Yu Watanabe
9b414a38fa tree-wide: drop unused libcap dependencies 2025-10-24 01:52:59 +09:00
Yu Watanabe
a98f710fdf capability-util: use capability_get() and _apply() in capability_quintet_enforce() 2025-10-24 01:52:59 +09:00
Yu Watanabe
6e5f07756f capability-util: use capability_get() and _apply() in change_capability() 2025-10-24 01:52:59 +09:00
Yu Watanabe
69eb331b6c capability-util: use capability_apply() in drop_privileges() 2025-10-24 01:52:59 +09:00
Yu Watanabe
256d6f3f2f capability-util: rework capability_gain_cap_setpcap() and capability_bounding_set_drop()
This makes the functions use CapabilityQuintet, capability_get(), and
capability_apply().
2025-10-24 01:52:59 +09:00
Yu Watanabe
2bf880892e test: use have_inheritable_cap() in test_apply_ambient_caps()
This also make the test case use ASSERT_XYZ() macros.
2025-10-24 01:52:59 +09:00
Yu Watanabe
96f2255637 test: replace cap_to_text() with capability_get() and capability_set_to_string() 2025-10-24 01:52:59 +09:00
Yu Watanabe
aa8ab67a6d capability-util: introduce capability_apply() and use it in capability_ambient_set_apply() 2025-10-24 01:52:59 +09:00
Yu Watanabe
e1c134ba9c capability-util: introduce capability_get() and use it in have_effective_cap()
capability_get() is a wrapper of capget() syscall and converts its
result to CapabilityQuintet.

This also introduce have_inheritable_cap(), which is similar to
have_effective_cap(). It is currently unused, but will be used later.
2025-10-24 01:52:59 +09:00
Yu Watanabe
e804256b80 capability-util: several coding style updates
- rebreak comments,
- add short comment for constant arguments,
- drop unnecessary {},
- use BIT_SET() macro.
2025-10-24 01:52:59 +09:00
Yu Watanabe
2038ad725d capability-util: introduce capability_quintet_equal() helper function
Currently unused, but will be used later.
2025-10-24 01:52:59 +09:00
Yu Watanabe
50053a0212 capability-util: move several definitions 2025-10-24 01:52:59 +09:00
Yu Watanabe
7de349c1ed test: use CAP_LIMIT at one more place 2025-10-24 01:52:56 +09:00
Yu Watanabe
4c0cdc4a2c capability-util: tighten requirement for CAP_LAST_CAP off by one
Otherwise, we cannot use UINT64_MAX as 'unset'.
2025-10-24 01:52:18 +09:00
Yu Watanabe
2a6b084cc6 capability-list: make capability_list_length() return unsigned 2025-10-23 23:46:49 +09:00
Luca Boccassi
e84aa21af8 man: RootImageOptions= is only supported for system services right now
Support via mountfsd is being worked on but will take more time,
fix the documentation to be correct in the meanwhile

Follow-up for fad01f798d1308fa6bd81eac1b13b3d14d9a5380
2025-10-22 17:22:03 +01:00
Luca Boccassi
53d49fbf3f mountfsd: allow privileged users to mount bare unprotected filesystems
This is useful when we start to call mountfsd from root, for example
from the tests where we just use a simple squashfs/erofs.
Note that this requires the caller to be root, and it will be rejected
otherwise, as such images are classified as 'unprotected' and the
enforced policy does not accept them for unprivileged users.
2025-10-22 16:47:46 +01:00
25 changed files with 322 additions and 458 deletions

View File

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

1
README
View File

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

4
TODO
View File

@ -1640,10 +1640,6 @@ Features:
work for ECDSA keys since their signatures contain a random component, but work for ECDSA keys since their signatures contain a random component, but
will work for RSA and Ed25519 keys. 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 * userdbd: implement an additional varlink service socket that provides the
host user db in restricted form, then allow this to be bind mounted into 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 sandboxed environments that want the host database in minimal form. All

View File

@ -20,8 +20,6 @@ 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. 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). 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 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. 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.
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. 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>esp</constant>, <constant>xbootldr</constant>, <constant>tmp</constant>,
<constant>var</constant>.</para> <constant>var</constant>.</para>
<xi:include href="system-or-user-ns-mountfsd.xml" xpointer="singular"/> <xi:include href="system-only.xml" xpointer="singular"/>
<xi:include href="version-info.xml" xpointer="v247"/></listitem> <xi:include href="version-info.xml" xpointer="v247"/></listitem>
</varlistentry> </varlistentry>

View File

@ -692,7 +692,6 @@ conf.set('GPERF_LEN_TYPE', gperf_len_type,
foreach header : [ foreach header : [
'crypt.h', 'crypt.h',
'sys/capability.h',
] ]
if not cc.has_header(header) if not cc.has_header(header)
@ -1001,7 +1000,6 @@ threads = dependency('threads')
librt = cc.find_library('rt') librt = cc.find_library('rt')
libm = cc.find_library('m') libm = cc.find_library('m')
libdl = cc.find_library('dl') libdl = cc.find_library('dl')
libcap = dependency('libcap')
# On some architectures, libatomic is required. But on some installations, # 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. # it is found, but actual linking fails. So let's try to use it opportunistically.
@ -2141,8 +2139,7 @@ if static_libsystemd != 'false'
install_tag: 'libsystemd', install_tag: 'libsystemd',
install_dir : libdir, install_dir : libdir,
pic : static_libsystemd_pic, pic : static_libsystemd_pic,
dependencies : [libcap, dependencies : [libdl,
libdl,
libgcrypt_cflags, libgcrypt_cflags,
liblz4_cflags, liblz4_cflags,
libm, 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) { const char* capability_to_name(int id) {
if (id < 0) if (id < 0)
return NULL; return NULL;
if (id >= capability_list_length()) if ((unsigned) id >= capability_list_length())
return NULL; return NULL;
return capability_names[id]; 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 * 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 * highest supported capability. Hence with everyone agreeing on the same capabilities list, this function
* will return one higher than cap_last_cap(). */ * will return one higher than cap_last_cap(). */
int capability_list_length(void) { unsigned capability_list_length(void) {
return MIN((int) ELEMENTSOF(capability_names), CAP_LIMIT + 1); return MIN((unsigned) ELEMENTSOF(capability_names), (unsigned) (CAP_LIMIT + 1));
} }
int capability_set_to_string(uint64_t set, char **ret) { 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]) {}) #define CAPABILITY_TO_STRING(id) capability_to_string(id, (char[CAPABILITY_TO_STRING_MAX]) {})
int capability_from_name(const char *name); int capability_from_name(const char *name);
int capability_list_length(void); unsigned capability_list_length(void);
int capability_set_to_string(uint64_t set, char **ret); int capability_set_to_string(uint64_t set, char **ret);
int capability_set_to_string_negative(uint64_t set, char **ret); int capability_set_to_string_negative(uint64_t set, char **ret);

View File

@ -4,35 +4,67 @@
#include <stdatomic.h> #include <stdatomic.h>
#include <stdio.h> #include <stdio.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "bitfield.h" #include "bitfield.h"
#include "capability-list.h" #include "capability-list.h"
#include "capability-util.h" #include "capability-util.h"
#include "errno-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
#include "log.h" #include "log.h"
#include "logarithm.h"
#include "parse-util.h" #include "parse-util.h"
#include "pidref.h" #include "pidref.h"
#include "process-util.h" #include "process-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "user-util.h" #include "user-util.h"
int have_effective_cap(int value) { int capability_get(CapabilityQuintet *ret) {
_cleanup_cap_free_ cap_t cap = NULL; assert(ret);
cap_flag_value_t fv = CAP_CLEAR; /* To avoid false-positive use-of-uninitialized-value error reported
* by fuzzers. */
cap = cap_get_proc(); struct __user_cap_header_struct hdr = {
if (!cap) .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)
return -errno; return -errno;
if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0) *ret = (CapabilityQuintet) {
return -errno; .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;
}
return fv == CAP_SET; 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));
} }
unsigned cap_last_cap(void) { unsigned cap_last_cap(void) {
@ -89,25 +121,33 @@ unsigned cap_last_cap(void) {
return c; return c;
} }
int capability_update_inherited_set(cap_t caps, uint64_t set) { int have_effective_cap(unsigned cap) {
/* Add capabilities in the set to the inherited caps, drops capabilities not in the set. CapabilityQuintet q;
* Do not apply them yet. */ int r;
for (unsigned i = 0; i <= cap_last_cap(); i++) { assert(cap <= CAP_LIMIT);
cap_flag_value_t flag = set & (UINT64_C(1) << i) ? CAP_SET : CAP_CLEAR;
cap_value_t v;
v = (cap_value_t) i; r = capability_get(&q);
if (r < 0)
return r;
if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, flag) < 0) return BIT_SET(q.effective, cap);
return -errno; }
}
return 0; 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);
} }
int capability_ambient_set_apply(uint64_t set, bool also_inherit) { int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
_cleanup_cap_free_ cap_t caps = NULL;
int r; int r;
/* Remove capabilities requested in ambient set, but not in the bounding set */ /* Remove capabilities requested in ambient set, but not in the bounding set */
@ -125,19 +165,20 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
/* Add the capabilities to the ambient set (an possibly also the inheritable set) */ /* Add the capabilities to the ambient set (an possibly also the inheritable set) */
if (also_inherit) { if (also_inherit) {
caps = cap_get_proc(); CapabilityQuintet q;
if (!caps)
return -errno;
r = capability_update_inherited_set(caps, set); r = capability_get(&q);
if (r < 0) if (r < 0)
return -errno; return r;
if (cap_set_proc(caps) < 0) q.inheritable = set;
return -errno;
r = capability_apply(&q);
if (r < 0)
return r;
} }
for (unsigned i = 0; i <= cap_last_cap(); i++) { for (unsigned i = 0; i <= cap_last_cap(); i++)
if (BIT_SET(set, i)) { if (BIT_SET(set, i)) {
/* Add the capability to the ambient set. */ /* Add the capability to the ambient set. */
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0) if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0)
@ -151,107 +192,85 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, i, 0, 0) < 0) if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, i, 0, 0) < 0)
return -errno; return -errno;
} }
}
return 0; return 0;
} }
int capability_gain_cap_setpcap(cap_t *ret_before_caps) { int capability_gain_cap_setpcap(void) {
_cleanup_cap_free_ cap_t caps = NULL; CapabilityQuintet q;
cap_flag_value_t fv;
caps = cap_get_proc();
if (!caps)
return -errno;
if (cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0)
return -errno;
if (fv != CAP_SET) {
_cleanup_cap_free_ cap_t temp_cap = NULL;
static const cap_value_t v = CAP_SETPCAP;
temp_cap = cap_dup(caps);
if (!temp_cap)
return -errno;
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 0;
}
int capability_bounding_set_drop(uint64_t keep, bool right_now) {
_cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL;
int r; int r;
/* If we are run as PID 1 we will lack CAP_SETPCAP by default r = capability_get(&q);
* 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. */
r = capability_gain_cap_setpcap(&before_cap);
if (r < 0) if (r < 0)
return r; return r;
after_cap = cap_dup(before_cap); if (BIT_SET(q.effective, CAP_SETPCAP))
if (!after_cap) return 1; /* We already have capability. */
return -errno;
SET_BIT(q.effective, CAP_SETPCAP);
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;
}
return 1; /* acquired */
}
int capability_bounding_set_drop(uint64_t keep, bool right_now) {
int k, 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. */
CapabilityQuintet q;
r = capability_get(&q);
if (r < 0)
return r;
CapabilityQuintet saved = q;
r = capability_gain_cap_setpcap();
if (r < 0)
return r;
for (unsigned i = 0; i <= cap_last_cap(); i++) { for (unsigned i = 0; i <= cap_last_cap(); i++) {
cap_value_t v; if (BIT_SET(keep, i))
if ((keep & (UINT64_C(1) << i)))
continue; continue;
/* Drop it from the bounding set */ /* Drop it from the bounding set */
if (prctl(PR_CAPBSET_DROP, i) < 0) { if (prctl(PR_CAPBSET_DROP, i) < 0) {
r = -errno; r = -errno;
/* If dropping the capability failed, let's see if we didn't have it in the first place. If so, /* If dropping the capability failed, let's see if we didn't have it in the first
* continue anyway, as dropping a capability we didn't have in the first place doesn't really * place. If so, continue anyway, as dropping a capability we didn't have in the
* matter anyway. */ * first place doesn't really matter anyway. */
if (prctl(PR_CAPBSET_READ, i) != 0) if (prctl(PR_CAPBSET_READ, i) != 0)
goto finish; goto finish;
} }
v = (cap_value_t) i;
/* Also drop it from the inheritable set, so /* Also drop it from the inheritable set, so that anything we exec() loses the capability for
* that anything we exec() loses the * good. */
* capability for good. */ CLEAR_BIT(q.inheritable, i);
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 /* If we shall apply this right now drop it also from our own capability sets. */
* also from our own capability sets. */
if (right_now) { if (right_now) {
if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || CLEAR_BIT(q.effective, i);
cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { CLEAR_BIT(q.permitted, i);
r = -errno;
goto finish;
}
} }
} }
r = 0; r = 0;
finish: finish:
if (cap_set_proc(after_cap) < 0) { k = capability_apply(&q);
if (k < 0)
/* If there are no actual changes anyway then let's ignore this error. */ /* If there are no actual changes anyway then let's ignore this error. */
if (cap_compare(before_cap, after_cap) != 0) if (!capability_quintet_equal(&q, &saved))
r = -errno; return k;
}
return r; return r;
} }
@ -307,7 +326,7 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
if (setresgid(gid, gid, gid) < 0) if (setresgid(gid, gid, gid) < 0)
return log_error_errno(errno, "Failed to change group ID: %m"); return log_error_errno(errno, "Failed to change group ID: %m");
r = maybe_setgroups(0, NULL); r = maybe_setgroups(/* size= */ 0, /* list= */ NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to drop auxiliary groups list: %m"); return log_error_errno(r, "Failed to drop auxiliary groups list: %m");
@ -325,64 +344,54 @@ 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 /* Drop all caps from the bounding set (as well as the inheritable/permitted/effective sets), except
* the ones we want to keep */ * the ones we want to keep */
r = capability_bounding_set_drop(keep_capabilities, true); r = capability_bounding_set_drop(keep_capabilities, /* right_now= */ 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");
/* Now upgrade the permitted caps we still kept to effective caps */ /* Now upgrade the permitted caps we still kept to effective caps */
if (keep_capabilities != 0) { if (keep_capabilities != 0) {
cap_value_t bits[log2u64(keep_capabilities) + 1]; CapabilityQuintet q = {
_cleanup_cap_free_ cap_t d = NULL; .effective = keep_capabilities,
unsigned i, j = 0; .permitted = keep_capabilities,
};
d = cap_init(); r = capability_apply(&q);
if (!d) if (r < 0)
return log_oom(); return log_error_errno(r, "Failed to increase capabilities: %m");
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; return 0;
} }
static int change_capability(cap_value_t cv, cap_flag_value_t flag) { static int change_capability(unsigned cap, bool b) {
_cleanup_cap_free_ cap_t tmp_cap = NULL; CapabilityQuintet q;
int r;
tmp_cap = cap_get_proc(); assert(cap <= CAP_LIMIT);
if (!tmp_cap)
return -errno;
if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, flag) < 0) || r = capability_get(&q);
(cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, flag) < 0) || if (r < 0)
(cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, flag) < 0)) return r;
return -errno;
if (cap_set_proc(tmp_cap) < 0) if (b) {
return -errno; 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);
}
return 0; return capability_apply(&q);
} }
int drop_capability(cap_value_t cv) { int drop_capability(unsigned cap) {
return change_capability(cv, CAP_CLEAR); return change_capability(cap, false);
} }
int keep_capability(cap_value_t cv) { int keep_capability(unsigned cap) {
return change_capability(cv, CAP_SET); return change_capability(cap, true);
} }
bool capability_quintet_mangle(CapabilityQuintet *q) { bool capability_quintet_mangle(CapabilityQuintet *q) {
@ -414,161 +423,73 @@ bool capability_quintet_mangle(CapabilityQuintet *q) {
} }
int capability_quintet_enforce(const CapabilityQuintet *q) { int capability_quintet_enforce(const CapabilityQuintet *q) {
_cleanup_cap_free_ cap_t c = NULL, modified = NULL; CapabilityQuintet c;
bool modified = false;
int r; int r;
if (q->ambient != CAP_MASK_UNSET) { if (q->ambient != CAP_MASK_UNSET ||
bool changed = false; q->inheritable != CAP_MASK_UNSET ||
q->permitted != CAP_MASK_UNSET ||
c = cap_get_proc(); q->effective != CAP_MASK_UNSET) {
if (!c) r = capability_get(&c);
return -errno; if (r < 0)
return r;
/* In order to raise the ambient caps set we first need to raise the matching
* inheritable + permitted cap */
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;
if ((q->ambient & m) == 0)
continue;
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;
} }
if (changed) if (q->ambient != CAP_MASK_UNSET) {
if (cap_set_proc(c) < 0) /* In order to raise the ambient caps set we first need to raise the matching
return -errno; * inheritable + permitted cap */
if (!FLAGS_SET(c.permitted, q->ambient) ||
!FLAGS_SET(c.inheritable, q->ambient)) {
r = capability_ambient_set_apply(q->ambient, false); c.permitted |= q->ambient;
c.inheritable |= q->ambient;
r = capability_apply(&c);
if (r < 0)
return r;
}
r = capability_ambient_set_apply(q->ambient, /* also_inherit= */ false);
if (r < 0) if (r < 0)
return r; return r;
} }
if (q->inheritable != CAP_MASK_UNSET || q->permitted != CAP_MASK_UNSET || q->effective != CAP_MASK_UNSET) { if (q->inheritable != CAP_MASK_UNSET || q->permitted != CAP_MASK_UNSET || q->effective != CAP_MASK_UNSET) {
bool changed = false; if (!FLAGS_SET(c.effective, q->effective) ||
!FLAGS_SET(c.permitted, q->permitted) ||
!FLAGS_SET(c.inheritable, q->inheritable)) {
if (!c) { c.effective |= q->effective;
c = cap_get_proc(); c.permitted |= q->permitted;
if (!c) c.inheritable |= q->inheritable;
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 /* 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 * 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 * dropping the bounding caps below, since at that point we can never acquire new
* caps in inherited/permitted/effective anymore, but only lose them. */ * caps in inherited/permitted/effective anymore, but only lose them.
if (cap_set_proc(modified ?: c) < 0) *
return -errno; * 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;
} }
} }
if (q->bounding != CAP_MASK_UNSET) { if (q->bounding != CAP_MASK_UNSET) {
r = capability_bounding_set_drop(q->bounding, false); r = capability_bounding_set_drop(q->bounding, /* right_now= */ false);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -577,9 +498,11 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
* we have already set only in the CAP_SETPCAP bit, which we needed for dropping the bounding * 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 * bits. This call only undoes bits and doesn't acquire any which means the bounding caps don't
* matter. */ * matter. */
if (modified) if (modified) {
if (cap_set_proc(c) < 0) r = capability_apply(&c);
return -errno; if (r < 0)
return r;
}
return 0; return 0;
} }

View File

@ -1,14 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
#include <sys/capability.h> /* IWYU pragma: export */ #include <linux/capability.h> /* IWYU pragma: export */
#include "basic-forward.h" #include "basic-forward.h"
/* Special marker used when storing a capabilities mask as "unset". This would need to be updated as soon as /* 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. */ * Linux learns more than 63 caps. */
#define CAP_MASK_UNSET UINT64_MAX #define CAP_MASK_UNSET UINT64_MAX
assert_cc(CAP_LAST_CAP < 64);
/* All possible capabilities bits on */ /* All possible capabilities bits on */
#define CAP_MASK_ALL UINT64_C(0x7fffffffffffffff) #define CAP_MASK_ALL UINT64_C(0x7fffffffffffffff)
@ -16,35 +15,7 @@ assert_cc(CAP_LAST_CAP < 64);
/* The largest capability we can deal with, given we want to be able to store cap masks in uint64_t but still /* 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. */ * be able to use UINT64_MAX as indicator for "not set". The latter makes capability 63 unavailable. */
#define CAP_LIMIT 62 #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 /* 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. */ * order to avoid complaints about shifting a signed int left by 31 bits, which would make it negative. */
@ -59,7 +30,42 @@ typedef struct CapabilityQuintet {
uint64_t ambient; uint64_t ambient;
} CapabilityQuintet; } CapabilityQuintet;
#define CAPABILITY_QUINTET_NULL (const CapabilityQuintet) { CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET } #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());
}
static inline bool capability_quintet_is_set(const CapabilityQuintet *q) { static inline bool capability_quintet_is_set(const CapabilityQuintet *q) {
return capability_is_set(q->effective) || return capability_is_set(q->effective) ||
@ -77,6 +83,14 @@ static inline bool capability_quintet_is_fully_set(const CapabilityQuintet *q) {
capability_is_set(q->ambient); 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: /* 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. * drops all caps from all five sets if our bounding set doesn't allow them.
* Returns true if the quintet was modified. */ * Returns true if the quintet was modified. */

View File

@ -202,8 +202,7 @@ libbasic_static = static_library(
fundamental_sources, fundamental_sources,
include_directories : basic_includes, include_directories : basic_includes,
implicit_include_directories : false, implicit_include_directories : false,
dependencies : [libcap, dependencies : [libdl,
libdl,
libgcrypt_cflags, libgcrypt_cflags,
liblz4_cflags, liblz4_cflags,
libm, 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 * Hence there is no security impact to raise it in the effective set before execve
*/ */
r = capability_gain_cap_setpcap(/* ret_before_caps = */ NULL); r = capability_gain_cap_setpcap();
if (r < 0) { if (r < 0) {
*exit_status = EXIT_CAPABILITIES; *exit_status = EXIT_CAPABILITIES;
return log_error_errno(r, "Failed to gain CAP_SETPCAP for setting secure bits"); return log_error_errno(r, "Failed to gain CAP_SETPCAP for setting secure bits");

View File

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

View File

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

View File

@ -449,7 +449,9 @@ static int vl_method_mount_image(
DISSECT_IMAGE_ADD_PARTITION_DEVICES | DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_PIN_PARTITION_DEVICES | DISSECT_IMAGE_PIN_PARTITION_DEVICES |
(p.verity_sharing ? DISSECT_IMAGE_VERITY_SHARE : 0) | (p.verity_sharing ? DISSECT_IMAGE_VERITY_SHARE : 0) |
(p.verity_data_fd_idx != UINT_MAX ? DISSECT_IMAGE_NO_PARTITION_TABLE : 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 |
DISSECT_IMAGE_ALLOW_USERSPACE_VERITY; DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
/* Let's see if we have acquired the privilege to mount untrusted images already */ /* 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; break;
if (streq(t, "help")) { if (streq(t, "help")) {
for (int i = 0; i < capability_list_length(); i++) { for (unsigned i = 0; i < capability_list_length(); i++) {
const char *name; const char *name;
name = capability_to_name(i); name = capability_to_name(i);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,11 +11,6 @@
#include "strv.h" #include "strv.h"
#include "tests.h" #include "tests.h"
static inline void cap_free_charpp(char **p) {
if (*p)
cap_free(*p);
}
/* verify the capability parser */ /* verify the capability parser */
TEST(cap_list) { TEST(cap_list) {
assert_se(!capability_to_name(-1)); assert_se(!capability_to_name(-1));
@ -28,12 +23,12 @@ TEST(cap_list) {
ASSERT_STREQ(CAPABILITY_TO_STRING(62), "0x3e"); ASSERT_STREQ(CAPABILITY_TO_STRING(62), "0x3e");
assert_se(!CAPABILITY_TO_STRING(64)); assert_se(!CAPABILITY_TO_STRING(64));
for (int i = 0; i < capability_list_length(); i++) { for (unsigned i = 0; i < capability_list_length(); i++) {
const char *n; const char *n;
assert_se(n = capability_to_name(i)); ASSERT_NOT_NULL(n = capability_to_name(i));
assert_se(capability_from_name(n) == i); ASSERT_OK_EQ(capability_from_name(n), (int) i);
printf("%s = %i\n", n, i); printf("%s = %u\n", n, i);
ASSERT_STREQ(CAPABILITY_TO_STRING(i), n); ASSERT_STREQ(CAPABILITY_TO_STRING(i), n);
} }
@ -48,25 +43,6 @@ TEST(cap_list) {
assert_se(capability_from_name("63") == -EINVAL); assert_se(capability_from_name("63") == -EINVAL);
assert_se(capability_from_name("64") == -EINVAL); assert_se(capability_from_name("64") == -EINVAL);
assert_se(capability_from_name("-1") == -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) { static void test_capability_set_one(uint64_t c, const char *t) {
@ -150,9 +126,9 @@ static void test_capability_set_to_string_invalid(uint64_t invalid_cap_set) {
TEST(capability_set_to_string) { TEST(capability_set_to_string) {
test_capability_set_to_string_invalid(0); test_capability_set_to_string_invalid(0);
/* once the kernel supports 62 caps, there are no 'invalid' numbers /* once the kernel supports 62 (CAP_LIMIT) caps, there are no 'invalid' numbers
* for us to test with */ * for us to test with */
if (cap_last_cap() < 62) if (cap_last_cap() < CAP_LIMIT)
test_capability_set_to_string_invalid(all_capabilities() + 1); test_capability_set_to_string_invalid(all_capabilities() + 1);
} }

View File

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

View File

@ -93,6 +93,15 @@ if [ "$VERITY_SIG_SUPPORTED" -eq 1 ]; then
mv /tmp/app0.roothash.p7s.bak /tmp/app0.roothash.p7s mv /tmp/app0.roothash.p7s.bak /tmp/app0.roothash.p7s
fi 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 # Install key in keychain
mkdir -p /run/verity.d mkdir -p /run/verity.d
cp /tmp/test-50-unpriv-cert.crt /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 update
apt-get install -y gperf m4 gettext python3-pip \ apt-get install -y gperf m4 gettext python3-pip \
libcap-dev libmount-dev \ libmount-dev \
pkg-config wget python3-jinja2 zipmerge zstd pkg-config wget python3-jinja2 zipmerge zstd
if [[ "$ARCHITECTURE" == i386 ]]; then if [[ "$ARCHITECTURE" == i386 ]]; then
apt-get install -y pkg-config:i386 libcap-dev:i386 libmount-dev:i386 apt-get install -y pkg-config:i386 libmount-dev:i386
fi fi
pip3 install -r .github/workflows/requirements.txt --require-hashes pip3 install -r .github/workflows/requirements.txt --require-hashes