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