xref: /freebsd/usr.sbin/binmiscctl/binmiscctl.c (revision dad64f0e)
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 
46 enum cmd {
47 	CMD_ADD = 0,
48 	CMD_REMOVE,
49 	CMD_DISABLE,
50 	CMD_ENABLE,
51 	CMD_LOOKUP,
52 	CMD_LIST,
53 };
54 
55 extern char *__progname;
56 
57 typedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
58 
59 int add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
60 int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
61 int noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
62 
63 static 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] \\\n"
79 		"\t\t[--pre-open]"
80 	},
81 	{
82 		CMD_REMOVE,
83 		"remove",
84 		name_cmd,
85 		"Remove a binary image activator (requires 'root' privilege)",
86 		"<name>"
87 	},
88 	{
89 		CMD_DISABLE,
90 		"disable",
91 		name_cmd,
92 		"Disable a binary image activator (requires 'root' privilege)",
93 		"<name>"
94 	},
95 	{
96 		CMD_ENABLE,
97 		"enable",
98 		name_cmd,
99 		"Enable a binary image activator (requires 'root' privilege)",
100 		"<name>"
101 	},
102 	{
103 		CMD_LOOKUP,
104 		"lookup",
105 		name_cmd,
106 		"Lookup a binary image activator",
107 		"<name>"
108 	},
109 	{
110 		CMD_LIST,
111 		"list",
112 		noname_cmd,
113 		"List all the binary image activators",
114 		""
115 	},
116 };
117 
118 static const struct option
119 add_opts[] = {
120 	{ "set-enabled",	no_argument,		NULL,	'e' },
121 	{ "interpreter",	required_argument,	NULL,	'i' },
122 	{ "mask",		required_argument,	NULL,	'M' },
123 	{ "magic",		required_argument,	NULL,	'm' },
124 	{ "offset",		required_argument,	NULL,	'o' },
125 	{ "size",		required_argument,	NULL,	's' },
126 	{ "pre-open",		no_argument,		NULL,	'p' },
127 	{ NULL,			0,			NULL,	0   }
128 };
129 
130 static char const *cmd_sysctl_name[] = {
131 	IBE_SYSCTL_NAME_ADD,
132 	IBE_SYSCTL_NAME_REMOVE,
133 	IBE_SYSCTL_NAME_DISABLE,
134 	IBE_SYSCTL_NAME_ENABLE,
135 	IBE_SYSCTL_NAME_LOOKUP,
136 	IBE_SYSCTL_NAME_LIST
137 };
138 
139 static void __dead2
140 usage(const char *format, ...)
141 {
142 	va_list args;
143 	size_t i;
144 	int error = 0;
145 
146 	va_start(args, format);
147 	if (format) {
148 		vfprintf(stderr, format, args);
149 		error = -1;
150 	}
151 	va_end(args);
152 	fprintf(stderr, "\n");
153 	fprintf(stderr, "usage: %s command [args...]\n\n", __progname);
154 
155 	for(i = 0; i < nitems(cmds); i++) {
156 		fprintf(stderr, "%s:\n", cmds[i].desc);
157 		fprintf(stderr, "\t%s %s %s\n\n", __progname, cmds[i].name,
158 		    cmds[i].args);
159 	}
160 
161 	exit (error);
162 }
163 
164 static void __dead2
165 fatal(const char *format, ...)
166 {
167 	va_list args;
168 
169 	va_start(args, format);
170 	if (format)
171 		vfprintf(stderr, format, args);
172 	fprintf(stderr, "\n");
173 
174 	exit(-1);
175 }
176 
177 static void
178 getoptstr(char *str, size_t size, const char *argname)
179 {
180 	if (strlen(optarg) > size)
181 		usage("'%s' too large", argname);
182 	strlcpy(str, optarg, size);
183 }
184 
185 static void
186 printxbe(ximgact_binmisc_entry_t *xbe)
187 {
188 	uint32_t i, flags = xbe->xbe_flags;
189 
190 	if (xbe->xbe_version != IBE_VERSION) {
191 		fprintf(stderr, "Error: XBE version mismatch\n");
192 		return;
193 	}
194 
195 	printf("name: %s\n", xbe->xbe_name);
196 	printf("interpreter: %s\n", xbe->xbe_interpreter);
197 	printf("flags: %s%s%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "",
198 	    (flags & IBF_USE_MASK) ? "USE_MASK " : "",
199 	    (flags & IBF_PRE_OPEN) ? "PRE_OPEN " : "");
200 	printf("magic size: %u\n", xbe->xbe_msize);
201 	printf("magic offset: %u\n", xbe->xbe_moffset);
202 
203 	printf("magic: ");
204 	for(i = 0; i < xbe->xbe_msize;  i++) {
205 		if (i && !(i % 12))
206 			printf("\n       ");
207 		else
208 			if (i && !(i % 4))
209 				printf(" ");
210 		printf("0x%02x ", xbe->xbe_magic[i]);
211 	}
212 	printf("\n");
213 
214 	if (flags & IBF_USE_MASK) {
215 		printf("mask:  ");
216 		for(i = 0; i < xbe->xbe_msize;  i++) {
217 			if (i && !(i % 12))
218 				printf("\n       ");
219 			else
220 				if (i && !(i % 4))
221 					printf(" ");
222 			printf("0x%02x ", xbe->xbe_mask[i]);
223 		}
224 		printf("\n");
225 	}
226 
227 	printf("\n");
228 }
229 
230 static int
231 demux_cmd(__unused int argc, char *const argv[])
232 {
233 	size_t i;
234 
235 	optind = 1;
236 	optreset = 1;
237 
238 	for(i = 0; i < nitems(cmds); i++) {
239 		if (!strcasecmp(cmds[i].name, argv[0])) {
240 			return (i);
241 		}
242 	}
243 
244 	/* Unknown command */
245 	return (-1);
246 }
247 
248 static int
249 strlit2bin_cpy(uint8_t *d, char *s, size_t size)
250 {
251 	int c;
252 	size_t cnt = 0;
253 
254 	while((c = *s++) != '\0') {
255 		if (c == '\\') {
256 			/* Do '\' escapes. */
257 			switch (*s) {
258 			case '\\':
259 				*d++ = '\\';
260 				break;
261 
262 			case 'x':
263 				s++;
264 				c = toupper(*s++);
265 				*d = (c - (isdigit(c) ? '0' : ('A' - 10))) << 4;
266 				c = toupper(*s++);
267 				*d++ |= c - (isdigit(c) ? '0' : ('A' - 10));
268 				break;
269 
270 			default:
271 				return (-1);
272 			}
273 		} else
274 			*d++ = c;
275 
276 		if (++cnt > size)
277 			return (-1);
278 	}
279 
280 	return (cnt);
281 }
282 
283 int
284 add_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
285 {
286 	int ch;
287 	char *magic = NULL, *mask = NULL;
288 	int sz;
289 
290 	if (argc == 0)
291 		usage("Required argument missing\n");
292 	if (strlen(argv[0]) > IBE_NAME_MAX)
293 		usage("'%s' string length longer than IBE_NAME_MAX (%d)",
294 		    IBE_NAME_MAX);
295 	strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
296 
297 	while ((ch = getopt_long(argc, argv, "epi:m:M:o:s:", add_opts, NULL))
298 	    != -1) {
299 
300 		switch(ch) {
301 		case 'i':
302 			getoptstr(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX,
303 			    "interpreter");
304 			break;
305 
306 		case 'm':
307 			free(magic);
308 			magic = strdup(optarg);
309 			break;
310 
311 		case 'M':
312 			free(mask);
313 			mask = strdup(optarg);
314 			xbe->xbe_flags |= IBF_USE_MASK;
315 			break;
316 
317 		case 'e':
318 			xbe->xbe_flags |= IBF_ENABLED;
319 			break;
320 
321 		case 'o':
322 			xbe->xbe_moffset = atol(optarg);
323 			break;
324 
325 		case 's':
326 			xbe->xbe_msize = atol(optarg);
327 			if (xbe->xbe_msize == 0 ||
328 			    xbe->xbe_msize > IBE_MAGIC_MAX)
329 				usage("Error: Not valid '--size' value. "
330 				    "(Must be > 0 and < %u.)\n",
331 				    xbe->xbe_msize);
332 			break;
333 
334 		case 'p':
335 			xbe->xbe_flags |= IBF_PRE_OPEN;
336 			break;
337 
338 		default:
339 			usage("Unknown argument: '%c'", ch);
340 		}
341 	}
342 
343 	if (xbe->xbe_msize == 0) {
344 		if (NULL != magic)
345 			free(magic);
346 		if (NULL != mask)
347 			free(mask);
348 		usage("Error: Missing '--size' argument");
349 	}
350 
351 	if (NULL != magic) {
352 		if (xbe->xbe_msize == 0) {
353 			if (magic)
354 				free(magic);
355 			if (mask)
356 				free(mask);
357 			usage("Error: Missing magic size argument");
358 		}
359 		sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX);
360 		free(magic);
361 		if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) {
362 			if (mask)
363 				free(mask);
364 			usage("Error: invalid magic argument");
365 		}
366 		if (mask) {
367 			sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX);
368 			free(mask);
369 			if (sz == -1 || (uint32_t)sz != xbe->xbe_msize)
370 				usage("Error: invalid mask argument");
371 		}
372 	} else {
373 		if (mask)
374 			free(mask);
375 		usage("Error: Missing magic argument");
376 	}
377 
378 	if (!strnlen(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX)) {
379 		usage("Error: Missing 'interpreter' argument");
380 	}
381 
382 	return (0);
383 }
384 
385 int
386 name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
387 {
388 	if (argc == 0)
389 		usage("Required argument missing\n");
390 	if (strlen(argv[0]) > IBE_NAME_MAX)
391 		usage("'%s' string length longer than IBE_NAME_MAX (%d)",
392 		    IBE_NAME_MAX);
393 	strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
394 
395 	return (0);
396 }
397 
398 int
399 noname_cmd(__unused int argc, __unused char *argv[],
400     __unused ximgact_binmisc_entry_t *xbe)
401 {
402 
403 	return (0);
404 }
405 
406 int
407 main(int argc, char **argv)
408 {
409 	int error = 0, cmd = -1;
410 	ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL;
411 	ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL;
412 	size_t xbe_in_sz = 0;
413 	size_t xbe_out_sz = 0, *xbe_out_szp = NULL;
414 	uint32_t i;
415 
416 	if (modfind(KMOD_NAME) == -1) {
417 		if (kldload(KMOD_NAME) == -1)
418 			fatal("Can't load %s kernel module: %s",
419 			    KMOD_NAME, strerror(errno));
420 	}
421 
422 	bzero(&xbe_in, sizeof(xbe_in));
423 	bzero(&xbe_out, sizeof(xbe_out));
424 	xbe_in.xbe_version = IBE_VERSION;
425 
426 	if (argc < 2)
427 		usage("Error: requires at least one argument");
428 
429 	argc--, argv++;
430 	cmd = demux_cmd(argc, argv);
431 	if (cmd < 0)
432 		usage("Error: Unknown command \"%s\"", argv[0]);
433 	argc--, argv++;
434 
435 	error = (*cmds[cmd].func)(argc, argv, &xbe_in);
436 	if (error)
437 		usage("Can't parse command-line for '%s' command",
438 		    cmds[cmd].name);
439 
440 	if (cmd != CMD_LIST) {
441 		xbe_inp = &xbe_in;
442 		xbe_in_sz = sizeof(xbe_in);
443 	} else
444 		xbe_out_szp = &xbe_out_sz;
445 	if (cmd == CMD_LOOKUP) {
446 		xbe_out_sz = sizeof(xbe_out);
447 		xbe_outp = &xbe_out;
448 		xbe_out_szp = &xbe_out_sz;
449 	}
450 
451 	error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp,
452 	    xbe_inp, xbe_in_sz);
453 
454 	if (error)
455 		switch(errno) {
456 		case EINVAL:
457 			usage("Invalid interpreter name or --interpreter, "
458 			    "--magic, --mask, or --size argument value");
459 			break;
460 
461 		case EEXIST:
462 			usage("'%s' is not unique in activator list",
463 			    xbe_in.xbe_name);
464 			break;
465 
466 		case ENOENT:
467 			usage("'%s' is not found in activator list",
468 			    xbe_in.xbe_name);
469 			break;
470 
471 		case ENOSPC:
472 			fatal("Fatal: no more room in the activator list "
473 			    "(limited to %d enties)", IBE_MAX_ENTRIES);
474 			break;
475 
476 		case EPERM:
477 			usage("Insufficient privileges for '%s' command",
478 			    cmds[cmd].name);
479 			break;
480 
481 		default:
482 			fatal("Fatal: sysctlbyname() returned: %s",
483 			    strerror(errno));
484 			break;
485 		}
486 
487 
488 	if (cmd == CMD_LOOKUP)
489 		printxbe(xbe_outp);
490 
491 	if (cmd == CMD_LIST && xbe_out_sz > 0) {
492 		xbe_outp = malloc(xbe_out_sz);
493 		if (!xbe_outp)
494 			fatal("Fatal: out of memory");
495 		while(1) {
496 			size_t osize = xbe_out_sz;
497 			error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp,
498 			    &xbe_out_sz, NULL, 0);
499 
500 			if (error == -1 && errno == ENOMEM &&
501 			    xbe_out_sz == osize) {
502 				/*
503 				 * Buffer too small. Increase it by one
504 				 * entry.
505 				 */
506 				xbe_out_sz += sizeof(xbe_out);
507 				xbe_outp = realloc(xbe_outp, xbe_out_sz);
508 				if (!xbe_outp)
509 					fatal("Fatal: out of memory");
510 			} else
511 				break;
512 		}
513 		if (error) {
514 			free(xbe_outp);
515 			fatal("Fatal: %s", strerror(errno));
516 		}
517 		for(i = 0; i < howmany(xbe_out_sz, sizeof(xbe_out)); i++)
518 			printxbe(&xbe_outp[i]);
519 	}
520 
521 	return (error);
522 }
523