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