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