mirror of
https://github.com/systemd/systemd
synced 2025-11-19 08:44:44 +01:00
Compare commits
20 Commits
8eefd0f4de
...
1835ce2f04
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1835ce2f04 | ||
|
|
fe5625cbba | ||
|
|
f4072a9da2 | ||
|
|
9b414a38fa | ||
|
|
a98f710fdf | ||
|
|
6e5f07756f | ||
|
|
69eb331b6c | ||
|
|
256d6f3f2f | ||
|
|
2bf880892e | ||
|
|
96f2255637 | ||
|
|
aa8ab67a6d | ||
|
|
e1c134ba9c | ||
|
|
e804256b80 | ||
|
|
2038ad725d | ||
|
|
50053a0212 | ||
|
|
7de349c1ed | ||
|
|
4c0cdc4a2c | ||
|
|
2a6b084cc6 | ||
|
|
e84aa21af8 | ||
|
|
53d49fbf3f |
1
.github/workflows/build-test.sh
vendored
1
.github/workflows/build-test.sh
vendored
@ -30,7 +30,6 @@ PACKAGES=(
|
||||
libarchive-dev
|
||||
libblkid-dev
|
||||
libbpf-dev
|
||||
libcap-dev
|
||||
libcurl4-gnutls-dev
|
||||
libfdisk-dev
|
||||
libfido2-dev
|
||||
|
||||
1
README
1
README
@ -212,7 +212,6 @@ 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
4
TODO
@ -1640,10 +1640,6 @@ 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
|
||||
|
||||
@ -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.
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
@ -225,7 +225,7 @@
|
||||
<constant>esp</constant>, <constant>xbootldr</constant>, <constant>tmp</constant>,
|
||||
<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>
|
||||
</varlistentry>
|
||||
|
||||
@ -692,7 +692,6 @@ conf.set('GPERF_LEN_TYPE', gperf_len_type,
|
||||
|
||||
foreach header : [
|
||||
'crypt.h',
|
||||
'sys/capability.h',
|
||||
]
|
||||
|
||||
if not cc.has_header(header)
|
||||
@ -1001,7 +1000,6 @@ 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.
|
||||
@ -2141,8 +2139,7 @@ if static_libsystemd != 'false'
|
||||
install_tag: 'libsystemd',
|
||||
install_dir : libdir,
|
||||
pic : static_libsystemd_pic,
|
||||
dependencies : [libcap,
|
||||
libdl,
|
||||
dependencies : [libdl,
|
||||
libgcrypt_cflags,
|
||||
liblz4_cflags,
|
||||
libm,
|
||||
|
||||
@ -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 (id >= capability_list_length())
|
||||
if ((unsigned) 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(). */
|
||||
int capability_list_length(void) {
|
||||
return MIN((int) ELEMENTSOF(capability_names), CAP_LIMIT + 1);
|
||||
unsigned capability_list_length(void) {
|
||||
return MIN((unsigned) ELEMENTSOF(capability_names), (unsigned) (CAP_LIMIT + 1));
|
||||
}
|
||||
|
||||
int capability_set_to_string(uint64_t set, char **ret) {
|
||||
|
||||
@ -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);
|
||||
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_negative(uint64_t set, char **ret);
|
||||
|
||||
@ -4,35 +4,67 @@
|
||||
#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 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. */
|
||||
int capability_get(CapabilityQuintet *ret) {
|
||||
assert(ret);
|
||||
|
||||
cap = cap_get_proc();
|
||||
if (!cap)
|
||||
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)
|
||||
return -errno;
|
||||
|
||||
if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -89,25 +121,33 @@ unsigned cap_last_cap(void) {
|
||||
return c;
|
||||
}
|
||||
|
||||
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. */
|
||||
int have_effective_cap(unsigned cap) {
|
||||
CapabilityQuintet q;
|
||||
int r;
|
||||
|
||||
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;
|
||||
assert(cap <= CAP_LIMIT);
|
||||
|
||||
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 -errno;
|
||||
return BIT_SET(q.effective, cap);
|
||||
}
|
||||
|
||||
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) {
|
||||
_cleanup_cap_free_ cap_t caps = NULL;
|
||||
int r;
|
||||
|
||||
/* 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) */
|
||||
|
||||
if (also_inherit) {
|
||||
caps = cap_get_proc();
|
||||
if (!caps)
|
||||
return -errno;
|
||||
CapabilityQuintet q;
|
||||
|
||||
r = capability_update_inherited_set(caps, set);
|
||||
r = capability_get(&q);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
return r;
|
||||
|
||||
if (cap_set_proc(caps) < 0)
|
||||
return -errno;
|
||||
q.inheritable = set;
|
||||
|
||||
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)) {
|
||||
/* Add the capability to the ambient set. */
|
||||
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)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 capability_gain_cap_setpcap(void) {
|
||||
CapabilityQuintet q;
|
||||
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. */
|
||||
|
||||
r = capability_gain_cap_setpcap(&before_cap);
|
||||
r = capability_get(&q);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
after_cap = cap_dup(before_cap);
|
||||
if (!after_cap)
|
||||
return -errno;
|
||||
if (BIT_SET(q.effective, CAP_SETPCAP))
|
||||
return 1; /* We already have capability. */
|
||||
|
||||
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++) {
|
||||
cap_value_t v;
|
||||
|
||||
if ((keep & (UINT64_C(1) << i)))
|
||||
if (BIT_SET(keep, 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. */
|
||||
if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
/* Also drop it from the inheritable set, so that anything we exec() loses the capability for
|
||||
* good. */
|
||||
CLEAR_BIT(q.inheritable, i);
|
||||
|
||||
/* 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) {
|
||||
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;
|
||||
}
|
||||
CLEAR_BIT(q.effective, i);
|
||||
CLEAR_BIT(q.permitted, i);
|
||||
}
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
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 (cap_compare(before_cap, after_cap) != 0)
|
||||
r = -errno;
|
||||
}
|
||||
if (!capability_quintet_equal(&q, &saved))
|
||||
return k;
|
||||
|
||||
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)
|
||||
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)
|
||||
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
|
||||
* 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)
|
||||
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) {
|
||||
cap_value_t bits[log2u64(keep_capabilities) + 1];
|
||||
_cleanup_cap_free_ cap_t d = NULL;
|
||||
unsigned i, j = 0;
|
||||
CapabilityQuintet q = {
|
||||
.effective = keep_capabilities,
|
||||
.permitted = keep_capabilities,
|
||||
};
|
||||
|
||||
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");
|
||||
r = capability_apply(&q);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to increase capabilities: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int change_capability(cap_value_t cv, cap_flag_value_t flag) {
|
||||
_cleanup_cap_free_ cap_t tmp_cap = NULL;
|
||||
static int change_capability(unsigned cap, bool b) {
|
||||
CapabilityQuintet q;
|
||||
int r;
|
||||
|
||||
tmp_cap = cap_get_proc();
|
||||
if (!tmp_cap)
|
||||
return -errno;
|
||||
assert(cap <= CAP_LIMIT);
|
||||
|
||||
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;
|
||||
r = capability_get(&q);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (cap_set_proc(tmp_cap) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
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);
|
||||
}
|
||||
|
||||
int drop_capability(cap_value_t cv) {
|
||||
return change_capability(cv, CAP_CLEAR);
|
||||
return capability_apply(&q);
|
||||
}
|
||||
|
||||
int keep_capability(cap_value_t cv) {
|
||||
return change_capability(cv, CAP_SET);
|
||||
int drop_capability(unsigned cap) {
|
||||
return change_capability(cap, false);
|
||||
}
|
||||
|
||||
int keep_capability(unsigned cap) {
|
||||
return change_capability(cap, true);
|
||||
}
|
||||
|
||||
bool capability_quintet_mangle(CapabilityQuintet *q) {
|
||||
@ -414,161 +423,73 @@ bool capability_quintet_mangle(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;
|
||||
|
||||
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 */
|
||||
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 (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 (changed)
|
||||
if (cap_set_proc(c) < 0)
|
||||
return -errno;
|
||||
if (q->ambient != CAP_MASK_UNSET) {
|
||||
/* 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)) {
|
||||
|
||||
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)
|
||||
return r;
|
||||
}
|
||||
|
||||
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 = 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;
|
||||
}
|
||||
}
|
||||
c.effective |= q->effective;
|
||||
c.permitted |= q->permitted;
|
||||
c.inheritable |= q->inheritable;
|
||||
|
||||
/* 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. */
|
||||
if (cap_set_proc(modified ?: c) < 0)
|
||||
return -errno;
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
* bits. This call only undoes bits and doesn't acquire any which means the bounding caps don't
|
||||
* matter. */
|
||||
if (modified)
|
||||
if (cap_set_proc(c) < 0)
|
||||
return -errno;
|
||||
if (modified) {
|
||||
r = capability_apply(&c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <sys/capability.h> /* IWYU pragma: export */
|
||||
#include <linux/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)
|
||||
@ -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
|
||||
* be able to use UINT64_MAX as indicator for "not set". The latter makes capability 63 unavailable. */
|
||||
#define CAP_LIMIT 62
|
||||
|
||||
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());
|
||||
}
|
||||
assert_cc(CAP_LAST_CAP <= CAP_LIMIT);
|
||||
|
||||
/* 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. */
|
||||
@ -59,7 +30,42 @@ 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 }
|
||||
#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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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. */
|
||||
|
||||
@ -202,8 +202,7 @@ libbasic_static = static_library(
|
||||
fundamental_sources,
|
||||
include_directories : basic_includes,
|
||||
implicit_include_directories : false,
|
||||
dependencies : [libcap,
|
||||
libdl,
|
||||
dependencies : [libdl,
|
||||
libgcrypt_cflags,
|
||||
liblz4_cflags,
|
||||
libm,
|
||||
|
||||
@ -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(/* ret_before_caps = */ NULL);
|
||||
r = capability_gain_cap_setpcap();
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_CAPABILITIES;
|
||||
return log_error_errno(r, "Failed to gain CAP_SETPCAP for setting secure bits");
|
||||
|
||||
@ -18,4 +18,3 @@ URL: {{PROJECT_URL}}
|
||||
Version: {{PROJECT_VERSION}}
|
||||
Libs: -L${libdir} -lsystemd
|
||||
Cflags: -I${includedir}
|
||||
Requires.private: libcap
|
||||
|
||||
@ -18,4 +18,3 @@ Version: {{PROJECT_VERSION}}
|
||||
Libs: -L${libdir} -ludev
|
||||
Libs.private: -lrt -pthread
|
||||
Cflags: -I${includedir}
|
||||
Requires.private: libcap
|
||||
|
||||
@ -449,7 +449,9 @@ static int vl_method_mount_image(
|
||||
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
|
||||
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
|
||||
(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;
|
||||
|
||||
/* Let's see if we have acquired the privilege to mount untrusted images already */
|
||||
|
||||
@ -480,7 +480,7 @@ static int parse_capability_spec(const char *spec, uint64_t *ret_mask) {
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
name = capability_to_name(i);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -361,7 +361,6 @@ libshared_deps = [threads,
|
||||
libacl_cflags,
|
||||
libaudit_cflags,
|
||||
libblkid_cflags,
|
||||
libcap,
|
||||
libcrypt,
|
||||
libdl,
|
||||
libgcrypt_cflags,
|
||||
|
||||
@ -56,7 +56,6 @@ executables += [
|
||||
'extract' : systemctl_extract_sources,
|
||||
'link_with' : systemctl_link_with,
|
||||
'dependencies' : [
|
||||
libcap,
|
||||
liblz4_cflags,
|
||||
libxz_cflags,
|
||||
libzstd_cflags,
|
||||
|
||||
@ -69,6 +69,8 @@ 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',
|
||||
@ -255,14 +257,6 @@ 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',
|
||||
|
||||
@ -11,11 +11,6 @@
|
||||
#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));
|
||||
@ -28,12 +23,12 @@ TEST(cap_list) {
|
||||
ASSERT_STREQ(CAPABILITY_TO_STRING(62), "0x3e");
|
||||
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;
|
||||
|
||||
assert_se(n = capability_to_name(i));
|
||||
assert_se(capability_from_name(n) == i);
|
||||
printf("%s = %i\n", n, i);
|
||||
ASSERT_NOT_NULL(n = capability_to_name(i));
|
||||
ASSERT_OK_EQ(capability_from_name(n), (int) i);
|
||||
printf("%s = %u\n", n, i);
|
||||
|
||||
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("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) {
|
||||
@ -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_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 */
|
||||
if (cap_last_cap() < 62)
|
||||
if (cap_last_cap() < CAP_LIMIT)
|
||||
test_capability_set_to_string_invalid(all_capabilities() + 1);
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#define TEST_CAPABILITY_C
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "capability-list.h"
|
||||
#include "capability-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
@ -84,18 +85,15 @@ static void fork_test(void (*test_func)(void)) {
|
||||
}
|
||||
|
||||
static void show_capabilities(void) {
|
||||
cap_t caps;
|
||||
char *text;
|
||||
_cleanup_free_ char *e = NULL, *p = NULL, *i = NULL;
|
||||
CapabilityQuintet q;
|
||||
|
||||
caps = cap_get_proc();
|
||||
assert_se(caps);
|
||||
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));
|
||||
|
||||
text = cap_to_text(caps, NULL);
|
||||
assert_se(text);
|
||||
|
||||
log_info("Capabilities:%s", text);
|
||||
cap_free(caps);
|
||||
cap_free(text);
|
||||
log_info("Capabilities:e=%s p=%s, i=%s", e, p, i);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
cap_t caps;
|
||||
uint64_t set = 0;
|
||||
cap_flag_value_t fv;
|
||||
ASSERT_OK_EQ_ERRNO(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0), 0);
|
||||
|
||||
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_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);
|
||||
ASSERT_OK_EQ_ERRNO(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0), 0);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
/* 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);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
static void test_capability_get_ambient(void) {
|
||||
@ -338,8 +302,6 @@ 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);
|
||||
|
||||
|
||||
@ -93,6 +93,15 @@ 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/
|
||||
|
||||
@ -42,11 +42,11 @@ else
|
||||
|
||||
apt-get update
|
||||
apt-get install -y gperf m4 gettext python3-pip \
|
||||
libcap-dev libmount-dev \
|
||||
libmount-dev \
|
||||
pkg-config wget python3-jinja2 zipmerge zstd
|
||||
|
||||
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
|
||||
|
||||
pip3 install -r .github/workflows/requirements.txt --require-hashes
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user