1 /* $OpenBSD: file.c,v 1.60 2017/06/28 13:37:56 brynet 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/queue.h> 23 #include <sys/socket.h> 24 #include <sys/stat.h> 25 #include <sys/uio.h> 26 #include <sys/wait.h> 27 28 #include <err.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <getopt.h> 32 #include <libgen.h> 33 #include <limits.h> 34 #include <pwd.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <time.h> 38 #include <unistd.h> 39 40 #include "file.h" 41 #include "magic.h" 42 #include "xmalloc.h" 43 44 struct input_file { 45 struct magic *m; 46 47 const char *path; 48 struct stat sb; 49 int fd; 50 int error; 51 52 char link_path[PATH_MAX]; 53 int link_error; 54 int link_target; 55 56 void *base; 57 size_t size; 58 int mapped; 59 char *result; 60 }; 61 62 extern char *__progname; 63 64 __dead void usage(void); 65 66 static void prepare_input(struct input_file *, const char *); 67 68 static void read_link(struct input_file *, const char *); 69 70 static void test_file(struct input_file *, size_t); 71 72 static int try_stat(struct input_file *); 73 static int try_empty(struct input_file *); 74 static int try_access(struct input_file *); 75 static int try_text(struct input_file *); 76 static int try_magic(struct input_file *); 77 static int try_unknown(struct input_file *); 78 79 static int bflag; 80 static int cflag; 81 static int iflag; 82 static int Lflag; 83 static int sflag; 84 static int Wflag; 85 86 static struct option longopts[] = { 87 { "brief", no_argument, NULL, 'b' }, 88 { "dereference", no_argument, NULL, 'L' }, 89 { "mime", no_argument, NULL, 'i' }, 90 { "mime-type", no_argument, NULL, 'i' }, 91 { NULL, 0, NULL, 0 } 92 }; 93 94 __dead void 95 usage(void) 96 { 97 fprintf(stderr, "usage: %s [-bchiLsW] file ...\n", __progname); 98 exit(1); 99 } 100 101 int 102 main(int argc, char **argv) 103 { 104 int opt, idx; 105 char *home, *magicpath; 106 struct passwd *pw; 107 FILE *magicfp; 108 struct magic *m; 109 struct input_file *inf = NULL; 110 size_t len, width = 0; 111 112 tzset(); 113 114 for (;;) { 115 opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL); 116 if (opt == -1) 117 break; 118 switch (opt) { 119 case 'b': 120 bflag = 1; 121 break; 122 case 'c': 123 cflag = 1; 124 break; 125 case 'h': 126 Lflag = 0; 127 break; 128 case 'i': 129 iflag = 1; 130 break; 131 case 'L': 132 Lflag = 1; 133 break; 134 case 's': 135 sflag = 1; 136 break; 137 case 'W': 138 Wflag = 1; 139 break; 140 default: 141 usage(); 142 } 143 } 144 argc -= optind; 145 argv += optind; 146 if (cflag) { 147 if (argc != 0) 148 usage(); 149 } else if (argc == 0) 150 usage(); 151 152 magicfp = NULL; 153 if (geteuid() != 0 && !issetugid()) { 154 home = getenv("HOME"); 155 if (home == NULL || *home == '\0') { 156 pw = getpwuid(getuid()); 157 if (pw != NULL) 158 home = pw->pw_dir; 159 else 160 home = NULL; 161 } 162 if (home != NULL) { 163 xasprintf(&magicpath, "%s/.magic", home); 164 magicfp = fopen(magicpath, "r"); 165 if (magicfp == NULL) 166 free(magicpath); 167 } 168 } 169 if (magicfp == NULL) { 170 magicpath = xstrdup("/etc/magic"); 171 magicfp = fopen(magicpath, "r"); 172 } 173 if (magicfp == NULL) 174 err(1, "%s", magicpath); 175 176 if (!cflag) { 177 inf = xcalloc(argc, sizeof *inf); 178 for (idx = 0; idx < argc; idx++) { 179 len = strlen(argv[idx]) + 1; 180 if (len > width) 181 width = len; 182 prepare_input(&inf[idx], argv[idx]); 183 } 184 } 185 186 if (pledge("stdio getpw id", NULL) == -1) 187 err(1, "pledge"); 188 189 if (geteuid() == 0) { 190 pw = getpwnam(FILE_USER); 191 if (pw == NULL) 192 errx(1, "unknown user %s", FILE_USER); 193 if (setgroups(1, &pw->pw_gid) != 0) 194 err(1, "setgroups"); 195 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) 196 err(1, "setresgid"); 197 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) 198 err(1, "setresuid"); 199 } 200 201 if (pledge("stdio", NULL) == -1) 202 err(1, "pledge"); 203 204 m = magic_load(magicfp, magicpath, cflag || Wflag); 205 if (cflag) { 206 magic_dump(m); 207 exit(0); 208 } 209 fclose(magicfp); 210 211 for (idx = 0; idx < argc; idx++) { 212 inf[idx].m = m; 213 test_file(&inf[idx], width); 214 } 215 exit(0); 216 } 217 218 static void 219 prepare_input(struct input_file *inf, const char *path) 220 { 221 int fd, mode, error; 222 223 if (strcmp(path, "-") == 0) { 224 if (fstat(STDIN_FILENO, &inf->sb) == -1) { 225 inf->error = errno; 226 inf->fd = -1; 227 } 228 inf->fd = STDIN_FILENO; 229 } 230 231 if (Lflag) 232 error = stat(path, &inf->sb); 233 else 234 error = lstat(path, &inf->sb); 235 if (error == -1) { 236 inf->error = errno; 237 inf->fd = -1; 238 } 239 240 /* We don't need them, so don't open directories or symlinks. */ 241 mode = inf->sb.st_mode; 242 if (!S_ISDIR(mode) && !S_ISLNK(mode)) { 243 fd = open(path, O_RDONLY|O_NONBLOCK); 244 if (fd == -1 && (errno == ENFILE || errno == EMFILE)) 245 err(1, "open"); 246 } else 247 fd = -1; 248 if (S_ISLNK(mode)) 249 read_link(inf, path); 250 inf->fd = fd; 251 inf->path = path; 252 } 253 254 static void 255 read_link(struct input_file *inf, const char *path) 256 { 257 struct stat sb; 258 char lpath[PATH_MAX]; 259 char *copy, *root; 260 int used; 261 ssize_t size; 262 263 size = readlink(path, lpath, sizeof lpath - 1); 264 if (size == -1) { 265 inf->link_error = errno; 266 return; 267 } 268 lpath[size] = '\0'; 269 270 if (*lpath == '/') 271 strlcpy(inf->link_path, lpath, sizeof inf->link_path); 272 else { 273 copy = xstrdup(path); 274 275 root = dirname(copy); 276 if (*root == '\0' || strcmp(root, ".") == 0 || 277 strcmp (root, "/") == 0) 278 strlcpy(inf->link_path, lpath, sizeof inf->link_path); 279 else { 280 used = snprintf(inf->link_path, sizeof inf->link_path, 281 "%s/%s", root, lpath); 282 if (used < 0 || (size_t)used >= sizeof inf->link_path) { 283 inf->link_error = ENAMETOOLONG; 284 free(copy); 285 return; 286 } 287 } 288 289 free(copy); 290 } 291 292 if (!Lflag && stat(path, &sb) == -1) 293 inf->link_target = errno; 294 } 295 296 static void * 297 fill_buffer(int fd, size_t size, size_t *used) 298 { 299 static void *buffer; 300 ssize_t got; 301 size_t left; 302 void *next; 303 304 if (buffer == NULL) 305 buffer = xmalloc(FILE_READ_SIZE); 306 307 next = buffer; 308 left = size; 309 while (left != 0) { 310 got = read(fd, next, left); 311 if (got == -1) { 312 if (errno == EINTR) 313 continue; 314 return (NULL); 315 } 316 if (got == 0) 317 break; 318 next = (char *)next + got; 319 left -= got; 320 } 321 *used = size - left; 322 return (buffer); 323 } 324 325 static int 326 load_file(struct input_file *inf) 327 { 328 size_t used; 329 330 if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode)) 331 return (0); /* empty file */ 332 if (inf->sb.st_size == 0 || inf->sb.st_size > FILE_READ_SIZE) 333 inf->size = FILE_READ_SIZE; 334 else 335 inf->size = inf->sb.st_size; 336 337 if (!S_ISREG(inf->sb.st_mode)) 338 goto try_read; 339 340 inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0); 341 if (inf->base == MAP_FAILED) 342 goto try_read; 343 inf->mapped = 1; 344 return (0); 345 346 try_read: 347 inf->base = fill_buffer(inf->fd, inf->size, &used); 348 if (inf->base == NULL) { 349 xasprintf(&inf->result, "cannot read '%s' (%s)", inf->path, 350 strerror(errno)); 351 return (1); 352 } 353 inf->size = used; 354 return (0); 355 } 356 357 static int 358 try_stat(struct input_file *inf) 359 { 360 if (inf->error != 0) { 361 xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path, 362 strerror(inf->error)); 363 return (1); 364 } 365 if (sflag || strcmp(inf->path, "-") == 0) { 366 switch (inf->sb.st_mode & S_IFMT) { 367 case S_IFIFO: 368 if (strcmp(inf->path, "-") != 0) 369 break; 370 case S_IFBLK: 371 case S_IFCHR: 372 case S_IFREG: 373 return (0); 374 } 375 } 376 377 if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) { 378 xasprintf(&inf->result, "application/x-not-regular-file"); 379 return (1); 380 } 381 382 switch (inf->sb.st_mode & S_IFMT) { 383 case S_IFDIR: 384 xasprintf(&inf->result, "directory"); 385 return (1); 386 case S_IFLNK: 387 if (inf->link_error != 0) { 388 xasprintf(&inf->result, "unreadable symlink '%s' (%s)", 389 inf->path, strerror(inf->link_error)); 390 return (1); 391 } 392 if (inf->link_target == ELOOP) 393 xasprintf(&inf->result, "symbolic link in a loop"); 394 else if (inf->link_target != 0) { 395 xasprintf(&inf->result, "broken symbolic link to '%s'", 396 inf->link_path); 397 } else { 398 xasprintf(&inf->result, "symbolic link to '%s'", 399 inf->link_path); 400 } 401 return (1); 402 case S_IFSOCK: 403 xasprintf(&inf->result, "socket"); 404 return (1); 405 case S_IFBLK: 406 xasprintf(&inf->result, "block special (%ld/%ld)", 407 (long)major(inf->sb.st_rdev), 408 (long)minor(inf->sb.st_rdev)); 409 return (1); 410 case S_IFCHR: 411 xasprintf(&inf->result, "character special (%ld/%ld)", 412 (long)major(inf->sb.st_rdev), 413 (long)minor(inf->sb.st_rdev)); 414 return (1); 415 case S_IFIFO: 416 xasprintf(&inf->result, "fifo (named pipe)"); 417 return (1); 418 } 419 return (0); 420 } 421 422 static int 423 try_empty(struct input_file *inf) 424 { 425 if (inf->size != 0) 426 return (0); 427 428 if (iflag) 429 xasprintf(&inf->result, "application/x-empty"); 430 else 431 xasprintf(&inf->result, "empty"); 432 return (1); 433 } 434 435 static int 436 try_access(struct input_file *inf) 437 { 438 char tmp[256] = ""; 439 440 if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode)) 441 return (0); /* empty file */ 442 if (inf->fd != -1) 443 return (0); 444 445 if (inf->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) 446 strlcat(tmp, "writable, ", sizeof tmp); 447 if (inf->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) 448 strlcat(tmp, "executable, ", sizeof tmp); 449 if (S_ISREG(inf->sb.st_mode)) 450 strlcat(tmp, "regular file, ", sizeof tmp); 451 strlcat(tmp, "no read permission", sizeof tmp); 452 453 inf->result = xstrdup(tmp); 454 return (1); 455 } 456 457 static int 458 try_text(struct input_file *inf) 459 { 460 const char *type, *s; 461 int flags; 462 463 flags = MAGIC_TEST_TEXT; 464 if (iflag) 465 flags |= MAGIC_TEST_MIME; 466 467 type = text_get_type(inf->base, inf->size); 468 if (type == NULL) 469 return (0); 470 471 s = magic_test(inf->m, inf->base, inf->size, flags); 472 if (s != NULL) { 473 inf->result = xstrdup(s); 474 return (1); 475 } 476 477 s = text_try_words(inf->base, inf->size, flags); 478 if (s != NULL) { 479 if (iflag) 480 inf->result = xstrdup(s); 481 else 482 xasprintf(&inf->result, "%s %s text", type, s); 483 return (1); 484 } 485 486 if (iflag) 487 inf->result = xstrdup("text/plain"); 488 else 489 xasprintf(&inf->result, "%s text", type); 490 return (1); 491 } 492 493 static int 494 try_magic(struct input_file *inf) 495 { 496 const char *s; 497 int flags; 498 499 flags = 0; 500 if (iflag) 501 flags |= MAGIC_TEST_MIME; 502 503 s = magic_test(inf->m, inf->base, inf->size, flags); 504 if (s != NULL) { 505 inf->result = xstrdup(s); 506 return (1); 507 } 508 return (0); 509 } 510 511 static int 512 try_unknown(struct input_file *inf) 513 { 514 if (iflag) 515 xasprintf(&inf->result, "application/x-not-regular-file"); 516 else 517 xasprintf(&inf->result, "data"); 518 return (1); 519 } 520 521 static void 522 test_file(struct input_file *inf, size_t width) 523 { 524 char *label; 525 int stop; 526 527 stop = 0; 528 if (!stop) 529 stop = try_stat(inf); 530 if (!stop) 531 stop = try_access(inf); 532 if (!stop) 533 stop = load_file(inf); 534 if (!stop) 535 stop = try_empty(inf); 536 if (!stop) 537 stop = try_magic(inf); 538 if (!stop) 539 stop = try_text(inf); 540 if (!stop) 541 stop = try_unknown(inf); 542 543 if (bflag) 544 printf("%s\n", inf->result); 545 else { 546 if (strcmp(inf->path, "-") == 0) 547 xasprintf(&label, "/dev/stdin:"); 548 else 549 xasprintf(&label, "%s:", inf->path); 550 printf("%-*s %s\n", (int)width, label, inf->result); 551 free(label); 552 } 553 free(inf->result); 554 555 if (inf->mapped && inf->base != NULL) 556 munmap(inf->base, inf->size); 557 } 558