X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Flibdevmapper.c;h=ad6e908c6f2ac956290f1e4921d2956e53928d7c;hb=db0f5f8d22ce2574774c9b37babae29ea35ec787;hp=7510dec5f9ab827957a99874eb5df5f3797c3a18;hpb=c2a33b480fe16bde27a4602d3384bc512780cdc4;p=platform%2Fupstream%2Fcryptsetup.git diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index 7510dec..ad6e908 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -4,10 +4,12 @@ * Copyright (C) 2004, Christophe Saout * Copyright (C) 2004-2007, Clemens Fruhwirth * Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved. + * Copyright (C) 2009-2012, Milan Broz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -26,22 +28,24 @@ #include #include #include +#include #include "internal.h" -#include "luks.h" #define DM_UUID_LEN 129 #define DM_UUID_PREFIX "CRYPT-" #define DM_UUID_PREFIX_LEN 6 #define DM_CRYPT_TARGET "crypt" +#define DM_VERITY_TARGET "verity" #define RETRY_COUNT 5 /* Set if dm-crypt version was probed */ static int _dm_crypt_checked = 0; +static int _quiet_log = 0; static uint32_t _dm_crypt_flags = 0; -static int _dm_use_count = 0; static struct crypt_device *_context = NULL; +static int _dm_use_count = 0; /* Check if we have DM flag to instruct kernel to force wipe buffers */ #if !HAVE_DECL_DM_TASK_SECURE_DATA @@ -81,11 +85,14 @@ static void set_dm_error(int level, va_start(va, f); if (vasprintf(&msg, f, va) > 0) { - if (level < 4) { + if (level < 4 && !_quiet_log) { log_err(_context, msg); log_err(_context, "\n"); - } else - log_dbg(msg); + } else { + /* We do not use DM visual stack backtrace here */ + if (strncmp(msg, "", 11)) + log_dbg(msg); + } } free(msg); va_end(va); @@ -127,28 +134,50 @@ static void _dm_set_crypt_compat(const char *dm_version, unsigned crypt_maj, _dm_crypt_checked = 1; } +static void _dm_set_verity_compat(const char *dm_version, unsigned verity_maj, + unsigned verity_min, unsigned verity_patch) +{ + if (verity_maj > 0) + _dm_crypt_flags |= DM_VERITY_SUPPORTED; + + log_dbg("Detected dm-verity version %i.%i.%i.", + verity_maj, verity_min, verity_patch); +} + +static void _dm_kernel_info(void) +{ + struct utsname uts; + + if (!uname(&uts)) + log_dbg("Detected kernel %s %s %s.", + uts.sysname, uts.release, uts.machine); + +} + static int _dm_check_versions(void) { struct dm_task *dmt; struct dm_versions *target, *last_target; char dm_version[16]; + int r = 0; if (_dm_crypt_checked) return 1; + _dm_kernel_info(); + + /* Shut up DM while checking */ + _quiet_log = 1; + /* FIXME: add support to DM so it forces crypt target module load here */ if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS))) - return 0; + goto out; - if (!dm_task_run(dmt)) { - dm_task_destroy(dmt); - return 0; - } + if (!dm_task_run(dmt)) + goto out; - if (!dm_task_get_driver_version(dmt, dm_version, sizeof(dm_version))) { - dm_task_destroy(dmt); - return 0; - } + if (!dm_task_get_driver_version(dmt, dm_version, sizeof(dm_version))) + goto out; target = dm_task_get_versions(dmt); do { @@ -158,54 +187,74 @@ static int _dm_check_versions(void) (unsigned)target->version[0], (unsigned)target->version[1], (unsigned)target->version[2]); + } else if (!strcmp(DM_VERITY_TARGET, target->name)) { + _dm_set_verity_compat(dm_version, + (unsigned)target->version[0], + (unsigned)target->version[1], + (unsigned)target->version[2]); } target = (struct dm_versions *)((char *) target + target->next); } while (last_target != target); - dm_task_destroy(dmt); - return 1; + r = 1; + log_dbg("Device-mapper backend running with UDEV support %sabled.", + _dm_use_udev() ? "en" : "dis"); +out: + if (dmt) + dm_task_destroy(dmt); + + _quiet_log = 0; + return r; } uint32_t dm_flags(void) { - if (!_dm_crypt_checked) - _dm_check_versions(); - + _dm_check_versions(); return _dm_crypt_flags; } -int dm_init(struct crypt_device *context, int check_kernel) +/* This doesn't run any kernel checks, just set up userspace libdevmapper */ +void dm_backend_init(void) { if (!_dm_use_count++) { - log_dbg("Initialising device-mapper backend%s, UDEV is %sabled.", - check_kernel ? "" : " (NO kernel check requested)", - _dm_use_udev() ? "en" : "dis"); - if (check_kernel && !_dm_check_versions()) { - log_err(context, _("Cannot initialize device-mapper. Is dm_mod kernel module loaded?\n")); - return -1; - } - if (getuid() || geteuid()) - log_dbg(("WARNING: Running as a non-root user. Functionality may be unavailable.")); + log_dbg("Initialising device-mapper backend library."); dm_log_init(set_dm_error); dm_log_init_verbose(10); } - - // FIXME: global context is not safe - if (context) - _context = context; - - return 1; /* unsafe memory */ } -void dm_exit(void) +void dm_backend_exit(void) { if (_dm_use_count && (!--_dm_use_count)) { log_dbg("Releasing device-mapper backend."); dm_log_init_verbose(0); dm_log_init(NULL); dm_lib_release(); + } +} + +/* + * libdevmapper is not context friendly, switch context on every DM call. + * FIXME: this is not safe if called in parallel but neither is DM lib. + */ +static int dm_init_context(struct crypt_device *cd) +{ + _context = cd; + if (!_dm_check_versions()) { + if (getuid() || geteuid()) + log_err(cd, _("Cannot initialize device-mapper, " + "running as non-root user.\n")); + else + log_err(cd, _("Cannot initialize device-mapper. " + "Is dm_mod kernel module loaded?\n")); _context = NULL; + return -ENOTSUP; } + return 0; +} +static void dm_exit_context(void) +{ + _context = NULL; } /* Return path to DM device */ @@ -241,12 +290,16 @@ static void hex_key(char *hexkey, size_t key_size, const char *key) sprintf(&hexkey[i * 2], "%02x", (unsigned char)key[i]); } -static char *get_params(struct crypt_dm_active_device *dmd) +/* http://code.google.com/p/cryptsetup/wiki/DMCrypt */ +static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd) { int r, max_size, null_cipher = 0; char *params, *hexkey; const char *features = ""; + if (!dmd) + return NULL; + if (dmd->flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) { if (dm_flags() & DM_DISCARDS_SUPPORTED) { features = " 1 allow_discards"; @@ -255,27 +308,29 @@ static char *get_params(struct crypt_dm_active_device *dmd) log_dbg("Discard/TRIM is not supported by the kernel."); } - if (!strncmp(dmd->cipher, "cipher_null-", 12)) + if (!strncmp(dmd->u.crypt.cipher, "cipher_null-", 12)) null_cipher = 1; - hexkey = crypt_safe_alloc(null_cipher ? 2 : (dmd->vk->keylength * 2 + 1)); + hexkey = crypt_safe_alloc(null_cipher ? 2 : (dmd->u.crypt.vk->keylength * 2 + 1)); if (!hexkey) return NULL; if (null_cipher) strncpy(hexkey, "-", 2); else - hex_key(hexkey, dmd->vk->keylength, dmd->vk->key); + hex_key(hexkey, dmd->u.crypt.vk->keylength, dmd->u.crypt.vk->key); - max_size = strlen(hexkey) + strlen(dmd->cipher) + - strlen(dmd->device) + strlen(features) + 64; + max_size = strlen(hexkey) + strlen(dmd->u.crypt.cipher) + + strlen(device_block_path(dmd->data_device)) + + strlen(features) + 64; params = crypt_safe_alloc(max_size); if (!params) goto out; r = snprintf(params, max_size, "%s %s %" PRIu64 " %s %" PRIu64 "%s", - dmd->cipher, hexkey, dmd->iv_offset, dmd->device, - dmd->offset, features); + dmd->u.crypt.cipher, hexkey, dmd->u.crypt.iv_offset, + device_block_path(dmd->data_device), dmd->u.crypt.offset, + features); if (r < 0 || r >= max_size) { crypt_safe_free(params); params = NULL; @@ -285,6 +340,56 @@ out: return params; } +/* http://code.google.com/p/cryptsetup/wiki/DMVerity */ +static char *get_dm_verity_params(struct crypt_params_verity *vp, + struct crypt_dm_active_device *dmd) +{ + int max_size, r; + char *params = NULL, *hexroot = NULL, *hexsalt = NULL; + + if (!vp || !dmd) + return NULL; + + hexroot = crypt_safe_alloc(dmd->u.verity.root_hash_size * 2 + 1); + if (!hexroot) + goto out; + hex_key(hexroot, dmd->u.verity.root_hash_size, dmd->u.verity.root_hash); + + hexsalt = crypt_safe_alloc(vp->salt_size ? vp->salt_size * 2 + 1 : 2); + if (!hexsalt) + goto out; + if (vp->salt_size) + hex_key(hexsalt, vp->salt_size, vp->salt); + else + strncpy(hexsalt, "-", 2); + + max_size = strlen(hexroot) + strlen(hexsalt) + + strlen(device_block_path(dmd->data_device)) + + strlen(device_block_path(dmd->u.verity.hash_device)) + + strlen(vp->hash_name) + 128; + + params = crypt_safe_alloc(max_size); + if (!params) + goto out; + + r = snprintf(params, max_size, + "%u %s %s %u %u %" PRIu64 " %" PRIu64 " %s %s %s", + vp->hash_type, device_block_path(dmd->data_device), + device_block_path(dmd->u.verity.hash_device), + vp->data_block_size, vp->hash_block_size, + vp->data_size, dmd->u.verity.hash_offset, + vp->hash_name, hexroot, hexsalt); + if (r < 0 || r >= max_size) { + crypt_safe_free(params); + params = NULL; + } +out: + crypt_safe_free(hexroot); + crypt_safe_free(hexsalt); + return params; + +} + /* DM helpers */ static int _dm_simple(int task, const char *name, int udev_wait) { @@ -354,7 +459,8 @@ error: return r; } -int dm_remove_device(const char *name, int force, uint64_t size) +int dm_remove_device(struct crypt_device *cd, const char *name, + int force, uint64_t size) { int r = -EINVAL; int retries = force ? RETRY_COUNT : 1; @@ -363,13 +469,14 @@ int dm_remove_device(const char *name, int force, uint64_t size) if (!name || (force && !size)) return -EINVAL; + if (dm_init_context(cd)) + return -ENOTSUP; + do { r = _dm_simple(DM_DEVICE_REMOVE, name, 1) ? 0 : -EINVAL; if (--retries && r) { log_dbg("WARNING: other process locked internal device %s, %s.", name, retries ? "retrying remove" : "giving up"); - if (force && (crypt_get_debug_level() == CRYPT_LOG_DEBUG)) - debug_processes_using_device(name); sleep(1); if (force && !error_target) { /* If force flag is set, replace device with error, read-only target. @@ -387,6 +494,7 @@ int dm_remove_device(const char *name, int force, uint64_t size) } while (r == -EINVAL && retries); dm_task_update_nodes(); + dm_exit_context(); return r; } @@ -398,14 +506,19 @@ int dm_remove_device(const char *name, int force, uint64_t size) * CRYPT-LUKS1-00000000000000000000000000000000-name * CRYPT-TEMP-name */ -static void dm_prepare_uuid(const char *name, const char *type, const char *uuid, char *buf, size_t buflen) +static int dm_prepare_uuid(const char *name, const char *type, const char *uuid, char *buf, size_t buflen) { char *ptr, uuid2[UUID_LEN] = {0}; uuid_t uu; unsigned i = 0; /* Remove '-' chars */ - if (uuid && !uuid_parse(uuid, uu)) { + if (uuid) { + if (uuid_parse(uuid, uu) < 0) { + log_dbg("Requested UUID %s has invalid format.", uuid); + return -EINVAL; + } + for (ptr = uuid2, i = 0; i < UUID_LEN; i++) if (uuid[i] != '-') { *ptr = uuid[i]; @@ -421,27 +534,24 @@ static void dm_prepare_uuid(const char *name, const char *type, const char *uuid log_dbg("DM-UUID is %s", buf); if (i >= buflen) log_err(NULL, _("DM-UUID for device %s was truncated.\n"), name); + + return 0; } -int dm_create_device(const char *name, - const char *type, - struct crypt_dm_active_device *dmd, - int reload) +static int _dm_create_device(const char *name, const char *type, + struct device *device, uint32_t flags, + const char *uuid, uint64_t size, + char *params, int reload) { struct dm_task *dmt = NULL; struct dm_info dmi; - char *params = NULL; char dev_uuid[DM_UUID_LEN] = {0}; int r = -EINVAL; uint32_t read_ahead = 0; uint32_t cookie = 0; uint16_t udev_flags = 0; - params = get_params(dmd); - if (!params) - goto out_no_removal; - - if (dmd->flags & CRYPT_ACTIVATE_PRIVATE) + if (flags & CRYPT_ACTIVATE_PRIVATE) udev_flags = CRYPT_TEMP_UDEV_FLAGS; /* All devices must have DM_UUID, only resize on old device is exception */ @@ -452,7 +562,9 @@ int dm_create_device(const char *name, if (!dm_task_set_name(dmt, name)) goto out_no_removal; } else { - dm_prepare_uuid(name, type, dmd->uuid, dev_uuid, sizeof(dev_uuid)); + r = dm_prepare_uuid(name, type, uuid, dev_uuid, sizeof(dev_uuid)); + if (r < 0) + return r; if (!(dmt = dm_task_create(DM_DEVICE_CREATE))) goto out_no_removal; @@ -469,13 +581,15 @@ int dm_create_device(const char *name, if ((dm_flags() & DM_SECURE_SUPPORTED) && !dm_task_secure_data(dmt)) goto out_no_removal; - if ((dmd->flags & CRYPT_ACTIVATE_READONLY) && !dm_task_set_ro(dmt)) + if ((flags & CRYPT_ACTIVATE_READONLY) && !dm_task_set_ro(dmt)) goto out_no_removal; - if (!dm_task_add_target(dmt, 0, dmd->size, DM_CRYPT_TARGET, params)) + + if (!dm_task_add_target(dmt, 0, size, + !strcmp("VERITY", type) ? DM_VERITY_TARGET : DM_CRYPT_TARGET, params)) goto out_no_removal; #ifdef DM_READ_AHEAD_MINIMUM_FLAG - if (device_read_ahead(dmd->device, &read_ahead) && + if (device_read_ahead(device, &read_ahead) && !dm_task_set_read_ahead(dmt, read_ahead, DM_READ_AHEAD_MINIMUM_FLAG)) goto out_no_removal; #endif @@ -489,7 +603,7 @@ int dm_create_device(const char *name, goto out; if (!dm_task_set_name(dmt, name)) goto out; - if (dmd->uuid && !dm_task_set_uuid(dmt, dev_uuid)) + if (uuid && !dm_task_set_uuid(dmt, dev_uuid)) goto out; if (_dm_use_udev() && !_dm_task_set_cookie(dmt, &cookie, udev_flags)) goto out; @@ -508,7 +622,7 @@ out: } if (r < 0 && !reload) - dm_remove_device(name, 0, 0); + _dm_simple(DM_DEVICE_REMOVE, name, 1); out_no_removal: if (cookie && _dm_use_udev()) @@ -523,11 +637,39 @@ out_no_removal: return r; } -static int dm_status_dmi(const char *name, struct dm_info *dmi) +int dm_create_device(struct crypt_device *cd, const char *name, + const char *type, + struct crypt_dm_active_device *dmd, + int reload) +{ + char *table_params = NULL; + int r = -EINVAL; + + if (!type) + return -EINVAL; + + if (dm_init_context(cd)) + return -ENOTSUP; + + if (dmd->target == DM_CRYPT) + table_params = get_dm_crypt_params(dmd); + else if (dmd->target == DM_VERITY) + table_params = get_dm_verity_params(dmd->u.verity.vp, dmd); + + if (table_params) + r = _dm_create_device(name, type, dmd->data_device, + dmd->flags, dmd->uuid, dmd->size, + table_params, reload); + dm_exit_context(); + return r; +} + +static int dm_status_dmi(const char *name, struct dm_info *dmi, + const char *target, char **status_line) { struct dm_task *dmt; uint64_t start, length; - char *target_type, *params; + char *target_type, *params = NULL; void *next = NULL; int r = -EINVAL; @@ -550,172 +692,383 @@ static int dm_status_dmi(const char *name, struct dm_info *dmi) next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms); - if (!target_type || strcmp(target_type, DM_CRYPT_TARGET) != 0 || - start != 0 || next) - r = -EINVAL; - else - r = 0; + + if (!target_type || start != 0 || next) + goto out; + + if (target && strcmp(target_type, target)) + goto out; + + /* for target == NULL check all supported */ + if (!target && (strcmp(target_type, DM_CRYPT_TARGET) && + strcmp(target_type, DM_VERITY_TARGET))) + goto out; + r = 0; out: + if (!r && status_line && !(*status_line = strdup(params))) + r = -ENOMEM; + if (dmt) dm_task_destroy(dmt); return r; } -int dm_status_device(const char *name) +int dm_status_device(struct crypt_device *cd, const char *name) { int r; struct dm_info dmi; + struct stat st; - r = dm_status_dmi(name, &dmi); + /* libdevmapper is too clever and handles + * path argument differenly with error. + * Fail early here if parameter is non-existent path. + */ + if (strchr(name, '/') && stat(name, &st) < 0) + return -ENODEV; + + if (dm_init_context(cd)) + return -ENOTSUP; + r = dm_status_dmi(name, &dmi, NULL, NULL); + dm_exit_context(); if (r < 0) return r; return (dmi.open_count > 0); } -int dm_status_suspended(const char *name) +int dm_status_suspended(struct crypt_device *cd, const char *name) { int r; struct dm_info dmi; - r = dm_status_dmi(name, &dmi); + if (dm_init_context(cd)) + return -ENOTSUP; + r = dm_status_dmi(name, &dmi, DM_CRYPT_TARGET, NULL); + dm_exit_context(); if (r < 0) return r; return dmi.suspended ? 1 : 0; } -int dm_query_device(const char *name, uint32_t get_flags, - struct crypt_dm_active_device *dmd) +static int _dm_status_verity_ok(const char *name) { - struct dm_task *dmt; + int r; struct dm_info dmi; - uint64_t start, length, val64; - char *target_type, *params, *rcipher, *key_, *rdevice, *endp, buffer[3], *arg; - const char *tmp_uuid; - void *next = NULL; - unsigned int i; - int r = -EINVAL; + char *status_line = NULL; - memset(dmd, 0, sizeof(*dmd)); + r = dm_status_dmi(name, &dmi, DM_VERITY_TARGET, &status_line); + if (r < 0 || !status_line) { + free(status_line); + return r; + } - if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) - goto out; - if ((dm_flags() & DM_SECURE_SUPPORTED) && !dm_task_secure_data(dmt)) - goto out; - if (!dm_task_set_name(dmt, name)) - goto out; - r = -ENODEV; - if (!dm_task_run(dmt)) - goto out; + log_dbg("Verity volume %s status is %s.", name, status_line ?: ""); + r = status_line[0] == 'V' ? 1 : 0; + free(status_line); - r = -EINVAL; - if (!dm_task_get_info(dmt, &dmi)) - goto out; + return r; +} - if (!dmi.exists) { - r = -ENODEV; - goto out; - } +int dm_status_verity_ok(struct crypt_device *cd, const char *name) +{ + int r; - tmp_uuid = dm_task_get_uuid(dmt); + if (dm_init_context(cd)) + return -ENOTSUP; + r = _dm_status_verity_ok(name); + dm_exit_context(); + return r; +} - next = dm_get_next_target(dmt, next, &start, &length, - &target_type, ¶ms); - if (!target_type || strcmp(target_type, DM_CRYPT_TARGET) != 0 || - start != 0 || next) - goto out; +/* FIXME use hex wrapper, user val wrappers for line parsing */ +static int _dm_query_crypt(uint32_t get_flags, + struct dm_info *dmi, + char *params, + struct crypt_dm_active_device *dmd) +{ + uint64_t val64; + char *rcipher, *key_, *rdevice, *endp, buffer[3], *arg; + unsigned int i; + int r; - dmd->size = length; + memset(dmd, 0, sizeof(*dmd)); + dmd->target = DM_CRYPT; rcipher = strsep(¶ms, " "); /* cipher */ - if (get_flags & DM_ACTIVE_CIPHER) - dmd->cipher = strdup(rcipher); + if (get_flags & DM_ACTIVE_CRYPT_CIPHER) + dmd->u.crypt.cipher = strdup(rcipher); /* skip */ key_ = strsep(¶ms, " "); if (!params) - goto out; + return -EINVAL; val64 = strtoull(params, ¶ms, 10); if (*params != ' ') - goto out; + return -EINVAL; params++; - dmd->iv_offset = val64; + dmd->u.crypt.iv_offset = val64; /* device */ rdevice = strsep(¶ms, " "); - if (get_flags & DM_ACTIVE_DEVICE) - dmd->device = crypt_lookup_dev(rdevice); + if (get_flags & DM_ACTIVE_DEVICE) { + arg = crypt_lookup_dev(rdevice); + r = device_alloc(&dmd->data_device, arg); + free(arg); + if (r < 0 && r != -ENOTBLK) + return r; + } /*offset */ if (!params) - goto out; + return -EINVAL; val64 = strtoull(params, ¶ms, 10); - dmd->offset = val64; + dmd->u.crypt.offset = val64; /* Features section, available since crypt target version 1.11 */ if (*params) { if (*params != ' ') - goto out; + return -EINVAL; params++; /* Number of arguments */ val64 = strtoull(params, ¶ms, 10); if (*params != ' ') - goto out; + return -EINVAL; params++; for (i = 0; i < val64; i++) { if (!params) - goto out; + return -EINVAL; arg = strsep(¶ms, " "); if (!strcasecmp(arg, "allow_discards")) dmd->flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; else /* unknown option */ - goto out; + return -EINVAL; } /* All parameters shold be processed */ if (params) - goto out; + return -EINVAL; } /* Never allow to return empty key */ - if ((get_flags & DM_ACTIVE_KEY) && dmi.suspended) { + if ((get_flags & DM_ACTIVE_CRYPT_KEY) && dmi->suspended) { log_dbg("Cannot read volume key while suspended."); - r = -EINVAL; - goto out; + return -EINVAL; } - if (get_flags & DM_ACTIVE_KEYSIZE) { - dmd->vk = crypt_alloc_volume_key(strlen(key_) / 2, NULL); - if (!dmd->vk) { - r = -ENOMEM; - goto out; - } + if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) { + dmd->u.crypt.vk = crypt_alloc_volume_key(strlen(key_) / 2, NULL); + if (!dmd->u.crypt.vk) + return -ENOMEM; - if (get_flags & DM_ACTIVE_KEY) { + if (get_flags & DM_ACTIVE_CRYPT_KEY) { buffer[2] = '\0'; - for(i = 0; i < dmd->vk->keylength; i++) { + for(i = 0; i < dmd->u.crypt.vk->keylength; i++) { memcpy(buffer, &key_[i * 2], 2); - dmd->vk->key[i] = strtoul(buffer, &endp, 16); + dmd->u.crypt.vk->key[i] = strtoul(buffer, &endp, 16); if (endp != &buffer[2]) { - crypt_free_volume_key(dmd->vk); - dmd->vk = NULL; - r = -EINVAL; - goto out; + crypt_free_volume_key(dmd->u.crypt.vk); + dmd->u.crypt.vk = NULL; + return -EINVAL; } } } } memset(key_, 0, strlen(key_)); + return 0; +} + +static int _dm_query_verity(uint32_t get_flags, + struct dm_info *dmi, + char *params, + struct crypt_dm_active_device *dmd) +{ + struct crypt_params_verity *vp = NULL; + uint32_t val32; + uint64_t val64; + ssize_t len; + char *str, *str2; + int r; + + if (get_flags & DM_ACTIVE_VERITY_PARAMS) + vp = dmd->u.verity.vp; + + memset(dmd, 0, sizeof(*dmd)); + + dmd->target = DM_VERITY; + dmd->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(&dmd->data_device, str2); + free(str2); + if (r < 0 && r != -ENOTBLK) + return r; + } + + /* hash device */ + str = strsep(¶ms, " "); + if (!params) + return -EINVAL; + if (get_flags & DM_ACTIVE_VERITY_HASH_DEVICE) { + str2 = crypt_lookup_dev(str); + r = device_alloc(&dmd->u.verity.hash_device, str2); + free(str2); + if (r < 0 && r != -ENOTBLK) + return r; + } + + /* data block size*/ + val32 = strtoul(params, ¶ms, 10); + if (*params != ' ') + return -EINVAL; + if (vp) + vp->data_block_size = val32; + params++; + + /* hash block size */ + val32 = strtoul(params, ¶ms, 10); + if (*params != ' ') + return -EINVAL; + if (vp) + vp->hash_block_size = val32; + params++; + + /* data blocks */ + val64 = strtoull(params, ¶ms, 10); + if (*params != ' ') + return -EINVAL; + if (vp) + vp->data_size = val64; + params++; + + /* hash start */ + val64 = strtoull(params, ¶ms, 10); + if (*params != ' ') + return -EINVAL; + dmd->u.verity.hash_offset = val64; + params++; + + /* hash algorithm */ + str = strsep(¶ms, " "); + if (!params) + return -EINVAL; + if (vp) + vp->hash_name = strdup(str); + + /* root digest */ + str = strsep(¶ms, " "); + if (!params) + return -EINVAL; + len = crypt_hex_to_bytes(str, &str2, 0); + if (len < 0) + return len; + dmd->u.verity.root_hash_size = len; + if (get_flags & DM_ACTIVE_VERITY_ROOT_HASH) + dmd->u.verity.root_hash = str2; + else + free(str2); + + /* salt */ + str = strsep(¶ms, " "); + if (params) + return -EINVAL; + if (vp) { + if (!strcmp(str, "-")) { + vp->salt_size = 0; + vp->salt = NULL; + } else { + len = crypt_hex_to_bytes(str, &str2, 0); + if (len < 0) + return len; + vp->salt_size = len; + vp->salt = str2; + } + } + + return 0; +} + +int dm_query_device(struct crypt_device *cd, const char *name, + uint32_t get_flags, struct crypt_dm_active_device *dmd) +{ + struct dm_task *dmt; + struct dm_info dmi; + uint64_t start, length; + char *target_type, *params; + const char *tmp_uuid; + void *next = NULL; + int r = -EINVAL; + + if (dm_init_context(cd)) + return -ENOTSUP; + if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) + goto out; + if ((dm_flags() & DM_SECURE_SUPPORTED) && !dm_task_secure_data(dmt)) + goto out; + if (!dm_task_set_name(dmt, name)) + goto out; + r = -ENODEV; + if (!dm_task_run(dmt)) + goto out; + + r = -EINVAL; + if (!dm_task_get_info(dmt, &dmi)) + goto out; + + if (!dmi.exists) { + r = -ENODEV; + goto out; + } + + next = dm_get_next_target(dmt, next, &start, &length, + &target_type, ¶ms); + + if (!target_type || start != 0 || next) + goto out; + + if (!strcmp(target_type, DM_CRYPT_TARGET)) { + r = _dm_query_crypt(get_flags, &dmi, params, dmd); + } else if (!strcmp(target_type, DM_VERITY_TARGET)) { + r = _dm_query_verity(get_flags, &dmi, params, dmd); + if (r < 0) + goto out; + r = _dm_status_verity_ok(name); + if (r < 0) + goto out; + if (r == 0) + dmd->flags |= CRYPT_ACTIVATE_CORRUPTED; + r = 0; + } else + r = -EINVAL; + + if (r < 0) + goto out; + + dmd->size = length; + if (dmi.read_only) dmd->flags |= CRYPT_ACTIVATE_READONLY; + tmp_uuid = dm_task_get_uuid(dmt); if (!tmp_uuid) dmd->flags |= CRYPT_ACTIVATE_NO_UUID; else if (get_flags & DM_ACTIVE_UUID) { @@ -728,6 +1081,7 @@ out: if (dmt) dm_task_destroy(dmt); + dm_exit_context(); return r; } @@ -758,52 +1112,63 @@ static int _dm_message(const char *name, const char *msg) return r; } -int dm_suspend_and_wipe_key(const char *name) +int dm_suspend_and_wipe_key(struct crypt_device *cd, const char *name) { - if (!_dm_check_versions()) + int r = -ENOTSUP; + + if (dm_init_context(cd)) return -ENOTSUP; if (!(_dm_crypt_flags & DM_KEY_WIPE_SUPPORTED)) - return -ENOTSUP; + goto out; - if (!_dm_simple(DM_DEVICE_SUSPEND, name, 0)) - return -EINVAL; + if (!_dm_simple(DM_DEVICE_SUSPEND, name, 0)) { + r = -EINVAL; + goto out; + } if (!_dm_message(name, "key wipe")) { _dm_simple(DM_DEVICE_RESUME, name, 1); - return -EINVAL; + r = -EINVAL; + goto out; } - - return 0; + r = 0; +out: + dm_exit_context(); + return r; } -int dm_resume_and_reinstate_key(const char *name, - size_t key_size, - const char *key) +int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name, + size_t key_size, const char *key) { int msg_size = key_size * 2 + 10; // key set - char *msg; - int r = 0; + char *msg = NULL; + int r = -ENOTSUP; - if (!_dm_check_versions()) + if (dm_init_context(cd)) return -ENOTSUP; if (!(_dm_crypt_flags & DM_KEY_WIPE_SUPPORTED)) - return -ENOTSUP; + goto out; msg = crypt_safe_alloc(msg_size); - if (!msg) - return -ENOMEM; + if (!msg) { + r = -ENOMEM; + goto out; + } - memset(msg, 0, msg_size); strcpy(msg, "key set "); hex_key(&msg[8], key_size, key); if (!_dm_message(name, msg) || - !_dm_simple(DM_DEVICE_RESUME, name, 1)) + !_dm_simple(DM_DEVICE_RESUME, name, 1)) { r = -EINVAL; - + goto out; + } + r = 0; +out: crypt_safe_free(msg); + dm_exit_context(); return r; } @@ -821,26 +1186,3 @@ int dm_is_dm_kernel_name(const char *name) { return strncmp(name, "dm-", 3) ? 0 : 1; } - -int dm_check_segment(const char *name, uint64_t offset, uint64_t size) -{ - struct crypt_dm_active_device dmd; - int r; - - log_dbg("Checking segments for device %s.", name); - - r = dm_query_device(name, 0, &dmd); - if (r < 0) - return r; - - if (offset >= (dmd.offset + dmd.size) || (offset + size) <= dmd.offset) - r = 0; - else - r = -EBUSY; - - log_dbg("seg: %" PRIu64 " - %" PRIu64 ", new %" PRIu64 " - %" PRIu64 "%s", - dmd.offset, dmd.offset + dmd.size, offset, offset + size, - r ? " (overlapping)" : " (ok)"); - - return r; -}