1
0
mirror of https://github.com/systemd/systemd synced 2026-04-23 07:24:51 +02:00

Compare commits

...

12 Commits

Author SHA1 Message Date
Grigori Goronzy
da29de23ef tpm2: enable parameter encryption
Use a salted, unbound HMAC session with the primary key used as tpmKey,
which mean that the random salt will be encrypted with the primary
key while in transit. Decrypt/encrypt flags are set on the new session
with AES in CFB mode. There is no fallback to XOR mode.

This provides confidentiality and replay protection, both when sealing
and unsealing. There is no protection against man in the middle
attacks since we have no way to authenticate the TPM at the moment.
The exception is unsealing with PIN, as an attacker will be unable
to generate the proper HMAC digest.
2022-03-16 22:52:42 +01:00
Anita Zhang
c68ac12a0e
Merge pull request #22768 from poettering/cgls-delegate-xattr
make "delegate" xattr also available for unpriv programs
2022-03-16 14:49:00 -07:00
Lennart Poettering
e127ac90ef
Merge pull request #22761 from poettering/pcr-fix
sd-boot: change kernel cmdline PCR from 8 to 12
2022-03-16 22:32:43 +01:00
Lennart Poettering
bbfabc4498 NEWS: add entry announcing PCR change 2022-03-16 17:44:46 +01:00
Lennart Poettering
27818e2ece man: only document new PCR 12 2022-03-16 17:44:46 +01:00
Lennart Poettering
4d32507f51 sd-boot: measure kernel cmdline into PCR 12 rather than 8
Apparently Grub is measuring all kinds of garbage into PCR 8. Since people
apparently chainload sd-boot from grub, let's thus stay away from PCR 8,
and use PCR 12 instead for the kernel command line.

As discussed here: #22635

Fixes: #22635
2022-03-16 17:44:32 +01:00
Lennart Poettering
34604d6af7 boot: use UINT32 as type for PCR indexes
Thisis what the TPM2/UEFI headers use, and most of our EFI codebase.
Let's also use the same type here in cpio.[ch]
2022-03-16 17:33:28 +01:00
Lennart Poettering
c5a408ee35 boot: drop const from EFI_PHYSICAL_ADDRESS parameter
It's not a pointer after all, but a numeric value. As such the const
applies to the value and not the target, but we genreally don#t do that
for value parameters. Hence drop the const.
2022-03-16 17:33:28 +01:00
Lennart Poettering
1fa3b6c247 cgroup: also set user.invocation_id in addition to trusted.invocation_id
Similar thinking as the preceeding commit.

(While we are at it, let's unify some code we use over and over again in
two helper functions)
2022-03-16 16:47:07 +01:00
Lennart Poettering
200aa3583f docs: document the user.delegate xattr 2022-03-16 16:32:47 +01:00
Lennart Poettering
d9bc1c3614 cgroup: also indicate cgroup delegation state in user-accessible xattr
So far we set the "trusted.delegate" xattr on cgroups where delegation
is on. This duplicates this behaviour with the "user.delegate" xattr.
This has two benefits:

1. unprivileged clients can *read* the xattr. "systemd-cgls" can thus
   show delegated cgroups as such properly, even when invoked without
   privs

2. unprivileged systemd instances can set the xattr, i.e. when systemd
   --user delegates a cgroup to further payloads.

This weakens security a tiny bit, given that code that got a cgroup
delegated can manipulate the xattr, but I think that's OK, given they
have a higher trust level regarding cgroups anyway, if they got a
subtree delegated, and access controls on the cgroup itself are still
enforced. Moreover PID 1 as the cgroup manager only sets these xattrs,
never reads them — the xattr is primarily a way to tell payloads about
the delegation, and it's strictly this one way.
2022-03-16 16:32:44 +01:00
Lennart Poettering
bde2607563 cgroup-show: split out delegation xattr check into its own function
Just some refactoring.
2022-03-16 14:30:01 +01:00
15 changed files with 267 additions and 102 deletions

13
NEWS
View File

@ -120,6 +120,19 @@ CHANGES WITH 251:
250. For newer kernels, non-x86 systems, or older x86 systems, 250. For newer kernels, non-x86 systems, or older x86 systems,
there should be no visible changes. there should be no visible changes.
* sd-boot will now measure the kernel command line into TPM PCR 12
rather than PCR 8. This improves usefulness of the measurements on
sytems where sd-boot is chainloaded from Grub. Grub measures all
commands its executes into PCR 8, which makes it very hard to use
reasonably, hence separate ourselves from that and use PCR 12
instead, which is already what certain Ubuntu editions use it for. To
retain compatibility with systems running older systemd systems a new
Meson option 'efi-tpm-pcr-compat' has been added (which defaults to
false). If enabled, the measurement is done twice: into the new-style
PCR 12 *and* the old-style PCR 8. It's strongly advised to migrate
all users to PCR 12 for this purpose in the long run, as we intend to
remove this compatibility feature again in two year's time.
CHANGES WITH 250: CHANGES WITH 250:
* Support for encrypted and authenticated credentials has been added. * Support for encrypted and authenticated credentials has been added.

View File

@ -253,6 +253,13 @@ So, if you want to do your own raw cgroups kernel level access, then allocate a
scope unit, or a service unit (or just use the service unit you already have scope unit, or a service unit (or just use the service unit you already have
for your service code), and turn on delegation for it. for your service code), and turn on delegation for it.
The service manager sets the `user.delegate` extended attribute (readable via
`getxattr(2)` and related calls) to the character `1` on cgroup directories
where delegation is enabled (and removes it on those cgroups where it is
not). This may be used by service programs to determine whether a cgroup tree
was delegated to them. Note that this is only supported on kernels 5.6 and
newer in combination with systemd 251 and newer.
(OK, here's one caveat: if you turn on delegation for a service, and that (OK, here's one caveat: if you turn on delegation for a service, and that
service has `ExecStartPost=`, `ExecReload=`, `ExecStop=` or `ExecStopPost=` service has `ExecStartPost=`, `ExecReload=`, `ExecStop=` or `ExecStopPost=`
set, then these commands will be executed within the `.control/` sub-cgroup of set, then these commands will be executed within the `.control/` sub-cgroup of

View File

@ -214,6 +214,8 @@
<!-- See: https://github.com/rhboot/shim/blob/main/README.tpm --> <!-- See: https://github.com/rhboot/shim/blob/main/README.tpm -->
<!-- See: https://www.gnu.org/software/grub/manual/grub/html_node/Measured-Boot.html --> <!-- See: https://www.gnu.org/software/grub/manual/grub/html_node/Measured-Boot.html -->
<!-- See: https://sourceforge.net/p/linux-ima/wiki/Home/ --> <!-- See: https://sourceforge.net/p/linux-ima/wiki/Home/ -->
<!-- See: https://github.com/tianocore-docs/edk2-TrustedBootChain/blob/main/4_Other_Trusted_Boot_Chains.md -->
<!-- See: https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers -->
<tgroup cols='2' align='left' colsep='1' rowsep='1'> <tgroup cols='2' align='left' colsep='1' rowsep='1'>
<colspec colname="pcr" /> <colspec colname="pcr" />
@ -267,14 +269,14 @@
<entry>Secure boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes. The shim project will measure most of its (non-MOK) certificates and SBAT data into this PCR.</entry> <entry>Secure boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes. The shim project will measure most of its (non-MOK) certificates and SBAT data into this PCR.</entry>
</row> </row>
<row> <!-- Grub measures all its commands and the kernel command line into PCR 8… -->
<entry>8</entry>
<entry><citerefentry><refentrytitle>sd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the kernel command line into this PCR.</entry>
<!-- Grub measures all its commands and the kernel command line into PCR 8 too… -->
</row>
<!-- Grub measures all files it reads (including kernel image, initrd, …) into PCR 9… --> <!-- Grub measures all files it reads (including kernel image, initrd, …) into PCR 9… -->
<row>
<entry>12</entry>
<entry><citerefentry><refentrytitle>sd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the kernel command line into this PCR.</entry>
</row>
<row> <row>
<entry>10</entry> <entry>10</entry>
<entry>The IMA project measures its runtime state into this PCR.</entry> <entry>The IMA project measures its runtime state into this PCR.</entry>

View File

@ -426,6 +426,8 @@ option('efi-libdir', type : 'string',
description : 'path to the EFI lib directory') description : 'path to the EFI lib directory')
option('efi-includedir', type : 'string', value : '/usr/include/efi', option('efi-includedir', type : 'string', value : '/usr/include/efi',
description : 'path to the EFI header directory') description : 'path to the EFI header directory')
option('efi-tpm-pcr-compat', type : 'boolean', value : 'false',
description : 'Measure kernel command line also into TPM PCR 8 (in addition to 12)')
option('sbat-distro', type : 'string', value : 'auto', option('sbat-distro', type : 'string', value : 'auto',
description : 'SBAT distribution ID, e.g. fedora, or auto for autodetection') description : 'SBAT distribution ID, e.g. fedora, or auto for autodetection')
option('sbat-distro-generation', type : 'integer', value : 1, option('sbat-distro-generation', type : 'integer', value : 1,

View File

@ -311,7 +311,8 @@ EFI_STATUS pack_cpio(
const CHAR8 *target_dir_prefix, const CHAR8 *target_dir_prefix,
UINT32 dir_mode, UINT32 dir_mode,
UINT32 access_mode, UINT32 access_mode,
UINTN tpm_pcr, const UINT32 tpm_pcr[],
UINTN n_tpm_pcr,
const CHAR16 *tpm_description, const CHAR16 *tpm_description,
void **ret_buffer, void **ret_buffer,
UINTN *ret_buffer_size) { UINTN *ret_buffer_size) {
@ -328,6 +329,7 @@ EFI_STATUS pack_cpio(
assert(loaded_image); assert(loaded_image);
assert(target_dir_prefix); assert(target_dir_prefix);
assert(tpm_pcr || n_tpm_pcr == 0);
assert(ret_buffer); assert(ret_buffer);
assert(ret_buffer_size); assert(ret_buffer_size);
@ -449,13 +451,15 @@ EFI_STATUS pack_cpio(
if (EFI_ERROR(err)) if (EFI_ERROR(err))
return log_error_status_stall(err, L"Failed to pack cpio trailer: %r"); return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
err = tpm_log_event( for (UINTN i = 0; i < n_tpm_pcr; i++) {
tpm_pcr, err = tpm_log_event(
POINTER_TO_PHYSICAL_ADDRESS(buffer), tpm_pcr[i],
buffer_size, POINTER_TO_PHYSICAL_ADDRESS(buffer),
tpm_description); buffer_size,
if (EFI_ERROR(err)) tpm_description);
log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr, tpm_description, err); if (EFI_ERROR(err))
log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr[i], tpm_description, err);
}
*ret_buffer = TAKE_PTR(buffer); *ret_buffer = TAKE_PTR(buffer);
*ret_buffer_size = buffer_size; *ret_buffer_size = buffer_size;

View File

@ -10,8 +10,8 @@ EFI_STATUS pack_cpio(
const CHAR8 *target_dir_prefix, const CHAR8 *target_dir_prefix,
UINT32 dir_mode, UINT32 dir_mode,
UINT32 access_mode, UINT32 access_mode,
UINTN tpm_pcr, const UINT32 tpm_pcr[],
UINTN n_tpm_pcr,
const CHAR16 *tpm_description, const CHAR16 *tpm_description,
void **ret_buffer, void **ret_buffer,
UINTN *ret_buffer_size); UINTN *ret_buffer_size);

View File

@ -139,12 +139,16 @@ BOOLEAN tpm_present(void) {
return tcg2_interface_check() || tcg1_interface_check(); return tcg2_interface_check() || tcg1_interface_check();
} }
EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) { EFI_STATUS tpm_log_event(UINT32 pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) {
EFI_TCG *tpm1; EFI_TCG *tpm1;
EFI_TCG2 *tpm2; EFI_TCG2 *tpm2;
assert(description); assert(description);
/* PCR disabled */
if (pcrindex == UINT32_MAX)
return EFI_SUCCESS;
tpm2 = tcg2_interface_check(); tpm2 = tcg2_interface_check();
if (tpm2) if (tpm2)
return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description); return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
@ -162,11 +166,15 @@ EFI_STATUS tpm_log_load_options(const CHAR16 *load_options) {
/* Measures a load options string into the TPM2, i.e. the kernel command line */ /* Measures a load options string into the TPM2, i.e. the kernel command line */
err = tpm_log_event(TPM_PCR_INDEX_KERNEL_PARAMETERS, for (UINTN i = 0; i < 2; i++) {
POINTER_TO_PHYSICAL_ADDRESS(load_options), UINT32 pcr = i == 0 ? TPM_PCR_INDEX_KERNEL_PARAMETERS : TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT;
StrSize(load_options), load_options);
if (EFI_ERROR(err)) err = tpm_log_event(pcr,
return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement: %r", err); POINTER_TO_PHYSICAL_ADDRESS(load_options),
StrSize(load_options), load_options);
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement to PCR %u: %r", pcr, err);
}
return EFI_SUCCESS; return EFI_SUCCESS;
} }

View File

@ -3,10 +3,24 @@
#include <efi.h> #include <efi.h>
/* This TPM PCR is where we extend the kernel command line and any passed credentials here. */
#define TPM_PCR_INDEX_KERNEL_PARAMETERS 12U
/* We used to write the the kernel command line/credentials into PCR 8, in systemd <= 250. Let's provide for
* some compatibility. (Remove in 2023!) */
#if EFI_TPM_PCR_COMPAT
#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT 8U
#else
#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT UINT32_MAX
#endif
/* This TPM PCR is where most Linux infrastructure extends the initrd binary images into, and so do we. */
#define TPM_PCR_INDEX_INITRD 4U
#if ENABLE_TPM #if ENABLE_TPM
BOOLEAN tpm_present(void); BOOLEAN tpm_present(void);
EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description); EFI_STATUS tpm_log_event(UINT32 pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description);
EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline); EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline);
#else #else
@ -14,9 +28,11 @@ EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline);
static inline BOOLEAN tpm_present(void) { static inline BOOLEAN tpm_present(void) {
return FALSE; return FALSE;
} }
static inline EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) {
static inline EFI_STATUS tpm_log_event(UINT32 pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) {
return EFI_SUCCESS; return EFI_SUCCESS;
} }
static inline EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline) { static inline EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline) {
return EFI_SUCCESS; return EFI_SUCCESS;
} }

View File

@ -104,6 +104,7 @@ conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0])
efi_conf = configuration_data() efi_conf = configuration_data()
efi_conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0]) efi_conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0])
efi_conf.set10('ENABLE_TPM', get_option('tpm')) efi_conf.set10('ENABLE_TPM', get_option('tpm'))
efi_conf.set10('EFI_TPM_PCR_COMPAT', get_option('efi-tpm-pcr-compat'))
foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit'] foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit']
c = get_option('efi-' + ctype).split(',') c = get_option('efi-' + ctype).split(',')

View File

@ -234,7 +234,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
(const CHAR8*) ".extra/credentials", (const CHAR8*) ".extra/credentials",
/* dir_mode= */ 0500, /* dir_mode= */ 0500,
/* access_mode= */ 0400, /* access_mode= */ 0400,
/* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS, /* tpm_pcr= */ (UINT32[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
/* n_tpm_pcr= */ 2,
L"Credentials initrd", L"Credentials initrd",
&credential_initrd, &credential_initrd,
&credential_initrd_size); &credential_initrd_size);
@ -245,7 +246,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
(const CHAR8*) ".extra/global_credentials", (const CHAR8*) ".extra/global_credentials",
/* dir_mode= */ 0500, /* dir_mode= */ 0500,
/* access_mode= */ 0400, /* access_mode= */ 0400,
/* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS, /* tpm_pcr= */ (UINT32[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
/* n_tpm_pcr= */ 2,
L"Global credentials initrd", L"Global credentials initrd",
&global_credential_initrd, &global_credential_initrd,
&global_credential_initrd_size); &global_credential_initrd_size);
@ -256,7 +258,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
(const CHAR8*) ".extra/sysext", (const CHAR8*) ".extra/sysext",
/* dir_mode= */ 0555, /* dir_mode= */ 0555,
/* access_mode= */ 0444, /* access_mode= */ 0444,
/* tpm_pcr= */ TPM_PCR_INDEX_INITRD, /* tpm_pcr= */ (UINT32[]) { TPM_PCR_INDEX_INITRD },
/* n_tpm_pcr= */ 1,
L"System extension initrd", L"System extension initrd",
&sysext_initrd, &sysext_initrd,
&sysext_initrd_size); &sysext_initrd_size);

View File

@ -6,13 +6,6 @@
#include "string-util-fundamental.h" #include "string-util-fundamental.h"
/* This TPM PCR is where most Linux infrastructure extends the kernel command line into, and so do we. We also extend
* any passed credentials here. */
#define TPM_PCR_INDEX_KERNEL_PARAMETERS 8
/* This TPM PCR is where most Linux infrastructure extends the initrd binary images into, and so do we. */
#define TPM_PCR_INDEX_INITRD 4
#define offsetof(type, member) __builtin_offsetof(type, member) #define offsetof(type, member) __builtin_offsetof(type, member)
#define UINTN_MAX (~(UINTN)0) #define UINTN_MAX (~(UINTN)0)

View File

@ -736,9 +736,44 @@ int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const
UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_low); UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_low);
UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_min); UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_min);
static void unit_set_xattr_graceful(Unit *u, const char *cgroup_path, const char *name, const void *data, size_t size) {
int r;
assert(u);
assert(name);
if (!cgroup_path) {
if (!u->cgroup_path)
return;
cgroup_path = u->cgroup_path;
}
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, name, data, size, 0);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set '%s' xattr on control group %s, ignoring: %m", name, empty_to_root(cgroup_path));
}
static void unit_remove_xattr_graceful(Unit *u, const char *cgroup_path, const char *name) {
int r;
assert(u);
assert(name);
if (!cgroup_path) {
if (!u->cgroup_path)
return;
cgroup_path = u->cgroup_path;
}
r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, name);
if (r < 0 && r != -ENODATA)
log_unit_debug_errno(u, r, "Failed to remove '%s' xattr flag on control group %s, ignoring: %m", name, empty_to_root(cgroup_path));
}
void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path) { void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path) {
CGroupContext *c; CGroupContext *c;
int r;
assert(u); assert(u);
@ -746,59 +781,50 @@ void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path) {
if (!c) if (!c)
return; return;
if (c->moom_preference == MANAGED_OOM_PREFERENCE_OMIT) { if (c->moom_preference == MANAGED_OOM_PREFERENCE_OMIT)
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_omit", "1", 1, 0); unit_set_xattr_graceful(u, cgroup_path, "user.oomd_omit", "1", 1);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set oomd_omit flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
}
if (c->moom_preference == MANAGED_OOM_PREFERENCE_AVOID) { if (c->moom_preference == MANAGED_OOM_PREFERENCE_AVOID)
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_avoid", "1", 1, 0); unit_set_xattr_graceful(u, cgroup_path, "user.oomd_avoid", "1", 1);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set oomd_avoid flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
}
if (c->moom_preference != MANAGED_OOM_PREFERENCE_AVOID) { if (c->moom_preference != MANAGED_OOM_PREFERENCE_AVOID)
r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_avoid"); unit_remove_xattr_graceful(u, cgroup_path, "user.oomd_avoid");
if (r < 0 && r != -ENODATA)
log_unit_debug_errno(u, r, "Failed to remove oomd_avoid flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
}
if (c->moom_preference != MANAGED_OOM_PREFERENCE_OMIT) { if (c->moom_preference != MANAGED_OOM_PREFERENCE_OMIT)
r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_omit"); unit_remove_xattr_graceful(u, cgroup_path, "user.oomd_omit");
if (r < 0 && r != -ENODATA)
log_unit_debug_errno(u, r, "Failed to remove oomd_omit flag on control group %s, ignoring: %m", empty_to_root(cgroup_path));
}
} }
static void cgroup_xattr_apply(Unit *u) { static void cgroup_xattr_apply(Unit *u) {
int r; const char *xn;
bool b;
assert(u); assert(u);
if (!MANAGER_IS_SYSTEM(u->manager)) if (!MANAGER_IS_SYSTEM(u->manager))
return; return;
if (!sd_id128_is_null(u->invocation_id)) { b = !sd_id128_is_null(u->invocation_id);
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, FOREACH_STRING(xn, "trusted.invocation_id", "user.invocation_id") {
"trusted.invocation_id", if (b)
SD_ID128_TO_STRING(u->invocation_id), 32, unit_set_xattr_graceful(u, NULL, xn, SD_ID128_TO_STRING(u->invocation_id), 32);
0); else
if (r < 0) unit_remove_xattr_graceful(u, NULL, xn);
log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
} }
if (unit_cgroup_delegate(u)) { /* Indicate on the cgroup whether delegation is on, via an xattr. This is best-effort, as old kernels
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, * didn't support xattrs on cgroups at all. Later they got support for setting 'trusted.*' xattrs,
"trusted.delegate", * and even later 'user.*' xattrs. We started setting this field when 'trusted.*' was added, and
"1", 1, * given this is now pretty much API, let's continue to support that. But also set 'user.*' as well,
0); * since it is readable by any user, not just CAP_SYS_ADMIN. This hence comes with slightly weaker
if (r < 0) * security (as users who got delegated cgroups could turn it off if they like), but this shouldn't
log_unit_debug_errno(u, r, "Failed to set delegate flag on control group %s, ignoring: %m", empty_to_root(u->cgroup_path)); * be a big problem given this communicates delegation state to clients, but the manager never reads
} else { * it. */
r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "trusted.delegate"); b = unit_cgroup_delegate(u);
if (r < 0 && r != -ENODATA) FOREACH_STRING(xn, "trusted.delegate", "user.delegate") {
log_unit_debug_errno(u, r, "Failed to remove delegate flag on control group %s, ignoring: %m", empty_to_root(u->cgroup_path)); if (b)
unit_set_xattr_graceful(u, NULL, xn, "1", 1);
else
unit_remove_xattr_graceful(u, NULL, xn);
} }
cgroup_oomd_xattr_apply(u, u->cgroup_path); cgroup_oomd_xattr_apply(u, u->cgroup_path);

View File

@ -128,6 +128,31 @@ static int show_cgroup_one_by_path(
return 0; return 0;
} }
static int is_delegated(int cgfd, const char *path) {
_cleanup_free_ char *b = NULL;
int r;
assert(cgfd >= 0 || path);
r = getxattr_malloc(cgfd < 0 ? path : FORMAT_PROC_FD_PATH(cgfd), "trusted.delegate", &b);
if (r == -ENODATA) {
/* If the trusted xattr isn't set (preferred), then check the untrusted one. Under the
* assumption that whoever is trusted enough to own the cgroup, is also trusted enough to
* decide if it is delegated or not this should be safe. */
r = getxattr_malloc(cgfd < 0 ? path : FORMAT_PROC_FD_PATH(cgfd), "user.delegate", &b);
if (r == -ENODATA)
return false;
}
if (r < 0)
return log_debug_errno(r, "Failed to read delegate xattr, ignoring: %m");
r = parse_boolean(b);
if (r < 0)
return log_debug_errno(r, "Failed to parse delegate xattr boolean value, ignoring: %m");
return r;
}
static int show_cgroup_name( static int show_cgroup_name(
const char *path, const char *path,
const char *prefix, const char *prefix,
@ -137,7 +162,7 @@ static int show_cgroup_name(
uint64_t cgroupid = UINT64_MAX; uint64_t cgroupid = UINT64_MAX;
_cleanup_free_ char *b = NULL; _cleanup_free_ char *b = NULL;
_cleanup_close_ int fd = -1; _cleanup_close_ int fd = -1;
bool delegate = false; bool delegate;
int r; int r;
if (FLAGS_SET(flags, OUTPUT_CGROUP_XATTRS) || FLAGS_SET(flags, OUTPUT_CGROUP_ID)) { if (FLAGS_SET(flags, OUTPUT_CGROUP_XATTRS) || FLAGS_SET(flags, OUTPUT_CGROUP_ID)) {
@ -146,19 +171,7 @@ static int show_cgroup_name(
log_debug_errno(errno, "Failed to open cgroup '%s', ignoring: %m", path); log_debug_errno(errno, "Failed to open cgroup '%s', ignoring: %m", path);
} }
r = getxattr_malloc(fd < 0 ? path : FORMAT_PROC_FD_PATH(fd), "trusted.delegate", &b); delegate = is_delegated(fd, path) > 0;
if (r < 0) {
if (r != -ENODATA)
log_debug_errno(r, "Failed to read trusted.delegate extended attribute, ignoring: %m");
} else {
r = parse_boolean(b);
if (r < 0)
log_debug_errno(r, "Failed to parse trusted.delegate extended attribute boolean value, ignoring: %m");
else
delegate = r > 0;
b = mfree(b);
}
if (FLAGS_SET(flags, OUTPUT_CGROUP_ID)) { if (FLAGS_SET(flags, OUTPUT_CGROUP_ID)) {
cg_file_handle fh = CG_FILE_HANDLE_INIT; cg_file_handle fh = CG_FILE_HANDLE_INIT;

View File

@ -36,6 +36,7 @@ TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySes
TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs) = NULL; TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs) = NULL;
TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL; TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL; TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask);
TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue) = NULL; TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue) = NULL;
TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData) = NULL; TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData) = NULL;
@ -66,6 +67,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_PolicyPCR), DLSYM_ARG(Esys_PolicyPCR),
DLSYM_ARG(Esys_StartAuthSession), DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup), DLSYM_ARG(Esys_Startup),
DLSYM_ARG(Esys_TRSess_SetAttributes),
DLSYM_ARG(Esys_TR_SetAuth), DLSYM_ARG(Esys_TR_SetAuth),
DLSYM_ARG(Esys_Unseal)); DLSYM_ARG(Esys_Unseal));
if (r < 0) if (r < 0)
@ -595,8 +597,71 @@ static int tpm2_get_best_pcr_bank(
return 0; return 0;
} }
static int tpm2_make_encryption_session(
ESYS_CONTEXT *c,
ESYS_TR tpmKey,
ESYS_TR *ret_session) {
static const TPMT_SYM_DEF symmetric = {
.algorithm = TPM2_ALG_AES,
.keyBits = {
.aes = 128,
},
.mode = {
.aes = TPM2_ALG_CFB,
},
};
const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
TPMA_SESSION_CONTINUESESSION;
ESYS_TR session = ESYS_TR_NONE;
TSS2_RC rc;
assert(c);
log_debug("Starting HMAC encryption session.");
/* Start a salted, unbound HMAC session with a well-known key (e.g. primary key) as tpmKey, which
* means that the random salt will be encrypted with the well-known key. That way, only the TPM can
* recover the salt, which is then used for key derivation. */
rc = sym_Esys_StartAuthSession(
c,
tpmKey,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
NULL,
TPM2_SE_HMAC,
&symmetric,
TPM2_ALG_SHA256,
&session);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
/* Enable parameter encryption/decryption with AES in CFB mode. Together with HMAC digests (which are
* always used for sessions), this provides confidentiality, integrity and replay protection for
* operations that use this session. */
rc = sym_Esys_TRSess_SetAttributes(c, session, sessionAttributes, 0xff);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to configure TPM session: %s",
sym_Tss2_RC_Decode(rc));
if (ret_session) {
*ret_session = session;
session = ESYS_TR_NONE;
}
session = flush_context_verbose(c, session);
return 0;
}
static int tpm2_make_pcr_session( static int tpm2_make_pcr_session(
ESYS_CONTEXT *c, ESYS_CONTEXT *c,
ESYS_TR tpmKey,
ESYS_TR parent_session,
uint32_t pcr_mask, uint32_t pcr_mask,
uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */ uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
bool use_pin, bool use_pin,
@ -645,9 +710,9 @@ static int tpm2_make_pcr_session(
rc = sym_Esys_StartAuthSession( rc = sym_Esys_StartAuthSession(
c, c,
tpmKey,
ESYS_TR_NONE, ESYS_TR_NONE,
ESYS_TR_NONE, parent_session,
ESYS_TR_NONE,
ESYS_TR_NONE, ESYS_TR_NONE,
ESYS_TR_NONE, ESYS_TR_NONE,
NULL, NULL,
@ -771,7 +836,7 @@ int tpm2_seal(
_cleanup_(erase_and_freep) void *secret = NULL; _cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_free_ void *blob = NULL, *hash = NULL; _cleanup_free_ void *blob = NULL, *hash = NULL;
TPM2B_SENSITIVE_CREATE hmac_sensitive; TPM2B_SENSITIVE_CREATE hmac_sensitive;
ESYS_TR primary = ESYS_TR_NONE; ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE;
TPMI_ALG_PUBLIC primary_alg; TPMI_ALG_PUBLIC primary_alg;
TPM2B_PUBLIC hmac_template; TPM2B_PUBLIC hmac_template;
TPMI_ALG_HASH pcr_bank; TPMI_ALG_HASH pcr_bank;
@ -816,8 +881,12 @@ int tpm2_seal(
if (r < 0) if (r < 0)
return r; return r;
r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, !!pin, NULL, &policy_digest, r = tpm2_make_encryption_session(c.esys_context, primary, &session);
&pcr_bank); if (r < 0)
goto finish;
r = tpm2_make_pcr_session(c.esys_context, primary, session, pcr_mask, UINT16_MAX, !!pin, NULL,
&policy_digest, &pcr_bank);
if (r < 0) if (r < 0)
goto finish; goto finish;
@ -869,7 +938,7 @@ int tpm2_seal(
rc = sym_Esys_Create( rc = sym_Esys_Create(
c.esys_context, c.esys_context,
primary, primary,
ESYS_TR_PASSWORD, session, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE, ESYS_TR_NONE,
ESYS_TR_NONE, ESYS_TR_NONE,
&hmac_sensitive, &hmac_sensitive,
@ -951,6 +1020,7 @@ int tpm2_seal(
finish: finish:
explicit_bzero_safe(&hmac_sensitive, sizeof(hmac_sensitive)); explicit_bzero_safe(&hmac_sensitive, sizeof(hmac_sensitive));
primary = flush_context_verbose(c.esys_context, primary); primary = flush_context_verbose(c.esys_context, primary);
session = flush_context_verbose(c.esys_context, session);
return r; return r;
} }
@ -968,7 +1038,8 @@ int tpm2_unseal(
size_t *ret_secret_size) { size_t *ret_secret_size) {
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {}; _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE, hmac_key = ESYS_TR_NONE; ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE, hmac_session = ESYS_TR_NONE,
hmac_key = ESYS_TR_NONE;
_cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL; _cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL; _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
_cleanup_(erase_and_freep) char *secret = NULL; _cleanup_(erase_and_freep) char *secret = NULL;
@ -1019,7 +1090,16 @@ int tpm2_unseal(
if (r < 0) if (r < 0)
return r; return r;
r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, !!pin, &session, &policy_digest, NULL); r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL);
if (r < 0)
return r;
r = tpm2_make_encryption_session(c.esys_context, primary, &hmac_session);
if (r < 0)
goto finish;
r = tpm2_make_pcr_session(c.esys_context, primary, hmac_session, pcr_mask, pcr_bank, !!pin, &session,
&policy_digest, NULL);
if (r < 0) if (r < 0)
goto finish; goto finish;
@ -1030,16 +1110,12 @@ int tpm2_unseal(
return log_error_errno(SYNTHETIC_ERRNO(EPERM), return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"Current policy digest does not match stored policy digest, cancelling TPM2 authentication attempt."); "Current policy digest does not match stored policy digest, cancelling TPM2 authentication attempt.");
r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL);
if (r < 0)
return r;
log_debug("Loading HMAC key into TPM."); log_debug("Loading HMAC key into TPM.");
rc = sym_Esys_Load( rc = sym_Esys_Load(
c.esys_context, c.esys_context,
primary, primary,
ESYS_TR_PASSWORD, hmac_session, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE, ESYS_TR_NONE,
ESYS_TR_NONE, ESYS_TR_NONE,
&private, &private,
@ -1084,7 +1160,7 @@ int tpm2_unseal(
c.esys_context, c.esys_context,
hmac_key, hmac_key,
session, session,
ESYS_TR_NONE, hmac_session, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE, ESYS_TR_NONE,
&unsealed); &unsealed);
if (rc != TSS2_RC_SUCCESS) { if (rc != TSS2_RC_SUCCESS) {

View File

@ -31,6 +31,7 @@ extern TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR po
extern TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs); extern TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs);
extern TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle); extern TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle);
extern TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType); extern TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType);
extern TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask);
extern TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue); extern TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue);
extern TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData); extern TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData);