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/types.h>
29#include <sys/imgact_binmisc.h>
30#include <sys/linker.h>
31#include <sys/module.h>
32#include <sys/sysctl.h>
33
34#include <ctype.h>
35#include <errno.h>
36#include <getopt.h>
37#include <stdio.h>
38#include <stdarg.h>
39#include <stdint.h>
40#include <stdlib.h>
41#include <string.h>
42
43enum cmd {
44	CMD_ADD = 0,
45	CMD_REMOVE,
46	CMD_DISABLE,
47	CMD_ENABLE,
48	CMD_LOOKUP,
49	CMD_LIST,
50};
51
52extern char *__progname;
53
54typedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
55
56int add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
57int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
58int noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
59
60static const struct {
61	const int token;
62	const char *name;
63	cmd_func_t func;
64	const char *desc;
65	const char *args;
66} cmds[] = {
67	{
68		CMD_ADD,
69		"add",
70		add_cmd,
71		"Add a new binary image activator (requires 'root' privilege)",
72		"<name> --interpreter <path_and_arguments> \\\n"
73		"\t\t--magic <magic_bytes> [--mask <mask_bytes>] \\\n"
74		"\t\t--size <magic_size> [--offset <magic_offset>] \\\n"
75		"\t\t[--set-enabled] \\\n"
76		"\t\t[--pre-open]"
77	},
78	{
79		CMD_REMOVE,
80		"remove",
81		name_cmd,
82		"Remove a binary image activator (requires 'root' privilege)",
83		"<name>"
84	},
85	{
86		CMD_DISABLE,
87		"disable",
88		name_cmd,
89		"Disable a binary image activator (requires 'root' privilege)",
90		"<name>"
91	},
92	{
93		CMD_ENABLE,
94		"enable",
95		name_cmd,
96		"Enable a binary image activator (requires 'root' privilege)",
97		"<name>"
98	},
99	{
100		CMD_LOOKUP,
101		"lookup",
102		name_cmd,
103		"Lookup a binary image activator",
104		"<name>"
105	},
106	{
107		CMD_LIST,
108		"list",
109		noname_cmd,
110		"List all the binary image activators",
111		""
112	},
113};
114
115static const struct option
116add_opts[] = {
117	{ "set-enabled",	no_argument,		NULL,	'e' },
118	{ "interpreter",	required_argument,	NULL,	'i' },
119	{ "mask",		required_argument,	NULL,	'M' },
120	{ "magic",		required_argument,	NULL,	'm' },
121	{ "offset",		required_argument,	NULL,	'o' },
122	{ "size",		required_argument,	NULL,	's' },
123	{ "pre-open",		no_argument,		NULL,	'p' },
124	{ NULL,			0,			NULL,	0   }
125};
126
127static char const *cmd_sysctl_name[] = {
128	IBE_SYSCTL_NAME_ADD,
129	IBE_SYSCTL_NAME_REMOVE,
130	IBE_SYSCTL_NAME_DISABLE,
131	IBE_SYSCTL_NAME_ENABLE,
132	IBE_SYSCTL_NAME_LOOKUP,
133	IBE_SYSCTL_NAME_LIST
134};
135
136static void __dead2
137usage(const char *format, ...)
138{
139	va_list args;
140	size_t i;
141	int error = 0;
142
143	va_start(args, format);
144	if (format) {
145		vfprintf(stderr, format, args);
146		error = -1;
147	}
148	va_end(args);
149	fprintf(stderr, "\n");
150	fprintf(stderr, "usage: %s command [args...]\n\n", __progname);
151
152	for(i = 0; i < nitems(cmds); i++) {
153		fprintf(stderr, "%s:\n", cmds[i].desc);
154		fprintf(stderr, "\t%s %s %s\n\n", __progname, cmds[i].name,
155		    cmds[i].args);
156	}
157
158	exit (error);
159}
160
161static void __dead2
162fatal(const char *format, ...)
163{
164	va_list args;
165
166	va_start(args, format);
167	if (format)
168		vfprintf(stderr, format, args);
169	fprintf(stderr, "\n");
170
171	exit(-1);
172}
173
174static void
175getoptstr(char *str, size_t size, const char *argname)
176{
177	if (strlen(optarg) > size)
178		usage("'%s' too large", argname);
179	strlcpy(str, optarg, size);
180}
181
182static void
183printxbe(ximgact_binmisc_entry_t *xbe)
184{
185	uint32_t i, flags = xbe->xbe_flags;
186
187	if (xbe->xbe_version != IBE_VERSION) {
188		fprintf(stderr, "Error: XBE version mismatch\n");
189		return;
190	}
191
192	printf("name: %s\n", xbe->xbe_name);
193	printf("interpreter: %s\n", xbe->xbe_interpreter);
194	printf("flags: %s%s%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "",
195	    (flags & IBF_USE_MASK) ? "USE_MASK " : "",
196	    (flags & IBF_PRE_OPEN) ? "PRE_OPEN " : "");
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, "epi: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		case 'p':
332			xbe->xbe_flags |= IBF_PRE_OPEN;
333			break;
334
335		default:
336			usage("Unknown argument: '%c'", ch);
337		}
338	}
339
340	if (xbe->xbe_msize == 0) {
341		if (NULL != magic)
342			free(magic);
343		if (NULL != mask)
344			free(mask);
345		usage("Error: Missing '--size' argument");
346	}
347
348	if (NULL != magic) {
349		if (xbe->xbe_msize == 0) {
350			if (magic)
351				free(magic);
352			if (mask)
353				free(mask);
354			usage("Error: Missing magic size argument");
355		}
356		sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX);
357		free(magic);
358		if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) {
359			if (mask)
360				free(mask);
361			usage("Error: invalid magic argument");
362		}
363		if (mask) {
364			sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX);
365			free(mask);
366			if (sz == -1 || (uint32_t)sz != xbe->xbe_msize)
367				usage("Error: invalid mask argument");
368		}
369	} else {
370		if (mask)
371			free(mask);
372		usage("Error: Missing magic argument");
373	}
374
375	if (!strnlen(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX)) {
376		usage("Error: Missing 'interpreter' argument");
377	}
378
379	return (0);
380}
381
382int
383name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
384{
385	if (argc == 0)
386		usage("Required argument missing\n");
387	if (strlen(argv[0]) > IBE_NAME_MAX)
388		usage("'%s' string length longer than IBE_NAME_MAX (%d)",
389		    IBE_NAME_MAX);
390	strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
391
392	return (0);
393}
394
395int
396noname_cmd(__unused int argc, __unused char *argv[],
397    __unused ximgact_binmisc_entry_t *xbe)
398{
399
400	return (0);
401}
402
403int
404main(int argc, char **argv)
405{
406	int error = 0, cmd = -1;
407	ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL;
408	ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL;
409	size_t xbe_in_sz = 0;
410	size_t xbe_out_sz = 0, *xbe_out_szp = NULL;
411	uint32_t i;
412
413	if (modfind(KMOD_NAME) == -1) {
414		if (kldload(KMOD_NAME) == -1)
415			fatal("Can't load %s kernel module: %s",
416			    KMOD_NAME, strerror(errno));
417	}
418
419	bzero(&xbe_in, sizeof(xbe_in));
420	bzero(&xbe_out, sizeof(xbe_out));
421	xbe_in.xbe_version = IBE_VERSION;
422
423	if (argc < 2)
424		usage("Error: requires at least one argument");
425
426	argc--, argv++;
427	cmd = demux_cmd(argc, argv);
428	if (cmd < 0)
429		usage("Error: Unknown command \"%s\"", argv[0]);
430	argc--, argv++;
431
432	error = (*cmds[cmd].func)(argc, argv, &xbe_in);
433	if (error)
434		usage("Can't parse command-line for '%s' command",
435		    cmds[cmd].name);
436
437	if (cmd != CMD_LIST) {
438		xbe_inp = &xbe_in;
439		xbe_in_sz = sizeof(xbe_in);
440	} else
441		xbe_out_szp = &xbe_out_sz;
442	if (cmd == CMD_LOOKUP) {
443		xbe_out_sz = sizeof(xbe_out);
444		xbe_outp = &xbe_out;
445		xbe_out_szp = &xbe_out_sz;
446	}
447
448	error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp,
449	    xbe_inp, xbe_in_sz);
450
451	if (error)
452		switch(errno) {
453		case EINVAL:
454			usage("Invalid interpreter name or --interpreter, "
455			    "--magic, --mask, or --size argument value");
456			break;
457
458		case EEXIST:
459			usage("'%s' is not unique in activator list",
460			    xbe_in.xbe_name);
461			break;
462
463		case ENOENT:
464			usage("'%s' is not found in activator list",
465			    xbe_in.xbe_name);
466			break;
467
468		case ENOSPC:
469			fatal("Fatal: no more room in the activator list "
470			    "(limited to %d enties)", IBE_MAX_ENTRIES);
471			break;
472
473		case EPERM:
474			usage("Insufficient privileges for '%s' command",
475			    cmds[cmd].name);
476			break;
477
478		default:
479			fatal("Fatal: sysctlbyname() returned: %s",
480			    strerror(errno));
481			break;
482		}
483
484
485	if (cmd == CMD_LOOKUP)
486		printxbe(xbe_outp);
487
488	if (cmd == CMD_LIST && xbe_out_sz > 0) {
489		xbe_outp = malloc(xbe_out_sz);
490		if (!xbe_outp)
491			fatal("Fatal: out of memory");
492		while(1) {
493			size_t osize = xbe_out_sz;
494			error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp,
495			    &xbe_out_sz, NULL, 0);
496
497			if (error == -1 && errno == ENOMEM &&
498			    xbe_out_sz == osize) {
499				/*
500				 * Buffer too small. Increase it by one
501				 * entry.
502				 */
503				xbe_out_sz += sizeof(xbe_out);
504				xbe_outp = realloc(xbe_outp, xbe_out_sz);
505				if (!xbe_outp)
506					fatal("Fatal: out of memory");
507			} else
508				break;
509		}
510		if (error) {
511			free(xbe_outp);
512			fatal("Fatal: %s", strerror(errno));
513		}
514		for(i = 0; i < howmany(xbe_out_sz, sizeof(xbe_out)); i++)
515			printxbe(&xbe_outp[i]);
516	}
517
518	return (error);
519}
520