magic.c revision 284778
1/*
2 * Copyright (c) Christos Zoulas 2003.
3 * All Rights Reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice immediately at the beginning of the file, without modification,
10 *    this list of conditions, and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#ifdef WIN32
29#include <windows.h>
30#include <shlwapi.h>
31#endif
32
33#include "file.h"
34
35#ifndef	lint
36FILE_RCSID("@(#)$File: magic.c,v 1.93 2015/04/15 23:47:58 christos Exp $")
37#endif	/* lint */
38
39#include "magic.h"
40
41#include <stdlib.h>
42#include <unistd.h>
43#include <string.h>
44#ifdef QUICK
45#include <sys/mman.h>
46#endif
47#ifdef HAVE_LIMITS_H
48#include <limits.h>	/* for PIPE_BUF */
49#endif
50
51#if defined(HAVE_UTIMES)
52# include <sys/time.h>
53#elif defined(HAVE_UTIME)
54# if defined(HAVE_SYS_UTIME_H)
55#  include <sys/utime.h>
56# elif defined(HAVE_UTIME_H)
57#  include <utime.h>
58# endif
59#endif
60
61#ifdef HAVE_UNISTD_H
62#include <unistd.h>	/* for read() */
63#endif
64
65#ifndef PIPE_BUF
66/* Get the PIPE_BUF from pathconf */
67#ifdef _PC_PIPE_BUF
68#define PIPE_BUF pathconf(".", _PC_PIPE_BUF)
69#else
70#define PIPE_BUF 512
71#endif
72#endif
73
74private void close_and_restore(const struct magic_set *, const char *, int,
75    const struct stat *);
76private int unreadable_info(struct magic_set *, mode_t, const char *);
77private const char* get_default_magic(void);
78#ifndef COMPILE_ONLY
79private const char *file_or_fd(struct magic_set *, const char *, int);
80#endif
81
82#ifndef	STDIN_FILENO
83#define	STDIN_FILENO	0
84#endif
85
86#ifdef WIN32
87/* HINSTANCE of this shared library. Needed for get_default_magic() */
88static HINSTANCE _w32_dll_instance = NULL;
89
90static void
91_w32_append_path(char **hmagicpath, const char *fmt, ...)
92{
93	char *tmppath;
94        char *newpath;
95	va_list ap;
96
97	va_start(ap, fmt);
98	if (vasprintf(&tmppath, fmt, ap) < 0) {
99		va_end(ap);
100		return;
101	}
102	va_end(ap);
103
104	if (access(tmppath, R_OK) == -1)
105		goto out;
106
107	if (*hmagicpath == NULL) {
108		*hmagicpath = tmppath;
109		return;
110	}
111
112	if (asprintf(&newpath, "%s%c%s", *hmagicpath, PATHSEP, tmppath) < 0)
113		goto out;
114
115	free(*hmagicpath);
116	free(tmppath);
117	*hmagicpath = newpath;
118	return;
119out:
120	free(tmppath);
121}
122
123static void
124_w32_get_magic_relative_to(char **hmagicpath, HINSTANCE module)
125{
126	static const char *trypaths[] = {
127		"%s/share/misc/magic.mgc",
128		"%s/magic.mgc",
129	};
130	LPSTR dllpath;
131	size_t sp;
132
133	dllpath = calloc(MAX_PATH + 1, sizeof(*dllpath));
134
135	if (!GetModuleFileNameA(module, dllpath, MAX_PATH))
136		goto out;
137
138	PathRemoveFileSpecA(dllpath);
139
140	sp = strlen(dllpath);
141	if (sp > 3 && stricmp(&dllpath[sp - 3], "bin") == 0) {
142		_w32_append_path(hmagicpath,
143		    "%s/../share/misc/magic.mgc", dllpath);
144		goto out;
145	}
146
147	for (sp = 0; sp < __arraycount(trypaths); sp++)
148		_w32_append_path(hmagicpath, trypaths[sp], dllpath);
149out:
150	free(dllpath);
151}
152
153/* Placate GCC by offering a sacrificial previous prototype */
154BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID);
155
156BOOL WINAPI
157DllMain(HINSTANCE hinstDLL, DWORD fdwReason,
158    LPVOID lpvReserved __attribute__((__unused__)))
159{
160	if (fdwReason == DLL_PROCESS_ATTACH)
161		_w32_dll_instance = hinstDLL;
162	return TRUE;
163}
164#endif
165
166private const char *
167get_default_magic(void)
168{
169	static const char hmagic[] = "/.magic/magic.mgc";
170	static char *default_magic;
171	char *home, *hmagicpath;
172
173#ifndef WIN32
174	struct stat st;
175
176	if (default_magic) {
177		free(default_magic);
178		default_magic = NULL;
179	}
180	if ((home = getenv("HOME")) == NULL)
181		return MAGIC;
182
183	if (asprintf(&hmagicpath, "%s/.magic.mgc", home) < 0)
184		return MAGIC;
185	if (stat(hmagicpath, &st) == -1) {
186		free(hmagicpath);
187		if (asprintf(&hmagicpath, "%s/.magic", home) < 0)
188			return MAGIC;
189		if (stat(hmagicpath, &st) == -1)
190			goto out;
191		if (S_ISDIR(st.st_mode)) {
192			free(hmagicpath);
193			if (asprintf(&hmagicpath, "%s/%s", home, hmagic) < 0)
194				return MAGIC;
195			if (access(hmagicpath, R_OK) == -1)
196				goto out;
197		}
198	}
199
200	if (asprintf(&default_magic, "%s:%s", hmagicpath, MAGIC) < 0)
201		goto out;
202	free(hmagicpath);
203	return default_magic;
204out:
205	default_magic = NULL;
206	free(hmagicpath);
207	return MAGIC;
208#else
209	hmagicpath = NULL;
210
211	if (default_magic) {
212		free(default_magic);
213		default_magic = NULL;
214	}
215
216	/* First, try to get a magic file from user-application data */
217	if ((home = getenv("LOCALAPPDATA")) != NULL)
218		_w32_append_path(&hmagicpath, "%s%s", home, hmagic);
219
220	/* Second, try to get a magic file from the user profile data */
221	if ((home = getenv("USERPROFILE")) != NULL)
222		_w32_append_path(&hmagicpath,
223		    "%s/Local Settings/Application Data%s", home, hmagic);
224
225	/* Third, try to get a magic file from Common Files */
226	if ((home = getenv("COMMONPROGRAMFILES")) != NULL)
227		_w32_append_path(&hmagicpath, "%s%s", home, hmagic);
228
229	/* Fourth, try to get magic file relative to exe location */
230        _w32_get_magic_relative_to(&hmagicpath, NULL);
231
232	/* Fifth, try to get magic file relative to dll location */
233        _w32_get_magic_relative_to(&hmagicpath, _w32_dll_instance);
234
235	/* Avoid MAGIC constant - it likely points to a file within MSys tree */
236	default_magic = hmagicpath;
237	return default_magic;
238#endif
239}
240
241public const char *
242magic_getpath(const char *magicfile, int action)
243{
244	if (magicfile != NULL)
245		return magicfile;
246
247	magicfile = getenv("MAGIC");
248	if (magicfile != NULL)
249		return magicfile;
250
251	return action == FILE_LOAD ? get_default_magic() : MAGIC;
252}
253
254public struct magic_set *
255magic_open(int flags)
256{
257	return file_ms_alloc(flags);
258}
259
260private int
261unreadable_info(struct magic_set *ms, mode_t md, const char *file)
262{
263	if (file) {
264		/* We cannot open it, but we were able to stat it. */
265		if (access(file, W_OK) == 0)
266			if (file_printf(ms, "writable, ") == -1)
267				return -1;
268		if (access(file, X_OK) == 0)
269			if (file_printf(ms, "executable, ") == -1)
270				return -1;
271	}
272	if (S_ISREG(md))
273		if (file_printf(ms, "regular file, ") == -1)
274			return -1;
275	if (file_printf(ms, "no read permission") == -1)
276		return -1;
277	return 0;
278}
279
280public void
281magic_close(struct magic_set *ms)
282{
283	if (ms == NULL)
284		return;
285	file_ms_free(ms);
286}
287
288/*
289 * load a magic file
290 */
291public int
292magic_load(struct magic_set *ms, const char *magicfile)
293{
294	if (ms == NULL)
295		return -1;
296	return file_apprentice(ms, magicfile, FILE_LOAD);
297}
298
299#ifndef COMPILE_ONLY
300/*
301 * Install a set of compiled magic buffers.
302 */
303public int
304magic_load_buffers(struct magic_set *ms, void **bufs, size_t *sizes,
305    size_t nbufs)
306{
307	if (ms == NULL)
308		return -1;
309	return buffer_apprentice(ms, (struct magic **)bufs, sizes, nbufs);
310}
311#endif
312
313public int
314magic_compile(struct magic_set *ms, const char *magicfile)
315{
316	if (ms == NULL)
317		return -1;
318	return file_apprentice(ms, magicfile, FILE_COMPILE);
319}
320
321public int
322magic_check(struct magic_set *ms, const char *magicfile)
323{
324	if (ms == NULL)
325		return -1;
326	return file_apprentice(ms, magicfile, FILE_CHECK);
327}
328
329public int
330magic_list(struct magic_set *ms, const char *magicfile)
331{
332	if (ms == NULL)
333		return -1;
334	return file_apprentice(ms, magicfile, FILE_LIST);
335}
336
337private void
338close_and_restore(const struct magic_set *ms, const char *name, int fd,
339    const struct stat *sb)
340{
341	if (fd == STDIN_FILENO || name == NULL)
342		return;
343	(void) close(fd);
344
345	if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) {
346		/*
347		 * Try to restore access, modification times if read it.
348		 * This is really *bad* because it will modify the status
349		 * time of the file... And of course this will affect
350		 * backup programs
351		 */
352#ifdef HAVE_UTIMES
353		struct timeval  utsbuf[2];
354		(void)memset(utsbuf, 0, sizeof(utsbuf));
355		utsbuf[0].tv_sec = sb->st_atime;
356		utsbuf[1].tv_sec = sb->st_mtime;
357
358		(void) utimes(name, utsbuf); /* don't care if loses */
359#elif defined(HAVE_UTIME_H) || defined(HAVE_SYS_UTIME_H)
360		struct utimbuf  utbuf;
361
362		(void)memset(&utbuf, 0, sizeof(utbuf));
363		utbuf.actime = sb->st_atime;
364		utbuf.modtime = sb->st_mtime;
365		(void) utime(name, &utbuf); /* don't care if loses */
366#endif
367	}
368}
369
370#ifndef COMPILE_ONLY
371
372/*
373 * find type of descriptor
374 */
375public const char *
376magic_descriptor(struct magic_set *ms, int fd)
377{
378	if (ms == NULL)
379		return NULL;
380	return file_or_fd(ms, NULL, fd);
381}
382
383/*
384 * find type of named file
385 */
386public const char *
387magic_file(struct magic_set *ms, const char *inname)
388{
389	if (ms == NULL)
390		return NULL;
391	return file_or_fd(ms, inname, STDIN_FILENO);
392}
393
394private const char *
395file_or_fd(struct magic_set *ms, const char *inname, int fd)
396{
397	int	rv = -1;
398	unsigned char *buf;
399	struct stat	sb;
400	ssize_t nbytes = 0;	/* number of bytes read from a datafile */
401	int	ispipe = 0;
402	off_t	pos = (off_t)-1;
403
404	if (file_reset(ms) == -1)
405		goto out;
406
407	/*
408	 * one extra for terminating '\0', and
409	 * some overlapping space for matches near EOF
410	 */
411#define SLOP (1 + sizeof(union VALUETYPE))
412	if ((buf = CAST(unsigned char *, malloc(HOWMANY + SLOP))) == NULL)
413		return NULL;
414
415	switch (file_fsmagic(ms, inname, &sb)) {
416	case -1:		/* error */
417		goto done;
418	case 0:			/* nothing found */
419		break;
420	default:		/* matched it and printed type */
421		rv = 0;
422		goto done;
423	}
424
425#ifdef WIN32
426	/* Place stdin in binary mode, so EOF (Ctrl+Z) doesn't stop early. */
427	if (fd == STDIN_FILENO)
428		_setmode(STDIN_FILENO, O_BINARY);
429#endif
430
431	if (inname == NULL) {
432		if (fstat(fd, &sb) == 0 && S_ISFIFO(sb.st_mode))
433			ispipe = 1;
434		else
435			pos = lseek(fd, (off_t)0, SEEK_CUR);
436	} else {
437		int flags = O_RDONLY|O_BINARY;
438		int okstat = stat(inname, &sb) == 0;
439
440		if (okstat && S_ISFIFO(sb.st_mode)) {
441#ifdef O_NONBLOCK
442			flags |= O_NONBLOCK;
443#endif
444			ispipe = 1;
445		}
446
447		errno = 0;
448		if ((fd = open(inname, flags)) < 0) {
449#ifdef WIN32
450			/*
451			 * Can't stat, can't open.  It may have been opened in
452			 * fsmagic, so if the user doesn't have read permission,
453			 * allow it to say so; otherwise an error was probably
454			 * displayed in fsmagic.
455			 */
456			if (!okstat && errno == EACCES) {
457				sb.st_mode = S_IFBLK;
458				okstat = 1;
459			}
460#endif
461			if (okstat &&
462			    unreadable_info(ms, sb.st_mode, inname) == -1)
463				goto done;
464			rv = 0;
465			goto done;
466		}
467#ifdef O_NONBLOCK
468		if ((flags = fcntl(fd, F_GETFL)) != -1) {
469			flags &= ~O_NONBLOCK;
470			(void)fcntl(fd, F_SETFL, flags);
471		}
472#endif
473	}
474
475	/*
476	 * try looking at the first HOWMANY bytes
477	 */
478	if (ispipe) {
479		ssize_t r = 0;
480
481		while ((r = sread(fd, (void *)&buf[nbytes],
482		    (size_t)(HOWMANY - nbytes), 1)) > 0) {
483			nbytes += r;
484			if (r < PIPE_BUF) break;
485		}
486
487		if (nbytes == 0) {
488			/* We can not read it, but we were able to stat it. */
489			if (unreadable_info(ms, sb.st_mode, inname) == -1)
490				goto done;
491			rv = 0;
492			goto done;
493		}
494
495	} else {
496		/* Windows refuses to read from a big console buffer. */
497		size_t howmany =
498#if defined(WIN32) && HOWMANY > 8 * 1024
499				_isatty(fd) ? 8 * 1024 :
500#endif
501				HOWMANY;
502		if ((nbytes = read(fd, (char *)buf, howmany)) == -1) {
503			if (inname == NULL && fd != STDIN_FILENO)
504				file_error(ms, errno, "cannot read fd %d", fd);
505			else
506				file_error(ms, errno, "cannot read `%s'",
507				    inname == NULL ? "/dev/stdin" : inname);
508			goto done;
509		}
510	}
511
512	(void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */
513	if (file_buffer(ms, fd, inname, buf, (size_t)nbytes) == -1)
514		goto done;
515	rv = 0;
516done:
517	free(buf);
518	if (pos != (off_t)-1)
519		(void)lseek(fd, pos, SEEK_SET);
520	close_and_restore(ms, inname, fd, &sb);
521out:
522	return rv == 0 ? file_getbuffer(ms) : NULL;
523}
524
525
526public const char *
527magic_buffer(struct magic_set *ms, const void *buf, size_t nb)
528{
529	if (ms == NULL)
530		return NULL;
531	if (file_reset(ms) == -1)
532		return NULL;
533	/*
534	 * The main work is done here!
535	 * We have the file name and/or the data buffer to be identified.
536	 */
537	if (file_buffer(ms, -1, NULL, buf, nb) == -1) {
538		return NULL;
539	}
540	return file_getbuffer(ms);
541}
542#endif
543
544public const char *
545magic_error(struct magic_set *ms)
546{
547	if (ms == NULL)
548		return "Magic database is not open";
549	return (ms->event_flags & EVENT_HAD_ERR) ? ms->o.buf : NULL;
550}
551
552public int
553magic_errno(struct magic_set *ms)
554{
555	if (ms == NULL)
556		return EINVAL;
557	return (ms->event_flags & EVENT_HAD_ERR) ? ms->error : 0;
558}
559
560public int
561magic_setflags(struct magic_set *ms, int flags)
562{
563	if (ms == NULL)
564		return -1;
565#if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES)
566	if (flags & MAGIC_PRESERVE_ATIME)
567		return -1;
568#endif
569	ms->flags = flags;
570	return 0;
571}
572
573public int
574magic_version(void)
575{
576	return MAGIC_VERSION;
577}
578
579public int
580magic_setparam(struct magic_set *ms, int param, const void *val)
581{
582	switch (param) {
583	case MAGIC_PARAM_INDIR_MAX:
584		ms->indir_max = (uint16_t)*(const size_t *)val;
585		return 0;
586	case MAGIC_PARAM_NAME_MAX:
587		ms->name_max = (uint16_t)*(const size_t *)val;
588		return 0;
589	case MAGIC_PARAM_ELF_PHNUM_MAX:
590		ms->elf_phnum_max = (uint16_t)*(const size_t *)val;
591		return 0;
592	case MAGIC_PARAM_ELF_SHNUM_MAX:
593		ms->elf_shnum_max = (uint16_t)*(const size_t *)val;
594		return 0;
595	case MAGIC_PARAM_ELF_NOTES_MAX:
596		ms->elf_notes_max = (uint16_t)*(const size_t *)val;
597		return 0;
598	default:
599		errno = EINVAL;
600		return -1;
601	}
602}
603
604public int
605magic_getparam(struct magic_set *ms, int param, void *val)
606{
607	switch (param) {
608	case MAGIC_PARAM_INDIR_MAX:
609		*(size_t *)val = ms->indir_max;
610		return 0;
611	case MAGIC_PARAM_NAME_MAX:
612		*(size_t *)val = ms->name_max;
613		return 0;
614	case MAGIC_PARAM_ELF_PHNUM_MAX:
615		*(size_t *)val = ms->elf_phnum_max;
616		return 0;
617	case MAGIC_PARAM_ELF_SHNUM_MAX:
618		*(size_t *)val = ms->elf_shnum_max;
619		return 0;
620	case MAGIC_PARAM_ELF_NOTES_MAX:
621		*(size_t *)val = ms->elf_notes_max;
622		return 0;
623	default:
624		errno = EINVAL;
625		return -1;
626	}
627}
628