blob: 931cb076f1e5a4eed669cdf446a8b261cd21e59b [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* fs-verity userspace tool
*
* Copyright (C) 2018, Google, Inc.
*/
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "fsverity_api.h"
#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
static const struct fsverity_hash_alg {
const char *name;
int digest_size;
} fsverity_hash_algs[] = {
[FS_VERITY_ALG_SHA256] = {
.name = "sha256",
.digest_size = 32,
},
[FS_VERITY_ALG_CRC32] = {
.name = "crc32",
.digest_size = 4,
},
};
static void show_hash_algs(void)
{
size_t i;
fprintf(stderr, "Available hash algorithms:");
for (i = 0; i < ARRAY_SIZE(fsverity_hash_algs); i++) {
if (fsverity_hash_algs[i].name)
fprintf(stderr, " %s", fsverity_hash_algs[i].name);
}
fprintf(stderr, "\n");
}
static const struct fsverity_hash_alg *find_hash_alg(const char *name)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(fsverity_hash_algs); i++) {
if (fsverity_hash_algs[i].name &&
!strcmp(name, fsverity_hash_algs[i].name))
return &fsverity_hash_algs[i];
}
return NULL;
}
static int hex2bin_char(char c)
{
if (c >= 'a' && c <= 'f')
return 10 + c - 'a';
if (c >= 'A' && c <= 'F')
return 10 + c - 'A';
if (c >= '0' && c <= '9')
return c - '0';
return -1;
}
static bool parse_hex_digest(const char *hex, __u8 *bin, size_t bin_len)
{
size_t i;
if (strlen(hex) != 2 * bin_len)
return false;
for (i = 0; i < bin_len; i++) {
int hi = hex2bin_char(hex[i * 2]);
int lo = hex2bin_char(hex[i * 2 + 1]);
if (hi < 0 || lo < 0)
return false;
bin[i] = (hi << 4) | lo;
}
return true;
}
enum {
OPT_HASH,
};
static void usage(void)
{
const char * const usage_str =
"Usage: fsverity enable FILE\n"
" fsverity set_measurement [--hash=HASH] FILE EXPECTED_MEASUREMENT\n"
"\n"
"EXPECTED_MEASUREMENT must be given as a hex string.\n"
"The default HASH algorithm is sha256.\n"
;
fputs(usage_str, stderr);
show_hash_algs();
exit(2);
}
static int fsverity_enable(int argc, char *argv[])
{
int fd;
if (argc != 2)
usage();
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Can't open %s: %m\n", argv[1]);
return 1;
}
if (ioctl(fd, FS_IOC_ENABLE_VERITY, NULL)) {
fprintf(stderr, "FS_IOC_ENABLE_VERITY: %m\n");
return 1;
}
close(fd);
return 0;
}
static int fsverity_set_measurement(int argc, char *argv[])
{
static const struct option longopts[] = {
{"hash", required_argument, NULL, OPT_HASH},
{NULL, 0, NULL, 0},
};
const struct fsverity_hash_alg *alg =
&fsverity_hash_algs[FS_VERITY_ALG_SHA256];
int c;
int fd;
struct fsverity_measurement *measurement;
while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
switch (c) {
case OPT_HASH:
alg = find_hash_alg(optarg);
if (!alg) {
fprintf(stderr,
"Unknown hash algorithm: '%s'\n",
optarg);
show_hash_algs();
return 2;
}
break;
default:
usage();
}
}
argv += optind;
argc -= optind;
if (argc != 2)
usage();
fd = open(argv[0], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Can't open %s: %m\n", argv[0]);
return 1;
}
measurement = calloc(1, sizeof(*measurement) + alg->digest_size);
measurement->digest_algorithm = alg - &fsverity_hash_algs[0];
measurement->digest_size = alg->digest_size;
if (!parse_hex_digest(argv[1], measurement->digest, alg->digest_size)) {
fprintf(stderr,
"Invalid EXPECTED_MEASUREMENT hex string. Expected %u-character hex string for hash algorithm '%s'\n",
alg->digest_size * 2, alg->name);
return 2;
}
if (ioctl(fd, FS_IOC_SET_VERITY_MEASUREMENT, measurement)) {
fprintf(stderr, "FS_IOC_SET_VERITY_MEASUREMENT: %m\n");
return 1;
}
close(fd);
return 0;
}
static const struct {
const char *name;
int (*func)(int argc, char *argv[]);
} commands[] = {
{ "enable", fsverity_enable },
{ "set_measurement", fsverity_set_measurement },
};
int main(int argc, char *argv[])
{
size_t i;
if (argc < 2)
usage();
for (i = 0; i < ARRAY_SIZE(commands); i++) {
if (!strcmp(argv[1], commands[i].name))
return commands[i].func(argc - 1, argv + 1);
}
usage();
}