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
usage(const char * format,...)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
fatal(const char * format,...)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
getoptstr(char * str,size_t size,const char * argname)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
printxbe(ximgact_binmisc_entry_t * xbe)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
demux_cmd(__unused int argc,char * const argv[])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
strlit2bin_cpy(uint8_t * d,char * s,size_t size)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
add_cmd(__unused int argc,char * argv[],ximgact_binmisc_entry_t * xbe)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
name_cmd(int argc,char * argv[],ximgact_binmisc_entry_t * xbe)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
noname_cmd(__unused int argc,__unused char * argv[],__unused ximgact_binmisc_entry_t * xbe)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
main(int argc,char ** argv)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