Check various number limits.

This commit is contained in:
Milan Broz
2012-06-11 00:04:39 +02:00
parent 6d2c15ea79
commit 62f334cfa5
5 changed files with 104 additions and 25 deletions

View File

@@ -70,7 +70,7 @@ struct crypt_device {
/* used in CRYPT_VERITY */ /* used in CRYPT_VERITY */
struct crypt_params_verity verity_hdr; struct crypt_params_verity verity_hdr;
char *verity_root_hash; char *verity_root_hash;
uint64_t verity_root_hash_size; unsigned int verity_root_hash_size;
char *verity_uuid; char *verity_uuid;
/* callbacks definitions */ /* callbacks definitions */
@@ -689,7 +689,10 @@ static int _crypt_load_verity(struct crypt_device *cd, struct crypt_params_verit
(r = crypt_set_data_device(cd, params->data_device)) < 0) (r = crypt_set_data_device(cd, params->data_device)) < 0)
return r; return r;
/* Hash availability checked in sb load */
cd->verity_root_hash_size = crypt_hash_size(cd->verity_hdr.hash_name); 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))) if (!cd->type && !(cd->type = strdup(CRYPT_VERITY)))
return -ENOMEM; return -ENOMEM;
@@ -1046,7 +1049,7 @@ static int _crypt_format_verity(struct crypt_device *cd,
const char *uuid, const char *uuid,
struct crypt_params_verity *params) struct crypt_params_verity *params)
{ {
int r = 0; int r = 0, hash_size;
uint64_t data_device_size; uint64_t data_device_size;
if (!mdata_device(cd)) { if (!mdata_device(cd)) {
@@ -1057,6 +1060,22 @@ static int _crypt_format_verity(struct crypt_device *cd,
if (!params || !params->data_device) if (!params || !params->data_device)
return -EINVAL; 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))) if (!(cd->type = strdup(CRYPT_VERITY)))
return -ENOMEM; return -ENOMEM;
@@ -1064,7 +1083,7 @@ static int _crypt_format_verity(struct crypt_device *cd,
if (r) if (r)
return r; return r;
if (!params->data_size) { if (!params->data_size) {
r = device_size(params->data_device, &data_device_size); r = device_size(cd->device, &data_device_size);
if (r < 0) if (r < 0)
return r; return r;
@@ -1072,9 +1091,13 @@ static int _crypt_format_verity(struct crypt_device *cd,
} else } else
cd->verity_hdr.data_size = params->data_size; cd->verity_hdr.data_size = params->data_size;
cd->verity_root_hash_size = crypt_hash_size(params->hash_name); hash_size = crypt_hash_size(params->hash_name);
if (!cd->verity_root_hash_size) if (hash_size <= 0) {
log_err(cd, _("Hash algorithm %s not supported.\n"),
params->hash_name);
return -EINVAL; return -EINVAL;
}
cd->verity_root_hash_size = hash_size;
cd->verity_root_hash = malloc(cd->verity_root_hash_size); cd->verity_root_hash = malloc(cd->verity_root_hash_size);
if (!cd->verity_root_hash) if (!cd->verity_root_hash)

View File

@@ -88,6 +88,11 @@ int VERITY_read_sb(struct crypt_device *cd,
return -EINVAL; return -EINVAL;
} }
if (sb_offset % 512) {
log_err(cd, _("Unsupported VERITY hash offset.\n"));
return -EINVAL;
}
devfd = open(device ,O_RDONLY | O_DIRECT); devfd = open(device ,O_RDONLY | O_DIRECT);
if(devfd == -1) { if(devfd == -1) {
log_err(cd, _("Cannot open device %s.\n"), device); log_err(cd, _("Cannot open device %s.\n"), device);
@@ -141,14 +146,15 @@ int VERITY_read_sb(struct crypt_device *cd,
return -EINVAL; return -EINVAL;
} }
params->hash_type = le32_to_cpu(sb.hash_type); 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); log_err(cd, _("Unsupported VERITY hash type %d.\n"), params->hash_type);
return -EINVAL; return -EINVAL;
} }
params->data_block_size = le64_to_cpu(sb.data_block_size); params->data_block_size = le64_to_cpu(sb.data_block_size);
params->hash_block_size = le64_to_cpu(sb.hash_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")); log_err(cd, _("Unsupported VERITY block size.\n"));
return -EINVAL; return -EINVAL;
} }
@@ -157,6 +163,12 @@ int VERITY_read_sb(struct crypt_device *cd,
params->hash_name = strndup((const char*)sb.algorithm, sizeof(sb.algorithm)); params->hash_name = strndup((const char*)sb.algorithm, sizeof(sb.algorithm));
if (!params->hash_name) if (!params->hash_name)
return -ENOMEM; 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); params->salt_size = le64_to_cpu(sb.salt_size);
if (params->salt_size > sizeof(sb.salt)) { if (params->salt_size > sizeof(sb.salt)) {

View File

@@ -23,6 +23,10 @@
#include <unistd.h> #include <unistd.h>
#include "config.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_device;
struct crypt_params_verity; struct crypt_params_verity;

View File

@@ -88,6 +88,14 @@ out:
return r; 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, static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr,
off_t data_block, size_t data_block_size, off_t data_block, size_t data_block_size,
off_t hash_block, size_t hash_block_size, off_t hash_block, size_t hash_block_size,
@@ -102,16 +110,23 @@ static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr,
size_t hash_per_block = 1 << get_bits_down(hash_block_size / digest_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); 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 blocks_to_write = (blocks + hash_per_block - 1) / hash_per_block;
off_t seek_rd, seek_wr;
size_t left_bytes; size_t left_bytes;
unsigned i; unsigned i;
int r; 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."); log_dbg("Cannot seek to requested position in data device.");
return -EIO; 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."); log_dbg("Cannot seek to requested position in hash device.");
return -EIO; return -EIO;
} }
@@ -205,7 +220,8 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd,
off_t hash_level_size[VERITY_MAX_LEVELS]; off_t hash_level_size[VERITY_MAX_LEVELS];
off_t data_file_blocks, s; off_t data_file_blocks, s;
size_t hash_per_block, hash_per_block_bits; 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; int levels, i, r;
log_dbg("Hash %s %s, data device %s, data blocks %" PRIu64 log_dbg("Hash %s %s, data device %s, data blocks %" PRIu64
@@ -213,15 +229,23 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd,
verify ? "verification" : "creation", hash_name, verify ? "verification" : "creation", hash_name,
data_device, data_blocks, hash_device, hash_position); 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) { if (!data_blocks) {
r = device_size(data_device, &data_device_size); r = device_size(data_device, &dev_size);
if (r < 0) if (r < 0)
return r; return r;
data_file_blocks = data_device_size / data_block_size; data_file_blocks = dev_size / data_block_size;
} else { } else
data_device_size = data_blocks * data_block_size;
data_file_blocks = data_blocks; 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); hash_per_block_bits = get_bits_down(hash_block_size / digest_size);
@@ -251,12 +275,16 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd,
if (hash_position + s < hash_position || if (hash_position + s < hash_position ||
(hash_position + s) < 0 || (hash_position + s) < 0 ||
(hash_position + s) != hash_position + s) { (hash_position + s) != hash_position + s) {
log_dbg("Hash device offset overflow."); log_err(cd, _("Device offset overflow.\n"));
return -EINVAL; return -EINVAL;
} }
hash_position += s; 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.", log_dbg("Data device size required: %" PRIu64 " bytes.",
data_device_size); data_device_size);
@@ -383,10 +411,9 @@ int VERITY_create(struct crypt_device *cd,
if (verity_hdr->salt_size > 256) if (verity_hdr->salt_size > 256)
return -EINVAL; return -EINVAL;
if (verity_hdr->hash_block_size > pgsize || if (verity_hdr->data_block_size > pgsize)
verity_hdr->data_block_size > pgsize) log_err(cd, _("WARNING: Kernel cannot activate device if data "
log_err(cd, _("WARNING: Kernel cannot activate device if block " "block size exceeds page size (%u).\n"), pgsize);
"size exceeds page size (%u).\n"), pgsize);
return VERITY_create_or_verify_hash(cd, 0, return VERITY_create_or_verify_hash(cd, 0,
verity_hdr->hash_type, verity_hdr->hash_type,

View File

@@ -22,6 +22,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include <inttypes.h> #include <inttypes.h>
#include <popt.h> #include <popt.h>
#include <limits.h> #include <limits.h>
@@ -126,9 +127,11 @@ static int _prepare_format(struct crypt_params_verity *params,
params->salt_size = 0; params->salt_size = 0;
params->salt = NULL; params->salt = NULL;
} else if (salt_string) { } else if (salt_string) {
params->salt_size = strlen(salt_string) / 2; if (hex_to_bytes(salt_string, salt_bytes) * 2 != strlen(salt_string)) {
if (hex_to_bytes(salt_string, salt_bytes) != params->salt_size) log_err(_("Invalid salt string specified.\n"));
return -EINVAL; return -EINVAL;
}
params->salt_size = strlen(salt_string) / 2;
params->salt = salt_bytes; params->salt = salt_bytes;
} else } else
params->salt_size = DEFAULT_VERITY_SALT_SIZE; params->salt_size = DEFAULT_VERITY_SALT_SIZE;
@@ -201,7 +204,9 @@ static int _activate(const char *dm_device,
goto out; goto out;
hash_size = crypt_get_volume_key_size(cd); 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; r = -EINVAL;
goto out; goto out;
} }
@@ -528,8 +533,8 @@ int main(int argc, const char **argv)
char *endp; char *endp;
errno = 0; errno = 0;
ull_value = strtoull(popt_tmp, &endp, 0); ull_value = strtoull(popt_tmp, &endp, 10);
if (*endp || !*popt_tmp || if (*endp || !*popt_tmp || !isdigit(*popt_tmp) ||
(errno == ERANGE && ull_value == ULLONG_MAX) || (errno == ERANGE && ull_value == ULLONG_MAX) ||
(errno != 0 && ull_value == 0)) (errno != 0 && ull_value == 0))
r = POPT_ERROR_BADNUMBER; r = POPT_ERROR_BADNUMBER;
@@ -540,6 +545,8 @@ int main(int argc, const char **argv)
break; break;
case 2: case 2:
hash_start = ull_value * 512; hash_start = ull_value * 512;
if (hash_start / 512 != ull_value)
r = POPT_ERROR_BADNUMBER;
break; break;
} }
@@ -584,6 +591,12 @@ int main(int argc, const char **argv)
poptGetInvocationName(popt_context)); 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) { if (opt_debug) {
opt_verbose = 1; opt_verbose = 1;
crypt_set_debug_level(-1); crypt_set_debug_level(-1);