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