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] \\\n" 79 "\t\t[--pre-open]" 80 }, 81 { 82 CMD_REMOVE, 83 "remove", 84 name_cmd, 85 "Remove a binary image activator (requires 'root' privilege)", 86 "<name>" 87 }, 88 { 89 CMD_DISABLE, 90 "disable", 91 name_cmd, 92 "Disable a binary image activator (requires 'root' privilege)", 93 "<name>" 94 }, 95 { 96 CMD_ENABLE, 97 "enable", 98 name_cmd, 99 "Enable a binary image activator (requires 'root' privilege)", 100 "<name>" 101 }, 102 { 103 CMD_LOOKUP, 104 "lookup", 105 name_cmd, 106 "Lookup a binary image activator", 107 "<name>" 108 }, 109 { 110 CMD_LIST, 111 "list", 112 noname_cmd, 113 "List all the binary image activators", 114 "" 115 }, 116 }; 117 118 static const struct option 119 add_opts[] = { 120 { "set-enabled", no_argument, NULL, 'e' }, 121 { "interpreter", required_argument, NULL, 'i' }, 122 { "mask", required_argument, NULL, 'M' }, 123 { "magic", required_argument, NULL, 'm' }, 124 { "offset", required_argument, NULL, 'o' }, 125 { "size", required_argument, NULL, 's' }, 126 { "pre-open", no_argument, NULL, 'p' }, 127 { NULL, 0, NULL, 0 } 128 }; 129 130 static char const *cmd_sysctl_name[] = { 131 IBE_SYSCTL_NAME_ADD, 132 IBE_SYSCTL_NAME_REMOVE, 133 IBE_SYSCTL_NAME_DISABLE, 134 IBE_SYSCTL_NAME_ENABLE, 135 IBE_SYSCTL_NAME_LOOKUP, 136 IBE_SYSCTL_NAME_LIST 137 }; 138 139 static void __dead2 140 usage(const char *format, ...) 141 { 142 va_list args; 143 size_t i; 144 int error = 0; 145 146 va_start(args, format); 147 if (format) { 148 vfprintf(stderr, format, args); 149 error = -1; 150 } 151 va_end(args); 152 fprintf(stderr, "\n"); 153 fprintf(stderr, "usage: %s command [args...]\n\n", __progname); 154 155 for(i = 0; i < nitems(cmds); i++) { 156 fprintf(stderr, "%s:\n", cmds[i].desc); 157 fprintf(stderr, "\t%s %s %s\n\n", __progname, cmds[i].name, 158 cmds[i].args); 159 } 160 161 exit (error); 162 } 163 164 static void __dead2 165 fatal(const char *format, ...) 166 { 167 va_list args; 168 169 va_start(args, format); 170 if (format) 171 vfprintf(stderr, format, args); 172 fprintf(stderr, "\n"); 173 174 exit(-1); 175 } 176 177 static void 178 getoptstr(char *str, size_t size, const char *argname) 179 { 180 if (strlen(optarg) > size) 181 usage("'%s' too large", argname); 182 strlcpy(str, optarg, size); 183 } 184 185 static void 186 printxbe(ximgact_binmisc_entry_t *xbe) 187 { 188 uint32_t i, flags = xbe->xbe_flags; 189 190 if (xbe->xbe_version != IBE_VERSION) { 191 fprintf(stderr, "Error: XBE version mismatch\n"); 192 return; 193 } 194 195 printf("name: %s\n", xbe->xbe_name); 196 printf("interpreter: %s\n", xbe->xbe_interpreter); 197 printf("flags: %s%s%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "", 198 (flags & IBF_USE_MASK) ? "USE_MASK " : "", 199 (flags & IBF_PRE_OPEN) ? "PRE_OPEN " : ""); 200 printf("magic size: %u\n", xbe->xbe_msize); 201 printf("magic offset: %u\n", xbe->xbe_moffset); 202 203 printf("magic: "); 204 for(i = 0; i < xbe->xbe_msize; i++) { 205 if (i && !(i % 12)) 206 printf("\n "); 207 else 208 if (i && !(i % 4)) 209 printf(" "); 210 printf("0x%02x ", xbe->xbe_magic[i]); 211 } 212 printf("\n"); 213 214 if (flags & IBF_USE_MASK) { 215 printf("mask: "); 216 for(i = 0; i < xbe->xbe_msize; i++) { 217 if (i && !(i % 12)) 218 printf("\n "); 219 else 220 if (i && !(i % 4)) 221 printf(" "); 222 printf("0x%02x ", xbe->xbe_mask[i]); 223 } 224 printf("\n"); 225 } 226 227 printf("\n"); 228 } 229 230 static int 231 demux_cmd(__unused int argc, char *const argv[]) 232 { 233 size_t i; 234 235 optind = 1; 236 optreset = 1; 237 238 for(i = 0; i < nitems(cmds); i++) { 239 if (!strcasecmp(cmds[i].name, argv[0])) { 240 return (i); 241 } 242 } 243 244 /* Unknown command */ 245 return (-1); 246 } 247 248 static int 249 strlit2bin_cpy(uint8_t *d, char *s, size_t size) 250 { 251 int c; 252 size_t cnt = 0; 253 254 while((c = *s++) != '\0') { 255 if (c == '\\') { 256 /* Do '\' escapes. */ 257 switch (*s) { 258 case '\\': 259 *d++ = '\\'; 260 break; 261 262 case 'x': 263 s++; 264 c = toupper(*s++); 265 *d = (c - (isdigit(c) ? '0' : ('A' - 10))) << 4; 266 c = toupper(*s++); 267 *d++ |= c - (isdigit(c) ? '0' : ('A' - 10)); 268 break; 269 270 default: 271 return (-1); 272 } 273 } else 274 *d++ = c; 275 276 if (++cnt > size) 277 return (-1); 278 } 279 280 return (cnt); 281 } 282 283 int 284 add_cmd(__unused int argc, char *argv[], ximgact_binmisc_entry_t *xbe) 285 { 286 int ch; 287 char *magic = NULL, *mask = NULL; 288 int sz; 289 290 if (argc == 0) 291 usage("Required argument missing\n"); 292 if (strlen(argv[0]) > IBE_NAME_MAX) 293 usage("'%s' string length longer than IBE_NAME_MAX (%d)", 294 IBE_NAME_MAX); 295 strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX); 296 297 while ((ch = getopt_long(argc, argv, "epi:m:M:o:s:", add_opts, NULL)) 298 != -1) { 299 300 switch(ch) { 301 case 'i': 302 getoptstr(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX, 303 "interpreter"); 304 break; 305 306 case 'm': 307 free(magic); 308 magic = strdup(optarg); 309 break; 310 311 case 'M': 312 free(mask); 313 mask = strdup(optarg); 314 xbe->xbe_flags |= IBF_USE_MASK; 315 break; 316 317 case 'e': 318 xbe->xbe_flags |= IBF_ENABLED; 319 break; 320 321 case 'o': 322 xbe->xbe_moffset = atol(optarg); 323 break; 324 325 case 's': 326 xbe->xbe_msize = atol(optarg); 327 if (xbe->xbe_msize == 0 || 328 xbe->xbe_msize > IBE_MAGIC_MAX) 329 usage("Error: Not valid '--size' value. " 330 "(Must be > 0 and < %u.)\n", 331 xbe->xbe_msize); 332 break; 333 334 case 'p': 335 xbe->xbe_flags |= IBF_PRE_OPEN; 336 break; 337 338 default: 339 usage("Unknown argument: '%c'", ch); 340 } 341 } 342 343 if (xbe->xbe_msize == 0) { 344 if (NULL != magic) 345 free(magic); 346 if (NULL != mask) 347 free(mask); 348 usage("Error: Missing '--size' argument"); 349 } 350 351 if (NULL != magic) { 352 if (xbe->xbe_msize == 0) { 353 if (magic) 354 free(magic); 355 if (mask) 356 free(mask); 357 usage("Error: Missing magic size argument"); 358 } 359 sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX); 360 free(magic); 361 if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) { 362 if (mask) 363 free(mask); 364 usage("Error: invalid magic argument"); 365 } 366 if (mask) { 367 sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX); 368 free(mask); 369 if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) 370 usage("Error: invalid mask argument"); 371 } 372 } else { 373 if (mask) 374 free(mask); 375 usage("Error: Missing magic argument"); 376 } 377 378 if (!strnlen(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX)) { 379 usage("Error: Missing 'interpreter' argument"); 380 } 381 382 return (0); 383 } 384 385 int 386 name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe) 387 { 388 if (argc == 0) 389 usage("Required argument missing\n"); 390 if (strlen(argv[0]) > IBE_NAME_MAX) 391 usage("'%s' string length longer than IBE_NAME_MAX (%d)", 392 IBE_NAME_MAX); 393 strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX); 394 395 return (0); 396 } 397 398 int 399 noname_cmd(__unused int argc, __unused char *argv[], 400 __unused ximgact_binmisc_entry_t *xbe) 401 { 402 403 return (0); 404 } 405 406 int 407 main(int argc, char **argv) 408 { 409 int error = 0, cmd = -1; 410 ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL; 411 ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL; 412 size_t xbe_in_sz = 0; 413 size_t xbe_out_sz = 0, *xbe_out_szp = NULL; 414 uint32_t i; 415 416 if (modfind(KMOD_NAME) == -1) { 417 if (kldload(KMOD_NAME) == -1) 418 fatal("Can't load %s kernel module: %s", 419 KMOD_NAME, strerror(errno)); 420 } 421 422 bzero(&xbe_in, sizeof(xbe_in)); 423 bzero(&xbe_out, sizeof(xbe_out)); 424 xbe_in.xbe_version = IBE_VERSION; 425 426 if (argc < 2) 427 usage("Error: requires at least one argument"); 428 429 argc--, argv++; 430 cmd = demux_cmd(argc, argv); 431 if (cmd < 0) 432 usage("Error: Unknown command \"%s\"", argv[0]); 433 argc--, argv++; 434 435 error = (*cmds[cmd].func)(argc, argv, &xbe_in); 436 if (error) 437 usage("Can't parse command-line for '%s' command", 438 cmds[cmd].name); 439 440 if (cmd != CMD_LIST) { 441 xbe_inp = &xbe_in; 442 xbe_in_sz = sizeof(xbe_in); 443 } else 444 xbe_out_szp = &xbe_out_sz; 445 if (cmd == CMD_LOOKUP) { 446 xbe_out_sz = sizeof(xbe_out); 447 xbe_outp = &xbe_out; 448 xbe_out_szp = &xbe_out_sz; 449 } 450 451 error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp, 452 xbe_inp, xbe_in_sz); 453 454 if (error) 455 switch(errno) { 456 case EINVAL: 457 usage("Invalid interpreter name or --interpreter, " 458 "--magic, --mask, or --size argument value"); 459 break; 460 461 case EEXIST: 462 usage("'%s' is not unique in activator list", 463 xbe_in.xbe_name); 464 break; 465 466 case ENOENT: 467 usage("'%s' is not found in activator list", 468 xbe_in.xbe_name); 469 break; 470 471 case ENOSPC: 472 fatal("Fatal: no more room in the activator list " 473 "(limited to %d enties)", IBE_MAX_ENTRIES); 474 break; 475 476 case EPERM: 477 usage("Insufficient privileges for '%s' command", 478 cmds[cmd].name); 479 break; 480 481 default: 482 fatal("Fatal: sysctlbyname() returned: %s", 483 strerror(errno)); 484 break; 485 } 486 487 488 if (cmd == CMD_LOOKUP) 489 printxbe(xbe_outp); 490 491 if (cmd == CMD_LIST && xbe_out_sz > 0) { 492 xbe_outp = malloc(xbe_out_sz); 493 if (!xbe_outp) 494 fatal("Fatal: out of memory"); 495 while(1) { 496 size_t osize = xbe_out_sz; 497 error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, 498 &xbe_out_sz, NULL, 0); 499 500 if (error == -1 && errno == ENOMEM && 501 xbe_out_sz == osize) { 502 /* 503 * Buffer too small. Increase it by one 504 * entry. 505 */ 506 xbe_out_sz += sizeof(xbe_out); 507 xbe_outp = realloc(xbe_outp, xbe_out_sz); 508 if (!xbe_outp) 509 fatal("Fatal: out of memory"); 510 } else 511 break; 512 } 513 if (error) { 514 free(xbe_outp); 515 fatal("Fatal: %s", strerror(errno)); 516 } 517 for(i = 0; i < howmany(xbe_out_sz, sizeof(xbe_out)); i++) 518 printxbe(&xbe_outp[i]); 519 } 520 521 return (error); 522 } 523