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