1
0
mirror of https://github.com/systemd/systemd synced 2026-04-25 16:34:50 +02:00

Compare commits

...

26 Commits

Author SHA1 Message Date
Lennart Poettering
ebf3ee4105 man: update TPM2 PCR documentation
The assignments were partly simply incorrectly documented, partly changed
with 4d32507f5186a89e98093659fbbe386787a97b9f and partly missing.
Moreover kernel 5.17 now measures all initrds to PCR 9 on its own
(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f046fff8bc4c4d8f8a478022e76e40b818f692df)

Let's correct all this and bring it up-to-date.

And while we are at it extend the docs about this in systemd-stub, with
a new table that indicates which OS resource is protected by which PCR.
2022-04-20 21:30:49 +02:00
Yu Watanabe
f4bdbae725
Merge pull request #23084 from poettering/creds-no-tpm2-fallback
creds: add semi-automatic fallback support for initrd credentials on systems lacking TPM2
2022-04-21 04:11:19 +09:00
Lennart Poettering
947914cb44
Merge pull request #23099 from yuwata/sd-bus-track-fixlets
sd-bus: fix counter
2022-04-20 18:23:05 +02:00
Lennart Poettering
fe43a638c5 update TODO 2022-04-20 17:49:17 +02:00
Lennart Poettering
b6553329c0 creds-util: permit credentials encrypted/signed by fixed zero length keys as fallback for systems lacking TPM2
This is supposed to be useful when generating credentials for immutable
initrd environments, where it is is relevant to support credentials even
on systems lacking a TPM2 chip.

With this, if `systemd-creds encrypt --with-key=auto-initrd` is used a
credential will be encrypted/signed with the TPM2 if it is available and
recognized by the firmware. Otherwise it will be encrypted/signed with
the fixed empty key, thus providing no confidentiality or authenticity.

The idea is that distributions use this mode to generically create
credentials that are as locked down as possible on the specific
platform.
2022-04-20 17:49:17 +02:00
Lennart Poettering
571d829ee4 creds-util: add an explicit 128bit ID for identifying "automatic" key determination
Previously, when encrypting creds you could pick which key to use for
this via a 128bit ID identifying the key type, and use an all zero ID
for rquesting automatic mode.

Let's change this to use an explicitly picked 128bit ID for automatic
mode, i.e. something other than all zeros. This is in preparation for
adding one further automatic mode with slightly different semantics.

no change in behaviour.

Note that the new 128bit id is never written to disk but only used
internally to indicate a specific case.
2022-04-20 17:49:16 +02:00
Lennart Poettering
7cac4a2e2d creds-util: refuse unexpected key types explicitly 2022-04-20 17:19:45 +02:00
Daan De Meyer
b9b156ea3c
Merge pull request #23124 from yuwata/fixes-for-post-merge-review
Fixes for post merge review
2022-04-20 17:15:40 +02:00
Lennart Poettering
47a9f91760 update TODO 2022-04-20 15:45:10 +02:00
Lennart Poettering
8b82513375 update TODO 2022-04-20 14:49:53 +02:00
Yu Watanabe
5a560d4cc3 firewall-util: emphasize that nfnl_netlink_sendv() takes at least one message
Addresses https://github.com/systemd/systemd/pull/23090#discussion_r853002631.
2022-04-20 02:28:54 +09:00
Yu Watanabe
2a86ed67ed efi-api: use string_replace_char() 2022-04-20 02:23:11 +09:00
Yu Watanabe
9dbfcaf229 sd-device: use string_replace_char() and strspn_from_end() 2022-04-20 02:19:44 +09:00
Yu Watanabe
146f4482b2 string-util: introduce strspn_from_end() 2022-04-20 02:15:01 +09:00
Yu Watanabe
072f5f9b18 string-util: introduce string_replace_char() 2022-04-20 02:14:35 +09:00
Yu Watanabe
e4e1353c25 sd-device: refuse O_DIRECTORY returned from path_extract_filename()
In both cases, it is expected that the symlink targets do not end with '/'.

Addresses https://github.com/systemd/systemd/pull/23089#discussion_r853007218.
2022-04-20 01:43:27 +09:00
Yu Watanabe
bb60956b39 path-util: make readlink_value() refuse O_DIRECTORY returned from path_extract_filename()
The function is now only used by sd-device.c and pam_systemd.c, and they
expects the result are not directory. Hence, it is safe to change the
behavior.

Addresses https://github.com/systemd/systemd/pull/23089#discussion_r853006017.
2022-04-20 01:40:39 +09:00
Yu Watanabe
056a18e465 test: add several tests for track item 2022-04-19 18:02:10 +09:00
Yu Watanabe
63ec7a8490 test: shorten code a bit 2022-04-19 18:02:10 +09:00
Yu Watanabe
c399ed923d sd-bus: use hashmap_contains() and drop unnecessary cast 2022-04-19 18:02:10 +09:00
Yu Watanabe
55bfacc6c3 sd-bus: do not return negative errno when unknown name is specified
When 'recursive' is false, then sd_bus_track_remove_name() does not
return negative errno when unknown name is specified. Let's follow the
same pattern for the case that 'recursive' is true.
2022-04-19 18:02:10 +09:00
Yu Watanabe
6a7ca27740 sd-bus: do not read unused value 2022-04-19 18:02:10 +09:00
Yu Watanabe
c2d7dd35d2 sd-bus: introduce ref/unref function for track_item 2022-04-19 18:01:46 +09:00
Yu Watanabe
b21f237d99 sd-bus: fix reference counter to be incremented
Fixes #23097.
2022-04-19 17:58:08 +09:00
Yu Watanabe
c8431e9e35 macro: check over flow in reference counter 2022-04-19 17:58:02 +09:00
Yu Watanabe
6dd18b34cf json: use unsigned for refernce counter
For other places, we use unsigned for reference counter.
2022-04-19 17:56:40 +09:00
19 changed files with 391 additions and 119 deletions

30
TODO
View File

@ -78,6 +78,14 @@ Janitorial Clean-ups:
Features:
* 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
see an inotify wd event check against this set, and if it is contained ignore
the event. (to be fully correct this would have to count the occurances, in
case the same wd is reused multiple times before we start processing
IN_IGNORED again)
* sd-stub: set efi var indicating stub features, i.e. whether they pick up
creds, sysexts and so on. similar to existing variable of sd-boot
@ -106,12 +114,6 @@ Features:
- sd-stub: automatically pick up microcode from ESP (/loader/microcode/*)
and synthesize initrd from it, and measure it. Signing is not necessary, as
microcode does that on its own. Pass as first initrd to kernel.
- systemd-creds should have a fallback logic that uses neither TPM nor the
system key in /var for encryption and instead some fixed key. This should
be opt in (since it provides no security properties) but be used by
kernel-install when encrypting the creds it generates on systems that lack
a TPM, so that we can have very similar codepaths on TPM and TPM-less
systems. i.e. --with-key=tpm-graceful or so.
- sd-stub should measure the kernel/initrd/… into a separate PCR, so that we
have one PCR we can bind the encrypted creds to that is not effected by
anything else but what we drop in via kernel-install, i.e. by earlier EFI
@ -194,6 +196,22 @@ Features:
as directory manifest. The file would be a standard directory listing as
generated by GNU sha256sums.
* sd-boot: maybe add support for embedding the various auxiliary resources we
look for right in the sd-boot binary. i.e. take inspiration from sd-stub
logic: allow combining sd-boot via objcopy with kernels to enumerate, .conf
files, drivers, keys to enroll and so on. Then, add whatever we find that way
to the menu. Usecase: allow building a single PE image you can boot into via
UEFI HTTP boot.
* maybe add a new UEFI stub binary "sd-http". It works similar to sd-stub, but
all it does is download a file from a http server, and execute it, after
optionally checking its hash sum. idea would be: combine this "sd-http" stub
binary with some minimal info about an URL + hash sum, plus .osrel data, and
drop it into the unified kernel dir in the ESP. And bam you have something
that is tiny, feels a lot like a unified kernel, but all it does is chainload
the real kernel. benefit: downloading these stubs would be tiny and quick,
hence cheap for enumeration.
* initialize machine ID from systemd credential picked up from the ESP via
sd-stub, so that machine ID is stable even on systems where unified kernels
are used, and hence kernel cmdline cannot be modified locally

View File

@ -263,23 +263,32 @@
<term><option>-H</option></term>
<term><option>-T</option></term>
<listitem><para>When specified with the <command>encrypt</command> command controls the encryption
key to use. Takes one of <literal>host</literal>, <literal>tpm2</literal>,
<literal>host+tpm2</literal> or <literal>auto</literal>. See above for details on the three key
types. If set to <literal>auto</literal> (which is the default) the TPM2 key is used if a TPM2 device
is found and not running in a container. The host key is used if
<filename>/var/lib/systemd/</filename> is on persistent media. This means on typical systems the
encryption is by default bound to both the TPM2 chip and the OS installation, and both need to be
available to decrypt the credential again. If <literal>auto</literal> is selected but neither TPM2 is
available (or running in container) nor <filename>/var/lib/systemd/</filename> is on persistent
media, encryption will fail.</para>
<listitem><para>When specified with the <command>encrypt</command> command controls the
encryption/signature key to use. Takes one of <literal>host</literal>, <literal>tpm2</literal>,
<literal>host+tpm2</literal>, <literal>tpm2-absent</literal>, <literal>auto</literal>,
<literal>auto-initrd</literal>. See above for details on the three key types. If set to
<literal>auto</literal> (which is the default) the TPM2 key is used if a TPM2 device is found and not
running in a container. The host key is used if <filename>/var/lib/systemd/</filename> is on
persistent media. This means on typical systems the encryption is by default bound to both the TPM2
chip and the OS installation, and both need to be available to decrypt the credential again. If
<literal>auto</literal> is selected but neither TPM2 is available (or running in container) nor
<filename>/var/lib/systemd/</filename> is on persistent media, encryption will fail. If set to
<literal>tpm2-absent</literal> a fixed zero length key is used (thus, in this mode no confidentiality
nor authenticity are provided!). This logic is useful to cover for systems that lack a TPM2 chip but
where credentials shall be generated. Note that decryption of such credentials is refused on systems
that have a TPM2 chip and where UEFI SecureBoot is enabled (this is done so that such a locked down
system cannot be tricked into loading a credential generated this way that lacks authentication
information). If set to <literal>auto-initrd</literal> a TPM2 key is used if a TPM2 is found. If not
a fixed zero length key is used, equivalent to <literal>tpm2-absent</literal> mode. This option is
particularly useful to generate credentials files that are encrypted/authenticated against TPM2 where
available but still work on systems lacking support for this.</para>
<para>The <option>-H</option> switch is a shortcut for <option>--with-key=host</option>. Similar,
<option>-T</option> is a shortcut for <option>--with-key=tpm2</option>.</para>
<para>When encrypting credentials that shall be used in the initial RAM disk (initrd) where
<filename>/var/lib/systemd/</filename> is typically not available make sure to use
<option>--with-key=tpm2</option> mode, to disable binding against the host secret.</para>
<option>--with-key=auto-initrd</option> mode, to disable binding against the host secret.</para>
<para>This switch has no effect on the <command>decrypt</command> command, as information on which
key to use for decryption is included in the encrypted credential already.</para></listitem>

View File

@ -251,7 +251,7 @@
<row>
<entry>4</entry>
<entry>Boot loader; changes on boot loader updates. The shim project will measure the PE binary it chain loads into this PCR.</entry>
<entry>Boot loader and additional drivers; changes on boot loader updates. The shim project will measure the PE binary it chain loads into this PCR. If the Linux kernel is invoked as UEFI PE binary, it is measured here, too. <citerefentry><refentrytitle>sd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures system extension images read from the ESP here too (see <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>).</entry>
</row>
<row>
@ -273,8 +273,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>
<entry>9</entry>
<entry>The Linux kernel measures all initial RAM file systems it receives into this PCR.</entry>
<!-- Strictly speaking only Linux >= 5.17 using the LOAD_FILE2 protocol, see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f046fff8bc4c4d8f8a478022e76e40b818f692df -->
</row>
<row>
@ -282,6 +283,11 @@
<entry>The IMA project measures its runtime state into this PCR.</entry>
</row>
<row>
<entry>12</entry>
<entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any specified kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>sytemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry>
</row>
<row>
<entry>14</entry>
<entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry>

View File

@ -70,7 +70,7 @@
image, any attempts to override the kernel command line by passing one as invocation parameters to the
EFI binary are ignored. Thus, in order to allow overriding the kernel command line, either disable UEFI
SecureBoot, or don't include a kernel command line PE section in the kernel image file. If a command line
is accepted via EFI invocation parameters to the EFI binary it is measured into TPM PCR 8 (if a TPM is
is accepted via EFI invocation parameters to the EFI binary it is measured into TPM PCR 12 (if a TPM is
present).</para>
<para>If a DeviceTree is embedded in the <literal>.dtb</literal> section, it replaces an existing
@ -100,7 +100,7 @@
<citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for
details on encrypted credentials. The generated <command>cpio</command> archive is measured into TPM
PCR 4 (if a TPM is present).</para></listitem>
PCR 12 (if a TPM is present).</para></listitem>
<listitem><para>Similarly, files <filename><replaceable>foo</replaceable>.efi.extra.d/*.raw</filename>
are packed up in a <command>cpio</command> archive and placed in the <filename>/.extra/sysext/</filename>
@ -108,13 +108,13 @@
images to the initrd. See
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
details on system extension images. The generated <command>cpio</command> archive containing these
system extension images is measured into TPM PCR 8 (if a TPM is present).</para></listitem>
system extension images is measured into TPM PCR 4 (if a TPM is present).</para></listitem>
<listitem><para>Files <filename>/loader/credentials/*.cred</filename> are packed up in a
<command>cpio</command> archive and placed in the <filename>/.extra/global_credentials/</filename>
directory of the initrd file hierarchy. This is supposed to be used to pass additional credentials to
the initrd, regardless of the kernel being booted. The generated <command>cpio</command> archive is
measured into TPM PCR 4 (if a TPM is present)</para></listitem>
measured into TPM PCR 12 (if a TPM is present)</para></listitem>
</itemizedlist>
<para>These mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd
@ -125,6 +125,78 @@
details); in case of the system extension images by using signed Verity images.</para>
</refsect1>
<refsect1>
<title>TPM2 PCR Notes</title>
<para>Note that when a unified kernel using <command>systemd-stub</command> is invoked the firmware will
measure it as a whole to TPM PCR 4, covering all embedded resources, such as the stub code itself, the
core kernel, the embedded initrd and kernel command line (see above for a full list).</para>
<para>Also note that the Linux kernel will measure all initrds it receives into TPM PCR 9. This means
every type of initrd will be measured twice: the initrd embedded in the kernel image will be measured to
both PCR 4 and PCR 9; the initrd synthesized from credentials will be measured to both PCR 12 and PCR 9;
the initrd synthesized from system extensions will be measured to both PCR 4 and PCR 9. Let's summarize
the OS resources and the PCRs they are measured to:</para>
<table>
<title>OS Resource PCR Summary</title>
<tgroup cols='2' align='left' colsep='1' rowsep='1'>
<colspec colname="pcr" />
<colspec colname="definition" />
<thead>
<row>
<entry>OS Resource</entry>
<entry>Measurement PCR</entry>
</row>
</thead>
<tbody>
<row>
<entry><command>systemd-stub</command> code (the entry point of the unified PE binary)</entry>
<entry>4</entry>
</row>
<row>
<entry>Boot splash (embedded in the unified PE binary)</entry>
<entry>4</entry>
</row>
<row>
<entry>Core kernel code (embedded in unified PE binary)</entry>
<entry>4</entry>
</row>
<row>
<entry>Main initrd (embedded in unified PE binary)</entry>
<entry>4 + 9</entry>
</row>
<row>
<entry>Default kernel command line (embedded in unified PE binary)</entry>
<entry>4</entry>
</row>
<row>
<entry>Overriden kernel command line</entry>
<entry>12</entry>
</row>
<row>
<entry>Credentials (synthesized initrd from companion files)</entry>
<entry>12 + 9</entry>
</row>
<row>
<entry>System Extensions (synthesized initrd from companion files)</entry>
<entry>4 + 9</entry>
</row>
</tbody>
</tgroup>
</table>
</refsect1>
<refsect1>
<title>EFI Variables</title>

View File

@ -155,7 +155,7 @@ int readlink_malloc(const char *p, char **ret) {
}
int readlink_value(const char *p, char **ret) {
_cleanup_free_ char *link = NULL;
_cleanup_free_ char *link = NULL, *name = NULL;
int r;
assert(p);
@ -165,7 +165,14 @@ int readlink_value(const char *p, char **ret) {
if (r < 0)
return r;
return path_extract_filename(link, ret);
r = path_extract_filename(link, &name);
if (r < 0)
return r;
if (r == O_DIRECTORY)
return -EINVAL;
*ret = TAKE_PTR(name);
return 0;
}
int readlink_and_make_absolute(const char *p, char **r) {

View File

@ -396,8 +396,12 @@ static inline int __coverity_check_and_return__(int condition) {
if (!p) \
return NULL; \
\
assert(p->n_ref > 0); \
p->n_ref++; \
/* For type check. */ \
unsigned *q = &p->n_ref; \
assert(*q > 0); \
assert(*q < UINT_MAX); \
\
(*q)++; \
return p; \
}

View File

@ -1162,3 +1162,30 @@ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
return in_charset(s1, ok) && in_charset(s2, ok);
}
char *string_replace_char(char *str, char old_char, char new_char) {
assert(str);
assert(old_char != '\0');
assert(new_char != '\0');
assert(old_char != new_char);
for (char *p = strchr(str, old_char); p; p = strchr(p + 1, old_char))
*p = new_char;
return str;
}
size_t strspn_from_end(const char *str, const char *accept) {
size_t n = 0;
if (isempty(str))
return 0;
if (isempty(accept))
return 0;
for (const char *p = str + strlen(str); p > str && strchr(accept, p[-1]); p--)
n++;
return n;
}

View File

@ -233,3 +233,7 @@ static inline int string_contains_word(const char *string, const char *separator
}
bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok);
char *string_replace_char(char *str, char old_char, char new_char);
size_t strspn_from_end(const char *str, const char *accept);

View File

@ -40,7 +40,7 @@ static bool arg_legend = true;
static bool arg_system = false;
static TranscodeMode arg_transcode = TRANSCODE_OFF;
static int arg_newline = -1;
static sd_id128_t arg_with_key = SD_ID128_NULL;
static sd_id128_t arg_with_key = _CRED_AUTO;
static const char *arg_tpm2_device = NULL;
static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
static const char *arg_name = NULL;
@ -560,7 +560,7 @@ static int verb_help(int argc, char **argv, void *userdata) {
" --timestamp=TIME Include specified timestamp in encrypted credential\n"
" --not-after=TIME Include specified invalidation time in encrypted\n"
" credential\n"
" --with-key=host|tpm2|host+tpm2|auto\n"
" --with-key=host|tpm2|host+tpm2|tpm2-absent|auto|auto-initrd\n"
" Which keys to encrypt with\n"
" -H Shortcut for --with-key=host\n"
" -T Shortcut for --with-key=tpm2\n"
@ -684,13 +684,17 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_WITH_KEY:
if (isempty(optarg) || streq(optarg, "auto"))
arg_with_key = SD_ID128_NULL;
arg_with_key = _CRED_AUTO;
else if (streq(optarg, "auto-initrd"))
arg_with_key = _CRED_AUTO_INITRD;
else if (streq(optarg, "host"))
arg_with_key = CRED_AES256_GCM_BY_HOST;
else if (streq(optarg, "tpm2"))
arg_with_key = CRED_AES256_GCM_BY_TPM2_HMAC;
else if (STR_IN_SET(optarg, "host+tpm2", "tpm2+host"))
arg_with_key = CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC;
else if (streq(optarg, "tpm2-absent"))
arg_with_key = CRED_AES256_GCM_BY_TPM2_ABSENT;
else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown key type: %s", optarg);

View File

@ -40,7 +40,6 @@ struct sd_bus_track {
"arg0='", name, "'")
static struct track_item* track_item_free(struct track_item *i) {
if (!i)
return NULL;
@ -49,7 +48,8 @@ static struct track_item* track_item_free(struct track_item *i) {
return mfree(i);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
DEFINE_PRIVATE_TRIVIAL_REF_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);
@ -165,13 +165,13 @@ DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus_track, sd_bus_track, track_free);
static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
sd_bus_track *track = userdata;
const char *name, *old, *new;
const char *name;
int r;
assert(message);
assert(track);
r = sd_bus_message_read(message, "sss", &name, &old, &new);
r = sd_bus_message_read(message, "sss", &name, NULL, NULL);
if (r < 0)
return 0;
@ -180,7 +180,7 @@ static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus
}
_public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
_cleanup_(track_item_freep) struct track_item *n = NULL;
_cleanup_(track_item_unrefp) struct track_item *n = NULL;
struct track_item *i;
const char *match;
int r;
@ -190,14 +190,8 @@ _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) {
unsigned k = track->n_ref + 1;
if (k < track->n_ref) /* Check for overflow */
return -EOVERFLOW;
track->n_ref = k;
}
if (track->recursive)
track_item_ref(i);
bus_track_remove_from_queue(track);
return 0;
@ -207,9 +201,14 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
if (r < 0)
return r;
n = new0(struct track_item, 1);
n = new(struct track_item, 1);
if (!n)
return -ENOMEM;
*n = (struct track_item) {
.n_ref = 1,
};
n->name = strdup(name);
if (!n->name)
return -ENOMEM;
@ -241,8 +240,7 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
return r;
}
n->n_ref = 1;
n = NULL;
TAKE_PTR(n);
bus_track_remove_from_queue(track);
track->modified = true;
@ -258,20 +256,16 @@ _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
if (!track) /* Treat a NULL track object as an empty track object */
return 0;
if (!track->recursive)
return bus_track_remove_name_fully(track, name);
i = hashmap_get(track->names, name);
if (!i)
return -EUNATCH;
if (i->n_ref <= 0)
return -EUNATCH;
return 0;
i->n_ref--;
if (i->n_ref <= 0)
assert(i->n_ref >= 1);
if (i->n_ref <= 1)
return bus_track_remove_name_fully(track, name);
track_item_unref(i);
return 1;
}
@ -293,7 +287,7 @@ _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name
if (!track) /* Let's consider a NULL object equivalent to an empty object */
return NULL;
return hashmap_get(track->names, (void*) name) ? name : NULL;
return hashmap_contains(track->names, name) ? name : NULL;
}
_public_ const char* sd_bus_track_first(sd_bus_track *track) {

View File

@ -10,6 +10,7 @@
static bool track_cb_called_x = false;
static bool track_cb_called_y = false;
static bool track_destroy_called_z = false;
static int track_cb_x(sd_bus_track *t, void *userdata) {
@ -26,7 +27,6 @@ static int track_cb_x(sd_bus_track *t, void *userdata) {
}
static int track_cb_y(sd_bus_track *t, void *userdata) {
int r;
log_error("TRACK CB Y");
@ -35,15 +35,22 @@ static int track_cb_y(sd_bus_track *t, void *userdata) {
/* We got disconnected, let's close everything */
r = sd_event_exit(sd_bus_get_event(sd_bus_track_get_bus(t)), EXIT_SUCCESS);
assert_se(r >= 0);
assert_se(sd_event_exit(sd_bus_get_event(sd_bus_track_get_bus(t)), EXIT_SUCCESS) >= 0);
return 0;
}
static int track_cb_z(sd_bus_track *t, void *userdata) {
assert_not_reached();
}
static void track_destroy_z(void *userdata) {
track_destroy_called_z = true;
}
int main(int argc, char *argv[]) {
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_(sd_bus_track_unrefp) sd_bus_track *x = NULL, *y = NULL;
_cleanup_(sd_bus_track_unrefp) sd_bus_track *x = NULL, *y = NULL, *z = NULL;
_cleanup_(sd_bus_unrefp) sd_bus *a = NULL, *b = NULL;
bool use_system_bus = false;
const char *unique;
@ -51,8 +58,7 @@ int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO);
r = sd_event_default(&event);
assert_se(r >= 0);
assert_se(sd_event_default(&event) >= 0);
r = sd_bus_open_user(&a);
if (IN_SET(r, -ECONNREFUSED, -ENOENT, -ENOMEDIUM)) {
@ -63,43 +69,80 @@ int main(int argc, char *argv[]) {
}
assert_se(r >= 0);
r = sd_bus_attach_event(a, event, SD_EVENT_PRIORITY_NORMAL);
assert_se(r >= 0);
assert_se(sd_bus_attach_event(a, event, SD_EVENT_PRIORITY_NORMAL) >= 0);
if (use_system_bus)
r = sd_bus_open_system(&b);
assert_se(sd_bus_open_system(&b) >= 0);
else
r = sd_bus_open_user(&b);
assert_se(r >= 0);
assert_se(sd_bus_open_user(&b) >= 0);
r = sd_bus_attach_event(b, event, SD_EVENT_PRIORITY_NORMAL);
assert_se(r >= 0);
assert_se(sd_bus_attach_event(b, event, SD_EVENT_PRIORITY_NORMAL) >= 0);
/* Watch b's name from a */
r = sd_bus_track_new(a, &x, track_cb_x, NULL);
assert_se(r >= 0);
assert_se(sd_bus_track_new(a, &x, track_cb_x, NULL) >= 0);
r = sd_bus_get_unique_name(b, &unique);
assert_se(r >= 0);
assert_se(sd_bus_get_unique_name(b, &unique) >= 0);
r = sd_bus_track_add_name(x, unique);
assert_se(r >= 0);
assert_se(sd_bus_track_add_name(x, unique) >= 0);
/* Watch's a's own name from a */
r = sd_bus_track_new(a, &y, track_cb_y, NULL);
assert_se(r >= 0);
assert_se(sd_bus_track_new(a, &y, track_cb_y, NULL) >= 0);
r = sd_bus_get_unique_name(a, &unique);
assert_se(r >= 0);
assert_se(sd_bus_get_unique_name(a, &unique) >= 0);
r = sd_bus_track_add_name(y, unique);
assert_se(r >= 0);
assert_se(sd_bus_track_add_name(y, unique) >= 0);
/* Basic tests. */
assert_se(sd_bus_track_new(a, &z, track_cb_z, NULL) >= 0);
/* non-recursive case */
assert_se(sd_bus_track_set_recursive(z, false) >= 0);
assert_se(sd_bus_track_get_recursive(z) == 0);
assert_se(!sd_bus_track_contains(z, unique));
assert_se(sd_bus_track_count_name(z, unique) == 0);
assert_se(sd_bus_track_remove_name(z, unique) == 0);
assert_se(sd_bus_track_add_name(z, unique) >= 0);
assert_se(sd_bus_track_add_name(z, unique) >= 0);
assert_se(sd_bus_track_add_name(z, unique) >= 0);
assert_se(sd_bus_track_set_recursive(z, true) == -EBUSY);
assert_se(sd_bus_track_contains(z, unique));
assert_se(sd_bus_track_count_name(z, unique) == 1);
assert_se(sd_bus_track_remove_name(z, unique) == 1);
assert_se(!sd_bus_track_contains(z, unique));
assert_se(sd_bus_track_count_name(z, unique) == 0);
assert_se(sd_bus_track_remove_name(z, unique) == 0);
/* recursive case */
assert_se(sd_bus_track_set_recursive(z, true) >= 0);
assert_se(sd_bus_track_get_recursive(z) == 1);
assert_se(!sd_bus_track_contains(z, unique));
assert_se(sd_bus_track_count_name(z, unique) == 0);
assert_se(sd_bus_track_remove_name(z, unique) == 0);
assert_se(sd_bus_track_add_name(z, unique) >= 0);
assert_se(sd_bus_track_add_name(z, unique) >= 0);
assert_se(sd_bus_track_add_name(z, unique) >= 0);
assert_se(sd_bus_track_set_recursive(z, false) == -EBUSY);
assert_se(sd_bus_track_contains(z, unique));
assert_se(sd_bus_track_count_name(z, unique) == 3);
assert_se(sd_bus_track_remove_name(z, unique) == 1);
assert_se(sd_bus_track_contains(z, unique));
assert_se(sd_bus_track_count_name(z, unique) == 2);
assert_se(sd_bus_track_remove_name(z, unique) == 1);
assert_se(sd_bus_track_contains(z, unique));
assert_se(sd_bus_track_count_name(z, unique) == 1);
assert_se(sd_bus_track_remove_name(z, unique) == 1);
assert_se(!sd_bus_track_contains(z, unique));
assert_se(sd_bus_track_count_name(z, unique) == 0);
assert_se(sd_bus_track_remove_name(z, unique) == 0);
assert_se(sd_bus_track_set_destroy_callback(z, track_destroy_z) >= 0);
z = sd_bus_track_unref(z);
assert_se(track_destroy_called_z);
/* Now make b's name disappear */
sd_bus_close(b);
r = sd_event_loop(event);
assert_se(r >= 0);
assert_se(sd_event_loop(event) >= 0);
assert_se(track_cb_called_x);
assert_se(track_cb_called_y);

View File

@ -1173,7 +1173,7 @@ _public_ int sd_device_get_devname(sd_device *device, const char **devname) {
static int device_set_sysname_and_sysnum(sd_device *device) {
_cleanup_free_ char *sysname = NULL;
char *p;
size_t len, n;
int r;
assert(device);
@ -1181,16 +1181,19 @@ static int device_set_sysname_and_sysnum(sd_device *device) {
r = path_extract_filename(device->devpath, &sysname);
if (r < 0)
return r;
if (r == O_DIRECTORY)
return -EINVAL;
/* some devices have '!' in their name, change that to '/' */
for (p = strchrnul(sysname, '!'); *p != '\0'; p = strchrnul(p, '!'))
*p = '/';
string_replace_char(sysname, '!', '/');
/* trailing number (refuse number only sysname)*/
for (; p > sysname && isdigit(p[-1]); p--)
;
n = strspn_from_end(sysname, DIGITS);
len = strlen(sysname);
assert(n <= len);
if (n == len)
n = 0; /* Do not set sysnum for number only sysname. */
device->sysnum = p > sysname && *p != '\0' ? p : NULL;
device->sysnum = n > 0 ? sysname + len - n : NULL;
return free_and_replace(device->sysname, sysname);
}
@ -1457,6 +1460,8 @@ int device_get_device_id(sd_device *device, const char **ret) {
r = path_extract_filename(device->devpath, &sysname);
if (r < 0)
return r;
if (r == O_DIRECTORY)
return -EINVAL;
if (streq(subsystem, "drivers")) {
/* the 'drivers' pseudo-subsystem is special, and needs the real

View File

@ -11,6 +11,7 @@
#include "blockdev-util.h"
#include "chattr-util.h"
#include "creds-util.h"
#include "efi-api.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
@ -366,6 +367,11 @@ int get_credential_host_secret(CredentialSecretFlags flags, void **ret, size_t *
*
* 3. The concatenation of the above.
*
* 4. Or a fixed "empty" key. This will not provide confidentiality or authenticity, of course, but is
* useful to encode credentials for the initrd on TPM-less systems, where we simply have no better
* concept to bind things to. Note that decryption of a key set up like this will be refused on
* systems that have a TPM and have SecureBoot enabled.
*
* The above is hashed with SHA256 which is then used as encryption key for AES256-GCM. The encrypted
* credential is a short (unencrypted) header describing which of the three keys to use, the IV to use for
* AES256-GCM and some more meta information (sizes of certain objects) that is strictly speaking redundant,
@ -474,15 +480,21 @@ int encrypt_credential_and_warn(
int ksz, bsz, ivsz, tsz, added, r;
uint8_t md[SHA256_DIGEST_LENGTH];
const EVP_CIPHER *cc;
#if HAVE_TPM2
bool try_tpm2 = false;
#endif
sd_id128_t id;
assert(input || input_size == 0);
assert(ret);
assert(ret_size);
if (!sd_id128_in_set(with_key,
_CRED_AUTO,
_CRED_AUTO_INITRD,
CRED_AES256_GCM_BY_HOST,
CRED_AES256_GCM_BY_TPM2_HMAC,
CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC,
CRED_AES256_GCM_BY_TPM2_ABSENT))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid key type: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(with_key));
if (name && !credential_name_valid(name))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid credential name: %s", name);
@ -500,23 +512,26 @@ int encrypt_credential_and_warn(
log_debug("Including not-after timestamp '%s' in encrypted credential.", format_timestamp(buf, sizeof(buf), not_after));
}
if (sd_id128_is_null(with_key) ||
sd_id128_in_set(with_key, CRED_AES256_GCM_BY_HOST, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC)) {
if (sd_id128_in_set(with_key,
_CRED_AUTO,
CRED_AES256_GCM_BY_HOST,
CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC)) {
r = get_credential_host_secret(
CREDENTIAL_SECRET_GENERATE|
CREDENTIAL_SECRET_WARN_NOT_ENCRYPTED|
(sd_id128_is_null(with_key) ? CREDENTIAL_SECRET_FAIL_ON_TEMPORARY_FS : 0),
(sd_id128_equal(with_key, _CRED_AUTO) ? CREDENTIAL_SECRET_FAIL_ON_TEMPORARY_FS : 0),
&host_key,
&host_key_size);
if (r == -ENOMEDIUM && sd_id128_is_null(with_key))
if (r == -ENOMEDIUM && sd_id128_equal(with_key, _CRED_AUTO))
log_debug_errno(r, "Credential host secret location on temporary file system, not using.");
else if (r < 0)
return log_error_errno(r, "Failed to determine local credential host secret: %m");
}
#if HAVE_TPM2
if (sd_id128_is_null(with_key)) {
bool try_tpm2;
if (sd_id128_equal(with_key, _CRED_AUTO)) {
/* If automatic mode is selected and we are running in a container, let's not try TPM2. OTOH
* if user picks TPM2 explicitly, let's always honour the request and try. */
@ -527,11 +542,17 @@ int encrypt_credential_and_warn(
log_debug("Running in container, not attempting to use TPM2.");
try_tpm2 = r <= 0;
}
} else if (sd_id128_equal(with_key, _CRED_AUTO_INITRD)) {
/* If automatic mode for initrds is selected, we'll use the TPM2 key if the firmware does it,
* otherwise we'll use a fixed key */
if (try_tpm2 ||
sd_id128_in_set(with_key, CRED_AES256_GCM_BY_TPM2_HMAC, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC)) {
try_tpm2 = efi_has_tpm2();
if (!try_tpm2)
log_debug("Firmware lacks TPM2 support, not attempting to use TPM2.");
} else
try_tpm2 = sd_id128_in_set(with_key, CRED_AES256_GCM_BY_TPM2_HMAC, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC);
if (try_tpm2) {
r = tpm2_seal(tpm2_device,
tpm2_pcr_mask,
NULL,
@ -544,7 +565,9 @@ int encrypt_credential_and_warn(
&tpm2_pcr_bank,
&tpm2_primary_alg);
if (r < 0) {
if (!sd_id128_is_null(with_key))
if (sd_id128_equal(with_key, _CRED_AUTO_INITRD))
log_warning("Firmware reported a TPM2 being present and used, but we didn't manage to talk to it. Credential will be refused if SecureBoot is enabled.");
else if (!sd_id128_equal(with_key, _CRED_AUTO))
return r;
log_debug_errno(r, "TPM2 sealing didn't work, not using: %m");
@ -555,7 +578,7 @@ int encrypt_credential_and_warn(
}
#endif
if (sd_id128_is_null(with_key)) {
if (sd_id128_in_set(with_key, _CRED_AUTO, _CRED_AUTO_INITRD)) {
/* Let's settle the key type in auto mode now. */
if (host_key && tpm2_key)
@ -564,12 +587,17 @@ int encrypt_credential_and_warn(
id = CRED_AES256_GCM_BY_TPM2_HMAC;
else if (host_key)
id = CRED_AES256_GCM_BY_HOST;
else if (sd_id128_equal(with_key, _CRED_AUTO_INITRD))
id = CRED_AES256_GCM_BY_TPM2_ABSENT;
else
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"TPM2 not available and host key located on temporary file system, no encryption key available.");
} else
id = with_key;
if (sd_id128_equal(id, CRED_AES256_GCM_BY_TPM2_ABSENT))
log_warning("Using a null key for encryption and signing. Confidentiality or authenticity will not be provided.");
/* Let's now take the host key and the TPM2 key and hash it together, to use as encryption key for the data */
r = sha256_hash_host_and_tpm2_key(host_key, host_key_size, tpm2_key, tpm2_key_size, md);
if (r < 0)
@ -727,7 +755,7 @@ int decrypt_credential_and_warn(
struct encrypted_credential_header *h;
struct metadata_credential_header *m;
uint8_t md[SHA256_DIGEST_LENGTH];
bool with_tpm2, with_host_key;
bool with_tpm2, with_host_key, is_tpm2_absent;
const EVP_CIPHER *cc;
int r, added;
@ -743,10 +771,31 @@ int decrypt_credential_and_warn(
with_host_key = sd_id128_in_set(h->id, CRED_AES256_GCM_BY_HOST, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC);
with_tpm2 = sd_id128_in_set(h->id, CRED_AES256_GCM_BY_TPM2_HMAC, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC);
is_tpm2_absent = sd_id128_equal(h->id, CRED_AES256_GCM_BY_TPM2_ABSENT);
if (!with_host_key && !with_tpm2)
if (!with_host_key && !with_tpm2 && !is_tpm2_absent)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unknown encryption format, or corrupted data: %m");
if (is_tpm2_absent) {
/* So this is a credential encrypted with a zero length key. We support this to cover for the
* case where neither a host key not a TPM2 are available (specifically: initrd environments
* where the host key is not yet accessible and no TPM2 chip exists at all), to minimize
* different codeflow for TPM2 and non-TPM2 codepaths. Of course, credentials encoded this
* way offer no confidentiality nor authenticity. Because of that it's important we refuse to
* use them on systems that actually *do* have a TPM2 chip if we are in SecureBoot
* mode. Otherwise an attacker could hand us credentials like this and we'd use them thinking
* they are trusted, even though they are not. */
if (efi_has_tpm2()) {
if (is_efi_secure_boot())
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
"Credential uses fixed key for fallback use when TPM2 is absent — but TPM2 is present, and SecureBoot is enabled, refusing.");
log_warning("Credential uses fixed key for use when TPM2 is absent, but TPM2 is present! Accepting anyway, since SecureBoot is disabled.");
} else
log_debug("Credential uses fixed key for use when TPM2 is absent, and TPM2 indeed is absent. Accepting.");
}
/* Now we know the minimum header size */
if (input_size < offsetof(struct encrypted_credential_header, iv))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Encrypted file too short.");
@ -827,6 +876,9 @@ int decrypt_credential_and_warn(
return log_error_errno(r, "Failed to determine local credential key: %m");
}
if (is_tpm2_absent)
log_warning("Warning: using a null key for decryption and authentication. Confidentiality or authenticity are not provided.");
sha256_hash_host_and_tpm2_key(host_key, host_key_size, tpm2_key, tpm2_key_size, md);
assert_se(cc = EVP_aes_256_gcm());

View File

@ -38,10 +38,22 @@ typedef enum CredentialSecretFlags {
int get_credential_host_secret(CredentialSecretFlags flags, void **ret, size_t *ret_size);
/* The three modes we support: keyed only by on-disk key, only by TPM2 HMAC key, and by the combination of both */
/* The four modes we support: keyed only by on-disk key, only by TPM2 HMAC key, and by the combination of
* both, as well as one with a fixed zero length key if TPM2 is missing (the latter of course provides no
* authenticity or confidentiality, but is still useful for integrity protection, and makes things simpler
* for us to handle). */
#define CRED_AES256_GCM_BY_HOST SD_ID128_MAKE(5a,1c,6a,86,df,9d,40,96,b1,d5,a6,5e,08,62,f1,9a)
#define CRED_AES256_GCM_BY_TPM2_HMAC SD_ID128_MAKE(0c,7c,c0,7b,11,76,45,91,9c,4b,0b,ea,08,bc,20,fe)
#define CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC SD_ID128_MAKE(93,a8,94,09,48,74,44,90,90,ca,f2,fc,93,ca,b5,53)
#define CRED_AES256_GCM_BY_TPM2_ABSENT SD_ID128_MAKE(05,84,69,da,f6,f5,43,24,80,05,49,da,0f,8e,a2,fb)
/* Two special IDs to pick a general automatic mode (i.e. tpm2+host if TPM2 exists, only host otherwise) or
* an initrd-specific automatic mode (i.e. tpm2 if firmware can do it, otherwise fixed zero-length key, and
* never involve host keys). These IDs will never be stored on disk, but are useful only internally while
* figuring out what precisely to write to disk. To mark that these aren't a "real" type, we'll prefix them
* with an underscore. */
#define _CRED_AUTO SD_ID128_MAKE(a2,19,cb,07,85,b2,4c,04,b1,6d,18,ca,b9,d2,ee,01)
#define _CRED_AUTO_INITRD SD_ID128_MAKE(02,dc,8e,de,3a,02,43,ab,a9,ec,54,9c,05,e6,a0,71)
int encrypt_credential_and_warn(sd_id128_t with_key, const char *name, usec_t timestamp, usec_t not_after, const char *tpm2_device, uint32_t tpm2_pcr_mask, const void *input, size_t input_size, void **ret, size_t *ret_size);
int decrypt_credential_and_warn(const char *validate_name, usec_t validate_timestamp, const char *tpm2_device, const void *input, size_t input_size, void **ret, size_t *ret_size);

View File

@ -546,11 +546,3 @@ bool efi_has_tpm2(void) {
}
#endif
char *efi_tilt_backslashes(char *s) {
for (char *p = s; *p; p++)
if (*p == '\\')
*p = '/';
return s;
}

View File

@ -3,6 +3,7 @@
#include "efivars-fundamental.h"
#include "efivars.h"
#include "string-util.h"
/* Various calls for interfacing with EFI variables from the official UEFI specs. */
@ -65,4 +66,6 @@ static inline bool efi_has_tpm2(void) {
#endif
char *efi_tilt_backslashes(char *s);
static inline char *efi_tilt_backslashes(char *s) {
return string_replace_char(s, '\\', '/');
}

View File

@ -31,7 +31,7 @@
static int nfnl_netlink_sendv(
sd_netlink *nfnl,
sd_netlink_message **messages,
sd_netlink_message *messages[static 1],
size_t msgcount) {
_cleanup_free_ uint32_t *serial = NULL;

View File

@ -41,7 +41,7 @@ assert_cc(DEPTH_MAX <= UINT16_MAX);
typedef struct JsonSource {
/* When we parse from a file or similar, encodes the filename, to indicate the source of a json variant */
size_t n_ref;
unsigned n_ref;
unsigned max_line;
unsigned max_column;
char name[];
@ -53,7 +53,7 @@ struct JsonVariant {
/* We either maintain a reference counter for this variant itself, or we are embedded into an
* array/object, in which case only that surrounding object is ref-counted. (If 'embedded' is false,
* see below.) */
size_t n_ref;
unsigned n_ref;
/* If this JsonVariant is part of an array/object, then this field points to the surrounding
* JSON_VARIANT_ARRAY/JSON_VARIANT_OBJECT object. (If 'embedded' is true, see below.) */

View File

@ -935,6 +935,26 @@ TEST(strextendf) {
assert_se(streq(p, "<77>,<99>,< 88>,<00001234>"));
}
TEST(string_replace_char) {
assert_se(streq(string_replace_char(strdupa(""), 'a', 'b'), ""));
assert_se(streq(string_replace_char(strdupa("abc"), 'a', 'b'), "bbc"));
assert_se(streq(string_replace_char(strdupa("hoge"), 'a', 'b'), "hoge"));
assert_se(streq(string_replace_char(strdupa("aaaa"), 'a', 'b'), "bbbb"));
assert_se(streq(string_replace_char(strdupa("aaaa"), 'a', '\t'), "\t\t\t\t"));
}
TEST(strspn_from_end) {
assert_se(strspn_from_end(NULL, NULL) == 0);
assert_se(strspn_from_end("hoge", NULL) == 0);
assert_se(strspn_from_end(NULL, DIGITS) == 0);
assert_se(strspn_from_end("", DIGITS) == 0);
assert_se(strspn_from_end("hoge", DIGITS) == 0);
assert_se(strspn_from_end("1234", DIGITS) == 4);
assert_se(strspn_from_end("aaa1234", DIGITS) == 4);
assert_se(strspn_from_end("aaa1234aaa", DIGITS) == 0);
assert_se(strspn_from_end("aaa12aa34", DIGITS) == 2);
}
TEST(streq_skip_trailing_chars) {
/* NULL is WHITESPACE by default*/
assert_se(streq_skip_trailing_chars("foo bar", "foo bar", NULL));