+static int _crypt_load_luks1(struct crypt_device *cd, int require_header, int repair)
+{
+ struct luks_phdr hdr;
+ int r;
+
+ r = init_crypto(cd);
+ if (r < 0)
+ return r;
+
+ r = LUKS_read_phdr(&hdr, require_header, repair, cd);
+ if (r < 0)
+ return r;
+
+ if (!cd->type && !(cd->type = strdup(CRYPT_LUKS1)))
+ return -ENOMEM;
+
+ memcpy(&cd->u.luks1.hdr, &hdr, sizeof(hdr));
+
+ return r;
+}
+
+static int _crypt_load_tcrypt(struct crypt_device *cd, struct crypt_params_tcrypt *params)
+{
+ int r;
+
+ r = init_crypto(cd);
+ if (r < 0)
+ return r;
+
+ memcpy(&cd->u.tcrypt.params, params, sizeof(*params));
+
+ r = TCRYPT_read_phdr(cd, &cd->u.tcrypt.hdr, &cd->u.tcrypt.params);
+
+ cd->u.tcrypt.params.passphrase = NULL;
+ cd->u.tcrypt.params.passphrase_size = 0;
+ cd->u.tcrypt.params.keyfiles = NULL;
+ cd->u.tcrypt.params.keyfiles_count = 0;
+
+ if (r < 0)
+ return r;
+
+ if (!cd->type && !(cd->type = strdup(CRYPT_TCRYPT)))
+ return -ENOMEM;
+
+ return r;
+}
+
+static int _crypt_load_verity(struct crypt_device *cd, struct crypt_params_verity *params)
+{
+ int r;
+ size_t sb_offset = 0;
+
+ r = init_crypto(cd);
+ if (r < 0)
+ return r;
+
+ if (params && params->flags & CRYPT_VERITY_NO_HEADER)
+ return -EINVAL;
+
+ if (params)
+ sb_offset = params->hash_area_offset;
+
+ r = VERITY_read_sb(cd, sb_offset, &cd->u.verity.uuid, &cd->u.verity.hdr);
+ if (r < 0)
+ return r;
+
+ if (params)
+ cd->u.verity.hdr.flags = params->flags;
+
+ /* Hash availability checked in sb load */
+ cd->u.verity.root_hash_size = crypt_hash_size(cd->u.verity.hdr.hash_name);
+ if (cd->u.verity.root_hash_size > 4096)
+ return -EINVAL;
+
+ if (!cd->type && !(cd->type = strdup(CRYPT_VERITY)))
+ return -ENOMEM;
+
+ if (params && params->data_device &&
+ (r = crypt_set_data_device(cd, params->data_device)) < 0)
+ return r;
+
+ return r;
+}
+
+static int _init_by_name_crypt(struct crypt_device *cd, const char *name)
+{
+ struct crypt_dm_active_device dmd = {};
+ char cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
+ int key_nums, r;
+
+ r = dm_query_device(cd, name,
+ DM_ACTIVE_DEVICE |
+ DM_ACTIVE_UUID |
+ DM_ACTIVE_CRYPT_CIPHER |
+ DM_ACTIVE_CRYPT_KEYSIZE, &dmd);
+ if (r < 0)
+ goto out;
+
+ if (isPLAIN(cd->type)) {
+ cd->u.plain.uuid = dmd.uuid ? strdup(dmd.uuid) : NULL;
+ cd->u.plain.hdr.hash = NULL; /* no way to get this */
+ cd->u.plain.hdr.offset = dmd.u.crypt.offset;
+ cd->u.plain.hdr.skip = dmd.u.crypt.iv_offset;
+ cd->u.plain.key_size = dmd.u.crypt.vk->keylength;
+
+ r = crypt_parse_name_and_mode(dmd.u.crypt.cipher, cipher, NULL, cipher_mode);
+ if (!r) {
+ cd->u.plain.cipher = strdup(cipher);
+ cd->u.plain.cipher_mode = strdup(cipher_mode);
+ }
+ } else if (isLOOPAES(cd->type)) {
+ cd->u.loopaes.uuid = dmd.uuid ? strdup(dmd.uuid) : NULL;
+ cd->u.loopaes.hdr.offset = dmd.u.crypt.offset;
+
+ r = crypt_parse_name_and_mode(dmd.u.crypt.cipher, cipher,
+ &key_nums, cipher_mode);
+ if (!r) {
+ cd->u.loopaes.cipher = strdup(cipher);
+ cd->u.loopaes.cipher_mode = strdup(cipher_mode);
+ /* version 3 uses last key for IV */
+ if (dmd.u.crypt.vk->keylength % key_nums)
+ key_nums++;
+ cd->u.loopaes.key_size = dmd.u.crypt.vk->keylength / key_nums;
+ }
+ } else if (isLUKS(cd->type)) {
+ if (crypt_metadata_device(cd)) {
+ r = _crypt_load_luks1(cd, 0, 0);
+ if (r < 0) {
+ log_dbg("LUKS device header does not match active device.");
+ free(cd->type);
+ cd->type = NULL;
+ r = 0;
+ goto out;
+ }
+ /* check whether UUIDs match each other */
+ r = crypt_uuid_cmp(dmd.uuid, cd->u.luks1.hdr.uuid);
+ if (r < 0) {
+ log_dbg("LUKS device header uuid: %s mismatches DM returned uuid %s",
+ cd->u.luks1.hdr.uuid, dmd.uuid);
+ free(cd->type);
+ cd->type = NULL;
+ r = 0;
+ goto out;
+ }
+ }
+ } else if (isTCRYPT(cd->type)) {
+ r = TCRYPT_init_by_name(cd, name, &dmd, &cd->device,
+ &cd->u.tcrypt.params, &cd->u.tcrypt.hdr);
+ }
+out:
+ crypt_free_volume_key(dmd.u.crypt.vk);
+ device_free(dmd.data_device);
+ free(CONST_CAST(void*)dmd.u.crypt.cipher);
+ free(CONST_CAST(void*)dmd.uuid);
+ return r;
+}
+
+static int _init_by_name_verity(struct crypt_device *cd, const char *name)
+{
+ struct crypt_params_verity params = {};
+ struct crypt_dm_active_device dmd = {
+ .target = DM_VERITY,
+ .u.verity.vp = ¶ms,
+ };
+ int r;
+
+ r = dm_query_device(cd, name,
+ DM_ACTIVE_DEVICE |
+ DM_ACTIVE_UUID |
+ DM_ACTIVE_VERITY_HASH_DEVICE |
+ DM_ACTIVE_VERITY_PARAMS, &dmd);
+ if (r < 0)
+ goto out;
+
+ if (isVERITY(cd->type)) {
+ cd->u.verity.uuid = dmd.uuid ? strdup(dmd.uuid) : NULL;
+ cd->u.verity.hdr.flags = CRYPT_VERITY_NO_HEADER; //FIXME
+ cd->u.verity.hdr.data_size = params.data_size;
+ cd->u.verity.root_hash_size = dmd.u.verity.root_hash_size;
+ cd->u.verity.root_hash = NULL;
+ cd->u.verity.hdr.hash_name = params.hash_name;
+ cd->u.verity.hdr.data_device = NULL;
+ cd->u.verity.hdr.hash_device = NULL;
+ cd->u.verity.hdr.data_block_size = params.data_block_size;
+ cd->u.verity.hdr.hash_block_size = params.hash_block_size;
+ cd->u.verity.hdr.hash_area_offset = dmd.u.verity.hash_offset;
+ cd->u.verity.hdr.hash_type = params.hash_type;
+ cd->u.verity.hdr.flags = params.flags;
+ cd->u.verity.hdr.salt_size = params.salt_size;
+ cd->u.verity.hdr.salt = params.salt;
+ cd->metadata_device = dmd.u.verity.hash_device;
+ }
+out:
+ device_free(dmd.data_device);
+ free(CONST_CAST(void*)dmd.uuid);
+ return r;
+}
+