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/module.h>
44 #include <sys/sysctl.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
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 < ( sizeof (cmds) / sizeof (cmds[0])); 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
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 < ( sizeof (cmds) / sizeof (cmds[0])); 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 (strlen(argv[0]) > IBE_NAME_MAX)
288 usage("'%s' string length longer than IBE_NAME_MAX (%d)",
289 IBE_NAME_MAX);
290 strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
291
292 while ((ch = getopt_long(argc, argv, "ei:m:M:o:s:", add_opts, NULL))
293 != -1) {
294
295 switch(ch) {
296 case 'i':
297 getoptstr(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX,
298 "interpreter");
299 break;
300
301 case 'm':
302 free(magic);
303 magic = strdup(optarg);
304 break;
305
306 case 'M':
307 free(mask);
308 mask = strdup(optarg);
309 xbe->xbe_flags |= IBF_USE_MASK;
310 break;
311
312 case 'e':
313 xbe->xbe_flags |= IBF_ENABLED;
314 break;
315
316 case 'o':
317 xbe->xbe_moffset = atol(optarg);
318 break;
319
320 case 's':
321 xbe->xbe_msize = atol(optarg);
322 if (xbe->xbe_msize == 0 ||
323 xbe->xbe_msize > IBE_MAGIC_MAX)
324 usage("Error: Not valid '--size' value. "
325 "(Must be > 0 and < %u.)\n",
326 xbe->xbe_msize);
327 break;
328
329 default:
330 usage("Unknown argument: '%c'", ch);
331 }
332 }
333
334 if (xbe->xbe_msize == 0) {
335 if (NULL != magic)
336 free(magic);
337 if (NULL != mask)
338 free(mask);
339 usage("Error: Missing '--size' argument");
340 }
341
342 if (NULL != magic) {
343 if (xbe->xbe_msize == 0) {
344 if (magic)
345 free(magic);
346 if (mask)
347 free(mask);
348 usage("Error: Missing magic size argument");
349 }
350 sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX);
351 free(magic);
352 if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) {
353 if (mask)
354 free(mask);
355 usage("Error: invalid magic argument");
356 }
357 if (mask) {
358 sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX);
359 free(mask);
360 if (sz == -1 || (uint32_t)sz != xbe->xbe_msize)
361 usage("Error: invalid mask argument");
362 }
363 } else {
364 if (mask)
365 free(mask);
366 usage("Error: Missing magic argument");
367 }
368
369 if (!strnlen(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX)) {
370 usage("Error: Missing 'interpreter' argument");
371 }
372
373 return (0);
374 }
375
376 int
name_cmd(int argc,char * argv[],ximgact_binmisc_entry_t * xbe)377 name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe)
378 {
379 if (argc == 0)
380 usage("Required argument missing\n");
381 if (strlen(argv[0]) > IBE_NAME_MAX)
382 usage("'%s' string length longer than IBE_NAME_MAX (%d)",
383 IBE_NAME_MAX);
384 strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX);
385
386 return (0);
387 }
388
389 int
noname_cmd(__unused int argc,__unused char * argv[],__unused ximgact_binmisc_entry_t * xbe)390 noname_cmd(__unused int argc, __unused char *argv[],
391 __unused ximgact_binmisc_entry_t *xbe)
392 {
393
394 return (0);
395 }
396
397 int
main(int argc,char ** argv)398 main(int argc, char **argv)
399 {
400 int error = 0, cmd = -1;
401 ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL;
402 ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL;
403 size_t xbe_in_sz = 0;
404 size_t xbe_out_sz = 0, *xbe_out_szp = NULL;
405 uint32_t i;
406
407 if (modfind(KMOD_NAME) == -1) {
408 if (kldload(KMOD_NAME) == -1)
409 fatal("Can't load %s kernel module: %s",
410 KMOD_NAME, strerror(errno));
411 }
412
413 bzero(&xbe_in, sizeof(xbe_in));
414 bzero(&xbe_out, sizeof(xbe_out));
415 xbe_in.xbe_version = IBE_VERSION;
416
417 if (argc < 2)
418 usage("Error: requires at least one argument");
419
420 argc--, argv++;
421 cmd = demux_cmd(argc, argv);
422 if (cmd < 0)
423 usage("Error: Unknown command \"%s\"", argv[0]);
424 argc--, argv++;
425
426 error = (*cmds[cmd].func)(argc, argv, &xbe_in);
427 if (error)
428 usage("Can't parse command-line for '%s' command",
429 cmds[cmd].name);
430
431 if (cmd != CMD_LIST) {
432 xbe_inp = &xbe_in;
433 xbe_in_sz = sizeof(xbe_in);
434 } else
435 xbe_out_szp = &xbe_out_sz;
436 if (cmd == CMD_LOOKUP) {
437 xbe_out_sz = sizeof(xbe_out);
438 xbe_outp = &xbe_out;
439 xbe_out_szp = &xbe_out_sz;
440 }
441
442 error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp,
443 xbe_inp, xbe_in_sz);
444
445 if (error)
446 switch(errno) {
447 case EINVAL:
448 usage("Invalid interpreter name or --interpreter, "
449 "--magic, --mask, or --size argument value");
450 break;
451
452 case EEXIST:
453 usage("'%s' is not unique in activator list",
454 xbe_in.xbe_name);
455 break;
456
457 case ENOENT:
458 usage("'%s' is not found in activator list",
459 xbe_in.xbe_name);
460 break;
461
462 case ENOSPC:
463 fatal("Fatal: no more room in the activator list "
464 "(limited to %d enties)", IBE_MAX_ENTRIES);
465 break;
466
467 case EPERM:
468 usage("Insufficient privileges for '%s' command",
469 cmds[cmd].name);
470 break;
471
472 default:
473 fatal("Fatal: sysctlbyname() returned: %s",
474 strerror(errno));
475 break;
476 }
477
478
479 if (cmd == CMD_LOOKUP)
480 printxbe(xbe_outp);
481
482 if (cmd == CMD_LIST && xbe_out_sz > 0) {
483 xbe_outp = malloc(xbe_out_sz);
484 if (!xbe_outp)
485 fatal("Fatal: out of memory");
486 while(1) {
487 size_t osize = xbe_out_sz;
488 error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp,
489 &xbe_out_sz, NULL, 0);
490
491 if (error == -1 && errno == ENOMEM &&
492 xbe_out_sz == osize) {
493 /*
494 * Buffer too small. Increase it by one
495 * entry.
496 */
497 xbe_out_sz += sizeof(xbe_out);
498 xbe_outp = realloc(xbe_outp, xbe_out_sz);
499 if (!xbe_outp)
500 fatal("Fatal: out of memory");
501 } else
502 break;
503 }
504 if (error) {
505 free(xbe_outp);
506 fatal("Fatal: %s", strerror(errno));
507 }
508 for(i = 0; i < (xbe_out_sz / sizeof(xbe_out)); i++)
509 printxbe(&xbe_outp[i]);
510 }
511
512 return (error);
513 }
514