xref: /openbsd-src/usr.bin/file/file.c (revision 4d4ce5ea4bf409dc059c2ac2ee4c02f874b8ef0a)
1 /* $OpenBSD: file.c,v 1.54 2015/11/13 08:30:18 nicm 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, error;
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 {
222 			if (Lflag)
223 				error = stat(argv[idx], &msg.sb);
224 			else
225 				error = lstat(argv[idx], &msg.sb);
226 			if (error == -1) {
227 				fd = -1;
228 				msg.error = errno;
229 			} else {
230 				/*
231 				 * pledge(2) doesn't let us pass directory file
232 				 * descriptors around - but in fact we don't
233 				 * need them, so just don't open directories or
234 				 * symlinks (which could be to directories).
235 				 */
236 				mode = msg.sb.st_mode;
237 				if (!S_ISDIR(mode) && !S_ISLNK(mode)) {
238 					fd = open(argv[idx],
239 					    O_RDONLY|O_NONBLOCK);
240 					if (fd == -1 && (errno == ENFILE ||
241 					    errno == EMFILE))
242 						err(1, "open");
243 				} else
244 					fd = -1;
245 				if (S_ISLNK(mode))
246 					read_link(&msg, argv[idx]);
247 			}
248 		}
249 		send_message(&ibuf, &msg, sizeof msg, fd);
250 
251 		if (read_message(&ibuf, &imsg, pid) == 0)
252 			break;
253 		if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *ack)
254 			errx(1, "message too small");
255 		ack = imsg.data;
256 		if (ack->idx != idx)
257 			errx(1, "index not expected");
258 		imsg_free(&imsg);
259 	}
260 
261 wait_for_child:
262 	close(pair[0]);
263 	while (wait(NULL) == -1 && errno != ECHILD) {
264 		if (errno != EINTR)
265 			err(1, "wait");
266 	}
267 	_exit(0); /* let the child flush */
268 }
269 
270 static void
271 send_message(struct imsgbuf *ibuf, void *msg, size_t msglen, int fd)
272 {
273 	if (imsg_compose(ibuf, -1, -1, 0, fd, msg, msglen) != 1)
274 		err(1, "imsg_compose");
275 	if (imsg_flush(ibuf) != 0)
276 		err(1, "imsg_flush");
277 }
278 
279 static int
280 read_message(struct imsgbuf *ibuf, struct imsg *imsg, pid_t from)
281 {
282 	int	n;
283 
284 	if ((n = imsg_read(ibuf)) == -1)
285 		err(1, "imsg_read");
286 	if (n == 0)
287 		return (0);
288 
289 	if ((n = imsg_get(ibuf, imsg)) == -1)
290 		err(1, "imsg_get");
291 	if (n == 0)
292 		return (0);
293 
294 	if ((pid_t)imsg->hdr.pid != from)
295 		errx(1, "PIDs don't match");
296 
297 	return (n);
298 
299 }
300 
301 static void
302 read_link(struct input_msg *msg, const char *path)
303 {
304 	struct stat	 sb;
305 	char		 lpath[PATH_MAX];
306 	char		*copy, *root;
307 	int		 used;
308 	ssize_t		 size;
309 
310 	size = readlink(path, lpath, sizeof lpath - 1);
311 	if (size == -1) {
312 		msg->link_error = errno;
313 		return;
314 	}
315 	lpath[size] = '\0';
316 
317 	if (*lpath == '/')
318 		strlcpy(msg->link_path, lpath, sizeof msg->link_path);
319 	else {
320 		copy = xstrdup(path);
321 
322 		root = dirname(copy);
323 		if (*root == '\0' || strcmp(root, ".") == 0 ||
324 		    strcmp (root, "/") == 0)
325 			strlcpy(msg->link_path, lpath, sizeof msg->link_path);
326 		else {
327 			used = snprintf(msg->link_path, sizeof msg->link_path,
328 			    "%s/%s", root, lpath);
329 			if (used < 0 || (size_t)used >= sizeof msg->link_path) {
330 				msg->link_error = ENAMETOOLONG;
331 				free(copy);
332 				return;
333 			}
334 		}
335 
336 		free(copy);
337 	}
338 
339 	if (!Lflag && stat(path, &sb) == -1)
340 		msg->link_target = errno;
341 }
342 
343 static __dead void
344 child(int fd, pid_t parent, int argc, char **argv)
345 {
346 	struct passwd		*pw;
347 	struct magic		*m;
348 	struct imsgbuf		 ibuf;
349 	struct imsg		 imsg;
350 	struct input_msg	*msg;
351 	struct input_ack	 ack;
352 	struct input_file	 inf;
353 	int			 i, idx;
354 	size_t			 len, width = 0;
355 
356 	if (pledge("stdio getpw recvfd id", NULL) == -1)
357 		err(1, "pledge");
358 
359 	if (geteuid() == 0) {
360 		pw = getpwnam(FILE_USER);
361 		if (pw == NULL)
362 			errx(1, "unknown user %s", FILE_USER);
363 		if (setgroups(1, &pw->pw_gid) != 0)
364 			err(1, "setgroups");
365 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
366 			err(1, "setresgid");
367 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
368 			err(1, "setresuid");
369 	}
370 
371 	if (pledge("stdio recvfd", NULL) == -1)
372 		err(1, "pledge");
373 
374 	m = magic_load(magicfp, magicpath, cflag || Wflag);
375 	if (cflag) {
376 		magic_dump(m);
377 		exit(0);
378 	}
379 
380 	for (i = 0; i < argc; i++) {
381 		len = strlen(argv[i]) + 1;
382 		if (len > width)
383 			width = len;
384 	}
385 
386 	imsg_init(&ibuf, fd);
387 	for (;;) {
388 		if (read_message(&ibuf, &imsg, parent) == 0)
389 			break;
390 		if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *msg)
391 			errx(1, "message too small");
392 		msg = imsg.data;
393 
394 		idx = msg->idx;
395 		if (idx < 0 || idx >= argc)
396 			errx(1, "index out of range");
397 
398 		memset(&inf, 0, sizeof inf);
399 		inf.m = m;
400 		inf.msg = msg;
401 
402 		inf.path = argv[idx];
403 		inf.fd = imsg.fd;
404 
405 		test_file(&inf, width);
406 
407 		if (imsg.fd != -1)
408 			close(imsg.fd);
409 		imsg_free(&imsg);
410 
411 		ack.idx = idx;
412 		send_message(&ibuf, &ack, sizeof ack, -1);
413 	}
414 	exit(0);
415 }
416 
417 static void *
418 fill_buffer(int fd, size_t size, size_t *used)
419 {
420 	static void	*buffer;
421 	ssize_t		 got;
422 	size_t		 left;
423 	void		*next;
424 
425 	if (buffer == NULL)
426 		buffer = xmalloc(FILE_READ_SIZE);
427 
428 	next = buffer;
429 	left = size;
430 	while (left != 0) {
431 		got = read(fd, next, left);
432 		if (got == -1) {
433 			if (errno == EINTR)
434 				continue;
435 			return NULL;
436 		}
437 		if (got == 0)
438 			break;
439 		next = (char *)next + got;
440 		left -= got;
441 	}
442 	*used = size - left;
443 	return buffer;
444 }
445 
446 static int
447 load_file(struct input_file *inf)
448 {
449 	size_t	used;
450 
451 	if (inf->msg->sb.st_size == 0 && S_ISREG(inf->msg->sb.st_mode))
452 		return (0); /* empty file */
453 	if (inf->msg->sb.st_size == 0 || inf->msg->sb.st_size > FILE_READ_SIZE)
454 		inf->size = FILE_READ_SIZE;
455 	else
456 		inf->size = inf->msg->sb.st_size;
457 
458 	if (!S_ISREG(inf->msg->sb.st_mode))
459 		goto try_read;
460 
461 	inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);
462 	if (inf->base == MAP_FAILED)
463 		goto try_read;
464 	inf->mapped = 1;
465 	return (0);
466 
467 try_read:
468 	inf->base = fill_buffer(inf->fd, inf->size, &used);
469 	if (inf->base == NULL) {
470 		xasprintf(&inf->result, "cannot read '%s' (%s)", inf->path,
471 		    strerror(errno));
472 		return (1);
473 	}
474 	inf->size = used;
475 	return (0);
476 }
477 
478 static int
479 try_stat(struct input_file *inf)
480 {
481 	if (inf->msg->error != 0) {
482 		xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,
483 		    strerror(inf->msg->error));
484 		return (1);
485 	}
486 	if (sflag || strcmp(inf->path, "-") == 0) {
487 		switch (inf->msg->sb.st_mode & S_IFMT) {
488 		case S_IFIFO:
489 			if (strcmp(inf->path, "-") != 0)
490 				break;
491 		case S_IFBLK:
492 		case S_IFCHR:
493 		case S_IFREG:
494 			return (0);
495 		}
496 	}
497 
498 	if (iflag && (inf->msg->sb.st_mode & S_IFMT) != S_IFREG) {
499 		xasprintf(&inf->result, "application/x-not-regular-file");
500 		return (1);
501 	}
502 
503 	switch (inf->msg->sb.st_mode & S_IFMT) {
504 	case S_IFDIR:
505 		xasprintf(&inf->result, "directory");
506 		return (1);
507 	case S_IFLNK:
508 		if (inf->msg->link_error != 0) {
509 			xasprintf(&inf->result, "unreadable symlink '%s' (%s)",
510 			    inf->path, strerror(inf->msg->link_error));
511 			return (1);
512 		}
513 		if (inf->msg->link_target == ELOOP)
514 			xasprintf(&inf->result, "symbolic link in a loop");
515 		else if (inf->msg->link_target != 0) {
516 			xasprintf(&inf->result, "broken symbolic link to '%s'",
517 			    inf->msg->link_path);
518 		} else {
519 			xasprintf(&inf->result, "symbolic link to '%s'",
520 			    inf->msg->link_path);
521 		}
522 		return (1);
523 	case S_IFSOCK:
524 		xasprintf(&inf->result, "socket");
525 		return (1);
526 	case S_IFBLK:
527 		xasprintf(&inf->result, "block special (%ld/%ld)",
528 		    (long)major(inf->msg->sb.st_rdev),
529 		    (long)minor(inf->msg->sb.st_rdev));
530 		return (1);
531 	case S_IFCHR:
532 		xasprintf(&inf->result, "character special (%ld/%ld)",
533 		    (long)major(inf->msg->sb.st_rdev),
534 		    (long)minor(inf->msg->sb.st_rdev));
535 		return (1);
536 	case S_IFIFO:
537 		xasprintf(&inf->result, "fifo (named pipe)");
538 		return (1);
539 	}
540 	return (0);
541 }
542 
543 static int
544 try_empty(struct input_file *inf)
545 {
546 	if (inf->size != 0)
547 		return (0);
548 
549 	if (iflag)
550 		xasprintf(&inf->result, "application/x-empty");
551 	else
552 		xasprintf(&inf->result, "empty");
553 	return (1);
554 }
555 
556 static int
557 try_access(struct input_file *inf)
558 {
559 	char tmp[256] = "";
560 
561 	if (inf->msg->sb.st_size == 0 && S_ISREG(inf->msg->sb.st_mode))
562 		return (0); /* empty file */
563 	if (inf->fd != -1)
564 		return (0);
565 
566 	if (inf->msg->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
567 		strlcat(tmp, "writable, ", sizeof tmp);
568 	if (inf->msg->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
569 		strlcat(tmp, "executable, ", sizeof tmp);
570 	if (S_ISREG(inf->msg->sb.st_mode))
571 		strlcat(tmp, "regular file, ", sizeof tmp);
572 	strlcat(tmp, "no read permission", sizeof tmp);
573 
574 	inf->result = xstrdup(tmp);
575 	return (1);
576 }
577 
578 static int
579 try_text(struct input_file *inf)
580 {
581 	const char	*type, *s;
582 	int		 flags;
583 
584 	flags = MAGIC_TEST_TEXT;
585 	if (iflag)
586 		flags |= MAGIC_TEST_MIME;
587 
588 	type = text_get_type(inf->base, inf->size);
589 	if (type == NULL)
590 		return (0);
591 
592 	s = magic_test(inf->m, inf->base, inf->size, flags);
593 	if (s != NULL) {
594 		inf->result = xstrdup(s);
595 		return (1);
596 	}
597 
598 	s = text_try_words(inf->base, inf->size, flags);
599 	if (s != NULL) {
600 		if (iflag)
601 			inf->result = xstrdup(s);
602 		else
603 			xasprintf(&inf->result, "%s %s text", type, s);
604 		return (1);
605 	}
606 
607 	if (iflag)
608 		inf->result = xstrdup("text/plain");
609 	else
610 		xasprintf(&inf->result, "%s text", type);
611 	return (1);
612 }
613 
614 static int
615 try_magic(struct input_file *inf)
616 {
617 	const char	*s;
618 	int		 flags;
619 
620 	flags = 0;
621 	if (iflag)
622 		flags |= MAGIC_TEST_MIME;
623 
624 	s = magic_test(inf->m, inf->base, inf->size, flags);
625 	if (s != NULL) {
626 		inf->result = xstrdup(s);
627 		return (1);
628 	}
629 	return (0);
630 }
631 
632 static int
633 try_unknown(struct input_file *inf)
634 {
635 	if (iflag)
636 		xasprintf(&inf->result, "application/x-not-regular-file");
637 	else
638 		xasprintf(&inf->result, "data");
639 	return (1);
640 }
641 
642 static void
643 test_file(struct input_file *inf, size_t width)
644 {
645 	char	*label;
646 	int	 stop;
647 
648 	stop = 0;
649 	if (!stop)
650 		stop = try_stat(inf);
651 	if (!stop)
652 		stop = try_access(inf);
653 	if (!stop)
654 		stop = load_file(inf);
655 	if (!stop)
656 		stop = try_empty(inf);
657 	if (!stop)
658 		stop = try_magic(inf);
659 	if (!stop)
660 		stop = try_text(inf);
661 	if (!stop)
662 		stop = try_unknown(inf);
663 
664 	if (bflag)
665 		printf("%s\n", inf->result);
666 	else {
667 		if (strcmp(inf->path, "-") == 0)
668 			xasprintf(&label, "/dev/stdin:");
669 		else
670 			xasprintf(&label, "%s:", inf->path);
671 		printf("%-*s %s\n", (int)width, label, inf->result);
672 		free(label);
673 	}
674 	free(inf->result);
675 
676 	if (inf->mapped && inf->base != NULL)
677 		munmap(inf->base, inf->size);
678 }
679