static EVP_PKEY *arg_private_key = NULL;
static X509 *arg_certificate = NULL;
static char *arg_tpm2_device = NULL;
+static uint32_t arg_tpm2_seal_key_handle = 0;
+static char *arg_tpm2_device_key = NULL;
static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL;
static size_t arg_tpm2_n_hash_pcr_values = 0;
-static bool arg_tpm2_hash_pcr_values_use_default = true;
static char *arg_tpm2_public_key = NULL;
static uint32_t arg_tpm2_public_key_pcr_mask = 0;
-static bool arg_tpm2_public_key_pcr_mask_use_default = true;
+static char *arg_tpm2_pcrlock = NULL;
static bool arg_split = false;
static GptPartitionType *arg_filter_partitions = NULL;
static size_t arg_n_filter_partitions = 0;
STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep);
STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep);
STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_copy_from, strv_freep);
int read_only;
int growfs;
- uint8_t *roothash;
- size_t roothash_size;
+ struct iovec roothash;
char *split_name_format;
char *split_path;
strv_free(p->subvolumes);
free(p->verity_match_key);
- free(p->roothash);
+ iovec_done(&p->roothash);
free(p->split_name_format);
unlink_and_free(p->split_path);
{ "Partition", "Priority", config_parse_int32, 0, &p->priority },
{ "Partition", "Weight", config_parse_weight, 0, &p->weight },
{ "Partition", "PaddingWeight", config_parse_weight, 0, &p->padding_weight },
- { "Partition", "SizeMinBytes", config_parse_size4096, 1, &p->size_min },
- { "Partition", "SizeMaxBytes", config_parse_size4096, -1, &p->size_max },
- { "Partition", "PaddingMinBytes", config_parse_size4096, 1, &p->padding_min },
- { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max },
+ { "Partition", "SizeMinBytes", config_parse_size4096, -1, &p->size_min },
+ { "Partition", "SizeMaxBytes", config_parse_size4096, 1, &p->size_max },
+ { "Partition", "PaddingMinBytes", config_parse_size4096, -1, &p->padding_min },
+ { "Partition", "PaddingMaxBytes", config_parse_size4096, 1, &p->padding_max },
{ "Partition", "FactoryReset", config_parse_bool, 0, &p->factory_reset },
{ "Partition", "CopyBlocks", config_parse_copy_blocks, 0, p },
{ "Partition", "Format", config_parse_fstype, 0, &p->format },
if (q) {
if (q->priority != p->priority)
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
- "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s",
- p->priority, q->priority, p->verity_match_key);
+ "Priority mismatch (%i != %i) for verity sibling partitions with VerityMatchKey=%s",
+ p->priority, q->priority, p->verity_match_key);
p->siblings[mode] = q;
}
return r;
if (fstat(context->backing_fd, &st) < 0)
- return log_error_errno(r, "Failed to stat %s: %m", context->node);
+ return log_error_errno(errno, "Failed to stat %s: %m", context->node);
/* Auto-detect sector size if not specified. */
r = probe_sector_size_prefer_ioctl(context->backing_fd, &ssz);
if (p->verity != VERITY_OFF) {
Partition *hp = p->verity == VERITY_HASH ? p : p->siblings[VERITY_HASH];
- rh = hp->roothash ? hexmem(hp->roothash, hp->roothash_size) : strdup("TBD");
+ rh = iovec_is_set(&hp->roothash) ? hexmem(hp->roothash.iov_base, hp->roothash.iov_len) : strdup("TBD");
if (!rh)
return log_oom();
}
static bool context_has_roothash(Context *context) {
LIST_FOREACH(partitions, p, context->partitions)
- if (p->roothash)
+ if (iovec_is_set(&p->roothash))
return true;
return false;
errno = 0;
r = blkid_do_probe(probe);
if (r < 0)
- return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe for file systems.");
+ return log_error_errno(errno_or_else(EIO), "Failed to probe for file systems.");
if (r > 0)
break;
errno = 0;
if (blkid_do_wipe(probe, false) < 0)
- return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to wipe file system signature.");
+ return log_error_errno(errno_or_else(EIO), "Failed to wipe file system signature.");
}
return 0;
if (ftruncate(fd, size) < 0)
return log_error_errno(errno, "Failed to truncate temporary file to %s: %m",
- FORMAT_BYTES(size));
+ FORMAT_BYTES(size));
t->fd = TAKE_FD(fd);
t->path = TAKE_PTR(temp);
};
if (!need_path) {
- if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
+ if (lseek(whole_fd, p->offset, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to partition offset: %m");
t->whole_fd = whole_fd;
} else if (t->fd >= 0) {
struct stat st;
- if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
+ if (lseek(whole_fd, p->offset, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to partition offset: %m");
- if (lseek(t->fd, 0, SEEK_SET) == (off_t) -1)
+ if (lseek(t->fd, 0, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to start of temporary file: %m");
if (fstat(t->fd, &st) < 0)
/* Weird cryptsetup requirement which requires the header file to be the size of at least one
* sector. */
- r = ftruncate(fileno(h), luks_params.sector_size);
- if (r < 0)
- return log_error_errno(r, "Failed to grow temporary LUKS header file: %m");
+ if (ftruncate(fileno(h), luks_params.sector_size) < 0)
+ return log_error_errno(errno, "Failed to grow temporary LUKS header file: %m");
} else {
if (asprintf(&dm_name, "luks-repart-%08" PRIx64, random_u64()) < 0)
return log_oom();
return log_error_errno(r, "Failed to set data offset: %m");
}
- r = sym_crypt_format(cd,
- CRYPT_LUKS2,
- "aes",
- "xts-plain64",
- SD_ID128_TO_UUID_STRING(p->luks_uuid),
- NULL,
- VOLUME_KEY_SIZE,
- &luks_params);
+ r = sym_crypt_format(
+ cd,
+ CRYPT_LUKS2,
+ "aes",
+ "xts-plain64",
+ SD_ID128_TO_UUID_STRING(p->luks_uuid),
+ NULL,
+ VOLUME_KEY_SIZE,
+ &luks_params);
if (r < 0)
return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
size_t secret_size, blob_size, pubkey_size = 0, srk_buf_size = 0;
ssize_t base64_encoded_size;
int keyslot;
+ TPM2Flags flags = 0;
if (arg_tpm2_public_key_pcr_mask != 0) {
r = tpm2_load_pcr_public_key(arg_tpm2_public_key, &pubkey, &pubkey_size);
if (r < 0) {
if (arg_tpm2_public_key || r != -ENOENT)
- return log_error_errno(r, "Failed read TPM PCR public key: %m");
+ return log_error_errno(r, "Failed to read TPM PCR public key: %m");
log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
arg_tpm2_public_key_pcr_mask = 0;
}
}
- _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
- r = tpm2_context_new(arg_tpm2_device, &tpm2_context);
- if (r < 0)
- return log_error_errno(r, "Failed to create TPM2 context: %m");
-
TPM2B_PUBLIC public;
if (pubkey) {
r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public);
return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
}
- r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values);
- if (r < 0)
- return log_error_errno(r, "Could not read pcr values: %m");
+ _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {};
+ if (arg_tpm2_pcrlock) {
+ r = tpm2_pcrlock_policy_load(arg_tpm2_pcrlock, &pcrlock_policy);
+ if (r < 0)
+ return r;
+
+ flags |= TPM2_FLAGS_USE_PCRLOCK;
+ }
+
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL;
+ TPM2B_PUBLIC device_key_public = {};
+ if (arg_tpm2_device_key) {
+ r = tpm2_load_public_key_file(arg_tpm2_device_key, &device_key_public);
+ if (r < 0)
+ return r;
+
+ if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Must provide all PCR values when using TPM2 device key.");
+ } else {
+ r = tpm2_context_new(arg_tpm2_device, &tpm2_context);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create TPM2 context: %m");
+
+ if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values)) {
+ r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values);
+ if (r < 0)
+ return log_error_errno(r, "Could not read pcr values: %m");
+ }
+ }
uint16_t hash_pcr_bank = 0;
uint32_t hash_pcr_mask = 0;
}
TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
- r = tpm2_calculate_sealing_policy(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, &public, /* use_pin= */ false, &policy);
+ r = tpm2_calculate_sealing_policy(
+ arg_tpm2_hash_pcr_values,
+ arg_tpm2_n_hash_pcr_values,
+ pubkey ? &public : NULL,
+ /* use_pin= */ false,
+ arg_tpm2_pcrlock ? &pcrlock_policy : NULL,
+ &policy);
if (r < 0)
return log_error_errno(r, "Could not calculate sealing policy digest: %m");
- r = tpm2_seal(tpm2_context,
- &policy,
- /* pin= */ NULL,
- &secret, &secret_size,
- &blob, &blob_size,
- /* ret_primary_alg= */ NULL,
- &srk_buf, &srk_buf_size);
+ if (arg_tpm2_device_key)
+ r = tpm2_calculate_seal(
+ arg_tpm2_seal_key_handle,
+ &device_key_public,
+ /* attributes= */ NULL,
+ /* secret= */ NULL, /* secret_size= */ 0,
+ &policy,
+ /* pin= */ NULL,
+ &secret, &secret_size,
+ &blob, &blob_size,
+ &srk_buf, &srk_buf_size);
+ else
+ r = tpm2_seal(tpm2_context,
+ arg_tpm2_seal_key_handle,
+ &policy,
+ /* pin= */ NULL,
+ &secret, &secret_size,
+ &blob, &blob_size,
+ /* ret_primary_alg= */ NULL,
+ &srk_buf, &srk_buf_size);
if (r < 0)
return log_error_errno(r, "Failed to seal to TPM2: %m");
keyslot = sym_crypt_keyslot_add_by_volume_key(
cd,
CRYPT_ANY_SLOT,
- NULL,
- VOLUME_KEY_SIZE,
+ /* volume_key= */ NULL,
+ /* volume_key_size= */ VOLUME_KEY_SIZE,
base64_encoded,
base64_encoded_size);
if (keyslot < 0)
policy.buffer, policy.size,
NULL, 0, /* no salt because tpm2_seal has no pin */
srk_buf, srk_buf_size,
- 0,
+ flags,
&v);
if (r < 0)
return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m");
_cleanup_(partition_target_freep) PartitionTarget *t = NULL;
_cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
_cleanup_free_ char *hint = NULL;
- _cleanup_free_ uint8_t *rh = NULL;
- size_t rhs;
int r;
assert(context);
r = sym_crypt_get_volume_key_size(cd);
if (r < 0)
return log_error_errno(r, "Failed to determine verity root hash size of partition %s: %m", strna(hint));
- rhs = (size_t) r;
- rh = malloc(rhs);
- if (!rh)
+ _cleanup_(iovec_done) struct iovec rh = {
+ .iov_base = malloc(r),
+ .iov_len = r,
+ };
+ if (!rh.iov_base)
return log_oom();
- r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh, &rhs, NULL, 0);
+ r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh.iov_base, &rh.iov_len, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to get verity root hash of partition %s: %m", strna(hint));
- assert(rhs >= sizeof(sd_id128_t) * 2);
+ assert(rh.iov_len >= sizeof(sd_id128_t) * 2);
if (!dp->new_uuid_is_set) {
- memcpy_safe(dp->new_uuid.bytes, rh, sizeof(sd_id128_t));
+ memcpy_safe(dp->new_uuid.bytes, rh.iov_base, sizeof(sd_id128_t));
dp->new_uuid_is_set = true;
}
if (!p->new_uuid_is_set) {
- memcpy_safe(p->new_uuid.bytes, rh + rhs - sizeof(sd_id128_t), sizeof(sd_id128_t));
+ memcpy_safe(p->new_uuid.bytes, (uint8_t*) rh.iov_base + (rh.iov_len - sizeof(sd_id128_t)), sizeof(sd_id128_t));
p->new_uuid_is_set = true;
}
- p->roothash = TAKE_PTR(rh);
- p->roothash_size = rhs;
+ p->roothash = TAKE_STRUCT(rh);
return 0;
#else
}
static int sign_verity_roothash(
- const uint8_t *roothash,
- size_t roothash_size,
- uint8_t **ret_signature,
- size_t *ret_signature_size) {
+ const struct iovec *roothash,
+ struct iovec *ret_signature) {
#if HAVE_OPENSSL
_cleanup_(BIO_freep) BIO *rb = NULL;
int sigsz;
assert(roothash);
- assert(roothash_size > 0);
+ assert(iovec_is_set(roothash));
assert(ret_signature);
- assert(ret_signature_size);
- hex = hexmem(roothash, roothash_size);
+ hex = hexmem(roothash->iov_base, roothash->iov_len);
if (!hex)
return log_oom();
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s",
ERR_error_string(ERR_get_error(), NULL));
- *ret_signature = TAKE_PTR(sig);
- *ret_signature_size = sigsz;
+ ret_signature->iov_base = TAKE_PTR(sig);
+ ret_signature->iov_len = sigsz;
return 0;
#else
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot setup verity signature: %m");
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot setup verity signature: %m");
#endif
}
static int partition_format_verity_sig(Context *context, Partition *p) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
- _cleanup_free_ uint8_t *sig = NULL;
+ _cleanup_(iovec_done) struct iovec sig = {};
_cleanup_free_ char *text = NULL, *hint = NULL;
Partition *hp;
uint8_t fp[X509_FINGERPRINT_SIZE];
- size_t sigsz = 0; /* avoid false maybe-uninitialized warning */
int whole_fd, r;
assert(p->verity == VERITY_SIG);
assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
- r = sign_verity_roothash(hp->roothash, hp->roothash_size, &sig, &sigsz);
+ r = sign_verity_roothash(&hp->roothash, &sig);
if (r < 0)
return r;
r = json_build(&v,
JSON_BUILD_OBJECT(
- JSON_BUILD_PAIR("rootHash", JSON_BUILD_HEX(hp->roothash, hp->roothash_size)),
+ JSON_BUILD_PAIR("rootHash", JSON_BUILD_HEX(hp->roothash.iov_base, hp->roothash.iov_len)),
JSON_BUILD_PAIR(
"certificateFingerprint",
JSON_BUILD_HEX(fp, sizeof(fp))
),
- JSON_BUILD_PAIR("signature", JSON_BUILD_BASE64(sig, sigsz))
+ JSON_BUILD_PAIR("signature", JSON_BUILD_IOVEC_BASE64(&sig))
)
);
if (r < 0)
if (r < 0)
return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(p->new_size));
- if (lseek(whole_fd, p->offset, SEEK_SET) == (off_t) -1)
+ if (lseek(whole_fd, p->offset, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to partition %s offset: %m", strna(hint));
r = loop_write(whole_fd, text, p->new_size);
assert(p->copy_blocks_size != UINT64_MAX);
assert(p->new_size >= p->copy_blocks_size + (p->encrypt != ENCRYPT_OFF ? LUKS2_METADATA_KEEP_FREE : 0));
+ usec_t start_timestamp = now(CLOCK_MONOTONIC);
+
r = partition_target_prepare(context, p, p->new_size,
/*need_path=*/ p->encrypt != ENCRYPT_OFF || p->siblings[VERITY_HASH],
&t);
return r;
}
- log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
- p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
+ if (p->copy_blocks_offset == UINT64_MAX)
+ log_info("Copying in '%s' (%s) on block level into future partition %" PRIu64 ".",
+ p->copy_blocks_path, FORMAT_BYTES(p->copy_blocks_size), p->partno);
+ else {
+ log_info("Copying in '%s' @ %" PRIu64 " (%s) on block level into future partition %" PRIu64 ".",
+ p->copy_blocks_path, p->copy_blocks_offset, FORMAT_BYTES(p->copy_blocks_size), p->partno);
- if (p->copy_blocks_offset != UINT64_MAX && lseek(p->copy_blocks_fd, p->copy_blocks_offset, SEEK_SET) < 0)
- return log_error_errno(errno, "Failed to seek to copy blocks offset in %s: %m", p->copy_blocks_path);
+ if (lseek(p->copy_blocks_fd, p->copy_blocks_offset, SEEK_SET) < 0)
+ return log_error_errno(errno, "Failed to seek to copy blocks offset in %s: %m", p->copy_blocks_path);
+ }
r = copy_bytes(p->copy_blocks_fd, partition_target_fd(t), p->copy_blocks_size, COPY_REFLINK);
if (r < 0)
if (r < 0)
return r;
+ usec_t time_spent = usec_sub_unsigned(now(CLOCK_MONOTONIC), start_timestamp);
+ if (time_spent > 250 * USEC_PER_MSEC) /* Show throughput, but not if we spent too little time on it, since it's just noise then */
+ log_info("Block level copying and synchronization of partition %" PRIu64 " complete in %s (%s/s).",
+ p->partno, FORMAT_TIMESPAN(time_spent, 0), FORMAT_BYTES((uint64_t) ((double) p->copy_blocks_size / time_spent * USEC_PER_SEC)));
+ else
+ log_info("Block level copying and synchronization of partition %" PRIu64 " complete in %s.",
+ p->partno, FORMAT_TIMESPAN(time_spent, 0));
+
if (p->siblings[VERITY_HASH] && !partition_type_defer(&p->siblings[VERITY_HASH]->type)) {
r = partition_format_verity_hash(context, p->siblings[VERITY_HASH],
/* node = */ NULL, partition_target_path(t));
rfd = open(root, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
if (rfd < 0)
- return rfd;
+ return -errno;
sfd = chase_and_open(*source, arg_copy_source, CHASE_PREFIX_ROOT, O_PATH|O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL);
if (sfd < 0)
* appear in the host namespace. Hence we fork a child that has its own file system namespace and
* detached mount propagation. */
- r = safe_fork("(sd-copy)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL);
+ r = safe_fork("(sd-copy)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL);
if (r < 0)
return r;
if (r == 0) {
if (r < 0)
return r;
+ /* The mkfs binary we invoked might have removed our temporary file when we're not operating
+ * on a loop device, so let's make sure we open the file again to make sure our file
+ * descriptor points to any potential new file. */
+
+ if (t->fd >= 0 && t->path && !t->loop) {
+ safe_close(t->fd);
+ t->fd = open(t->path, O_RDWR|O_CLOEXEC);
+ if (t->fd < 0)
+ return log_error_errno(errno, "Failed to reopen temporary file: %m");
+ }
+
log_info("Successfully formatted future partition %" PRIu64 ".", p->partno);
/* If we're writing to a loop device, we can now mount the empty filesystem and populate it. */
return 0;
#else
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse X509 certificate.");
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot parse X509 certificate.");
#endif
}
return 0;
#else
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "openssl is not supported, cannot parse private key.");
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot parse private key.");
#endif
}
return 0;
}
- log_info("Applying changes.");
+ log_info("Applying changes to %s.", context->node);
if (context->from_scratch && arg_empty != EMPTY_CREATE) {
/* Erase everything if we operate from scratch, except if the image was just created anyway, and thus is definitely empty. */
if (S_ISREG(st.st_mode))
size = st.st_size;
else if (S_ISBLK(st.st_mode)) {
- if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
- return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
+ r = blockdev_get_device_size(source_fd, &size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine size of block device to copy from: %m");
} else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", opened);
d = loop_device_unref(d);
/* Erase the previous filesystem first. */
- if (ftruncate(fd, 0))
+ if (ftruncate(fd, 0) < 0)
return log_error_errno(errno, "Failed to erase temporary file: %m");
- if (ftruncate(fd, fsz))
+ if (ftruncate(fd, fsz) < 0)
return log_error_errno(errno, "Failed to truncate temporary file to %s: %m", FORMAT_BYTES(fsz));
if (arg_offline <= 0) {
return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
if (fstat(fd, &st) < 0)
- return log_error_errno(r, "Failed to stat temporary file: %m");
+ return log_error_errno(errno, "Failed to stat temporary file: %m");
log_info("Minimal partition size of verity hash partition %s is %s",
strna(hint), FORMAT_BYTES(st.st_size));
" --certificate=PATH PEM certificate to use when generating verity\n"
" roothash signatures\n"
" --tpm2-device=PATH Path to TPM2 device node to use\n"
+ " --tpm2-device-key=PATH\n"
+ " Enroll a TPM2 device using its public key\n"
+ " --tpm2-seal-key-handle=HANDLE\n"
+ " Specify handle of key to use for sealing\n"
" --tpm2-pcrs=PCR1+PCR2+PCR3+…\n"
" TPM2 PCR indexes to use for TPM2 enrollment\n"
" --tpm2-public-key=PATH\n"
" Enroll signed TPM2 PCR policy against PEM public key\n"
" --tpm2-public-key-pcrs=PCR1+PCR2+PCR3+…\n"
" Enroll signed TPM2 PCR policy for specified TPM2 PCRs\n"
+ " --tpm2-pcrlock=PATH\n"
+ " Specify pcrlock policy to lock against\n"
" --seed=UUID 128-bit seed UUID to derive all UUIDs from\n"
" --size=BYTES Grow loopback file to specified size\n"
" --json=pretty|short|off\n"
ARG_PRIVATE_KEY,
ARG_CERTIFICATE,
ARG_TPM2_DEVICE,
+ ARG_TPM2_DEVICE_KEY,
+ ARG_TPM2_SEAL_KEY_HANDLE,
ARG_TPM2_PCRS,
ARG_TPM2_PUBLIC_KEY,
ARG_TPM2_PUBLIC_KEY_PCRS,
+ ARG_TPM2_PCRLOCK,
ARG_SPLIT,
ARG_INCLUDE_PARTITIONS,
ARG_EXCLUDE_PARTITIONS,
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
+ { "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY },
+ { "tpm2-seal-key-handle", required_argument, NULL, ARG_TPM2_SEAL_KEY_HANDLE },
{ "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS },
{ "tpm2-public-key", required_argument, NULL, ARG_TPM2_PUBLIC_KEY },
{ "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS },
+ { "tpm2-pcrlock", required_argument, NULL, ARG_TPM2_PCRLOCK },
{ "split", required_argument, NULL, ARG_SPLIT },
{ "include-partitions", required_argument, NULL, ARG_INCLUDE_PARTITIONS },
{ "exclude-partitions", required_argument, NULL, ARG_EXCLUDE_PARTITIONS },
{}
};
+ bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true;
int c, r;
assert(argc >= 0);
break;
}
+ case ARG_TPM2_DEVICE_KEY:
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_device_key);
+ if (r < 0)
+ return r;
+
+ break;
+
+ case ARG_TPM2_SEAL_KEY_HANDLE:
+ r = safe_atou32_full(optarg, 16, &arg_tpm2_seal_key_handle);
+ if (r < 0)
+ return log_error_errno(r, "Could not parse TPM2 seal key handle index '%s': %m", optarg);
+
+ break;
+
case ARG_TPM2_PCRS:
- arg_tpm2_hash_pcr_values_use_default = false;
+ auto_hash_pcr_values = false;
r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values);
if (r < 0)
return r;
break;
case ARG_TPM2_PUBLIC_KEY_PCRS:
- arg_tpm2_public_key_pcr_mask_use_default = false;
+ auto_public_key_pcr_mask = false;
r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask);
if (r < 0)
return r;
break;
+ case ARG_TPM2_PCRLOCK:
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_pcrlock);
+ if (r < 0)
+ return r;
+
+ auto_pcrlock = false;
+ break;
+
case ARG_SPLIT:
r = parse_boolean_argument("--split=", optarg, NULL);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"A path to an image file must be specified when --split is used.");
- if (arg_tpm2_public_key_pcr_mask_use_default && arg_tpm2_public_key)
+ if (auto_pcrlock) {
+ assert(!arg_tpm2_pcrlock);
+
+ r = tpm2_pcrlock_search_file(NULL, NULL, &arg_tpm2_pcrlock);
+ if (r < 0) {
+ if (r != -ENOENT)
+ log_warning_errno(r, "Search for pcrlock.json failed, assuming it does not exist: %m");
+ } else
+ log_debug("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock);
+ }
+
+ if (auto_public_key_pcr_mask) {
+ assert(arg_tpm2_public_key_pcr_mask == 0);
arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT);
+ }
- if (arg_tpm2_hash_pcr_values_use_default && !GREEDY_REALLOC_APPEND(
- arg_tpm2_hash_pcr_values,
- arg_tpm2_n_hash_pcr_values,
- &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}),
- 1))
- return log_oom();
+ if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 if no pcr policy is specified. */
+ assert(arg_tpm2_n_hash_pcr_values == 0);
+
+ if (!GREEDY_REALLOC_APPEND(
+ arg_tpm2_hash_pcr_values,
+ arg_tpm2_n_hash_pcr_values,
+ &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}),
+ 1))
+ return log_oom();
+ }
if (arg_pretty < 0 && isatty(STDOUT_FILENO))
arg_pretty = true;
assert(loop_device);
- if (ioctl(*fd, BLKGETSIZE64, ¤t_size) < 0)
- return log_error_errno(errno, "Failed to determine size of block device %s: %m", node);
+ r = blockdev_get_device_size(*fd, ¤t_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine size of block device %s: %m", node);
} else {
r = stat_verify_regular(&st);
if (r < 0)
if (!d)
return log_oom();
- r = search_and_access(d, F_OK, arg_root, CONF_PATHS_USR_STRV("systemd/repart/definitions"), &dp);
+ r = search_and_access(d, F_OK, NULL, CONF_PATHS_USR_STRV("systemd/repart/definitions"), &dp);
if (r < 0)
- return log_error_errno(errno, "DDI type '%s' is not defined: %m", arg_make_ddi);
+ return log_error_errno(r, "DDI type '%s' is not defined: %m", arg_make_ddi);
if (strv_consume(&arg_definitions, TAKE_PTR(dp)) < 0)
return log_oom();