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