xref: /openbsd-src/usr.bin/file/file.c (revision 09f2f01e9c82c802fea3a2c6b0c796d4a34d310e)
1 /* $OpenBSD: file.c,v 1.60 2017/06/28 13:37:56 brynet 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;
108 	struct magic		*m;
109 	struct input_file	*inf = NULL;
110 	size_t			 len, width = 0;
111 
112 	tzset();
113 
114 	for (;;) {
115 		opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL);
116 		if (opt == -1)
117 			break;
118 		switch (opt) {
119 		case 'b':
120 			bflag = 1;
121 			break;
122 		case 'c':
123 			cflag = 1;
124 			break;
125 		case 'h':
126 			Lflag = 0;
127 			break;
128 		case 'i':
129 			iflag = 1;
130 			break;
131 		case 'L':
132 			Lflag = 1;
133 			break;
134 		case 's':
135 			sflag = 1;
136 			break;
137 		case 'W':
138 			Wflag = 1;
139 			break;
140 		default:
141 			usage();
142 		}
143 	}
144 	argc -= optind;
145 	argv += optind;
146 	if (cflag) {
147 		if (argc != 0)
148 			usage();
149 	} else if (argc == 0)
150 		usage();
151 
152 	magicfp = NULL;
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 	if (pledge("stdio getpw id", NULL) == -1)
187 		err(1, "pledge");
188 
189 	if (geteuid() == 0) {
190 		pw = getpwnam(FILE_USER);
191 		if (pw == NULL)
192 			errx(1, "unknown user %s", FILE_USER);
193 		if (setgroups(1, &pw->pw_gid) != 0)
194 			err(1, "setgroups");
195 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
196 			err(1, "setresgid");
197 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
198 			err(1, "setresuid");
199 	}
200 
201 	if (pledge("stdio", NULL) == -1)
202 		err(1, "pledge");
203 
204 	m = magic_load(magicfp, magicpath, cflag || Wflag);
205 	if (cflag) {
206 		magic_dump(m);
207 		exit(0);
208 	}
209 	fclose(magicfp);
210 
211 	for (idx = 0; idx < argc; idx++) {
212 		inf[idx].m = m;
213 		test_file(&inf[idx], width);
214 	}
215 	exit(0);
216 }
217 
218 static void
219 prepare_input(struct input_file *inf, const char *path)
220 {
221 	int	fd, mode, error;
222 
223 	if (strcmp(path, "-") == 0) {
224 		if (fstat(STDIN_FILENO, &inf->sb) == -1) {
225 			inf->error = errno;
226 			inf->fd = -1;
227 		}
228 		inf->fd = STDIN_FILENO;
229 	}
230 
231 	if (Lflag)
232 		error = stat(path, &inf->sb);
233 	else
234 		error = lstat(path, &inf->sb);
235 	if (error == -1) {
236 		inf->error = errno;
237 		inf->fd = -1;
238 	}
239 
240 	/* We don't need them, so don't open directories or symlinks. */
241 	mode = inf->sb.st_mode;
242 	if (!S_ISDIR(mode) && !S_ISLNK(mode)) {
243 		fd = open(path, O_RDONLY|O_NONBLOCK);
244 		if (fd == -1 && (errno == ENFILE || errno == EMFILE))
245 			err(1, "open");
246 	} else
247 		fd = -1;
248 	if (S_ISLNK(mode))
249 		read_link(inf, path);
250 	inf->fd = fd;
251 	inf->path = path;
252 }
253 
254 static void
255 read_link(struct input_file *inf, const char *path)
256 {
257 	struct stat	 sb;
258 	char		 lpath[PATH_MAX];
259 	char		*copy, *root;
260 	int		 used;
261 	ssize_t		 size;
262 
263 	size = readlink(path, lpath, sizeof lpath - 1);
264 	if (size == -1) {
265 		inf->link_error = errno;
266 		return;
267 	}
268 	lpath[size] = '\0';
269 
270 	if (*lpath == '/')
271 		strlcpy(inf->link_path, lpath, sizeof inf->link_path);
272 	else {
273 		copy = xstrdup(path);
274 
275 		root = dirname(copy);
276 		if (*root == '\0' || strcmp(root, ".") == 0 ||
277 		    strcmp (root, "/") == 0)
278 			strlcpy(inf->link_path, lpath, sizeof inf->link_path);
279 		else {
280 			used = snprintf(inf->link_path, sizeof inf->link_path,
281 			    "%s/%s", root, lpath);
282 			if (used < 0 || (size_t)used >= sizeof inf->link_path) {
283 				inf->link_error = ENAMETOOLONG;
284 				free(copy);
285 				return;
286 			}
287 		}
288 
289 		free(copy);
290 	}
291 
292 	if (!Lflag && stat(path, &sb) == -1)
293 		inf->link_target = errno;
294 }
295 
296 static void *
297 fill_buffer(int fd, size_t size, size_t *used)
298 {
299 	static void	*buffer;
300 	ssize_t		 got;
301 	size_t		 left;
302 	void		*next;
303 
304 	if (buffer == NULL)
305 		buffer = xmalloc(FILE_READ_SIZE);
306 
307 	next = buffer;
308 	left = size;
309 	while (left != 0) {
310 		got = read(fd, next, left);
311 		if (got == -1) {
312 			if (errno == EINTR)
313 				continue;
314 			return (NULL);
315 		}
316 		if (got == 0)
317 			break;
318 		next = (char *)next + got;
319 		left -= got;
320 	}
321 	*used = size - left;
322 	return (buffer);
323 }
324 
325 static int
326 load_file(struct input_file *inf)
327 {
328 	size_t	used;
329 
330 	if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode))
331 		return (0); /* empty file */
332 	if (inf->sb.st_size == 0 || inf->sb.st_size > FILE_READ_SIZE)
333 		inf->size = FILE_READ_SIZE;
334 	else
335 		inf->size = inf->sb.st_size;
336 
337 	if (!S_ISREG(inf->sb.st_mode))
338 		goto try_read;
339 
340 	inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);
341 	if (inf->base == MAP_FAILED)
342 		goto try_read;
343 	inf->mapped = 1;
344 	return (0);
345 
346 try_read:
347 	inf->base = fill_buffer(inf->fd, inf->size, &used);
348 	if (inf->base == NULL) {
349 		xasprintf(&inf->result, "cannot read '%s' (%s)", inf->path,
350 		    strerror(errno));
351 		return (1);
352 	}
353 	inf->size = used;
354 	return (0);
355 }
356 
357 static int
358 try_stat(struct input_file *inf)
359 {
360 	if (inf->error != 0) {
361 		xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,
362 		    strerror(inf->error));
363 		return (1);
364 	}
365 	if (sflag || strcmp(inf->path, "-") == 0) {
366 		switch (inf->sb.st_mode & S_IFMT) {
367 		case S_IFIFO:
368 			if (strcmp(inf->path, "-") != 0)
369 				break;
370 		case S_IFBLK:
371 		case S_IFCHR:
372 		case S_IFREG:
373 			return (0);
374 		}
375 	}
376 
377 	if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) {
378 		xasprintf(&inf->result, "application/x-not-regular-file");
379 		return (1);
380 	}
381 
382 	switch (inf->sb.st_mode & S_IFMT) {
383 	case S_IFDIR:
384 		xasprintf(&inf->result, "directory");
385 		return (1);
386 	case S_IFLNK:
387 		if (inf->link_error != 0) {
388 			xasprintf(&inf->result, "unreadable symlink '%s' (%s)",
389 			    inf->path, strerror(inf->link_error));
390 			return (1);
391 		}
392 		if (inf->link_target == ELOOP)
393 			xasprintf(&inf->result, "symbolic link in a loop");
394 		else if (inf->link_target != 0) {
395 			xasprintf(&inf->result, "broken symbolic link to '%s'",
396 			    inf->link_path);
397 		} else {
398 			xasprintf(&inf->result, "symbolic link to '%s'",
399 			    inf->link_path);
400 		}
401 		return (1);
402 	case S_IFSOCK:
403 		xasprintf(&inf->result, "socket");
404 		return (1);
405 	case S_IFBLK:
406 		xasprintf(&inf->result, "block special (%ld/%ld)",
407 		    (long)major(inf->sb.st_rdev),
408 		    (long)minor(inf->sb.st_rdev));
409 		return (1);
410 	case S_IFCHR:
411 		xasprintf(&inf->result, "character special (%ld/%ld)",
412 		    (long)major(inf->sb.st_rdev),
413 		    (long)minor(inf->sb.st_rdev));
414 		return (1);
415 	case S_IFIFO:
416 		xasprintf(&inf->result, "fifo (named pipe)");
417 		return (1);
418 	}
419 	return (0);
420 }
421 
422 static int
423 try_empty(struct input_file *inf)
424 {
425 	if (inf->size != 0)
426 		return (0);
427 
428 	if (iflag)
429 		xasprintf(&inf->result, "application/x-empty");
430 	else
431 		xasprintf(&inf->result, "empty");
432 	return (1);
433 }
434 
435 static int
436 try_access(struct input_file *inf)
437 {
438 	char tmp[256] = "";
439 
440 	if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode))
441 		return (0); /* empty file */
442 	if (inf->fd != -1)
443 		return (0);
444 
445 	if (inf->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
446 		strlcat(tmp, "writable, ", sizeof tmp);
447 	if (inf->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
448 		strlcat(tmp, "executable, ", sizeof tmp);
449 	if (S_ISREG(inf->sb.st_mode))
450 		strlcat(tmp, "regular file, ", sizeof tmp);
451 	strlcat(tmp, "no read permission", sizeof tmp);
452 
453 	inf->result = xstrdup(tmp);
454 	return (1);
455 }
456 
457 static int
458 try_text(struct input_file *inf)
459 {
460 	const char	*type, *s;
461 	int		 flags;
462 
463 	flags = MAGIC_TEST_TEXT;
464 	if (iflag)
465 		flags |= MAGIC_TEST_MIME;
466 
467 	type = text_get_type(inf->base, inf->size);
468 	if (type == NULL)
469 		return (0);
470 
471 	s = magic_test(inf->m, inf->base, inf->size, flags);
472 	if (s != NULL) {
473 		inf->result = xstrdup(s);
474 		return (1);
475 	}
476 
477 	s = text_try_words(inf->base, inf->size, flags);
478 	if (s != NULL) {
479 		if (iflag)
480 			inf->result = xstrdup(s);
481 		else
482 			xasprintf(&inf->result, "%s %s text", type, s);
483 		return (1);
484 	}
485 
486 	if (iflag)
487 		inf->result = xstrdup("text/plain");
488 	else
489 		xasprintf(&inf->result, "%s text", type);
490 	return (1);
491 }
492 
493 static int
494 try_magic(struct input_file *inf)
495 {
496 	const char	*s;
497 	int		 flags;
498 
499 	flags = 0;
500 	if (iflag)
501 		flags |= MAGIC_TEST_MIME;
502 
503 	s = magic_test(inf->m, inf->base, inf->size, flags);
504 	if (s != NULL) {
505 		inf->result = xstrdup(s);
506 		return (1);
507 	}
508 	return (0);
509 }
510 
511 static int
512 try_unknown(struct input_file *inf)
513 {
514 	if (iflag)
515 		xasprintf(&inf->result, "application/x-not-regular-file");
516 	else
517 		xasprintf(&inf->result, "data");
518 	return (1);
519 }
520 
521 static void
522 test_file(struct input_file *inf, size_t width)
523 {
524 	char	*label;
525 	int	 stop;
526 
527 	stop = 0;
528 	if (!stop)
529 		stop = try_stat(inf);
530 	if (!stop)
531 		stop = try_access(inf);
532 	if (!stop)
533 		stop = load_file(inf);
534 	if (!stop)
535 		stop = try_empty(inf);
536 	if (!stop)
537 		stop = try_magic(inf);
538 	if (!stop)
539 		stop = try_text(inf);
540 	if (!stop)
541 		stop = try_unknown(inf);
542 
543 	if (bflag)
544 		printf("%s\n", inf->result);
545 	else {
546 		if (strcmp(inf->path, "-") == 0)
547 			xasprintf(&label, "/dev/stdin:");
548 		else
549 			xasprintf(&label, "%s:", inf->path);
550 		printf("%-*s %s\n", (int)width, label, inf->result);
551 		free(label);
552 	}
553 	free(inf->result);
554 
555 	if (inf->mapped && inf->base != NULL)
556 		munmap(inf->base, inf->size);
557 }
558