Annotation of src/usr.bin/file/file.c, Revision 1.42
1.42 ! nicm 1: /* $OpenBSD: file.c,v 1.41 2015/05/29 11:03:37 nicm Exp $ */
1.27 nicm 2:
1.14 tedu 3: /*
1.27 nicm 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.
1.14 tedu 17: */
1.11 ian 18:
1.20 deraadt 19: #include <sys/types.h>
1.27 nicm 20: #include <sys/ioctl.h>
21: #include <sys/mman.h>
1.35 nicm 22: #include <sys/socket.h>
23: #include <sys/queue.h>
24: #include <sys/uio.h>
25: #include <sys/wait.h>
1.20 deraadt 26:
1.27 nicm 27: #include <errno.h>
1.35 nicm 28: #include <imsg.h>
1.27 nicm 29: #include <libgen.h>
30: #include <getopt.h>
31: #include <fcntl.h>
32: #include <pwd.h>
1.1 deraadt 33: #include <stdlib.h>
1.14 tedu 34: #include <unistd.h>
35:
1.27 nicm 36: #include "file.h"
37: #include "magic.h"
38: #include "xmalloc.h"
1.1 deraadt 39:
1.35 nicm 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:
1.27 nicm 57: struct input_file
58: {
1.35 nicm 59: struct magic *m;
60: struct input_msg *msg;
1.1 deraadt 61:
1.35 nicm 62: const char *path;
63: int fd;
1.14 tedu 64:
1.35 nicm 65: void *base;
66: size_t size;
67: int mapped;
68: char *result;
1.27 nicm 69: };
1.1 deraadt 70:
1.27 nicm 71: extern char *__progname;
1.1 deraadt 72:
1.27 nicm 73: __dead void usage(void);
1.1 deraadt 74:
1.35 nicm 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);
1.27 nicm 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:
1.35 nicm 98: static char *magicpath;
99: static FILE *magicfp;
100:
1.27 nicm 101: static struct option longopts[] = {
102: { "mime", no_argument, NULL, 'i' },
103: { "mime-type", no_argument, NULL, 'i' },
104: { NULL, 0, NULL, 0 }
105: };
1.14 tedu 106:
1.27 nicm 107: __dead void
108: usage(void)
109: {
1.39 jmc 110: fprintf(stderr, "usage: %s [-bchiLsW] file ...\n", __progname);
1.27 nicm 111: exit(1);
112: }
1.14 tedu 113:
1.1 deraadt 114: int
1.27 nicm 115: main(int argc, char **argv)
1.1 deraadt 116: {
1.35 nicm 117: int opt, pair[2], fd, idx;
118: char *home;
1.27 nicm 119: struct passwd *pw;
1.35 nicm 120: struct imsgbuf ibuf;
121: struct imsg imsg;
122: struct input_msg msg;
123: struct input_ack *ack;
124: pid_t pid, parent;
1.27 nicm 125:
126: for (;;) {
127: opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL);
128: if (opt == -1)
1.18 chl 129: break;
1.27 nicm 130: switch (opt) {
1.9 millert 131: case 'b':
1.27 nicm 132: bflag = 1;
1.9 millert 133: break;
1.1 deraadt 134: case 'c':
1.27 nicm 135: cflag = 1;
1.14 tedu 136: break;
1.27 nicm 137: case 'h':
138: Lflag = 0;
1.14 tedu 139: break;
1.19 chl 140: case 'i':
1.27 nicm 141: iflag = 1;
1.19 chl 142: break;
1.27 nicm 143: case 'L':
144: Lflag = 1;
1.14 tedu 145: break;
146: case 's':
1.27 nicm 147: sflag = 1;
1.14 tedu 148: break;
1.27 nicm 149: case 'W':
150: Wflag = 1;
1.18 chl 151: break;
1.1 deraadt 152: default:
1.27 nicm 153: usage();
1.1 deraadt 154: }
1.27 nicm 155: }
156: argc -= optind;
157: argv += optind;
158: if (cflag) {
159: if (argc != 0)
160: usage();
161: } else if (argc == 0)
162: usage();
1.1 deraadt 163:
1.35 nicm 164: magicfp = NULL;
1.32 nicm 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) {
1.35 nicm 175: xasprintf(&magicpath, "%s/.magic", home);
176: magicfp = fopen(magicpath, "r");
177: if (magicfp == NULL)
178: free(magicpath);
1.32 nicm 179: }
1.27 nicm 180: }
1.35 nicm 181: if (magicfp == NULL) {
182: magicpath = xstrdup("/etc/magic");
183: magicfp = fopen(magicpath, "r");
184: }
185: if (magicfp == NULL)
186: err(1, "%s", magicpath);
187:
188: parent = getpid();
189: if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
190: err(1, "socketpair");
1.36 nicm 191: pid = sandbox_fork(FILE_USER);
192: if (pid == 0) {
1.35 nicm 193: close(pair[0]);
194: child(pair[1], parent, argc, argv);
195: }
196: close(pair[1]);
197:
198: fclose(magicfp);
199: magicfp = NULL;
200:
201: if (cflag)
202: goto wait_for_child;
203:
204: imsg_init(&ibuf, pair[0]);
205: for (idx = 0; idx < argc; idx++) {
206: memset(&msg, 0, sizeof msg);
207: msg.idx = idx;
208:
209: if (lstat(argv[idx], &msg.sb) == -1) {
210: fd = -1;
211: msg.error = errno;
212: } else {
213: fd = open(argv[idx], O_RDONLY|O_NONBLOCK);
214: if (fd == -1 && (errno == ENFILE || errno == EMFILE))
215: err(1, "open");
216: if (S_ISLNK(msg.sb.st_mode))
217: read_link(&msg, argv[idx]);
218: }
219: send_message(&ibuf, &msg, sizeof msg, fd);
1.1 deraadt 220:
1.35 nicm 221: if (read_message(&ibuf, &imsg, pid) == 0)
222: break;
223: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *ack)
224: errx(1, "message too small");
225: ack = imsg.data;
226: if (ack->idx != idx)
227: errx(1, "index not expected");
228: imsg_free(&imsg);
1.1 deraadt 229: }
230:
1.35 nicm 231: wait_for_child:
232: close(pair[0]);
233: while (wait(NULL) == -1 && errno != ECHILD) {
234: if (errno != EINTR)
235: err(1, "wait");
1.30 nicm 236: }
1.35 nicm 237: _exit(0); /* let the child flush */
1.1 deraadt 238: }
239:
1.27 nicm 240: static void
1.35 nicm 241: send_message(struct imsgbuf *ibuf, void *msg, size_t msglen, int fd)
242: {
243: if (imsg_compose(ibuf, -1, -1, 0, fd, msg, msglen) != 1)
244: err(1, "imsg_compose");
245: if (imsg_flush(ibuf) != 0)
246: err(1, "imsg_flush");
247: }
248:
249: static int
250: read_message(struct imsgbuf *ibuf, struct imsg *imsg, pid_t from)
1.27 nicm 251: {
1.35 nicm 252: int n;
1.1 deraadt 253:
1.35 nicm 254: if ((n = imsg_read(ibuf)) == -1)
255: err(1, "imsg_read");
256: if (n == 0)
257: return (0);
1.27 nicm 258:
1.35 nicm 259: if ((n = imsg_get(ibuf, imsg)) == -1)
260: err(1, "imsg_get");
261: if (n == 0)
262: return (0);
1.31 nicm 263:
1.35 nicm 264: if ((pid_t)imsg->hdr.pid != from)
265: errx(1, "PIDs don't match");
1.27 nicm 266:
1.35 nicm 267: return (n);
1.27 nicm 268:
269: }
270:
271: static void
1.35 nicm 272: read_link(struct input_msg *msg, const char *path)
1.14 tedu 273: {
1.27 nicm 274: struct stat sb;
1.35 nicm 275: char lpath[PATH_MAX];
1.27 nicm 276: char *copy, *root;
277: int used;
278: ssize_t size;
279:
1.35 nicm 280: size = readlink(path, lpath, sizeof lpath);
1.27 nicm 281: if (size == -1) {
1.35 nicm 282: msg->link_error = errno;
1.14 tedu 283: return;
1.27 nicm 284: }
1.35 nicm 285: lpath[size] = '\0';
1.27 nicm 286:
1.35 nicm 287: if (*lpath == '/')
288: strlcpy(msg->link_path, lpath, sizeof msg->link_path);
1.27 nicm 289: else {
1.35 nicm 290: copy = xstrdup(path);
1.27 nicm 291:
292: root = dirname(copy);
293: if (*root == '\0' || strcmp(root, ".") == 0 ||
294: strcmp (root, "/") == 0)
1.35 nicm 295: strlcpy(msg->link_path, lpath, sizeof msg->link_path);
1.27 nicm 296: else {
1.35 nicm 297: used = snprintf(msg->link_path, sizeof msg->link_path,
298: "%s/%s", root, lpath);
299: if (used < 0 || (size_t)used >= sizeof msg->link_path) {
300: msg->link_error = ENAMETOOLONG;
1.37 lteo 301: free(copy);
1.27 nicm 302: return;
303: }
304: }
305:
306: free(copy);
307: }
308:
309: if (Lflag) {
1.35 nicm 310: if (stat(path, &msg->sb) == -1)
311: msg->error = errno;
1.27 nicm 312: } else {
1.35 nicm 313: if (stat(path, &sb) == -1)
314: msg->link_target = errno;
1.14 tedu 315: }
316: }
317:
1.35 nicm 318: static __dead void
319: child(int fd, pid_t parent, int argc, char **argv)
320: {
321: struct magic *m;
322: struct imsgbuf ibuf;
323: struct imsg imsg;
324: struct input_msg *msg;
325: struct input_ack ack;
326: struct input_file inf;
327: int i, idx;
328: size_t len, width = 0;
329:
330: m = magic_load(magicfp, magicpath, cflag || Wflag);
331: if (cflag) {
332: magic_dump(m);
333: exit(0);
334: }
335:
336: for (i = 0; i < argc; i++) {
337: len = strlen(argv[i]) + 1;
338: if (len > width)
339: width = len;
340: }
341:
342: imsg_init(&ibuf, fd);
343: for (;;) {
344: if (read_message(&ibuf, &imsg, parent) == 0)
345: break;
346: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *msg)
347: errx(1, "message too small");
348: msg = imsg.data;
349:
350: idx = msg->idx;
351: if (idx < 0 || idx >= argc)
352: errx(1, "index out of range");
353:
354: memset(&inf, 0, sizeof inf);
355: inf.m = m;
356: inf.msg = msg;
357:
358: inf.path = argv[idx];
359: inf.fd = imsg.fd;
360:
361: test_file(&inf, width);
362:
363: if (imsg.fd != -1)
364: close(imsg.fd);
365: imsg_free(&imsg);
366:
367: ack.idx = idx;
368: send_message(&ibuf, &ack, sizeof ack, -1);
369: }
370: exit(0);
371: }
372:
1.27 nicm 373: static void *
1.42 ! nicm 374: fill_buffer(int fd, size_t size, size_t *used)
1.1 deraadt 375: {
1.27 nicm 376: static void *buffer;
377: ssize_t got;
378: size_t left;
379: void *next;
380:
381: if (buffer == NULL)
382: buffer = xmalloc(FILE_READ_SIZE);
383:
384: next = buffer;
1.42 ! nicm 385: left = size;
1.27 nicm 386: while (left != 0) {
1.42 ! nicm 387: got = read(fd, next, left);
1.27 nicm 388: if (got == -1) {
389: if (errno == EINTR)
390: continue;
391: return NULL;
1.5 millert 392: }
1.27 nicm 393: if (got == 0)
394: break;
1.30 nicm 395: next = (char *)next + got;
1.27 nicm 396: left -= got;
397: }
1.42 ! nicm 398: *used = size - left;
1.27 nicm 399: return buffer;
400: }
401:
402: static int
403: load_file(struct input_file *inf)
404: {
1.42 ! nicm 405: size_t used;
! 406:
1.35 nicm 407: inf->size = inf->msg->sb.st_size;
1.27 nicm 408: if (inf->size > FILE_READ_SIZE)
409: inf->size = FILE_READ_SIZE;
1.34 nicm 410: if (inf->size == 0) {
1.35 nicm 411: if (!S_ISREG(inf->msg->sb.st_mode))
1.34 nicm 412: inf->size = FILE_READ_SIZE;
413: else
414: return (0);
415: }
1.27 nicm 416:
417: inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);
418: if (inf->base == MAP_FAILED) {
1.42 ! nicm 419: inf->base = fill_buffer(inf->fd, inf->size, &used);
1.27 nicm 420: if (inf->base == NULL) {
421: xasprintf(&inf->result, "cannot read '%s' (%s)",
422: inf->path, strerror(errno));
423: return (1);
424: }
1.42 ! nicm 425: inf->size = used;
1.27 nicm 426: } else
427: inf->mapped = 1;
428: return (0);
429: }
1.5 millert 430:
1.27 nicm 431: static int
432: try_stat(struct input_file *inf)
433: {
1.35 nicm 434: if (inf->msg->error != 0) {
1.27 nicm 435: xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,
1.35 nicm 436: strerror(inf->msg->error));
1.27 nicm 437: return (1);
438: }
439: if (sflag) {
1.35 nicm 440: switch (inf->msg->sb.st_mode & S_IFMT) {
1.27 nicm 441: case S_IFBLK:
442: case S_IFCHR:
443: case S_IFREG:
444: return (0);
1.5 millert 445: }
1.27 nicm 446: }
1.1 deraadt 447:
1.35 nicm 448: if (iflag && (inf->msg->sb.st_mode & S_IFMT) != S_IFREG) {
1.27 nicm 449: xasprintf(&inf->result, "application/x-not-regular-file");
450: return (1);
1.1 deraadt 451: }
452:
1.35 nicm 453: switch (inf->msg->sb.st_mode & S_IFMT) {
1.27 nicm 454: case S_IFDIR:
455: xasprintf(&inf->result, "directory");
456: return (1);
457: case S_IFLNK:
1.35 nicm 458: if (inf->msg->link_error != 0) {
1.27 nicm 459: xasprintf(&inf->result, "unreadable symlink '%s' (%s)",
1.35 nicm 460: inf->path, strerror(inf->msg->link_error));
1.27 nicm 461: return (1);
462: }
1.35 nicm 463: if (inf->msg->link_target == ELOOP)
1.27 nicm 464: xasprintf(&inf->result, "symbolic link in a loop");
1.35 nicm 465: else if (inf->msg->link_target != 0) {
1.27 nicm 466: xasprintf(&inf->result, "broken symbolic link to '%s'",
1.35 nicm 467: inf->msg->link_path);
1.27 nicm 468: } else {
469: xasprintf(&inf->result, "symbolic link to '%s'",
1.35 nicm 470: inf->msg->link_path);
1.27 nicm 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)",
1.35 nicm 478: (long)major(inf->msg->sb.st_rdev),
479: (long)minor(inf->msg->sb.st_rdev));
1.27 nicm 480: return (1);
481: case S_IFCHR:
482: xasprintf(&inf->result, "character special (%ld/%ld)",
1.35 nicm 483: (long)major(inf->msg->sb.st_rdev),
484: (long)minor(inf->msg->sb.st_rdev));
1.27 nicm 485: return (1);
486: case S_IFIFO:
487: xasprintf(&inf->result, "fifo (named pipe)");
488: return (1);
1.1 deraadt 489: }
1.27 nicm 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);
1.1 deraadt 513:
1.35 nicm 514: if (inf->msg->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
1.27 nicm 515: strlcat(tmp, "writable, ", sizeof tmp);
1.35 nicm 516: if (inf->msg->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
1.27 nicm 517: strlcat(tmp, "executable, ", sizeof tmp);
1.35 nicm 518: if (S_ISREG(inf->msg->sb.st_mode))
1.27 nicm 519: strlcat(tmp, "regular file, ", sizeof tmp);
520: strlcat(tmp, "no read permission", sizeof tmp);
521:
522: inf->result = xstrdup(tmp);
523: return (1);
1.1 deraadt 524: }
525:
1.27 nicm 526: static int
527: try_text(struct input_file *inf)
1.14 tedu 528: {
1.27 nicm 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);
1.14 tedu 539:
1.27 nicm 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);
1.18 chl 550: else
1.27 nicm 551: xasprintf(&inf->result, "%s %s text", type, s);
552: return (1);
1.18 chl 553: }
1.14 tedu 554:
1.27 nicm 555: if (iflag)
556: inf->result = xstrdup("text/plain");
1.14 tedu 557: else
1.27 nicm 558: xasprintf(&inf->result, "%s text", type);
559: return (1);
1.14 tedu 560: }
561:
1.27 nicm 562: static int
563: try_magic(struct input_file *inf)
1.1 deraadt 564: {
1.27 nicm 565: const char *s;
566: int flags;
1.1 deraadt 567:
1.27 nicm 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);
1.1 deraadt 576: }
1.27 nicm 577: return (0);
1.14 tedu 578: }
1.1 deraadt 579:
1.27 nicm 580: static int
581: try_unknown(struct input_file *inf)
1.14 tedu 582: {
1.27 nicm 583: if (iflag)
584: xasprintf(&inf->result, "application/x-not-regular-file");
585: else
586: xasprintf(&inf->result, "data");
587: return (1);
1.1 deraadt 588: }
589:
1.27 nicm 590: static void
1.35 nicm 591: test_file(struct input_file *inf, size_t width)
1.1 deraadt 592: {
1.35 nicm 593: char *label;
594: int stop;
1.27 nicm 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);
1.35 nicm 614: else {
615: xasprintf(&label, "%s:", inf->path);
616: printf("%-*s %s\n", (int)width, label, inf->result);
617: free(label);
618: }
1.30 nicm 619: free(inf->result);
1.27 nicm 620:
621: if (inf->mapped && inf->base != NULL)
622: munmap(inf->base, inf->size);
1.1 deraadt 623: }