xref: /freebsd/usr.sbin/binmiscctl/binmiscctl.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
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 
43 enum cmd {
44 	CMD_ADD = 0,
45 	CMD_REMOVE,
46 	CMD_DISABLE,
47 	CMD_ENABLE,
48 	CMD_LOOKUP,
49 	CMD_LIST,
50 };
51 
52 extern char *__progname;
53 
54 typedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
55 
56 int add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
57 int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
58 int noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
59 
60 static 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 
115 static const struct option
116 add_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 
127 static 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 
136 static void __dead2
137 usage(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 
161 static void __dead2
162 fatal(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 
174 static void
175 getoptstr(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 
182 static void
183 printxbe(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 
227 static int
228 demux_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 
245 static int
246 strlit2bin_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 
280 int
281 add_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 
382 int
383 name_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 
395 int
396 noname_cmd(__unused int argc, __unused char *argv[],
397     __unused ximgact_binmisc_entry_t *xbe)
398 {
399 
400 	return (0);
401 }
402 
403 int
404 main(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