1
0
mirror of https://github.com/systemd/systemd synced 2026-03-27 17:24:51 +01:00

Compare commits

...

14 Commits

Author SHA1 Message Date
Benjamin Herrenschmidt
f90eea7d18 virt: Improve detection of EC2 metal instances
The current detection code relies on /sys/firmware/dmi/entries/0-0/raw
to disambiguate Amazon EC2 virtualized from metal instances.

Unfortunately this file is root only. Thus on a c6g.metal instance
(aarch64), we observe something like this:

$ systemd-detect-virt
amazon
$ sudo systemd-detect-virt
none

Only the latter is correct.

The right long term fix is to extend the kernel to expose the SMBIOS BIOS
Characteristics properly via /sys/class/dmi, but until this happens (and
for backwards compatibility when it does), we need a plan B.

This change implements such a workaround by falling back to using the
instance type from DMI and looking at the ".metal" string present on
metal instances.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
2021-09-14 01:44:20 +09:00
Lennart Poettering
c116f23394
Merge pull request #20716 from poettering/tpm2-primary-rsa
tpm2: be smarter when picking primary key algorithm and PCR bank
2021-09-13 17:12:20 +02:00
Lennart Poettering
f4529c4d97 update TODO 2021-09-13 15:02:57 +02:00
Lennart Poettering
d38466bae6 tpm2: log about invalid PCRs on each unsealing
Let's log every time we use uninitialized PCRs when unsealing a secret
via TPM2. This indicates a firmware issue usually, and is something we
shouldn't just show when enrolling but also show every time we unseal,
so that the fact that the selected PCR policy is pretty much pointless
is repeatedly shown.
2021-09-13 15:02:53 +02:00
Lennart Poettering
321a9d9ee5 tpm2: check if PCR values make sense before using them
Fixes: #20684
2021-09-13 15:02:53 +02:00
Lennart Poettering
2b92a67261 tpm2: support RSA primary keys as fallback if TPM2 devices don't support ECC
Previously, we hardcoded use of ECC as primary keys, since they are much
faster (i.e. saving multiple seconds) to do TPM2 operations with. Alas,
not all TPM2 chips appear to support ECC. Bummer.

Let's hence add a fallback logic: if we can't create an ECC primary key,
use an RSA key, and store that fact away.

AFIU the security guarantees should be roughly the same, it's just that
RSA primary keys is so much slower to work with than ECC.

The primary key algorithm is used is stored in the JSON header of LUKS
disks, in a new field. If the field is absent we assume to use ECC, to
provide full compatibility with old systemd versions.

The primary key algorithm is stored in a new field in the credentials
file format (in fact, a previously unused zero space is used), too.

Hopefully, this should ensure that TPM2 support will "just work" on more
systems.

Fixes: #20361
2021-09-13 14:48:23 +02:00
Lennart Poettering
3f9992d82e memory-util: replace memeqzero() by a more generic memeqbyte()
The new helper can check for any byte, no just zeroes. The old name is
then converted into a macro that wraps our new version of the helper.
2021-09-13 12:48:27 +02:00
Luca Boccassi
503994bada
Merge pull request #20701 from yuwata/network-address-cleanups
network: several address_update() related cleanups
2021-09-13 11:12:43 +01:00
Yu Watanabe
ecedc48b19 network: do not drop IPv6LL address in link_drop_addresses() 2021-09-11 10:49:11 +09:00
Yu Watanabe
981fab5d0a network: always call address ready callback if address is ready
The address ready callback is used for cleaning up old addresses or
routes acquired by e.g. DHCP. However, the callback was called only
when the address was previously not ready. So, maybe, unnecessary
addresses or routes may not be cleared.

Also, this makes the callback is called slightly earlier. As it may
remove several addresses or routes, and possibly changes the link state.
2021-09-11 10:49:11 +09:00
Yu Watanabe
18d8a33a37 network: define Address earlier
As the type is used in defining address_ready_callback_t.
2021-09-11 10:49:11 +09:00
Yu Watanabe
7657ec3eb8 network: store IPv6LL address even if link is in failed state
Otherwise, if IPv6LL is received when the link is in failed state,
we may fail to reconfigure the link.
2021-09-11 10:49:11 +09:00
Yu Watanabe
7a8685ffef network: enable IP masquerade when address is assigned
Previously, IP masquerade is enabled when configuring the address. But,
the request of assigning the address may be refused by kernel.
2021-09-11 10:49:11 +09:00
Yu Watanabe
2c40a8895e network: simplify code a bit 2021-09-11 10:49:11 +09:00
19 changed files with 496 additions and 160 deletions

4
TODO
View File

@ -83,6 +83,10 @@ Janitorial Clean-ups:
Features:
* in uefi stub: query firmware regarding which PCRs are being used, store that
in EFI var. then use this when enrolling TPM2 in cryptsetup to verify that
the selected PCRs actually are used by firmware.
* rework recursive read-only remount to use new mount API
* PAM: pick auf one authentication token from credentials

View File

@ -18,26 +18,25 @@ size_t page_size(void) {
return pgsz;
}
bool memeqzero(const void *data, size_t length) {
/* Does the buffer consist entirely of NULs?
bool memeqbyte(uint8_t byte, const void *data, size_t length) {
/* Does the buffer consist entirely of the same specific byte value?
* Copied from https://github.com/systemd/casync/, copied in turn from
* https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92,
* which is licensed CC-0.
*/
const uint8_t *p = data;
size_t i;
/* Check first 16 bytes manually */
for (i = 0; i < 16; i++, length--) {
for (size_t i = 0; i < 16; i++, length--) {
if (length == 0)
return true;
if (p[i])
if (p[i] != byte)
return false;
}
/* Now we know first 16 bytes are NUL, memcmp with self. */
return memcmp(data, p + i, length) == 0;
/* Now we know first 16 bytes match, memcmp() with self. */
return memcmp(data, p + 16, length) == 0;
}
#if !HAVE_EXPLICIT_BZERO

View File

@ -47,7 +47,9 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2
#define zero(x) (memzero(&(x), sizeof(x)))
bool memeqzero(const void *data, size_t length);
bool memeqbyte(uint8_t byte, const void *data, size_t length);
#define memeqzero(data, length) memeqbyte(0x00, data, length)
#define eqzero(x) memeqzero(x, sizeof(x))

View File

@ -235,8 +235,36 @@ static int detect_vm_dmi(void) {
/* The DMI vendor tables in /sys/class/dmi/id don't help us distinguish between Amazon EC2
* virtual machines and bare-metal instances, so we need to look at SMBIOS. */
if (r == VIRTUALIZATION_AMAZON && detect_vm_smbios() == SMBIOS_VM_BIT_UNSET)
return VIRTUALIZATION_NONE;
if (r == VIRTUALIZATION_AMAZON) {
switch (detect_vm_smbios()) {
case SMBIOS_VM_BIT_SET:
return VIRTUALIZATION_AMAZON;
case SMBIOS_VM_BIT_UNSET:
return VIRTUALIZATION_NONE;
case SMBIOS_VM_BIT_UNKNOWN: {
/* The DMI information we are after is only accessible to the root user,
* so we fallback to using the product name which is less restricted
* to distinguish metal systems from virtualized instances */
_cleanup_free_ char *s = NULL;
r = read_full_virtual_file("/sys/class/dmi/id/product_name", &s, NULL);
/* In EC2, virtualized is much more common than metal, so if for some reason
* we fail to read the DMI data, assume we are virtualized. */
if (r < 0) {
log_debug_errno(r, "Can't read /sys/class/dmi/id/product_name,"
" assuming virtualized: %m");
return VIRTUALIZATION_AMAZON;
}
if (endswith(truncate_nl(s), ".metal")) {
log_debug("DMI product name ends with '.metal', assuming no virtualization");
return VIRTUALIZATION_NONE;
} else
return VIRTUALIZATION_AMAZON;
}
default:
assert_not_reached();
}
}
/* If we haven't identified a VM, but the firmware indicates that there is one, indicate as much. We
* have no further information about what it is. */

View File

@ -65,7 +65,7 @@ int enroll_tpm2(struct crypt_device *cd,
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
size_t secret_size, secret2_size, blob_size, hash_size;
_cleanup_free_ void *blob = NULL, *hash = NULL;
uint16_t pcr_bank;
uint16_t pcr_bank, primary_alg;
const char *node;
int r, keyslot;
@ -76,7 +76,7 @@ int enroll_tpm2(struct crypt_device *cd,
assert_se(node = crypt_get_device_name(cd));
r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank);
r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg);
if (r < 0)
return r;
@ -93,7 +93,7 @@ int enroll_tpm2(struct crypt_device *cd,
/* Quick verification that everything is in order, we are not in a hurry after all. */
log_debug("Unsealing for verification...");
r = tpm2_unseal(device, pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
r = tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
if (r < 0)
return r;
@ -119,7 +119,7 @@ int enroll_tpm2(struct crypt_device *cd,
if (keyslot < 0)
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &v);
r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &v);
if (r < 0)
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");

View File

@ -57,7 +57,7 @@ _public_ int cryptsetup_token_open(
const char *json;
size_t blob_size, policy_hash_size, decrypted_key_size;
uint32_t pcr_mask;
uint16_t pcr_bank;
uint16_t pcr_bank, primary_alg;
systemd_tpm2_plugin_params params = {
.search_pcr_mask = UINT32_MAX
};
@ -78,7 +78,7 @@ _public_ int cryptsetup_token_open(
if (usrptr)
params = *(systemd_tpm2_plugin_params *)usrptr;
r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &base64_blob, &hex_policy_hash);
r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash);
if (r < 0)
return log_debug_open_error(cd, r);
@ -95,6 +95,7 @@ _public_ int cryptsetup_token_open(
r = acquire_luks2_key(
pcr_mask,
pcr_bank,
primary_alg,
params.device,
blob,
blob_size,
@ -135,7 +136,7 @@ _public_ void cryptsetup_token_dump(
int r;
uint32_t pcr_mask;
uint16_t pcr_bank;
uint16_t pcr_bank, primary_alg;
size_t decoded_blob_size;
_cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL,
*pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL;
@ -143,7 +144,7 @@ _public_ void cryptsetup_token_dump(
assert(json);
r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &base64_blob, &hex_policy_hash);
r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
@ -167,6 +168,7 @@ _public_ void cryptsetup_token_dump(
crypt_log(cd, "\ttpm2-pcrs: %s\n", strna(pcrs_str));
crypt_log(cd, "\ttpm2-bank: %s\n", strna(tpm2_pcr_bank_to_string(pcr_bank)));
crypt_log(cd, "\ttpm2-primary-alg: %s\n", strna(tpm2_primary_alg_to_string(primary_alg)));
crypt_log(cd, "\ttpm2-blob: %s\n", blob_str);
crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
}
@ -212,7 +214,8 @@ _public_ int cryptsetup_token_validate(
}
}
/* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded to SHA256 */
/* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded
* to SHA256. */
w = json_variant_by_key(v, "tpm2-pcr-bank");
if (w) {
/* The PCR bank field is optional */
@ -228,6 +231,23 @@ _public_ int cryptsetup_token_validate(
}
}
/* The primary key algorithm field is optional, since it was also added in systemd 250 only. Before
* the algorithm was hardcoded to ECC. */
w = json_variant_by_key(v, "tpm2-primary-alg");
if (w) {
/* The primary key algorithm is optional */
if (!json_variant_is_string(w)) {
crypt_log_debug(cd, "TPM2 primary key algorithm is not a string.");
return 1;
}
if (tpm2_primary_alg_from_string(json_variant_string(w)) < 0) {
crypt_log_debug(cd, "TPM2 primary key algorithm invalid or not supported: %s", json_variant_string(w));
return 1;
}
}
w = json_variant_by_key(v, "tpm2-blob");
if (!w || !json_variant_is_string(w)) {
crypt_log_debug(cd, "TPM2 token data lacks 'tpm2-blob' field.");

View File

@ -11,6 +11,7 @@
int acquire_luks2_key(
uint32_t pcr_mask,
uint16_t pcr_bank,
uint16_t primary_alg,
const char *device,
const void *key_data,
size_t key_data_size,
@ -38,6 +39,7 @@ int acquire_luks2_key(
return tpm2_unseal(
device,
pcr_mask, pcr_bank,
primary_alg,
key_data, key_data_size,
policy_hash, policy_hash_size,
ret_decrypted_key, ret_decrypted_key_size);
@ -49,19 +51,21 @@ int parse_luks2_tpm2_data(
uint32_t search_pcr_mask,
uint32_t *ret_pcr_mask,
uint16_t *ret_pcr_bank,
uint16_t *ret_primary_alg,
char **ret_base64_blob,
char **ret_hex_policy_hash) {
int r;
JsonVariant *w, *e;
uint32_t pcr_mask = 0;
uint16_t pcr_bank = UINT16_MAX;
uint16_t pcr_bank = UINT16_MAX, primary_alg = TPM2_ALG_ECC;
_cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
assert(json);
assert(ret_pcr_mask);
assert(ret_pcr_bank);
assert(ret_primary_alg);
assert(ret_base64_blob);
assert(ret_hex_policy_hash);
@ -104,6 +108,20 @@ int parse_luks2_tpm2_data(
pcr_bank = r;
}
w = json_variant_by_key(v, "tpm2-primary-alg");
if (w) {
/* The primary key algorithm is optional */
if (!json_variant_is_string(w))
return -EINVAL;
r = tpm2_primary_alg_from_string(json_variant_string(w));
if (r < 0)
return r;
primary_alg = r;
}
w = json_variant_by_key(v, "tpm2-blob");
if (!w || !json_variant_is_string(w))
return -EINVAL;
@ -122,6 +140,7 @@ int parse_luks2_tpm2_data(
*ret_pcr_mask = pcr_mask;
*ret_pcr_bank = pcr_bank;
*ret_primary_alg = primary_alg;
*ret_base64_blob = TAKE_PTR(base64_blob);
*ret_hex_policy_hash = TAKE_PTR(hex_policy_hash);

View File

@ -7,6 +7,7 @@ struct crypt_device;
int acquire_luks2_key(
uint32_t pcr_mask,
uint16_t pcr_bank,
uint16_t primary_alg,
const char *device,
const void *key_data,
size_t key_data_size,
@ -20,5 +21,6 @@ int parse_luks2_tpm2_data(
uint32_t search_pcr_mask,
uint32_t *ret_pcr_mask,
uint16_t *ret_pcr_bank,
uint16_t *ret_primary_alg,
char **ret_base64_blob,
char **ret_hex_policy_hash);

View File

@ -14,6 +14,7 @@ int acquire_tpm2_key(
const char *device,
uint32_t pcr_mask,
uint16_t pcr_bank,
uint16_t primary_alg,
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
@ -63,7 +64,7 @@ int acquire_tpm2_key(
blob = loaded_blob;
}
return tpm2_unseal(device, pcr_mask, pcr_bank, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
return tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
}
int find_tpm2_auto_data(
@ -72,6 +73,7 @@ int find_tpm2_auto_data(
int start_token,
uint32_t *ret_pcr_mask,
uint16_t *ret_pcr_bank,
uint16_t *ret_primary_alg,
void **ret_blob,
size_t *ret_blob_size,
void **ret_policy_hash,
@ -84,6 +86,7 @@ int find_tpm2_auto_data(
int r, keyslot = -1, token = -1;
uint32_t pcr_mask = 0;
uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
uint16_t primary_alg = TPM2_ALG_ECC; /* ECC was the only supported algorithm in systemd < 250, use that as implied default, for compatibility */
assert(cd);
@ -122,8 +125,11 @@ int find_tpm2_auto_data(
search_pcr_mask != pcr_mask) /* PCR mask doesn't match what is configured, ignore this entry */
continue;
/* The bank field is optional, since it was added in systemd 250 only. Before the bank was hardcoded to SHA256 */
assert(pcr_bank == UINT16_MAX);
assert(primary_alg == TPM2_ALG_ECC);
/* The bank field is optional, since it was added in systemd 250 only. Before the bank was
* hardcoded to SHA256. */
w = json_variant_by_key(v, "tpm2-pcr-bank");
if (w) {
/* The PCR bank field is optional */
@ -139,6 +145,23 @@ int find_tpm2_auto_data(
pcr_bank = r;
}
/* The primary key algorithm field is optional, since it was also added in systemd 250
* only. Before the algorithm was hardcoded to ECC. */
w = json_variant_by_key(v, "tpm2-primary-alg");
if (w) {
/* The primary key algorithm is optional */
if (!json_variant_is_string(w))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"TPM2 primary key algorithm is not a string.");
r = tpm2_primary_alg_from_string(json_variant_string(w));
if (r < 0)
return log_error_errno(r, "TPM2 primary key algorithm invalid or not supported: %s", json_variant_string(w));
primary_alg = r;
}
assert(!blob);
w = json_variant_by_key(v, "tpm2-blob");
if (!w || !json_variant_is_string(w))
@ -184,6 +207,7 @@ int find_tpm2_auto_data(
*ret_keyslot = keyslot;
*ret_token = token;
*ret_pcr_bank = pcr_bank;
*ret_primary_alg = primary_alg;
return 0;
}

View File

@ -14,6 +14,7 @@ int acquire_tpm2_key(
const char *device,
uint32_t pcr_mask,
uint16_t pcr_bank,
uint16_t primary_alg,
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
@ -30,6 +31,7 @@ int find_tpm2_auto_data(
int start_token,
uint32_t *ret_pcr_mask,
uint16_t *ret_pcr_bank,
uint16_t *ret_primary_alg,
void **ret_blob,
size_t *ret_blob_size,
void **ret_policy_hash,
@ -44,6 +46,7 @@ static inline int acquire_tpm2_key(
const char *device,
uint32_t pcr_mask,
uint16_t pcr_bank,
uint16_t primary_alg,
const char *key_file,
size_t key_file_size,
uint64_t key_file_offset,
@ -64,6 +67,7 @@ static inline int find_tpm2_auto_data(
int start_token,
uint32_t *ret_pcr_mask,
uint16_t *ret_pcr_bank,
uint16_t *ret_primary_alg,
void **ret_blob,
size_t *ret_blob_size,
void **ret_policy_hash,

View File

@ -1248,6 +1248,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
arg_tpm2_device,
arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT : arg_tpm2_pcr_mask,
UINT16_MAX,
0,
key_file, arg_keyfile_size, arg_keyfile_offset,
key_data, key_data_size,
NULL, 0, /* we don't know the policy hash */
@ -1284,7 +1285,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
for (;;) {
uint32_t pcr_mask;
uint16_t pcr_bank;
uint16_t pcr_bank, primary_alg;
r = find_tpm2_auto_data(
cd,
@ -1292,6 +1293,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
token, /* search for the token with this index, or any later index than this */
&pcr_mask,
&pcr_bank,
&primary_alg,
&blob, &blob_size,
&policy_hash, &policy_hash_size,
&keyslot,
@ -1314,6 +1316,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
arg_tpm2_device,
pcr_mask,
pcr_bank,
primary_alg,
NULL, 0, 0, /* no key file */
blob, blob_size,
policy_hash, policy_hash_size,

View File

@ -436,42 +436,46 @@ static int address_add(Link *link, const Address *in, Address **ret) {
}
static int address_update(Address *address, const Address *src) {
bool ready;
Link *link;
int r;
assert(address);
assert(address->link);
assert(src);
ready = address_is_ready(address);
link = address->link;
address->flags = src->flags;
address->scope = src->scope;
address->cinfo = src->cinfo;
if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 0;
if (address_is_ready(address) &&
address->family == AF_INET6 &&
in6_addr_is_link_local(&address->in_addr.in6) &&
in6_addr_is_null(&link->ipv6ll_address)) {
link_update_operstate(address->link, true);
link_check_ready(address->link);
link->ipv6ll_address = address->in_addr.in6;
if (!ready && address_is_ready(address)) {
if (address->callback) {
r = address->callback(address);
if (r < 0)
return r;
}
if (address->family == AF_INET6 &&
in6_addr_is_link_local(&address->in_addr.in6) > 0 &&
in6_addr_is_null(&address->link->ipv6ll_address)) {
r = link_ipv6ll_gained(address->link, &address->in_addr.in6);
if (r < 0)
return r;
}
r = link_ipv6ll_gained(link);
if (r < 0)
return r;
}
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 0;
r = address_set_masquerade(address, true);
if (r < 0)
return log_link_warning_errno(link, r, "Could not enable IP masquerading: %m");
if (address_is_ready(address) && address->callback) {
r = address->callback(address);
if (r < 0)
return r;
}
link_update_operstate(link, true);
link_check_ready(link);
return 0;
}
@ -878,7 +882,7 @@ int link_drop_foreign_addresses(Link *link) {
SET_FOREACH(address, link->addresses_foreign) {
/* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */
if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6) == 1)
if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6))
continue;
if (link_address_is_dynamic(link, address)) {
@ -911,8 +915,8 @@ int link_drop_addresses(Link *link) {
assert(link);
SET_FOREACH(address, link->addresses) {
/* we consider IPv6LL addresses to be managed by the kernel */
if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6) == 1 && link_ipv6ll_enabled(link))
/* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */
if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6))
continue;
k = address_remove(address, link);
@ -1288,10 +1292,6 @@ int request_process_address(Request *req) {
return r;
}
r = address_set_masquerade(a, true);
if (r < 0)
log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m");
return 1;
}

View File

@ -14,12 +14,13 @@
#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
typedef struct Address Address;
typedef struct Manager Manager;
typedef struct Network Network;
typedef struct Request Request;
typedef int (*address_ready_callback_t)(Address *address);
typedef struct Address {
struct Address {
Network *network;
NetworkConfigSection *section;
@ -48,7 +49,7 @@ typedef struct Address {
/* Called when address become ready */
address_ready_callback_t callback;
} Address;
};
const char* format_lifetime(char *buf, size_t l, uint32_t lifetime) _warn_unused_result_;
/* Note: the lifetime of the compound literal is the immediately surrounding block,

View File

@ -701,24 +701,21 @@ static int link_acquire_dynamic_conf(Link *link) {
return 0;
}
int link_ipv6ll_gained(Link *link, const struct in6_addr *address) {
int link_ipv6ll_gained(Link *link) {
int r;
assert(link);
log_link_info(link, "Gained IPv6LL");
link->ipv6ll_address = *address;
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
return 0;
r = link_acquire_dynamic_ipv6_conf(link);
if (r < 0)
return r;
link_check_ready(link);
if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
r = link_acquire_dynamic_ipv6_conf(link);
if (r < 0) {
link_enter_failed(link);
return r;
}
}
return 0;
}

View File

@ -236,7 +236,7 @@ bool link_has_carrier(Link *link);
bool link_ipv6_enabled(Link *link);
bool link_ipv6ll_enabled(Link *link);
int link_ipv6ll_gained(Link *link, const struct in6_addr *address);
int link_ipv6ll_gained(Link *link);
bool link_ipv4ll_enabled(Link *link);

View File

@ -2618,10 +2618,10 @@ static int partition_encrypt(
_cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_free_ void *blob = NULL, *hash = NULL;
size_t secret_size, blob_size, hash_size;
uint16_t pcr_bank;
uint16_t pcr_bank, primary_alg;
int keyslot;
r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank);
r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg);
if (r < 0)
return log_error_errno(r, "Failed to seal to TPM2: %m");
@ -2643,7 +2643,7 @@ static int partition_encrypt(
if (keyslot < 0)
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &v);
r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &v);
if (r < 0)
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");

View File

@ -371,10 +371,10 @@ struct _packed_ encrypted_credential_header {
};
struct _packed_ tpm2_credential_header {
le64_t pcr_mask; /* Note that the spec for PC Clients only mandates 24 PCRs, and that's what systems
* generally have. But keep the door open for more. */
le16_t pcr_bank; /* For now, either TPM2_ALG_SHA256 or TPM2_ALG_SHA1 */
le16_t _zero; /* Filler to maintain 32bit alignment */
le64_t pcr_mask; /* Note that the spec for PC Clients only mandates 24 PCRs, and that's what systems
* generally have. But keep the door open for more. */
le16_t pcr_bank; /* For now, either TPM2_ALG_SHA256 or TPM2_ALG_SHA1 */
le16_t primary_alg; /* Primary key algorithm (either TPM2_ALG_RSA or TPM2_ALG_ECC for now) */
le32_t blob_size;
le32_t policy_hash_size;
uint8_t policy_hash_and_blob[];
@ -441,10 +441,10 @@ int encrypt_credential_and_warn(
size_t host_key_size = 0, tpm2_key_size = 0, tpm2_blob_size = 0, tpm2_policy_hash_size = 0, output_size, p, ml;
_cleanup_free_ void *tpm2_blob = NULL, *tpm2_policy_hash = NULL, *iv = NULL, *output = NULL;
_cleanup_free_ struct metadata_credential_header *m = NULL;
uint16_t tpm2_pcr_bank = 0, tpm2_primary_alg = 0;
struct encrypted_credential_header *h;
int ksz, bsz, ivsz, tsz, added, r;
uint8_t md[SHA256_DIGEST_LENGTH];
uint16_t tpm2_pcr_bank = 0;
const EVP_CIPHER *cc;
#if HAVE_TPM2
bool try_tpm2 = false;
@ -512,7 +512,8 @@ int encrypt_credential_and_warn(
&tpm2_blob_size,
&tpm2_policy_hash,
&tpm2_policy_hash_size,
&tpm2_pcr_bank);
&tpm2_pcr_bank,
&tpm2_primary_alg);
if (r < 0) {
if (!sd_id128_is_null(with_key))
return r;
@ -606,6 +607,7 @@ int encrypt_credential_and_warn(
t = (struct tpm2_credential_header*) ((uint8_t*) output + p);
t->pcr_mask = htole64(tpm2_pcr_mask);
t->pcr_bank = htole16(tpm2_pcr_bank);
t->primary_alg = htole16(tpm2_primary_alg);
t->blob_size = htole32(tpm2_blob_size);
t->policy_hash_size = htole32(tpm2_policy_hash_size);
memcpy(t->policy_hash_and_blob, tpm2_blob, tpm2_blob_size);
@ -747,10 +749,10 @@ int decrypt_credential_and_warn(
if (le64toh(t->pcr_mask) >= (UINT64_C(1) << TPM2_PCRS_MAX))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR mask out of range.");
if (!tpm2_pcr_bank_supported(le16toh(t->pcr_bank)))
if (!tpm2_pcr_bank_to_string(le16toh(t->pcr_bank)))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR bank invalid or not supported");
if (le16toh(t->_zero) != 0)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 padding space not zero.");
if (!tpm2_primary_alg_to_string(le16toh(t->primary_alg)))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 primary key algorithm invalid or not supported.");
if (le32toh(t->blob_size) > CREDENTIAL_FIELD_SIZE_MAX)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected TPM2 blob size.");
if (le32toh(t->policy_hash_size) > CREDENTIAL_FIELD_SIZE_MAX)
@ -768,6 +770,7 @@ int decrypt_credential_and_warn(
r = tpm2_unseal(tpm2_device,
le64toh(t->pcr_mask),
le16toh(t->pcr_bank),
le16toh(t->primary_alg),
t->policy_hash_and_blob,
le32toh(t->blob_size),
t->policy_hash_and_blob + le32toh(t->blob_size),

View File

@ -29,6 +29,7 @@ TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, E
TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes) = NULL;
TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion) = NULL;
TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle) = NULL;
TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues);
TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest) = 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;
@ -56,6 +57,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_GetRandom),
DLSYM_ARG(Esys_Initialize),
DLSYM_ARG(Esys_Load),
DLSYM_ARG(Esys_PCR_Read),
DLSYM_ARG(Esys_PolicyGetDigest),
DLSYM_ARG(Esys_PolicyPCR),
DLSYM_ARG(Esys_StartAuthSession),
@ -257,10 +259,12 @@ static int tpm2_credit_random(ESYS_CONTEXT *c) {
static int tpm2_make_primary(
ESYS_CONTEXT *c,
ESYS_TR *ret_primary) {
ESYS_TR *ret_primary,
TPMI_ALG_PUBLIC alg,
TPMI_ALG_PUBLIC *ret_alg) {
static const TPM2B_SENSITIVE_CREATE primary_sensitive = {};
static const TPM2B_PUBLIC primary_template = {
static const TPM2B_PUBLIC primary_template_ecc = {
.size = sizeof(TPMT_PUBLIC),
.publicArea = {
.type = TPM2_ALG_ECC,
@ -280,48 +284,207 @@ static int tpm2_make_primary(
},
},
};
static const TPM2B_PUBLIC primary_template_rsa = {
.size = sizeof(TPMT_PUBLIC),
.publicArea = {
.type = TPM2_ALG_RSA,
.nameAlg = TPM2_ALG_SHA256,
.objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
.parameters = {
.rsaDetail = {
.symmetric = {
.algorithm = TPM2_ALG_AES,
.keyBits.aes = 128,
.mode.aes = TPM2_ALG_CFB,
},
.scheme.scheme = TPM2_ALG_NULL,
.keyBits = 2048,
},
},
},
};
static const TPML_PCR_SELECTION creation_pcr = {};
ESYS_TR primary = ESYS_TR_NONE;
TSS2_RC rc;
usec_t ts;
log_debug("Creating primary key on TPM.");
rc = sym_Esys_CreatePrimary(
c,
ESYS_TR_RH_OWNER,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
&primary_sensitive,
&primary_template,
NULL,
&creation_pcr,
&primary,
NULL,
NULL,
NULL,
NULL);
/* So apparently not all TPM2 devices support ECC. ECC is generally preferably, because it's so much
* faster, noticeably so (~10s vs. ~240ms on my system). Hence, unless explicitly configured let's
* try to use ECC first, and if that does not work, let's fall back to RSA. */
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to generate primary key in TPM: %s", sym_Tss2_RC_Decode(rc));
ts = now(CLOCK_MONOTONIC);
log_debug("Successfully created primary key on TPM.");
if (IN_SET(alg, 0, TPM2_ALG_ECC)) {
rc = sym_Esys_CreatePrimary(
c,
ESYS_TR_RH_OWNER,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
&primary_sensitive,
&primary_template_ecc,
NULL,
&creation_pcr,
&primary,
NULL,
NULL,
NULL,
NULL);
if (rc != TSS2_RC_SUCCESS) {
if (alg != 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to generate ECC primary key in TPM: %s", sym_Tss2_RC_Decode(rc));
log_debug("Failed to generate ECC primary key in TPM, trying RSA: %s", sym_Tss2_RC_Decode(rc));
} else {
log_debug("Successfully created ECC primary key on TPM.");
alg = TPM2_ALG_ECC;
}
}
if (IN_SET(alg, 0, TPM2_ALG_RSA)) {
rc = sym_Esys_CreatePrimary(
c,
ESYS_TR_RH_OWNER,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
&primary_sensitive,
&primary_template_rsa,
NULL,
&creation_pcr,
&primary,
NULL,
NULL,
NULL,
NULL);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to generate RSA primary key in TPM: %s", sym_Tss2_RC_Decode(rc));
else if (alg == 0) {
log_notice("TPM2 chip apparently does not support ECC primary keys, falling back to RSA. "
"This likely means TPM2 operations will be relatively slow, please be patient.");
alg = TPM2_ALG_RSA;
}
log_debug("Successfully created RSA primary key on TPM.");
}
log_debug("Generating primary key on TPM2 took %s.", FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - ts, USEC_PER_MSEC));
*ret_primary = primary;
if (ret_alg)
*ret_alg = alg;
return 0;
}
static void tpm2_pcr_mask_to_selecion(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret) {
assert(ret);
/* We only do 24bit here, as that's what PC TPMs are supposed to support */
assert(mask <= 0xFFFFFFU);
*ret = (TPML_PCR_SELECTION) {
.count = 1,
.pcrSelections[0].hash = bank,
.pcrSelections[0].sizeofSelect = 3,
.pcrSelections[0].pcrSelect[0] = mask & 0xFF,
.pcrSelections[0].pcrSelect[1] = (mask >> 8) & 0xFF,
.pcrSelections[0].pcrSelect[2] = (mask >> 16) & 0xFF,
};
}
static unsigned find_nth_bit(uint32_t mask, unsigned n) {
uint32_t bit = 1;
assert(n < 32);
/* Returns the bit index of the nth set bit, e.g. mask=0b101001, n=3 → 5 */
for (unsigned i = 0; i < sizeof(mask)*8; i++) {
if (bit & mask) {
if (n == 0)
return i;
n--;
}
bit <<= 1;
}
return UINT_MAX;
}
static int tpm2_pcr_mask_good(
ESYS_CONTEXT *c,
TPMI_ALG_HASH bank,
uint32_t mask) {
_cleanup_(Esys_Freep) TPML_DIGEST *pcr_values = NULL;
TPML_PCR_SELECTION selection;
bool good = false;
TSS2_RC rc;
assert(c);
/* So we have the problem that some systems might have working TPM2 chips, but the firmware doesn't
* actually measure into them, or only into a suboptimal bank. If so, the PCRs should be all zero or
* all 0xFF. Detect that, so that we can warn and maybe pick a better bank. */
tpm2_pcr_mask_to_selecion(mask, bank, &selection);
rc = sym_Esys_PCR_Read(
c,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
&selection,
NULL,
NULL,
&pcr_values);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to read TPM2 PCRs: %s", sym_Tss2_RC_Decode(rc));
/* If at least one of the selected PCR values is something other than all 0x00 or all 0xFF we are happy. */
for (unsigned i = 0; i < pcr_values->count; i++) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *h = NULL;
unsigned j;
h = hexmem(pcr_values->digests[i].buffer, pcr_values->digests[i].size);
j = find_nth_bit(mask, i);
assert(j != UINT_MAX);
log_debug("PCR %u value: %s", j, strna(h));
}
if (!memeqbyte(0x00, pcr_values->digests[i].buffer, pcr_values->digests[i].size) &&
!memeqbyte(0xFF, pcr_values->digests[i].buffer, pcr_values->digests[i].size))
good = true;
}
return good;
}
static int tpm2_get_best_pcr_bank(
ESYS_CONTEXT *c,
uint32_t pcr_mask,
TPMI_ALG_HASH *ret) {
_cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *pcap = NULL;
TPMI_ALG_HASH hash = TPM2_ALG_SHA1;
bool found = false;
TPMI_ALG_HASH supported_hash = 0, hash_with_valid_pcr = 0;
TPMI_YES_NO more;
TSS2_RC rc;
assert(c);
rc = sym_Esys_GetCapability(
c,
ESYS_TR_NONE,
@ -340,6 +503,11 @@ static int tpm2_get_best_pcr_bank(
for (size_t i = 0; i < pcap->data.assignedPCR.count; i++) {
bool valid = true;
int good;
/* For now we are only interested in the SHA1 and SHA256 banks */
if (!IN_SET(pcap->data.assignedPCR.pcrSelections[i].hash, TPM2_ALG_SHA256, TPM2_ALG_SHA1))
continue;
/* As per
* https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf a
@ -367,28 +535,58 @@ static int tpm2_get_best_pcr_bank(
continue;
}
if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA256) {
hash = TPM2_ALG_SHA256;
found = true;
break;
}
good = tpm2_pcr_mask_good(c, pcap->data.assignedPCR.pcrSelections[i].hash, pcr_mask);
if (good < 0)
return good;
if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA1)
found = true;
if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA256) {
supported_hash = TPM2_ALG_SHA256;
if (good) {
/* Great, SHA256 is supported and has initialized PCR values, we are done. */
hash_with_valid_pcr = TPM2_ALG_SHA256;
break;
}
} else {
assert(pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA1);
if (supported_hash == 0)
supported_hash = TPM2_ALG_SHA1;
if (good && hash_with_valid_pcr == 0)
hash_with_valid_pcr = TPM2_ALG_SHA1;
}
}
if (!found)
/* We preferably pick SHA256, but only if its PCRs are initialized or neither the SHA1 nor the SHA256
* PCRs are initialized. If SHA256 is not supported but SHA1 is and its PCRs are too, we prefer
* SHA1.
*
* We log at LOG_NOTICE level whenever we end up using the SHA1 bank or when the PCRs we bind to are
* not initialized. */
if (hash_with_valid_pcr == TPM2_ALG_SHA256) {
assert(supported_hash == TPM2_ALG_SHA256);
log_debug("TPM2 device supports SHA256 PCR bank and SHA256 PCRs are valid, yay!");
*ret = TPM2_ALG_SHA256;
} else if (hash_with_valid_pcr == TPM2_ALG_SHA1) {
if (supported_hash == TPM2_ALG_SHA256)
log_notice("TPM2 device supports both SHA1 and SHA256 PCR banks, but only SHA1 PCRs are valid, falling back to SHA1 bank. This reduces the security level substantially.");
else {
assert(supported_hash == TPM2_ALG_SHA1);
log_notice("TPM2 device lacks support for SHA256 PCR bank, but SHA1 bank is supported and SHA1 PCRs are valid, falling back to SHA1 bank. This reduces the security level substantially.");
}
*ret = TPM2_ALG_SHA1;
} else if (supported_hash == TPM2_ALG_SHA256) {
log_notice("TPM2 device supports SHA256 PCR bank but none of the selected PCRs are valid! Firmware apparently did not initialize any of the selected PCRs. Proceeding anyway with SHA256 bank. PCR policy effectively unenforced!");
*ret = TPM2_ALG_SHA256;
} else if (supported_hash == TPM2_ALG_SHA1) {
log_notice("TPM2 device lacks support for SHA256 bank, but SHA1 bank is supported, but none of the selected PCRs are valid! Firmware apparently did not initialize any of the selected PCRs. Proceeding anyway with SHA1 bank. PCR policy effectively unenforced!");
*ret = TPM2_ALG_SHA1;
} else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"TPM2 module supports neither SHA1 nor SHA256 PCR banks, cannot operate.");
if (hash == TPM2_ALG_SHA256)
log_debug("TPM2 device supports SHA256 PCR banks, yay!");
else {
assert(hash == TPM2_ALG_SHA1);
log_debug("TPM2 device lacks support for SHA256 PCR banks, falling back to SHA1 banks.");
}
*ret = hash;
return 0;
}
@ -409,15 +607,8 @@ static int tpm2_make_pcr_session(
.aes = TPM2_ALG_CFB,
}
};
TPML_PCR_SELECTION pcr_selection = {
.count = 1,
.pcrSelections[0].hash = TPM2_ALG_SHA256, /* overridden below, depending on TPM2 capabilities */
.pcrSelections[0].sizeofSelect = 3,
.pcrSelections[0].pcrSelect[0] = pcr_mask & 0xFF,
.pcrSelections[0].pcrSelect[1] = (pcr_mask >> 8) & 0xFF,
.pcrSelections[0].pcrSelect[2] = (pcr_mask >> 16) & 0xFF,
};
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
TPML_PCR_SELECTION pcr_selection;
ESYS_TR session = ESYS_TR_NONE;
TSS2_RC rc;
int r;
@ -426,14 +617,24 @@ static int tpm2_make_pcr_session(
log_debug("Starting authentication session.");
if (pcr_bank != UINT16_MAX)
pcr_selection.pcrSelections[0].hash = pcr_bank;
else {
/* No bank configured, pick automatically. Some TPM2 devices only can do SHA1. If we detect
* that use that, but preferably use SHA256 */
r = tpm2_get_best_pcr_bank(c, &pcr_selection.pcrSelections[0].hash);
if (pcr_bank != UINT16_MAX) {
r = tpm2_pcr_mask_good(c, pcr_bank, pcr_mask);
if (r < 0)
return r;
if (r == 0)
log_notice("Selected TPM2 PCRs are not initialized on this system, most likely due to a firmware issue. PCR policy is effectively not enforced. Proceeding anyway.");
tpm2_pcr_mask_to_selecion(pcr_mask, pcr_bank, &pcr_selection);
} else {
TPMI_ALG_HASH h;
/* No bank configured, pick automatically. Some TPM2 devices only can do SHA1. If we detect
* that use that, but preferably use SHA256 */
r = tpm2_get_best_pcr_bank(c, pcr_mask, &h);
if (r < 0)
return r;
tpm2_pcr_mask_to_selecion(pcr_mask, h, &pcr_selection);
}
rc = sym_Esys_StartAuthSession(
@ -525,7 +726,8 @@ int tpm2_seal(
size_t *ret_blob_size,
void **ret_pcr_hash,
size_t *ret_pcr_hash_size,
uint16_t *ret_pcr_bank) {
uint16_t *ret_pcr_bank,
uint16_t *ret_primary_alg) {
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
@ -536,6 +738,7 @@ int tpm2_seal(
_cleanup_free_ void *blob = NULL, *hash = NULL;
TPM2B_SENSITIVE_CREATE hmac_sensitive;
ESYS_TR primary = ESYS_TR_NONE;
TPMI_ALG_PUBLIC primary_alg;
TPM2B_PUBLIC hmac_template;
TPMI_ALG_HASH pcr_bank;
size_t k, blob_size;
@ -555,13 +758,14 @@ int tpm2_seal(
/* So here's what we do here: we connect to the TPM2 chip. It persistently contains a "seed" key that
* is randomized when the TPM2 is first initialized or reset and remains stable across boots. We
* generate a "primary" key pair derived from that (RSA). Given the seed remains fixed this will
* result in the same key pair whenever we specify the exact same parameters for it. We then create a
* PCR-bound policy session, which calculates a hash on the current PCR values of the indexes we
* specify. We then generate a randomized key on the host (which is the key we actually enroll in the
* LUKS2 keyslots), which we upload into the TPM2, where it is encrypted with the "primary" key,
* taking the PCR policy session into account. We then download the encrypted key from the TPM2
* ("sealing") and marshall it into binary form, which is ultimately placed in the LUKS2 JSON header.
* generate a "primary" key pair derived from that (ECC if possible, RSA as fallback). Given the seed
* remains fixed this will result in the same key pair whenever we specify the exact same parameters
* for it. We then create a PCR-bound policy session, which calculates a hash on the current PCR
* values of the indexes we specify. We then generate a randomized key on the host (which is the key
* we actually enroll in the LUKS2 keyslots), which we upload into the TPM2, where it is encrypted
* with the "primary" key, taking the PCR policy session into account. We then download the encrypted
* key from the TPM2 ("sealing") and marshall it into binary form, which is ultimately placed in the
* LUKS2 JSON header.
*
* The TPM2 "seed" key and "primary" keys never leave the TPM2 chip (and cannot be extracted at
* all). The random key we enroll in LUKS2 we generate on the host using the Linux random device. It
@ -574,7 +778,7 @@ int tpm2_seal(
if (r < 0)
return r;
r = tpm2_make_primary(c.esys_context, &primary);
r = tpm2_make_primary(c.esys_context, &primary, 0, &primary_alg);
if (r < 0)
return r;
@ -701,6 +905,7 @@ int tpm2_seal(
*ret_pcr_hash = TAKE_PTR(hash);
*ret_pcr_hash_size = policy_digest->size;
*ret_pcr_bank = pcr_bank;
*ret_primary_alg = primary_alg;
r = 0;
@ -713,6 +918,7 @@ int tpm2_unseal(
const char *device,
uint32_t pcr_mask,
uint16_t pcr_bank,
uint16_t primary_alg,
const void *blob,
size_t blob_size,
const void *known_policy_hash,
@ -783,7 +989,7 @@ int tpm2_unseal(
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"Current policy digest does not match stored policy digest, cancelling TPM2 authentication attempt.");
r = tpm2_make_primary(c.esys_context, &primary);
r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL);
if (r < 0)
return r;
@ -1012,6 +1218,7 @@ int tpm2_make_luks2_json(
int keyslot,
uint32_t pcr_mask,
uint16_t pcr_bank,
uint16_t primary_alg,
const void *blob,
size_t blob_size,
const void *policy_hash,
@ -1055,6 +1262,7 @@ int tpm2_make_luks2_json(
JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_BASE64(blob, blob_size)),
JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(a)),
JSON_BUILD_PAIR_CONDITION(!!tpm2_pcr_bank_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_pcr_bank_to_string(pcr_bank))),
JSON_BUILD_PAIR_CONDITION(!!tpm2_primary_alg_to_string(primary_alg), "tpm2-primary-alg", JSON_BUILD_STRING(tpm2_primary_alg_to_string(primary_alg))),
JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size))));
if (r < 0)
return r;
@ -1065,24 +1273,9 @@ int tpm2_make_luks2_json(
return keyslot;
}
/* We want the helpers below to work also if TPM2 libs are not available, hence define these two defines if
* they are missing. */
#ifndef TPM2_ALG_SHA256
#define TPM2_ALG_SHA256 0xB
#endif
#ifndef TPM2_ALG_SHA1
#define TPM2_ALG_SHA1 0x4
#endif
int tpm2_pcr_bank_supported(uint16_t bank) {
const char *tpm2_pcr_bank_to_string(uint16_t bank) {
/* For now, let's officially only support these two. We can extend this later on, should the need
* arise. */
return IN_SET(bank, TPM2_ALG_SHA256, TPM2_ALG_SHA1);
}
const char *tpm2_pcr_bank_to_string(uint16_t bank) {
/* Similar here, only support the two for now, we can always extend this later. */
if (bank == TPM2_ALG_SHA256)
return "sha256";
if (bank == TPM2_ALG_SHA1)
@ -1097,3 +1290,19 @@ int tpm2_pcr_bank_from_string(const char *bank) {
return TPM2_ALG_SHA1;
return -EINVAL;
}
const char *tpm2_primary_alg_to_string(uint16_t alg) {
if (alg == TPM2_ALG_ECC)
return "ecc";
if (alg == TPM2_ALG_RSA)
return "rsa";
return NULL;
}
int tpm2_primary_alg_from_string(const char *alg) {
if (streq_ptr(alg, "ecc"))
return TPM2_ALG_ECC;
if (streq_ptr(alg, "rsa"))
return TPM2_ALG_RSA;
return -EINVAL;
}

View File

@ -19,6 +19,7 @@ extern TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shan
extern TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes);
extern TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion);
extern TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle);
extern TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues);
extern TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest);
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);
@ -34,8 +35,8 @@ extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], siz
int dlopen_tpm2(void);
int tpm2_seal(const char *device, uint32_t pcr_mask, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank);
int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size);
int tpm2_seal(const char *device, uint32_t pcr_mask, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg);
int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size);
#endif
@ -44,17 +45,37 @@ int tpm2_find_device_auto(int log_level, char **ret);
int tpm2_parse_pcrs(const char *s, uint32_t *ret);
int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret);
int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret);
#define TPM2_PCRS_MAX 24
/* Default to PCR 7 only */
#define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7)
int tpm2_pcr_bank_supported(uint16_t bank);
/* We want the helpers below to work also if TPM2 libs are not available, hence define these four defines if
* they are missing. */
#ifndef TPM2_ALG_SHA256
#define TPM2_ALG_SHA256 0xB
#endif
#ifndef TPM2_ALG_SHA1
#define TPM2_ALG_SHA1 0x4
#endif
#ifndef TPM2_ALG_ECC
#define TPM2_ALG_ECC 0x23
#endif
#ifndef TPM2_ALG_RSA
#define TPM2_ALG_RSA 0x1
#endif
const char *tpm2_pcr_bank_to_string(uint16_t bank);
int tpm2_pcr_bank_from_string(const char *bank);
const char *tpm2_primary_alg_to_string(uint16_t bank);
int tpm2_primary_alg_from_string(const char *alg);
typedef struct {
uint32_t search_pcr_mask;
const char *device;