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 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 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 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 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 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 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 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 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 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 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