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 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 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\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 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 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 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 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 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 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 < 0) 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