+ return dmi.suspended ? 1 : 0;
+}
+
+static int _dm_status_verity_ok(struct crypt_device *cd, const char *name)
+{
+ int r;
+ struct dm_info dmi;
+ char *status_line = NULL;
+
+ r = dm_status_dmi(name, &dmi, DM_VERITY_TARGET, &status_line);
+ if (r < 0 || !status_line) {
+ free(status_line);
+ return r;
+ }
+
+ log_dbg(cd, "Verity volume %s status is %s.", name, status_line ?: "");
+ r = status_line[0] == 'V' ? 1 : 0;
+ free(status_line);
+
+ return r;
+}
+
+int dm_status_verity_ok(struct crypt_device *cd, const char *name)
+{
+ int r;
+
+ if (dm_init_context(cd, DM_VERITY))
+ return -ENOTSUP;
+ r = _dm_status_verity_ok(cd, name);
+ dm_exit_context();
+ return r;
+}
+
+int dm_status_integrity_failures(struct crypt_device *cd, const char *name, uint64_t *count)
+{
+ int r;
+ struct dm_info dmi;
+ char *status_line = NULL;
+
+ if (dm_init_context(cd, DM_INTEGRITY))
+ return -ENOTSUP;
+
+ r = dm_status_dmi(name, &dmi, DM_INTEGRITY_TARGET, &status_line);
+ if (r < 0 || !status_line) {
+ free(status_line);
+ dm_exit_context();
+ return r;
+ }
+
+ log_dbg(cd, "Integrity volume %s failure status is %s.", name, status_line ?: "");
+ *count = strtoull(status_line, NULL, 10);
+ free(status_line);
+ dm_exit_context();
+
+ return 0;
+}
+
+/* FIXME use hex wrapper, user val wrappers for line parsing */
+static int _dm_target_query_crypt(struct crypt_device *cd, uint32_t get_flags,
+ char *params, struct dm_target *tgt,
+ uint32_t *act_flags)
+{
+ uint64_t val64;
+ char *rcipher, *rintegrity, *key_, *rdevice, *endp, buffer[3], *arg, *key_desc;
+ unsigned int i, val;
+ int r;
+ size_t key_size;
+ struct device *data_device = NULL;
+ char *cipher = NULL, *integrity = NULL;
+ struct volume_key *vk = NULL;
+
+ tgt->type = DM_CRYPT;
+ tgt->direction = TARGET_QUERY;
+ tgt->u.crypt.sector_size = SECTOR_SIZE;
+
+ r = -EINVAL;
+
+ rcipher = strsep(¶ms, " ");
+ rintegrity = NULL;
+
+ /* skip */
+ key_ = strsep(¶ms, " ");
+ if (!params)
+ goto err;
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ params++;
+
+ tgt->u.crypt.iv_offset = val64;
+
+ /* device */
+ rdevice = strsep(¶ms, " ");
+ if (get_flags & DM_ACTIVE_DEVICE) {
+ arg = crypt_lookup_dev(rdevice);
+ r = device_alloc(cd, &data_device, arg);
+ free(arg);
+ if (r < 0 && r != -ENOTBLK)
+ goto err;
+ }
+
+ r = -EINVAL;
+
+ /*offset */
+ if (!params)
+ goto err;
+ val64 = strtoull(params, ¶ms, 10);
+ tgt->u.crypt.offset = val64;
+
+ tgt->u.crypt.tag_size = 0;
+
+ /* Features section, available since crypt target version 1.11 */
+ if (*params) {
+ if (*params != ' ')
+ goto err;
+ params++;
+
+ /* Number of arguments */
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ params++;
+
+ for (i = 0; i < val64; i++) {
+ if (!params)
+ goto err;
+ arg = strsep(¶ms, " ");
+ if (!strcasecmp(arg, "allow_discards"))
+ *act_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
+ else if (!strcasecmp(arg, "same_cpu_crypt"))
+ *act_flags |= CRYPT_ACTIVATE_SAME_CPU_CRYPT;
+ else if (!strcasecmp(arg, "submit_from_crypt_cpus"))
+ *act_flags |= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS;
+ else if (!strcasecmp(arg, "iv_large_sectors"))
+ *act_flags |= CRYPT_ACTIVATE_IV_LARGE_SECTORS;
+ else if (sscanf(arg, "integrity:%u:", &val) == 1) {
+ tgt->u.crypt.tag_size = val;
+ rintegrity = strchr(arg + strlen("integrity:"), ':');
+ if (!rintegrity)
+ goto err;
+ rintegrity++;
+ } else if (sscanf(arg, "sector_size:%u", &val) == 1) {
+ tgt->u.crypt.sector_size = val;
+ } else /* unknown option */
+ goto err;
+ }
+
+ /* All parameters should be processed */
+ if (params)
+ goto err;
+ }
+
+ /* cipher */
+ if (get_flags & DM_ACTIVE_CRYPT_CIPHER) {
+ r = cipher_dm2c(CONST_CAST(char**)&cipher,
+ CONST_CAST(char**)&integrity,
+ rcipher, rintegrity);
+ if (r < 0)
+ goto err;
+ }
+
+ r = -EINVAL;
+
+ if (key_[0] == ':')
+ *act_flags |= CRYPT_ACTIVATE_KEYRING_KEY;
+
+ if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) {
+ /* we will trust kernel the key_string is in expected format */
+ if (key_[0] == ':') {
+ if (sscanf(key_ + 1, "%zu", &key_size) != 1)
+ goto err;
+ } else
+ key_size = strlen(key_) / 2;
+
+ vk = crypt_alloc_volume_key(key_size, NULL);
+ if (!vk) {
+ r = -ENOMEM;
+ goto err;
+ }
+
+ if (get_flags & DM_ACTIVE_CRYPT_KEY) {
+ if (key_[0] == ':') {
+ /* :<key_size>:<key_type>:<key_description> */
+ key_desc = NULL;
+ endp = strpbrk(key_ + 1, ":");
+ if (endp)
+ key_desc = strpbrk(endp + 1, ":");
+ if (!key_desc) {
+ r = -ENOMEM;
+ goto err;
+ }
+ key_desc++;
+ crypt_volume_key_set_description(vk, key_desc);
+ } else {
+ buffer[2] = '\0';
+ for(i = 0; i < vk->keylength; i++) {
+ memcpy(buffer, &key_[i * 2], 2);
+ vk->key[i] = strtoul(buffer, &endp, 16);
+ if (endp != &buffer[2]) {
+ r = -EINVAL;
+ goto err;
+ }
+ }
+ }
+ }
+ }
+ memset(key_, 0, strlen(key_));
+
+ if (cipher)
+ tgt->u.crypt.cipher = cipher;
+ if (integrity)
+ tgt->u.crypt.integrity = integrity;
+ if (data_device)
+ tgt->data_device = data_device;
+ if (vk)
+ tgt->u.crypt.vk = vk;
+ return 0;
+err:
+ free(cipher);
+ free(integrity);
+ device_free(cd, data_device);
+ crypt_free_volume_key(vk);
+ return r;
+}
+
+static int _dm_target_query_verity(struct crypt_device *cd,
+ uint32_t get_flags,
+ char *params,
+ struct dm_target *tgt,
+ uint32_t *act_flags)
+{
+ struct crypt_params_verity *vp = NULL;
+ uint32_t val32;
+ uint64_t val64;
+ ssize_t len;
+ char *str, *str2, *arg;
+ unsigned int i, features;
+ int r;
+ struct device *data_device = NULL, *hash_device = NULL, *fec_device = NULL;
+ char *hash_name = NULL, *root_hash = NULL, *salt = NULL, *fec_dev_str = NULL;
+ char *root_hash_sig_key_desc = NULL;
+
+ if (get_flags & DM_ACTIVE_VERITY_PARAMS) {
+ vp = crypt_zalloc(sizeof(*vp));
+ if (!vp)
+ return -ENOMEM;
+ }
+
+ tgt->type = DM_VERITY;
+ tgt->direction = TARGET_QUERY;
+ tgt->u.verity.vp = vp;
+
+ /* version */
+ val32 = strtoul(params, ¶ms, 10);
+ if (*params != ' ')
+ return -EINVAL;
+ if (vp)
+ vp->hash_type = val32;
+ params++;
+
+ /* data device */
+ str = strsep(¶ms, " ");
+ if (!params)
+ return -EINVAL;
+ if (get_flags & DM_ACTIVE_DEVICE) {
+ str2 = crypt_lookup_dev(str);
+ r = device_alloc(cd, &data_device, str2);
+ free(str2);
+ if (r < 0 && r != -ENOTBLK)
+ return r;
+ }
+
+ r = -EINVAL;
+
+ /* hash device */
+ str = strsep(¶ms, " ");
+ if (!params)
+ goto err;
+ if (get_flags & DM_ACTIVE_VERITY_HASH_DEVICE) {
+ str2 = crypt_lookup_dev(str);
+ r = device_alloc(cd, &hash_device, str2);
+ free(str2);
+ if (r < 0 && r != -ENOTBLK)
+ goto err;
+ }
+
+ r = -EINVAL;
+
+ /* data block size*/
+ val32 = strtoul(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ if (vp)
+ vp->data_block_size = val32;
+ params++;
+
+ /* hash block size */
+ val32 = strtoul(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ if (vp)
+ vp->hash_block_size = val32;
+ params++;
+
+ /* data blocks */
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ if (vp)
+ vp->data_size = val64;
+ params++;
+
+ /* hash start */
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ tgt->u.verity.hash_offset = val64;
+ params++;
+
+ /* hash algorithm */
+ str = strsep(¶ms, " ");
+ if (!params)
+ goto err;
+ if (vp) {
+ hash_name = strdup(str);
+ if (!hash_name) {
+ r = -ENOMEM;
+ goto err;
+ }
+ }
+
+ /* root digest */
+ str = strsep(¶ms, " ");
+ if (!params)
+ goto err;
+ len = crypt_hex_to_bytes(str, &str2, 0);
+ if (len < 0) {
+ r = len;
+ goto err;
+ }
+ tgt->u.verity.root_hash_size = len;
+ if (get_flags & DM_ACTIVE_VERITY_ROOT_HASH)
+ root_hash = str2;
+ else
+ free(str2);
+
+ /* salt */
+ str = strsep(¶ms, " ");
+ if (vp) {
+ if (!strcmp(str, "-")) {
+ vp->salt_size = 0;
+ vp->salt = NULL;
+ } else {
+ len = crypt_hex_to_bytes(str, &str2, 0);
+ if (len < 0) {
+ r = len;
+ goto err;
+ }
+ vp->salt_size = len;
+ salt = str2;
+ }
+ }
+
+ r = -EINVAL;
+
+ /* Features section, available since verity target version 1.3 */
+ if (params) {
+ /* Number of arguments */
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ params++;
+
+ features = (int)val64;
+ for (i = 0; i < features; i++) {
+ r = -EINVAL;
+ if (!params)
+ goto err;
+ arg = strsep(¶ms, " ");
+ if (!strcasecmp(arg, "ignore_corruption"))
+ *act_flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION;
+ else if (!strcasecmp(arg, "restart_on_corruption"))
+ *act_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION;
+ else if (!strcasecmp(arg, "ignore_zero_blocks"))
+ *act_flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS;
+ else if (!strcasecmp(arg, "check_at_most_once"))
+ *act_flags |= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE;
+ else if (!strcasecmp(arg, "use_fec_from_device")) {
+ str = strsep(¶ms, " ");
+ str2 = crypt_lookup_dev(str);
+ if (get_flags & DM_ACTIVE_VERITY_HASH_DEVICE) {
+ r = device_alloc(cd, &fec_device, str2);
+ if (r < 0 && r != -ENOTBLK) {
+ free(str2);
+ goto err;
+ }
+ }
+ if (vp) {
+ free(fec_dev_str);
+ fec_dev_str = str2;
+ } else
+ free(str2);
+ i++;
+ } else if (!strcasecmp(arg, "fec_start")) {
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params)
+ params++;
+ tgt->u.verity.fec_offset = val64;
+ if (vp)
+ vp->fec_area_offset = val64 * vp->hash_block_size;
+ i++;
+ } else if (!strcasecmp(arg, "fec_blocks")) {
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params)
+ params++;
+ tgt->u.verity.fec_blocks = val64;
+ i++;
+ } else if (!strcasecmp(arg, "fec_roots")) {
+ val32 = strtoul(params, ¶ms, 10);
+ if (*params)
+ params++;
+ if (vp)
+ vp->fec_roots = val32;
+ i++;
+ } else if (!strcasecmp(arg, "root_hash_sig_key_desc")) {
+ str = strsep(¶ms, " ");
+ if (!str)
+ goto err;
+ if (!root_hash_sig_key_desc)
+ root_hash_sig_key_desc = strdup(str);
+ i++;
+ if (vp)
+ vp->flags |= CRYPT_VERITY_ROOT_HASH_SIGNATURE;
+ } else /* unknown option */
+ goto err;
+ }
+
+ /* All parameters should be processed */
+ if (params && *params) {
+ r = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (data_device)
+ tgt->data_device = data_device;
+ if (hash_device)
+ tgt->u.verity.hash_device = hash_device;
+ if (fec_device)
+ tgt->u.verity.fec_device = fec_device;
+ if (root_hash)
+ tgt->u.verity.root_hash = root_hash;
+ if (vp && hash_name)
+ vp->hash_name = hash_name;
+ if (vp && salt)
+ vp->salt = salt;
+ if (vp && fec_dev_str)
+ vp->fec_device = fec_dev_str;
+ if (root_hash_sig_key_desc)
+ tgt->u.verity.root_hash_sig_key_desc = root_hash_sig_key_desc;
+
+ return 0;
+err:
+ device_free(cd, data_device);
+ device_free(cd, hash_device);
+ device_free(cd, fec_device);
+ free(root_hash_sig_key_desc);
+ free(root_hash);
+ free(hash_name);
+ free(salt);
+ free(fec_dev_str);
+ free(vp);
+ return r;
+}
+
+static int _dm_target_query_integrity(struct crypt_device *cd,
+ uint32_t get_flags,
+ char *params,
+ struct dm_target *tgt,
+ uint32_t *act_flags)
+{
+ uint32_t val32;
+ uint64_t val64;
+ char c, *str, *str2, *arg;
+ unsigned int i, features, val;
+ ssize_t len;
+ int r;
+ struct device *data_device = NULL, *meta_device = NULL;
+ char *integrity = NULL, *journal_crypt = NULL, *journal_integrity = NULL;
+ struct volume_key *vk = NULL;
+
+ tgt->type = DM_INTEGRITY;
+ tgt->direction = TARGET_QUERY;
+
+ /* data device */
+ str = strsep(¶ms, " ");
+ if (get_flags & DM_ACTIVE_DEVICE) {
+ str2 = crypt_lookup_dev(str);
+ r = device_alloc(cd, &data_device, str2);
+ free(str2);
+ if (r < 0 && r != -ENOTBLK)
+ return r;
+ }
+
+ r = -EINVAL;
+
+ /*offset */
+ if (!params)
+ goto err;
+ val64 = strtoull(params, ¶ms, 10);
+ if (!*params || *params != ' ')
+ goto err;
+ tgt->u.integrity.offset = val64;
+
+ /* tag size*/
+ val32 = strtoul(params, ¶ms, 10);
+ tgt->u.integrity.tag_size = val32;
+ if (!*params || *params != ' ')
+ goto err;
+
+ /* journal */
+ c = toupper(*(++params));
+ if (!*params || *(++params) != ' ' || (c != 'D' && c != 'J' && c != 'R' && c != 'B'))
+ goto err;
+ if (c == 'D')
+ *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL;
+ if (c == 'R')
+ *act_flags |= CRYPT_ACTIVATE_RECOVERY;
+ if (c == 'B') {
+ *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL;
+ *act_flags |= CRYPT_ACTIVATE_NO_JOURNAL_BITMAP;
+ }
+
+ tgt->u.integrity.sector_size = SECTOR_SIZE;
+
+ /* Features section */
+ if (params) {
+ /* Number of arguments */
+ val64 = strtoull(params, ¶ms, 10);
+ if (*params != ' ')
+ goto err;
+ params++;
+
+ features = (int)val64;
+ for (i = 0; i < features; i++) {
+ r = -EINVAL;
+ if (!params)
+ goto err;
+ arg = strsep(¶ms, " ");
+ if (sscanf(arg, "journal_sectors:%u", &val) == 1)
+ tgt->u.integrity.journal_size = val * SECTOR_SIZE;
+ else if (sscanf(arg, "journal_watermark:%u", &val) == 1)
+ tgt->u.integrity.journal_watermark = val;
+ else if (sscanf(arg, "sectors_per_bit:%" PRIu64, &val64) == 1) {
+ if (val64 > UINT_MAX)
+ goto err;
+ /* overloaded value for bitmap mode */
+ tgt->u.integrity.journal_watermark = (unsigned int)val64;
+ } else if (sscanf(arg, "commit_time:%u", &val) == 1)
+ tgt->u.integrity.journal_commit_time = val;
+ else if (sscanf(arg, "bitmap_flush_interval:%u", &val) == 1)
+ /* overloaded value for bitmap mode */
+ tgt->u.integrity.journal_commit_time = val;
+ else if (sscanf(arg, "interleave_sectors:%u", &val) == 1)
+ tgt->u.integrity.interleave_sectors = val;
+ else if (sscanf(arg, "block_size:%u", &val) == 1)
+ tgt->u.integrity.sector_size = val;
+ else if (sscanf(arg, "buffer_sectors:%u", &val) == 1)
+ tgt->u.integrity.buffer_sectors = val;
+ else if (!strncmp(arg, "internal_hash:", 14) && !integrity) {
+ str = &arg[14];
+ arg = strsep(&str, ":");
+ if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) {
+ integrity = strdup(arg);
+ if (!integrity) {
+ r = -ENOMEM;
+ goto err;
+ }
+ }
+
+ if (str) {
+ len = crypt_hex_to_bytes(str, &str2, 1);
+ if (len < 0) {
+ r = len;
+ goto err;
+ }
+
+ r = 0;
+ if (get_flags & DM_ACTIVE_CRYPT_KEY) {
+ vk = crypt_alloc_volume_key(len, str2);
+ if (!vk)
+ r = -ENOMEM;
+ } else if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) {
+ vk = crypt_alloc_volume_key(len, NULL);
+ if (!vk)
+ r = -ENOMEM;
+ }
+ crypt_safe_free(str2);
+ if (r < 0)
+ goto err;
+ }
+ } else if (!strncmp(arg, "meta_device:", 12) && !meta_device) {
+ if (get_flags & DM_ACTIVE_DEVICE) {
+ str = crypt_lookup_dev(&arg[12]);
+ r = device_alloc(cd, &meta_device, str);
+ free(str);
+ if (r < 0 && r != -ENOTBLK)
+ goto err;
+ }
+ } else if (!strncmp(arg, "journal_crypt:", 14) && !journal_crypt) {
+ str = &arg[14];
+ arg = strsep(&str, ":");
+ if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) {
+ journal_crypt = strdup(arg);
+ if (!journal_crypt) {
+ r = -ENOMEM;
+ goto err;
+ }
+ }
+ } else if (!strncmp(arg, "journal_mac:", 12) && !journal_integrity) {
+ str = &arg[12];
+ arg = strsep(&str, ":");
+ if (get_flags & DM_ACTIVE_INTEGRITY_PARAMS) {
+ journal_integrity = strdup(arg);
+ if (!journal_integrity) {
+ r = -ENOMEM;
+ goto err;
+ }
+ }
+ } else if (!strcmp(arg, "recalculate")) {
+ *act_flags |= CRYPT_ACTIVATE_RECALCULATE;
+ } else if (!strcmp(arg, "fix_padding")) {
+ tgt->u.integrity.fix_padding = true;
+ } else if (!strcmp(arg, "allow_discards")) {
+ *act_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
+ } else /* unknown option */
+ goto err;
+ }
+
+ /* All parameters should be processed */
+ if (params && *params) {
+ r = -EINVAL;
+ goto err;