1 /* $OpenBSD: file.c,v 1.51 2015/10/06 15:39:44 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/ioctl.h> 21 #include <sys/mman.h> 22 #include <sys/socket.h> 23 #include <sys/queue.h> 24 #include <sys/uio.h> 25 #include <sys/wait.h> 26 27 #include <errno.h> 28 #include <imsg.h> 29 #include <libgen.h> 30 #include <getopt.h> 31 #include <fcntl.h> 32 #include <pwd.h> 33 #include <stdlib.h> 34 #include <time.h> 35 #include <unistd.h> 36 #include <limits.h> 37 38 #include "file.h" 39 #include "magic.h" 40 #include "xmalloc.h" 41 42 struct input_msg 43 { 44 int idx; 45 46 struct stat sb; 47 int error; 48 49 char link_path[PATH_MAX]; 50 int link_error; 51 int link_target; 52 }; 53 54 struct input_ack 55 { 56 int idx; 57 }; 58 59 struct input_file 60 { 61 struct magic *m; 62 struct input_msg *msg; 63 64 const char *path; 65 int fd; 66 67 void *base; 68 size_t size; 69 int mapped; 70 char *result; 71 }; 72 73 extern char *__progname; 74 75 __dead void usage(void); 76 77 static void send_message(struct imsgbuf *, void *, size_t, int); 78 static int read_message(struct imsgbuf *, struct imsg *, pid_t); 79 80 static void read_link(struct input_msg *, const char *); 81 82 static __dead void child(int, pid_t, int, char **); 83 84 static void test_file(struct input_file *, size_t); 85 86 static int try_stat(struct input_file *); 87 static int try_empty(struct input_file *); 88 static int try_access(struct input_file *); 89 static int try_text(struct input_file *); 90 static int try_magic(struct input_file *); 91 static int try_unknown(struct input_file *); 92 93 static int bflag; 94 static int cflag; 95 static int iflag; 96 static int Lflag; 97 static int sflag; 98 static int Wflag; 99 100 static char *magicpath; 101 static FILE *magicfp; 102 103 static struct option longopts[] = { 104 { "mime", no_argument, NULL, 'i' }, 105 { "mime-type", no_argument, NULL, 'i' }, 106 { NULL, 0, NULL, 0 } 107 }; 108 109 __dead void 110 usage(void) 111 { 112 fprintf(stderr, "usage: %s [-bchiLsW] file ...\n", __progname); 113 exit(1); 114 } 115 116 int 117 main(int argc, char **argv) 118 { 119 int opt, pair[2], fd, idx, mode; 120 char *home; 121 struct passwd *pw; 122 struct imsgbuf ibuf; 123 struct imsg imsg; 124 struct input_msg msg; 125 struct input_ack *ack; 126 pid_t pid, parent; 127 128 tzset(); 129 130 for (;;) { 131 opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL); 132 if (opt == -1) 133 break; 134 switch (opt) { 135 case 'b': 136 bflag = 1; 137 break; 138 case 'c': 139 cflag = 1; 140 break; 141 case 'h': 142 Lflag = 0; 143 break; 144 case 'i': 145 iflag = 1; 146 break; 147 case 'L': 148 Lflag = 1; 149 break; 150 case 's': 151 sflag = 1; 152 break; 153 case 'W': 154 Wflag = 1; 155 break; 156 default: 157 usage(); 158 } 159 } 160 argc -= optind; 161 argv += optind; 162 if (cflag) { 163 if (argc != 0) 164 usage(); 165 } else if (argc == 0) 166 usage(); 167 168 magicfp = NULL; 169 if (geteuid() != 0 && !issetugid()) { 170 home = getenv("HOME"); 171 if (home == NULL || *home == '\0') { 172 pw = getpwuid(getuid()); 173 if (pw != NULL) 174 home = pw->pw_dir; 175 else 176 home = NULL; 177 } 178 if (home != NULL) { 179 xasprintf(&magicpath, "%s/.magic", home); 180 magicfp = fopen(magicpath, "r"); 181 if (magicfp == NULL) 182 free(magicpath); 183 } 184 } 185 if (magicfp == NULL) { 186 magicpath = xstrdup("/etc/magic"); 187 magicfp = fopen(magicpath, "r"); 188 } 189 if (magicfp == NULL) 190 err(1, "%s", magicpath); 191 192 parent = getpid(); 193 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) 194 err(1, "socketpair"); 195 switch (pid = fork()) { 196 case -1: 197 err(1, "fork"); 198 case 0: 199 close(pair[0]); 200 child(pair[1], parent, argc, argv); 201 } 202 close(pair[1]); 203 204 fclose(magicfp); 205 magicfp = NULL; 206 207 if (cflag) 208 goto wait_for_child; 209 210 imsg_init(&ibuf, pair[0]); 211 for (idx = 0; idx < argc; idx++) { 212 memset(&msg, 0, sizeof msg); 213 msg.idx = idx; 214 215 if (strcmp(argv[idx], "-") == 0) { 216 if (fstat(STDIN_FILENO, &msg.sb) == -1) { 217 fd = -1; 218 msg.error = errno; 219 } else 220 fd = STDIN_FILENO; 221 } else if (lstat(argv[idx], &msg.sb) == -1) { 222 fd = -1; 223 msg.error = errno; 224 } else { 225 /* 226 * tame(2) doesn't let us pass directory file 227 * descriptors around - but in fact we don't need them, 228 * so just don't open directories or symlinks (which 229 * could be to directories). 230 */ 231 mode = msg.sb.st_mode; 232 if (!S_ISDIR(mode) && !S_ISLNK(mode)) { 233 fd = open(argv[idx], O_RDONLY|O_NONBLOCK); 234 if (fd == -1 && 235 (errno == ENFILE || errno == EMFILE)) 236 err(1, "open"); 237 } else 238 fd = -1; 239 if (S_ISLNK(mode)) 240 read_link(&msg, argv[idx]); 241 } 242 send_message(&ibuf, &msg, sizeof msg, fd); 243 244 if (read_message(&ibuf, &imsg, pid) == 0) 245 break; 246 if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *ack) 247 errx(1, "message too small"); 248 ack = imsg.data; 249 if (ack->idx != idx) 250 errx(1, "index not expected"); 251 imsg_free(&imsg); 252 } 253 254 wait_for_child: 255 close(pair[0]); 256 while (wait(NULL) == -1 && errno != ECHILD) { 257 if (errno != EINTR) 258 err(1, "wait"); 259 } 260 _exit(0); /* let the child flush */ 261 } 262 263 static void 264 send_message(struct imsgbuf *ibuf, void *msg, size_t msglen, int fd) 265 { 266 if (imsg_compose(ibuf, -1, -1, 0, fd, msg, msglen) != 1) 267 err(1, "imsg_compose"); 268 if (imsg_flush(ibuf) != 0) 269 err(1, "imsg_flush"); 270 } 271 272 static int 273 read_message(struct imsgbuf *ibuf, struct imsg *imsg, pid_t from) 274 { 275 int n; 276 277 if ((n = imsg_read(ibuf)) == -1) 278 err(1, "imsg_read"); 279 if (n == 0) 280 return (0); 281 282 if ((n = imsg_get(ibuf, imsg)) == -1) 283 err(1, "imsg_get"); 284 if (n == 0) 285 return (0); 286 287 if ((pid_t)imsg->hdr.pid != from) 288 errx(1, "PIDs don't match"); 289 290 return (n); 291 292 } 293 294 static void 295 read_link(struct input_msg *msg, const char *path) 296 { 297 struct stat sb; 298 char lpath[PATH_MAX]; 299 char *copy, *root; 300 int used; 301 ssize_t size; 302 303 size = readlink(path, lpath, sizeof lpath - 1); 304 if (size == -1) { 305 msg->link_error = errno; 306 return; 307 } 308 lpath[size] = '\0'; 309 310 if (*lpath == '/') 311 strlcpy(msg->link_path, lpath, sizeof msg->link_path); 312 else { 313 copy = xstrdup(path); 314 315 root = dirname(copy); 316 if (*root == '\0' || strcmp(root, ".") == 0 || 317 strcmp (root, "/") == 0) 318 strlcpy(msg->link_path, lpath, sizeof msg->link_path); 319 else { 320 used = snprintf(msg->link_path, sizeof msg->link_path, 321 "%s/%s", root, lpath); 322 if (used < 0 || (size_t)used >= sizeof msg->link_path) { 323 msg->link_error = ENAMETOOLONG; 324 free(copy); 325 return; 326 } 327 } 328 329 free(copy); 330 } 331 332 if (Lflag) { 333 if (stat(path, &msg->sb) == -1) 334 msg->error = errno; 335 } else { 336 if (stat(path, &sb) == -1) 337 msg->link_target = errno; 338 } 339 } 340 341 static __dead void 342 child(int fd, pid_t parent, int argc, char **argv) 343 { 344 struct passwd *pw; 345 struct magic *m; 346 struct imsgbuf ibuf; 347 struct imsg imsg; 348 struct input_msg *msg; 349 struct input_ack ack; 350 struct input_file inf; 351 int i, idx; 352 size_t len, width = 0; 353 354 if (tame("stdio getpw proc recvfd", NULL) == -1) 355 err(1, "tame"); 356 357 if (geteuid() == 0) { 358 pw = getpwnam(FILE_USER); 359 if (pw == NULL) 360 errx(1, "unknown user %s", FILE_USER); 361 if (setgroups(1, &pw->pw_gid) != 0) 362 err(1, "setgroups"); 363 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) 364 err(1, "setresgid"); 365 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) 366 err(1, "setresuid"); 367 } 368 369 if (tame("stdio recvfd", NULL) == -1) 370 err(1, "tame"); 371 372 m = magic_load(magicfp, magicpath, cflag || Wflag); 373 if (cflag) { 374 magic_dump(m); 375 exit(0); 376 } 377 378 for (i = 0; i < argc; i++) { 379 len = strlen(argv[i]) + 1; 380 if (len > width) 381 width = len; 382 } 383 384 imsg_init(&ibuf, fd); 385 for (;;) { 386 if (read_message(&ibuf, &imsg, parent) == 0) 387 break; 388 if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *msg) 389 errx(1, "message too small"); 390 msg = imsg.data; 391 392 idx = msg->idx; 393 if (idx < 0 || idx >= argc) 394 errx(1, "index out of range"); 395 396 memset(&inf, 0, sizeof inf); 397 inf.m = m; 398 inf.msg = msg; 399 400 inf.path = argv[idx]; 401 inf.fd = imsg.fd; 402 403 test_file(&inf, width); 404 405 if (imsg.fd != -1) 406 close(imsg.fd); 407 imsg_free(&imsg); 408 409 ack.idx = idx; 410 send_message(&ibuf, &ack, sizeof ack, -1); 411 } 412 exit(0); 413 } 414 415 static void * 416 fill_buffer(int fd, size_t size, size_t *used) 417 { 418 static void *buffer; 419 ssize_t got; 420 size_t left; 421 void *next; 422 423 if (buffer == NULL) 424 buffer = xmalloc(FILE_READ_SIZE); 425 426 next = buffer; 427 left = size; 428 while (left != 0) { 429 got = read(fd, next, left); 430 if (got == -1) { 431 if (errno == EINTR) 432 continue; 433 return NULL; 434 } 435 if (got == 0) 436 break; 437 next = (char *)next + got; 438 left -= got; 439 } 440 *used = size - left; 441 return buffer; 442 } 443 444 static int 445 load_file(struct input_file *inf) 446 { 447 size_t used; 448 449 if (inf->msg->sb.st_size == 0 && S_ISREG(inf->msg->sb.st_mode)) 450 return (0); /* empty file */ 451 if (inf->msg->sb.st_size == 0 || inf->msg->sb.st_size > FILE_READ_SIZE) 452 inf->size = FILE_READ_SIZE; 453 else 454 inf->size = inf->msg->sb.st_size; 455 456 if (!S_ISREG(inf->msg->sb.st_mode)) 457 goto try_read; 458 459 inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0); 460 if (inf->base == MAP_FAILED) 461 goto try_read; 462 inf->mapped = 1; 463 return (0); 464 465 try_read: 466 inf->base = fill_buffer(inf->fd, inf->size, &used); 467 if (inf->base == NULL) { 468 xasprintf(&inf->result, "cannot read '%s' (%s)", inf->path, 469 strerror(errno)); 470 return (1); 471 } 472 inf->size = used; 473 return (0); 474 } 475 476 static int 477 try_stat(struct input_file *inf) 478 { 479 if (inf->msg->error != 0) { 480 xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path, 481 strerror(inf->msg->error)); 482 return (1); 483 } 484 if (sflag || strcmp(inf->path, "-") == 0) { 485 switch (inf->msg->sb.st_mode & S_IFMT) { 486 case S_IFIFO: 487 if (strcmp(inf->path, "-") != 0) 488 break; 489 case S_IFBLK: 490 case S_IFCHR: 491 case S_IFREG: 492 return (0); 493 } 494 } 495 496 if (iflag && (inf->msg->sb.st_mode & S_IFMT) != S_IFREG) { 497 xasprintf(&inf->result, "application/x-not-regular-file"); 498 return (1); 499 } 500 501 switch (inf->msg->sb.st_mode & S_IFMT) { 502 case S_IFDIR: 503 xasprintf(&inf->result, "directory"); 504 return (1); 505 case S_IFLNK: 506 if (inf->msg->link_error != 0) { 507 xasprintf(&inf->result, "unreadable symlink '%s' (%s)", 508 inf->path, strerror(inf->msg->link_error)); 509 return (1); 510 } 511 if (inf->msg->link_target == ELOOP) 512 xasprintf(&inf->result, "symbolic link in a loop"); 513 else if (inf->msg->link_target != 0) { 514 xasprintf(&inf->result, "broken symbolic link to '%s'", 515 inf->msg->link_path); 516 } else { 517 xasprintf(&inf->result, "symbolic link to '%s'", 518 inf->msg->link_path); 519 } 520 return (1); 521 case S_IFSOCK: 522 xasprintf(&inf->result, "socket"); 523 return (1); 524 case S_IFBLK: 525 xasprintf(&inf->result, "block special (%ld/%ld)", 526 (long)major(inf->msg->sb.st_rdev), 527 (long)minor(inf->msg->sb.st_rdev)); 528 return (1); 529 case S_IFCHR: 530 xasprintf(&inf->result, "character special (%ld/%ld)", 531 (long)major(inf->msg->sb.st_rdev), 532 (long)minor(inf->msg->sb.st_rdev)); 533 return (1); 534 case S_IFIFO: 535 xasprintf(&inf->result, "fifo (named pipe)"); 536 return (1); 537 } 538 return (0); 539 } 540 541 static int 542 try_empty(struct input_file *inf) 543 { 544 if (inf->size != 0) 545 return (0); 546 547 if (iflag) 548 xasprintf(&inf->result, "application/x-empty"); 549 else 550 xasprintf(&inf->result, "empty"); 551 return (1); 552 } 553 554 static int 555 try_access(struct input_file *inf) 556 { 557 char tmp[256] = ""; 558 559 if (inf->msg->sb.st_size == 0 && S_ISREG(inf->msg->sb.st_mode)) 560 return (0); /* empty file */ 561 if (inf->fd != -1) 562 return (0); 563 564 if (inf->msg->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) 565 strlcat(tmp, "writable, ", sizeof tmp); 566 if (inf->msg->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) 567 strlcat(tmp, "executable, ", sizeof tmp); 568 if (S_ISREG(inf->msg->sb.st_mode)) 569 strlcat(tmp, "regular file, ", sizeof tmp); 570 strlcat(tmp, "no read permission", sizeof tmp); 571 572 inf->result = xstrdup(tmp); 573 return (1); 574 } 575 576 static int 577 try_text(struct input_file *inf) 578 { 579 const char *type, *s; 580 int flags; 581 582 flags = MAGIC_TEST_TEXT; 583 if (iflag) 584 flags |= MAGIC_TEST_MIME; 585 586 type = text_get_type(inf->base, inf->size); 587 if (type == NULL) 588 return (0); 589 590 s = magic_test(inf->m, inf->base, inf->size, flags); 591 if (s != NULL) { 592 inf->result = xstrdup(s); 593 return (1); 594 } 595 596 s = text_try_words(inf->base, inf->size, flags); 597 if (s != NULL) { 598 if (iflag) 599 inf->result = xstrdup(s); 600 else 601 xasprintf(&inf->result, "%s %s text", type, s); 602 return (1); 603 } 604 605 if (iflag) 606 inf->result = xstrdup("text/plain"); 607 else 608 xasprintf(&inf->result, "%s text", type); 609 return (1); 610 } 611 612 static int 613 try_magic(struct input_file *inf) 614 { 615 const char *s; 616 int flags; 617 618 flags = 0; 619 if (iflag) 620 flags |= MAGIC_TEST_MIME; 621 622 s = magic_test(inf->m, inf->base, inf->size, flags); 623 if (s != NULL) { 624 inf->result = xstrdup(s); 625 return (1); 626 } 627 return (0); 628 } 629 630 static int 631 try_unknown(struct input_file *inf) 632 { 633 if (iflag) 634 xasprintf(&inf->result, "application/x-not-regular-file"); 635 else 636 xasprintf(&inf->result, "data"); 637 return (1); 638 } 639 640 static void 641 test_file(struct input_file *inf, size_t width) 642 { 643 char *label; 644 int stop; 645 646 stop = 0; 647 if (!stop) 648 stop = try_stat(inf); 649 if (!stop) 650 stop = try_access(inf); 651 if (!stop) 652 stop = load_file(inf); 653 if (!stop) 654 stop = try_empty(inf); 655 if (!stop) 656 stop = try_magic(inf); 657 if (!stop) 658 stop = try_text(inf); 659 if (!stop) 660 stop = try_unknown(inf); 661 662 if (bflag) 663 printf("%s\n", inf->result); 664 else { 665 if (strcmp(inf->path, "-") == 0) 666 xasprintf(&label, "/dev/stdin:"); 667 else 668 xasprintf(&label, "%s:", inf->path); 669 printf("%-*s %s\n", (int)width, label, inf->result); 670 free(label); 671 } 672 free(inf->result); 673 674 if (inf->mapped && inf->base != NULL) 675 munmap(inf->base, inf->size); 676 } 677