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 #include <sys/types.h>
30 #include <sys/imgact_binmisc.h>
31 #include <sys/linker.h>
32 #include <sys/module.h>
33 #include <sys/sysctl.h>
34
35 #include <ctype.h>
36 #include <errno.h>
37 #include <getopt.h>
38 #include <stdio.h>
39 #include <stdarg.h>
40 #include <stdint.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 enum cmd {
45 CMD_ADD = 0,
46 CMD_REMOVE,
47 CMD_DISABLE,
48 CMD_ENABLE,
49 CMD_LOOKUP,
50 CMD_LIST,
51 };
52
53 extern char *__progname;
54
55 typedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
56
57 int add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
58 int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
59 int noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe);
60
61 static const struct {
62 const int token;
63 const char *name;
64 cmd_func_t func;
65 const char *desc;
66 const char *args;
67 } cmds[] = {
68 {
69 CMD_ADD,
70 "add",
71 add_cmd,
72 "Add a new binary image activator (requires 'root' privilege)",
73 "<name> --interpreter <path_and_arguments> \\\n"
74 "\t\t--magic <magic_bytes> [--mask <mask_bytes>] \\\n"
75 "\t\t--size <magic_size> [--offset <magic_offset>] \\\n"
76 "\t\t[--set-enabled] \\\n"
77 "\t\t[--pre-open]"
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 { "pre-open", no_argument, NULL, 'p' },
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%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "",
196 (flags & IBF_USE_MASK) ? "USE_MASK " : "",
197 (flags & IBF_PRE_OPEN) ? "PRE_OPEN " : "");
198 printf("magic size: %u\n", xbe->xbe_msize);
199 printf("magic offset: %u\n", xbe->xbe_moffset);
200
201 printf("magic: ");
202 for(i = 0; i < xbe->xbe_msize; i++) {
203 if (i && !(i % 12))
204 printf("\n ");
205 else
206 if (i && !(i % 4))
207 printf(" ");
208 printf("0x%02x ", xbe->xbe_magic[i]);
209 }
210 printf("\n");
211
212 if (flags & IBF_USE_MASK) {
213 printf("mask: ");
214 for(i = 0; i < xbe->xbe_msize; i++) {
215 if (i && !(i % 12))
216 printf("\n ");
217 else
218 if (i && !(i % 4))
219 printf(" ");
220 printf("0x%02x ", xbe->xbe_mask[i]);
221 }
222 printf("\n");
223 }
224
225 printf("\n");
226 }
227
228 static int
demux_cmd(__unused int argc,char * const argv[])229 demux_cmd(__unused int argc, char *const argv[])
230 {
231 size_t i;
232
233 optind = 1;
234 optreset = 1;
235
236 for(i = 0; i < nitems(cmds); i++) {
237 if (!strcasecmp(cmds[i].name, argv[0])) {
238 return (i);
239 }
240 }
241
242 /* Unknown command */
243 return (-1);
244 }
245
246 static int
strlit2bin_cpy(uint8_t * d,char * s,size_t size)247 strlit2bin_cpy(uint8_t *d, char *s, size_t size)
248 {
249 int c;
250 size_t cnt = 0;
251
252 while((c = *s++) != '\0') {
253 if (c == '\\') {
254 /* Do '\' escapes. */
255 switch (*s) {
256 case '\\':
257 *d++ = '\\';
258 break;
259
260 case 'x':
261 s++;
262 c = toupper(*s++);
263 *d = (c - (isdigit(c) ? '0' : ('A' - 10))) << 4;
264 c = toupper(*s++);
265 *d++ |= c - (isdigit(c) ? '0' : ('A' - 10));
266 break;
267
268 default:
269 return (-1);
270 }
271 } else
272 *d++ = c;
273
274 if (++cnt > size)
275 return (-1);
276 }
277
278 return (cnt);
279 }
280
281 int
add_cmd(__unused int argc,char * argv[],ximgact_binmisc_entry_t * xbe)282 add_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
283 {
284 int ch;
285 char *magic = NULL, *mask = NULL;
286 int sz;
287
288 if (argc == 0)
289 usage("Required argument missing\n");
290 if (strlen(argv[0]) > IBE_NAME_MAX)
291 usage("'%s' string length longer than IBE_NAME_MAX (%d)",
292 IBE_NAME_MAX);
293 strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
294
295 while ((ch = getopt_long(argc, argv, "epi:m:M:o:s:", add_opts, NULL))
296 != -1) {
297
298 switch(ch) {
299 case 'i':
300 getoptstr(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX,
301 "interpreter");
302 break;
303
304 case 'm':
305 free(magic);
306 magic = strdup(optarg);
307 break;
308
309 case 'M':
310 free(mask);
311 mask = strdup(optarg);
312 xbe->xbe_flags |= IBF_USE_MASK;
313 break;
314
315 case 'e':
316 xbe->xbe_flags |= IBF_ENABLED;
317 break;
318
319 case 'o':
320 xbe->xbe_moffset = atol(optarg);
321 break;
322
323 case 's':
324 xbe->xbe_msize = atol(optarg);
325 if (xbe->xbe_msize == 0 ||
326 xbe->xbe_msize > IBE_MAGIC_MAX)
327 usage("Error: Not valid '--size' value. "
328 "(Must be > 0 and < %u.)\n",
329 xbe->xbe_msize);
330 break;
331
332 case 'p':
333 xbe->xbe_flags |= IBF_PRE_OPEN;
334 break;
335
336 default:
337 usage("Unknown argument: '%c'", ch);
338 }
339 }
340
341 if (xbe->xbe_msize == 0) {
342 if (NULL != magic)
343 free(magic);
344 if (NULL != mask)
345 free(mask);
346 usage("Error: Missing '--size' argument");
347 }
348
349 if (NULL != magic) {
350 if (xbe->xbe_msize == 0) {
351 if (magic)
352 free(magic);
353 if (mask)
354 free(mask);
355 usage("Error: Missing magic size argument");
356 }
357 sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX);
358 free(magic);
359 if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) {
360 if (mask)
361 free(mask);
362 usage("Error: invalid magic argument");
363 }
364 if (mask) {
365 sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX);
366 free(mask);
367 if (sz == -1 || (uint32_t)sz != xbe->xbe_msize)
368 usage("Error: invalid mask argument");
369 }
370 } else {
371 if (mask)
372 free(mask);
373 usage("Error: Missing magic argument");
374 }
375
376 if (!strnlen(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX)) {
377 usage("Error: Missing 'interpreter' argument");
378 }
379
380 return (0);
381 }
382
383 int
name_cmd(int argc,char * argv[],ximgact_binmisc_entry_t * xbe)384 name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
385 {
386 if (argc == 0)
387 usage("Required argument missing\n");
388 if (strlen(argv[0]) > IBE_NAME_MAX)
389 usage("'%s' string length longer than IBE_NAME_MAX (%d)",
390 IBE_NAME_MAX);
391 strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
392
393 return (0);
394 }
395
396 int
noname_cmd(__unused int argc,__unused char * argv[],__unused ximgact_binmisc_entry_t * xbe)397 noname_cmd(__unused int argc, __unused char *argv[],
398 __unused ximgact_binmisc_entry_t *xbe)
399 {
400
401 return (0);
402 }
403
404 int
main(int argc,char ** argv)405 main(int argc, char **argv)
406 {
407 int error = 0, cmd = -1;
408 ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL;
409 ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL;
410 size_t xbe_in_sz = 0;
411 size_t xbe_out_sz = 0, *xbe_out_szp = NULL;
412 uint32_t i;
413
414 if (modfind(KMOD_NAME) == -1) {
415 if (kldload(KMOD_NAME) == -1)
416 fatal("Can't load %s kernel module: %s",
417 KMOD_NAME, strerror(errno));
418 }
419
420 bzero(&xbe_in, sizeof(xbe_in));
421 bzero(&xbe_out, sizeof(xbe_out));
422 xbe_in.xbe_version = IBE_VERSION;
423
424 if (argc < 2)
425 usage("Error: requires at least one argument");
426
427 argc--, argv++;
428 cmd = demux_cmd(argc, argv);
429 if (cmd < 0)
430 usage("Error: Unknown command \"%s\"", argv[0]);
431 argc--, argv++;
432
433 error = (*cmds[cmd].func)(argc, argv, &xbe_in);
434 if (error)
435 usage("Can't parse command-line for '%s' command",
436 cmds[cmd].name);
437
438 if (cmd != CMD_LIST) {
439 xbe_inp = &xbe_in;
440 xbe_in_sz = sizeof(xbe_in);
441 } else
442 xbe_out_szp = &xbe_out_sz;
443 if (cmd == CMD_LOOKUP) {
444 xbe_out_sz = sizeof(xbe_out);
445 xbe_outp = &xbe_out;
446 xbe_out_szp = &xbe_out_sz;
447 }
448
449 error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp,
450 xbe_inp, xbe_in_sz);
451
452 if (error)
453 switch(errno) {
454 case EINVAL:
455 usage("Invalid interpreter name or --interpreter, "
456 "--magic, --mask, or --size argument value");
457 break;
458
459 case EEXIST:
460 usage("'%s' is not unique in activator list",
461 xbe_in.xbe_name);
462 break;
463
464 case ENOENT:
465 usage("'%s' is not found in activator list",
466 xbe_in.xbe_name);
467 break;
468
469 case ENOSPC:
470 fatal("Fatal: no more room in the activator list "
471 "(limited to %d enties)", IBE_MAX_ENTRIES);
472 break;
473
474 case EPERM:
475 usage("Insufficient privileges for '%s' command",
476 cmds[cmd].name);
477 break;
478
479 default:
480 fatal("Fatal: sysctlbyname() returned: %s",
481 strerror(errno));
482 break;
483 }
484
485
486 if (cmd == CMD_LOOKUP)
487 printxbe(xbe_outp);
488
489 if (cmd == CMD_LIST && xbe_out_sz > 0) {
490 xbe_outp = malloc(xbe_out_sz);
491 if (!xbe_outp)
492 fatal("Fatal: out of memory");
493 while(1) {
494 size_t osize = xbe_out_sz;
495 error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp,
496 &xbe_out_sz, NULL, 0);
497
498 if (error == -1 && errno == ENOMEM &&
499 xbe_out_sz == osize) {
500 /*
501 * Buffer too small. Increase it by one
502 * entry.
503 */
504 xbe_out_sz += sizeof(xbe_out);
505 xbe_outp = realloc(xbe_outp, xbe_out_sz);
506 if (!xbe_outp)
507 fatal("Fatal: out of memory");
508 } else
509 break;
510 }
511 if (error) {
512 free(xbe_outp);
513 fatal("Fatal: %s", strerror(errno));
514 }
515 for(i = 0; i < howmany(xbe_out_sz, sizeof(xbe_out)); i++)
516 printxbe(&xbe_outp[i]);
517 }
518
519 return (error);
520 }
521