1
0
mirror of https://github.com/systemd/systemd synced 2026-04-25 08:25:12 +02:00

Compare commits

...

9 Commits

Author SHA1 Message Date
Lennart Poettering
7f40cb7c86 sd-bus: switch to a manual overflow check in sd_bus_track_add_name()
This is generally used in a directly client controllable way, hence we
should handle ref count overflow gracefully, instead of hitting an
assert().

As discussed:

https://github.com/systemd/systemd/pull/23099#discussion_r854341850
2022-04-21 08:58:35 +09:00
Lennart Poettering
7d3e856e82 macro: upgrade ref counting overflow check assert() → assert_se()
The overflow check for ref counting should not be subject to NDEBUG,
hence upgrade assert() → assert_se(). (The check for zero is an
immediate bug in our code, and should be impossible to trigger, hence
it's fine if the check is optimized away if people are crazy enough to
set NDEBUG, so that can stay assert())

https://github.com/systemd/systemd/pull/23099#discussion_r854341850
2022-04-21 08:56:32 +09:00
Lennart Poettering
55fc776bbc update TODO 2022-04-20 23:20:08 +02:00
Lennart Poettering
231a1caf5e
Merge pull request #23122 from poettering/creds-has-tpm2
tpm2: beef up tpm2 support checks
2022-04-20 23:18:02 +02:00
Lennart Poettering
eb81249e8a man: document new has-tpm2 verb 2022-04-20 16:58:18 +02:00
Lennart Poettering
6e0cb81505 creds-tool: add new "has-tpm2" verb
Sometimes it's useful from shell scripts to check if we have a working
TPM2 chip around. For example, when putting together encrypted
credentials for the initrd (after all: it might be wise to place the
root pw in a credential for the initrd to consume, but do so only if we
can lock it to the TPM2, and not otherwise, so that we risk nothing).

Hence, let's add a new "systemd-creds has-tpm2" verb: it returns zero if we
have a working TPM2 (which means: supported by kernel + firmware + us),
or non-zero otherwise. Also show which parts are available.

Use-case: in future the 'kernel-install' script should use this when
deciding whether to augment kernels with security sensitive credentials.
2022-04-20 16:58:18 +02:00
Lennart Poettering
0ea911d14c bootctl: use new tpm2_support() helper to show TPM2 info
Let's improve the output regarding TPM2 support in "bootctl": let's show
whether we have local driver support and/or firmware support, and
colorize it.

(For now, don't show if we natively support TPM2, since the tool is
mostly bout boot time stuff, where it dosn't really matter much what we
do in userspace)
2022-04-20 16:58:18 +02:00
Lennart Poettering
e1be2c779c condition: rework ConditionSecurity=tpm2 check on top of tpm2_support()
No change in behaviour. Let's just use our new helper here.
2022-04-20 16:58:18 +02:00
Lennart Poettering
ba57855628 tpm2-util: add helper that checks for the various facets of TPM2 support
So far we were a bit sloppy regarding checks for TPM2 support. Let's
make things more precise and introduce a single helper that checks for
three axis of TPM2 support: whether we have a loaded kernel driver,
whether the firmware used it, and whether we ourselves are compiled for
it.

This only adds the helper. Follow-up patches will use it at various
places.
2022-04-20 16:58:18 +02:00
9 changed files with 143 additions and 34 deletions

4
TODO
View File

@ -78,6 +78,10 @@ Janitorial Clean-ups:
Features:
* drop support for kernels that lack ambient capabilities support (i.e. make
4.3 new baseline). Then drop support for "!!" modifier for ExecStart= which
is only supported for such old kernels
* sd-event: compat wd reuse in inotify code: keep a set of removed watch
descriptors, and clear this set piecemeal when we see the IN_IGNORED event
for it, or when read() returns EAGAIN or on IN_Q_OVERFLOW. Then, whenever we

View File

@ -163,6 +163,20 @@
and thus decryption is entirely automatic.</para></listitem>
</varlistentry>
<varlistentry>
<term><command>has-tpm2</command></term>
<listitem><para>Reports whether the system is equipped with a TPM2 device usable for protecting
credentials. If the a TPM2 device has been discovered, is supported, and is being used by firmware,
by the OS kernel drivers and by userspace (i.e. systemd) this prints <literal>yes</literal> and exits
with exit status zero. If no such device is discovered/supported/used, prints
<literal>no</literal>. Otherwise prints <literal>partial</literal>. In either of these two cases
exits with non-zero exit status. It also shows three lines indicating separately whether drivers,
firmware and the system discovered/support/use TPM2.</para>
<para>Combine with <option>--quiet</option> to suppress the output.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
@ -314,6 +328,14 @@
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--quiet</option></term>
<term><option>-q</option></term>
<listitem><para>When used with <command>has-tpm2</command> suppresses the output, and only returns an
exit status indicating support for TPM2.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="json" />
@ -324,6 +346,12 @@
<title>Exit status</title>
<para>On success, 0 is returned.</para>
<para>In case of the <command>has-tpm2</command> command returns 0 if a TPM2 device is discovered,
supported and used by firmware, driver, and userspace (i.e. systemd). Otherwise returns the OR
combination of the value 1 (in case firmware support is missing), 2 (in case driver support is missing)
and 4 (in case userspace support is missing). If no TPM2 support is available at all, value 7 is hence
returned.</para>
</refsect1>
<refsect1>

View File

@ -399,7 +399,7 @@ static inline int __coverity_check_and_return__(int condition) {
/* For type check. */ \
unsigned *q = &p->n_ref; \
assert(*q > 0); \
assert(*q < UINT_MAX); \
assert_se(*q < UINT_MAX); \
\
(*q)++; \
return p; \

View File

@ -47,6 +47,7 @@
#include "terminal-util.h"
#include "tmpfile-util.h"
#include "tmpfile-util-label.h"
#include "tpm2-util.h"
#include "umask-util.h"
#include "utf8.h"
#include "util.h"
@ -1697,10 +1698,10 @@ static int verb_status(int argc, char *argv[], void *userdata) {
{ EFI_LOADER_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
{ EFI_LOADER_FEATURE_LOAD_DRIVER, "Load drop-in drivers" },
};
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
sd_id128_t loader_part_uuid = SD_ID128_NULL;
uint64_t loader_features = 0;
Tpm2Support s;
int have;
read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
@ -1723,7 +1724,15 @@ static int verb_status(int argc, char *argv[], void *userdata) {
printf(" Secure Boot: %sd (%s)\n",
enable_disable(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)),
secure_boot_mode_to_string(secure));
printf(" TPM2 Support: %s\n", yes_no(efi_has_tpm2()));
s = tpm2_support();
printf(" TPM2 Support: %s%s%s\n",
FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? ansi_highlight_green() :
(s & (TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? "yes" :
(s & TPM2_SUPPORT_FIRMWARE) ? "firmware only, driver unavailable" :
(s & TPM2_SUPPORT_DRIVER) ? "driver only, firmware unavailable" : "no",
ansi_normal());
k = efi_get_reboot_to_firmware();
if (k > 0)

View File

@ -48,6 +48,7 @@ static bool arg_name_any = false;
static usec_t arg_timestamp = USEC_INFINITY;
static usec_t arg_not_after = USEC_INFINITY;
static bool arg_pretty = false;
static bool arg_quiet = false;
static const char* transcode_mode_table[_TRANSCODE_MAX] = {
[TRANSCODE_OFF] = "off",
@ -525,6 +526,34 @@ static int verb_setup(int argc, char **argv, void *userdata) {
return EXIT_SUCCESS;
}
static int verb_has_tpm2(int argc, char **argv, void *userdata) {
Tpm2Support s;
s = tpm2_support();
if (!arg_quiet) {
if (s == TPM2_SUPPORT_FULL)
puts("yes");
else if (s == TPM2_SUPPORT_NONE)
puts("no");
else
puts("partial");
printf("%sfirmware\n"
"%sdriver\n"
"%ssystem\n",
plus_minus(s & TPM2_SUPPORT_FIRMWARE),
plus_minus(s & TPM2_SUPPORT_DRIVER),
plus_minus(s & TPM2_SUPPORT_SYSTEM));
}
/* Return inverted bit flags. So that TPM2_SUPPORT_FULL becomes EXIT_SUCCESS and the other values
* become some reasonable values 17. i.e. the flags we return here tell what is missing rather than
* what is there, acknowledging the fact that for process exit statusses it is customary to return
* zero (EXIT_FAILURE) when all is good, instead of all being bad. */
return ~s & TPM2_SUPPORT_FULL;
}
static int verb_help(int argc, char **argv, void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@ -543,6 +572,7 @@ static int verb_help(int argc, char **argv, void *userdata) {
" ciphertext credential file\n"
" decrypt INPUT [OUTPUT] Decrypt ciphertext credential file and write to\n"
" plaintext credential file\n"
" has-tpm2 Report whether TPM2 support is available\n"
" -h --help Show this help\n"
" --version Show package version\n"
"\n%3$sOptions:%4$s\n"
@ -568,6 +598,7 @@ static int verb_help(int argc, char **argv, void *userdata) {
" Pick TPM2 device\n"
" --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
" Specify TPM2 PCRs to seal against\n"
" -q --quiet Suppress output for 'has-tpm2' verb\n"
"\nSee the %2$s for details.\n"
, program_invocation_short_name
, link
@ -612,6 +643,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "name", required_argument, NULL, ARG_NAME },
{ "timestamp", required_argument, NULL, ARG_TIMESTAMP },
{ "not-after", required_argument, NULL, ARG_NOT_AFTER },
{ "quiet", no_argument, NULL, 'q' },
{}
};
@ -620,7 +652,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hHTp", options, NULL)) >= 0) {
while ((c = getopt_long(argc, argv, "hHTpq", options, NULL)) >= 0) {
switch (c) {
@ -761,6 +793,10 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'q':
arg_quiet = true;
break;
case '?':
return -EINVAL;
@ -784,6 +820,7 @@ static int creds_main(int argc, char *argv[]) {
{ "decrypt", 2, 3, 0, verb_decrypt },
{ "setup", VERB_ANY, 1, 0, verb_setup },
{ "help", VERB_ANY, 1, 0, verb_help },
{ "has-tpm2", VERB_ANY, 1, 0, verb_has_tpm2 },
{}
};

View File

@ -48,7 +48,7 @@ static struct track_item* track_item_free(struct track_item *i) {
return mfree(i);
}
DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(struct track_item, track_item, track_item_free);
DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(struct track_item, track_item, track_item_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_unref);
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(track_item_hash_ops, char, string_hash_func, string_compare_func,
struct track_item, track_item_free);
@ -190,8 +190,18 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
i = hashmap_get(track->names, name);
if (i) {
if (track->recursive)
track_item_ref(i);
if (track->recursive) {
assert(i->n_ref > 0);
/* Manual oveflow check (instead of a DEFINE_TRIVIAL_REF_FUNC() helper or so), so
* that we can return a proper error, given this is almost always called in a
* directly client controllable way, and thus better should never hit an assertion
* here. */
if (i->n_ref >= UINT_MAX)
return -EOVERFLOW;
i->n_ref++;
}
bus_track_remove_from_queue(track);
return 0;

View File

@ -50,6 +50,7 @@
#include "string-table.h"
#include "string-util.h"
#include "tomoyo-util.h"
#include "tpm2-util.h"
#include "udev-util.h"
#include "uid-alloc-range.h"
#include "user-util.h"
@ -623,29 +624,14 @@ static int condition_test_ac_power(Condition *c, char **env) {
}
static int has_tpm2(void) {
int r;
/* Checks whether the system has at least one TPM2 resource manager device, i.e. at least one "tpmrm"
* class device */
* class device. Alternatively, we are also happy if the firmware reports support (this is to cover
* for cases where we simply haven't loaded the driver for it yet, i.e. during early boot where we
* very likely want to use this condition check).
*
* Note that we don't check if we ourselves are built with TPM2 support here! */
r = dir_is_empty("/sys/class/tpmrm");
if (r == 0)
return true; /* nice! we have a device */
/* Hmm, so Linux doesn't know of the TPM2 device (or we couldn't check for it), most likely because
* the driver wasn't loaded yet. Let's see if the firmware knows about a TPM2 device, in this
* case. This way we can answer the TPM2 question already during early boot (where we most likely
* need it) */
if (efi_has_tpm2())
return true;
/* OK, this didn't work either, in this case propagate the original errors */
if (r == -ENOENT)
return false;
if (r < 0)
return log_debug_errno(r, "Failed to determine whether system has TPM2 support: %m");
return !r;
return (tpm2_support() & (TPM2_SUPPORT_DRIVER|TPM2_SUPPORT_FIRMWARE)) != 0;
}
static int condition_test_security(Condition *c, char **env) {

View File

@ -1,7 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "efi-api.h"
#include "extract-word.h"
#include "parse-util.h"
#include "stat-util.h"
#include "tpm2-util.h"
#if HAVE_TPM2
@ -1453,3 +1455,24 @@ int tpm2_primary_alg_from_string(const char *alg) {
return TPM2_ALG_RSA;
return -EINVAL;
}
Tpm2Support tpm2_support(void) {
Tpm2Support support = TPM2_SUPPORT_NONE;
int r;
r = dir_is_empty("/sys/class/tpmrm");
if (r < 0) {
if (r != -ENOENT)
log_debug_errno(r, "Unable to test whether /sys/class/tpmrm/ exists and is populated, assuming it is not: %m");
} else if (r == 0) /* populated! */
support |= TPM2_SUPPORT_DRIVER;
if (efi_has_tpm2())
support |= TPM2_SUPPORT_FIRMWARE;
#if HAVE_TPM2
support |= TPM2_SUPPORT_SYSTEM;
#endif
return support;
}

View File

@ -89,3 +89,15 @@ typedef struct {
uint32_t search_pcr_mask;
const char *device;
} systemd_tpm2_plugin_params;
typedef enum Tpm2Support {
/* NOTE! The systemd-creds tool returns these flags 1:1 as exit status. Hence these flags are pretty
* much ABI! Hence, be extra careful when changing/extending these definitions. */
TPM2_SUPPORT_NONE = 0, /* no support */
TPM2_SUPPORT_FIRMWARE = 1 << 0, /* firmware reports TPM2 was used */
TPM2_SUPPORT_DRIVER = 1 << 1, /* the kernel has a driver loaded for it */
TPM2_SUPPORT_SYSTEM = 1 << 2, /* we support it ourselves */
TPM2_SUPPORT_FULL = TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER|TPM2_SUPPORT_SYSTEM,
} Tpm2Support;
Tpm2Support tpm2_support(void);