1/* $NetBSD: veriexecctl.c,v 1.34 2009/03/16 13:38:09 lukem Exp $ */ 2 3/*- 4 * Copyright 2005 Elad Efrat <elad@NetBSD.org> 5 * Copyright 2005 Brett Lymn <blymn@netbsd.org> 6 * 7 * All rights reserved. 8 * 9 * This code has been donated to The NetBSD Foundation by the Author. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. The name of the author may not be used to endorse or promote products 17 * derived from this software withough specific prior written permission 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * 31 */ 32 33#include <sys/param.h> 34#include <sys/statvfs.h> 35#include <sys/verified_exec.h> 36 37#include <stdio.h> 38#include <stdlib.h> 39#include <stdbool.h> 40#include <string.h> 41#include <fcntl.h> 42#include <unistd.h> 43#include <err.h> 44#include <errno.h> 45#include <sys/ioctl.h> 46 47#include <prop/proplib.h> 48 49#include "veriexecctl.h" 50 51#define VERIEXEC_DEVICE "/dev/veriexec" 52#define VERIEXEC_DEFAULT_CONFIG "/etc/signatures" 53 54#define STATUS_STRING(status) ((status) == FINGERPRINT_NOTEVAL ? \ 55 "not evaluated" : \ 56 (status) == FINGERPRINT_VALID ? \ 57 "valid" : \ 58 (status) == FINGERPRINT_NOMATCH ? \ 59 "mismatch" : \ 60 "<unknown>") 61 62extern int yyparse(void); 63 64int gfd, verbose = 0, error = EXIT_SUCCESS; 65size_t line = 0; 66 67__dead static void 68usage(void) 69{ 70 const char *progname = getprogname(); 71 72 (void)fprintf(stderr, "Usage:\n" 73 "%s [-ekv] load [signature_file]\n" 74 "%s delete <file | mount_point>\n" 75 "%s query <file>\n" 76 "%s dump\n" 77 "%s flush\n", progname, progname, progname, progname, progname); 78 79 exit(1); 80} 81 82static void 83flags2str(uint8_t flags, char *buf, size_t len) 84{ 85 uint8_t all; 86 87 all = (VERIEXEC_DIRECT | VERIEXEC_INDIRECT | VERIEXEC_FILE | 88 VERIEXEC_UNTRUSTED); 89 if (flags & ~all) { 90 if (verbose) 91 warnx("Contaminated flags `0x%x'", (flags & ~all)); 92 return; 93 } 94 95 while (flags) { 96 if (*buf) 97 strlcat(buf, ", ", len); 98 99 if (flags & VERIEXEC_DIRECT) { 100 strlcat(buf, "direct", len); 101 flags &= ~VERIEXEC_DIRECT; 102 continue; 103 } 104 if (flags & VERIEXEC_INDIRECT) { 105 strlcat(buf, "indirect", len); 106 flags &= ~VERIEXEC_INDIRECT; 107 continue; 108 } 109 if (flags & VERIEXEC_FILE) { 110 strlcat(buf, "file", len); 111 flags &= ~VERIEXEC_FILE; 112 continue; 113 } 114 if (flags & VERIEXEC_UNTRUSTED) { 115 strlcat(buf, "untrusted", len); 116 flags &= ~VERIEXEC_UNTRUSTED; 117 continue; 118 } 119 } 120} 121 122static void 123print_query(prop_dictionary_t qp, char *file) 124{ 125 struct statvfs sv; 126 const char *v; 127 size_t i; 128 uint8_t u8; 129 char buf[64]; 130 131 if (statvfs(file, &sv) != 0) 132 err(1, "Can't statvfs() `%s'\n", file); 133 134 printf("Filename: %s\n", file); 135 printf("Mount: %s\n", sv.f_mntonname); 136 prop_dictionary_get_uint8(qp, "entry-type", &u8); 137 memset(buf, 0, sizeof(buf)); 138 flags2str(u8, buf, sizeof(buf)); 139 printf("Entry flags: %s\n", buf); 140 prop_dictionary_get_uint8(qp, "status", &u8); 141 printf("Entry status: %s\n", STATUS_STRING(u8)); 142 printf("Fingerprint algorithm: %s\n", dict_gets(qp, "fp-type")); 143 printf("Fingerprint: "); 144 v = dict_getd(qp, "fp"); 145 for (i = 0; i < prop_data_size(prop_dictionary_get(qp, "fp")); i++) 146 printf("%02x", v[i] & 0xff); 147 printf("\n"); 148} 149 150static char * 151escape(const char *s) 152{ 153 char *q, *p; 154 size_t len; 155 156 len = strlen(s); 157 if (len >= MAXPATHLEN) 158 return (NULL); 159 160 len *= 2; 161 q = p = calloc(1, len + 1); 162 163 while (*s) { 164 if (*s == ' ' || *s == '\t') 165 *p++ = '\\'; 166 167 *p++ = *s++; 168 } 169 170 return (q); 171} 172 173static void 174print_entry(prop_dictionary_t entry) 175{ 176 char *file, *fp; 177 const uint8_t *v; 178 size_t len, i; 179 uint8_t u8; 180 char flags[64]; 181 182 /* Get fingerprint in ASCII. */ 183 len = prop_data_size(prop_dictionary_get(entry, "fp")); 184 len *= 2; 185 fp = calloc(1, len + 1); 186 v = dict_getd(entry, "fp"); 187 for (i = 0; i < len; i++) 188 snprintf(fp, len + 1, "%s%02x", fp, v[i] & 0xff); 189 190 /* Get flags. */ 191 memset(flags, 0, sizeof(flags)); 192 prop_dictionary_get_uint8(entry, "entry-type", &u8); 193 flags2str(u8, flags, sizeof(flags)); 194 195 file = escape(dict_gets(entry, "file")); 196 printf("%s %s %s %s\n", file, dict_gets(entry, "fp-type"), fp, flags); 197 free(file); 198 free(fp); 199} 200 201int 202main(int argc, char **argv) 203{ 204 extern bool keep_filename, eval_on_load; 205 int c; 206 207 setprogname(argv[0]); 208 209 while ((c = getopt(argc, argv, "ekv")) != -1) 210 switch (c) { 211 case 'e': 212 eval_on_load = true; 213 break; 214 215 case 'k': 216 keep_filename = true; 217 break; 218 219 case 'v': 220 verbose = 1; 221 break; 222 223 default: 224 usage(); 225 } 226 227 argc -= optind; 228 argv += optind; 229 230 if ((gfd = open(VERIEXEC_DEVICE, O_RDWR, 0)) == -1) 231 err(1, "Cannot open `%s'", VERIEXEC_DEVICE); 232 233 /* 234 * Handle the different commands we can do. 235 */ 236 if ((argc == 1 || argc == 2) && strcasecmp(argv[0], "load") == 0) { 237 extern FILE *yyin; 238 const char *file; 239 int lfd; 240 241 if (argc != 2) 242 file = VERIEXEC_DEFAULT_CONFIG; 243 else 244 file = argv[1]; 245 246 lfd = open(file, O_RDONLY|O_EXLOCK, 0); 247 if (lfd == -1) 248 err(1, "Cannot open `%s'", argv[1]); 249 250 yyin = fdopen(lfd, "r"); 251 252 yyparse(); 253 254 (void)fclose(yyin); 255 } else if (argc == 2 && strcasecmp(argv[0], "delete") == 0) { 256 prop_dictionary_t dp; 257 struct stat sb; 258 259 if (stat(argv[1], &sb) == -1) 260 err(1, "Can't stat `%s'", argv[1]); 261 262 /* 263 * If it's a regular file, remove it. If it's a directory, 264 * remove the entire table. If it's neither, abort. 265 */ 266 if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) 267 errx(1, "`%s' is not a regular file or directory.", 268 argv[1]); 269 270 dp = prop_dictionary_create(); 271 dict_sets(dp, "file", argv[1]); 272 273 if (prop_dictionary_send_ioctl(dp, gfd, VERIEXEC_DELETE) != 0) 274 err(1, "Error deleting `%s'", argv[1]); 275 276 prop_object_release(dp); 277 } else if (argc == 2 && strcasecmp(argv[0], "query") == 0) { 278 prop_dictionary_t qp, rqp; 279 int r; 280 281 qp = prop_dictionary_create(); 282 283 dict_sets(qp, "file", argv[1]); 284 285 r = prop_dictionary_sendrecv_ioctl(qp, gfd, VERIEXEC_QUERY, 286 &rqp); 287 if (r) { 288 if (r == ENOENT) 289 errx(1, "No Veriexec entry for `%s'", argv[1]); 290 291 err(1, "Error querying `%s'", argv[1]); 292 } 293 294 if (rqp != NULL) { 295 print_query(rqp, argv[1]); 296 prop_object_release(rqp); 297 } 298 299 prop_object_release(qp); 300 } else if (argc == 1 && strcasecmp(argv[0], "dump") == 0) { 301 prop_array_t entries; 302 size_t nentries, i; 303 304 if (prop_array_recv_ioctl(gfd, VERIEXEC_DUMP, 305 &entries) == -1) 306 err(1, "Error dumping tables"); 307 308 nentries = prop_array_count(entries); 309 for (i = 0; i < nentries; i++) 310 print_entry(prop_array_get(entries, i)); 311 312 prop_object_release(entries); 313 } else if (argc == 1 && strcasecmp(argv[0], "flush") == 0) { 314 if (ioctl(gfd, VERIEXEC_FLUSH) == -1) 315 err(1, "Cannot flush Veriexec database"); 316 } else 317 usage(); 318 319 (void)close(gfd); 320 return error; 321} 322