1/*-
2 * Copyright (c) 2013 Stacey D. Son
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/types.h>
32#include <sys/imgact_binmisc.h>
33#include <sys/linker.h>
34#include <sys/module.h>
35#include <sys/sysctl.h>
36
37#include <ctype.h>
38#include <errno.h>
39#include <getopt.h>
40#include <stdio.h>
41#include <stdarg.h>
42#include <stdint.h>
43#include <stdlib.h>
44#include <string.h>
45
46enum cmd {
47	CMD_ADD = 0,
48	CMD_REMOVE,
49	CMD_DISABLE,
50	CMD_ENABLE,
51	CMD_LOOKUP,
52	CMD_LIST,
53};
54
55extern char *__progname;
56
57typedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
58
59int add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
60int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
61int noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
62
63static const struct {
64	const int token;
65	const char *name;
66	cmd_func_t func;
67	const char *desc;
68	const char *args;
69} cmds[] = {
70	{
71		CMD_ADD,
72		"add",
73		add_cmd,
74		"Add a new binary image activator (requires 'root' privilege)",
75		"<name> --interpreter <path_and_arguments> \\\n"
76		"\t\t--magic <magic_bytes> [--mask <mask_bytes>] \\\n"
77		"\t\t--size <magic_size> [--offset <magic_offset>] \\\n"
78		"\t\t[--set-enabled]"
79	},
80	{
81		CMD_REMOVE,
82		"remove",
83		name_cmd,
84		"Remove a binary image activator (requires 'root' privilege)",
85		"<name>"
86	},
87	{
88		CMD_DISABLE,
89		"disable",
90		name_cmd,
91		"Disable a binary image activator (requires 'root' privilege)",
92		"<name>"
93	},
94	{
95		CMD_ENABLE,
96		"enable",
97		name_cmd,
98		"Enable a binary image activator (requires 'root' privilege)",
99		"<name>"
100	},
101	{
102		CMD_LOOKUP,
103		"lookup",
104		name_cmd,
105		"Lookup a binary image activator",
106		"<name>"
107	},
108	{
109		CMD_LIST,
110		"list",
111		noname_cmd,
112		"List all the binary image activators",
113		""
114	},
115};
116
117static const struct option
118add_opts[] = {
119	{ "set-enabled",	no_argument,		NULL,	'e' },
120	{ "interpreter",	required_argument,	NULL,	'i' },
121	{ "mask",		required_argument,	NULL,	'M' },
122	{ "magic",		required_argument,	NULL,	'm' },
123	{ "offset",		required_argument,	NULL,	'o' },
124	{ "size",		required_argument,	NULL,	's' },
125	{ NULL,			0,			NULL,	0   }
126};
127
128static char const *cmd_sysctl_name[] = {
129	IBE_SYSCTL_NAME_ADD,
130	IBE_SYSCTL_NAME_REMOVE,
131	IBE_SYSCTL_NAME_DISABLE,
132	IBE_SYSCTL_NAME_ENABLE,
133	IBE_SYSCTL_NAME_LOOKUP,
134	IBE_SYSCTL_NAME_LIST
135};
136
137static void __dead2
138usage(const char *format, ...)
139{
140	va_list args;
141	size_t i;
142	int error = 0;
143
144	va_start(args, format);
145	if (format) {
146		vfprintf(stderr, format, args);
147		error = -1;
148	}
149	va_end(args);
150	fprintf(stderr, "\n");
151	fprintf(stderr, "usage: %s command [args...]\n\n", __progname);
152
153	for(i = 0; i < nitems(cmds); i++) {
154		fprintf(stderr, "%s:\n", cmds[i].desc);
155		fprintf(stderr, "\t%s %s %s\n\n", __progname, cmds[i].name,
156		    cmds[i].args);
157	}
158
159	exit (error);
160}
161
162static void __dead2
163fatal(const char *format, ...)
164{
165	va_list args;
166
167	va_start(args, format);
168	if (format)
169		vfprintf(stderr, format, args);
170	fprintf(stderr, "\n");
171
172	exit(-1);
173}
174
175static void
176getoptstr(char *str, size_t size, const char *argname)
177{
178	if (strlen(optarg) > size)
179		usage("'%s' too large", argname);
180	strlcpy(str, optarg, size);
181}
182
183static void
184printxbe(ximgact_binmisc_entry_t *xbe)
185{
186	uint32_t i, flags = xbe->xbe_flags;
187
188	if (xbe->xbe_version != IBE_VERSION) {
189		fprintf(stderr, "Error: XBE version mismatch\n");
190		return;
191	}
192
193	printf("name: %s\n", xbe->xbe_name);
194	printf("interpreter: %s\n", xbe->xbe_interpreter);
195	printf("flags: %s%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "",
196	    (flags & IBF_USE_MASK) ? "USE_MASK " : "");
197	printf("magic size: %u\n", xbe->xbe_msize);
198	printf("magic offset: %u\n", xbe->xbe_moffset);
199
200	printf("magic: ");
201	for(i = 0; i < xbe->xbe_msize;  i++) {
202		if (i && !(i % 12))
203			printf("\n       ");
204		else
205			if (i && !(i % 4))
206				printf(" ");
207		printf("0x%02x ", xbe->xbe_magic[i]);
208	}
209	printf("\n");
210
211	if (flags & IBF_USE_MASK) {
212		printf("mask:  ");
213		for(i = 0; i < xbe->xbe_msize;  i++) {
214			if (i && !(i % 12))
215				printf("\n       ");
216			else
217				if (i && !(i % 4))
218					printf(" ");
219			printf("0x%02x ", xbe->xbe_mask[i]);
220		}
221		printf("\n");
222	}
223
224	printf("\n");
225}
226
227static int
228demux_cmd(__unused int argc, char *const argv[])
229{
230	size_t i;
231
232	optind = 1;
233	optreset = 1;
234
235	for(i = 0; i < nitems(cmds); i++) {
236		if (!strcasecmp(cmds[i].name, argv[0])) {
237			return (i);
238		}
239	}
240
241	/* Unknown command */
242	return (-1);
243}
244
245static int
246strlit2bin_cpy(uint8_t *d, char *s, size_t size)
247{
248	int c;
249	size_t cnt = 0;
250
251	while((c = *s++) != '\0') {
252		if (c == '\\') {
253			/* Do '\' escapes. */
254			switch (*s) {
255			case '\\':
256				*d++ = '\\';
257				break;
258
259			case 'x':
260				s++;
261				c = toupper(*s++);
262				*d = (c - (isdigit(c) ? '0' : ('A' - 10))) << 4;
263				c = toupper(*s++);
264				*d++ |= c - (isdigit(c) ? '0' : ('A' - 10));
265				break;
266
267			default:
268				return (-1);
269			}
270		} else
271			*d++ = c;
272
273		if (++cnt > size)
274			return (-1);
275	}
276
277	return (cnt);
278}
279
280int
281add_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
282{
283	int ch;
284	char *magic = NULL, *mask = NULL;
285	int sz;
286
287	if (argc == 0)
288		usage("Required argument missing\n");
289	if (strlen(argv[0]) > IBE_NAME_MAX)
290		usage("'%s' string length longer than IBE_NAME_MAX (%d)",
291		    IBE_NAME_MAX);
292	strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
293
294	while ((ch = getopt_long(argc, argv, "ei:m:M:o:s:", add_opts, NULL))
295	    != -1) {
296
297		switch(ch) {
298		case 'i':
299			getoptstr(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX,
300			    "interpreter");
301			break;
302
303		case 'm':
304			free(magic);
305			magic = strdup(optarg);
306			break;
307
308		case 'M':
309			free(mask);
310			mask = strdup(optarg);
311			xbe->xbe_flags |= IBF_USE_MASK;
312			break;
313
314		case 'e':
315			xbe->xbe_flags |= IBF_ENABLED;
316			break;
317
318		case 'o':
319			xbe->xbe_moffset = atol(optarg);
320			break;
321
322		case 's':
323			xbe->xbe_msize = atol(optarg);
324			if (xbe->xbe_msize == 0 ||
325			    xbe->xbe_msize > IBE_MAGIC_MAX)
326				usage("Error: Not valid '--size' value. "
327				    "(Must be > 0 and < %u.)\n",
328				    xbe->xbe_msize);
329			break;
330
331		default:
332			usage("Unknown argument: '%c'", ch);
333		}
334	}
335
336	if (xbe->xbe_msize == 0) {
337		if (NULL != magic)
338			free(magic);
339		if (NULL != mask)
340			free(mask);
341		usage("Error: Missing '--size' argument");
342	}
343
344	if (NULL != magic) {
345		if (xbe->xbe_msize == 0) {
346			if (magic)
347				free(magic);
348			if (mask)
349				free(mask);
350			usage("Error: Missing magic size argument");
351		}
352		sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX);
353		free(magic);
354		if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) {
355			if (mask)
356				free(mask);
357			usage("Error: invalid magic argument");
358		}
359		if (mask) {
360			sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX);
361			free(mask);
362			if (sz == -1 || (uint32_t)sz != xbe->xbe_msize)
363				usage("Error: invalid mask argument");
364		}
365	} else {
366		if (mask)
367			free(mask);
368		usage("Error: Missing magic argument");
369	}
370
371	if (!strnlen(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX)) {
372		usage("Error: Missing 'interpreter' argument");
373	}
374
375	return (0);
376}
377
378int
379name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
380{
381	if (argc == 0)
382		usage("Required argument missing\n");
383	if (strlen(argv[0]) > IBE_NAME_MAX)
384		usage("'%s' string length longer than IBE_NAME_MAX (%d)",
385		    IBE_NAME_MAX);
386	strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
387
388	return (0);
389}
390
391int
392noname_cmd(__unused int argc, __unused char *argv[],
393    __unused ximgact_binmisc_entry_t *xbe)
394{
395
396	return (0);
397}
398
399int
400main(int argc, char **argv)
401{
402	int error = 0, cmd = -1;
403	ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL;
404	ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL;
405	size_t xbe_in_sz = 0;
406	size_t xbe_out_sz = 0, *xbe_out_szp = NULL;
407	uint32_t i;
408
409	if (modfind(KMOD_NAME) == -1) {
410		if (kldload(KMOD_NAME) == -1)
411			fatal("Can't load %s kernel module: %s",
412			    KMOD_NAME, strerror(errno));
413	}
414
415	bzero(&xbe_in, sizeof(xbe_in));
416	bzero(&xbe_out, sizeof(xbe_out));
417	xbe_in.xbe_version = IBE_VERSION;
418
419	if (argc < 2)
420		usage("Error: requires at least one argument");
421
422	argc--, argv++;
423	cmd = demux_cmd(argc, argv);
424	if (cmd < 0)
425		usage("Error: Unknown command \"%s\"", argv[0]);
426	argc--, argv++;
427
428	error = (*cmds[cmd].func)(argc, argv, &xbe_in);
429	if (error)
430		usage("Can't parse command-line for '%s' command",
431		    cmds[cmd].name);
432
433	if (cmd != CMD_LIST) {
434		xbe_inp = &xbe_in;
435		xbe_in_sz = sizeof(xbe_in);
436	} else
437		xbe_out_szp = &xbe_out_sz;
438	if (cmd == CMD_LOOKUP) {
439		xbe_out_sz = sizeof(xbe_out);
440		xbe_outp = &xbe_out;
441		xbe_out_szp = &xbe_out_sz;
442	}
443
444	error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp,
445	    xbe_inp, xbe_in_sz);
446
447	if (error)
448		switch(errno) {
449		case EINVAL:
450			usage("Invalid interpreter name or --interpreter, "
451			    "--magic, --mask, or --size argument value");
452			break;
453
454		case EEXIST:
455			usage("'%s' is not unique in activator list",
456			    xbe_in.xbe_name);
457			break;
458
459		case ENOENT:
460			usage("'%s' is not found in activator list",
461			    xbe_in.xbe_name);
462			break;
463
464		case ENOSPC:
465			fatal("Fatal: no more room in the activator list "
466			    "(limited to %d enties)", IBE_MAX_ENTRIES);
467			break;
468
469		case EPERM:
470			usage("Insufficient privileges for '%s' command",
471			    cmds[cmd].name);
472			break;
473
474		default:
475			fatal("Fatal: sysctlbyname() returned: %s",
476			    strerror(errno));
477			break;
478		}
479
480
481	if (cmd == CMD_LOOKUP)
482		printxbe(xbe_outp);
483
484	if (cmd == CMD_LIST && xbe_out_sz > 0) {
485		xbe_outp = malloc(xbe_out_sz);
486		if (!xbe_outp)
487			fatal("Fatal: out of memory");
488		while(1) {
489			size_t osize = xbe_out_sz;
490			error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp,
491			    &xbe_out_sz, NULL, 0);
492
493			if (error == -1 && errno == ENOMEM &&
494			    xbe_out_sz == osize) {
495				/*
496				 * Buffer too small. Increase it by one
497				 * entry.
498				 */
499				xbe_out_sz += sizeof(xbe_out);
500				xbe_outp = realloc(xbe_outp, xbe_out_sz);
501				if (!xbe_outp)
502					fatal("Fatal: out of memory");
503			} else
504				break;
505		}
506		if (error) {
507			free(xbe_outp);
508			fatal("Fatal: %s", strerror(errno));
509		}
510		for(i = 0; i < howmany(xbe_out_sz, sizeof(xbe_out)); i++)
511			printxbe(&xbe_outp[i]);
512	}
513
514	return (error);
515}
516