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 char *arg_tpm2_public_key = NULL;
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);
{ "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);
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);
/* 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);
}
}
- _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;
return log_error_errno(r, "Could not get hash mask: %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;
- }
-
TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
r = tpm2_calculate_sealing_policy(
arg_tpm2_hash_pcr_values,
if (r < 0)
return log_error_errno(r, "Could not calculate sealing policy digest: %m");
- r = tpm2_seal(tpm2_context,
- /* seal_key_handle= */ 0,
- &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");
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
}
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)
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"
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,
{ "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 },
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:
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 != -ENOENT)
log_warning_errno(r, "Search for pcrlock.json failed, assuming it does not exist: %m");
} else
- log_info("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock);
+ log_debug("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock);
}
if (auto_public_key_pcr_mask) {
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();