xref: /freebsd/usr.sbin/binmiscctl/binmiscctl.c (revision 9268022b74279434ed6300244e3f977e56a8ceb5)
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 <ctype.h>
32 #include <errno.h>
33 #include <getopt.h>
34 #include <stdio.h>
35 #include <stdarg.h>
36 #include <stdint.h>
37 #include <stdlib.h>
38 #include <string.h>
39 
40 #include <sys/types.h>
41 #include <sys/imgact_binmisc.h>
42 #include <sys/linker.h>
43 #include <sys/sysctl.h>
44 
45 enum cmd {
46 	CMD_ADD = 0,
47 	CMD_REMOVE,
48 	CMD_DISABLE,
49 	CMD_ENABLE,
50 	CMD_LOOKUP,
51 	CMD_LIST,
52 };
53 
54 extern char *__progname;
55 
56 typedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
57 
58 int add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
59 int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
60 int noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
61 
62 static const struct {
63 	const int token;
64 	const char *name;
65 	cmd_func_t func;
66 	const char *desc;
67 	const char *args;
68 } cmds[] = {
69 	{
70 		CMD_ADD,
71 		"add",
72 		add_cmd,
73 		"Add a new binary image activator (requires 'root' privilege)",
74 		"<name> --interpreter <path_and_arguments> \\\n"
75 		"\t\t--magic <magic_bytes> [--mask <mask_bytes>] \\\n"
76 		"\t\t--size <magic_size> [--offset <magic_offset>] \\\n"
77 		"\t\t[--set-enabled]"
78 	},
79 	{
80 		CMD_REMOVE,
81 		"remove",
82 		name_cmd,
83 		"Remove a binary image activator (requires 'root' privilege)",
84 		"<name>"
85 	},
86 	{
87 		CMD_DISABLE,
88 		"disable",
89 		name_cmd,
90 		"Disable a binary image activator (requires 'root' privilege)",
91 		"<name>"
92 	},
93 	{
94 		CMD_ENABLE,
95 		"enable",
96 		name_cmd,
97 		"Enable a binary image activator (requires 'root' privilege)",
98 		"<name>"
99 	},
100 	{
101 		CMD_LOOKUP,
102 		"lookup",
103 		name_cmd,
104 		"Lookup a binary image activator",
105 		"<name>"
106 	},
107 	{
108 		CMD_LIST,
109 		"list",
110 		noname_cmd,
111 		"List all the binary image activators",
112 		""
113 	},
114 };
115 
116 static const struct option
117 add_opts[] = {
118 	{ "set-enabled",	no_argument,		NULL,	'e' },
119 	{ "interpreter",	required_argument,	NULL,	'i' },
120 	{ "mask",		required_argument,	NULL,	'M' },
121 	{ "magic",		required_argument,	NULL,	'm' },
122 	{ "offset",		required_argument,	NULL,	'o' },
123 	{ "size",		required_argument,	NULL,	's' },
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
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 < ( sizeof (cmds) / sizeof (cmds[0])); 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
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\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 
226 static int
227 demux_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 
244 static int
245 strlit2bin_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 
279 int
280 add_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 
373 int
374 name_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
375 {
376 	if (strlen(argv[0]) > IBE_NAME_MAX)
377 		usage("'%s' string length longer than IBE_NAME_MAX (%d)",
378 		    IBE_NAME_MAX);
379 	strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
380 
381 	return (0);
382 }
383 
384 int
385 noname_cmd(__unused int argc, __unused char *argv[],
386     __unused ximgact_binmisc_entry_t *xbe)
387 {
388 
389 	return (0);
390 }
391 
392 int
393 main(int argc, char **argv)
394 {
395 	int error = 0, cmd = -1;
396 	ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL;
397 	ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL;
398 	size_t xbe_in_sz = 0;
399 	size_t xbe_out_sz = 0, *xbe_out_szp = NULL;
400 	uint32_t i;
401 
402 	if (kldfind(KMOD_NAME) == -1) {
403 		if (kldload(KMOD_NAME) == -1)
404 			fatal("Can't load %s kernel module: %s",
405 			    KMOD_NAME, strerror(errno));
406 	}
407 
408 	bzero(&xbe_in, sizeof(xbe_in));
409 	bzero(&xbe_out, sizeof(xbe_out));
410 	xbe_in.xbe_version = IBE_VERSION;
411 
412 	if (argc < 2)
413 		usage("Error: requires at least one argument");
414 
415 	argc--, argv++;
416 	cmd = demux_cmd(argc, argv);
417 	if (cmd == -1)
418 		usage("Error: Unknown command \"%s\"", argv[0]);
419 	argc--, argv++;
420 
421 	error = (*cmds[cmd].func)(argc, argv, &xbe_in);
422 	if (error)
423 		usage("Can't parse command-line for '%s' command",
424 		    cmds[cmd].name);
425 
426 	if (cmd != CMD_LIST) {
427 		xbe_inp = &xbe_in;
428 		xbe_in_sz = sizeof(xbe_in);
429 	} else
430 		xbe_out_szp = &xbe_out_sz;
431 	if (cmd == CMD_LOOKUP) {
432 		xbe_out_sz = sizeof(xbe_out);
433 		xbe_outp = &xbe_out;
434 		xbe_out_szp = &xbe_out_sz;
435 	}
436 
437 	error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp,
438 	    xbe_inp, xbe_in_sz);
439 
440 	if (error)
441 		switch(errno) {
442 		case EINVAL:
443 			usage("Invalid interpreter name or --interpreter, "
444 			    "--magic, --mask, or --size argument value");
445 			break;
446 
447 		case EEXIST:
448 			usage("'%s' is not unique in activator list",
449 			    xbe_in.xbe_name);
450 			break;
451 
452 		case ENOENT:
453 			usage("'%s' is not found in activator list",
454 			    xbe_in.xbe_name);
455 			break;
456 
457 		case ENOSPC:
458 			fatal("Fatal: no more room in the activator list "
459 			    "(limited to %d enties)", IBE_MAX_ENTRIES);
460 			break;
461 
462 		case EPERM:
463 			usage("Insufficient privileges for '%s' command",
464 			    cmds[cmd].name);
465 			break;
466 
467 		default:
468 			fatal("Fatal: sysctlbyname() returned: %s",
469 			    strerror(errno));
470 			break;
471 		}
472 
473 
474 	if (cmd == CMD_LOOKUP)
475 		printxbe(xbe_outp);
476 
477 	if (cmd == CMD_LIST && xbe_out_sz > 0) {
478 		xbe_outp = malloc(xbe_out_sz);
479 		if (!xbe_outp)
480 			fatal("Fatal: out of memory");
481 		while(1) {
482 			size_t osize = xbe_out_sz;
483 			error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp,
484 			    &xbe_out_sz, NULL, 0);
485 
486 			if (error == -1 && errno == ENOMEM &&
487 			    xbe_out_sz == osize) {
488 				/*
489 				 * Buffer too small. Increase it by one
490 				 * entry.
491 				 */
492 				xbe_out_sz += sizeof(xbe_out);
493 				xbe_outp = realloc(xbe_outp, xbe_out_sz);
494 				if (!xbe_outp)
495 					fatal("Fatal: out of memory");
496 			} else
497 				break;
498 		}
499 		if (error) {
500 			free(xbe_outp);
501 			fatal("Fatal: %s", strerror(errno));
502 		}
503 		for(i = 0; i < (xbe_out_sz / sizeof(xbe_out)); i++)
504 			printxbe(&xbe_outp[i]);
505 	}
506 
507 	return (error);
508 }
509