mirror of
https://github.com/systemd/systemd
synced 2026-03-24 07:44:52 +01:00
Compare commits
8 Commits
a4d9c121cc
...
7d963260a1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d963260a1 | ||
|
|
ee96d41eba | ||
|
|
07697bfee6 | ||
|
|
9554c51425 | ||
|
|
56ce4adafe | ||
|
|
30763a32b2 | ||
|
|
bf18f4b671 | ||
|
|
1f0fb7d544 |
@ -375,6 +375,9 @@ const char *special_glyph(SpecialGlyph code) {
|
|||||||
[SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[",
|
[SPECIAL_GLYPH_DEPRESSED_SMILEY] = ":-[",
|
||||||
[SPECIAL_GLYPH_LOCK_AND_KEY] = "o-,",
|
[SPECIAL_GLYPH_LOCK_AND_KEY] = "o-,",
|
||||||
[SPECIAL_GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */
|
[SPECIAL_GLYPH_TOUCH] = "O=", /* Yeah, not very convincing, can you do it better? */
|
||||||
|
[SPECIAL_GLYPH_RECYCLING] = "~",
|
||||||
|
[SPECIAL_GLYPH_DOWNLOAD] = "\\",
|
||||||
|
[SPECIAL_GLYPH_SPARKLES] = "*",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* UTF-8 */
|
/* UTF-8 */
|
||||||
@ -421,7 +424,12 @@ const char *special_glyph(SpecialGlyph code) {
|
|||||||
[SPECIAL_GLYPH_LOCK_AND_KEY] = "\360\237\224\220", /* 🔐 (actually called: CLOSED LOCK WITH KEY) */
|
[SPECIAL_GLYPH_LOCK_AND_KEY] = "\360\237\224\220", /* 🔐 (actually called: CLOSED LOCK WITH KEY) */
|
||||||
|
|
||||||
/* This emoji is a single character cell glyph in Unicode, and two in ASCII */
|
/* This emoji is a single character cell glyph in Unicode, and two in ASCII */
|
||||||
[SPECIAL_GLYPH_TOUCH] = "\360\237\221\206", /* 👆 (actually called: BACKHAND INDEX POINTING UP */
|
[SPECIAL_GLYPH_TOUCH] = "\360\237\221\206", /* 👆 (actually called: BACKHAND INDEX POINTING UP) */
|
||||||
|
|
||||||
|
/* These three emojis are single character cell glyphs in Unicode and also in ASCII. */
|
||||||
|
[SPECIAL_GLYPH_RECYCLING] = "\u267B\uFE0F ", /* ♻️ (actually called: UNIVERSAL RECYCLNG SYMBOL) */
|
||||||
|
[SPECIAL_GLYPH_DOWNLOAD] = "\u2935\uFE0F ", /* ⤵️ (actually called: RIGHT ARROW CURVING DOWN) */
|
||||||
|
[SPECIAL_GLYPH_SPARKLES] = "\u2728", /* ✨ */
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -69,6 +69,9 @@ typedef enum SpecialGlyph {
|
|||||||
SPECIAL_GLYPH_DEPRESSED_SMILEY,
|
SPECIAL_GLYPH_DEPRESSED_SMILEY,
|
||||||
SPECIAL_GLYPH_LOCK_AND_KEY,
|
SPECIAL_GLYPH_LOCK_AND_KEY,
|
||||||
SPECIAL_GLYPH_TOUCH,
|
SPECIAL_GLYPH_TOUCH,
|
||||||
|
SPECIAL_GLYPH_RECYCLING,
|
||||||
|
SPECIAL_GLYPH_DOWNLOAD,
|
||||||
|
SPECIAL_GLYPH_SPARKLES,
|
||||||
_SPECIAL_GLYPH_MAX,
|
_SPECIAL_GLYPH_MAX,
|
||||||
_SPECIAL_GLYPH_INVALID = -EINVAL,
|
_SPECIAL_GLYPH_INVALID = -EINVAL,
|
||||||
} SpecialGlyph;
|
} SpecialGlyph;
|
||||||
@ -95,3 +98,7 @@ static inline void locale_variables_freep(char*(*l)[_VARIABLE_LC_MAX]) {
|
|||||||
static inline const char *special_glyph_check_mark(bool b) {
|
static inline const char *special_glyph_check_mark(bool b) {
|
||||||
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
|
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const char *special_glyph_check_mark_space(bool b) {
|
||||||
|
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : " ";
|
||||||
|
}
|
||||||
|
|||||||
@ -65,6 +65,7 @@ int enroll_tpm2(struct crypt_device *cd,
|
|||||||
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
|
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
|
||||||
size_t secret_size, secret2_size, blob_size, hash_size;
|
size_t secret_size, secret2_size, blob_size, hash_size;
|
||||||
_cleanup_free_ void *blob = NULL, *hash = NULL;
|
_cleanup_free_ void *blob = NULL, *hash = NULL;
|
||||||
|
uint16_t pcr_bank;
|
||||||
const char *node;
|
const char *node;
|
||||||
int r, keyslot;
|
int r, keyslot;
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ int enroll_tpm2(struct crypt_device *cd,
|
|||||||
|
|
||||||
assert_se(node = crypt_get_device_name(cd));
|
assert_se(node = crypt_get_device_name(cd));
|
||||||
|
|
||||||
r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size);
|
r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -92,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. */
|
/* Quick verification that everything is in order, we are not in a hurry after all. */
|
||||||
log_debug("Unsealing for verification...");
|
log_debug("Unsealing for verification...");
|
||||||
r = tpm2_unseal(device, pcr_mask, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
|
r = tpm2_unseal(device, pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &secret2, &secret2_size);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -118,7 +119,7 @@ int enroll_tpm2(struct crypt_device *cd,
|
|||||||
if (keyslot < 0)
|
if (keyslot < 0)
|
||||||
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
|
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
|
||||||
|
|
||||||
r = tpm2_make_luks2_json(keyslot, pcr_mask, blob, blob_size, hash, hash_size, &v);
|
r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &v);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
|
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ int acquire_tpm2_key(
|
|||||||
const char *volume_name,
|
const char *volume_name,
|
||||||
const char *device,
|
const char *device,
|
||||||
uint32_t pcr_mask,
|
uint32_t pcr_mask,
|
||||||
|
uint16_t pcr_bank,
|
||||||
const char *key_file,
|
const char *key_file,
|
||||||
size_t key_file_size,
|
size_t key_file_size,
|
||||||
uint64_t key_file_offset,
|
uint64_t key_file_offset,
|
||||||
@ -62,7 +63,7 @@ int acquire_tpm2_key(
|
|||||||
blob = loaded_blob;
|
blob = loaded_blob;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tpm2_unseal(device, pcr_mask, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
|
return tpm2_unseal(device, pcr_mask, pcr_bank, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int find_tpm2_auto_data(
|
int find_tpm2_auto_data(
|
||||||
@ -70,6 +71,7 @@ int find_tpm2_auto_data(
|
|||||||
uint32_t search_pcr_mask,
|
uint32_t search_pcr_mask,
|
||||||
int start_token,
|
int start_token,
|
||||||
uint32_t *ret_pcr_mask,
|
uint32_t *ret_pcr_mask,
|
||||||
|
uint16_t *ret_pcr_bank,
|
||||||
void **ret_blob,
|
void **ret_blob,
|
||||||
size_t *ret_blob_size,
|
size_t *ret_blob_size,
|
||||||
void **ret_policy_hash,
|
void **ret_policy_hash,
|
||||||
@ -81,6 +83,7 @@ int find_tpm2_auto_data(
|
|||||||
size_t blob_size = 0, policy_hash_size = 0;
|
size_t blob_size = 0, policy_hash_size = 0;
|
||||||
int r, keyslot = -1, token = -1;
|
int r, keyslot = -1, token = -1;
|
||||||
uint32_t pcr_mask = 0;
|
uint32_t pcr_mask = 0;
|
||||||
|
uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
|
||||||
|
|
||||||
assert(cd);
|
assert(cd);
|
||||||
|
|
||||||
@ -119,6 +122,23 @@ int find_tpm2_auto_data(
|
|||||||
search_pcr_mask != pcr_mask) /* PCR mask doesn't match what is configured, ignore this entry */
|
search_pcr_mask != pcr_mask) /* PCR mask doesn't match what is configured, ignore this entry */
|
||||||
continue;
|
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);
|
||||||
|
w = json_variant_by_key(v, "tpm2-pcr-bank");
|
||||||
|
if (w) {
|
||||||
|
/* The PCR bank field is optional */
|
||||||
|
|
||||||
|
if (!json_variant_is_string(w))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
"TPM2 PCR bank is not a string.");
|
||||||
|
|
||||||
|
r = tpm2_pcr_bank_from_string(json_variant_string(w));
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "TPM2 PCR bank invalid or not supported: %s", json_variant_string(w));
|
||||||
|
|
||||||
|
pcr_bank = r;
|
||||||
|
}
|
||||||
|
|
||||||
assert(!blob);
|
assert(!blob);
|
||||||
w = json_variant_by_key(v, "tpm2-blob");
|
w = json_variant_by_key(v, "tpm2-blob");
|
||||||
if (!w || !json_variant_is_string(w))
|
if (!w || !json_variant_is_string(w))
|
||||||
@ -163,6 +183,7 @@ int find_tpm2_auto_data(
|
|||||||
*ret_policy_hash_size = policy_hash_size;
|
*ret_policy_hash_size = policy_hash_size;
|
||||||
*ret_keyslot = keyslot;
|
*ret_keyslot = keyslot;
|
||||||
*ret_token = token;
|
*ret_token = token;
|
||||||
|
*ret_pcr_bank = pcr_bank;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ int acquire_tpm2_key(
|
|||||||
const char *volume_name,
|
const char *volume_name,
|
||||||
const char *device,
|
const char *device,
|
||||||
uint32_t pcr_mask,
|
uint32_t pcr_mask,
|
||||||
|
uint16_t pcr_bank,
|
||||||
const char *key_file,
|
const char *key_file,
|
||||||
size_t key_file_size,
|
size_t key_file_size,
|
||||||
uint64_t key_file_offset,
|
uint64_t key_file_offset,
|
||||||
@ -28,6 +29,7 @@ int find_tpm2_auto_data(
|
|||||||
uint32_t search_pcr_mask,
|
uint32_t search_pcr_mask,
|
||||||
int start_token,
|
int start_token,
|
||||||
uint32_t *ret_pcr_mask,
|
uint32_t *ret_pcr_mask,
|
||||||
|
uint16_t *ret_pcr_bank,
|
||||||
void **ret_blob,
|
void **ret_blob,
|
||||||
size_t *ret_blob_size,
|
size_t *ret_blob_size,
|
||||||
void **ret_policy_hash,
|
void **ret_policy_hash,
|
||||||
@ -41,6 +43,7 @@ static inline int acquire_tpm2_key(
|
|||||||
const char *volume_name,
|
const char *volume_name,
|
||||||
const char *device,
|
const char *device,
|
||||||
uint32_t pcr_mask,
|
uint32_t pcr_mask,
|
||||||
|
uint16_t pcr_bank,
|
||||||
const char *key_file,
|
const char *key_file,
|
||||||
size_t key_file_size,
|
size_t key_file_size,
|
||||||
uint64_t key_file_offset,
|
uint64_t key_file_offset,
|
||||||
@ -60,6 +63,7 @@ static inline int find_tpm2_auto_data(
|
|||||||
uint32_t search_pcr_mask,
|
uint32_t search_pcr_mask,
|
||||||
int start_token,
|
int start_token,
|
||||||
uint32_t *ret_pcr_mask,
|
uint32_t *ret_pcr_mask,
|
||||||
|
uint16_t *ret_pcr_bank,
|
||||||
void **ret_blob,
|
void **ret_blob,
|
||||||
size_t *ret_blob_size,
|
size_t *ret_blob_size,
|
||||||
void **ret_policy_hash,
|
void **ret_policy_hash,
|
||||||
|
|||||||
@ -1103,6 +1103,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
|
|||||||
name,
|
name,
|
||||||
arg_tpm2_device,
|
arg_tpm2_device,
|
||||||
arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT : arg_tpm2_pcr_mask,
|
arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT : arg_tpm2_pcr_mask,
|
||||||
|
UINT16_MAX,
|
||||||
key_file, arg_keyfile_size, arg_keyfile_offset,
|
key_file, arg_keyfile_size, arg_keyfile_offset,
|
||||||
key_data, key_data_size,
|
key_data, key_data_size,
|
||||||
NULL, 0, /* we don't know the policy hash */
|
NULL, 0, /* we don't know the policy hash */
|
||||||
@ -1139,12 +1140,14 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
uint32_t pcr_mask;
|
uint32_t pcr_mask;
|
||||||
|
uint16_t pcr_bank;
|
||||||
|
|
||||||
r = find_tpm2_auto_data(
|
r = find_tpm2_auto_data(
|
||||||
cd,
|
cd,
|
||||||
arg_tpm2_pcr_mask, /* if != UINT32_MAX we'll only look for tokens with this PCR mask */
|
arg_tpm2_pcr_mask, /* if != UINT32_MAX we'll only look for tokens with this PCR mask */
|
||||||
token, /* search for the token with this index, or any later index than this */
|
token, /* search for the token with this index, or any later index than this */
|
||||||
&pcr_mask,
|
&pcr_mask,
|
||||||
|
&pcr_bank,
|
||||||
&blob, &blob_size,
|
&blob, &blob_size,
|
||||||
&policy_hash, &policy_hash_size,
|
&policy_hash, &policy_hash_size,
|
||||||
&keyslot,
|
&keyslot,
|
||||||
@ -1166,6 +1169,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
|
|||||||
name,
|
name,
|
||||||
arg_tpm2_device,
|
arg_tpm2_device,
|
||||||
pcr_mask,
|
pcr_mask,
|
||||||
|
pcr_bank,
|
||||||
NULL, 0, 0, /* no key file */
|
NULL, 0, 0, /* no key file */
|
||||||
blob, blob_size,
|
blob, blob_size,
|
||||||
policy_hash, policy_hash_size,
|
policy_hash, policy_hash_size,
|
||||||
|
|||||||
@ -2624,9 +2624,10 @@ static int partition_encrypt(
|
|||||||
_cleanup_(erase_and_freep) void *secret = NULL;
|
_cleanup_(erase_and_freep) void *secret = NULL;
|
||||||
_cleanup_free_ void *blob = NULL, *hash = NULL;
|
_cleanup_free_ void *blob = NULL, *hash = NULL;
|
||||||
size_t secret_size, blob_size, hash_size;
|
size_t secret_size, blob_size, hash_size;
|
||||||
|
uint16_t pcr_bank;
|
||||||
int keyslot;
|
int keyslot;
|
||||||
|
|
||||||
r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size);
|
r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to seal to TPM2: %m");
|
return log_error_errno(r, "Failed to seal to TPM2: %m");
|
||||||
|
|
||||||
@ -2648,7 +2649,7 @@ static int partition_encrypt(
|
|||||||
if (keyslot < 0)
|
if (keyslot < 0)
|
||||||
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
|
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, blob, blob_size, hash, hash_size, &v);
|
r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, blob, blob_size, hash, hash_size, &v);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
|
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
|
||||||
|
|
||||||
|
|||||||
@ -369,7 +369,10 @@ struct _packed_ encrypted_credential_header {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct _packed_ tpm2_credential_header {
|
struct _packed_ tpm2_credential_header {
|
||||||
le64_t pcr_mask;
|
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 */
|
||||||
le32_t blob_size;
|
le32_t blob_size;
|
||||||
le32_t policy_hash_size;
|
le32_t policy_hash_size;
|
||||||
uint8_t policy_hash_and_blob[];
|
uint8_t policy_hash_and_blob[];
|
||||||
@ -439,6 +442,7 @@ int encrypt_credential_and_warn(
|
|||||||
struct encrypted_credential_header *h;
|
struct encrypted_credential_header *h;
|
||||||
int ksz, bsz, ivsz, tsz, added, r;
|
int ksz, bsz, ivsz, tsz, added, r;
|
||||||
uint8_t md[SHA256_DIGEST_LENGTH];
|
uint8_t md[SHA256_DIGEST_LENGTH];
|
||||||
|
uint16_t tpm2_pcr_bank = 0;
|
||||||
const EVP_CIPHER *cc;
|
const EVP_CIPHER *cc;
|
||||||
#if HAVE_TPM2
|
#if HAVE_TPM2
|
||||||
bool try_tpm2 = false;
|
bool try_tpm2 = false;
|
||||||
@ -505,7 +509,8 @@ int encrypt_credential_and_warn(
|
|||||||
&tpm2_blob,
|
&tpm2_blob,
|
||||||
&tpm2_blob_size,
|
&tpm2_blob_size,
|
||||||
&tpm2_policy_hash,
|
&tpm2_policy_hash,
|
||||||
&tpm2_policy_hash_size);
|
&tpm2_policy_hash_size,
|
||||||
|
&tpm2_pcr_bank);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (!sd_id128_is_null(with_key))
|
if (!sd_id128_is_null(with_key))
|
||||||
return r;
|
return r;
|
||||||
@ -598,6 +603,7 @@ int encrypt_credential_and_warn(
|
|||||||
|
|
||||||
t = (struct tpm2_credential_header*) ((uint8_t*) output + p);
|
t = (struct tpm2_credential_header*) ((uint8_t*) output + p);
|
||||||
t->pcr_mask = htole64(tpm2_pcr_mask);
|
t->pcr_mask = htole64(tpm2_pcr_mask);
|
||||||
|
t->pcr_bank = htole16(tpm2_pcr_bank);
|
||||||
t->blob_size = htole32(tpm2_blob_size);
|
t->blob_size = htole32(tpm2_blob_size);
|
||||||
t->policy_hash_size = htole32(tpm2_policy_hash_size);
|
t->policy_hash_size = htole32(tpm2_policy_hash_size);
|
||||||
memcpy(t->policy_hash_and_blob, tpm2_blob, tpm2_blob_size);
|
memcpy(t->policy_hash_and_blob, tpm2_blob, tpm2_blob_size);
|
||||||
@ -739,6 +745,10 @@ int decrypt_credential_and_warn(
|
|||||||
|
|
||||||
if (le64toh(t->pcr_mask) >= (UINT64_C(1) << TPM2_PCRS_MAX))
|
if (le64toh(t->pcr_mask) >= (UINT64_C(1) << TPM2_PCRS_MAX))
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR mask out of range.");
|
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR mask out of range.");
|
||||||
|
if (!tpm2_pcr_bank_supported(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 (le32toh(t->blob_size) > CREDENTIAL_FIELD_SIZE_MAX)
|
if (le32toh(t->blob_size) > CREDENTIAL_FIELD_SIZE_MAX)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected TPM2 blob size.");
|
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected TPM2 blob size.");
|
||||||
if (le32toh(t->policy_hash_size) > CREDENTIAL_FIELD_SIZE_MAX)
|
if (le32toh(t->policy_hash_size) > CREDENTIAL_FIELD_SIZE_MAX)
|
||||||
@ -755,6 +765,7 @@ int decrypt_credential_and_warn(
|
|||||||
|
|
||||||
r = tpm2_unseal(tpm2_device,
|
r = tpm2_unseal(tpm2_device,
|
||||||
le64toh(t->pcr_mask),
|
le64toh(t->pcr_mask),
|
||||||
|
le16toh(t->pcr_bank),
|
||||||
t->policy_hash_and_blob,
|
t->policy_hash_and_blob,
|
||||||
le32toh(t->blob_size),
|
le32toh(t->blob_size),
|
||||||
t->policy_hash_and_blob + le32toh(t->blob_size),
|
t->policy_hash_and_blob + le32toh(t->blob_size),
|
||||||
|
|||||||
@ -14,53 +14,114 @@
|
|||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
|
|
||||||
int import_url_last_component(const char *url, char **ret) {
|
static const char *skip_protocol_and_hostname(const char *url) {
|
||||||
const char *e, *p;
|
const char *d;
|
||||||
char *s;
|
size_t n;
|
||||||
|
|
||||||
e = strchrnul(url, '?');
|
/* A very very lenient implementation of RFC3986 Section 3.2 */
|
||||||
|
|
||||||
while (e > url && e[-1] == '/')
|
/* Find colon separating protocol and hostname */
|
||||||
|
d = strchr(url, ':');
|
||||||
|
if (!d || url == d)
|
||||||
|
return NULL;
|
||||||
|
d++;
|
||||||
|
|
||||||
|
/* Skip slashes after colon */
|
||||||
|
d += strspn(d, "/");
|
||||||
|
|
||||||
|
/* Skip everything till next slash or end */
|
||||||
|
n = strcspn(d, "/?#");
|
||||||
|
if (n == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return d + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int import_url_last_component(
|
||||||
|
const char *url,
|
||||||
|
char **ret) {
|
||||||
|
|
||||||
|
const char *e, *p, *h;
|
||||||
|
|
||||||
|
/* This extracts the last path component of the specified URI, i.e. the last non-empty substrings
|
||||||
|
* between two "/" characters. This ignores "Query" and "Fragment" suffixes (as per RFC3986). */
|
||||||
|
|
||||||
|
h = skip_protocol_and_hostname(url);
|
||||||
|
if (!h)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
|
||||||
|
|
||||||
|
while (e > h && e[-1] == '/') /* Eat trailing slashes */
|
||||||
e--;
|
e--;
|
||||||
|
|
||||||
p = e;
|
p = e;
|
||||||
while (p > url && p[-1] != '/')
|
while (p > h && p[-1] != '/') /* Find component before that */
|
||||||
p--;
|
p--;
|
||||||
|
|
||||||
if (e <= p)
|
if (e <= p) /* Empty component? */
|
||||||
return -EINVAL;
|
return -EADDRNOTAVAIL;
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
char *s;
|
||||||
|
|
||||||
s = strndup(p, e - p);
|
s = strndup(p, e - p);
|
||||||
if (!s)
|
if (!s)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
*ret = s;
|
*ret = s;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
|
int import_url_change_suffix(
|
||||||
const char *e;
|
const char *url,
|
||||||
|
size_t n_drop_components,
|
||||||
|
const char *suffix,
|
||||||
|
char **ret) {
|
||||||
|
|
||||||
|
const char *e, *h;
|
||||||
char *s;
|
char *s;
|
||||||
|
|
||||||
assert(url);
|
assert(url);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
e = strchrnul(url, '?');
|
/* This drops the specified number of path components of the specified URI, i.e. the specified number
|
||||||
|
* of non-empty substring between two "/" characters from the end of the string, and then append the
|
||||||
|
* specified suffix instead. Before doing all this it chops off the "Query" and "Fragment" suffixes
|
||||||
|
* (they are *not* readded to the final URL). Note that n_drop_components may be 0 (in which case the
|
||||||
|
* component are simply added to the end). The suffix may be specified as NULL or empty string in
|
||||||
|
* which case nothing is appended, only the specified number of components chopped off. Note that the
|
||||||
|
* function may be called with n_drop_components == 0 and suffix == NULL, in which case the "Query"
|
||||||
|
* and "Fragment" is chopped off, and ensured the URL ends in a single "/", and that's it. */
|
||||||
|
|
||||||
while (e > url && e[-1] == '/')
|
h = skip_protocol_and_hostname(url);
|
||||||
e--;
|
if (!h)
|
||||||
|
|
||||||
while (e > url && e[-1] != '/')
|
|
||||||
e--;
|
|
||||||
|
|
||||||
if (e <= url)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
s = new(char, (e - url) + strlen(suffix) + 1);
|
e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
|
||||||
|
|
||||||
|
while (e > h && e[-1] == '/') /* Eat trailing slashes */
|
||||||
|
e--;
|
||||||
|
|
||||||
|
/* Drop the specified number of components from the end. Note that this is pretty lenient: if there
|
||||||
|
* are less component we silently drop those and then append the suffix to the top. */
|
||||||
|
while (n_drop_components > 0) {
|
||||||
|
while (e > h && e[-1] != '/') /* Eat last word (we don't mind if empty) */
|
||||||
|
e--;
|
||||||
|
|
||||||
|
while (e > h && e[-1] == '/') /* Eat slashes before the last word */
|
||||||
|
e--;
|
||||||
|
|
||||||
|
n_drop_components--;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = new(char, (e - url) + 1 + strlen_ptr(suffix) + 1);
|
||||||
if (!s)
|
if (!s)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
strcpy(mempcpy(s, url, e - url), suffix);
|
strcpy(stpcpy(mempcpy(s, url, e - url), "/"), strempty(suffix));
|
||||||
*ret = s;
|
*ret = s;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,16 @@ typedef enum ImportVerify {
|
|||||||
} ImportVerify;
|
} ImportVerify;
|
||||||
|
|
||||||
int import_url_last_component(const char *url, char **ret);
|
int import_url_last_component(const char *url, char **ret);
|
||||||
int import_url_change_last_component(const char *url, const char *suffix, char **ret);
|
|
||||||
|
int import_url_change_suffix(const char *url, size_t n_drop_components, const char *suffix, char **ret);
|
||||||
|
|
||||||
|
static inline int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
|
||||||
|
return import_url_change_suffix(url, 1, suffix, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int import_url_append_component(const char *url, const char *suffix, char **ret) {
|
||||||
|
return import_url_change_suffix(url, 0, suffix, ret);
|
||||||
|
}
|
||||||
|
|
||||||
const char* import_verify_to_string(ImportVerify v) _const_;
|
const char* import_verify_to_string(ImportVerify v) _const_;
|
||||||
ImportVerify import_verify_from_string(const char *s) _pure_;
|
ImportVerify import_verify_from_string(const char *s) _pure_;
|
||||||
|
|||||||
@ -19,6 +19,9 @@
|
|||||||
#include "stat-util.h"
|
#include "stat-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
|
|
||||||
|
/* We treat tmpfs/ramfs + cgroupfs as non-physical file sytems. cgroupfs is similar to tmpfs in a way after
|
||||||
|
* all: we can create arbitrary directory hierarchies in it, and hence can also use rm_rf() on it to remove
|
||||||
|
* those again. */
|
||||||
static bool is_physical_fs(const struct statfs *sfs) {
|
static bool is_physical_fs(const struct statfs *sfs) {
|
||||||
return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
|
return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
|
||||||
}
|
}
|
||||||
@ -113,133 +116,145 @@ int fstatat_harder(int dfd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
|
static int rm_rf_children_inner(
|
||||||
_cleanup_closedir_ DIR *d = NULL;
|
int fd,
|
||||||
struct dirent *de;
|
const char *fname,
|
||||||
int ret = 0, r;
|
int is_dir,
|
||||||
struct statfs sfs;
|
RemoveFlags flags,
|
||||||
|
const struct stat *root_dev) {
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
|
assert(fname);
|
||||||
|
|
||||||
/* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
|
if (is_dir < 0 || (is_dir > 0 && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
|
||||||
* fd, in all cases, including on failure.. */
|
|
||||||
|
|
||||||
if (!(flags & REMOVE_PHYSICAL)) {
|
r = fstatat_harder(fd, fname, &st, AT_SYMLINK_NOFOLLOW, flags);
|
||||||
|
if (r < 0)
|
||||||
r = fstatfs(fd, &sfs);
|
return r;
|
||||||
if (r < 0) {
|
|
||||||
safe_close(fd);
|
|
||||||
return -errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_physical_fs(&sfs)) {
|
|
||||||
/* We refuse to clean physical file systems with this call,
|
|
||||||
* unless explicitly requested. This is extra paranoia just
|
|
||||||
* to be sure we never ever remove non-state data. */
|
|
||||||
_cleanup_free_ char *path = NULL;
|
|
||||||
|
|
||||||
(void) fd_get_path(fd, &path);
|
|
||||||
log_error("Attempted to remove disk file system under \"%s\", and we can't allow that.",
|
|
||||||
strna(path));
|
|
||||||
|
|
||||||
safe_close(fd);
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d = fdopendir(fd);
|
|
||||||
if (!d) {
|
|
||||||
safe_close(fd);
|
|
||||||
return errno == ENOENT ? 0 : -errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
FOREACH_DIRENT_ALL(de, d, return -errno) {
|
|
||||||
bool is_dir;
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if (dot_or_dot_dot(de->d_name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (de->d_type == DT_UNKNOWN ||
|
|
||||||
(de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) {
|
|
||||||
r = fstatat_harder(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW, flags);
|
|
||||||
if (r < 0) {
|
|
||||||
if (ret == 0 && r != -ENOENT)
|
|
||||||
ret = r;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
is_dir = S_ISDIR(st.st_mode);
|
is_dir = S_ISDIR(st.st_mode);
|
||||||
} else
|
}
|
||||||
is_dir = de->d_type == DT_DIR;
|
|
||||||
|
|
||||||
if (is_dir) {
|
if (is_dir) {
|
||||||
_cleanup_close_ int subdir_fd = -1;
|
_cleanup_close_ int subdir_fd = -1;
|
||||||
|
int q;
|
||||||
|
|
||||||
/* if root_dev is set, remove subdirectories only if device is same */
|
/* if root_dev is set, remove subdirectories only if device is same */
|
||||||
if (root_dev && st.st_dev != root_dev->st_dev)
|
if (root_dev && st.st_dev != root_dev->st_dev)
|
||||||
continue;
|
return 0;
|
||||||
|
|
||||||
subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
|
||||||
if (subdir_fd < 0) {
|
|
||||||
if (ret == 0 && errno != ENOENT)
|
|
||||||
ret = -errno;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop at mount points */
|
/* Stop at mount points */
|
||||||
r = fd_is_mount_point(fd, de->d_name, 0);
|
r = fd_is_mount_point(fd, fname, 0);
|
||||||
if (r < 0) {
|
if (r < 0)
|
||||||
if (ret == 0 && r != -ENOENT)
|
return r;
|
||||||
ret = r;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (r > 0)
|
if (r > 0)
|
||||||
continue;
|
return 0;
|
||||||
|
|
||||||
if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
|
if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
|
||||||
|
|
||||||
/* This could be a subvolume, try to remove it */
|
/* This could be a subvolume, try to remove it */
|
||||||
|
|
||||||
r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
|
r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (!IN_SET(r, -ENOTTY, -EINVAL)) {
|
if (!IN_SET(r, -ENOTTY, -EINVAL))
|
||||||
if (ret == 0)
|
return r;
|
||||||
ret = r;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ENOTTY, then it wasn't a btrfs subvolume, continue below. */
|
/* ENOTTY, then it wasn't a btrfs subvolume, continue below. */
|
||||||
} else
|
} else
|
||||||
/* It was a subvolume, continue. */
|
/* It was a subvolume, done. */
|
||||||
continue;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file
|
subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
|
||||||
* system type again for each directory */
|
if (subdir_fd < 0)
|
||||||
r = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
|
return -errno;
|
||||||
if (r < 0 && ret == 0)
|
|
||||||
ret = r;
|
|
||||||
|
|
||||||
r = unlinkat_harder(fd, de->d_name, AT_REMOVEDIR, flags);
|
/* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type
|
||||||
if (r < 0 && r != -ENOENT && ret == 0)
|
* again for each directory */
|
||||||
ret = r;
|
q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
|
||||||
|
|
||||||
|
r = unlinkat_harder(fd, fname, AT_REMOVEDIR, flags);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (q < 0)
|
||||||
|
return q;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
} else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
|
} else if (!(flags & REMOVE_ONLY_DIRECTORIES)) {
|
||||||
|
r = unlinkat_harder(fd, fname, 0, flags);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = unlinkat_harder(fd, de->d_name, 0, flags);
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rm_rf_children(
|
||||||
|
int fd,
|
||||||
|
RemoveFlags flags,
|
||||||
|
const struct stat *root_dev) {
|
||||||
|
|
||||||
|
_cleanup_closedir_ DIR *d = NULL;
|
||||||
|
struct dirent *de;
|
||||||
|
int ret = 0, r;
|
||||||
|
|
||||||
|
assert(fd >= 0);
|
||||||
|
|
||||||
|
/* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
|
||||||
|
* fd, in all cases, including on failure. */
|
||||||
|
|
||||||
|
d = fdopendir(fd);
|
||||||
|
if (!d) {
|
||||||
|
safe_close(fd);
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags & REMOVE_PHYSICAL)) {
|
||||||
|
struct statfs sfs;
|
||||||
|
|
||||||
|
if (fstatfs(dirfd(d), &sfs) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
if (is_physical_fs(&sfs)) {
|
||||||
|
/* We refuse to clean physical file systems with this call, unless explicitly
|
||||||
|
* requested. This is extra paranoia just to be sure we never ever remove non-state
|
||||||
|
* data. */
|
||||||
|
|
||||||
|
_cleanup_free_ char *path = NULL;
|
||||||
|
|
||||||
|
(void) fd_get_path(fd, &path);
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
|
||||||
|
"Attempted to remove disk file system under \"%s\", and we can't allow that.",
|
||||||
|
strna(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FOREACH_DIRENT_ALL(de, d, return -errno) {
|
||||||
|
int is_dir;
|
||||||
|
|
||||||
|
if (dot_or_dot_dot(de->d_name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
is_dir =
|
||||||
|
de->d_type == DT_UNKNOWN ? -1 :
|
||||||
|
de->d_type == DT_DIR;
|
||||||
|
|
||||||
|
r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev);
|
||||||
if (r < 0 && r != -ENOENT && ret == 0)
|
if (r < 0 && r != -ENOENT && ret == 0)
|
||||||
ret = r;
|
ret = r;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rm_rf(const char *path, RemoveFlags flags) {
|
int rm_rf(const char *path, RemoveFlags flags) {
|
||||||
int fd, r;
|
int fd, r;
|
||||||
struct statfs s;
|
|
||||||
|
|
||||||
assert(path);
|
assert(path);
|
||||||
|
|
||||||
@ -284,9 +299,10 @@ int rm_rf(const char *path, RemoveFlags flags) {
|
|||||||
if (FLAGS_SET(flags, REMOVE_ROOT)) {
|
if (FLAGS_SET(flags, REMOVE_ROOT)) {
|
||||||
|
|
||||||
if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
|
if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
|
||||||
|
struct statfs s;
|
||||||
|
|
||||||
if (statfs(path, &s) < 0)
|
if (statfs(path, &s) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
if (is_physical_fs(&s))
|
if (is_physical_fs(&s))
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
|
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
|
||||||
"Attempted to remove files from a disk file system under \"%s\", refusing.",
|
"Attempted to remove files from a disk file system under \"%s\", refusing.",
|
||||||
@ -314,3 +330,22 @@ int rm_rf(const char *path, RemoveFlags flags) {
|
|||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int rm_rf_child(int fd, const char *name, RemoveFlags flags) {
|
||||||
|
|
||||||
|
/* Removes one specific child of the specified directory */
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
return -EBADF;
|
||||||
|
|
||||||
|
if (!filename_is_valid(name))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if ((flags & (REMOVE_ROOT|REMOVE_MISSING_OK)) != 0) /* Doesn't really make sense here, we are not supposed to remove 'fd' anyway */
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return rm_rf_children_inner(fd, name, -1, flags, NULL);
|
||||||
|
}
|
||||||
|
|||||||
@ -23,7 +23,8 @@ int fstatat_harder(int dfd,
|
|||||||
int fstatat_flags,
|
int fstatat_flags,
|
||||||
RemoveFlags remove_flags);
|
RemoveFlags remove_flags);
|
||||||
|
|
||||||
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
|
int rm_rf_children(int fd, RemoveFlags flags, const struct stat *root_dev);
|
||||||
|
int rm_rf_child(int fd, const char *name, RemoveFlags flags);
|
||||||
int rm_rf(const char *path, RemoveFlags flags);
|
int rm_rf(const char *path, RemoveFlags flags);
|
||||||
|
|
||||||
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
|
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
|
||||||
|
|||||||
@ -25,6 +25,7 @@ TSS2_RC (*sym_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext, ESYS_TR primaryHand
|
|||||||
void (*sym_Esys_Finalize)(ESYS_CONTEXT **context) = NULL;
|
void (*sym_Esys_Finalize)(ESYS_CONTEXT **context) = NULL;
|
||||||
TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle) = NULL;
|
TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle) = NULL;
|
||||||
void (*sym_Esys_Free)(void *ptr) = NULL;
|
void (*sym_Esys_Free)(void *ptr) = NULL;
|
||||||
|
TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData);
|
||||||
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_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_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_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;
|
||||||
@ -51,6 +52,7 @@ int dlopen_tpm2(void) {
|
|||||||
DLSYM_ARG(Esys_Finalize),
|
DLSYM_ARG(Esys_Finalize),
|
||||||
DLSYM_ARG(Esys_FlushContext),
|
DLSYM_ARG(Esys_FlushContext),
|
||||||
DLSYM_ARG(Esys_Free),
|
DLSYM_ARG(Esys_Free),
|
||||||
|
DLSYM_ARG(Esys_GetCapability),
|
||||||
DLSYM_ARG(Esys_GetRandom),
|
DLSYM_ARG(Esys_GetRandom),
|
||||||
DLSYM_ARG(Esys_Initialize),
|
DLSYM_ARG(Esys_Initialize),
|
||||||
DLSYM_ARG(Esys_Load),
|
DLSYM_ARG(Esys_Load),
|
||||||
@ -310,11 +312,93 @@ static int tpm2_make_primary(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tpm2_get_best_pcr_bank(
|
||||||
|
ESYS_CONTEXT *c,
|
||||||
|
TPMI_ALG_HASH *ret) {
|
||||||
|
|
||||||
|
_cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *pcap = NULL;
|
||||||
|
TPMI_ALG_HASH hash = TPM2_ALG_SHA1;
|
||||||
|
bool found = false;
|
||||||
|
TPMI_YES_NO more;
|
||||||
|
TSS2_RC rc;
|
||||||
|
|
||||||
|
rc = sym_Esys_GetCapability(
|
||||||
|
c,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
TPM2_CAP_PCRS,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
&more,
|
||||||
|
&pcap);
|
||||||
|
if (rc != TSS2_RC_SUCCESS)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||||
|
"Failed to determine TPM2 PCR bank capabilities: %s", sym_Tss2_RC_Decode(rc));
|
||||||
|
|
||||||
|
assert(pcap->capability == TPM2_CAP_PCRS);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < pcap->data.assignedPCR.count; i++) {
|
||||||
|
bool valid = true;
|
||||||
|
|
||||||
|
/* As per
|
||||||
|
* https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf a
|
||||||
|
* TPM2 on a Client PC must have at least 24 PCRs. If this TPM has less, just skip over
|
||||||
|
* it. */
|
||||||
|
if (pcap->data.assignedPCR.pcrSelections[i].sizeofSelect < TPM2_PCRS_MAX/8) {
|
||||||
|
log_debug("Skipping TPM2 PCR bank %s with fewer than 24 PCRs.",
|
||||||
|
strna(tpm2_pcr_bank_to_string(pcap->data.assignedPCR.pcrSelections[i].hash)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_cc(TPM2_PCRS_MAX % 8 == 0);
|
||||||
|
|
||||||
|
/* It's not enought to check how many PCRs there are, we also need to check that the 24 are
|
||||||
|
* enabled for this bank. Otherwise this TPM doesn't qualify. */
|
||||||
|
for (size_t j = 0; j < TPM2_PCRS_MAX/8; j++)
|
||||||
|
if (pcap->data.assignedPCR.pcrSelections[i].pcrSelect[j] != 0xFF) {
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
log_debug("TPM2 PCR bank %s has fewer than 24 PCR bits enabled, ignoring.",
|
||||||
|
strna(tpm2_pcr_bank_to_string(pcap->data.assignedPCR.pcrSelections[i].hash)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA256) {
|
||||||
|
hash = TPM2_ALG_SHA256;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA1)
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static int tpm2_make_pcr_session(
|
static int tpm2_make_pcr_session(
|
||||||
ESYS_CONTEXT *c,
|
ESYS_CONTEXT *c,
|
||||||
uint32_t pcr_mask,
|
uint32_t pcr_mask,
|
||||||
|
uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
|
||||||
ESYS_TR *ret_session,
|
ESYS_TR *ret_session,
|
||||||
TPM2B_DIGEST **ret_policy_digest) {
|
TPM2B_DIGEST **ret_policy_digest,
|
||||||
|
TPMI_ALG_HASH *ret_pcr_bank) {
|
||||||
|
|
||||||
static const TPMT_SYM_DEF symmetric = {
|
static const TPMT_SYM_DEF symmetric = {
|
||||||
.algorithm = TPM2_ALG_AES,
|
.algorithm = TPM2_ALG_AES,
|
||||||
@ -327,7 +411,7 @@ static int tpm2_make_pcr_session(
|
|||||||
};
|
};
|
||||||
TPML_PCR_SELECTION pcr_selection = {
|
TPML_PCR_SELECTION pcr_selection = {
|
||||||
.count = 1,
|
.count = 1,
|
||||||
.pcrSelections[0].hash = TPM2_ALG_SHA256,
|
.pcrSelections[0].hash = TPM2_ALG_SHA256, /* overriden below, depending on TPM2 capabilities */
|
||||||
.pcrSelections[0].sizeofSelect = 3,
|
.pcrSelections[0].sizeofSelect = 3,
|
||||||
.pcrSelections[0].pcrSelect[0] = pcr_mask & 0xFF,
|
.pcrSelections[0].pcrSelect[0] = pcr_mask & 0xFF,
|
||||||
.pcrSelections[0].pcrSelect[1] = (pcr_mask >> 8) & 0xFF,
|
.pcrSelections[0].pcrSelect[1] = (pcr_mask >> 8) & 0xFF,
|
||||||
@ -342,6 +426,16 @@ static int tpm2_make_pcr_session(
|
|||||||
|
|
||||||
log_debug("Starting authentication 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 (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
rc = sym_Esys_StartAuthSession(
|
rc = sym_Esys_StartAuthSession(
|
||||||
c,
|
c,
|
||||||
ESYS_TR_NONE,
|
ESYS_TR_NONE,
|
||||||
@ -412,6 +506,9 @@ static int tpm2_make_pcr_session(
|
|||||||
if (ret_policy_digest)
|
if (ret_policy_digest)
|
||||||
*ret_policy_digest = TAKE_PTR(policy_digest);
|
*ret_policy_digest = TAKE_PTR(policy_digest);
|
||||||
|
|
||||||
|
if (ret_pcr_bank)
|
||||||
|
*ret_pcr_bank = pcr_selection.pcrSelections[0].hash;
|
||||||
|
|
||||||
r = 0;
|
r = 0;
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
@ -427,7 +524,8 @@ int tpm2_seal(
|
|||||||
void **ret_blob,
|
void **ret_blob,
|
||||||
size_t *ret_blob_size,
|
size_t *ret_blob_size,
|
||||||
void **ret_pcr_hash,
|
void **ret_pcr_hash,
|
||||||
size_t *ret_pcr_hash_size) {
|
size_t *ret_pcr_hash_size,
|
||||||
|
uint16_t *ret_pcr_bank) {
|
||||||
|
|
||||||
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
|
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
|
||||||
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
|
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
|
||||||
@ -439,6 +537,7 @@ int tpm2_seal(
|
|||||||
TPM2B_SENSITIVE_CREATE hmac_sensitive;
|
TPM2B_SENSITIVE_CREATE hmac_sensitive;
|
||||||
ESYS_TR primary = ESYS_TR_NONE;
|
ESYS_TR primary = ESYS_TR_NONE;
|
||||||
TPM2B_PUBLIC hmac_template;
|
TPM2B_PUBLIC hmac_template;
|
||||||
|
TPMI_ALG_HASH pcr_bank;
|
||||||
size_t k, blob_size;
|
size_t k, blob_size;
|
||||||
usec_t start;
|
usec_t start;
|
||||||
TSS2_RC rc;
|
TSS2_RC rc;
|
||||||
@ -450,6 +549,7 @@ int tpm2_seal(
|
|||||||
assert(ret_blob_size);
|
assert(ret_blob_size);
|
||||||
assert(ret_pcr_hash);
|
assert(ret_pcr_hash);
|
||||||
assert(ret_pcr_hash_size);
|
assert(ret_pcr_hash_size);
|
||||||
|
assert(ret_pcr_bank);
|
||||||
|
|
||||||
assert(pcr_mask < (UINT32_C(1) << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
|
assert(pcr_mask < (UINT32_C(1) << TPM2_PCRS_MAX)); /* Support 24 PCR banks */
|
||||||
|
|
||||||
@ -478,7 +578,7 @@ int tpm2_seal(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = tpm2_make_pcr_session(c.esys_context, pcr_mask, NULL, &policy_digest);
|
r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, NULL, &policy_digest, &pcr_bank);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
@ -600,6 +700,7 @@ int tpm2_seal(
|
|||||||
*ret_blob_size = blob_size;
|
*ret_blob_size = blob_size;
|
||||||
*ret_pcr_hash = TAKE_PTR(hash);
|
*ret_pcr_hash = TAKE_PTR(hash);
|
||||||
*ret_pcr_hash_size = policy_digest->size;
|
*ret_pcr_hash_size = policy_digest->size;
|
||||||
|
*ret_pcr_bank = pcr_bank;
|
||||||
|
|
||||||
r = 0;
|
r = 0;
|
||||||
|
|
||||||
@ -611,6 +712,7 @@ finish:
|
|||||||
int tpm2_unseal(
|
int tpm2_unseal(
|
||||||
const char *device,
|
const char *device,
|
||||||
uint32_t pcr_mask,
|
uint32_t pcr_mask,
|
||||||
|
uint16_t pcr_bank,
|
||||||
const void *blob,
|
const void *blob,
|
||||||
size_t blob_size,
|
size_t blob_size,
|
||||||
const void *known_policy_hash,
|
const void *known_policy_hash,
|
||||||
@ -670,7 +772,7 @@ int tpm2_unseal(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = tpm2_make_pcr_session(c.esys_context, pcr_mask, &session, &policy_digest);
|
r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, &session, &policy_digest, NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
@ -909,6 +1011,7 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
|
|||||||
int tpm2_make_luks2_json(
|
int tpm2_make_luks2_json(
|
||||||
int keyslot,
|
int keyslot,
|
||||||
uint32_t pcr_mask,
|
uint32_t pcr_mask,
|
||||||
|
uint16_t pcr_bank,
|
||||||
const void *blob,
|
const void *blob,
|
||||||
size_t blob_size,
|
size_t blob_size,
|
||||||
const void *policy_hash,
|
const void *policy_hash,
|
||||||
@ -951,6 +1054,7 @@ int tpm2_make_luks2_json(
|
|||||||
JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
|
JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
|
||||||
JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_BASE64(blob, blob_size)),
|
JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_BASE64(blob, blob_size)),
|
||||||
JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(a)),
|
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("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size))));
|
JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size))));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -960,3 +1064,36 @@ int tpm2_make_luks2_json(
|
|||||||
|
|
||||||
return keyslot;
|
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) {
|
||||||
|
/* 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)
|
||||||
|
return "sha1";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tpm2_pcr_bank_from_string(const char *bank) {
|
||||||
|
if (streq_ptr(bank, "sha256"))
|
||||||
|
return TPM2_ALG_SHA256;
|
||||||
|
if (streq_ptr(bank, "sha1"))
|
||||||
|
return TPM2_ALG_SHA1;
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ extern TSS2_RC (*sym_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext, ESYS_TR prim
|
|||||||
extern void (*sym_Esys_Finalize)(ESYS_CONTEXT **context);
|
extern void (*sym_Esys_Finalize)(ESYS_CONTEXT **context);
|
||||||
extern TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle);
|
extern TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle);
|
||||||
extern void (*sym_Esys_Free)(void *ptr);
|
extern void (*sym_Esys_Free)(void *ptr);
|
||||||
|
extern TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData);
|
||||||
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_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_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_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);
|
||||||
@ -33,8 +34,8 @@ extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], siz
|
|||||||
|
|
||||||
int dlopen_tpm2(void);
|
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);
|
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, 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_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);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -43,13 +44,17 @@ int tpm2_find_device_auto(int log_level, char **ret);
|
|||||||
|
|
||||||
int tpm2_parse_pcrs(const char *s, uint32_t *ret);
|
int tpm2_parse_pcrs(const char *s, uint32_t *ret);
|
||||||
|
|
||||||
int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, 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, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret);
|
||||||
|
|
||||||
#define TPM2_PCRS_MAX 24
|
#define TPM2_PCRS_MAX 24
|
||||||
|
|
||||||
/* Default to PCR 7 only */
|
/* Default to PCR 7 only */
|
||||||
#define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7)
|
#define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7)
|
||||||
|
|
||||||
|
int tpm2_pcr_bank_supported(uint16_t bank);
|
||||||
|
const char *tpm2_pcr_bank_to_string(uint16_t bank);
|
||||||
|
int tpm2_pcr_bank_from_string(const char *bank);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t search_pcr_mask;
|
uint32_t search_pcr_mask;
|
||||||
const char *device;
|
const char *device;
|
||||||
|
|||||||
@ -250,6 +250,8 @@ tests += [
|
|||||||
|
|
||||||
[['src/test/test-sysctl-util.c']],
|
[['src/test/test-sysctl-util.c']],
|
||||||
|
|
||||||
|
[['src/test/test-import-util.c']],
|
||||||
|
|
||||||
[['src/test/test-user-record.c']],
|
[['src/test/test-user-record.c']],
|
||||||
|
|
||||||
[['src/test/test-user-util.c']],
|
[['src/test/test-user-util.c']],
|
||||||
|
|||||||
72
src/test/test-import-util.c
Normal file
72
src/test/test-import-util.c
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "import-util.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
#include "tests.h"
|
||||||
|
|
||||||
|
static void test_import_url_last_component_one(const char *input, const char *output, int ret) {
|
||||||
|
_cleanup_free_ char *s = NULL;
|
||||||
|
|
||||||
|
assert_se(import_url_last_component(input, &s) == ret);
|
||||||
|
assert_se(streq_ptr(output, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_import_url_last_component(void) {
|
||||||
|
test_import_url_last_component_one("https://foobar/waldo/quux", "quux", 0);
|
||||||
|
test_import_url_last_component_one("https://foobar/waldo/quux/", "quux", 0);
|
||||||
|
test_import_url_last_component_one("https://foobar/waldo/", "waldo", 0);
|
||||||
|
test_import_url_last_component_one("https://foobar/", NULL, -EADDRNOTAVAIL);
|
||||||
|
test_import_url_last_component_one("https://foobar", NULL, -EADDRNOTAVAIL);
|
||||||
|
test_import_url_last_component_one("https://foobar/waldo/quux?foo=bar", "quux", 0);
|
||||||
|
test_import_url_last_component_one("https://foobar/waldo/quux/?foo=bar", "quux", 0);
|
||||||
|
test_import_url_last_component_one("https://foobar/waldo/quux/?foo=bar#piep", "quux", 0);
|
||||||
|
test_import_url_last_component_one("https://foobar/waldo/quux/#piep", "quux", 0);
|
||||||
|
test_import_url_last_component_one("https://foobar/waldo/quux#piep", "quux", 0);
|
||||||
|
test_import_url_last_component_one("https://", NULL, -EINVAL);
|
||||||
|
test_import_url_last_component_one("", NULL, -EINVAL);
|
||||||
|
test_import_url_last_component_one(":", NULL, -EINVAL);
|
||||||
|
test_import_url_last_component_one(":/", NULL, -EINVAL);
|
||||||
|
test_import_url_last_component_one("x:/", NULL, -EINVAL);
|
||||||
|
test_import_url_last_component_one("x:y", NULL, -EADDRNOTAVAIL);
|
||||||
|
test_import_url_last_component_one("x:y/z", "z", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_import_url_change_suffix_one(const char *input, size_t n, const char *suffix, const char *output, int ret) {
|
||||||
|
_cleanup_free_ char *s = NULL;
|
||||||
|
|
||||||
|
assert_se(import_url_change_suffix(input, n, suffix, &s) == ret);
|
||||||
|
assert_se(streq_ptr(output, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_import_url_change_suffix(void) {
|
||||||
|
test_import_url_change_suffix_one("https://foobar/waldo/quux", 1, "wuff", "https://foobar/waldo/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("https://foobar/waldo/quux/", 1, "wuff", "https://foobar/waldo/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("https://foobar/waldo/quux///?mief", 1, "wuff", "https://foobar/waldo/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("https://foobar/waldo/quux///?mief#opopo", 1, "wuff", "https://foobar/waldo/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("https://foobar/waldo/quux/quff", 2, "wuff", "https://foobar/waldo/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("https://foobar/waldo/quux/quff/", 2, "wuff", "https://foobar/waldo/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("https://foobar/waldo/quux/quff", 0, "wuff", "https://foobar/waldo/quux/quff/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("https://foobar/waldo/quux/quff?aa?bb##4", 0, "wuff", "https://foobar/waldo/quux/quff/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("https://", 0, "wuff", NULL, -EINVAL);
|
||||||
|
test_import_url_change_suffix_one("", 0, "wuff", NULL, -EINVAL);
|
||||||
|
test_import_url_change_suffix_one(":", 0, "wuff", NULL, -EINVAL);
|
||||||
|
test_import_url_change_suffix_one(":/", 0, "wuff", NULL, -EINVAL);
|
||||||
|
test_import_url_change_suffix_one("x:/", 0, "wuff", NULL, -EINVAL);
|
||||||
|
test_import_url_change_suffix_one("x:y", 0, "wuff", "x:y/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("x:y/z", 0, "wuff", "x:y/z/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("x:y/z/", 0, "wuff", "x:y/z/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("x:y/z/", 1, "wuff", "x:y/wuff", 0);
|
||||||
|
test_import_url_change_suffix_one("x:y/z/", 2, "wuff", "x:y/wuff", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
test_setup_logging(LOG_INFO);
|
||||||
|
|
||||||
|
test_import_url_last_component();
|
||||||
|
test_import_url_change_suffix();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@ -89,7 +89,7 @@ static void test_keymaps(void) {
|
|||||||
|
|
||||||
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
|
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
|
||||||
static void dump_special_glyphs(void) {
|
static void dump_special_glyphs(void) {
|
||||||
assert_cc(SPECIAL_GLYPH_TOUCH + 1 == _SPECIAL_GLYPH_MAX);
|
assert_cc(SPECIAL_GLYPH_SPARKLES + 1 == _SPECIAL_GLYPH_MAX);
|
||||||
|
|
||||||
log_info("/* %s */", __func__);
|
log_info("/* %s */", __func__);
|
||||||
|
|
||||||
@ -120,6 +120,9 @@ static void dump_special_glyphs(void) {
|
|||||||
dump_glyph(SPECIAL_GLYPH_DEPRESSED_SMILEY);
|
dump_glyph(SPECIAL_GLYPH_DEPRESSED_SMILEY);
|
||||||
dump_glyph(SPECIAL_GLYPH_LOCK_AND_KEY);
|
dump_glyph(SPECIAL_GLYPH_LOCK_AND_KEY);
|
||||||
dump_glyph(SPECIAL_GLYPH_TOUCH);
|
dump_glyph(SPECIAL_GLYPH_TOUCH);
|
||||||
|
dump_glyph(SPECIAL_GLYPH_RECYCLING);
|
||||||
|
dump_glyph(SPECIAL_GLYPH_DOWNLOAD);
|
||||||
|
dump_glyph(SPECIAL_GLYPH_SPARKLES);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user