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