blob: 35f126c9650943fdaf90227a6a9a454b421a94e7 [file] [log] [blame]
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* pread and pwrite want this */
#define _GNU_SOURCE 1
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <verity/dm-bht.h>
#include <verity/dm-bht-userspace.h>
#define IO_BUF_SIZE (unsigned long)(1 * 1024 * 1024)
/* 512 bytes in a sector */
#define SECTOR_SHIFT (9ULL)
int chromeos_verity(const char *alg, const char *device, unsigned blocksize,
uint64_t fs_blocks, const char *salt, const char *expected,
int warn)
{
struct dm_bht bht;
int ret, fd;
uint8_t *io_buffer;
uint8_t *hash_buffer;
size_t hash_size;
uint8_t digest[DM_BHT_MAX_DIGEST_SIZE];
uint64_t cur_block = 0;
/* blocksize better be a power of two and fit into 1 MB*/
if (IO_BUF_SIZE % blocksize != 0) {
printf("%s: blocksize %% %lu != 0\n", __func__,
IO_BUF_SIZE);
return -EINVAL;
}
/* we don't need to call dm_bht_destroy after this because we're */
/* supplying our own buffer -- in fact calling it will trigger a bogus */
/* assert */
if ((ret = dm_bht_create(&bht, fs_blocks, alg))) {
printf("%s: dm_bht_create failed %d\n", __func__, ret);
return ret;
}
if ((ret = posix_memalign((void**)&io_buffer, blocksize, IO_BUF_SIZE))) {
printf("%s: posix_memalign io_buffer failed %d\n", __func__, ret);
return ret;
}
/* we aren't going to do any automatic reading */
dm_bht_set_read_cb(&bht, dm_bht_zeroread_callback);
dm_bht_set_salt(&bht, salt);
hash_size = dm_bht_sectors(&bht) << SECTOR_SHIFT;
if ((ret = posix_memalign((void**)&hash_buffer, blocksize, hash_size))) {
printf("%s: posix_memalign hash_buffer failed %d\n", __func__, ret);
free(io_buffer);
return ret;
}
memset(hash_buffer, 0, hash_size);
dm_bht_set_buffer(&bht, hash_buffer);
fd = open(device, O_RDWR );
if (fd < 0) {
printf("%s error opening %s: %s\n", __func__, device, strerror(errno));
free(io_buffer);
free(hash_buffer);
return errno;
}
while (cur_block < fs_blocks) {
unsigned int i;
ssize_t readb;
size_t count = (fs_blocks - cur_block) * blocksize;
if (count > IO_BUF_SIZE)
count = IO_BUF_SIZE;
readb = pread(fd, io_buffer, count, cur_block * blocksize);
if (readb < 0) {
printf("%s: read returned error %s\n", __func__, strerror(errno));
close(fd);
free(io_buffer);
free(hash_buffer);
return errno;
}
for (i = 0 ; i < (count / blocksize) ; i++) {
ret = dm_bht_store_block(&bht, cur_block, io_buffer + (i * blocksize));
if (ret) {
printf("%s: dm_bht_store_block returned error %d\n", __func__, ret);
close(fd);
free(io_buffer);
free(hash_buffer);
return ret;
}
cur_block++;
}
}
free(io_buffer);
ret = dm_bht_compute(&bht);
if (ret) {
printf("%s: dm_bht_compute returned error %d\n", __func__, ret);
close(fd);
free(hash_buffer);
return ret;
}
dm_bht_root_hexdigest(&bht, digest, DM_BHT_MAX_DIGEST_SIZE);
if (warn && memcmp(digest, expected, bht.digest_size)) {
printf("Filesystem hash verification failed\n");
printf("Expected %s != %s\n",digest, expected);
free(hash_buffer);
close(fd);
return -1;
}
if (pwrite(fd, hash_buffer, hash_size, cur_block * blocksize) !=
(ssize_t)hash_size) {
printf("%s: writing out hash failed %s\n", __func__, strerror(errno));
free(hash_buffer);
close(fd);
return errno;
}
free(hash_buffer);
close(fd);
return 0;
}