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#include "file.h"
29
30#ifndef lint
31FILE_RCSID("@(#)$File: magic.c,v 1.91 2014/12/16 23:18:40 christos Exp $")
32#endif  /* lint */
33
34#include "magic.h"
35
36#include <stdlib.h>
37#ifdef PHP_WIN32
38#include "win32/unistd.h"
39#else
40#include <unistd.h>
41#endif
42#include <string.h>
43#ifdef PHP_WIN32
44# include "config.w32.h"
45#else
46# include "php_config.h"
47#endif
48
49#ifdef PHP_WIN32
50#include <shlwapi.h>
51#endif
52
53#include <limits.h> /* for PIPE_BUF */
54
55#if defined(HAVE_UTIMES)
56# include <sys/time.h>
57#elif defined(HAVE_UTIME)
58# if defined(HAVE_SYS_UTIME_H)
59#  include <sys/utime.h>
60# elif defined(HAVE_UTIME_H)
61#  include <utime.h>
62# endif
63#endif
64
65#ifdef HAVE_UNISTD_H
66#include <unistd.h> /* for read() */
67#endif
68
69#ifndef PIPE_BUF
70/* Get the PIPE_BUF from pathconf */
71#ifdef _PC_PIPE_BUF
72#define PIPE_BUF pathconf(".", _PC_PIPE_BUF)
73#else
74#define PIPE_BUF 512
75#endif
76#endif
77
78#ifdef PHP_WIN32
79# undef S_IFLNK
80# undef S_IFIFO
81#endif
82
83private void close_and_restore(const struct magic_set *, const char *, int,
84    const zend_stat_t *);
85private int unreadable_info(struct magic_set *, mode_t, const char *);
86#if 0
87private const char* get_default_magic(void);
88#endif
89private const char *file_or_stream(struct magic_set *, const char *, php_stream *);
90
91#ifndef STDIN_FILENO
92#define STDIN_FILENO    0
93#endif
94
95/* XXX this functionality is excluded in php, enable it in apprentice.c:340 */
96#if 0
97private const char *
98get_default_magic(void)
99{
100    static const char hmagic[] = "/.magic/magic.mgc";
101    static char *default_magic;
102    char *home, *hmagicpath;
103
104#ifndef PHP_WIN32
105    struct stat st;
106
107    if (default_magic) {
108        free(default_magic);
109        default_magic = NULL;
110    }
111    if ((home = getenv("HOME")) == NULL)
112        return MAGIC;
113
114    if (asprintf(&hmagicpath, "%s/.magic.mgc", home) < 0)
115        return MAGIC;
116    if (stat(hmagicpath, &st) == -1) {
117        free(hmagicpath);
118    if (asprintf(&hmagicpath, "%s/.magic", home) < 0)
119        return MAGIC;
120    if (stat(hmagicpath, &st) == -1)
121        goto out;
122    if (S_ISDIR(st.st_mode)) {
123        free(hmagicpath);
124        if (asprintf(&hmagicpath, "%s/%s", home, hmagic) < 0)
125            return MAGIC;
126        if (access(hmagicpath, R_OK) == -1)
127            goto out;
128    }
129    }
130
131    if (asprintf(&default_magic, "%s:%s", hmagicpath, MAGIC) < 0)
132        goto out;
133    free(hmagicpath);
134    return default_magic;
135out:
136    default_magic = NULL;
137    free(hmagicpath);
138    return MAGIC;
139#else
140    char *hmagicp = hmagicpath;
141    char *tmppath = NULL;
142    LPTSTR dllpath;
143
144#define APPENDPATH() \
145    do { \
146        if (tmppath && access(tmppath, R_OK) != -1) { \
147            if (hmagicpath == NULL) \
148                hmagicpath = tmppath; \
149            else { \
150                if (asprintf(&hmagicp, "%s%c%s", hmagicpath, \
151                    PATHSEP, tmppath) >= 0) { \
152                    free(hmagicpath); \
153                    hmagicpath = hmagicp; \
154                } \
155                free(tmppath); \
156            } \
157            tmppath = NULL; \
158        } \
159    } while (/*CONSTCOND*/0)
160
161    if (default_magic) {
162        free(default_magic);
163        default_magic = NULL;
164    }
165
166    /* First, try to get user-specific magic file */
167    if ((home = getenv("LOCALAPPDATA")) == NULL) {
168        if ((home = getenv("USERPROFILE")) != NULL)
169            if (asprintf(&tmppath,
170                "%s/Local Settings/Application Data%s", home,
171                hmagic) < 0)
172                tmppath = NULL;
173    } else {
174        if (asprintf(&tmppath, "%s%s", home, hmagic) < 0)
175            tmppath = NULL;
176    }
177
178    APPENDPATH();
179
180    /* Second, try to get a magic file from Common Files */
181    if ((home = getenv("COMMONPROGRAMFILES")) != NULL) {
182        if (asprintf(&tmppath, "%s%s", home, hmagic) >= 0)
183            APPENDPATH();
184    }
185
186    /* Third, try to get magic file relative to dll location */
187    dllpath = malloc(sizeof(*dllpath) * (MAX_PATH + 1));
188    dllpath[MAX_PATH] = 0;  /* just in case long path gets truncated and not null terminated */
189    if (GetModuleFileNameA(NULL, dllpath, MAX_PATH)){
190        PathRemoveFileSpecA(dllpath);
191        if (strlen(dllpath) > 3 &&
192            stricmp(&dllpath[strlen(dllpath) - 3], "bin") == 0) {
193            if (asprintf(&tmppath,
194                "%s/../share/misc/magic.mgc", dllpath) >= 0)
195                APPENDPATH();
196        } else {
197            if (asprintf(&tmppath,
198                "%s/share/misc/magic.mgc", dllpath) >= 0)
199                APPENDPATH();
200            else if (asprintf(&tmppath,
201                "%s/magic.mgc", dllpath) >= 0)
202                APPENDPATH();
203        }
204    }
205
206    /* Don't put MAGIC constant - it likely points to a file within MSys
207    tree */
208    default_magic = hmagicpath;
209    return default_magic;
210#endif
211}
212
213public const char *
214magic_getpath(const char *magicfile, int action)
215{
216    if (magicfile != NULL)
217        return magicfile;
218
219    magicfile = getenv("MAGIC");
220    if (magicfile != NULL)
221        return magicfile;
222
223    return action == FILE_LOAD ? get_default_magic() : MAGIC;
224}
225#endif
226
227public struct magic_set *
228magic_open(int flags)
229{
230    return file_ms_alloc(flags);
231}
232
233private int
234unreadable_info(struct magic_set *ms, mode_t md, const char *file)
235{
236    if (file) {
237        /* We cannot open it, but we were able to stat it. */
238        if (access(file, W_OK) == 0)
239            if (file_printf(ms, "writable, ") == -1)
240                return -1;
241        if (access(file, X_OK) == 0)
242            if (file_printf(ms, "executable, ") == -1)
243                return -1;
244    }
245    if (S_ISREG(md))
246        if (file_printf(ms, "regular file, ") == -1)
247            return -1;
248    if (file_printf(ms, "no read permission") == -1)
249        return -1;
250    return 0;
251}
252
253public void
254magic_close(struct magic_set *ms)
255{
256    if (ms == NULL)
257        return;
258    file_ms_free(ms);
259}
260
261/*
262 * load a magic file
263 */
264public int
265magic_load(struct magic_set *ms, const char *magicfile)
266{
267    if (ms == NULL)
268        return -1;
269    return file_apprentice(ms, magicfile, FILE_LOAD);
270}
271
272public int
273magic_compile(struct magic_set *ms, const char *magicfile)
274{
275    if (ms == NULL)
276        return -1;
277    return file_apprentice(ms, magicfile, FILE_COMPILE);
278}
279
280
281public int
282magic_list(struct magic_set *ms, const char *magicfile)
283{
284    if (ms == NULL)
285        return -1;
286    return file_apprentice(ms, magicfile, FILE_LIST);
287}
288
289private void
290close_and_restore(const struct magic_set *ms, const char *name, int fd,
291    const zend_stat_t *sb)
292{
293
294    if ((ms->flags & MAGIC_PRESERVE_ATIME) != 0) {
295        /*
296         * Try to restore access, modification times if read it.
297         * This is really *bad* because it will modify the status
298         * time of the file... And of course this will affect
299         * backup programs
300         */
301#ifdef HAVE_UTIMES
302        struct timeval  utsbuf[2];
303        (void)memset(utsbuf, 0, sizeof(utsbuf));
304        utsbuf[0].tv_sec = sb->st_atime;
305        utsbuf[1].tv_sec = sb->st_mtime;
306
307        (void) utimes(name, utsbuf); /* don't care if loses */
308#elif defined(HAVE_UTIME_H) || defined(HAVE_SYS_UTIME_H)
309        struct utimbuf  utbuf;
310
311        (void)memset(&utbuf, 0, sizeof(utbuf));
312        utbuf.actime = sb->st_atime;
313        utbuf.modtime = sb->st_mtime;
314        (void) utime(name, &utbuf); /* don't care if loses */
315#endif
316    }
317}
318
319
320/*
321 * find type of descriptor
322 */
323public const char *
324magic_descriptor(struct magic_set *ms, int fd)
325{
326    if (ms == NULL)
327        return NULL;
328    return file_or_stream(ms, NULL, NULL);
329}
330
331/*
332 * find type of named file
333 */
334public const char *
335magic_file(struct magic_set *ms, const char *inname)
336{
337    if (ms == NULL)
338        return NULL;
339    return file_or_stream(ms, inname, NULL);
340}
341
342public const char *
343magic_stream(struct magic_set *ms, php_stream *stream)
344{
345    if (ms == NULL)
346        return NULL;
347    return file_or_stream(ms, NULL, stream);
348}
349
350private const char *
351file_or_stream(struct magic_set *ms, const char *inname, php_stream *stream)
352{
353    int rv = -1;
354    unsigned char *buf;
355    zend_stat_t   sb;
356    ssize_t nbytes = 0; /* number of bytes read from a datafile */
357    int no_in_stream = 0;
358
359    if (!inname && !stream) {
360        return NULL;
361    }
362
363    /*
364     * one extra for terminating '\0', and
365     * some overlapping space for matches near EOF
366     */
367#define SLOP (1 + sizeof(union VALUETYPE))
368    buf = emalloc(HOWMANY + SLOP);
369
370    if (file_reset(ms) == -1)
371        goto done;
372
373    switch (file_fsmagic(ms, inname, &sb, stream)) {
374    case -1:        /* error */
375        goto done;
376    case 0:         /* nothing found */
377        break;
378    default:        /* matched it and printed type */
379        rv = 0;
380        goto done;
381    }
382
383    errno = 0;
384
385    if (!stream && inname) {
386        no_in_stream = 1;
387        stream = php_stream_open_wrapper((char *)inname, "rb", REPORT_ERRORS, NULL);
388    }
389
390    if (!stream) {
391        if (unreadable_info(ms, sb.st_mode, inname) == -1)
392            goto done;
393        rv = 0;
394        goto done;
395    }
396
397#ifdef O_NONBLOCK
398/* we should be already be in non blocking mode for network socket */
399#endif
400
401    /*
402     * try looking at the first HOWMANY bytes
403     */
404    if ((nbytes = php_stream_read(stream, (char *)buf, HOWMANY)) < 0) {
405        file_error(ms, errno, "cannot read `%s'", inname);
406        goto done;
407    }
408
409    (void)memset(buf + nbytes, 0, SLOP); /* NUL terminate */
410    if (file_buffer(ms, stream, inname, buf, (size_t)nbytes) == -1)
411        goto done;
412    rv = 0;
413done:
414    efree(buf);
415
416    if (no_in_stream && stream) {
417        php_stream_close(stream);
418    }
419
420    close_and_restore(ms, inname, 0, &sb);
421    return rv == 0 ? file_getbuffer(ms) : NULL;
422}
423
424
425public const char *
426magic_buffer(struct magic_set *ms, const void *buf, size_t nb)
427{
428    if (ms == NULL)
429        return NULL;
430    if (file_reset(ms) == -1)
431        return NULL;
432    /*
433     * The main work is done here!
434     * We have the file name and/or the data buffer to be identified.
435     */
436    if (file_buffer(ms, NULL, NULL, buf, nb) == -1) {
437        return NULL;
438    }
439    return file_getbuffer(ms);
440}
441
442public const char *
443magic_error(struct magic_set *ms)
444{
445    if (ms == NULL)
446        return "Magic database is not open";
447    return (ms->event_flags & EVENT_HAD_ERR) ? ms->o.buf : NULL;
448}
449
450public int
451magic_errno(struct magic_set *ms)
452{
453    if (ms == NULL)
454        return EINVAL;
455    return (ms->event_flags & EVENT_HAD_ERR) ? ms->error : 0;
456}
457
458public int
459magic_setflags(struct magic_set *ms, int flags)
460{
461    if (ms == NULL)
462        return -1;
463#if !defined(HAVE_UTIME) && !defined(HAVE_UTIMES)
464    if (flags & MAGIC_PRESERVE_ATIME)
465        return -1;
466#endif
467    ms->flags = flags;
468    return 0;
469}
470
471public int
472magic_version(void)
473{
474    return MAGIC_VERSION;
475}
476
477public int
478magic_setparam(struct magic_set *ms, int param, const void *val)
479{
480    switch (param) {
481    case MAGIC_PARAM_INDIR_MAX:
482        ms->indir_max = (uint16_t)*(const size_t *)val;
483        return 0;
484    case MAGIC_PARAM_NAME_MAX:
485        ms->name_max = (uint16_t)*(const size_t *)val;
486        return 0;
487    case MAGIC_PARAM_ELF_PHNUM_MAX:
488        ms->elf_phnum_max = (uint16_t)*(const size_t *)val;
489        return 0;
490    case MAGIC_PARAM_ELF_SHNUM_MAX:
491        ms->elf_shnum_max = (uint16_t)*(const size_t *)val;
492        return 0;
493    case MAGIC_PARAM_ELF_NOTES_MAX:
494        ms->elf_notes_max = (uint16_t)*(const size_t *)val;
495        return 0;
496    default:
497        errno = EINVAL;
498        return -1;
499    }
500}
501
502public int
503magic_getparam(struct magic_set *ms, int param, void *val)
504{
505    switch (param) {
506    case MAGIC_PARAM_INDIR_MAX:
507        *(size_t *)val = ms->indir_max;
508        return 0;
509    case MAGIC_PARAM_NAME_MAX:
510        *(size_t *)val = ms->name_max;
511        return 0;
512    case MAGIC_PARAM_ELF_PHNUM_MAX:
513        *(size_t *)val = ms->elf_phnum_max;
514        return 0;
515    case MAGIC_PARAM_ELF_SHNUM_MAX:
516        *(size_t *)val = ms->elf_shnum_max;
517        return 0;
518    case MAGIC_PARAM_ELF_NOTES_MAX:
519        *(size_t *)val = ms->elf_notes_max;
520        return 0;
521    default:
522        errno = EINVAL;
523        return -1;
524    }
525}
526