NetBSD/sbin/veriexecctl/veriexecctl.c

324 lines
7.6 KiB
C

/* $NetBSD: veriexecctl.c,v 1.40 2017/01/10 20:48:12 christos Exp $ */
/*-
* Copyright 2005 Elad Efrat <elad@NetBSD.org>
* Copyright 2005 Brett Lymn <blymn@netbsd.org>
*
* All rights reserved.
*
* This code has been donated to The NetBSD Foundation by the Author.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software withough specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
*/
#include <sys/param.h>
#include <sys/statvfs.h>
#include <sys/verified_exec.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <prop/proplib.h>
#include "veriexecctl.h"
#define VERIEXEC_DEVICE "/dev/veriexec"
#define VERIEXEC_DEFAULT_CONFIG "/etc/signatures"
#define STATUS_STRING(status) ((status) == FINGERPRINT_NOTEVAL ? \
"not evaluated" : \
(status) == FINGERPRINT_VALID ? \
"valid" : \
(status) == FINGERPRINT_NOMATCH ? \
"mismatch" : \
"<unknown>")
extern int yyparse(void);
int gfd, verbose = 0, error = EXIT_SUCCESS;
size_t line = 0;
__dead static void
usage(void)
{
const char *progname = getprogname();
(void)fprintf(stderr, "Usage:\n"
"%s [-ekv] load [signature_file]\n"
"%s delete <file | mount_point>\n"
"%s query <file>\n"
"%s dump\n"
"%s flush\n", progname, progname, progname, progname, progname);
exit(1);
}
static void
flags2str(uint8_t flags, char *buf, size_t len)
{
uint8_t all;
all = (VERIEXEC_DIRECT | VERIEXEC_INDIRECT | VERIEXEC_FILE |
VERIEXEC_UNTRUSTED);
if (flags & ~all) {
if (verbose)
warnx("Contaminated flags `0x%x'", (flags & ~all));
return;
}
while (flags) {
if (*buf)
strlcat(buf, ", ", len);
if (flags & VERIEXEC_DIRECT) {
strlcat(buf, "direct", len);
flags &= ~VERIEXEC_DIRECT;
continue;
}
if (flags & VERIEXEC_INDIRECT) {
strlcat(buf, "indirect", len);
flags &= ~VERIEXEC_INDIRECT;
continue;
}
if (flags & VERIEXEC_FILE) {
strlcat(buf, "file", len);
flags &= ~VERIEXEC_FILE;
continue;
}
if (flags & VERIEXEC_UNTRUSTED) {
strlcat(buf, "untrusted", len);
flags &= ~VERIEXEC_UNTRUSTED;
continue;
}
}
}
static void
print_query(prop_dictionary_t qp, char *file)
{
struct statvfs sv;
const char *v;
size_t i;
uint8_t u8;
char buf[64];
if (statvfs(file, &sv) != 0)
err(EXIT_FAILURE, "Can't statvfs() `%s'", file);
printf("Filename: %s\n", file);
printf("Mount: %s\n", sv.f_mntonname);
prop_dictionary_get_uint8(qp, "entry-type", &u8);
memset(buf, 0, sizeof(buf));
flags2str(u8, buf, sizeof(buf));
printf("Entry flags: %s\n", buf);
prop_dictionary_get_uint8(qp, "status", &u8);
printf("Entry status: %s\n", STATUS_STRING(u8));
printf("Fingerprint algorithm: %s\n", dict_gets(qp, "fp-type"));
printf("Fingerprint: ");
v = dict_getd(qp, "fp");
for (i = 0; i < prop_data_size(prop_dictionary_get(qp, "fp")); i++)
printf("%02x", v[i] & 0xff);
printf("\n");
}
static char *
escape(const char *s)
{
char *q, *p;
size_t len;
len = strlen(s);
if (len >= MAXPATHLEN)
return (NULL);
len *= 2;
q = p = calloc(1, len + 1);
while (*s) {
if (*s == ' ' || *s == '\t')
*p++ = '\\';
*p++ = *s++;
}
return (q);
}
static void
print_entry(prop_dictionary_t entry)
{
char *file, *fp;
const uint8_t *v;
size_t len, i;
uint8_t u8;
char flags[64];
/* Get fingerprint in ASCII. */
len = prop_data_size(prop_dictionary_get(entry, "fp"));
fp = calloc(1, len*2 + 1);
v = dict_getd(entry, "fp");
for (i = 0; i < len; i++) {
snprintf(&fp[i*2], 3, "%02x", v[i] & 0xff);
}
/* Get flags. */
memset(flags, 0, sizeof(flags));
prop_dictionary_get_uint8(entry, "entry-type", &u8);
flags2str(u8, flags, sizeof(flags));
file = escape(dict_gets(entry, "file"));
printf("%s %s %s %s\n", file, dict_gets(entry, "fp-type"), fp, flags);
free(file);
free(fp);
}
int
main(int argc, char **argv)
{
extern bool keep_filename, eval_on_load;
int c;
setprogname(argv[0]);
while ((c = getopt(argc, argv, "ekv")) != -1)
switch (c) {
case 'e':
eval_on_load = true;
break;
case 'k':
keep_filename = true;
break;
case 'v':
verbose = 1;
break;
default:
usage();
}
argc -= optind;
argv += optind;
if ((gfd = open(VERIEXEC_DEVICE, O_RDWR, 0)) == -1)
err(EXIT_FAILURE, "Cannot open `%s'", VERIEXEC_DEVICE);
/*
* Handle the different commands we can do.
*/
if ((argc == 1 || argc == 2) && strcasecmp(argv[0], "load") == 0) {
extern FILE *yyin;
const char *file;
int lfd;
if (argc != 2)
file = VERIEXEC_DEFAULT_CONFIG;
else
file = argv[1];
lfd = open(file, O_RDONLY|O_EXLOCK, 0);
if (lfd == -1)
err(EXIT_FAILURE, "Cannot open `%s'", file);
yyin = fdopen(lfd, "r");
yyparse();
fclose(yyin);
if (error != EXIT_SUCCESS)
errx(1, "Cannot load '%s'", file);
} else if (argc == 2 && strcasecmp(argv[0], "delete") == 0) {
prop_dictionary_t dp;
struct stat sb;
if (stat(argv[1], &sb) == -1)
err(EXIT_FAILURE, "Can't stat `%s'", argv[1]);
/*
* If it's a regular file, remove it. If it's a directory,
* remove the entire table. If it's neither, abort.
*/
if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode))
errx(EXIT_FAILURE, "`%s' is not a regular file or directory.",
argv[1]);
dp = prop_dictionary_create();
dict_sets(dp, "file", argv[1]);
if (prop_dictionary_send_ioctl(dp, gfd, VERIEXEC_DELETE) != 0)
err(EXIT_FAILURE, "Error deleting `%s'", argv[1]);
prop_object_release(dp);
} else if (argc == 2 && strcasecmp(argv[0], "query") == 0) {
prop_dictionary_t qp, rqp;
int r;
qp = prop_dictionary_create();
dict_sets(qp, "file", argv[1]);
r = prop_dictionary_sendrecv_ioctl(qp, gfd, VERIEXEC_QUERY,
&rqp);
if (r) {
if (r == ENOENT)
errx(EXIT_FAILURE, "No Veriexec entry for `%s'", argv[1]);
err(EXIT_FAILURE, "Error querying `%s'", argv[1]);
}
if (rqp != NULL) {
print_query(rqp, argv[1]);
prop_object_release(rqp);
}
prop_object_release(qp);
} else if (argc == 1 && strcasecmp(argv[0], "dump") == 0) {
prop_array_t entries;
size_t nentries, i;
if (prop_array_recv_ioctl(gfd, VERIEXEC_DUMP,
&entries) == -1)
err(EXIT_FAILURE, "Error dumping tables");
nentries = prop_array_count(entries);
for (i = 0; i < nentries; i++)
print_entry(prop_array_get(entries, i));
prop_object_release(entries);
} else if (argc == 1 && strcasecmp(argv[0], "flush") == 0) {
if (ioctl(gfd, VERIEXEC_FLUSH) == -1)
err(EXIT_FAILURE, "Cannot flush Veriexec database");
} else
usage();
(void)close(gfd);
return error;
}