binmiscctl.c revision 278961
1306196Sjkim/*-
2110010Smarkm * Copyright (c) 2013 Stacey D. Son
3110010Smarkm * All rights reserved.
4142429Snectar *
5110010Smarkm * Redistribution and use in source and binary forms, with or without
6110010Smarkm * modification, are permitted provided that the following conditions
7110010Smarkm * are met:
8110010Smarkm * 1. Redistributions of source code must retain the above copyright
9110010Smarkm *    notice, this list of conditions and the following disclaimer.
10110010Smarkm * 2. Redistributions in binary form must reproduce the above copyright
11110010Smarkm *    notice, this list of conditions and the following disclaimer in the
12110010Smarkm *    documentation and/or other materials provided with the distribution.
13110010Smarkm *
14110010Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15110010Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16110010Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17110010Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18110010Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19110010Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20215698Ssimon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21215698Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22215698Ssimon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23215698Ssimon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24215698Ssimon * SUCH DAMAGE.
25110010Smarkm *
26110010Smarkm */
27110010Smarkm
28110010Smarkm#include <sys/cdefs.h>
29110010Smarkm__FBSDID("$FreeBSD: stable/10/usr.sbin/binmiscctl/binmiscctl.c 278961 2015-02-18 16:28:55Z sbruno $");
30110010Smarkm
31110010Smarkm#include <ctype.h>
32110010Smarkm#include <errno.h>
33110010Smarkm#include <getopt.h>
34110010Smarkm#include <stdio.h>
35110010Smarkm#include <stdarg.h>
36110010Smarkm#include <stdint.h>
37110010Smarkm#include <stdlib.h>
38110010Smarkm#include <string.h>
39110010Smarkm
40110010Smarkm#include <sys/types.h>
41276864Sjkim#include <sys/imgact_binmisc.h>
42276864Sjkim#include <sys/linker.h>
43110010Smarkm#include <sys/sysctl.h>
44110010Smarkm
45215698Ssimonenum cmd {
46215698Ssimon	CMD_ADD = 0,
47215698Ssimon	CMD_REMOVE,
48215698Ssimon	CMD_DISABLE,
49142429Snectar	CMD_ENABLE,
50215698Ssimon	CMD_LOOKUP,
51142429Snectar	CMD_LIST,
52142429Snectar};
53276864Sjkim
54276864Sjkimextern char *__progname;
55276864Sjkim
56110010Smarkmtypedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
57276864Sjkim
58276864Sjkimint add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
59276864Sjkimint name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
60276864Sjkimint noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
61276864Sjkim
62276864Sjkimstatic const struct {
63215698Ssimon	const int token;
64276864Sjkim	const char *name;
65276864Sjkim	cmd_func_t func;
66276864Sjkim	const char *desc;
67276864Sjkim	const char *args;
68276864Sjkim} cmds[] = {
69215698Ssimon	{
70276864Sjkim		CMD_ADD,
71110010Smarkm		"add",
72110010Smarkm		add_cmd,
73110010Smarkm		"Add a new binary image activator (requires 'root' privilege)",
74110010Smarkm		"<name> --interpreter <path_and_arguments> \\\n"
75110010Smarkm		"\t\t--magic <magic_bytes> [--mask <mask_bytes>] \\\n"
76110010Smarkm		"\t\t--size <magic_size> [--offset <magic_offset>] \\\n"
77110010Smarkm		"\t\t[--set-enabled]"
78110010Smarkm	},
79110010Smarkm	{
80110010Smarkm		CMD_REMOVE,
81110010Smarkm		"remove",
82110010Smarkm		name_cmd,
83110010Smarkm		"Remove a binary image activator (requires 'root' privilege)",
84110010Smarkm		"<name>"
85110010Smarkm	},
86110010Smarkm	{
87110010Smarkm		CMD_DISABLE,
88110010Smarkm		"disable",
89110010Smarkm		name_cmd,
90110010Smarkm		"Disable a binary image activator (requires 'root' privilege)",
91110010Smarkm		"<name>"
92110010Smarkm	},
93110010Smarkm	{
94110010Smarkm		CMD_ENABLE,
95110010Smarkm		"enable",
96110010Smarkm		name_cmd,
97110010Smarkm		"Enable a binary image activator (requires 'root' privilege)",
98110010Smarkm		"<name>"
99110010Smarkm	},
100110010Smarkm	{
101110010Smarkm		CMD_LOOKUP,
102110010Smarkm		"lookup",
103110010Smarkm		name_cmd,
104110010Smarkm		"Lookup a binary image activator",
105110010Smarkm		"<name>"
106110010Smarkm	},
107110010Smarkm	{
108110010Smarkm		CMD_LIST,
109110010Smarkm		"list",
110110010Smarkm		noname_cmd,
111110010Smarkm		"List all the binary image activators",
112110010Smarkm		""
113110010Smarkm	},
114110010Smarkm};
115110010Smarkm
116110010Smarkmstatic const struct option
117110010Smarkmadd_opts[] = {
118110010Smarkm	{ "set-enabled",	no_argument,		NULL,	'e' },
119110010Smarkm	{ "interpreter",	required_argument,	NULL,	'i' },
120110010Smarkm	{ "mask",		required_argument,	NULL,	'M' },
121110010Smarkm	{ "magic",		required_argument,	NULL,	'm' },
122110010Smarkm	{ "offset",		required_argument,	NULL,	'o' },
123110010Smarkm	{ "size",		required_argument,	NULL,	's' },
124110010Smarkm	{ NULL,			0,			NULL,	0   }
125110010Smarkm};
126110010Smarkm
127110010Smarkmstatic char const *cmd_sysctl_name[] = {
128110010Smarkm	IBE_SYSCTL_NAME_ADD,
129110010Smarkm	IBE_SYSCTL_NAME_REMOVE,
130110010Smarkm	IBE_SYSCTL_NAME_DISABLE,
131110010Smarkm	IBE_SYSCTL_NAME_ENABLE,
132110010Smarkm	IBE_SYSCTL_NAME_LOOKUP,
133142429Snectar	IBE_SYSCTL_NAME_LIST
134110010Smarkm};
135110010Smarkm
136306196Sjkimstatic void
137215698Ssimonusage(const char *format, ...)
138215698Ssimon{
139215698Ssimon	va_list args;
140215698Ssimon	size_t i;
141110010Smarkm	int error = 0;
142110010Smarkm
143110010Smarkm	va_start(args, format);
144110010Smarkm	if (format) {
145110010Smarkm		vfprintf(stderr, format, args);
146110010Smarkm		error = -1;
147215698Ssimon	}
148110010Smarkm	va_end(args);
149110010Smarkm	fprintf(stderr, "\n");
150110010Smarkm	fprintf(stderr, "usage: %s command [args...]\n\n", __progname);
151110010Smarkm
152110010Smarkm	for(i = 0; i < ( sizeof (cmds) / sizeof (cmds[0])); i++) {
153110010Smarkm		fprintf(stderr, "%s:\n", cmds[i].desc);
154142429Snectar		fprintf(stderr, "\t%s %s %s\n\n", __progname, cmds[i].name,
155110010Smarkm		    cmds[i].args);
156110010Smarkm	}
157110010Smarkm
158	exit (error);
159}
160
161static void
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\n", (flags & IBF_ENABLED) ? "ENABLED " : "",
195	    (flags & IBF_USE_MASK) ? "USE_MASK " : "");
196	printf("magic size: %u\n", xbe->xbe_msize);
197	printf("magic offset: %u\n", xbe->xbe_moffset);
198
199	printf("magic: ");
200	for(i = 0; i < xbe->xbe_msize;  i++) {
201		if (i && !(i % 12))
202			printf("\n       ");
203		else
204			if (i && !(i % 4))
205				printf(" ");
206		printf("0x%02x ", xbe->xbe_magic[i]);
207	}
208	printf("\n");
209
210	if (flags & IBF_USE_MASK) {
211		printf("mask:  ");
212		for(i = 0; i < xbe->xbe_msize;  i++) {
213			if (i && !(i % 12))
214				printf("\n       ");
215			else
216				if (i && !(i % 4))
217					printf(" ");
218			printf("0x%02x ", xbe->xbe_mask[i]);
219		}
220		printf("\n");
221	}
222
223	printf("\n");
224}
225
226static int
227demux_cmd(__unused int argc, char *const argv[])
228{
229	size_t i;
230
231	optind = 1;
232	optreset = 1;
233
234	for(i = 0; i < ( sizeof (cmds) / sizeof (cmds[0])); i++) {
235		if (!strcasecmp(cmds[i].name, argv[0])) {
236			return (i);
237		}
238	}
239
240	/* Unknown command */
241	return (-1);
242}
243
244static int
245strlit2bin_cpy(uint8_t *d, char *s, size_t size)
246{
247	int c;
248	size_t cnt = 0;
249
250	while((c = *s++) != '\0') {
251		if (c == '\\') {
252			/* Do '\' escapes. */
253			switch (*s) {
254			case '\\':
255				*d++ = '\\';
256				break;
257
258			case 'x':
259				s++;
260				c = toupper(*s++);
261				*d = (c - (isdigit(c) ? '0' : ('A' - 10))) << 4;
262				c = toupper(*s++);
263				*d++ |= c - (isdigit(c) ? '0' : ('A' - 10));
264				break;
265
266			default:
267				return (-1);
268			}
269		} else
270			*d++ = c;
271
272		if (++cnt > size)
273			return (-1);
274	}
275
276	return (cnt);
277}
278
279int
280add_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
281{
282	int ch;
283	char *magic = NULL, *mask = NULL;
284	int sz;
285
286	if (strlen(argv[0]) > IBE_NAME_MAX)
287		usage("'%s' string length longer than IBE_NAME_MAX (%d)",
288		    IBE_NAME_MAX);
289	strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
290
291	while ((ch = getopt_long(argc, argv, "ei:m:M:o:s:", add_opts, NULL))
292	    != -1) {
293
294		switch(ch) {
295		case 'i':
296			getoptstr(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX,
297			    "interpreter");
298			break;
299
300		case 'm':
301			magic = strdup(optarg);
302			break;
303
304		case 'M':
305			mask = strdup(optarg);
306			xbe->xbe_flags |= IBF_USE_MASK;
307			break;
308
309		case 'e':
310			xbe->xbe_flags |= IBF_ENABLED;
311			break;
312
313		case 'o':
314			xbe->xbe_moffset = atol(optarg);
315			break;
316
317		case 's':
318			xbe->xbe_msize = atol(optarg);
319			if (xbe->xbe_msize == 0 ||
320			    xbe->xbe_msize > IBE_MAGIC_MAX)
321				usage("Error: Not valid '--size' value. "
322				    "(Must be > 0 and < %u.)\n",
323				    xbe->xbe_msize);
324			break;
325
326		default:
327			usage("Unknown argument: '%c'", ch);
328		}
329	}
330
331	if (xbe->xbe_msize == 0) {
332		if (NULL != magic)
333			free(magic);
334		if (NULL != mask)
335			free(mask);
336		usage("Error: Missing '--size' argument");
337	}
338
339	if (NULL != magic) {
340		if (xbe->xbe_msize == 0) {
341			if (magic)
342				free(magic);
343			if (mask)
344				free(mask);
345			usage("Error: Missing magic size argument");
346		}
347		sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX);
348		free(magic);
349		if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) {
350			if (mask)
351				free(mask);
352			usage("Error: invalid magic argument");
353		}
354		if (mask) {
355			sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX);
356			free(mask);
357			if (sz == -1 || (uint32_t)sz != xbe->xbe_msize)
358				usage("Error: invalid mask argument");
359		}
360	} else {
361		if (mask)
362			free(mask);
363		usage("Error: Missing magic argument");
364	}
365
366	if (!xbe->xbe_interpreter) {
367		usage("Error: Missing 'interpreter' argument");
368	}
369
370	return (0);
371}
372
373int
374name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
375{
376	if (argc == 0)
377		usage("Required argument missing\n");
378	if (strlen(argv[0]) > IBE_NAME_MAX)
379		usage("'%s' string length longer than IBE_NAME_MAX (%d)",
380		    IBE_NAME_MAX);
381	strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
382
383	return (0);
384}
385
386int
387noname_cmd(__unused int argc, __unused char *argv[],
388    __unused ximgact_binmisc_entry_t *xbe)
389{
390
391	return (0);
392}
393
394int
395main(int argc, char **argv)
396{
397	int error = 0, cmd = -1;
398	ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL;
399	ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL;
400	size_t xbe_in_sz = 0;
401	size_t xbe_out_sz = 0, *xbe_out_szp = NULL;
402	uint32_t i;
403
404	if (kldfind(KMOD_NAME) == -1) {
405		if (kldload(KMOD_NAME) == -1)
406			fatal("Can't load %s kernel module: %s",
407			    KMOD_NAME, strerror(errno));
408	}
409
410	bzero(&xbe_in, sizeof(xbe_in));
411	bzero(&xbe_out, sizeof(xbe_out));
412	xbe_in.xbe_version = IBE_VERSION;
413
414	if (argc < 2)
415		usage("Error: requires at least one argument");
416
417	argc--, argv++;
418	cmd = demux_cmd(argc, argv);
419	if (cmd == -1)
420		usage("Error: Unknown command \"%s\"", argv[0]);
421	argc--, argv++;
422
423	error = (*cmds[cmd].func)(argc, argv, &xbe_in);
424	if (error)
425		usage("Can't parse command-line for '%s' command",
426		    cmds[cmd].name);
427
428	if (cmd != CMD_LIST) {
429		xbe_inp = &xbe_in;
430		xbe_in_sz = sizeof(xbe_in);
431	} else
432		xbe_out_szp = &xbe_out_sz;
433	if (cmd == CMD_LOOKUP) {
434		xbe_out_sz = sizeof(xbe_out);
435		xbe_outp = &xbe_out;
436		xbe_out_szp = &xbe_out_sz;
437	}
438
439	error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp,
440	    xbe_inp, xbe_in_sz);
441
442	if (error)
443		switch(errno) {
444		case EINVAL:
445			usage("Invalid interpreter name or --interpreter, "
446			    "--magic, --mask, or --size argument value");
447			break;
448
449		case EEXIST:
450			usage("'%s' is not unique in activator list",
451			    xbe_in.xbe_name);
452			break;
453
454		case ENOENT:
455			usage("'%s' is not found in activator list",
456			    xbe_in.xbe_name);
457			break;
458
459		case ENOSPC:
460			fatal("Fatal: no more room in the activator list "
461			    "(limited to %d enties)", IBE_MAX_ENTRIES);
462			break;
463
464		case EPERM:
465			usage("Insufficient privileges for '%s' command",
466			    cmds[cmd].name);
467			break;
468
469		default:
470			fatal("Fatal: sysctlbyname() returned: %s",
471			    strerror(errno));
472			break;
473		}
474
475
476	if (cmd == CMD_LOOKUP)
477		printxbe(xbe_outp);
478
479	if (cmd == CMD_LIST && xbe_out_sz > 0) {
480		xbe_outp = malloc(xbe_out_sz);
481		if (!xbe_outp)
482			fatal("Fatal: out of memory");
483		while(1) {
484			size_t osize = xbe_out_sz;
485			error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp,
486			    &xbe_out_sz, NULL, 0);
487
488			if (error == -1 && errno == ENOMEM &&
489			    xbe_out_sz == osize) {
490				/*
491				 * Buffer too small. Increase it by one
492				 * entry.
493				 */
494				xbe_out_sz += sizeof(xbe_out);
495				xbe_outp = realloc(xbe_outp, xbe_out_sz);
496				if (!xbe_outp)
497					fatal("Fatal: out of memory");
498			} else
499				break;
500		}
501		if (error) {
502			free(xbe_outp);
503			fatal("Fatal: %s", strerror(errno));
504		}
505		for(i = 0; i < (xbe_out_sz / sizeof(xbe_out)); i++)
506			printxbe(&xbe_outp[i]);
507	}
508
509	return (error);
510}
511