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/types.h> 29 #include <sys/imgact_binmisc.h> 30 #include <sys/linker.h> 31 #include <sys/module.h> 32 #include <sys/sysctl.h> 33 34 #include <ctype.h> 35 #include <errno.h> 36 #include <getopt.h> 37 #include <stdio.h> 38 #include <stdarg.h> 39 #include <stdint.h> 40 #include <stdlib.h> 41 #include <string.h> 42 43 enum cmd { 44 CMD_ADD = 0, 45 CMD_REMOVE, 46 CMD_DISABLE, 47 CMD_ENABLE, 48 CMD_LOOKUP, 49 CMD_LIST, 50 }; 51 52 extern char *__progname; 53 54 typedef int (*cmd_func_t)(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); 55 56 int add_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); 57 int name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); 58 int noname_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe); 59 60 static const struct { 61 const int token; 62 const char *name; 63 cmd_func_t func; 64 const char *desc; 65 const char *args; 66 } cmds[] = { 67 { 68 CMD_ADD, 69 "add", 70 add_cmd, 71 "Add a new binary image activator (requires 'root' privilege)", 72 "<name> --interpreter <path_and_arguments> \\\n" 73 "\t\t--magic <magic_bytes> [--mask <mask_bytes>] \\\n" 74 "\t\t--size <magic_size> [--offset <magic_offset>] \\\n" 75 "\t\t[--set-enabled] \\\n" 76 "\t\t[--pre-open]" 77 }, 78 { 79 CMD_REMOVE, 80 "remove", 81 name_cmd, 82 "Remove a binary image activator (requires 'root' privilege)", 83 "<name>" 84 }, 85 { 86 CMD_DISABLE, 87 "disable", 88 name_cmd, 89 "Disable a binary image activator (requires 'root' privilege)", 90 "<name>" 91 }, 92 { 93 CMD_ENABLE, 94 "enable", 95 name_cmd, 96 "Enable a binary image activator (requires 'root' privilege)", 97 "<name>" 98 }, 99 { 100 CMD_LOOKUP, 101 "lookup", 102 name_cmd, 103 "Lookup a binary image activator", 104 "<name>" 105 }, 106 { 107 CMD_LIST, 108 "list", 109 noname_cmd, 110 "List all the binary image activators", 111 "" 112 }, 113 }; 114 115 static const struct option 116 add_opts[] = { 117 { "set-enabled", no_argument, NULL, 'e' }, 118 { "interpreter", required_argument, NULL, 'i' }, 119 { "mask", required_argument, NULL, 'M' }, 120 { "magic", required_argument, NULL, 'm' }, 121 { "offset", required_argument, NULL, 'o' }, 122 { "size", required_argument, NULL, 's' }, 123 { "pre-open", no_argument, NULL, 'p' }, 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 __dead2 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 < nitems(cmds); 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 __dead2 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 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 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%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "", 195 (flags & IBF_USE_MASK) ? "USE_MASK " : "", 196 (flags & IBF_PRE_OPEN) ? "PRE_OPEN " : ""); 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 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 < nitems(cmds); 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 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 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 (argc == 0) 288 usage("Required argument missing\n"); 289 if (strlen(argv[0]) > IBE_NAME_MAX) 290 usage("'%s' string length longer than IBE_NAME_MAX (%d)", 291 IBE_NAME_MAX); 292 strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX); 293 294 while ((ch = getopt_long(argc, argv, "epi:m:M:o:s:", add_opts, NULL)) 295 != -1) { 296 297 switch(ch) { 298 case 'i': 299 getoptstr(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX, 300 "interpreter"); 301 break; 302 303 case 'm': 304 free(magic); 305 magic = strdup(optarg); 306 break; 307 308 case 'M': 309 free(mask); 310 mask = strdup(optarg); 311 xbe->xbe_flags |= IBF_USE_MASK; 312 break; 313 314 case 'e': 315 xbe->xbe_flags |= IBF_ENABLED; 316 break; 317 318 case 'o': 319 xbe->xbe_moffset = atol(optarg); 320 break; 321 322 case 's': 323 xbe->xbe_msize = atol(optarg); 324 if (xbe->xbe_msize == 0 || 325 xbe->xbe_msize > IBE_MAGIC_MAX) 326 usage("Error: Not valid '--size' value. " 327 "(Must be > 0 and < %u.)\n", 328 xbe->xbe_msize); 329 break; 330 331 case 'p': 332 xbe->xbe_flags |= IBF_PRE_OPEN; 333 break; 334 335 default: 336 usage("Unknown argument: '%c'", ch); 337 } 338 } 339 340 if (xbe->xbe_msize == 0) { 341 if (NULL != magic) 342 free(magic); 343 if (NULL != mask) 344 free(mask); 345 usage("Error: Missing '--size' argument"); 346 } 347 348 if (NULL != magic) { 349 if (xbe->xbe_msize == 0) { 350 if (magic) 351 free(magic); 352 if (mask) 353 free(mask); 354 usage("Error: Missing magic size argument"); 355 } 356 sz = strlit2bin_cpy(xbe->xbe_magic, magic, IBE_MAGIC_MAX); 357 free(magic); 358 if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) { 359 if (mask) 360 free(mask); 361 usage("Error: invalid magic argument"); 362 } 363 if (mask) { 364 sz = strlit2bin_cpy(xbe->xbe_mask, mask, IBE_MAGIC_MAX); 365 free(mask); 366 if (sz == -1 || (uint32_t)sz != xbe->xbe_msize) 367 usage("Error: invalid mask argument"); 368 } 369 } else { 370 if (mask) 371 free(mask); 372 usage("Error: Missing magic argument"); 373 } 374 375 if (!strnlen(xbe->xbe_interpreter, IBE_INTERP_LEN_MAX)) { 376 usage("Error: Missing 'interpreter' argument"); 377 } 378 379 return (0); 380 } 381 382 int 383 name_cmd(int argc, char *argv[], ximgact_binmisc_entry_t *xbe) 384 { 385 if (argc == 0) 386 usage("Required argument missing\n"); 387 if (strlen(argv[0]) > IBE_NAME_MAX) 388 usage("'%s' string length longer than IBE_NAME_MAX (%d)", 389 IBE_NAME_MAX); 390 strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX); 391 392 return (0); 393 } 394 395 int 396 noname_cmd(__unused int argc, __unused char *argv[], 397 __unused ximgact_binmisc_entry_t *xbe) 398 { 399 400 return (0); 401 } 402 403 int 404 main(int argc, char **argv) 405 { 406 int error = 0, cmd = -1; 407 ximgact_binmisc_entry_t xbe_in, *xbe_inp = NULL; 408 ximgact_binmisc_entry_t xbe_out, *xbe_outp = NULL; 409 size_t xbe_in_sz = 0; 410 size_t xbe_out_sz = 0, *xbe_out_szp = NULL; 411 uint32_t i; 412 413 if (modfind(KMOD_NAME) == -1) { 414 if (kldload(KMOD_NAME) == -1) 415 fatal("Can't load %s kernel module: %s", 416 KMOD_NAME, strerror(errno)); 417 } 418 419 bzero(&xbe_in, sizeof(xbe_in)); 420 bzero(&xbe_out, sizeof(xbe_out)); 421 xbe_in.xbe_version = IBE_VERSION; 422 423 if (argc < 2) 424 usage("Error: requires at least one argument"); 425 426 argc--, argv++; 427 cmd = demux_cmd(argc, argv); 428 if (cmd < 0) 429 usage("Error: Unknown command \"%s\"", argv[0]); 430 argc--, argv++; 431 432 error = (*cmds[cmd].func)(argc, argv, &xbe_in); 433 if (error) 434 usage("Can't parse command-line for '%s' command", 435 cmds[cmd].name); 436 437 if (cmd != CMD_LIST) { 438 xbe_inp = &xbe_in; 439 xbe_in_sz = sizeof(xbe_in); 440 } else 441 xbe_out_szp = &xbe_out_sz; 442 if (cmd == CMD_LOOKUP) { 443 xbe_out_sz = sizeof(xbe_out); 444 xbe_outp = &xbe_out; 445 xbe_out_szp = &xbe_out_sz; 446 } 447 448 error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, xbe_out_szp, 449 xbe_inp, xbe_in_sz); 450 451 if (error) 452 switch(errno) { 453 case EINVAL: 454 usage("Invalid interpreter name or --interpreter, " 455 "--magic, --mask, or --size argument value"); 456 break; 457 458 case EEXIST: 459 usage("'%s' is not unique in activator list", 460 xbe_in.xbe_name); 461 break; 462 463 case ENOENT: 464 usage("'%s' is not found in activator list", 465 xbe_in.xbe_name); 466 break; 467 468 case ENOSPC: 469 fatal("Fatal: no more room in the activator list " 470 "(limited to %d enties)", IBE_MAX_ENTRIES); 471 break; 472 473 case EPERM: 474 usage("Insufficient privileges for '%s' command", 475 cmds[cmd].name); 476 break; 477 478 default: 479 fatal("Fatal: sysctlbyname() returned: %s", 480 strerror(errno)); 481 break; 482 } 483 484 485 if (cmd == CMD_LOOKUP) 486 printxbe(xbe_outp); 487 488 if (cmd == CMD_LIST && xbe_out_sz > 0) { 489 xbe_outp = malloc(xbe_out_sz); 490 if (!xbe_outp) 491 fatal("Fatal: out of memory"); 492 while(1) { 493 size_t osize = xbe_out_sz; 494 error = sysctlbyname(cmd_sysctl_name[cmd], xbe_outp, 495 &xbe_out_sz, NULL, 0); 496 497 if (error == -1 && errno == ENOMEM && 498 xbe_out_sz == osize) { 499 /* 500 * Buffer too small. Increase it by one 501 * entry. 502 */ 503 xbe_out_sz += sizeof(xbe_out); 504 xbe_outp = realloc(xbe_outp, xbe_out_sz); 505 if (!xbe_outp) 506 fatal("Fatal: out of memory"); 507 } else 508 break; 509 } 510 if (error) { 511 free(xbe_outp); 512 fatal("Fatal: %s", strerror(errno)); 513 } 514 for(i = 0; i < howmany(xbe_out_sz, sizeof(xbe_out)); i++) 515 printxbe(&xbe_outp[i]); 516 } 517 518 return (error); 519 } 520