/* used in CRYPT_VERITY */
struct crypt_params_verity verity_hdr;
char *verity_root_hash;
- uint64_t verity_root_hash_size;
+ unsigned int verity_root_hash_size;
char *verity_uuid;
/* callbacks definitions */
(r = crypt_set_data_device(cd, params->data_device)) < 0)
return r;
+ /* Hash availability checked in sb load */
cd->verity_root_hash_size = crypt_hash_size(cd->verity_hdr.hash_name);
+ if (cd->verity_root_hash_size > 4096)
+ return -EINVAL;
if (!cd->type && !(cd->type = strdup(CRYPT_VERITY)))
return -ENOMEM;
const char *uuid,
struct crypt_params_verity *params)
{
- int r = 0;
+ int r = 0, hash_size;
uint64_t data_device_size;
if (!mdata_device(cd)) {
if (!params || !params->data_device)
return -EINVAL;
+ if (params->hash_type > VERITY_MAX_HASH_TYPE) {
+ log_err(cd, _("Unsupported VERITY hash type %d.\n"), params->hash_type);
+ return -EINVAL;
+ }
+
+ if (VERITY_BLOCK_SIZE_OK(params->data_block_size) ||
+ VERITY_BLOCK_SIZE_OK(params->hash_block_size)) {
+ log_err(cd, _("Unsupported VERITY block size.\n"));
+ return -EINVAL;
+ }
+
+ if (params->hash_area_offset % 512) {
+ log_err(cd, _("Unsupported VERITY hash offset.\n"));
+ return -EINVAL;
+ }
+
if (!(cd->type = strdup(CRYPT_VERITY)))
return -ENOMEM;
if (r)
return r;
if (!params->data_size) {
- r = device_size(params->data_device, &data_device_size);
+ r = device_size(cd->device, &data_device_size);
if (r < 0)
return r;
} else
cd->verity_hdr.data_size = params->data_size;
- cd->verity_root_hash_size = crypt_hash_size(params->hash_name);
- if (!cd->verity_root_hash_size)
+ hash_size = crypt_hash_size(params->hash_name);
+ if (hash_size <= 0) {
+ log_err(cd, _("Hash algorithm %s not supported.\n"),
+ params->hash_name);
return -EINVAL;
+ }
+ cd->verity_root_hash_size = hash_size;
cd->verity_root_hash = malloc(cd->verity_root_hash_size);
if (!cd->verity_root_hash)
return -EINVAL;
}
+ if (sb_offset % 512) {
+ log_err(cd, _("Unsupported VERITY hash offset.\n"));
+ return -EINVAL;
+ }
+
devfd = open(device ,O_RDONLY | O_DIRECT);
if(devfd == -1) {
log_err(cd, _("Cannot open device %s.\n"), device);
return -EINVAL;
}
params->hash_type = le32_to_cpu(sb.hash_type);
- if (params->hash_type > 1) {
+ if (params->hash_type > VERITY_MAX_HASH_TYPE) {
log_err(cd, _("Unsupported VERITY hash type %d.\n"), params->hash_type);
return -EINVAL;
}
params->data_block_size = le64_to_cpu(sb.data_block_size);
params->hash_block_size = le64_to_cpu(sb.hash_block_size);
- if (params->data_block_size % 512 || params->hash_block_size % 512) {
+ if (VERITY_BLOCK_SIZE_OK(params->data_block_size) ||
+ VERITY_BLOCK_SIZE_OK(params->hash_block_size)) {
log_err(cd, _("Unsupported VERITY block size.\n"));
return -EINVAL;
}
params->hash_name = strndup((const char*)sb.algorithm, sizeof(sb.algorithm));
if (!params->hash_name)
return -ENOMEM;
+ if (crypt_hash_size(params->hash_name) <= 0) {
+ log_err(cd, _("Hash algorithm %s not supported.\n"),
+ params->hash_name);
+ free(CONST_CAST(char*)params->hash_name);
+ return -EINVAL;
+ }
params->salt_size = le64_to_cpu(sb.salt_size);
if (params->salt_size > sizeof(sb.salt)) {
#include <unistd.h>
#include "config.h"
+#define VERITY_MAX_HASH_TYPE 1
+#define VERITY_BLOCK_SIZE_OK(x) ((x) % 512 || (x) < 512 || \
+ (x) > (512 * 1024) || (x) & ((x)-1))
+
struct crypt_device;
struct crypt_params_verity;
return r;
}
+static int mult_overflow(off_t *u, off_t b, size_t size)
+{
+ *u = (uint64_t)b * size;
+ if ((off_t)(*u / size) != b || (off_t)*u < 0 || (off_t)*u != *u)
+ return 1;
+ return 0;
+}
+
static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr,
off_t data_block, size_t data_block_size,
off_t hash_block, size_t hash_block_size,
size_t hash_per_block = 1 << get_bits_down(hash_block_size / digest_size);
size_t digest_size_full = 1 << get_bits_up(digest_size);
off_t blocks_to_write = (blocks + hash_per_block - 1) / hash_per_block;
+ off_t seek_rd, seek_wr;
size_t left_bytes;
unsigned i;
int r;
- if (fseeko(rd, data_block * data_block_size, SEEK_SET)) {
+ if (mult_overflow(&seek_rd, data_block, data_block_size) ||
+ mult_overflow(&seek_wr, hash_block, hash_block_size)) {
+ log_err(cd, _("Device offset overflow.\n"));
+ return -EINVAL;
+ }
+
+ if (fseeko(rd, seek_rd, SEEK_SET)) {
log_dbg("Cannot seek to requested position in data device.");
return -EIO;
}
- if (wr && fseeko(wr, hash_block * hash_block_size, SEEK_SET)) {
+ if (wr && fseeko(wr, seek_wr, SEEK_SET)) {
log_dbg("Cannot seek to requested position in hash device.");
return -EIO;
}
off_t hash_level_size[VERITY_MAX_LEVELS];
off_t data_file_blocks, s;
size_t hash_per_block, hash_per_block_bits;
- uint64_t data_device_size = 0, hash_device_size = 0;
+ off_t data_device_size = 0, hash_device_size = 0;
+ uint64_t dev_size;
int levels, i, r;
log_dbg("Hash %s %s, data device %s, data blocks %" PRIu64
verify ? "verification" : "creation", hash_name,
data_device, data_blocks, hash_device, hash_position);
+ if (data_blocks < 0 || hash_position < 0) {
+ log_err(cd, _("Invalid size parameters for verity device.\n"));
+ return -EINVAL;
+ }
+
if (!data_blocks) {
- r = device_size(data_device, &data_device_size);
+ r = device_size(data_device, &dev_size);
if (r < 0)
return r;
- data_file_blocks = data_device_size / data_block_size;
- } else {
- data_device_size = data_blocks * data_block_size;
+ data_file_blocks = dev_size / data_block_size;
+ } else
data_file_blocks = data_blocks;
+
+ if (mult_overflow(&data_device_size, data_blocks, data_block_size)) {
+ log_err(cd, _("Device offset overflow.\n"));
+ return -EINVAL;
}
hash_per_block_bits = get_bits_down(hash_block_size / digest_size);
if (hash_position + s < hash_position ||
(hash_position + s) < 0 ||
(hash_position + s) != hash_position + s) {
- log_dbg("Hash device offset overflow.");
+ log_err(cd, _("Device offset overflow.\n"));
return -EINVAL;
}
hash_position += s;
}
- hash_device_size = hash_position * hash_block_size;
+
+ if (mult_overflow(&hash_device_size, hash_position, hash_block_size)) {
+ log_err(cd, _("Device offset overflow.\n"));
+ return -EINVAL;
+ }
log_dbg("Data device size required: %" PRIu64 " bytes.",
data_device_size);
if (verity_hdr->salt_size > 256)
return -EINVAL;
- if (verity_hdr->hash_block_size > pgsize ||
- verity_hdr->data_block_size > pgsize)
- log_err(cd, _("WARNING: Kernel cannot activate device if block "
- "size exceeds page size (%u).\n"), pgsize);
+ if (verity_hdr->data_block_size > pgsize)
+ log_err(cd, _("WARNING: Kernel cannot activate device if data "
+ "block size exceeds page size (%u).\n"), pgsize);
return VERITY_create_or_verify_hash(cd, 0,
verity_hdr->hash_type,
#include <stdarg.h>
#include <errno.h>
#include <string.h>
+#include <ctype.h>
#include <inttypes.h>
#include <popt.h>
#include <limits.h>
params->salt_size = 0;
params->salt = NULL;
} else if (salt_string) {
- params->salt_size = strlen(salt_string) / 2;
- if (hex_to_bytes(salt_string, salt_bytes) != params->salt_size)
+ if (hex_to_bytes(salt_string, salt_bytes) * 2 != strlen(salt_string)) {
+ log_err(_("Invalid salt string specified.\n"));
return -EINVAL;
+ }
+ params->salt_size = strlen(salt_string) / 2;
params->salt = salt_bytes;
} else
params->salt_size = DEFAULT_VERITY_SALT_SIZE;
goto out;
hash_size = crypt_get_volume_key_size(cd);
- if (hex_to_bytes(root_hash, root_hash_bytes) != hash_size) {
+ if (hash_size * 2 != strlen(root_hash) ||
+ hex_to_bytes(root_hash, root_hash_bytes) != hash_size) {
+ log_err(_("Invalid root hash string specified.\n"));
r = -EINVAL;
goto out;
}
char *endp;
errno = 0;
- ull_value = strtoull(popt_tmp, &endp, 0);
- if (*endp || !*popt_tmp ||
+ ull_value = strtoull(popt_tmp, &endp, 10);
+ if (*endp || !*popt_tmp || !isdigit(*popt_tmp) ||
(errno == ERANGE && ull_value == ULLONG_MAX) ||
(errno != 0 && ull_value == 0))
r = POPT_ERROR_BADNUMBER;
break;
case 2:
hash_start = ull_value * 512;
+ if (hash_start / 512 != ull_value)
+ r = POPT_ERROR_BADNUMBER;
break;
}
poptGetInvocationName(popt_context));
}
+ if (data_block_size < 0 || hash_block_size < 0 || hash_type < 0) {
+ usage(popt_context, EXIT_FAILURE,
+ _("Negative number for option not permitted."),
+ poptGetInvocationName(popt_context));
+ }
+
if (opt_debug) {
opt_verbose = 1;
crypt_set_debug_level(-1);