Check various number limits.
authorMilan Broz <gmazyland@gmail.com>
Sun, 10 Jun 2012 22:04:39 +0000 (00:04 +0200)
committerMilan Broz <gmazyland@gmail.com>
Sun, 10 Jun 2012 22:09:15 +0000 (00:09 +0200)
lib/setup.c
lib/verity/verity.c
lib/verity/verity.h
lib/verity/verity_hash.c
src/veritysetup.c

index 69e6736..e7190eb 100644 (file)
@@ -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)
index 388a391..8cd3b36 100644 (file)
@@ -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)) {
index e37ed76..671c120 100644 (file)
 #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;
 
index c40bccf..bfc82e0 100644 (file)
@@ -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,
index 7c05791..c84f206 100644 (file)
@@ -22,6 +22,7 @@
 #include <stdarg.h>
 #include <errno.h>
 #include <string.h>
+#include <ctype.h>
 #include <inttypes.h>
 #include <popt.h>
 #include <limits.h>
@@ -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);