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