X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Fverity%2Fverity_hash.c;h=3f25e9152825f788b26e9ff383bdef05b97f0000;hb=4f990d5a74898e494f3b7043a79a9af904e253ab;hp=0e2c48cbdc591111811ce7e3d8ade46263a28afb;hpb=db51a343de360fd05b59edb299801200f1032612;p=platform%2Fupstream%2Fcryptsetup.git diff --git a/lib/verity/verity_hash.c b/lib/verity/verity_hash.c index 0e2c48c..3f25e91 100644 --- a/lib/verity/verity_hash.c +++ b/lib/verity/verity_hash.c @@ -3,17 +3,18 @@ * * Copyright (C) 2012, Red Hat, Inc. All rights reserved. * - * 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. + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * You should have received a copy of the GNU Lesser General Public + * License along with this file; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ @@ -31,7 +32,7 @@ static unsigned get_bits_up(size_t u) { unsigned i = 0; - while ((1 << i) < u) + while ((1U << i) < u) i++; return i; } @@ -39,7 +40,7 @@ static unsigned get_bits_up(size_t u) static unsigned get_bits_down(size_t u) { unsigned i = 0; - while ((u >> i) > 1) + while ((u >> i) > 1U) i++; return i; } @@ -49,8 +50,10 @@ static int verify_zero(struct crypt_device *cd, FILE *wr, size_t bytes) char block[bytes]; size_t i; - if (fread(block, bytes, 1, wr) != 1) + if (fread(block, bytes, 1, wr) != 1) { + log_dbg("EIO while reading spare area."); return -EIO; + } for (i = 0; i < bytes; i++) if (block[i]) { log_err(cd, _("Spare area is not zeroed at position %" PRIu64 ".\n"), @@ -86,6 +89,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) + 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, @@ -100,14 +111,26 @@ 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; - int i, r; + unsigned i; + int r; + + 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, data_block * data_block_size, SEEK_SET)) + 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; + } memset(left_block, 0, hash_block_size); while (blocks_to_write--) { @@ -116,8 +139,10 @@ static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr, if (!blocks) break; blocks--; - if (fread(data_buffer, data_block_size, 1, rd) != 1) + if (fread(data_buffer, data_block_size, 1, rd) != 1) { + log_dbg("Cannot read data device block."); return -EIO; + } if (verify_hash_block(hash_name, version, calculated_digest, digest_size, @@ -128,16 +153,20 @@ static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr, if (!wr) break; if (verify) { - if (fread(read_digest, digest_size, 1, wr) != 1) + if (fread(read_digest, digest_size, 1, wr) != 1) { + log_dbg("Cannot read digest form hash device."); return -EIO; + } if (memcmp(read_digest, calculated_digest, digest_size)) { log_err(cd, _("Verification failed at position %" PRIu64 ".\n"), ftello(rd) - data_block_size); return -EPERM; } } else { - if (fwrite(calculated_digest, digest_size, 1, wr) != 1) + if (fwrite(calculated_digest, digest_size, 1, wr) != 1) { + log_dbg("Cannot write digest to hash device."); return -EIO; + } } if (version == 0) { left_bytes -= digest_size; @@ -147,8 +176,10 @@ static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr, r = verify_zero(cd, wr, digest_size_full - digest_size); if (r) return r; - } else if (fwrite(left_block, digest_size_full - digest_size, 1, wr) != 1) + } else if (fwrite(left_block, digest_size_full - digest_size, 1, wr) != 1) { + log_dbg("Cannot write spare area to hash device."); return -EIO; + } } left_bytes -= digest_size_full; } @@ -158,8 +189,10 @@ static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr, r = verify_zero(cd , wr, left_bytes); if (r) return r; - } else if (fwrite(left_block, left_bytes, 1, wr) != 1) + } else if (fwrite(left_block, left_bytes, 1, wr) != 1) { + log_dbg("Cannot write remaining spare area to hash device."); return -EIO; + } } } @@ -170,8 +203,8 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, int verify, int version, const char *hash_name, - const char *hash_device, - const char *data_device, + struct device *hash_device, + struct device *data_device, size_t hash_block_size, size_t data_block_size, off_t data_blocks, @@ -187,26 +220,37 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, off_t hash_level_block[VERITY_MAX_LEVELS]; 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; + size_t hash_per_block_bits; + 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 ", hash_device %s, offset %" PRIu64 ".", verify ? "verification" : "creation", hash_name, - data_device, data_blocks, hash_device, hash_position); + device_path(data_device), data_blocks, + device_path(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; + 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); - hash_per_block = 1 << hash_per_block_bits; if (!hash_per_block_bits) return -EINVAL; @@ -216,6 +260,7 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, (data_file_blocks - 1) >> (hash_per_block_bits * levels)) levels++; } + log_dbg("Using %d hash levels.", levels); if (levels > VERITY_MAX_LEVELS) { log_err(cd, _("Too many tree levels for verity volume.\n")); @@ -225,28 +270,38 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, for (i = levels - 1; i >= 0; i--) { hash_level_block[i] = hash_position; // verity position of block data_file_blocks at level i - s = data_file_blocks >> (i * hash_per_block_bits); - s = (s + hash_per_block - 1) / hash_per_block; + s = (data_file_blocks + ((off_t)1 << ((i + 1) * hash_per_block_bits)) - 1) >> ((i + 1) * hash_per_block_bits); hash_level_size[i] = s; - if (hash_position + s < hash_position || - (hash_position + s) < 0 || - (hash_position + s) != hash_position + s) { - log_dbg("Hash device offset overflow."); + if ((hash_position + s) < hash_position || + (hash_position + s) < 0) { + log_err(cd, _("Device offset overflow.\n")); return -EINVAL; } hash_position += s; } - data_file = fopen(data_device, "r"); + 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); + data_file = fopen(device_path(data_device), "r"); if (!data_file) { - log_err(cd, _("Cannot open device %s.\n"), data_device); + log_err(cd, _("Cannot open device %s.\n"), + device_path(data_device) + ); r = -EIO; goto out; } - hash_file = fopen(hash_device, verify ? "r" : "r+"); + log_dbg("Hash device size required: %" PRIu64 " bytes.", + hash_device_size); + hash_file = fopen(device_path(hash_device), verify ? "r" : "r+"); if (!hash_file) { - log_err(cd, _("Cannot open device %s.\n"), hash_device); + log_err(cd, _("Cannot open device %s.\n"), + device_path(hash_device)); r = -EIO; goto out; } @@ -263,8 +318,10 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, if (r) goto out; } else { - hash_file_2 = fopen(hash_device, "r"); + hash_file_2 = fopen(device_path(hash_device), "r"); if (!hash_file_2) { + log_err(cd, _("Cannot open device %s.\n"), + device_path(hash_device)); r = -EIO; goto out; } @@ -282,34 +339,38 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd, if (levels) r = create_or_verify(cd, hash_file, NULL, hash_level_block[levels - 1], hash_block_size, - 0, 0, + 0, hash_block_size, 1, version, hash_name, verify, calculated_digest, digest_size, salt, salt_size); else r = create_or_verify(cd, data_file, NULL, 0, data_block_size, - 0, 0, + 0, hash_block_size, data_file_blocks, version, hash_name, verify, calculated_digest, digest_size, salt, salt_size); - - if (r == -EPERM) { - log_err(cd, _("Verification of data area failed.\n")); - goto out; - } else if (!r) - log_dbg("Verification of data area succeeded."); - - /* root hash verification */ +out: if (verify) { - r = memcmp(root_hash, calculated_digest, digest_size) ? -EPERM : 0; if (r) - log_err(cd, _("Verification of root hash failed.\n")); - else - log_dbg("Verification of root hash succeeded."); + log_err(cd, _("Verification of data area failed.\n")); + else { + log_dbg("Verification of data area succeeded."); + r = memcmp(root_hash, calculated_digest, digest_size) ? -EPERM : 0; + if (r) + log_err(cd, _("Verification of root hash failed.\n")); + else + log_dbg("Verification of root hash succeeded."); + } } else { - fsync(fileno(hash_file)); - memcpy(root_hash, calculated_digest, digest_size); + if (r == -EIO) + log_err(cd, _("Input/output error while creating hash area.\n")); + else if (r) + log_err(cd, _("Creation of hash area failed.\n")); + else { + fsync(fileno(hash_file)); + memcpy(root_hash, calculated_digest, digest_size); + } } -out: + if (data_file) fclose(data_file); if (hash_file) @@ -320,16 +381,14 @@ out: /* Verify verity device using userspace crypto backend */ int VERITY_verify(struct crypt_device *cd, struct crypt_params_verity *verity_hdr, - const char *data_device, - const char *hash_device, const char *root_hash, size_t root_hash_size) { return VERITY_create_or_verify_hash(cd, 1, verity_hdr->hash_type, verity_hdr->hash_name, - hash_device, - data_device, + crypt_metadata_device(cd), + crypt_data_device(cd), verity_hdr->hash_block_size, verity_hdr->data_block_size, verity_hdr->data_size, @@ -343,26 +402,23 @@ int VERITY_verify(struct crypt_device *cd, /* Create verity hash */ int VERITY_create(struct crypt_device *cd, struct crypt_params_verity *verity_hdr, - const char *data_device, - const char *hash_device, char *root_hash, size_t root_hash_size) { - int pgsize = crypt_getpagesize(); + unsigned pgsize = crypt_getpagesize(); 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, verity_hdr->hash_name, - hash_device, - data_device, + crypt_metadata_device(cd), + crypt_data_device(cd), verity_hdr->hash_block_size, verity_hdr->data_block_size, verity_hdr->data_size,