xref: /freebsd/usr.sbin/binmiscctl/binmiscctl.c (revision 8b959dd6a3921c35395bef4a6d7ad2426a3bd88e)
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]"
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 
117 static const struct option
118 add_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 
128 static 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 
137 static void __dead2
138 usage(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 
162 static void __dead2
163 fatal(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 
175 static void
176 getoptstr(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 
183 static void
184 printxbe(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 
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, "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 
378 int
379 name_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 
391 int
392 noname_cmd(__unused int argc, __unused char *argv[],
393     __unused ximgact_binmisc_entry_t *xbe)
394 {
395 
396 	return (0);
397 }
398 
399 int
400 main(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