1 /* $NetBSD: veriexecctl.c,v 1.39 2015/06/16 23:18:55 christos 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 62 extern int yyparse(void); 63 64 int gfd, verbose = 0, error = EXIT_SUCCESS; 65 size_t line = 0; 66 67 __dead static void 68 usage(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 82 static void 83 flags2str(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 122 static void 123 print_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(EXIT_FAILURE, "Can't statvfs() `%s'", 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 150 static char * 151 escape(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 173 static void 174 print_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 fp = calloc(1, len*2 + 1); 185 v = dict_getd(entry, "fp"); 186 for (i = 0; i < len; i++) { 187 snprintf(&fp[i*2], 3, "%02x", v[i] & 0xff); 188 } 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 201 int 202 main(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(EXIT_FAILURE, "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(EXIT_FAILURE, "Cannot open `%s'", file); 249 250 yyin = fdopen(lfd, "r"); 251 yyparse(); 252 fclose(yyin); 253 254 if (error != EXIT_SUCCESS) 255 errx(1, "Cannot load '%s'", file); 256 } else if (argc == 2 && strcasecmp(argv[0], "delete") == 0) { 257 prop_dictionary_t dp; 258 struct stat sb; 259 260 if (stat(argv[1], &sb) == -1) 261 err(EXIT_FAILURE, "Can't stat `%s'", argv[1]); 262 263 /* 264 * If it's a regular file, remove it. If it's a directory, 265 * remove the entire table. If it's neither, abort. 266 */ 267 if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) 268 errx(EXIT_FAILURE, "`%s' is not a regular file or directory.", 269 argv[1]); 270 271 dp = prop_dictionary_create(); 272 dict_sets(dp, "file", argv[1]); 273 274 if (prop_dictionary_send_ioctl(dp, gfd, VERIEXEC_DELETE) != 0) 275 err(EXIT_FAILURE, "Error deleting `%s'", argv[1]); 276 277 prop_object_release(dp); 278 } else if (argc == 2 && strcasecmp(argv[0], "query") == 0) { 279 prop_dictionary_t qp, rqp; 280 int r; 281 282 qp = prop_dictionary_create(); 283 284 dict_sets(qp, "file", argv[1]); 285 286 r = prop_dictionary_sendrecv_ioctl(qp, gfd, VERIEXEC_QUERY, 287 &rqp); 288 if (r) { 289 if (r == ENOENT) 290 errx(EXIT_FAILURE, "No Veriexec entry for `%s'", argv[1]); 291 292 err(EXIT_FAILURE, "Error querying `%s'", argv[1]); 293 } 294 295 if (rqp != NULL) { 296 print_query(rqp, argv[1]); 297 prop_object_release(rqp); 298 } 299 300 prop_object_release(qp); 301 } else if (argc == 1 && strcasecmp(argv[0], "dump") == 0) { 302 prop_array_t entries; 303 size_t nentries, i; 304 305 if (prop_array_recv_ioctl(gfd, VERIEXEC_DUMP, 306 &entries) == -1) 307 err(EXIT_FAILURE, "Error dumping tables"); 308 309 nentries = prop_array_count(entries); 310 for (i = 0; i < nentries; i++) 311 print_entry(prop_array_get(entries, i)); 312 313 prop_object_release(entries); 314 } else if (argc == 1 && strcasecmp(argv[0], "flush") == 0) { 315 if (ioctl(gfd, VERIEXEC_FLUSH) == -1) 316 err(EXIT_FAILURE, "Cannot flush Veriexec database"); 317 } else 318 usage(); 319 320 (void)close(gfd); 321 return error; 322 } 323