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
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 < ( 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
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\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
demux_cmd(__unused int argc,char * const argv[])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
strlit2bin_cpy(uint8_t * d,char * s,size_t size)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
add_cmd(__unused int argc,char * argv[],ximgact_binmisc_entry_t * xbe)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 (!strnlen(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX)) {
367 usage("Error: Missing 'interpreter' argument");
368 }
369
370 return (0);
371 }
372
373 int
name_cmd(int argc,char * argv[],ximgact_binmisc_entry_t * xbe)374 name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
375 {
376 if (argc == 0)
377 usage("Required argument missing\n");
378 if (strlen(argv[0]) > IBE_NAME_MAX)
379 usage("'%s' string length longer than IBE_NAME_MAX (%d)",
380 IBE_NAME_MAX);
381 strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
382
383 return (0);
384 }
385
386 int
noname_cmd(__unused int argc,__unused char * argv[],__unused ximgact_binmisc_entry_t * xbe)387 noname_cmd(__unused int argc, __unused char *argv[],
388 __unused ximgact_binmisc_entry_t *xbe)
389 {
390
391 return (0);
392 }
393
394 int
main(int argc,char ** argv)395 main(int argc, char **argv)
396 {
397 int error = 0, cmd = -1;
398 ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL;
399 ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL;
400 size_t xbe_in_sz = 0;
401 size_t xbe_out_sz = 0, *xbe_out_szp = NULL;
402 uint32_t i;
403
404 if (kldfind(KMOD_NAME) == -1) {
405 if (kldload(KMOD_NAME) == -1)
406 fatal("Can't load %s kernel module: %s",
407 KMOD_NAME, strerror(errno));
408 }
409
410 bzero(&xbe_in, sizeof(xbe_in));
411 bzero(&xbe_out, sizeof(xbe_out));
412 xbe_in.xbe_version = IBE_VERSION;
413
414 if (argc < 2)
415 usage("Error: requires at least one argument");
416
417 argc--, argv++;
418 cmd = demux_cmd(argc, argv);
419 if (cmd == -1)
420 usage("Error: Unknown command \"%s\"", argv[0]);
421 argc--, argv++;
422
423 error = (*cmds[cmd].func)(argc, argv, &xbe_in);
424 if (error)
425 usage("Can't parse command-line for '%s' command",
426 cmds[cmd].name);
427
428 if (cmd != CMD_LIST) {
429 xbe_inp = &xbe_in;
430 xbe_in_sz = sizeof(xbe_in);
431 } else
432 xbe_out_szp = &xbe_out_sz;
433 if (cmd == CMD_LOOKUP) {
434 xbe_out_sz = sizeof(xbe_out);
435 xbe_outp = &xbe_out;
436 xbe_out_szp = &xbe_out_sz;
437 }
438
439 error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp,
440 xbe_inp, xbe_in_sz);
441
442 if (error)
443 switch(errno) {
444 case EINVAL:
445 usage("Invalid interpreter name or --interpreter, "
446 "--magic, --mask, or --size argument value");
447 break;
448
449 case EEXIST:
450 usage("'%s' is not unique in activator list",
451 xbe_in.xbe_name);
452 break;
453
454 case ENOENT:
455 usage("'%s' is not found in activator list",
456 xbe_in.xbe_name);
457 break;
458
459 case ENOSPC:
460 fatal("Fatal: no more room in the activator list "
461 "(limited to %d enties)", IBE_MAX_ENTRIES);
462 break;
463
464 case EPERM:
465 usage("Insufficient privileges for '%s' command",
466 cmds[cmd].name);
467 break;
468
469 default:
470 fatal("Fatal: sysctlbyname() returned: %s",
471 strerror(errno));
472 break;
473 }
474
475
476 if (cmd == CMD_LOOKUP)
477 printxbe(xbe_outp);
478
479 if (cmd == CMD_LIST && xbe_out_sz > 0) {
480 xbe_outp = malloc(xbe_out_sz);
481 if (!xbe_outp)
482 fatal("Fatal: out of memory");
483 while(1) {
484 size_t osize = xbe_out_sz;
485 error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp,
486 &xbe_out_sz, NULL, 0);
487
488 if (error == -1 && errno == ENOMEM &&
489 xbe_out_sz == osize) {
490 /*
491 * Buffer too small. Increase it by one
492 * entry.
493 */
494 xbe_out_sz += sizeof(xbe_out);
495 xbe_outp = realloc(xbe_outp, xbe_out_sz);
496 if (!xbe_outp)
497 fatal("Fatal: out of memory");
498 } else
499 break;
500 }
501 if (error) {
502 free(xbe_outp);
503 fatal("Fatal: %s", strerror(errno));
504 }
505 for(i = 0; i < (xbe_out_sz / sizeof(xbe_out)); i++)
506 printxbe(&xbe_outp[i]);
507 }
508
509 return (error);
510 }
511