From: Milan Broz Date: Sun, 10 Jun 2012 22:04:39 +0000 (+0200) Subject: Check various number limits. X-Git-Tag: upstream/1.6~271^2~2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fupstream%2Fcryptsetup.git;a=commitdiff_plain;h=62f334cfa58f9a21b24facdec35fc99e4cb1a4a0 Check various number limits. --- diff --git a/lib/setup.c b/lib/setup.c index 69e6736..e7190eb 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -70,7 +70,7 @@ struct crypt_device { /* 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 */ @@ -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) 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; @@ -1046,7 +1049,7 @@ static int _crypt_format_verity(struct crypt_device *cd, 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)) { @@ -1057,6 +1060,22 @@ static int _crypt_format_verity(struct crypt_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; @@ -1064,7 +1083,7 @@ static int _crypt_format_verity(struct crypt_device *cd, 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; @@ -1072,9 +1091,13 @@ static int _crypt_format_verity(struct crypt_device *cd, } 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) diff --git a/lib/verity/verity.c b/lib/verity/verity.c index 388a391..8cd3b36 100644 --- a/lib/verity/verity.c +++ b/lib/verity/verity.c @@ -88,6 +88,11 @@ int VERITY_read_sb(struct crypt_device *cd, 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); @@ -141,14 +146,15 @@ int VERITY_read_sb(struct crypt_device *cd, 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; } @@ -157,6 +163,12 @@ int VERITY_read_sb(struct crypt_device *cd, 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)) { diff --git a/lib/verity/verity.h b/lib/verity/verity.h index e37ed76..671c120 100644 --- a/lib/verity/verity.h +++ b/lib/verity/verity.h @@ -23,6 +23,10 @@ #include #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; diff --git a/lib/verity/verity_hash.c b/lib/verity/verity_hash.c index c40bccf..bfc82e0 100644 --- a/lib/verity/verity_hash.c +++ b/lib/verity/verity_hash.c @@ -88,6 +88,14 @@ out: 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, @@ -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 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; } @@ -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 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 @@ -213,15 +229,23 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, 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); @@ -251,12 +275,16 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, 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); @@ -383,10 +411,9 @@ int VERITY_create(struct crypt_device *cd, 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, diff --git a/src/veritysetup.c b/src/veritysetup.c index 7c05791..c84f206 100644 --- a/src/veritysetup.c +++ b/src/veritysetup.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -126,9 +127,11 @@ static int _prepare_format(struct crypt_params_verity *params, 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; @@ -201,7 +204,9 @@ static int _activate(const char *dm_device, 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; } @@ -528,8 +533,8 @@ int main(int argc, const char **argv) 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; @@ -540,6 +545,8 @@ int main(int argc, const char **argv) break; case 2: hash_start = ull_value * 512; + if (hash_start / 512 != ull_value) + r = POPT_ERROR_BADNUMBER; break; } @@ -584,6 +591,12 @@ int main(int argc, const char **argv) 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);