xref: /openbsd/usr.bin/file/file.c (revision a1ab1972)
1 /* $OpenBSD: file.c,v 1.61 2017/06/28 15:40:54 deraadt 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/queue.h>
23 #include <sys/socket.h>
24 #include <sys/stat.h>
25 #include <sys/uio.h>
26 #include <sys/wait.h>
27 
28 #include <err.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <getopt.h>
32 #include <libgen.h>
33 #include <limits.h>
34 #include <pwd.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
39 
40 #include "file.h"
41 #include "magic.h"
42 #include "xmalloc.h"
43 
44 struct input_file {
45 	struct magic	*m;
46 
47 	const char	*path;
48 	struct stat	 sb;
49 	int		 fd;
50 	int		 error;
51 
52 	char		 link_path[PATH_MAX];
53 	int		 link_error;
54 	int		 link_target;
55 
56 	void		*base;
57 	size_t		 size;
58 	int		 mapped;
59 	char		*result;
60 };
61 
62 extern char	*__progname;
63 
64 __dead void	 usage(void);
65 
66 static void	 prepare_input(struct input_file *, const char *);
67 
68 static void	 read_link(struct input_file *, const char *);
69 
70 static void	 test_file(struct input_file *, size_t);
71 
72 static int	 try_stat(struct input_file *);
73 static int	 try_empty(struct input_file *);
74 static int	 try_access(struct input_file *);
75 static int	 try_text(struct input_file *);
76 static int	 try_magic(struct input_file *);
77 static int	 try_unknown(struct input_file *);
78 
79 static int	 bflag;
80 static int	 cflag;
81 static int	 iflag;
82 static int	 Lflag;
83 static int	 sflag;
84 static int	 Wflag;
85 
86 static struct option longopts[] = {
87 	{ "brief",       no_argument, NULL, 'b' },
88 	{ "dereference", no_argument, NULL, 'L' },
89 	{ "mime",        no_argument, NULL, 'i' },
90 	{ "mime-type",   no_argument, NULL, 'i' },
91 	{ NULL,          0,           NULL, 0   }
92 };
93 
94 __dead void
95 usage(void)
96 {
97 	fprintf(stderr, "usage: %s [-bchiLsW] file ...\n", __progname);
98 	exit(1);
99 }
100 
101 int
102 main(int argc, char **argv)
103 {
104 	int			 opt, idx;
105 	char			*home, *magicpath;
106 	struct passwd		*pw;
107 	FILE			*magicfp = NULL;
108 	struct magic		*m;
109 	struct input_file	*inf = NULL;
110 	size_t			 len, width = 0;
111 
112 	if (pledge("stdio rpath getpw id", NULL) == -1)
113 		err(1, "pledge");
114 
115 	for (;;) {
116 		opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL);
117 		if (opt == -1)
118 			break;
119 		switch (opt) {
120 		case 'b':
121 			bflag = 1;
122 			break;
123 		case 'c':
124 			cflag = 1;
125 			break;
126 		case 'h':
127 			Lflag = 0;
128 			break;
129 		case 'i':
130 			iflag = 1;
131 			break;
132 		case 'L':
133 			Lflag = 1;
134 			break;
135 		case 's':
136 			sflag = 1;
137 			break;
138 		case 'W':
139 			Wflag = 1;
140 			break;
141 		default:
142 			usage();
143 		}
144 	}
145 	argc -= optind;
146 	argv += optind;
147 	if (cflag) {
148 		if (argc != 0)
149 			usage();
150 	} else if (argc == 0)
151 		usage();
152 
153 	if (geteuid() != 0 && !issetugid()) {
154 		home = getenv("HOME");
155 		if (home == NULL || *home == '\0') {
156 			pw = getpwuid(getuid());
157 			if (pw != NULL)
158 				home = pw->pw_dir;
159 			else
160 				home = NULL;
161 		}
162 		if (home != NULL) {
163 			xasprintf(&magicpath, "%s/.magic", home);
164 			magicfp = fopen(magicpath, "r");
165 			if (magicfp == NULL)
166 				free(magicpath);
167 		}
168 	}
169 	if (magicfp == NULL) {
170 		magicpath = xstrdup("/etc/magic");
171 		magicfp = fopen(magicpath, "r");
172 	}
173 	if (magicfp == NULL)
174 		err(1, "%s", magicpath);
175 
176 	if (!cflag) {
177 		inf = xcalloc(argc, sizeof *inf);
178 		for (idx = 0; idx < argc; idx++) {
179 			len = strlen(argv[idx]) + 1;
180 			if (len > width)
181 				width = len;
182 			prepare_input(&inf[idx], argv[idx]);
183 		}
184 	}
185 
186 	tzset();
187 
188 	if (pledge("stdio getpw id", NULL) == -1)
189 		err(1, "pledge");
190 
191 	if (geteuid() == 0) {
192 		pw = getpwnam(FILE_USER);
193 		if (pw == NULL)
194 			errx(1, "unknown user %s", FILE_USER);
195 		if (setgroups(1, &pw->pw_gid) != 0)
196 			err(1, "setgroups");
197 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
198 			err(1, "setresgid");
199 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
200 			err(1, "setresuid");
201 	}
202 
203 	if (pledge("stdio", NULL) == -1)
204 		err(1, "pledge");
205 
206 	m = magic_load(magicfp, magicpath, cflag || Wflag);
207 	if (cflag) {
208 		magic_dump(m);
209 		exit(0);
210 	}
211 	fclose(magicfp);
212 
213 	for (idx = 0; idx < argc; idx++) {
214 		inf[idx].m = m;
215 		test_file(&inf[idx], width);
216 	}
217 	exit(0);
218 }
219 
220 static void
221 prepare_input(struct input_file *inf, const char *path)
222 {
223 	int	fd, mode, error;
224 
225 	if (strcmp(path, "-") == 0) {
226 		if (fstat(STDIN_FILENO, &inf->sb) == -1) {
227 			inf->error = errno;
228 			inf->fd = -1;
229 		}
230 		inf->fd = STDIN_FILENO;
231 	}
232 
233 	if (Lflag)
234 		error = stat(path, &inf->sb);
235 	else
236 		error = lstat(path, &inf->sb);
237 	if (error == -1) {
238 		inf->error = errno;
239 		inf->fd = -1;
240 	}
241 
242 	/* We don't need them, so don't open directories or symlinks. */
243 	mode = inf->sb.st_mode;
244 	if (!S_ISDIR(mode) && !S_ISLNK(mode)) {
245 		fd = open(path, O_RDONLY|O_NONBLOCK);
246 		if (fd == -1 && (errno == ENFILE || errno == EMFILE))
247 			err(1, "open");
248 	} else
249 		fd = -1;
250 	if (S_ISLNK(mode))
251 		read_link(inf, path);
252 	inf->fd = fd;
253 	inf->path = path;
254 }
255 
256 static void
257 read_link(struct input_file *inf, const char *path)
258 {
259 	struct stat	 sb;
260 	char		 lpath[PATH_MAX];
261 	char		*copy, *root;
262 	int		 used;
263 	ssize_t		 size;
264 
265 	size = readlink(path, lpath, sizeof lpath - 1);
266 	if (size == -1) {
267 		inf->link_error = errno;
268 		return;
269 	}
270 	lpath[size] = '\0';
271 
272 	if (*lpath == '/')
273 		strlcpy(inf->link_path, lpath, sizeof inf->link_path);
274 	else {
275 		copy = xstrdup(path);
276 
277 		root = dirname(copy);
278 		if (*root == '\0' || strcmp(root, ".") == 0 ||
279 		    strcmp (root, "/") == 0)
280 			strlcpy(inf->link_path, lpath, sizeof inf->link_path);
281 		else {
282 			used = snprintf(inf->link_path, sizeof inf->link_path,
283 			    "%s/%s", root, lpath);
284 			if (used < 0 || (size_t)used >= sizeof inf->link_path) {
285 				inf->link_error = ENAMETOOLONG;
286 				free(copy);
287 				return;
288 			}
289 		}
290 
291 		free(copy);
292 	}
293 
294 	if (!Lflag && stat(path, &sb) == -1)
295 		inf->link_target = errno;
296 }
297 
298 static void *
299 fill_buffer(int fd, size_t size, size_t *used)
300 {
301 	static void	*buffer;
302 	ssize_t		 got;
303 	size_t		 left;
304 	void		*next;
305 
306 	if (buffer == NULL)
307 		buffer = xmalloc(FILE_READ_SIZE);
308 
309 	next = buffer;
310 	left = size;
311 	while (left != 0) {
312 		got = read(fd, next, left);
313 		if (got == -1) {
314 			if (errno == EINTR)
315 				continue;
316 			return (NULL);
317 		}
318 		if (got == 0)
319 			break;
320 		next = (char *)next + got;
321 		left -= got;
322 	}
323 	*used = size - left;
324 	return (buffer);
325 }
326 
327 static int
328 load_file(struct input_file *inf)
329 {
330 	size_t	used;
331 
332 	if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode))
333 		return (0); /* empty file */
334 	if (inf->sb.st_size == 0 || inf->sb.st_size > FILE_READ_SIZE)
335 		inf->size = FILE_READ_SIZE;
336 	else
337 		inf->size = inf->sb.st_size;
338 
339 	if (!S_ISREG(inf->sb.st_mode))
340 		goto try_read;
341 
342 	inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);
343 	if (inf->base == MAP_FAILED)
344 		goto try_read;
345 	inf->mapped = 1;
346 	return (0);
347 
348 try_read:
349 	inf->base = fill_buffer(inf->fd, inf->size, &used);
350 	if (inf->base == NULL) {
351 		xasprintf(&inf->result, "cannot read '%s' (%s)", inf->path,
352 		    strerror(errno));
353 		return (1);
354 	}
355 	inf->size = used;
356 	return (0);
357 }
358 
359 static int
360 try_stat(struct input_file *inf)
361 {
362 	if (inf->error != 0) {
363 		xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,
364 		    strerror(inf->error));
365 		return (1);
366 	}
367 	if (sflag || strcmp(inf->path, "-") == 0) {
368 		switch (inf->sb.st_mode & S_IFMT) {
369 		case S_IFIFO:
370 			if (strcmp(inf->path, "-") != 0)
371 				break;
372 		case S_IFBLK:
373 		case S_IFCHR:
374 		case S_IFREG:
375 			return (0);
376 		}
377 	}
378 
379 	if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) {
380 		xasprintf(&inf->result, "application/x-not-regular-file");
381 		return (1);
382 	}
383 
384 	switch (inf->sb.st_mode & S_IFMT) {
385 	case S_IFDIR:
386 		xasprintf(&inf->result, "directory");
387 		return (1);
388 	case S_IFLNK:
389 		if (inf->link_error != 0) {
390 			xasprintf(&inf->result, "unreadable symlink '%s' (%s)",
391 			    inf->path, strerror(inf->link_error));
392 			return (1);
393 		}
394 		if (inf->link_target == ELOOP)
395 			xasprintf(&inf->result, "symbolic link in a loop");
396 		else if (inf->link_target != 0) {
397 			xasprintf(&inf->result, "broken symbolic link to '%s'",
398 			    inf->link_path);
399 		} else {
400 			xasprintf(&inf->result, "symbolic link to '%s'",
401 			    inf->link_path);
402 		}
403 		return (1);
404 	case S_IFSOCK:
405 		xasprintf(&inf->result, "socket");
406 		return (1);
407 	case S_IFBLK:
408 		xasprintf(&inf->result, "block special (%ld/%ld)",
409 		    (long)major(inf->sb.st_rdev),
410 		    (long)minor(inf->sb.st_rdev));
411 		return (1);
412 	case S_IFCHR:
413 		xasprintf(&inf->result, "character special (%ld/%ld)",
414 		    (long)major(inf->sb.st_rdev),
415 		    (long)minor(inf->sb.st_rdev));
416 		return (1);
417 	case S_IFIFO:
418 		xasprintf(&inf->result, "fifo (named pipe)");
419 		return (1);
420 	}
421 	return (0);
422 }
423 
424 static int
425 try_empty(struct input_file *inf)
426 {
427 	if (inf->size != 0)
428 		return (0);
429 
430 	if (iflag)
431 		xasprintf(&inf->result, "application/x-empty");
432 	else
433 		xasprintf(&inf->result, "empty");
434 	return (1);
435 }
436 
437 static int
438 try_access(struct input_file *inf)
439 {
440 	char tmp[256] = "";
441 
442 	if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode))
443 		return (0); /* empty file */
444 	if (inf->fd != -1)
445 		return (0);
446 
447 	if (inf->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
448 		strlcat(tmp, "writable, ", sizeof tmp);
449 	if (inf->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
450 		strlcat(tmp, "executable, ", sizeof tmp);
451 	if (S_ISREG(inf->sb.st_mode))
452 		strlcat(tmp, "regular file, ", sizeof tmp);
453 	strlcat(tmp, "no read permission", sizeof tmp);
454 
455 	inf->result = xstrdup(tmp);
456 	return (1);
457 }
458 
459 static int
460 try_text(struct input_file *inf)
461 {
462 	const char	*type, *s;
463 	int		 flags;
464 
465 	flags = MAGIC_TEST_TEXT;
466 	if (iflag)
467 		flags |= MAGIC_TEST_MIME;
468 
469 	type = text_get_type(inf->base, inf->size);
470 	if (type == NULL)
471 		return (0);
472 
473 	s = magic_test(inf->m, inf->base, inf->size, flags);
474 	if (s != NULL) {
475 		inf->result = xstrdup(s);
476 		return (1);
477 	}
478 
479 	s = text_try_words(inf->base, inf->size, flags);
480 	if (s != NULL) {
481 		if (iflag)
482 			inf->result = xstrdup(s);
483 		else
484 			xasprintf(&inf->result, "%s %s text", type, s);
485 		return (1);
486 	}
487 
488 	if (iflag)
489 		inf->result = xstrdup("text/plain");
490 	else
491 		xasprintf(&inf->result, "%s text", type);
492 	return (1);
493 }
494 
495 static int
496 try_magic(struct input_file *inf)
497 {
498 	const char	*s;
499 	int		 flags;
500 
501 	flags = 0;
502 	if (iflag)
503 		flags |= MAGIC_TEST_MIME;
504 
505 	s = magic_test(inf->m, inf->base, inf->size, flags);
506 	if (s != NULL) {
507 		inf->result = xstrdup(s);
508 		return (1);
509 	}
510 	return (0);
511 }
512 
513 static int
514 try_unknown(struct input_file *inf)
515 {
516 	if (iflag)
517 		xasprintf(&inf->result, "application/x-not-regular-file");
518 	else
519 		xasprintf(&inf->result, "data");
520 	return (1);
521 }
522 
523 static void
524 test_file(struct input_file *inf, size_t width)
525 {
526 	char	*label;
527 	int	 stop;
528 
529 	stop = 0;
530 	if (!stop)
531 		stop = try_stat(inf);
532 	if (!stop)
533 		stop = try_access(inf);
534 	if (!stop)
535 		stop = load_file(inf);
536 	if (!stop)
537 		stop = try_empty(inf);
538 	if (!stop)
539 		stop = try_magic(inf);
540 	if (!stop)
541 		stop = try_text(inf);
542 	if (!stop)
543 		stop = try_unknown(inf);
544 
545 	if (bflag)
546 		printf("%s\n", inf->result);
547 	else {
548 		if (strcmp(inf->path, "-") == 0)
549 			xasprintf(&label, "/dev/stdin:");
550 		else
551 			xasprintf(&label, "%s:", inf->path);
552 		printf("%-*s %s\n", (int)width, label, inf->result);
553 		free(label);
554 	}
555 	free(inf->result);
556 
557 	if (inf->mapped && inf->base != NULL)
558 		munmap(inf->base, inf->size);
559 }
560