diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/Makefile.in | 64 | ||||
-rw-r--r-- | src/apprentice.c | 409 | ||||
-rw-r--r-- | src/ascmagic.c | 20 | ||||
-rw-r--r-- | src/cdf.c | 593 | ||||
-rw-r--r-- | src/cdf.h | 22 | ||||
-rw-r--r-- | src/cdf_time.c | 4 | ||||
-rw-r--r-- | src/compress.c | 620 | ||||
-rw-r--r-- | src/der.c | 404 | ||||
-rw-r--r-- | src/der.h | 28 | ||||
-rw-r--r-- | src/dprintf.c | 58 | ||||
-rw-r--r-- | src/encoding.c | 38 | ||||
-rw-r--r-- | src/file.c | 119 | ||||
-rw-r--r-- | src/file.h | 74 | ||||
-rw-r--r-- | src/file_opts.h | 54 | ||||
-rw-r--r-- | src/fmtcheck.c | 17 | ||||
-rw-r--r-- | src/fsmagic.c | 19 | ||||
-rw-r--r-- | src/funcs.c | 131 | ||||
-rw-r--r-- | src/gmtime_r.c | 19 | ||||
-rw-r--r-- | src/is_tar.c | 39 | ||||
-rw-r--r-- | src/localtime_r.c | 19 | ||||
-rw-r--r-- | src/magic.c | 219 | ||||
-rw-r--r-- | src/magic.h | 119 | ||||
-rw-r--r-- | src/magic.h.in | 83 | ||||
-rw-r--r-- | src/print.c | 49 | ||||
-rw-r--r-- | src/readcdf.c | 293 | ||||
-rw-r--r-- | src/readelf.c | 369 | ||||
-rw-r--r-- | src/readelf.h | 70 | ||||
-rw-r--r-- | src/softmagic.c | 835 | ||||
-rw-r--r-- | src/vasprintf.c | 4 |
30 files changed, 3210 insertions, 1586 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index e3196ce..155aec4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ MAGIC = $(pkgdatadir)/magic lib_LTLIBRARIES = libmagic.la -include_HEADERS = magic.h +nodist_include_HEADERS = magic.h bin_PROGRAMS = file @@ -9,7 +9,7 @@ AM_CFLAGS = $(CFLAG_VISIBILITY) @WARNINGS@ libmagic_la_SOURCES = magic.c apprentice.c softmagic.c ascmagic.c \ encoding.c compress.c is_tar.c readelf.c print.c fsmagic.c \ - funcs.c file.h readelf.h tar.h apptype.c \ + funcs.c file.h readelf.h tar.h apptype.c der.c der.h \ file_opts.h elfclass.h mygetopt.h cdf.c cdf_time.c readcdf.c cdf.h libmagic_la_LDFLAGS = -no-undefined -version-info 1:0:0 if MINGW diff --git a/src/Makefile.in b/src/Makefile.in index 41cb9f5..b6eeb20 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.14 from Makefile.am. +# Makefile.in generated by automake 1.15 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2013 Free Software Foundation, Inc. +# Copyright (C) 1994-2014 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -17,7 +17,17 @@ VPATH = @srcdir@ -am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ @@ -81,10 +91,6 @@ build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = file$(EXEEXT) subdir = src -DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ctime_r.c \ - vasprintf.c asctime_r.c asprintf.c strcasestr.c pread.c \ - getline.c strlcpy.c strlcat.c fmtcheck.c getopt_long.c \ - $(top_srcdir)/depcomp $(include_HEADERS) ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ @@ -92,6 +98,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = @@ -130,8 +137,8 @@ am__DEPENDENCIES_1 = libmagic_la_DEPENDENCIES = $(LTLIBOBJS) $(am__DEPENDENCIES_1) am_libmagic_la_OBJECTS = magic.lo apprentice.lo softmagic.lo \ ascmagic.lo encoding.lo compress.lo is_tar.lo readelf.lo \ - print.lo fsmagic.lo funcs.lo apptype.lo cdf.lo cdf_time.lo \ - readcdf.lo + print.lo fsmagic.lo funcs.lo apptype.lo der.lo cdf.lo \ + cdf_time.lo readcdf.lo libmagic_la_OBJECTS = $(am_libmagic_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -185,7 +192,7 @@ am__can_run_installinfo = \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac -HEADERS = $(include_HEADERS) +HEADERS = $(nodist_include_HEADERS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is @@ -205,6 +212,10 @@ am__define_uniq_tagged_files = \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + asctime_r.c asprintf.c ctime_r.c dprintf.c fmtcheck.c \ + getline.c getopt_long.c gmtime_r.c localtime_r.c pread.c \ + strcasestr.c strlcat.c strlcpy.c vasprintf.c DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) pkgdatadir = @pkgdatadir@ ACLOCAL = @ACLOCAL@ @@ -328,12 +339,12 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ MAGIC = $(pkgdatadir)/magic lib_LTLIBRARIES = libmagic.la -include_HEADERS = magic.h +nodist_include_HEADERS = magic.h AM_CPPFLAGS = -DMAGIC='"$(MAGIC)"' AM_CFLAGS = $(CFLAG_VISIBILITY) @WARNINGS@ libmagic_la_SOURCES = magic.c apprentice.c softmagic.c ascmagic.c \ encoding.c compress.c is_tar.c readelf.c print.c fsmagic.c \ - funcs.c file.h readelf.h tar.h apptype.c \ + funcs.c file.h readelf.h tar.h apptype.c der.c der.h \ file_opts.h elfclass.h mygetopt.h cdf.c cdf_time.c readcdf.c cdf.h libmagic_la_LDFLAGS = -no-undefined -version-info 1:0:0 @@ -363,7 +374,6 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -482,9 +492,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asctime_r.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/asprintf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ctime_r.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/dprintf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/fmtcheck.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/getopt_long.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/gmtime_r.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/localtime_r.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/pread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strcasestr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strlcat.Plo@am__quote@ @@ -496,6 +509,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cdf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cdf_time.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compress.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/der.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encoding.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fsmagic.Plo@am__quote@ @@ -536,9 +550,9 @@ mostlyclean-libtool: clean-libtool: -rm -rf .libs _libs -install-includeHEADERS: $(include_HEADERS) +install-nodist_includeHEADERS: $(nodist_include_HEADERS) @$(NORMAL_INSTALL) - @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ + @list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ @@ -552,9 +566,9 @@ install-includeHEADERS: $(include_HEADERS) $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \ done -uninstall-includeHEADERS: +uninstall-nodist_includeHEADERS: @$(NORMAL_UNINSTALL) - @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ + @list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) @@ -706,7 +720,7 @@ info: info-am info-am: -install-data-am: install-includeHEADERS +install-data-am: install-nodist_includeHEADERS install-dvi: install-dvi-am @@ -752,8 +766,8 @@ ps: ps-am ps-am: -uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \ - uninstall-libLTLIBRARIES +uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-nodist_includeHEADERS .MAKE: all check install install-am install-strip @@ -764,15 +778,17 @@ uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-binPROGRAMS install-data \ install-data-am install-dvi install-dvi-am install-exec \ - install-exec-am install-html install-html-am \ - install-includeHEADERS install-info install-info-am \ - install-libLTLIBRARIES install-man install-pdf install-pdf-am \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man \ + install-nodist_includeHEADERS install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ - uninstall-includeHEADERS uninstall-libLTLIBRARIES + uninstall-libLTLIBRARIES uninstall-nodist_includeHEADERS + +.PRECIOUS: Makefile magic.h: ${HDR} diff --git a/src/apprentice.c b/src/apprentice.c index 47b4c87..a7b4dd8 100644 --- a/src/apprentice.c +++ b/src/apprentice.c @@ -32,7 +32,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: apprentice.c,v 1.229 2015/01/01 17:07:34 christos Exp $") +FILE_RCSID("@(#)$File: apprentice.c,v 1.262 2017/08/28 13:39:18 christos Exp $") #endif /* lint */ #include "magic.h" @@ -86,9 +86,9 @@ FILE_RCSID("@(#)$File: apprentice.c,v 1.229 2015/01/01 17:07:34 christos Exp $") #define ALLOC_CHUNK (size_t)10 #define ALLOC_INCR (size_t)200 -#define MAP_TYPE_MMAP 0 +#define MAP_TYPE_USER 0 #define MAP_TYPE_MALLOC 1 -#define MAP_TYPE_USER 2 +#define MAP_TYPE_MMAP 2 struct magic_entry { struct magic *mp; @@ -143,12 +143,13 @@ private int check_buffer(struct magic_set *, struct magic_map *, const char *); private void apprentice_unmap(struct magic_map *); private int apprentice_compile(struct magic_set *, struct magic_map *, const char *); -private int check_format_type(const char *, int); +private int check_format_type(const char *, int, const char **); private int check_format(struct magic_set *, struct magic *); private int get_op(char); private int parse_mime(struct magic_set *, struct magic_entry *, const char *); private int parse_strength(struct magic_set *, struct magic_entry *, const char *); private int parse_apple(struct magic_set *, struct magic_entry *, const char *); +private int parse_ext(struct magic_set *, struct magic_entry *, const char *); private size_t magicsize = sizeof(struct magic); @@ -163,6 +164,7 @@ private struct { #define DECLARE_FIELD(name) { # name, sizeof(# name) - 1, parse_ ## name } DECLARE_FIELD(mime), DECLARE_FIELD(apple), + DECLARE_FIELD(ext), DECLARE_FIELD(strength), #undef DECLARE_FIELD { NULL, 0, NULL } @@ -266,6 +268,7 @@ static const struct type_tbl_s type_tbl[] = { { XX("name"), FILE_NAME, FILE_FMT_NONE }, { XX("use"), FILE_USE, FILE_FMT_NONE }, { XX("clear"), FILE_CLEAR, FILE_FMT_NONE }, + { XX("der"), FILE_DER, FILE_FMT_STR }, { XX_NULL, FILE_INVALID, FILE_FMT_NONE }, }; @@ -274,6 +277,7 @@ static const struct type_tbl_s type_tbl[] = { * unsigned. */ static const struct type_tbl_s special_tbl[] = { + { XX("der"), FILE_DER, FILE_FMT_STR }, { XX("name"), FILE_NAME, FILE_FMT_STR }, { XX("use"), FILE_USE, FILE_FMT_STR }, { XX_NULL, FILE_INVALID, FILE_FMT_NONE }, @@ -404,11 +408,11 @@ add_mlist(struct mlist *mlp, struct magic_map *map, size_t idx) { struct mlist *ml; - mlp->map = idx == 0 ? map : NULL; + mlp->map = NULL; if ((ml = CAST(struct mlist *, malloc(sizeof(*ml)))) == NULL) return -1; - ml->map = NULL; + ml->map = idx == 0 ? map : NULL; ml->magic = map->magic[idx]; ml->nmagic = map->nmagic[idx]; @@ -447,6 +451,8 @@ apprentice_1(struct magic_set *ms, const char *fn, int action) #ifndef COMPILE_ONLY map = apprentice_map(ms, fn); + if (map == (struct magic_map *)-1) + return -1; if (map == NULL) { if (ms->flags & MAGIC_CHECK) file_magwarn(ms, "using regular magic file `%s'", fn); @@ -458,7 +464,7 @@ apprentice_1(struct magic_set *ms, const char *fn, int action) for (i = 0; i < MAGIC_SETS; i++) { if (add_mlist(ms->mlist[i], map, i) == -1) { file_oomem(ms, sizeof(*ml)); - goto fail; + return -1; } } @@ -472,12 +478,6 @@ apprentice_1(struct magic_set *ms, const char *fn, int action) } } return 0; -fail: - for (i = 0; i < MAGIC_SETS; i++) { - mlist_free(ms->mlist[i]); - ms->mlist[i] = NULL; - } - return -1; #else return 0; #endif /* COMPILE_ONLY */ @@ -529,6 +529,8 @@ file_ms_alloc(int flags) ms->elf_shnum_max = FILE_ELF_SHNUM_MAX; ms->elf_phnum_max = FILE_ELF_PHNUM_MAX; ms->elf_notes_max = FILE_ELF_NOTES_MAX; + ms->regex_max = FILE_REGEX_MAX; + ms->bytes_max = FILE_BYTES_MAX; return ms; free: free(ms); @@ -538,21 +540,30 @@ free: private void apprentice_unmap(struct magic_map *map) { + size_t i; if (map == NULL) return; switch (map->type) { -#ifdef QUICK - case MAP_TYPE_MMAP: - if (map->p) - (void)munmap(map->p, map->len); + case MAP_TYPE_USER: break; -#endif case MAP_TYPE_MALLOC: + for (i = 0; i < MAGIC_SETS; i++) { + void *b = map->magic[i]; + void *p = map->p; + if (CAST(char *, b) >= CAST(char *, p) && + CAST(char *, b) <= CAST(char *, p) + map->len) + continue; + free(map->magic[i]); + } free(map->p); break; - case MAP_TYPE_USER: +#ifdef QUICK + case MAP_TYPE_MMAP: + if (map->p && map->p != MAP_FAILED) + (void)munmap(map->p, map->len); break; +#endif default: abort(); } @@ -581,7 +592,7 @@ mlist_free(struct mlist *mlist) ml = mlist->next; for (ml = mlist->next; (next = ml->next) != NULL; ml = next) { if (ml->map) - apprentice_unmap(ml->map); + apprentice_unmap(CAST(struct magic_map *, ml->map)); free(ml); if (ml == mlist) break; @@ -601,8 +612,7 @@ buffer_apprentice(struct magic_set *ms, struct magic **bufs, if (nbufs == 0) return -1; - if (ms->mlist[0] != NULL) - file_reset(ms); + (void)file_reset(ms, 0); init_file_tables(); @@ -645,8 +655,7 @@ file_apprentice(struct magic_set *ms, const char *fn, int action) int file_err, errs = -1; size_t i; - if (ms->mlist[0] != NULL) - file_reset(ms); + (void)file_reset(ms, 0); if ((fn = magic_getpath(fn, action)) == NULL) return -1; @@ -768,6 +777,59 @@ nonmagic(const char *str) return rv == 0 ? 1 : rv; /* Return at least 1 */ } + +private size_t +typesize(int type) +{ + switch (type) { + case FILE_BYTE: + return 1; + + case FILE_SHORT: + case FILE_LESHORT: + case FILE_BESHORT: + return 2; + + case FILE_LONG: + case FILE_LELONG: + case FILE_BELONG: + case FILE_MELONG: + return 4; + + case FILE_DATE: + case FILE_LEDATE: + case FILE_BEDATE: + case FILE_MEDATE: + case FILE_LDATE: + case FILE_LELDATE: + case FILE_BELDATE: + case FILE_MELDATE: + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: + return 4; + + case FILE_QUAD: + case FILE_BEQUAD: + case FILE_LEQUAD: + case FILE_QDATE: + case FILE_LEQDATE: + case FILE_BEQDATE: + case FILE_QLDATE: + case FILE_LEQLDATE: + case FILE_BEQLDATE: + case FILE_QWDATE: + case FILE_LEQWDATE: + case FILE_BEQWDATE: + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: + return 8; + default: + return (size_t)~0; + } +} + /* * Get weight of this magic entry, for sorting purposes. */ @@ -775,7 +837,7 @@ private size_t apprentice_magic_strength(const struct magic *m) { #define MULT 10 - size_t v, val = 2 * MULT; /* baseline strength */ + size_t ts, v, val = 2 * MULT; /* baseline strength */ switch (m->type) { case FILE_DEFAULT: /* make sure this sorts last */ @@ -784,41 +846,13 @@ apprentice_magic_strength(const struct magic *m) return 0; case FILE_BYTE: - val += 1 * MULT; - break; - case FILE_SHORT: case FILE_LESHORT: case FILE_BESHORT: - val += 2 * MULT; - break; - case FILE_LONG: case FILE_LELONG: case FILE_BELONG: case FILE_MELONG: - val += 4 * MULT; - break; - - case FILE_PSTRING: - case FILE_STRING: - val += m->vallen * MULT; - break; - - case FILE_BESTRING16: - case FILE_LESTRING16: - val += m->vallen * MULT / 2; - break; - - case FILE_SEARCH: - val += m->vallen * MAX(MULT / m->vallen, 1); - break; - - case FILE_REGEX: - v = nonmagic(m->value.s); - val += v * MAX(MULT / v, 1); - break; - case FILE_DATE: case FILE_LEDATE: case FILE_BEDATE: @@ -830,9 +864,6 @@ apprentice_magic_strength(const struct magic *m) case FILE_FLOAT: case FILE_BEFLOAT: case FILE_LEFLOAT: - val += 4 * MULT; - break; - case FILE_QUAD: case FILE_BEQUAD: case FILE_LEQUAD: @@ -848,7 +879,29 @@ apprentice_magic_strength(const struct magic *m) case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: - val += 8 * MULT; + ts = typesize(m->type); + if (ts == (size_t)~0) + abort(); + val += ts * MULT; + break; + + case FILE_PSTRING: + case FILE_STRING: + val += m->vallen * MULT; + break; + + case FILE_BESTRING16: + case FILE_LESTRING16: + val += m->vallen * MULT / 2; + break; + + case FILE_SEARCH: + val += m->vallen * MAX(MULT / m->vallen, 1); + break; + + case FILE_REGEX: + v = nonmagic(m->value.s); + val += v * MAX(MULT / v, 1); break; case FILE_INDIRECT: @@ -856,6 +909,10 @@ apprentice_magic_strength(const struct magic *m) case FILE_USE: break; + case FILE_DER: + val += MULT; + break; + default: (void)fprintf(stderr, "Bad type %d\n", m->type); abort(); @@ -964,8 +1021,9 @@ apprentice_list(struct mlist *mlist, int mode) *ml->magic[magindex].mimetype == '\0') magindex++; - printf("Strength = %3" SIZE_T_FORMAT "u : %s [%s]\n", + printf("Strength = %3" SIZE_T_FORMAT "u@%u: %s [%s]\n", apprentice_magic_strength(m), + ml->magic[magindex].lineno, ml->magic[magindex].desc, ml->magic[magindex].mimetype); } @@ -1010,6 +1068,7 @@ set_test_type(struct magic *mstart, struct magic *m) case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: + case FILE_DER: mstart->flag |= BINTEST; break; case FILE_STRING: @@ -1285,6 +1344,7 @@ apprentice_load(struct magic_set *ms, const char *fn, int action) file_oomem(ms, sizeof(*map)); return NULL; } + map->type = MAP_TYPE_MALLOC; /* print silly verbose header for USG compat. */ if (action == FILE_CHECK) @@ -1298,6 +1358,8 @@ apprentice_load(struct magic_set *ms, const char *fn, int action) goto out; } while ((d = readdir(dir)) != NULL) { + if (d->d_name[0] == '.') + continue; if (asprintf(&mfn, "%s/%s", fn, d->d_name) < 0) { file_oomem(ms, strlen(fn) + strlen(d->d_name) + 2); @@ -1345,8 +1407,9 @@ apprentice_load(struct magic_set *ms, const char *fn, int action) } i = set_text_binary(ms, mset[j].me, mset[j].count, i); } - qsort(mset[j].me, mset[j].count, sizeof(*mset[j].me), - apprentice_sort); + if (mset[j].me) + qsort(mset[j].me, mset[j].count, sizeof(*mset[j].me), + apprentice_sort); /* * Make sure that any level 0 "default" line is last @@ -1439,6 +1502,7 @@ file_signextend(struct magic_set *ms, struct magic *m, uint64_t v) case FILE_NAME: case FILE_USE: case FILE_CLEAR: + case FILE_DER: break; default: if (ms->flags & MAGIC_CHECK) @@ -1839,24 +1903,31 @@ parse(struct magic_set *ms, struct magic_entry *me, const char *line, } } /* Indirect offsets are not valid at level 0. */ - if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD))) + if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD))) { if (ms->flags & MAGIC_CHECK) file_magwarn(ms, "relative offset at level 0"); + return -1; + } /* get offset, then skip over it */ m->offset = (uint32_t)strtoul(l, &t, 0); - if (l == t) + if (l == t) { if (ms->flags & MAGIC_CHECK) file_magwarn(ms, "offset `%s' invalid", l); + return -1; + } l = t; if (m->flag & INDIR) { m->in_type = FILE_LONG; m->in_offset = 0; + m->in_op = 0; /* - * read [.lbs][+-]nnnnn) + * read [.,lbs][+-]nnnnn) */ - if (*l == '.') { + if (*l == '.' || *l == ',') { + if (*l == ',') + m->in_op |= FILE_OPSIGNED; l++; switch (*l) { case 'l': @@ -1903,12 +1974,11 @@ parse(struct magic_set *ms, struct magic_entry *me, const char *line, file_magwarn(ms, "indirect offset type `%c' invalid", *l); - break; + return -1; } l++; } - m->in_op = 0; if (*l == '~') { m->in_op |= FILE_OPINVERSE; l++; @@ -1923,17 +1993,21 @@ parse(struct magic_set *ms, struct magic_entry *me, const char *line, } if (isdigit((unsigned char)*l) || *l == '-') { m->in_offset = (int32_t)strtol(l, &t, 0); - if (l == t) + if (l == t) { if (ms->flags & MAGIC_CHECK) file_magwarn(ms, "in_offset `%s' invalid", l); + return -1; + } l = t; } if (*l++ != ')' || - ((m->in_op & FILE_OPINDIRECT) && *l++ != ')')) + ((m->in_op & FILE_OPINDIRECT) && *l++ != ')')) { if (ms->flags & MAGIC_CHECK) file_magwarn(ms, "missing ')' in indirect offset"); + return -1; + } } EATAB; @@ -2086,7 +2160,7 @@ parse(struct magic_set *ms, struct magic_entry *me, const char *line, /* * TODO finish this macro and start using it! - * #define offsetcheck {if (offset > HOWMANY-1) + * #define offsetcheck {if (offset > ms->bytes_max -1) * magwarn("offset too big"); } */ @@ -2199,7 +2273,7 @@ parse_extra(struct magic_set *ms, struct magic_entry *me, const char *line, size_t i; const char *l = line; struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1]; - char *buf = (char *)m + off; + char *buf = CAST(char *, CAST(void *, m)) + off; if (buf[0] != '\0') { len = nt ? strlen(buf) : len; @@ -2248,8 +2322,22 @@ parse_apple(struct magic_set *ms, struct magic_entry *me, const char *line) { struct magic *m = &me->mp[0]; - return parse_extra(ms, me, line, offsetof(struct magic, apple), - sizeof(m->apple), "APPLE", "!+-./", 0); + return parse_extra(ms, me, line, + CAST(off_t, offsetof(struct magic, apple)), + sizeof(m->apple), "APPLE", "!+-./?", 0); +} + +/* + * Parse a comma-separated list of extensions + */ +private int +parse_ext(struct magic_set *ms, struct magic_entry *me, const char *line) +{ + struct magic *m = &me->mp[0]; + + return parse_extra(ms, me, line, + CAST(off_t, offsetof(struct magic, ext)), + sizeof(m->ext), "EXTENSION", ",!+-/@", 0); } /* @@ -2261,16 +2349,19 @@ parse_mime(struct magic_set *ms, struct magic_entry *me, const char *line) { struct magic *m = &me->mp[0]; - return parse_extra(ms, me, line, offsetof(struct magic, mimetype), + return parse_extra(ms, me, line, + CAST(off_t, offsetof(struct magic, mimetype)), sizeof(m->mimetype), "MIME", "+-/.", 1); } private int -check_format_type(const char *ptr, int type) +check_format_type(const char *ptr, int type, const char **estr) { int quad = 0, h; + size_t len, cnt; if (*ptr == '\0') { /* Missing format string; bad */ + *estr = "missing format spec"; return -1; } @@ -2307,15 +2398,24 @@ check_format_type(const char *ptr, int type) ptr++; if (*ptr == '.') ptr++; - while (isdigit((unsigned char)*ptr)) ptr++; + if (*ptr == '#') + ptr++; +#define CHECKLEN() do { \ + for (len = cnt = 0; isdigit((unsigned char)*ptr); ptr++, cnt++) \ + len = len * 10 + (*ptr - '0'); \ + if (cnt > 5 || len > 1024) \ + goto toolong; \ +} while (/*CONSTCOND*/0) + + CHECKLEN(); if (*ptr == '.') ptr++; - while (isdigit((unsigned char)*ptr)) ptr++; + CHECKLEN(); if (quad) { if (*ptr++ != 'l') - return -1; + goto invalid; if (*ptr++ != 'l') - return -1; + goto invalid; } switch (*ptr++) { @@ -2329,9 +2429,11 @@ check_format_type(const char *ptr, int type) case 'o': case 'x': case 'X': - return h != 0 ? -1 : 0; + if (h == 0) + return 0; + /*FALLTHROUGH*/ default: - return -1; + goto invalid; } /* @@ -2340,11 +2442,11 @@ check_format_type(const char *ptr, int type) */ case 'h': if (h-- <= 0) - return -1; + goto invalid; switch (*ptr++) { case 'h': if (h-- <= 0) - return -1; + goto invalid; switch (*ptr++) { case 'i': case 'd': @@ -2354,7 +2456,7 @@ check_format_type(const char *ptr, int type) case 'X': return 0; default: - return -1; + goto invalid; } case 'i': case 'd': @@ -2362,13 +2464,17 @@ check_format_type(const char *ptr, int type) case 'o': case 'x': case 'X': - return h != 0 ? -1 : 0; + if (h == 0) + return 0; + /*FALLTHROUGH*/ default: - return -1; + goto invalid; } #endif case 'c': - return h != 2 ? -1 : 0; + if (h == 2) + return 0; + goto invalid; case 'i': case 'd': case 'u': @@ -2376,12 +2482,14 @@ check_format_type(const char *ptr, int type) case 'x': case 'X': #ifdef STRICT_FORMAT - return h != 0 ? -1 : 0; + if (h == 0) + return 0; + /*FALLTHROUGH*/ #else return 0; #endif default: - return -1; + goto invalid; } case FILE_FMT_FLOAT: @@ -2390,11 +2498,10 @@ check_format_type(const char *ptr, int type) ptr++; if (*ptr == '.') ptr++; - while (isdigit((unsigned char)*ptr)) ptr++; + CHECKLEN(); if (*ptr == '.') ptr++; - while (isdigit((unsigned char)*ptr)) ptr++; - + CHECKLEN(); switch (*ptr++) { case 'e': case 'E': @@ -2405,7 +2512,7 @@ check_format_type(const char *ptr, int type) return 0; default: - return -1; + goto invalid; } @@ -2424,14 +2531,17 @@ check_format_type(const char *ptr, int type) case 's': return 0; default: - return -1; + goto invalid; } default: /* internal error */ abort(); } - /*NOTREACHED*/ +invalid: + *estr = "not valid"; +toolong: + *estr = "too long"; return -1; } @@ -2443,6 +2553,7 @@ private int check_format(struct magic_set *ms, struct magic *m) { char *ptr; + const char *estr; for (ptr = m->desc; *ptr; ptr++) if (*ptr == '%') @@ -2466,13 +2577,13 @@ check_format(struct magic_set *ms, struct magic *m) } ptr++; - if (check_format_type(ptr, m->type) == -1) { + if (check_format_type(ptr, m->type, &estr) == -1) { /* * TODO: this error message is unhelpful if the format * string is not one character long */ - file_magwarn(ms, "Printf format `%c' is not valid for type " - "`%s' in description `%s'", *ptr ? *ptr : '?', + file_magwarn(ms, "Printf format is %s for type " + "`%s' in description `%s'", estr, file_names[m->type], m->desc); return -1; } @@ -2506,6 +2617,7 @@ getvalue(struct magic_set *ms, struct magic *m, const char **p, int action) case FILE_SEARCH: case FILE_NAME: case FILE_USE: + case FILE_DER: *p = getstr(ms, m, *p, action == FILE_COMPILE); if (*p == NULL) { if (ms->flags & MAGIC_CHECK) @@ -2529,12 +2641,14 @@ getvalue(struct magic_set *ms, struct magic *m, const char **p, int action) case FILE_LEFLOAT: if (m->reln != 'x') { char *ep; + errno = 0; #ifdef HAVE_STRTOF m->value.f = strtof(*p, &ep); #else m->value.f = (float)strtod(*p, &ep); #endif - *p = ep; + if (errno == 0) + *p = ep; } return 0; case FILE_DOUBLE: @@ -2542,17 +2656,59 @@ getvalue(struct magic_set *ms, struct magic *m, const char **p, int action) case FILE_LEDOUBLE: if (m->reln != 'x') { char *ep; + errno = 0; m->value.d = strtod(*p, &ep); - *p = ep; + if (errno == 0) + *p = ep; } return 0; default: if (m->reln != 'x') { char *ep; - m->value.q = file_signextend(ms, m, - (uint64_t)strtoull(*p, &ep, 0)); - *p = ep; - eatsize(p); + uint64_t ull; + errno = 0; + ull = (uint64_t)strtoull(*p, &ep, 0); + m->value.q = file_signextend(ms, m, ull); + if (*p == ep) { + file_magwarn(ms, "Unparseable number `%s'", *p); + } else { + size_t ts = typesize(m->type); + uint64_t x; + const char *q; + + if (ts == (size_t)~0) { + file_magwarn(ms, "Expected numeric type got `%s'", + type_tbl[m->type].name); + } + for (q = *p; isspace((unsigned char)*q); q++) + continue; + if (*q == '-') + ull = -(int64_t)ull; + switch (ts) { + case 1: + x = ull & ~0xffULL; + break; + case 2: + x = ull & ~0xffffULL; + break; + case 4: + x = ull & ~0xffffffffULL; + break; + case 8: + x = 0; + break; + default: + abort(); + } + if (x) { + file_magwarn(ms, "Overflow for numeric type `%s' value %#" PRIx64, + type_tbl[m->type].name, ull); + } + } + if (errno == 0) { + *p = ep; + eatsize(p); + } } return 0; } @@ -2588,6 +2744,7 @@ getstr(struct magic_set *ms, struct magic *m, const char *s, int warn) case '\0': if (warn) file_magwarn(ms, "incomplete escape"); + s--; goto out; case '\t': @@ -2711,6 +2868,7 @@ getstr(struct magic_set *ms, struct magic *m, const char *s, int warn) } else *p++ = (char)c; } + --s; out: *p = '\0'; m->vallen = CAST(unsigned char, (p - origp)); @@ -2855,12 +3013,14 @@ apprentice_map(struct magic_set *ms, const char *fn) struct stat st; char *dbname = NULL; struct magic_map *map; + struct magic_map *rv = NULL; fd = -1; if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL) { file_oomem(ms, sizeof(*map)); goto error; } + map->type = MAP_TYPE_USER; /* unspecified */ dbname = mkdbname(ms, fn, 0); if (dbname == NULL) @@ -2881,13 +3041,14 @@ apprentice_map(struct magic_set *ms, const char *fn) map->len = (size_t)st.st_size; #ifdef QUICK + map->type = MAP_TYPE_MMAP; if ((map->p = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) { file_error(ms, errno, "cannot map `%s'", dbname); goto error; } - map->type = MAP_TYPE_MMAP; #else + map->type = MAP_TYPE_MALLOC; if ((map->p = CAST(void *, malloc(map->len))) == NULL) { file_oomem(ms, map->len); goto error; @@ -2896,14 +3057,21 @@ apprentice_map(struct magic_set *ms, const char *fn) file_badread(ms); goto error; } - map->type = MAP_TYPE_MALLOC; #define RET 1 #endif (void)close(fd); fd = -1; - if (check_buffer(ms, map, dbname) != 0) + if (check_buffer(ms, map, dbname) != 0) { + rv = (struct magic_map *)-1; + goto error; + } +#ifdef QUICK + if (mprotect(map->p, (size_t)st.st_size, PROT_READ) == -1) { + file_error(ms, errno, "cannot mprotect `%s'", dbname); goto error; + } +#endif free(dbname); return map; @@ -2913,7 +3081,7 @@ error: (void)close(fd); apprentice_unmap(map); free(dbname); - return NULL; + return rv; } private int @@ -3022,6 +3190,7 @@ apprentice_compile(struct magic_set *ms, struct magic_map *map, const char *fn) (void)close(fd); rv = 0; out: + apprentice_unmap(map); free(dbname); return rv; } @@ -3068,7 +3237,7 @@ mkdbname(struct magic_set *ms, const char *fn, int strip) return NULL; /* Compatibility with old code that looked in .mime */ - if (strstr(p, ".mime") != NULL) + if (strstr(fn, ".mime") != NULL) ms->flags &= MAGIC_MIME_TYPE; return buf; } @@ -3183,25 +3352,39 @@ file_pstring_length_size(const struct magic *m) } } protected size_t -file_pstring_get_length(const struct magic *m, const char *s) +file_pstring_get_length(const struct magic *m, const char *ss) { size_t len = 0; + const unsigned char *s = (const unsigned char *)ss; + unsigned int s3, s2, s1, s0; switch (m->str_flags & PSTRING_LEN) { case PSTRING_1_LE: len = *s; break; case PSTRING_2_LE: - len = (s[1] << 8) | s[0]; + s0 = s[0]; + s1 = s[1]; + len = (s1 << 8) | s0; break; case PSTRING_2_BE: - len = (s[0] << 8) | s[1]; + s0 = s[0]; + s1 = s[1]; + len = (s0 << 8) | s1; break; case PSTRING_4_LE: - len = (s[3] << 24) | (s[2] << 16) | (s[1] << 8) | s[0]; + s0 = s[0]; + s1 = s[1]; + s2 = s[2]; + s3 = s[3]; + len = (s3 << 24) | (s2 << 16) | (s1 << 8) | s0; break; case PSTRING_4_BE: - len = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; + s0 = s[0]; + s1 = s[1]; + s2 = s[2]; + s3 = s[3]; + len = (s0 << 24) | (s1 << 16) | (s2 << 8) | s3; break; default: abort(); /* Impossible */ diff --git a/src/ascmagic.c b/src/ascmagic.c index 78a6dbb..85a973e 100644 --- a/src/ascmagic.c +++ b/src/ascmagic.c @@ -35,7 +35,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: ascmagic.c,v 1.91 2014/11/28 02:46:39 christos Exp $") +FILE_RCSID("@(#)$File: ascmagic.c,v 1.97 2016/06/27 20:56:25 christos Exp $") #endif /* lint */ #include "magic.h" @@ -79,9 +79,6 @@ file_ascmagic(struct magic_set *ms, const unsigned char *buf, size_t nbytes, const char *code_mime = NULL; const char *type = NULL; - if (ms->flags & MAGIC_APPLE) - return 0; - nbytes = trim_nuls(buf, nbytes); /* If file doesn't look like any sort of text, give up. */ @@ -123,9 +120,6 @@ file_ascmagic_with_encoding(struct magic_set *ms, const unsigned char *buf, size_t last_line_end = (size_t)-1; int has_long_lines = 0; - if (ms->flags & MAGIC_APPLE) - return 0; - nbytes = trim_nuls(buf, nbytes); /* If we have fewer than 2 bytes, give up. */ @@ -147,10 +141,16 @@ file_ascmagic_with_encoding(struct magic_set *ms, const unsigned char *buf, == NULL) goto done; if ((rv = file_softmagic(ms, utf8_buf, - (size_t)(utf8_end - utf8_buf), 0, NULL, + (size_t)(utf8_end - utf8_buf), NULL, NULL, TEXTTEST, text)) == 0) rv = -1; + if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION))) { + rv = rv == -1 ? 0 : 1; + goto done; + } } + if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION))) + return 0; /* Now try to discover other details about the file. */ for (i = 0; i < ulen; i++) { @@ -183,10 +183,10 @@ file_ascmagic_with_encoding(struct magic_set *ms, const unsigned char *buf, } /* Beware, if the data has been truncated, the final CR could have - been followed by a LF. If we have HOWMANY bytes, it indicates + been followed by a LF. If we have ms->bytes_max bytes, it indicates that the data might have been truncated, probably even before this function was called. */ - if (seen_cr && nbytes < HOWMANY) + if (seen_cr && nbytes < ms->bytes_max) n_cr++; if (strcmp(type, "binary") == 0) { @@ -35,7 +35,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: cdf.c,v 1.69 2014/12/04 15:56:46 christos Exp $") +FILE_RCSID("@(#)$File: cdf.c,v 1.106 2017/04/30 17:05:02 christos Exp $") #endif #include <assert.h> @@ -73,10 +73,41 @@ static union { #define CDF_TOLE8(x) ((uint64_t)(NEED_SWAP ? _cdf_tole8(x) : (uint64_t)(x))) #define CDF_TOLE4(x) ((uint32_t)(NEED_SWAP ? _cdf_tole4(x) : (uint32_t)(x))) #define CDF_TOLE2(x) ((uint16_t)(NEED_SWAP ? _cdf_tole2(x) : (uint16_t)(x))) -#define CDF_TOLE(x) (sizeof(x) == 2 ? CDF_TOLE2(x) : (sizeof(x) == 4 ? \ - CDF_TOLE4(x) : CDF_TOLE8(x))) +#define CDF_TOLE(x) (/*CONSTCOND*/sizeof(x) == 2 ? \ + CDF_TOLE2(CAST(uint16_t, x)) : \ + (/*CONSTCOND*/sizeof(x) == 4 ? \ + CDF_TOLE4(CAST(uint32_t, x)) : \ + CDF_TOLE8(CAST(uint64_t, x)))) #define CDF_GETUINT32(x, y) cdf_getuint32(x, y) +#define CDF_MALLOC(n) cdf_malloc(__FILE__, __LINE__, (n)) +#define CDF_REALLOC(p, n) cdf_realloc(__FILE__, __LINE__, (p), (n)) +#define CDF_CALLOC(n, u) cdf_calloc(__FILE__, __LINE__, (n), (u)) + + +static void * +cdf_malloc(const char *file __attribute__((__unused__)), + size_t line __attribute__((__unused__)), size_t n) +{ + DPRINTF(("%s,%zu: %s %zu\n", file, line, __func__, n)); + return malloc(n); +} + +static void * +cdf_realloc(const char *file __attribute__((__unused__)), + size_t line __attribute__((__unused__)), void *p, size_t n) +{ + DPRINTF(("%s,%zu: %s %zu\n", file, line, __func__, n)); + return realloc(p, n); +} + +static void * +cdf_calloc(const char *file __attribute__((__unused__)), + size_t line __attribute__((__unused__)), size_t n, size_t u) +{ + DPRINTF(("%s,%zu: %s %zu %zu\n", file, line, __func__, n, u)); + return calloc(n, u); +} /* * swap a short @@ -263,15 +294,34 @@ cdf_unpack_dir(cdf_directory_t *d, char *buf) CDF_UNPACK(d->d_unused0); } +int +cdf_zero_stream(cdf_stream_t *scn) +{ + scn->sst_len = 0; + scn->sst_dirlen = 0; + scn->sst_ss = 0; + free(scn->sst_tab); + scn->sst_tab = NULL; + return -1; +} + +static size_t +cdf_check_stream(const cdf_stream_t *sst, const cdf_header_t *h) +{ + size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? + CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); + assert(ss == sst->sst_ss); + return sst->sst_ss; +} + static int cdf_check_stream_offset(const cdf_stream_t *sst, const cdf_header_t *h, const void *p, size_t tail, int line) { const char *b = (const char *)sst->sst_tab; const char *e = ((const char *)p) + tail; - size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? - CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); - (void)&line; + size_t ss = cdf_check_stream(sst, h); + /*LINTED*/(void)&line; if (e >= b && (size_t)(e - b) <= ss * sst->sst_len) return 0; DPRINTF(("%d: offset begin %p < end %p || %" SIZE_T_FORMAT "u" @@ -287,10 +337,8 @@ cdf_read(const cdf_info_t *info, off_t off, void *buf, size_t len) { size_t siz = (size_t)off + len; - if ((off_t)(off + len) != (off_t)siz) { - errno = EINVAL; - return -1; - } + if ((off_t)(off + len) != (off_t)siz) + goto out; if (info->i_buf != NULL && info->i_len >= siz) { (void)memcpy(buf, &info->i_buf[off], len); @@ -298,12 +346,15 @@ cdf_read(const cdf_info_t *info, off_t off, void *buf, size_t len) } if (info->i_fd == -1) - return -1; + goto out; if (pread(info->i_fd, buf, len, off) != (ssize_t)len) return -1; return (ssize_t)len; +out: + errno = EINVAL; + return -1; } int @@ -317,18 +368,18 @@ cdf_read_header(const cdf_info_t *info, cdf_header_t *h) cdf_unpack_header(h, buf); cdf_swap_header(h); if (h->h_magic != CDF_MAGIC) { - DPRINTF(("Bad magic 0x%" INT64_T_FORMAT "x != 0x%" + DPRINTF(("Bad magic %#" INT64_T_FORMAT "x != %#" INT64_T_FORMAT "x\n", (unsigned long long)h->h_magic, (unsigned long long)CDF_MAGIC)); goto out; } if (h->h_sec_size_p2 > 20) { - DPRINTF(("Bad sector size 0x%u\n", h->h_sec_size_p2)); + DPRINTF(("Bad sector size %hu\n", h->h_sec_size_p2)); goto out; } if (h->h_short_sec_size_p2 > 20) { - DPRINTF(("Bad short sector size 0x%u\n", + DPRINTF(("Bad short sector size %hu\n", h->h_short_sec_size_p2)); goto out; } @@ -360,11 +411,14 @@ cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs, DPRINTF(("Out of bounds read %" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", pos + len, CDF_SEC_SIZE(h) * sst->sst_len)); - return -1; + goto out; } (void)memcpy(((char *)buf) + offs, ((const char *)sst->sst_tab) + pos, len); return len; +out: + errno = EFTYPE; + return -1; } /* @@ -382,7 +436,7 @@ cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat) if (h->h_master_sat[i] == CDF_SECID_FREE) break; -#define CDF_SEC_LIMIT (UINT32_MAX / (4 * ss)) +#define CDF_SEC_LIMIT (UINT32_MAX / (8 * ss)) if ((nsatpersec > 0 && h->h_num_sectors_in_master_sat > CDF_SEC_LIMIT / nsatpersec) || i > CDF_SEC_LIMIT) { @@ -395,7 +449,7 @@ cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat) sat->sat_len = h->h_num_sectors_in_master_sat * nsatpersec + i; DPRINTF(("sat_len = %" SIZE_T_FORMAT "u ss = %" SIZE_T_FORMAT "u\n", sat->sat_len, ss)); - if ((sat->sat_tab = CAST(cdf_secid_t *, calloc(sat->sat_len, ss))) + if ((sat->sat_tab = CAST(cdf_secid_t *, CDF_CALLOC(sat->sat_len, ss))) == NULL) return -1; @@ -409,7 +463,7 @@ cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat) } } - if ((msa = CAST(cdf_secid_t *, calloc(1, ss))) == NULL) + if ((msa = CAST(cdf_secid_t *, CDF_CALLOC(1, ss))) == NULL) goto out1; mid = h->h_secid_first_sector_in_master_sat; @@ -418,8 +472,7 @@ cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat) goto out; if (j >= CDF_LOOP_LIMIT) { DPRINTF(("Reading master sector loop limit")); - errno = EFTYPE; - goto out2; + goto out3; } if (cdf_read_sector(info, msa, 0, ss, h, mid) != (ssize_t)ss) { DPRINTF(("Reading master sector %d", mid)); @@ -432,8 +485,7 @@ cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat) if (i >= sat->sat_len) { DPRINTF(("Out of bounds reading MSA %" SIZE_T_FORMAT "u >= %" SIZE_T_FORMAT "u", i, sat->sat_len)); - errno = EFTYPE; - goto out2; + goto out3; } if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h, sec) != (ssize_t)ss) { @@ -448,6 +500,8 @@ out: sat->sat_len = i; free(msa); return 0; +out3: + errno = EFTYPE; out2: free(msa); out1: @@ -473,23 +527,24 @@ cdf_count_chain(const cdf_sat_t *sat, cdf_secid_t sid, size_t size) DPRINTF((" %d", sid)); if (j >= CDF_LOOP_LIMIT) { DPRINTF(("Counting chain loop limit")); - errno = EFTYPE; - return (size_t)-1; + goto out; } if (sid >= maxsector) { DPRINTF(("Sector %d >= %d\n", sid, maxsector)); - errno = EFTYPE; - return (size_t)-1; + goto out; } sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]); } if (i == 0) { DPRINTF((" none, sid: %d\n", sid)); - return (size_t)-1; + goto out; } DPRINTF(("\n")); return i; +out: + errno = EFTYPE; + return (size_t)-1; } int @@ -498,27 +553,30 @@ cdf_read_long_sector_chain(const cdf_info_t *info, const cdf_header_t *h, { size_t ss = CDF_SEC_SIZE(h), i, j; ssize_t nr; + scn->sst_tab = NULL; scn->sst_len = cdf_count_chain(sat, sid, ss); - scn->sst_dirlen = len; + scn->sst_dirlen = MAX(h->h_min_size_standard_stream, len); + scn->sst_ss = ss; + + if (sid == CDF_SECID_END_OF_CHAIN || len == 0) + return cdf_zero_stream(scn); if (scn->sst_len == (size_t)-1) - return -1; + goto out; - scn->sst_tab = calloc(scn->sst_len, ss); + scn->sst_tab = CDF_CALLOC(scn->sst_len, ss); if (scn->sst_tab == NULL) - return -1; + return cdf_zero_stream(scn); for (j = i = 0; sid >= 0; i++, j++) { if (j >= CDF_LOOP_LIMIT) { DPRINTF(("Read long sector chain loop limit")); - errno = EFTYPE; goto out; } if (i >= scn->sst_len) { DPRINTF(("Out of bounds reading long sector chain " "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i, scn->sst_len)); - errno = EFTYPE; goto out; } if ((nr = cdf_read_sector(info, scn->sst_tab, i * ss, ss, h, @@ -534,8 +592,8 @@ cdf_read_long_sector_chain(const cdf_info_t *info, const cdf_header_t *h, } return 0; out: - free(scn->sst_tab); - return -1; + errno = EFTYPE; + return cdf_zero_stream(scn); } int @@ -544,27 +602,27 @@ cdf_read_short_sector_chain(const cdf_header_t *h, cdf_secid_t sid, size_t len, cdf_stream_t *scn) { size_t ss = CDF_SHORT_SEC_SIZE(h), i, j; + scn->sst_tab = NULL; scn->sst_len = cdf_count_chain(ssat, sid, CDF_SEC_SIZE(h)); scn->sst_dirlen = len; + scn->sst_ss = ss; - if (sst->sst_tab == NULL || scn->sst_len == (size_t)-1) - return -1; + if (scn->sst_len == (size_t)-1) + goto out; - scn->sst_tab = calloc(scn->sst_len, ss); + scn->sst_tab = CDF_CALLOC(scn->sst_len, ss); if (scn->sst_tab == NULL) - return -1; + return cdf_zero_stream(scn); for (j = i = 0; sid >= 0; i++, j++) { if (j >= CDF_LOOP_LIMIT) { DPRINTF(("Read short sector chain loop limit")); - errno = EFTYPE; goto out; } if (i >= scn->sst_len) { DPRINTF(("Out of bounds reading short sector chain " "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i, scn->sst_len)); - errno = EFTYPE; goto out; } if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h, @@ -576,8 +634,8 @@ cdf_read_short_sector_chain(const cdf_header_t *h, } return 0; out: - free(scn->sst_tab); - return -1; + errno = EFTYPE; + return cdf_zero_stream(scn); } int @@ -610,11 +668,11 @@ cdf_read_dir(const cdf_info_t *info, const cdf_header_t *h, dir->dir_len = ns * nd; dir->dir_tab = CAST(cdf_directory_t *, - calloc(dir->dir_len, sizeof(dir->dir_tab[0]))); + CDF_CALLOC(dir->dir_len, sizeof(dir->dir_tab[0]))); if (dir->dir_tab == NULL) return -1; - if ((buf = CAST(char *, malloc(ss))) == NULL) { + if ((buf = CAST(char *, CDF_MALLOC(ss))) == NULL) { free(dir->dir_tab); return -1; } @@ -622,7 +680,6 @@ cdf_read_dir(const cdf_info_t *info, const cdf_header_t *h, for (j = i = 0; i < ns; i++, j++) { if (j >= CDF_LOOP_LIMIT) { DPRINTF(("Read dir loop limit")); - errno = EFTYPE; goto out; } if (cdf_read_sector(info, buf, 0, ss, h, sid) != (ssize_t)ss) { @@ -643,6 +700,7 @@ cdf_read_dir(const cdf_info_t *info, const cdf_header_t *h, out: free(dir->dir_tab); free(buf); + errno = EFTYPE; return -1; } @@ -655,36 +713,37 @@ cdf_read_ssat(const cdf_info_t *info, const cdf_header_t *h, size_t ss = CDF_SEC_SIZE(h); cdf_secid_t sid = h->h_secid_first_sector_in_short_sat; - ssat->sat_len = cdf_count_chain(sat, sid, CDF_SEC_SIZE(h)); + ssat->sat_tab = NULL; + ssat->sat_len = cdf_count_chain(sat, sid, ss); if (ssat->sat_len == (size_t)-1) - return -1; + goto out; - ssat->sat_tab = CAST(cdf_secid_t *, calloc(ssat->sat_len, ss)); + ssat->sat_tab = CAST(cdf_secid_t *, CDF_CALLOC(ssat->sat_len, ss)); if (ssat->sat_tab == NULL) - return -1; + goto out1; for (j = i = 0; sid >= 0; i++, j++) { if (j >= CDF_LOOP_LIMIT) { DPRINTF(("Read short sat sector loop limit")); - errno = EFTYPE; goto out; } if (i >= ssat->sat_len) { DPRINTF(("Out of bounds reading short sector chain " "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i, ssat->sat_len)); - errno = EFTYPE; goto out; } if (cdf_read_sector(info, ssat->sat_tab, i * ss, ss, h, sid) != (ssize_t)ss) { DPRINTF(("Reading short sat sector %d", sid)); - goto out; + goto out1; } sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]); } return 0; out: + errno = EFTYPE; +out1: free(ssat->sat_tab); return -1; } @@ -703,21 +762,24 @@ cdf_read_short_stream(const cdf_info_t *info, const cdf_header_t *h, break; /* If the it is not there, just fake it; some docs don't have it */ - if (i == dir->dir_len) + if (i == dir->dir_len) { + DPRINTF(("Cannot find root storage dir\n")); goto out; + } d = &dir->dir_tab[i]; *root = d; /* If the it is not there, just fake it; some docs don't have it */ - if (d->d_stream_first_sector < 0) + if (d->d_stream_first_sector < 0) { + DPRINTF(("No first secror in dir\n")); goto out; + } - return cdf_read_long_sector_chain(info, h, sat, + return cdf_read_long_sector_chain(info, h, sat, d->d_stream_first_sector, d->d_size, scn); out: scn->sst_tab = NULL; - scn->sst_len = 0; - scn->sst_dirlen = 0; + (void)cdf_zero_stream(scn); return 0; } @@ -731,6 +793,15 @@ cdf_namecmp(const char *d, const uint16_t *s, size_t l) } int +cdf_read_doc_summary_info(const cdf_info_t *info, const cdf_header_t *h, + const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, + const cdf_dir_t *dir, cdf_stream_t *scn) +{ + return cdf_read_user_stream(info, h, sat, ssat, sst, dir, + "\05DocumentSummaryInformation", scn); +} + +int cdf_read_summary_info(const cdf_info_t *info, const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, const cdf_dir_t *dir, cdf_stream_t *scn) @@ -744,24 +815,129 @@ cdf_read_user_stream(const cdf_info_t *info, const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst, const cdf_dir_t *dir, const char *name, cdf_stream_t *scn) { - size_t i; const cdf_directory_t *d; - size_t name_len = strlen(name) + 1; + int i = cdf_find_stream(dir, name, CDF_DIR_TYPE_USER_STREAM); + + if (i <= 0) { + memset(scn, 0, sizeof(*scn)); + return -1; + } + + d = &dir->dir_tab[i - 1]; + return cdf_read_sector_chain(info, h, sat, ssat, sst, + d->d_stream_first_sector, d->d_size, scn); +} + +int +cdf_find_stream(const cdf_dir_t *dir, const char *name, int type) +{ + size_t i, name_len = strlen(name) + 1; for (i = dir->dir_len; i > 0; i--) - if (dir->dir_tab[i - 1].d_type == CDF_DIR_TYPE_USER_STREAM && + if (dir->dir_tab[i - 1].d_type == type && cdf_namecmp(name, dir->dir_tab[i - 1].d_name, name_len) == 0) break; + if (i > 0) + return CAST(int, i); - if (i == 0) { - DPRINTF(("Cannot find user stream `%s'\n", name)); - errno = ESRCH; - return -1; + DPRINTF(("Cannot find type %d `%s'\n", type, name)); + errno = ESRCH; + return 0; +} + +#define CDF_SHLEN_LIMIT (UINT32_MAX / 8) +#define CDF_PROP_LIMIT (UINT32_MAX / (8 * sizeof(cdf_property_info_t))) + +static const void * +cdf_offset(const void *p, size_t l) +{ + return CAST(const void *, CAST(const uint8_t *, p) + l); +} + +static const uint8_t * +cdf_get_property_info_pos(const cdf_stream_t *sst, const cdf_header_t *h, + const uint8_t *p, const uint8_t *e, size_t i) +{ + size_t tail = (i << 1) + 1; + size_t ofs; + const uint8_t *q; + + if (p >= e) { + DPRINTF(("Past end %p < %p\n", e, p)); + return NULL; } - d = &dir->dir_tab[i - 1]; - return cdf_read_sector_chain(info, h, sat, ssat, sst, - d->d_stream_first_sector, d->d_size, scn); + if (cdf_check_stream_offset(sst, h, p, (tail + 1) * sizeof(uint32_t), + __LINE__) == -1) + return NULL; + ofs = CDF_GETUINT32(p, tail); + q = CAST(const uint8_t *, cdf_offset(CAST(const void *, p), + ofs - 2 * sizeof(uint32_t))); + + if (q < p) { + DPRINTF(("Wrapped around %p < %p\n", q, p)); + return NULL; + } + + if (q >= e) { + DPRINTF(("Ran off the end %p >= %p\n", q, e)); + return NULL; + } + return q; +} + +static cdf_property_info_t * +cdf_grow_info(cdf_property_info_t **info, size_t *maxcount, size_t incr) +{ + cdf_property_info_t *inp; + size_t newcount = *maxcount + incr; + + if (newcount > CDF_PROP_LIMIT) { + DPRINTF(("exceeded property limit %zu > %zu\n", + newcount, CDF_PROP_LIMIT)); + goto out; + } + inp = CAST(cdf_property_info_t *, + CDF_REALLOC(*info, newcount * sizeof(*inp))); + if (inp == NULL) + goto out; + + *info = inp; + *maxcount = newcount; + return inp; +out: + free(*info); + *maxcount = 0; + *info = NULL; + return NULL; +} + +static int +cdf_copy_info(cdf_property_info_t *inp, const void *p, const void *e, + size_t len) +{ + if (inp->pi_type & CDF_VECTOR) + return 0; + + if ((size_t)(CAST(const char *, e) - CAST(const char *, p)) < len) + return 0; + + (void)memcpy(&inp->pi_val, p, len); + + switch (len) { + case 2: + inp->pi_u16 = CDF_TOLE2(inp->pi_u16); + break; + case 4: + inp->pi_u32 = CDF_TOLE4(inp->pi_u32); + break; + case 8: + inp->pi_u64 = CDF_TOLE8(inp->pi_u64); + break; + default: + abort(); + } + return 1; } int @@ -771,92 +947,69 @@ cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h, const cdf_section_header_t *shp; cdf_section_header_t sh; const uint8_t *p, *q, *e; - int16_t s16; - int32_t s32; - uint32_t u32; - int64_t s64; - uint64_t u64; - cdf_timestamp_t tp; - size_t i, o, o4, nelements, j; + size_t i, o4, nelements, j, slen, left; cdf_property_info_t *inp; if (offs > UINT32_MAX / 4) { errno = EFTYPE; goto out; } - shp = CAST(const cdf_section_header_t *, (const void *) - ((const char *)sst->sst_tab + offs)); + shp = CAST(const cdf_section_header_t *, + cdf_offset(sst->sst_tab, offs)); if (cdf_check_stream_offset(sst, h, shp, sizeof(*shp), __LINE__) == -1) goto out; sh.sh_len = CDF_TOLE4(shp->sh_len); -#define CDF_SHLEN_LIMIT (UINT32_MAX / 8) if (sh.sh_len > CDF_SHLEN_LIMIT) { errno = EFTYPE; goto out; } - sh.sh_properties = CDF_TOLE4(shp->sh_properties); -#define CDF_PROP_LIMIT (UINT32_MAX / (4 * sizeof(*inp))) - if (sh.sh_properties > CDF_PROP_LIMIT) + + if (cdf_check_stream_offset(sst, h, shp, sh.sh_len, __LINE__) == -1) goto out; + + sh.sh_properties = CDF_TOLE4(shp->sh_properties); DPRINTF(("section len: %u properties %u\n", sh.sh_len, sh.sh_properties)); - if (*maxcount) { - if (*maxcount > CDF_PROP_LIMIT) - goto out; - *maxcount += sh.sh_properties; - inp = CAST(cdf_property_info_t *, - realloc(*info, *maxcount * sizeof(*inp))); - } else { - *maxcount = sh.sh_properties; - inp = CAST(cdf_property_info_t *, - malloc(*maxcount * sizeof(*inp))); - } + if (sh.sh_properties > CDF_PROP_LIMIT) + goto out; + inp = cdf_grow_info(info, maxcount, sh.sh_properties); if (inp == NULL) goto out; - *info = inp; inp += *count; *count += sh.sh_properties; - p = CAST(const uint8_t *, (const void *) - ((const char *)(const void *)sst->sst_tab + - offs + sizeof(sh))); - e = CAST(const uint8_t *, (const void *) - (((const char *)(const void *)shp) + sh.sh_len)); - if (cdf_check_stream_offset(sst, h, e, 0, __LINE__) == -1) + p = CAST(const uint8_t *, cdf_offset(sst->sst_tab, offs + sizeof(sh))); + e = CAST(const uint8_t *, cdf_offset(shp, sh.sh_len)); + if (p >= e || cdf_check_stream_offset(sst, h, e, 0, __LINE__) == -1) goto out; + for (i = 0; i < sh.sh_properties; i++) { - size_t tail = (i << 1) + 1; - size_t ofs; - if (cdf_check_stream_offset(sst, h, p, tail * sizeof(uint32_t), - __LINE__) == -1) + if ((q = cdf_get_property_info_pos(sst, h, p, e, i)) == NULL) goto out; - ofs = CDF_GETUINT32(p, tail); - q = (const uint8_t *)(const void *) - ((const char *)(const void *)p + ofs - - 2 * sizeof(uint32_t)); - if (q < p) { - DPRINTF(("Wrapped around %p < %p\n", q, p)); - goto out; - } - if (q > e) { - DPRINTF(("Ran of the end %p > %p\n", q, e)); + inp[i].pi_id = CDF_GETUINT32(p, i << 1); + left = CAST(size_t, e - q); + if (left < sizeof(uint32_t)) { + DPRINTF(("short info (no type)_\n")); goto out; } - inp[i].pi_id = CDF_GETUINT32(p, i << 1); inp[i].pi_type = CDF_GETUINT32(q, 0); - DPRINTF(("%" SIZE_T_FORMAT "u) id=%x type=%x offs=0x%tx,0x%x\n", + DPRINTF(("%" SIZE_T_FORMAT "u) id=%#x type=%#x offs=%#tx,%#x\n", i, inp[i].pi_id, inp[i].pi_type, q - p, offs)); if (inp[i].pi_type & CDF_VECTOR) { + if (left < sizeof(uint32_t) * 2) { + DPRINTF(("missing CDF_VECTOR length\n")); + goto out; + } nelements = CDF_GETUINT32(q, 1); if (nelements == 0) { DPRINTF(("CDF_VECTOR with nelements == 0\n")); goto out; } - o = 2; + slen = 2; } else { nelements = 1; - o = 1; + slen = 1; } - o4 = o * sizeof(uint32_t); + o4 = slen * sizeof(uint32_t); if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED)) goto unknown; switch (inp[i].pi_type & CDF_TYPEMASK) { @@ -864,100 +1017,72 @@ cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h, case CDF_EMPTY: break; case CDF_SIGNED16: - if (inp[i].pi_type & CDF_VECTOR) + if (!cdf_copy_info(&inp[i], &q[o4], e, sizeof(int16_t))) goto unknown; - (void)memcpy(&s16, &q[o4], sizeof(s16)); - inp[i].pi_s16 = CDF_TOLE2(s16); break; case CDF_SIGNED32: - if (inp[i].pi_type & CDF_VECTOR) - goto unknown; - (void)memcpy(&s32, &q[o4], sizeof(s32)); - inp[i].pi_s32 = CDF_TOLE4((uint32_t)s32); - break; case CDF_BOOL: case CDF_UNSIGNED32: - if (inp[i].pi_type & CDF_VECTOR) + case CDF_FLOAT: + if (!cdf_copy_info(&inp[i], &q[o4], e, sizeof(int32_t))) goto unknown; - (void)memcpy(&u32, &q[o4], sizeof(u32)); - inp[i].pi_u32 = CDF_TOLE4(u32); break; case CDF_SIGNED64: - if (inp[i].pi_type & CDF_VECTOR) - goto unknown; - (void)memcpy(&s64, &q[o4], sizeof(s64)); - inp[i].pi_s64 = CDF_TOLE8((uint64_t)s64); - break; case CDF_UNSIGNED64: - if (inp[i].pi_type & CDF_VECTOR) - goto unknown; - (void)memcpy(&u64, &q[o4], sizeof(u64)); - inp[i].pi_u64 = CDF_TOLE8((uint64_t)u64); - break; - case CDF_FLOAT: - if (inp[i].pi_type & CDF_VECTOR) - goto unknown; - (void)memcpy(&u32, &q[o4], sizeof(u32)); - u32 = CDF_TOLE4(u32); - memcpy(&inp[i].pi_f, &u32, sizeof(inp[i].pi_f)); - break; case CDF_DOUBLE: - if (inp[i].pi_type & CDF_VECTOR) + case CDF_FILETIME: + if (!cdf_copy_info(&inp[i], &q[o4], e, sizeof(int64_t))) goto unknown; - (void)memcpy(&u64, &q[o4], sizeof(u64)); - u64 = CDF_TOLE8((uint64_t)u64); - memcpy(&inp[i].pi_d, &u64, sizeof(inp[i].pi_d)); break; case CDF_LENGTH32_STRING: case CDF_LENGTH32_WSTRING: if (nelements > 1) { size_t nelem = inp - *info; - if (*maxcount > CDF_PROP_LIMIT - || nelements > CDF_PROP_LIMIT) - goto out; - *maxcount += nelements; - inp = CAST(cdf_property_info_t *, - realloc(*info, *maxcount * sizeof(*inp))); + inp = cdf_grow_info(info, maxcount, nelements); if (inp == NULL) goto out; - *info = inp; - inp = *info + nelem; + inp += nelem; } DPRINTF(("nelements = %" SIZE_T_FORMAT "u\n", nelements)); for (j = 0; j < nelements && i < sh.sh_properties; j++, i++) { - uint32_t l = CDF_GETUINT32(q, o); + uint32_t l; + + if (o4 + sizeof(uint32_t) > left) + goto out; + + l = CDF_GETUINT32(q, slen); + o4 += sizeof(uint32_t); + if (o4 + l > left) + goto out; + inp[i].pi_str.s_len = l; - inp[i].pi_str.s_buf = (const char *) - (const void *)(&q[o4 + sizeof(l)]); - DPRINTF(("l = %d, r = %" SIZE_T_FORMAT - "u, s = %s\n", l, - CDF_ROUND(l, sizeof(l)), + inp[i].pi_str.s_buf = CAST(const char *, + CAST(const void *, &q[o4])); + + DPRINTF(("o=%zu l=%d(%" SIZE_T_FORMAT + "u), t=%zu s=%s\n", o4, l, + CDF_ROUND(l, sizeof(l)), left, inp[i].pi_str.s_buf)); + if (l & 1) l++; - o += l >> 1; - if (q + o >= e) - goto out; - o4 = o * sizeof(uint32_t); + + slen += l >> 1; + o4 = slen * sizeof(uint32_t); } i--; break; - case CDF_FILETIME: - if (inp[i].pi_type & CDF_VECTOR) - goto unknown; - (void)memcpy(&tp, &q[o4], sizeof(tp)); - inp[i].pi_tp = CDF_TOLE8((uint64_t)tp); - break; case CDF_CLIPBOARD: if (inp[i].pi_type & CDF_VECTOR) goto unknown; break; default: unknown: - DPRINTF(("Don't know how to deal with %x\n", + memset(&inp[i].pi_val, 0, sizeof(inp[i].pi_val)); + DPRINTF(("Don't know how to deal with %#x\n", inp[i].pi_type)); break; } @@ -965,6 +1090,10 @@ cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h, return 0; out: free(*info); + *info = NULL; + *count = 0; + *maxcount = 0; + errno = EFTYPE; return -1; } @@ -998,52 +1127,79 @@ cdf_unpack_summary_info(const cdf_stream_t *sst, const cdf_header_t *h, } -#define extract_catalog_field(f, l) \ - memcpy(&ce[i].f, b + (l), sizeof(ce[i].f)); \ - ce[i].f = CDF_TOLE(ce[i].f) +#define extract_catalog_field(t, f, l) \ + if (b + l + sizeof(cep->f) > eb) { \ + cep->ce_namlen = 0; \ + break; \ + } \ + memcpy(&cep->f, b + (l), sizeof(cep->f)); \ + ce[i].f = CAST(t, CDF_TOLE(cep->f)) int cdf_unpack_catalog(const cdf_header_t *h, const cdf_stream_t *sst, cdf_catalog_t **cat) { - size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? - CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); + size_t ss = cdf_check_stream(sst, h); const char *b = CAST(const char *, sst->sst_tab); - const char *eb = b + ss * sst->sst_len; - size_t nr, i, k; + const char *nb, *eb = b + ss * sst->sst_len; + size_t nr, i, j, k; cdf_catalog_entry_t *ce; uint16_t reclen; const uint16_t *np; - for (nr = 0; b < eb; nr++) { + for (nr = 0;; nr++) { memcpy(&reclen, b, sizeof(reclen)); reclen = CDF_TOLE2(reclen); if (reclen == 0) break; b += reclen; + if (b > eb) + break; } + if (nr == 0) + return -1; + nr--; *cat = CAST(cdf_catalog_t *, - malloc(sizeof(cdf_catalog_t) + nr * sizeof(*ce))); - (*cat)->cat_num = nr; + CDF_MALLOC(sizeof(cdf_catalog_t) + nr * sizeof(*ce))); + if (*cat == NULL) + return -1; ce = (*cat)->cat_e; + memset(ce, 0, nr * sizeof(*ce)); b = CAST(const char *, sst->sst_tab); - for (i = 0; i < nr; i++) { - extract_catalog_field(ce_namlen, 0); - extract_catalog_field(ce_num, 2); - extract_catalog_field(ce_timestamp, 6); - reclen = ce[i].ce_namlen; - ce[i].ce_namlen = - sizeof(ce[i].ce_name) / sizeof(ce[i].ce_name[0]) - 1; - if (ce[i].ce_namlen > reclen - 14) - ce[i].ce_namlen = reclen - 14; - np = CAST(const uint16_t *, (b + 16)); - for (k = 0; k < ce[i].ce_namlen; k++) { - ce[i].ce_name[k] = np[k]; - CDF_TOLE2(ce[i].ce_name[k]); + for (j = i = 0; i < nr; b += reclen) { + cdf_catalog_entry_t *cep = &ce[j]; + uint16_t rlen; + + extract_catalog_field(uint16_t, ce_namlen, 0); + extract_catalog_field(uint16_t, ce_num, 4); + extract_catalog_field(uint64_t, ce_timestamp, 8); + reclen = cep->ce_namlen; + + if (reclen < 14) { + cep->ce_namlen = 0; + continue; } - ce[i].ce_name[ce[i].ce_namlen] = 0; - b += reclen; + + cep->ce_namlen = __arraycount(cep->ce_name) - 1; + rlen = reclen - 14; + if (cep->ce_namlen > rlen) + cep->ce_namlen = rlen; + + np = CAST(const uint16_t *, CAST(const void *, (b + 16))); + nb = CAST(const char *, CAST(const void *, + (np + cep->ce_namlen))); + if (nb > eb) { + cep->ce_namlen = 0; + break; + } + + for (k = 0; k < cep->ce_namlen; k++) + cep->ce_name[k] = np[k]; /* XXX: CDF_TOLE2? */ + cep->ce_name[cep->ce_namlen] = 0; + j = i; + i++; } + (*cat)->cat_num = j; return 0; } @@ -1091,7 +1247,7 @@ cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p) for (i = 0; i < __arraycount(vn); i++) if (vn[i].v == p) return snprintf(buf, bufsiz, "%s", vn[i].n); - return snprintf(buf, bufsiz, "0x%x", p); + return snprintf(buf, bufsiz, "%#x", p); } int @@ -1150,7 +1306,7 @@ cdf_dump_header(const cdf_header_t *h) h->h_ ## b, 1 << h->h_ ## b) DUMP("%d", revision); DUMP("%d", version); - DUMP("0x%x", byte_order); + DUMP("%#x", byte_order); DUMP2("%d", sec_size_p2); DUMP2("%d", short_sec_size_p2); DUMP("%d", num_sectors_in_sat); @@ -1188,11 +1344,12 @@ cdf_dump_sat(const char *prefix, const cdf_sat_t *sat, size_t size) } void -cdf_dump(void *v, size_t len) +cdf_dump(const void *v, size_t len) { size_t i, j; - unsigned char *p = v; + const unsigned char *p = v; char abuf[16]; + (void)fprintf(stderr, "%.4x: ", 0); for (i = 0, j = 0; i < len; i++, p++) { (void)fprintf(stderr, "%.2x ", *p); @@ -1208,10 +1365,9 @@ cdf_dump(void *v, size_t len) } void -cdf_dump_stream(const cdf_header_t *h, const cdf_stream_t *sst) +cdf_dump_stream(const cdf_stream_t *sst) { - size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ? - CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h); + size_t ss = sst->sst_ss; cdf_dump(sst->sst_tab, ss * sst->sst_len); } @@ -1244,7 +1400,7 @@ cdf_dump_dir(const cdf_info_t *info, const cdf_header_t *h, d->d_color ? "black" : "red"); (void)fprintf(stderr, "Left child: %d\n", d->d_left_child); (void)fprintf(stderr, "Right child: %d\n", d->d_right_child); - (void)fprintf(stderr, "Flags: 0x%x\n", d->d_flags); + (void)fprintf(stderr, "Flags: %#x\n", d->d_flags); cdf_timestamp_to_timespec(&ts, d->d_created); (void)fprintf(stderr, "Created %s", cdf_ctime(&ts.tv_sec, buf)); cdf_timestamp_to_timespec(&ts, d->d_modified); @@ -1265,7 +1421,7 @@ cdf_dump_dir(const cdf_info_t *info, const cdf_header_t *h, name, d->d_stream_first_sector, d->d_size); break; } - cdf_dump_stream(h, &scn); + cdf_dump_stream(&scn); free(scn.sst_tab); break; default: @@ -1327,17 +1483,17 @@ cdf_dump_property_info(const cdf_property_info_t *info, size_t count) cdf_print_elapsed_time(buf, sizeof(buf), tp); (void)fprintf(stderr, "timestamp %s\n", buf); } else { - char buf[26]; + char tbuf[26]; cdf_timestamp_to_timespec(&ts, tp); (void)fprintf(stderr, "timestamp %s", - cdf_ctime(&ts.tv_sec, buf)); + cdf_ctime(&ts.tv_sec, tbuf)); } break; case CDF_CLIPBOARD: (void)fprintf(stderr, "CLIPBOARD %u\n", info[i].pi_u32); break; default: - DPRINTF(("Don't know how to deal with %x\n", + DPRINTF(("Don't know how to deal with %#x\n", info[i].pi_type)); break; } @@ -1356,7 +1512,7 @@ cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst) (void)&h; if (cdf_unpack_summary_info(sst, h, &ssi, &info, &count) == -1) return; - (void)fprintf(stderr, "Endian: %x\n", ssi.si_byte_order); + (void)fprintf(stderr, "Endian: %#x\n", ssi.si_byte_order); (void)fprintf(stderr, "Os Version %d.%d\n", ssi.si_os_version & 0xff, ssi.si_os_version >> 8); (void)fprintf(stderr, "Os %d\n", ssi.si_os); @@ -1401,7 +1557,10 @@ main(int argc, char *argv[]) cdf_dir_t dir; cdf_info_t info; const cdf_directory_t *root; - +#ifdef __linux__ +#define getprogname() __progname + extern char *__progname; +#endif if (argc < 2) { (void)fprintf(stderr, "Usage: %s <filename>\n", getprogname()); return -1; @@ -1438,7 +1597,7 @@ main(int argc, char *argv[]) == -1) err(1, "Cannot read short stream"); #ifdef CDF_DEBUG - cdf_dump_stream(&h, &sst); + cdf_dump_stream(&sst); #endif #ifdef CDF_DEBUG @@ -1453,8 +1612,8 @@ main(int argc, char *argv[]) else cdf_dump_summary_info(&h, &scn); #endif - if (cdf_read_catalog(&info, &h, &sat, &ssat, &sst, &dir, - &scn) == -1) + if (cdf_read_user_stream(&info, &h, &sat, &ssat, &sst, + &dir, "Catalog", &scn) == -1) warn("Cannot read catalog"); #ifdef CDF_DEBUG else @@ -127,8 +127,9 @@ typedef struct { typedef struct { void *sst_tab; - size_t sst_len; - size_t sst_dirlen; + size_t sst_len; /* Number of sectors */ + size_t sst_dirlen; /* Directory sector size */ + size_t sst_ss; /* Sector size */ } cdf_stream_t; typedef struct { @@ -277,7 +278,7 @@ typedef struct { typedef struct { size_t cat_num; - cdf_catalog_entry_t cat_e[0]; + cdf_catalog_entry_t cat_e[1]; } cdf_catalog_t; struct timespec; @@ -314,12 +315,11 @@ int cdf_read_property_info(const cdf_stream_t *, const cdf_header_t *, uint32_t, int cdf_read_user_stream(const cdf_info_t *, const cdf_header_t *, const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *, const cdf_dir_t *, const char *, cdf_stream_t *); -#define cdf_read_catalog(info, header, sat, ssat, stream, dir, scn) \ - cdf_read_user_stream(info, header, sat, ssat, stream, dir, "Catalog", \ - scn) -#define cdf_read_encrypted_package(info, header, sat, ssat, stream, dir, scn) \ - cdf_read_user_stream(info, header, sat, ssat, stream, dir, \ - "EncryptedPackage", scn) +int cdf_find_stream(const cdf_dir_t *, const char *, int); +int cdf_zero_stream(cdf_stream_t *); +int cdf_read_doc_summary_info(const cdf_info_t *, const cdf_header_t *, + const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *, + const cdf_dir_t *, cdf_stream_t *); int cdf_read_summary_info(const cdf_info_t *, const cdf_header_t *, const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *, const cdf_dir_t *, cdf_stream_t *); @@ -339,8 +339,8 @@ char *cdf_u16tos8(char *, size_t, const uint16_t *); #ifdef CDF_DEBUG void cdf_dump_header(const cdf_header_t *); void cdf_dump_sat(const char *, const cdf_sat_t *, size_t); -void cdf_dump(void *, size_t); -void cdf_dump_stream(const cdf_header_t *, const cdf_stream_t *); +void cdf_dump(const void *, size_t); +void cdf_dump_stream(const cdf_stream_t *); void cdf_dump_dir(const cdf_info_t *, const cdf_header_t *, const cdf_sat_t *, const cdf_sat_t *, const cdf_stream_t *, const cdf_dir_t *); void cdf_dump_property_info(const cdf_property_info_t *, size_t); diff --git a/src/cdf_time.c b/src/cdf_time.c index 1e572de..2bdcd2a 100644 --- a/src/cdf_time.c +++ b/src/cdf_time.c @@ -27,7 +27,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: cdf_time.c,v 1.15 2014/05/14 23:15:42 christos Exp $") +FILE_RCSID("@(#)$File: cdf_time.c,v 1.16 2017/03/29 15:57:48 christos Exp $") #endif #include <time.h> @@ -171,7 +171,7 @@ cdf_ctime(const time_t *sec, char *buf) char *ptr = ctime_r(sec, buf); if (ptr != NULL) return buf; - (void)snprintf(buf, 26, "*Bad* 0x%16.16" INT64_T_FORMAT "x\n", + (void)snprintf(buf, 26, "*Bad* %#16.16" INT64_T_FORMAT "x\n", (long long)*sec); return buf; } diff --git a/src/compress.c b/src/compress.c index e968bb4..2f789cd 100644 --- a/src/compress.c +++ b/src/compress.c @@ -35,7 +35,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: compress.c,v 1.77 2014/12/12 16:33:01 christos Exp $") +FILE_RCSID("@(#)$File: compress.c,v 1.105 2017/05/25 00:13:03 christos Exp $") #endif #include "magic.h" @@ -45,7 +45,14 @@ FILE_RCSID("@(#)$File: compress.c,v 1.77 2014/12/12 16:33:01 christos Exp $") #endif #include <string.h> #include <errno.h> +#include <ctype.h> +#include <stdarg.h> +#ifdef HAVE_SIGNAL_H #include <signal.h> +# ifndef HAVE_SIG_T +typedef void (*sig_t)(int); +# endif /* HAVE_SIG_T */ +#endif #if !defined(__MINGW32__) && !defined(WIN32) #include <sys/ioctl.h> #endif @@ -55,46 +62,126 @@ FILE_RCSID("@(#)$File: compress.c,v 1.77 2014/12/12 16:33:01 christos Exp $") #if defined(HAVE_SYS_TIME_H) #include <sys/time.h> #endif -#if defined(HAVE_ZLIB_H) && defined(HAVE_LIBZ) +#if defined(HAVE_ZLIB_H) && defined(ZLIBSUPPORT) #define BUILTIN_DECOMPRESS #include <zlib.h> #endif +#ifdef DEBUG +int tty = -1; +#define DPRINTF(...) do { \ + if (tty == -1) \ + tty = open("/dev/tty", O_RDWR); \ + if (tty == -1) \ + abort(); \ + dprintf(tty, __VA_ARGS__); \ +} while (/*CONSTCOND*/0) +#else +#define DPRINTF(...) +#endif + +#ifdef ZLIBSUPPORT +/* + * The following python code is not really used because ZLIBSUPPORT is only + * defined if we have a built-in zlib, and the built-in zlib handles that. + * That is not true for android where we have zlib.h and not -lz. + */ +static const char zlibcode[] = + "import sys, zlib; sys.stdout.write(zlib.decompress(sys.stdin.read()))"; + +static const char *zlib_args[] = { "python", "-c", zlibcode, NULL }; + +static int +zlibcmp(const unsigned char *buf) +{ + unsigned short x = 1; + unsigned char *s = CAST(unsigned char *, CAST(void *, &x)); + + if ((buf[0] & 0xf) != 8 || (buf[0] & 0x80) != 0) + return 0; + if (s[0] != 1) /* endianness test */ + x = buf[0] | (buf[1] << 8); + else + x = buf[1] | (buf[0] << 8); + if (x % 31) + return 0; + return 1; +} +#endif + +#define gzip_flags "-cd" +#define lrzip_flags "-do" +#define lzip_flags gzip_flags + +static const char *gzip_args[] = { + "gzip", gzip_flags, NULL +}; +static const char *uncompress_args[] = { + "uncompress", "-c", NULL +}; +static const char *bzip2_args[] = { + "bzip2", "-cd", NULL +}; +static const char *lzip_args[] = { + "lzip", lzip_flags, NULL +}; +static const char *xz_args[] = { + "xz", "-cd", NULL +}; +static const char *lrzip_args[] = { + "lrzip", lrzip_flags, NULL +}; +static const char *lz4_args[] = { + "lz4", "-cd", NULL +}; +static const char *zstd_args[] = { + "zstd", "-cd", NULL +}; private const struct { - const char magic[8]; + const void *magic; size_t maglen; - const char *argv[3]; - int silent; + const char **argv; } compr[] = { - { "\037\235", 2, { "gzip", "-cdq", NULL }, 1 }, /* compressed */ + { "\037\235", 2, gzip_args }, /* compressed */ /* Uncompress can get stuck; so use gzip first if we have it * Idea from Damien Clark, thanks! */ - { "\037\235", 2, { "uncompress", "-c", NULL }, 1 }, /* compressed */ - { "\037\213", 2, { "gzip", "-cdq", NULL }, 1 }, /* gzipped */ - { "\037\236", 2, { "gzip", "-cdq", NULL }, 1 }, /* frozen */ - { "\037\240", 2, { "gzip", "-cdq", NULL }, 1 }, /* SCO LZH */ + { "\037\235", 2, uncompress_args }, /* compressed */ + { "\037\213", 2, gzip_args }, /* gzipped */ + { "\037\236", 2, gzip_args }, /* frozen */ + { "\037\240", 2, gzip_args }, /* SCO LZH */ /* the standard pack utilities do not accept standard input */ - { "\037\036", 2, { "gzip", "-cdq", NULL }, 0 }, /* packed */ - { "PK\3\4", 4, { "gzip", "-cdq", NULL }, 1 }, /* pkzipped, */ - /* ...only first file examined */ - { "BZh", 3, { "bzip2", "-cd", NULL }, 1 }, /* bzip2-ed */ - { "LZIP", 4, { "lzip", "-cdq", NULL }, 1 }, - { "\3757zXZ\0",6,{ "xz", "-cd", NULL }, 1 }, /* XZ Utils */ - { "LRZI", 4, { "lrzip", "-dqo-", NULL }, 1 }, /* LRZIP */ - { "\004\"M\030", 4, { "lz4", "-cd", NULL }, 1 }, /* LZ4 */ + { "\037\036", 2, gzip_args }, /* packed */ + { "PK\3\4", 4, gzip_args }, /* pkzipped, */ + /* ...only first file examined */ + { "BZh", 3, bzip2_args }, /* bzip2-ed */ + { "LZIP", 4, lzip_args }, /* lzip-ed */ + { "\3757zXZ\0", 6, xz_args }, /* XZ Utils */ + { "LRZI", 4, lrzip_args }, /* LRZIP */ + { "\004\"M\030",4, lz4_args }, /* LZ4 */ + { "\x28\xB5\x2F\xFD", 4, zstd_args }, /* zstd */ +#ifdef ZLIBSUPPORT + { RCAST(const void *, zlibcmp), 0, zlib_args }, /* zlib */ +#endif }; -#define NODATA ((size_t)~0) +#define OKDATA 0 +#define NODATA 1 +#define ERRDATA 2 private ssize_t swrite(int, const void *, size_t); #if HAVE_FORK private size_t ncompr = sizeof(compr) / sizeof(compr[0]); -private size_t uncompressbuf(struct magic_set *, int, size_t, - const unsigned char *, unsigned char **, size_t); +private int uncompressbuf(int, size_t, size_t, const unsigned char *, + unsigned char **, size_t *); #ifdef BUILTIN_DECOMPRESS -private size_t uncompressgzipped(struct magic_set *, const unsigned char *, - unsigned char **, size_t); +private int uncompresszlib(const unsigned char *, unsigned char **, size_t, + size_t *, int); +private int uncompressgzipped(const unsigned char *, unsigned char **, size_t, + size_t *); #endif +static int makeerror(unsigned char **, size_t *, const char *, ...) + __attribute__((__format__(__printf__, 3, 4))); +private const char *methodname(size_t); protected int file_zmagic(struct magic_set *ms, int fd, const char *name, @@ -102,43 +189,95 @@ file_zmagic(struct magic_set *ms, int fd, const char *name, { unsigned char *newbuf = NULL; size_t i, nsz; - int rv = 0; + char *rbuf; + file_pushbuf_t *pb; + int urv, prv, rv = 0; int mime = ms->flags & MAGIC_MIME; +#ifdef HAVE_SIGNAL_H sig_t osigpipe; +#endif if ((ms->flags & MAGIC_COMPRESS) == 0) return 0; +#ifdef HAVE_SIGNAL_H osigpipe = signal(SIGPIPE, SIG_IGN); +#endif for (i = 0; i < ncompr; i++) { + int zm; if (nbytes < compr[i].maglen) continue; - if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 && - (nsz = uncompressbuf(ms, fd, i, buf, &newbuf, - nbytes)) != NODATA) { +#ifdef ZLIBSUPPORT + if (compr[i].maglen == 0) + zm = (RCAST(int (*)(const unsigned char *), + CCAST(void *, compr[i].magic)))(buf); + else +#endif + zm = memcmp(buf, compr[i].magic, compr[i].maglen) == 0; + + if (!zm) + continue; + nsz = nbytes; + urv = uncompressbuf(fd, ms->bytes_max, i, buf, &newbuf, &nsz); + DPRINTF("uncompressbuf = %d, %s, %zu\n", urv, (char *)newbuf, + nsz); + switch (urv) { + case OKDATA: + case ERRDATA: + ms->flags &= ~MAGIC_COMPRESS; - rv = -1; - if (file_buffer(ms, -1, name, newbuf, nsz) == -1) + if (urv == ERRDATA) + prv = file_printf(ms, "%s ERROR: %s", + methodname(i), newbuf); + else + prv = file_buffer(ms, -1, name, newbuf, nsz); + if (prv == -1) goto error; - - if (mime == MAGIC_MIME || mime == 0) { - if (file_printf(ms, mime ? - " compressed-encoding=" : " (") == -1) - goto error; - if (file_buffer(ms, -1, NULL, buf, nbytes) == -1) - goto error; - if (!mime && file_printf(ms, ")") == -1) + rv = 1; + if ((ms->flags & MAGIC_COMPRESS_TRANSP) != 0) + goto out; + if (mime != MAGIC_MIME && mime != 0) + goto out; + if ((file_printf(ms, + mime ? " compressed-encoding=" : " (")) == -1) + goto error; + if ((pb = file_push_buffer(ms)) == NULL) + goto error; + /* + * XXX: If file_buffer fails here, we overwrite + * the compressed text. FIXME. + */ + if (file_buffer(ms, -1, NULL, buf, nbytes) == -1) + goto error; + if ((rbuf = file_pop_buffer(ms, pb)) != NULL) { + if (file_printf(ms, "%s", rbuf) == -1) { + free(rbuf); goto error; + } + free(rbuf); } - - rv = 1; + if (!mime && file_printf(ms, ")") == -1) + goto error; + /*FALLTHROUGH*/ + case NODATA: + break; + default: + abort(); + /*NOTREACHED*/ + error: + rv = -1; break; } } -error: +out: + DPRINTF("rv = %d\n", rv); + +#ifdef HAVE_SIGNAL_H (void)signal(SIGPIPE, osigpipe); +#endif free(newbuf); ms->flags |= MAGIC_COMPRESS; + DPRINTF("Zmagic returns %d\n", rv); return rv; } #endif @@ -228,7 +367,7 @@ nocheck: return rn - n; default: n -= rv; - buf = ((char *)buf) + rv; + buf = CAST(char *, CCAST(void *, buf)) + rv; break; } while (n > 0); @@ -310,223 +449,314 @@ file_pipe2file(struct magic_set *ms, int fd, const void *startbuf, #define FNAME (1 << 3) #define FCOMMENT (1 << 4) -private size_t -uncompressgzipped(struct magic_set *ms, const unsigned char *old, - unsigned char **newch, size_t n) + +private int +uncompressgzipped(const unsigned char *old, unsigned char **newch, + size_t bytes_max, size_t *n) { unsigned char flg = old[3]; size_t data_start = 10; - z_stream z; - int rc; if (flg & FEXTRA) { - if (data_start+1 >= n) - return 0; + if (data_start + 1 >= *n) + goto err; data_start += 2 + old[data_start] + old[data_start + 1] * 256; } if (flg & FNAME) { - while(data_start < n && old[data_start]) + while(data_start < *n && old[data_start]) data_start++; data_start++; } - if(flg & FCOMMENT) { - while(data_start < n && old[data_start]) + if (flg & FCOMMENT) { + while(data_start < *n && old[data_start]) data_start++; data_start++; } - if(flg & FHCRC) + if (flg & FHCRC) data_start += 2; - if (data_start >= n) - return 0; - if ((*newch = CAST(unsigned char *, malloc(HOWMANY + 1))) == NULL) { - return 0; - } - - /* XXX: const castaway, via strchr */ - z.next_in = (Bytef *)strchr((const char *)old + data_start, - old[data_start]); - z.avail_in = CAST(uint32_t, (n - data_start)); + if (data_start >= *n) + goto err; + + *n -= data_start; + old += data_start; + return uncompresszlib(old, newch, bytes_max, n, 0); +err: + return makeerror(newch, n, "File too short"); +} + +private int +uncompresszlib(const unsigned char *old, unsigned char **newch, + size_t bytes_max, size_t *n, int zlib) +{ + int rc; + z_stream z; + + if ((*newch = CAST(unsigned char *, malloc(bytes_max + 1))) == NULL) + return makeerror(newch, n, "No buffer, %s", strerror(errno)); + + z.next_in = CCAST(Bytef *, old); + z.avail_in = CAST(uint32_t, *n); z.next_out = *newch; - z.avail_out = HOWMANY; + z.avail_out = CAST(unsigned int, bytes_max); z.zalloc = Z_NULL; z.zfree = Z_NULL; z.opaque = Z_NULL; /* LINTED bug in header macro */ - rc = inflateInit2(&z, -15); - if (rc != Z_OK) { - file_error(ms, 0, "zlib: %s", z.msg); - return 0; - } + rc = zlib ? inflateInit(&z) : inflateInit2(&z, -15); + if (rc != Z_OK) + goto err; rc = inflate(&z, Z_SYNC_FLUSH); - if (rc != Z_OK && rc != Z_STREAM_END) { - file_error(ms, 0, "zlib: %s", z.msg); - return 0; - } + if (rc != Z_OK && rc != Z_STREAM_END) + goto err; - n = (size_t)z.total_out; - (void)inflateEnd(&z); + *n = (size_t)z.total_out; + rc = inflateEnd(&z); + if (rc != Z_OK) + goto err; /* let's keep the nul-terminate tradition */ - (*newch)[n] = '\0'; + (*newch)[*n] = '\0'; - return n; + return OKDATA; +err: + strlcpy((char *)*newch, z.msg ? z.msg : zError(rc), bytes_max); + *n = strlen((char *)*newch); + return ERRDATA; } #endif -private size_t -uncompressbuf(struct magic_set *ms, int fd, size_t method, - const unsigned char *old, unsigned char **newch, size_t n) +static int +makeerror(unsigned char **buf, size_t *len, const char *fmt, ...) +{ + char *msg; + va_list ap; + int rv; + + va_start(ap, fmt); + rv = vasprintf(&msg, fmt, ap); + va_end(ap); + if (rv < 0) { + *buf = NULL; + *len = 0; + return NODATA; + } + *buf = (unsigned char *)msg; + *len = strlen(msg); + return ERRDATA; +} + +static void +closefd(int *fd, size_t i) +{ + if (fd[i] == -1) + return; + (void) close(fd[i]); + fd[i] = -1; +} + +static void +closep(int *fd) +{ + size_t i; + for (i = 0; i < 2; i++) + closefd(fd, i); +} + +static void +copydesc(int i, int *fd) +{ + int j = fd[i == STDIN_FILENO ? 0 : 1]; + if (j == i) + return; + if (dup2(j, i) == -1) { + DPRINTF("dup(%d, %d) failed (%s)\n", j, i, strerror(errno)); + exit(1); + } + closep(fd); +} + +static void +writechild(int fdp[3][2], const void *old, size_t n) { - int fdin[2], fdout[2]; int status; + + closefd(fdp[STDIN_FILENO], 0); + /* + * fork again, to avoid blocking because both + * pipes filled + */ + switch (fork()) { + case 0: /* child */ + closefd(fdp[STDOUT_FILENO], 0); + if (swrite(fdp[STDIN_FILENO][1], old, n) != (ssize_t)n) { + DPRINTF("Write failed (%s)\n", strerror(errno)); + exit(1); + } + exit(0); + /*NOTREACHED*/ + + case -1: + DPRINTF("Fork failed (%s)\n", strerror(errno)); + exit(1); + /*NOTREACHED*/ + + default: /* parent */ + if (wait(&status) == -1) { + DPRINTF("Wait failed (%s)\n", strerror(errno)); + exit(1); + } + DPRINTF("Grandchild wait return %#x\n", status); + } + closefd(fdp[STDIN_FILENO], 1); +} + +static ssize_t +filter_error(unsigned char *ubuf, ssize_t n) +{ + char *p; + char *buf; + + ubuf[n] = '\0'; + buf = (char *)ubuf; + while (isspace((unsigned char)*buf)) + buf++; + DPRINTF("Filter error[[[%s]]]\n", buf); + if ((p = strchr((char *)buf, '\n')) != NULL) + *p = '\0'; + if ((p = strchr((char *)buf, ';')) != NULL) + *p = '\0'; + if ((p = strrchr((char *)buf, ':')) != NULL) { + ++p; + while (isspace((unsigned char)*p)) + p++; + n = strlen(p); + memmove(ubuf, p, CAST(size_t, n + 1)); + } + DPRINTF("Filter error after[[[%s]]]\n", (char *)ubuf); + if (islower(*ubuf)) + *ubuf = toupper(*ubuf); + return n; +} + +private const char * +methodname(size_t method) +{ +#ifdef BUILTIN_DECOMPRESS + /* FIXME: This doesn't cope with bzip2 */ + if (method == 2 || compr[method].maglen == 0) + return "zlib"; +#endif + return compr[method].argv[0]; +} + +private int +uncompressbuf(int fd, size_t bytes_max, size_t method, const unsigned char *old, + unsigned char **newch, size_t* n) +{ + int fdp[3][2]; + int status, rv; + size_t i; ssize_t r; - pid_t pid; #ifdef BUILTIN_DECOMPRESS /* FIXME: This doesn't cope with bzip2 */ if (method == 2) - return uncompressgzipped(ms, old, newch, n); + return uncompressgzipped(old, newch, bytes_max, n); + if (compr[method].maglen == 0) + return uncompresszlib(old, newch, bytes_max, n, 1); #endif (void)fflush(stdout); (void)fflush(stderr); - if ((fd != -1 && pipe(fdin) == -1) || pipe(fdout) == -1) { - file_error(ms, errno, "cannot create pipe"); - return NODATA; + for (i = 0; i < __arraycount(fdp); i++) + fdp[i][0] = fdp[i][1] = -1; + + if ((fd == -1 && pipe(fdp[STDIN_FILENO]) == -1) || + pipe(fdp[STDOUT_FILENO]) == -1 || pipe(fdp[STDERR_FILENO]) == -1) { + closep(fdp[STDIN_FILENO]); + closep(fdp[STDOUT_FILENO]); + return makeerror(newch, n, "Cannot create pipe, %s", + strerror(errno)); } - switch (pid = fork()) { + switch (fork()) { case 0: /* child */ - (void) close(0); if (fd != -1) { - if (dup(fd) == -1) - _exit(1); - (void) lseek(0, (off_t)0, SEEK_SET); - } else { - if (dup(fdin[0]) == -1) - _exit(1); - (void) close(fdin[0]); - (void) close(fdin[1]); + fdp[STDIN_FILENO][0] = fd; + (void) lseek(fd, (off_t)0, SEEK_SET); } - - (void) close(1); - if (dup(fdout[1]) == -1) - _exit(1); - (void) close(fdout[0]); - (void) close(fdout[1]); -#ifndef DEBUG - if (compr[method].silent) - (void)close(2); -#endif + + for (i = 0; i < __arraycount(fdp); i++) + copydesc(CAST(int, i), fdp[i]); (void)execvp(compr[method].argv[0], (char *const *)(intptr_t)compr[method].argv); -#ifdef DEBUG - (void)fprintf(stderr, "exec `%s' failed (%s)\n", + dprintf(STDERR_FILENO, "exec `%s' failed, %s", compr[method].argv[0], strerror(errno)); -#endif exit(1); /*NOTREACHED*/ case -1: - file_error(ms, errno, "could not fork"); - return NODATA; + return makeerror(newch, n, "Cannot fork, %s", + strerror(errno)); default: /* parent */ - (void) close(fdout[1]); - if (fd == -1) { - (void) close(fdin[0]); - /* - * fork again, to avoid blocking because both - * pipes filled - */ - switch (fork()) { - case 0: /* child */ - (void)close(fdout[0]); - if (swrite(fdin[1], old, n) != (ssize_t)n) { -#ifdef DEBUG - (void)fprintf(stderr, - "Write failed (%s)\n", - strerror(errno)); -#endif - exit(1); - } - exit(0); - /*NOTREACHED*/ + for (i = 1; i < __arraycount(fdp); i++) + closefd(fdp[i], 1); - case -1: -#ifdef DEBUG - (void)fprintf(stderr, "Fork failed (%s)\n", - strerror(errno)); -#endif - exit(1); - /*NOTREACHED*/ + /* Write the buffer data to the child, if we don't have fd */ + if (fd == -1) + writechild(fdp, old, *n); - default: /* parent */ - if (wait(&status) == -1) { -#ifdef DEBUG - (void)fprintf(stderr, - "Wait failed (%s)\n", - strerror(errno)); -#endif - exit(1); - } - exit(WIFEXITED(status) ? - WEXITSTATUS(status) : 1); - /*NOTREACHED*/ - } - (void) close(fdin[1]); - fdin[1] = -1; - } - - if ((*newch = (unsigned char *) malloc(HOWMANY + 1)) == NULL) { -#ifdef DEBUG - (void)fprintf(stderr, "Malloc failed (%s)\n", + *newch = CAST(unsigned char *, malloc(bytes_max + 1)); + if (*newch == NULL) { + rv = makeerror(newch, n, "No buffer, %s", strerror(errno)); -#endif - n = NODATA; goto err; } - if ((r = sread(fdout[0], *newch, HOWMANY, 0)) <= 0) { -#ifdef DEBUG - (void)fprintf(stderr, "Read failed (%s)\n", - strerror(errno)); -#endif - free(*newch); - n = NODATA; - *newch = NULL; - goto err; - } else { - n = r; + rv = OKDATA; + if ((r = sread(fdp[STDOUT_FILENO][0], *newch, bytes_max, 0)) > 0) + break; + DPRINTF("Read stdout failed %d (%s)\n", fdp[STDOUT_FILENO][0], + r != -1 ? strerror(errno) : "no data"); + + rv = ERRDATA; + if (r == 0 && + (r = sread(fdp[STDERR_FILENO][0], *newch, bytes_max, 0)) > 0) + { + r = filter_error(*newch, r); + break; } - /* NUL terminate, as every buffer is handled here. */ - (*newch)[n] = '\0'; -err: - if (fdin[1] != -1) - (void) close(fdin[1]); - (void) close(fdout[0]); - if (wait(&status) == -1) { -#ifdef DEBUG - (void)fprintf(stderr, "Wait failed (%s)\n", + free(*newch); + if (r == 0) + rv = makeerror(newch, n, "Read failed, %s", strerror(errno)); -#endif - n = NODATA; - } else if (!WIFEXITED(status)) { -#ifdef DEBUG - (void)fprintf(stderr, "Child not exited (0x%x)\n", - status); -#endif - } else if (WEXITSTATUS(status) != 0) { -#ifdef DEBUG - (void)fprintf(stderr, "Child exited (0x%d)\n", - WEXITSTATUS(status)); -#endif - } + else + rv = makeerror(newch, n, "No data"); + goto err; + } - (void) close(fdin[0]); - - return n; + *n = r; + /* NUL terminate, as every buffer is handled here. */ + (*newch)[*n] = '\0'; +err: + closefd(fdp[STDIN_FILENO], 1); + closefd(fdp[STDOUT_FILENO], 0); + closefd(fdp[STDERR_FILENO], 0); + if (wait(&status) == -1) { + free(*newch); + rv = makeerror(newch, n, "Wait failed, %s", strerror(errno)); + DPRINTF("Child wait return %#x\n", status); + } else if (!WIFEXITED(status)) { + DPRINTF("Child not exited (%#x)\n", status); + } else if (WEXITSTATUS(status) != 0) { + DPRINTF("Child exited (%#x)\n", WEXITSTATUS(status)); } + + closefd(fdp[STDIN_FILENO], 0); + DPRINTF("Returning %p n=%zu rv=%d\n", *newch, *n, rv); + + return rv; } #endif diff --git a/src/der.c b/src/der.c new file mode 100644 index 0000000..4e22caf --- /dev/null +++ b/src/der.c @@ -0,0 +1,404 @@ +/*- + * Copyright (c) 2016 Christos Zoulas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * DER (Distinguished Encoding Rules) Parser + * + * Sources: + * https://en.wikipedia.org/wiki/X.690 + * http://fm4dd.com/openssl/certexamples.htm + * http://blog.engelke.com/2014/10/17/parsing-ber-and-der-encoded-asn-1-objects/ + */ +#ifndef TEST_DER +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: der.c,v 1.12 2017/02/10 18:14:01 christos Exp $") +#endif +#endif + +#include <sys/types.h> + +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#ifndef TEST_DER +#include "magic.h" +#include "der.h" +#else +#include <sys/mman.h> +#include <sys/stat.h> +#include <err.h> +#endif + +#define DER_BAD ((uint32_t)-1) + +#define DER_CLASS_UNIVERSAL 0 +#define DER_CLASS_APPLICATION 1 +#define DER_CLASS_CONTEXT 2 +#define DER_CLASS_PRIVATE 3 +#ifdef DEBUG_DER +static const char der_class[] = "UACP"; +#endif + +#define DER_TYPE_PRIMITIVE 0 +#define DER_TYPE_CONSTRUCTED 1 +#ifdef DEBUG_DER +static const char der_type[] = "PC"; +#endif + +#define DER_TAG_EOC 0x00 +#define DER_TAG_BOOLEAN 0x01 +#define DER_TAG_INTEGER 0x02 +#define DER_TAG_BIT STRING 0x03 +#define DER_TAG_OCTET_STRING 0x04 +#define DER_TAG_NULL 0x05 +#define DER_TAG_OBJECT_IDENTIFIER 0x06 +#define DER_TAG_OBJECT_DESCRIPTOR 0x07 +#define DER_TAG_EXTERNAL 0x08 +#define DER_TAG_REAL 0x09 +#define DER_TAG_ENUMERATED 0x0a +#define DER_TAG_EMBEDDED_PDV 0x0b +#define DER_TAG_UTF8_STRING 0x0c +#define DER_TAG_RELATIVE_OID 0x0d +#define DER_TAG_RESERVED_1 0x0e +#define DER_TAG_RESERVED_2 0x0f +#define DER_TAG_SEQUENCE 0x10 +#define DER_TAG_SET 0x11 +#define DER_TAG_NUMERIC_STRING 0x12 +#define DER_TAG_PRINTABLE_STRING 0x13 +#define DER_TAG_T61_STRING 0x14 +#define DER_TAG_VIDEOTEX_STRING 0x15 +#define DER_TAG_IA5_STRING 0x16 +#define DER_TAG_UTCTIME 0x17 +#define DER_TAG_GENERALIZED_TIME 0x18 +#define DER_TAG_GRAPHIC_STRING 0x19 +#define DER_TAG_VISIBLE_STRING 0x1a +#define DER_TAG_GENERAL_STRING 0x1b +#define DER_TAG_UNIVERSAL_STRING 0x1c +#define DER_TAG_CHARACTER_STRING 0x1d +#define DER_TAG_BMP_STRING 0x1e +#define DER_TAG_LONG 0x1f + +static const char *der__tag[] = { + "eoc", "bool", "int", "bit_str", "octet_str", + "null", "obj_id", "obj_desc", "ext", "real", + "enum", "embed", "utf8_str", "oid", "res1", + "res2", "seq", "set", "num_str", "prt_str", + "t61_str", "vid_str", "ia5_str", "utc_time", + "gen_time", "gr_str", "vis_str", "gen_str", + "char_str", "bmp_str", "long" +}; + +#ifdef DEBUG_DER +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif + +#ifdef TEST_DER +static uint8_t +getclass(uint8_t c) +{ + return c >> 6; +} + +static uint8_t +gettype(uint8_t c) +{ + return (c >> 5) & 1; +} +#endif + +static uint32_t +gettag(const uint8_t *c, size_t *p, size_t l) +{ + uint32_t tag; + + if (*p >= l) + return DER_BAD; + + tag = c[(*p)++] & 0x1f; + + if (tag != 0x1f) + return tag; + + if (*p >= l) + return DER_BAD; + + while (c[*p] >= 0x80) { + tag = tag * 128 + c[(*p)++] - 0x80; + if (*p >= l) + return DER_BAD; + } + return tag; +} + +/* + * Read the length of a DER tag from the input. + * + * `c` is the input, `p` is an output parameter that specifies how much of the + * input we consumed, and `l` is the maximum input length. + * + * Returns the length, or DER_BAD if the end of the input is reached or the + * length exceeds the remaining input. + */ +static uint32_t +getlength(const uint8_t *c, size_t *p, size_t l) +{ + uint8_t digits, i; + size_t len; + int is_onebyte_result; + + if (*p >= l) + return DER_BAD; + + /* + * Digits can either be 0b0 followed by the result, or 0b1 + * followed by the number of digits of the result. In either case, + * we verify that we can read so many bytes from the input. + */ + is_onebyte_result = (c[*p] & 0x80) == 0; + digits = c[(*p)++] & 0x7f; + if (*p + digits >= l) + return DER_BAD; + + if (is_onebyte_result) + return digits; + + /* + * Decode len. We've already verified that we're allowed to read + * `digits` bytes. + */ + len = 0; + for (i = 0; i < digits; i++) + len = (len << 8) | c[(*p)++]; + + if (*p + len >= l) + return DER_BAD; + return CAST(uint32_t, len); +} + +static const char * +der_tag(char *buf, size_t len, uint32_t tag) +{ + if (tag < DER_TAG_LONG) + strlcpy(buf, der__tag[tag], len); + else + snprintf(buf, len, "%#x", tag); + return buf; +} + +#ifndef TEST_DER +static int +der_data(char *buf, size_t blen, uint32_t tag, const void *q, uint32_t len) +{ + const uint8_t *d = CAST(const uint8_t *, q); + switch (tag) { + case DER_TAG_PRINTABLE_STRING: + case DER_TAG_UTF8_STRING: + case DER_TAG_IA5_STRING: + case DER_TAG_UTCTIME: + return snprintf(buf, blen, "%.*s", len, (const char *)q); + default: + break; + } + + for (uint32_t i = 0; i < len; i++) { + uint32_t z = i << 1; + if (z < blen - 2) + snprintf(buf + z, blen - z, "%.2x", d[i]); + } + return len * 2; +} + +int32_t +der_offs(struct magic_set *ms, struct magic *m, size_t nbytes) +{ + const uint8_t *b = RCAST(const uint8_t *, ms->search.s); + size_t offs = 0, len = ms->search.s_len ? ms->search.s_len : nbytes; + + if (gettag(b, &offs, len) == DER_BAD) + return -1; + DPRINTF(("%s1: %d %zu %u\n", __func__, ms->offset, offs, m->offset)); + + uint32_t tlen = getlength(b, &offs, len); + if (tlen == DER_BAD) + return -1; + DPRINTF(("%s2: %d %zu %u\n", __func__, ms->offset, offs, tlen)); + + offs += ms->offset + m->offset; + DPRINTF(("cont_level = %d\n", m->cont_level)); +#ifdef DEBUG_DER + for (size_t i = 0; i < m->cont_level; i++) + printf("cont_level[%zu] = %u\n", i, ms->c.li[i].off); +#endif + if (m->cont_level != 0) { + if (offs + tlen > nbytes) + return -1; + ms->c.li[m->cont_level - 1].off = CAST(int, offs + tlen); + DPRINTF(("cont_level[%u] = %u\n", m->cont_level - 1, + ms->c.li[m->cont_level - 1].off)); + } + return CAST(int32_t, offs); +} + +int +der_cmp(struct magic_set *ms, struct magic *m) +{ + const uint8_t *b = RCAST(const uint8_t *, ms->search.s); + const char *s = m->value.s; + size_t offs = 0, len = ms->search.s_len; + uint32_t tag, tlen; + char buf[128]; + + tag = gettag(b, &offs, len); + if (tag == DER_BAD) + return -1; + + tlen = getlength(b, &offs, len); + if (tlen == DER_BAD) + return -1; + + der_tag(buf, sizeof(buf), tag); + if ((ms->flags & MAGIC_DEBUG) != 0) + fprintf(stderr, "%s: tag %p got=%s exp=%s\n", __func__, b, + buf, s); + size_t slen = strlen(buf); + + if (strncmp(buf, s, slen) != 0) + return 0; + + s += slen; + +again: + switch (*s) { + case '\0': + return 1; + case '=': + s++; + goto val; + default: + if (!isdigit((unsigned char)*s)) + return 0; + + slen = 0; + do + slen = slen * 10 + *s - '0'; + while (isdigit((unsigned char)*++s)); + if ((ms->flags & MAGIC_DEBUG) != 0) + fprintf(stderr, "%s: len %zu %u\n", __func__, + slen, tlen); + if (tlen != slen) + return 0; + goto again; + } +val: + DPRINTF(("%s: before data %zu %u\n", __func__, offs, tlen)); + der_data(buf, sizeof(buf), tag, b + offs, tlen); + if ((ms->flags & MAGIC_DEBUG) != 0) + fprintf(stderr, "%s: data %s %s\n", __func__, buf, s); + if (strcmp(buf, s) != 0 && strcmp("x", s) != 0) + return 0; + strlcpy(ms->ms_value.s, buf, sizeof(ms->ms_value.s)); + return 1; +} +#endif + +#ifdef TEST_DER +static void +printtag(uint32_t tag, const void *q, uint32_t len) +{ + const uint8_t *d = q; + switch (tag) { + case DER_TAG_PRINTABLE_STRING: + case DER_TAG_UTF8_STRING: + printf("%.*s\n", len, (const char *)q); + return; + default: + break; + } + + for (uint32_t i = 0; i < len; i++) + printf("%.2x", d[i]); + printf("\n"); +} + +static void +printdata(size_t level, const void *v, size_t x, size_t l) +{ + const uint8_t *p = v, *ep = p + l; + size_t ox; + char buf[128]; + + while (p + x < ep) { + const uint8_t *q; + uint8_t c = getclass(p[x]); + uint8_t t = gettype(p[x]); + ox = x; + if (x != 0) + printf("%.2x %.2x %.2x\n", p[x - 1], p[x], p[x + 1]); + uint32_t tag = gettag(p, &x, ep - p + x); + if (p + x >= ep) + break; + uint32_t len = getlength(p, &x, ep - p + x); + + printf("%zu %zu-%zu %c,%c,%s,%u:", level, ox, x, + der_class[c], der_type[t], + der_tag(buf, sizeof(buf), tag), len); + q = p + x; + if (p + len > ep) + errx(EXIT_FAILURE, "corrupt der"); + printtag(tag, q, len); + if (t != DER_TYPE_PRIMITIVE) + printdata(level + 1, p, x, len + x); + x += len; + } +} + +int +main(int argc, char *argv[]) +{ + int fd; + struct stat st; + size_t l; + void *p; + + if ((fd = open(argv[1], O_RDONLY)) == -1) + err(EXIT_FAILURE, "open `%s'", argv[1]); + if (fstat(fd, &st) == -1) + err(EXIT_FAILURE, "stat `%s'", argv[1]); + l = (size_t)st.st_size; + if ((p = mmap(NULL, l, PROT_READ, MAP_FILE, fd, 0)) == MAP_FAILED) + err(EXIT_FAILURE, "mmap `%s'", argv[1]); + + printdata(0, p, 0, l); + munmap(p, l); + return 0; +} +#endif diff --git a/src/der.h b/src/der.h new file mode 100644 index 0000000..3333239 --- /dev/null +++ b/src/der.h @@ -0,0 +1,28 @@ +/*- + * Copyright (c) 2016 Christos Zoulas + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +extern int der_offs(struct magic_set *, struct magic *, size_t); +extern int der_cmp(struct magic_set *, struct magic *); diff --git a/src/dprintf.c b/src/dprintf.c new file mode 100644 index 0000000..3ae1581 --- /dev/null +++ b/src/dprintf.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) Ian F. Darwin 1986-1995. + * Software written by Ian F. Darwin and others; + * maintained 1995-present by Christos Zoulas and others. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: dprintf.c,v 1.1 2015/11/13 15:36:14 christos Exp $") +#endif /* lint */ + +#include <assert.h> +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> + +int +dprintf(int fd, const char *fmt, ...) +{ + va_list ap; + /* Simpler than using vasprintf() here, since we never need more */ + char buf[1024]; + int len; + + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if ((size_t)len >= sizeof(buf)) + return -1; + + if (write(fd, buf, (size_t)len) != len) + return -1; + + return len; +} diff --git a/src/encoding.c b/src/encoding.c index c1b23cc..3c116cd 100644 --- a/src/encoding.c +++ b/src/encoding.c @@ -35,7 +35,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: encoding.c,v 1.10 2014/09/11 12:08:52 christos Exp $") +FILE_RCSID("@(#)$File: encoding.c,v 1.13 2015/06/04 19:16:28 christos Exp $") #endif /* lint */ #include "magic.h" @@ -47,6 +47,7 @@ FILE_RCSID("@(#)$File: encoding.c,v 1.10 2014/09/11 12:08:52 christos Exp $") private int looks_ascii(const unsigned char *, size_t, unichar *, size_t *); private int looks_utf8_with_BOM(const unsigned char *, size_t, unichar *, size_t *); +private int looks_utf7(const unsigned char *, size_t, unichar *, size_t *); private int looks_ucs16(const unsigned char *, size_t, unichar *, size_t *); private int looks_latin1(const unsigned char *, size_t, unichar *, size_t *); private int looks_extended(const unsigned char *, size_t, unichar *, size_t *); @@ -88,9 +89,15 @@ file_encoding(struct magic_set *ms, const unsigned char *buf, size_t nbytes, uni } if (looks_ascii(buf, nbytes, *ubuf, ulen)) { - DPRINTF(("ascii %" SIZE_T_FORMAT "u\n", *ulen)); - *code = "ASCII"; - *code_mime = "us-ascii"; + if (looks_utf7(buf, nbytes, *ubuf, ulen) > 0) { + DPRINTF(("utf-7 %" SIZE_T_FORMAT "u\n", *ulen)); + *code = "UTF-7 Unicode"; + *code_mime = "utf-7"; + } else { + DPRINTF(("ascii %" SIZE_T_FORMAT "u\n", *ulen)); + *code = "ASCII"; + *code_mime = "us-ascii"; + } } else if (looks_utf8_with_BOM(buf, nbytes, *ubuf, ulen) > 0) { DPRINTF(("utf8/bom %" SIZE_T_FORMAT "u\n", *ulen)); *code = "UTF-8 Unicode (with BOM)"; @@ -199,8 +206,8 @@ file_encoding(struct magic_set *ms, const unsigned char *buf, size_t nbytes, uni #define X 3 /* character appears in non-ISO extended ASCII (Mac, IBM PC) */ private char text_chars[256] = { - /* BEL BS HT LF FF CR */ - F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F, /* 0x0X */ + /* BEL BS HT LF VT FF CR */ + F, F, F, F, F, F, F, T, T, T, T, T, T, T, F, F, /* 0x0X */ /* ESC */ F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */ @@ -372,6 +379,25 @@ looks_utf8_with_BOM(const unsigned char *buf, size_t nbytes, unichar *ubuf, } private int +looks_utf7(const unsigned char *buf, size_t nbytes, unichar *ubuf, size_t *ulen) +{ + if (nbytes > 4 && buf[0] == '+' && buf[1] == '/' && buf[2] == 'v') + switch (buf[3]) { + case '8': + case '9': + case '+': + case '/': + if (ubuf) + *ulen = 0; + return 1; + default: + return -1; + } + else + return -1; +} + +private int looks_ucs16(const unsigned char *buf, size_t nbytes, unichar *ubuf, size_t *ulen) { @@ -32,7 +32,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: file.c,v 1.160 2014/12/16 23:18:40 christos Exp $") +FILE_RCSID("@(#)$File: file.c,v 1.172 2016/10/24 15:21:07 christos Exp $") #endif /* lint */ #include "magic.h" @@ -68,14 +68,14 @@ int getopt_long(int argc, char * const *argv, const char *optstring, const struc #endif #ifdef S_IFLNK -#define FILE_FLAGS "-bcEhikLlNnprsvz0" +#define FILE_FLAGS "-bcEhikLlNnprsvzZ0" #else -#define FILE_FLAGS "-bcEiklNnprsvz0" +#define FILE_FLAGS "-bcEiklNnprsvzZ0" #endif # define USAGE \ "Usage: %s [" FILE_FLAGS \ - "] [--apple] [--mime-encoding] [--mime-type]\n" \ + "] [--apple] [--extension] [--mime-encoding] [--mime-type]\n" \ " [-e testname] [-F separator] [-f namefile] [-m magicfiles] " \ "file ...\n" \ " %s -C [-m magicfiles]\n" \ @@ -89,16 +89,21 @@ private int /* Global command-line options */ private const char *separator = ":"; /* Default field separator */ private const struct option long_options[] = { -#define OPT(shortname, longname, opt, doc) \ +#define OPT_HELP 1 +#define OPT_APPLE 2 +#define OPT_EXTENSIONS 3 +#define OPT_MIME_TYPE 4 +#define OPT_MIME_ENCODING 5 +#define OPT(shortname, longname, opt, def, doc) \ {longname, opt, NULL, shortname}, -#define OPT_LONGONLY(longname, opt, doc) \ - {longname, opt, NULL, 0}, +#define OPT_LONGONLY(longname, opt, def, doc, id) \ + {longname, opt, NULL, id}, #include "file_opts.h" #undef OPT #undef OPT_LONGONLY {0, 0, NULL, 0} }; -#define OPTSTRING "bcCde:Ef:F:hiklLm:nNpP:rsvz0" +#define OPTSTRING "bcCde:Ef:F:hiklLm:nNpP:rsvzZ0" private const struct { const char *name; @@ -126,12 +131,21 @@ private struct { { "elf_phnum", MAGIC_PARAM_ELF_PHNUM_MAX, 0 }, { "elf_shnum", MAGIC_PARAM_ELF_SHNUM_MAX, 0 }, { "elf_notes", MAGIC_PARAM_ELF_NOTES_MAX, 0 }, + { "regex", MAGIC_PARAM_REGEX_MAX, 0 }, + { "bytes", MAGIC_PARAM_BYTES_MAX, 0 }, }; private char *progname; /* used throughout */ +private int posixly; +#ifdef __dead +__dead +#endif private void usage(void); -private void docprint(const char *); +private void docprint(const char *, int); +#ifdef __dead +__dead +#endif private void help(void); private int unwrap(struct magic_set *, const char *); @@ -171,29 +185,29 @@ main(int argc, char *argv[]) progname = argv[0]; #ifdef S_IFLNK - flags |= getenv("POSIXLY_CORRECT") ? MAGIC_SYMLINK : 0; + posixly = getenv("POSIXLY_CORRECT") != NULL; + flags |= posixly ? MAGIC_SYMLINK : 0; #endif while ((c = getopt_long(argc, argv, OPTSTRING, long_options, &longindex)) != -1) switch (c) { - case 0 : - switch (longindex) { - case 0: - help(); - break; - case 10: - flags |= MAGIC_APPLE; - break; - case 11: - flags |= MAGIC_MIME_TYPE; - break; - case 12: - flags |= MAGIC_MIME_ENCODING; - break; - } + case OPT_HELP: + help(); + break; + case OPT_APPLE: + flags |= MAGIC_APPLE; + break; + case OPT_EXTENSIONS: + flags |= MAGIC_EXTENSION; + break; + case OPT_MIME_TYPE: + flags |= MAGIC_MIME_TYPE; + break; + case OPT_MIME_ENCODING: + flags |= MAGIC_MIME_ENCODING; break; case '0': - nulsep = 1; + nulsep++; break; case 'b': bflag++; @@ -227,6 +241,7 @@ main(int argc, char *argv[]) if (magic == NULL) if ((magic = load(magicfile, flags)) == NULL) return 1; + applyparam(magic); e |= unwrap(magic, optarg); ++didsomefiles; break; @@ -262,7 +277,6 @@ main(int argc, char *argv[]) case 'r': flags |= MAGIC_RAW; break; - break; case 's': flags |= MAGIC_DEVICES; break; @@ -276,6 +290,10 @@ main(int argc, char *argv[]) case 'z': flags |= MAGIC_COMPRESS; break; + + case 'Z': + flags |= MAGIC_COMPRESS|MAGIC_COMPRESS_TRANSP; + break; #ifdef S_IFLNK case 'L': flags |= MAGIC_SYMLINK; @@ -333,9 +351,10 @@ main(int argc, char *argv[]) if (c == -1) { (void)fprintf(stderr, "%s: %s\n", progname, magic_error(magic)); - return 1; + e = 1; + goto out; } - return 0; + goto out; default: if (magic == NULL) if ((magic = load(magicfile, flags)) == NULL) @@ -365,6 +384,7 @@ main(int argc, char *argv[]) e |= process(magic, argv[optind], wid); } +out: if (magic) magic_close(magic); return e; @@ -411,6 +431,8 @@ private struct magic_set * load(const char *magicfile, int flags) { struct magic_set *magic = magic_open(flags); + const char *e; + if (magic == NULL) { (void)fprintf(stderr, "%s: %s\n", progname, strerror(errno)); return NULL; @@ -421,6 +443,8 @@ load(const char *magicfile, int flags) magic_close(magic); return NULL; } + if ((e = magic_error(magic)) != NULL) + (void)fprintf(stderr, "%s: Warning: %s\n", progname, e); return magic; } @@ -477,24 +501,28 @@ unwrap(struct magic_set *ms, const char *fn) private int process(struct magic_set *ms, const char *inname, int wid) { - const char *type; + const char *type, c = nulsep > 1 ? '\0' : '\n'; int std_in = strcmp(inname, "-") == 0; if (wid > 0 && !bflag) { (void)printf("%s", std_in ? "/dev/stdin" : inname); if (nulsep) (void)putc('\0', stdout); - (void)printf("%s", separator); - (void)printf("%*s ", - (int) (nopad ? 0 : (wid - file_mbswidth(inname))), ""); + if (nulsep < 2) { + (void)printf("%s", separator); + (void)printf("%*s ", + (int) (nopad ? 0 : (wid - file_mbswidth(inname))), + ""); + } } type = magic_file(ms, std_in ? NULL : inname); + if (type == NULL) { - (void)printf("ERROR: %s\n", magic_error(ms)); + (void)printf("ERROR: %s%c", magic_error(ms), c); return 1; } else { - (void)printf("%s\n", type); + (void)printf("%s%c", type, c); return 0; } } @@ -544,7 +572,17 @@ usage(void) } private void -docprint(const char *opts) +defprint(int def) +{ + if (!def) + return; + if (((def & 1) && posixly) || ((def & 2) && !posixly)) + fprintf(stdout, " (default)"); + fputc('\n', stdout); +} + +private void +docprint(const char *opts, int def) { size_t i; int comma; @@ -553,6 +591,7 @@ docprint(const char *opts) p = strstr(opts, "%o"); if (p == NULL) { fprintf(stdout, "%s", opts); + defprint(def); return; } @@ -580,12 +619,12 @@ help(void) "Usage: file [OPTION...] [FILE...]\n" "Determine type of FILEs.\n" "\n", stdout); -#define OPT(shortname, longname, opt, doc) \ +#define OPT(shortname, longname, opt, def, doc) \ fprintf(stdout, " -%c, --" longname, shortname), \ - docprint(doc); -#define OPT_LONGONLY(longname, opt, doc) \ + docprint(doc, def); +#define OPT_LONGONLY(longname, opt, def, doc, id) \ fprintf(stdout, " --" longname), \ - docprint(doc); + docprint(doc, def); #include "file_opts.h" #undef OPT #undef OPT_LONGONLY @@ -27,7 +27,7 @@ */ /* * file.h - definitions for file(1) program - * @(#)$File: file.h,v 1.164 2015/01/01 17:07:34 christos Exp $ + * @(#)$File: file.h,v 1.183 2017/08/28 13:39:18 christos Exp $ */ #ifndef __file_h__ @@ -36,6 +36,10 @@ #ifdef HAVE_CONFIG_H #include <config.h> #endif +#ifdef HAVE_STDINT_H +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif #ifdef WIN32 #ifdef _WIN64 @@ -44,20 +48,18 @@ #define SIZE_T_FORMAT "" #endif #define INT64_T_FORMAT "I64" + #define INTMAX_T_FORMAT "I64" #else #define SIZE_T_FORMAT "z" #define INT64_T_FORMAT "ll" + #define INTMAX_T_FORMAT "j" +#endif +#include <stdint.h> #endif #include <stdio.h> /* Include that here, to make sure __P gets defined */ #include <errno.h> #include <fcntl.h> /* For open and flags */ -#ifdef HAVE_STDINT_H -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif -#include <stdint.h> -#endif #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif @@ -125,18 +127,18 @@ #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif -#ifndef HOWMANY -# define HOWMANY (256 * 1024) /* how much of the file to look at */ +#ifndef FILE_BYTES_MAX +# define FILE_BYTES_MAX (1024 * 1024) /* how much of the file to look at */ #endif #define MAXMAGIS 8192 /* max entries in any one magic file or directory */ #define MAXDESC 64 /* max len of text description/MIME type */ #define MAXMIME 80 /* max len of text MIME type */ -#define MAXstring 64 /* max len of "string" types */ +#define MAXstring 96 /* max len of "string" types */ #define MAGICNO 0xF11E041C -#define VERSIONNO 12 -#define FILE_MAGICSIZE 248 +#define VERSIONNO 14 +#define FILE_MAGICSIZE 344 #define FILE_LOAD 0 #define FILE_CHECK 1 @@ -225,7 +227,8 @@ struct magic { #define FILE_NAME 45 #define FILE_USE 46 #define FILE_CLEAR 47 -#define FILE_NAMES_SIZE 48 /* size of array to contain all names */ +#define FILE_DER 48 +#define FILE_NAMES_SIZE 49 /* size of array to contain all names */ #define IS_STRING(t) \ ((t) == FILE_STRING || \ @@ -272,7 +275,7 @@ struct magic { #define FILE_OPS_MASK 0x07 /* mask for above ops */ #define FILE_UNUSED_1 0x08 #define FILE_UNUSED_2 0x10 -#define FILE_UNUSED_3 0x20 +#define FILE_OPSIGNED 0x20 #define FILE_OPINVERSE 0x40 #define FILE_OPINDIRECT 0x80 @@ -300,14 +303,16 @@ struct magic { #define num_mask _u._mask #define str_range _u._s._count #define str_flags _u._s._flags - /* Words 9-16 */ + /* Words 9-24 */ union VALUETYPE value; /* either number or string */ - /* Words 17-32 */ + /* Words 25-40 */ char desc[MAXDESC]; /* description */ - /* Words 33-52 */ + /* Words 41-60 */ char mimetype[MAXMIME]; /* MIME type */ - /* Words 53-54 */ - char apple[8]; + /* Words 61-62 */ + char apple[8]; /* APPLE CREATOR/TYPE */ + /* Words 63-78 */ + char ext[64]; /* Popular extensions */ }; #define BIT(A) (1 << (A)) @@ -361,9 +366,11 @@ struct mlist { #ifdef __cplusplus #define CAST(T, b) static_cast<T>(b) #define RCAST(T, b) reinterpret_cast<T>(b) +#define CCAST(T, b) const_cast<T>(b) #else -#define CAST(T, b) (T)(b) -#define RCAST(T, b) (T)(b) +#define CAST(T, b) ((T)(b)) +#define RCAST(T, b) ((T)(b)) +#define CCAST(T, b) ((T)(uintptr_t)(b)) #endif struct level_info { @@ -411,11 +418,14 @@ struct magic_set { uint16_t elf_shnum_max; uint16_t elf_phnum_max; uint16_t elf_notes_max; -#define FILE_INDIR_MAX 15 + uint16_t regex_max; + size_t bytes_max; /* number of bytes to read from file */ +#define FILE_INDIR_MAX 50 #define FILE_NAME_MAX 30 #define FILE_ELF_SHNUM_MAX 32768 -#define FILE_ELF_PHNUM_MAX 128 +#define FILE_ELF_PHNUM_MAX 2048 #define FILE_ELF_NOTES_MAX 256 +#define FILE_REGEX_MAX 8192 }; /* Type for Unicode characters */ @@ -437,7 +447,7 @@ protected size_t file_printedlen(const struct magic_set *); protected int file_replace(struct magic_set *, const char *, const char *); protected int file_printf(struct magic_set *, const char *, ...) __attribute__((__format__(__printf__, 2, 3))); -protected int file_reset(struct magic_set *); +protected int file_reset(struct magic_set *, int); protected int file_tryelf(struct magic_set *, int, const unsigned char *, size_t); protected int file_trycdf(struct magic_set *, int, const unsigned char *, @@ -455,7 +465,7 @@ protected int file_encoding(struct magic_set *, const unsigned char *, size_t, unichar **, size_t *, const char **, const char **, const char **); protected int file_is_tar(struct magic_set *, const unsigned char *, size_t); protected int file_softmagic(struct magic_set *, const unsigned char *, size_t, - uint16_t, uint16_t *, int, int); + uint16_t *, uint16_t *, int, int); protected int file_apprentice(struct magic_set *, const char *, int); protected int buffer_apprentice(struct magic_set *, struct magic **, size_t *, size_t); @@ -500,6 +510,8 @@ typedef struct { #define USE_C_LOCALE locale_t old_lc_ctype; locale_t c_lc_ctype; +#else + char *old_lc_ctype; #endif int rc; regex_t rx; @@ -544,6 +556,9 @@ int vasprintf(char **, const char *, va_list); #ifndef HAVE_ASPRINTF int asprintf(char **, const char *, ...); #endif +#ifndef HAVE_DPRINTF +int dprintf(int, const char *, ...); +#endif #ifndef HAVE_STRLCPY size_t strlcpy(char *, const char *, size_t); @@ -564,6 +579,12 @@ char *ctime_r(const time_t *, char *); #ifndef HAVE_ASCTIME_R char *asctime_r(const struct tm *, char *); #endif +#ifndef HAVE_GMTIME_R +struct tm *gmtime_r(const time_t *, struct tm *); +#endif +#ifndef HAVE_LOCALTIME_R +struct tm *localtime_r(const time_t *, struct tm *); +#endif #ifndef HAVE_FMTCHECK const char *fmtcheck(const char *, const char *) __attribute__((__format_arg__(2))); @@ -590,5 +611,8 @@ static const char *rcsid(const char *p) { \ #else #define FILE_RCSID(id) #endif +#ifndef __RCSID +#define __RCSID(a) +#endif #endif /* __file_h__ */ diff --git a/src/file_opts.h b/src/file_opts.h index 3286ac6..52ace18 100644 --- a/src/file_opts.h +++ b/src/file_opts.h @@ -12,45 +12,47 @@ * switch statement! */ -OPT_LONGONLY("help", 0, " display this help and exit\n") -OPT('v', "version", 0, " output version information and exit\n") -OPT('m', "magic-file", 1, " LIST use LIST as a colon-separated list of magic\n" +OPT_LONGONLY("help", 0, 0, " display this help and exit\n", OPT_HELP) +OPT('v', "version", 0, 0, " output version information and exit\n") +OPT('m', "magic-file", 1, 0, " LIST use LIST as a colon-separated list of magic\n" " number files\n") -OPT('z', "uncompress", 0, " try to look inside compressed files\n") -OPT('b', "brief", 0, " do not prepend filenames to output lines\n") -OPT('c', "checking-printout", 0, " print the parsed form of the magic file, use in\n" +OPT('z', "uncompress", 0, 0, " try to look inside compressed files\n") +OPT('Z', "uncompress-noreport", 0, 0, " only print the contents of compressed files\n") +OPT('b', "brief", 0, 0, " do not prepend filenames to output lines\n") +OPT('c', "checking-printout", 0, 0, " print the parsed form of the magic file, use in\n" " conjunction with -m to debug a new magic file\n" " before installing it\n") -OPT('e', "exclude", 1, " TEST exclude TEST from the list of test to be\n" +OPT('e', "exclude", 1, 0, " TEST exclude TEST from the list of test to be\n" " performed for file. Valid tests are:\n" " %o\n") -OPT('f', "files-from", 1, " FILE read the filenames to be examined from FILE\n") -OPT('F', "separator", 1, " STRING use string as separator instead of `:'\n") -OPT('i', "mime", 0, " output MIME type strings (--mime-type and\n" +OPT('f', "files-from", 1, 0, " FILE read the filenames to be examined from FILE\n") +OPT('F', "separator", 1, 0, " STRING use string as separator instead of `:'\n") +OPT('i', "mime", 0, 0, " output MIME type strings (--mime-type and\n" " --mime-encoding)\n") -OPT_LONGONLY("apple", 0, " output the Apple CREATOR/TYPE\n") -OPT_LONGONLY("mime-type", 0, " output the MIME type\n") -OPT_LONGONLY("mime-encoding", 0, " output the MIME encoding\n") -OPT('k', "keep-going", 0, " don't stop at the first match\n") -OPT('l', "list", 0, " list magic strength\n") +OPT_LONGONLY("apple", 0, 0, " output the Apple CREATOR/TYPE\n", OPT_APPLE) +OPT_LONGONLY("extension", 0, 0, " output a slash-separated list of extensions\n", OPT_EXTENSIONS) +OPT_LONGONLY("mime-type", 0, 0, " output the MIME type\n", OPT_MIME_TYPE) +OPT_LONGONLY("mime-encoding", 0, 0, " output the MIME encoding\n", OPT_MIME_ENCODING) +OPT('k', "keep-going", 0, 0, " don't stop at the first match\n") +OPT('l', "list", 0, 0, " list magic strength\n") #ifdef S_IFLNK -OPT('L', "dereference", 0, " follow symlinks (default)\n") -OPT('h', "no-dereference", 0, " don't follow symlinks\n") +OPT('L', "dereference", 0, 1, " follow symlinks") +OPT('h', "no-dereference", 0, 2, " don't follow symlinks") #endif -OPT('n', "no-buffer", 0, " do not buffer output\n") -OPT('N', "no-pad", 0, " do not pad output\n") -OPT('0', "print0", 0, " terminate filenames with ASCII NUL\n") +OPT('n', "no-buffer", 0, 0, " do not buffer output\n") +OPT('N', "no-pad", 0, 0, " do not pad output\n") +OPT('0', "print0", 0, 0, " terminate filenames with ASCII NUL\n") #if defined(HAVE_UTIME) || defined(HAVE_UTIMES) -OPT('p', "preserve-date", 0, " preserve access times on files\n") +OPT('p', "preserve-date", 0, 0, " preserve access times on files\n") #endif -OPT('P', "parameter", 0, " set file engine parameter limits\n" +OPT('P', "parameter", 1, 0, " set file engine parameter limits\n" " indir 15 recursion limit for indirection\n" " name 30 use limit for name/use magic\n" " elf_notes 256 max ELF notes processed\n" " elf_phnum 128 max ELF prog sections processed\n" " elf_shnum 32768 max ELF sections processed\n") -OPT('r', "raw", 0, " don't translate unprintable chars to \\ooo\n") -OPT('s', "special-files", 0, " treat special (block/char devices) files as\n" +OPT('r', "raw", 0, 0, " don't translate unprintable chars to \\ooo\n") +OPT('s', "special-files", 0, 0, " treat special (block/char devices) files as\n" " ordinary ones\n") -OPT('C', "compile", 0, " compile file specified by -m\n") -OPT('d', "debug", 0, " print debugging messages\n") +OPT('C', "compile", 0, 0, " compile file specified by -m\n") +OPT('d', "debug", 0, 0, " print debugging messages\n") diff --git a/src/fmtcheck.c b/src/fmtcheck.c index 0fc7038..486aa08 100644 --- a/src/fmtcheck.c +++ b/src/fmtcheck.c @@ -91,6 +91,23 @@ get_next_format_from_precision(const char **pf) f++; longdouble = 1; break; +#ifdef WIN32 + case 'I': + f++; + if (!*f) RETURN(pf,f,FMTCHECK_UNKNOWN); + if (*f == '3' && f[1] == '2') { + f += 2; + } else if (*f == '6' && f[1] == '4') { + f += 2; + quad = 1; + } +#ifdef _WIN64 + else { + quad = 1; + } +#endif + break; +#endif default: break; } diff --git a/src/fsmagic.c b/src/fsmagic.c index 1e8fd74..c0a437a 100644 --- a/src/fsmagic.c +++ b/src/fsmagic.c @@ -32,7 +32,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: fsmagic.c,v 1.75 2014/12/04 15:56:46 christos Exp $") +FILE_RCSID("@(#)$File: fsmagic.c,v 1.77 2017/05/24 19:17:50 christos Exp $") #endif /* lint */ #include "magic.h" @@ -104,14 +104,13 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) { int ret, did = 0; int mime = ms->flags & MAGIC_MIME; + int silent = ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION); #ifdef S_IFLNK char buf[BUFSIZ+4]; ssize_t nch; struct stat tstatbuf; #endif - if (ms->flags & MAGIC_APPLE) - return 0; if (fn == NULL) return 0; @@ -168,7 +167,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) } ret = 1; - if (!mime) { + if (!mime && !silent) { #ifdef S_ISUID if (sb->st_mode & S_ISUID) if (file_printf(ms, "%ssetuid", COMMA) == -1) @@ -191,6 +190,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) if (mime) { if (handle_mime(ms, mime, "directory") == -1) return -1; + } else if (silent) { } else if (file_printf(ms, "%sdirectory", COMMA) == -1) return -1; break; @@ -208,6 +208,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) if (mime) { if (handle_mime(ms, mime, "chardevice") == -1) return -1; + } else if (silent) { } else { #ifdef HAVE_STRUCT_STAT_ST_RDEV # ifdef dv_unit @@ -242,6 +243,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) if (mime) { if (handle_mime(ms, mime, "blockdevice") == -1) return -1; + } else if (silent) { } else { #ifdef HAVE_STRUCT_STAT_ST_RDEV # ifdef dv_unit @@ -270,6 +272,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) if (mime) { if (handle_mime(ms, mime, "fifo") == -1) return -1; + } else if (silent) { } else if (file_printf(ms, "%sfifo (named pipe)", COMMA) == -1) return -1; break; @@ -279,6 +282,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) if (mime) { if (handle_mime(ms, mime, "door") == -1) return -1; + } else if (silent) { } else if (file_printf(ms, "%sdoor", COMMA) == -1) return -1; break; @@ -294,6 +298,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) if (mime) { if (handle_mime(ms, mime, "symlink") == -1) return -1; + } else if (silent) { } else if (file_printf(ms, "%sunreadable symlink `%s' (%s)", COMMA, fn, strerror(errno)) == -1) @@ -323,6 +328,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) if (handle_mime(ms, mime, "x-path-too-long") == -1) return -1; + } else if (silent) { } else if (file_printf(ms, "%spath too long: `%s'", COMMA, fn) == -1) @@ -352,6 +358,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) if (mime) { if (handle_mime(ms, mime, "symlink") == -1) return -1; + } else if (silent) { } else if (file_printf(ms, "%ssymbolic link to %s", COMMA, buf) == -1) return -1; @@ -364,6 +371,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) if (mime) { if (handle_mime(ms, mime, "socket") == -1) return -1; + } else if (silent) { } else if (file_printf(ms, "%ssocket", COMMA) == -1) return -1; break; @@ -386,6 +394,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) if (mime) { if (handle_mime(ms, mime, "x-empty") == -1) return -1; + } else if (silent) { } else if (file_printf(ms, "%sempty", COMMA) == -1) return -1; break; @@ -399,7 +408,7 @@ file_fsmagic(struct magic_set *ms, const char *fn, struct stat *sb) /*NOTREACHED*/ } - if (!mime && did && ret == 0) { + if (!silent && !mime && did && ret == 0) { if (file_printf(ms, " ") == -1) return -1; } diff --git a/src/funcs.c b/src/funcs.c index a60ccaa..d7a18f4 100644 --- a/src/funcs.c +++ b/src/funcs.c @@ -27,7 +27,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: funcs.c,v 1.79 2014/12/16 20:52:49 christos Exp $") +FILE_RCSID("@(#)$File: funcs.c,v 1.93 2017/08/28 13:39:18 christos Exp $") #endif /* lint */ #include "magic.h" @@ -76,7 +76,7 @@ file_vprintf(struct magic_set *ms, const char *fmt, va_list ap) ms->o.buf = buf; return 0; out: - file_error(ms, errno, "vasprintf failed"); + fprintf(stderr, "vasprintf failed (%s)", strerror(errno)); return -1; } @@ -107,8 +107,10 @@ file_error_core(struct magic_set *ms, int error, const char *f, va_list va, if (lineno != 0) { free(ms->o.buf); ms->o.buf = NULL; - file_printf(ms, "line %" SIZE_T_FORMAT "u: ", lineno); + file_printf(ms, "line %" SIZE_T_FORMAT "u:", lineno); } + if (ms->o.buf && *ms->o.buf) + file_printf(ms, " "); file_vprintf(ms, f, va); if (error > 0) file_printf(ms, " (%s)", strerror(error)); @@ -159,12 +161,23 @@ file_badread(struct magic_set *ms) } #ifndef COMPILE_ONLY + +static int +checkdone(struct magic_set *ms, int *rv) +{ + if ((ms->flags & MAGIC_CONTINUE) == 0) + return 1; + if (file_printf(ms, "\n- ") == -1) + *rv = -1; + return 0; +} + +/*ARGSUSED*/ protected int -file_buffer(struct magic_set *ms, int fd, const char *inname __attribute__ ((unused)), +file_buffer(struct magic_set *ms, int fd, const char *inname __attribute__ ((__unused__)), const void *buf, size_t nb) { int m = 0, rv = 0, looks_text = 0; - int mime = ms->flags & MAGIC_MIME; const unsigned char *ubuf = CAST(const unsigned char *, buf); unichar *u8buf = NULL; size_t ulen; @@ -190,7 +203,10 @@ file_buffer(struct magic_set *ms, int fd, const char *inname __attribute__ ((unu #ifdef __EMX__ if ((ms->flags & MAGIC_NO_CHECK_APPTYPE) == 0 && inname) { - switch (file_os2_apptype(ms, inname, buf, nb)) { + m = file_os2_apptype(ms, inname, buf, nb); + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "[try os2_apptype %d]\n", m); + switch (m) { case -1: return -1; case 0: @@ -202,35 +218,44 @@ file_buffer(struct magic_set *ms, int fd, const char *inname __attribute__ ((unu #endif #if HAVE_FORK /* try compression stuff */ - if ((ms->flags & MAGIC_NO_CHECK_COMPRESS) == 0) - if ((m = file_zmagic(ms, fd, inname, ubuf, nb)) != 0) { - if ((ms->flags & MAGIC_DEBUG) != 0) - (void)fprintf(stderr, "zmagic %d\n", m); + if ((ms->flags & MAGIC_NO_CHECK_COMPRESS) == 0) { + m = file_zmagic(ms, fd, inname, ubuf, nb); + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "[try zmagic %d]\n", m); + if (m) { goto done_encoding; } + } #endif /* Check if we have a tar file */ - if ((ms->flags & MAGIC_NO_CHECK_TAR) == 0) - if ((m = file_is_tar(ms, ubuf, nb)) != 0) { - if ((ms->flags & MAGIC_DEBUG) != 0) - (void)fprintf(stderr, "tar %d\n", m); - goto done; + if ((ms->flags & MAGIC_NO_CHECK_TAR) == 0) { + m = file_is_tar(ms, ubuf, nb); + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "[try tar %d]\n", m); + if (m) { + if (checkdone(ms, &rv)) + goto done; } + } /* Check if we have a CDF file */ - if ((ms->flags & MAGIC_NO_CHECK_CDF) == 0) - if ((m = file_trycdf(ms, fd, ubuf, nb)) != 0) { - if ((ms->flags & MAGIC_DEBUG) != 0) - (void)fprintf(stderr, "cdf %d\n", m); - goto done; + if ((ms->flags & MAGIC_NO_CHECK_CDF) == 0) { + m = file_trycdf(ms, fd, ubuf, nb); + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "[try cdf %d]\n", m); + if (m) { + if (checkdone(ms, &rv)) + goto done; } + } /* try soft magic tests */ - if ((ms->flags & MAGIC_NO_CHECK_SOFT) == 0) - if ((m = file_softmagic(ms, ubuf, nb, 0, NULL, BINTEST, - looks_text)) != 0) { - if ((ms->flags & MAGIC_DEBUG) != 0) - (void)fprintf(stderr, "softmagic %d\n", m); + if ((ms->flags & MAGIC_NO_CHECK_SOFT) == 0) { + m = file_softmagic(ms, ubuf, nb, NULL, NULL, BINTEST, + looks_text); + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "[try softmagic %d]\n", m); + if (m) { #ifdef BUILTIN_ELF if ((ms->flags & MAGIC_NO_CHECK_ELF) == 0 && m == 1 && nb > 5 && fd != -1) { @@ -243,31 +268,45 @@ file_buffer(struct magic_set *ms, int fd, const char *inname __attribute__ ((unu * ELF headers that cannot easily * be * extracted with rules in the magic file. */ - if ((m = file_tryelf(ms, fd, ubuf, nb)) != 0) - if ((ms->flags & MAGIC_DEBUG) != 0) - (void)fprintf(stderr, - "elf %d\n", m); + m = file_tryelf(ms, fd, ubuf, nb); + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "[try elf %d]\n", + m); } #endif - goto done; + if (checkdone(ms, &rv)) + goto done; } + } /* try text properties */ if ((ms->flags & MAGIC_NO_CHECK_TEXT) == 0) { - if ((m = file_ascmagic(ms, ubuf, nb, looks_text)) != 0) { - if ((ms->flags & MAGIC_DEBUG) != 0) - (void)fprintf(stderr, "ascmagic %d\n", m); - goto done; + m = file_ascmagic(ms, ubuf, nb, looks_text); + if ((ms->flags & MAGIC_DEBUG) != 0) + (void)fprintf(stderr, "[try ascmagic %d]\n", m); + if (m) { + if (checkdone(ms, &rv)) + goto done; } } simple: /* give up */ m = 1; - if ((!mime || (mime & MAGIC_MIME_TYPE)) && - file_printf(ms, "%s", mime ? type : def) == -1) { - rv = -1; + if (ms->flags & MAGIC_MIME) { + if ((ms->flags & MAGIC_MIME_TYPE) && + file_printf(ms, "%s", type) == -1) + rv = -1; + } else if (ms->flags & MAGIC_APPLE) { + if (file_printf(ms, "UNKNUNKN") == -1) + rv = -1; + } else if (ms->flags & MAGIC_EXTENSION) { + if (file_printf(ms, "???") == -1) + rv = -1; + } else { + if (file_printf(ms, "%s", def) == -1) + rv = -1; } done: if ((ms->flags & MAGIC_MIME_ENCODING) != 0) { @@ -289,9 +328,9 @@ simple: #endif protected int -file_reset(struct magic_set *ms) +file_reset(struct magic_set *ms, int checkloaded) { - if (ms->mlist[0] == NULL) { + if (checkloaded && ms->mlist[0] == NULL) { file_error(ms, 0, "no magic files loaded"); return -1; } @@ -400,7 +439,7 @@ file_check_mem(struct magic_set *ms, unsigned int level) size_t len; if (level >= ms->c.len) { - len = (ms->c.len += 20) * sizeof(*ms->c.li); + len = (ms->c.len = 20 + level) * sizeof(*ms->c.li); ms->c.li = CAST(struct level_info *, (ms->c.li == NULL) ? malloc(len) : realloc(ms->c.li, len)); @@ -457,6 +496,8 @@ file_regcomp(file_regex_t *rx, const char *pat, int flags) assert(rx->c_lc_ctype != NULL); rx->old_lc_ctype = uselocale(rx->c_lc_ctype); assert(rx->old_lc_ctype != NULL); +#else + rx->old_lc_ctype = setlocale(LC_CTYPE, "C"); #endif rx->pat = pat; @@ -468,6 +509,8 @@ file_regexec(file_regex_t *rx, const char *str, size_t nmatch, regmatch_t* pmatch, int eflags) { assert(rx->rc == 0); + /* XXX: force initialization because glibc does not always do this */ + memset(pmatch, 0, nmatch * sizeof(*pmatch)); return regexec(&rx->rx, str, nmatch, pmatch, eflags); } @@ -479,6 +522,8 @@ file_regfree(file_regex_t *rx) #ifdef USE_C_LOCALE (void)uselocale(rx->old_lc_ctype); freelocale(rx->c_lc_ctype); +#else + (void)setlocale(LC_CTYPE, rx->old_lc_ctype); #endif } @@ -549,9 +594,9 @@ file_printable(char *buf, size_t bufsiz, const char *str) if (ptr >= eptr - 3) break; *ptr++ = '\\'; - *ptr++ = ((*s >> 6) & 7) + '0'; - *ptr++ = ((*s >> 3) & 7) + '0'; - *ptr++ = ((*s >> 0) & 7) + '0'; + *ptr++ = ((CAST(unsigned int, *s) >> 6) & 7) + '0'; + *ptr++ = ((CAST(unsigned int, *s) >> 3) & 7) + '0'; + *ptr++ = ((CAST(unsigned int, *s) >> 0) & 7) + '0'; } *ptr = '\0'; return buf; diff --git a/src/gmtime_r.c b/src/gmtime_r.c new file mode 100644 index 0000000..469ec65 --- /dev/null +++ b/src/gmtime_r.c @@ -0,0 +1,19 @@ +/* $File: gmtime_r.c,v 1.2 2015/07/11 14:41:37 christos Exp $ */ + +#include "file.h" +#ifndef lint +FILE_RCSID("@(#)$File: gmtime_r.c,v 1.2 2015/07/11 14:41:37 christos Exp $") +#endif /* lint */ +#include <time.h> +#include <string.h> + +/* asctime_r is not thread-safe anyway */ +struct tm * +gmtime_r(const time_t *t, struct tm *tm) +{ + struct tm *tmp = gmtime(t); + if (tmp == NULL) + return NULL; + memcpy(tm, tmp, sizeof(*tm)); + return tmp; +} diff --git a/src/is_tar.c b/src/is_tar.c index 876c631..1953a7f 100644 --- a/src/is_tar.c +++ b/src/is_tar.c @@ -40,7 +40,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: is_tar.c,v 1.37 2010/11/30 14:58:53 rrt Exp $") +FILE_RCSID("@(#)$File: is_tar.c,v 1.39 2017/03/17 20:45:01 christos Exp $") #endif #include "magic.h" @@ -51,7 +51,7 @@ FILE_RCSID("@(#)$File: is_tar.c,v 1.37 2010/11/30 14:58:53 rrt Exp $") #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') ) private int is_tar(const unsigned char *, size_t); -private int from_oct(int, const char *); /* Decode octal number */ +private int from_oct(const char *, size_t); /* Decode octal number */ static const char tartype[][32] = { "tar archive", @@ -69,7 +69,7 @@ file_is_tar(struct magic_set *ms, const unsigned char *buf, size_t nbytes) int tar; int mime = ms->flags & MAGIC_MIME; - if ((ms->flags & MAGIC_APPLE) != 0) + if ((ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) != 0) return 0; tar = is_tar(buf, nbytes); @@ -93,31 +93,35 @@ private int is_tar(const unsigned char *buf, size_t nbytes) { const union record *header = (const union record *)(const void *)buf; - int i; - int sum, recsum; - const unsigned char *p; + size_t i; + int sum, recsum; + const unsigned char *p, *ep; - if (nbytes < sizeof(union record)) + if (nbytes < sizeof(*header)) return 0; - recsum = from_oct(8, header->header.chksum); + recsum = from_oct(header->header.chksum, sizeof(header->header.chksum)); sum = 0; p = header->charptr; - for (i = sizeof(union record); --i >= 0;) + ep = header->charptr + sizeof(*header); + while (p < ep) sum += *p++; /* Adjust checksum to count the "chksum" field as blanks. */ - for (i = sizeof(header->header.chksum); --i >= 0;) + for (i = 0; i < sizeof(header->header.chksum); i++) sum -= header->header.chksum[i]; - sum += ' ' * sizeof header->header.chksum; + sum += ' ' * sizeof(header->header.chksum); if (sum != recsum) return 0; /* Not a tar archive */ - if (strcmp(header->header.magic, GNUTMAGIC) == 0) + if (strncmp(header->header.magic, GNUTMAGIC, + sizeof(header->header.magic)) == 0) return 3; /* GNU Unix Standard tar archive */ - if (strcmp(header->header.magic, TMAGIC) == 0) + + if (strncmp(header->header.magic, TMAGIC, + sizeof(header->header.magic)) == 0) return 2; /* Unix Standard tar archive */ return 1; /* Old fashioned tar archive */ @@ -130,19 +134,22 @@ is_tar(const unsigned char *buf, size_t nbytes) * Result is -1 if the field is invalid (all blank, or non-octal). */ private int -from_oct(int digs, const char *where) +from_oct(const char *where, size_t digs) { int value; + if (digs == 0) + return -1; + while (isspace((unsigned char)*where)) { /* Skip spaces */ where++; - if (--digs <= 0) + if (digs-- == 0) return -1; /* All blank field */ } value = 0; while (digs > 0 && isodigit(*where)) { /* Scan til non-octal */ value = (value << 3) | (*where++ - '0'); - --digs; + digs--; } if (digs > 0 && *where && !isspace((unsigned char)*where)) diff --git a/src/localtime_r.c b/src/localtime_r.c new file mode 100644 index 0000000..b0d996d --- /dev/null +++ b/src/localtime_r.c @@ -0,0 +1,19 @@ +/* $File: localtime_r.c,v 1.2 2015/07/11 14:41:37 christos Exp $ */ + +#include "file.h" +#ifndef lint +FILE_RCSID("@(#)$File: localtime_r.c,v 1.2 2015/07/11 14:41:37 christos Exp $") +#endif /* lint */ +#include <time.h> +#include <string.h> + +/* asctime_r is not thread-safe anyway */ +struct tm * +localtime_r(const time_t *t, struct tm *tm) +{ + struct tm *tmp = localtime(t); + if (tmp == NULL) + return NULL; + memcpy(tm, tmp, sizeof(*tm)); + return tmp; +} diff --git a/src/magic.c b/src/magic.c index d16f8c6..1448a69 100644 --- a/src/magic.c +++ b/src/magic.c @@ -33,7 +33,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: magic.c,v 1.91 2014/12/16 23:18:40 christos Exp $") +FILE_RCSID("@(#)$File: magic.c,v 1.102 2017/08/28 13:39:18 christos Exp $") #endif /* lint */ #include "magic.h" @@ -83,6 +83,94 @@ private const char *file_or_fd(struct magic_set *, const char *, int); #define STDIN_FILENO 0 #endif +#ifdef WIN32 +/* HINSTANCE of this shared library. Needed for get_default_magic() */ +static HINSTANCE _w32_dll_instance = NULL; + +static void +_w32_append_path(char **hmagicpath, const char *fmt, ...) +{ + char *tmppath; + char *newpath; + va_list ap; + + va_start(ap, fmt); + if (vasprintf(&tmppath, fmt, ap) < 0) { + va_end(ap); + return; + } + va_end(ap); + + if (access(tmppath, R_OK) == -1) + goto out; + + if (*hmagicpath == NULL) { + *hmagicpath = tmppath; + return; + } + + if (asprintf(&newpath, "%s%c%s", *hmagicpath, PATHSEP, tmppath) < 0) + goto out; + + free(*hmagicpath); + free(tmppath); + *hmagicpath = newpath; + return; +out: + free(tmppath); +} + +static void +_w32_get_magic_relative_to(char **hmagicpath, HINSTANCE module) +{ + static const char *trypaths[] = { + "%s/share/misc/magic.mgc", + "%s/magic.mgc", + }; + LPSTR dllpath; + size_t sp; + + dllpath = calloc(MAX_PATH + 1, sizeof(*dllpath)); + + if (!GetModuleFileNameA(module, dllpath, MAX_PATH)) + goto out; + + PathRemoveFileSpecA(dllpath); + + if (module) { + char exepath[MAX_PATH]; + GetModuleFileNameA(NULL, exepath, MAX_PATH); + PathRemoveFileSpecA(exepath); + if (stricmp(exepath, dllpath) == 0) + goto out; + } + + sp = strlen(dllpath); + if (sp > 3 && stricmp(&dllpath[sp - 3], "bin") == 0) { + _w32_append_path(hmagicpath, + "%s/../share/misc/magic.mgc", dllpath); + goto out; + } + + for (sp = 0; sp < __arraycount(trypaths); sp++) + _w32_append_path(hmagicpath, trypaths[sp], dllpath); +out: + free(dllpath); +} + +/* Placate GCC by offering a sacrificial previous prototype */ +BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID); + +BOOL WINAPI +DllMain(HINSTANCE hinstDLL, DWORD fdwReason, + LPVOID lpvReserved __attribute__((__unused__))) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + _w32_dll_instance = hinstDLL; + return 1; +} +#endif + private const char * get_default_magic(void) { @@ -126,75 +214,33 @@ out: free(hmagicpath); return MAGIC; #else - char *hmagicp; - char *tmppath = NULL; - LPTSTR dllpath; hmagicpath = NULL; -#define APPENDPATH() \ - do { \ - if (tmppath && access(tmppath, R_OK) != -1) { \ - if (hmagicpath == NULL) \ - hmagicpath = tmppath; \ - else { \ - if (asprintf(&hmagicp, "%s%c%s", hmagicpath, \ - PATHSEP, tmppath) >= 0) { \ - free(hmagicpath); \ - hmagicpath = hmagicp; \ - } \ - free(tmppath); \ - } \ - tmppath = NULL; \ - } \ - } while (/*CONSTCOND*/0) - if (default_magic) { free(default_magic); default_magic = NULL; } - /* First, try to get user-specific magic file */ - if ((home = getenv("LOCALAPPDATA")) == NULL) { - if ((home = getenv("USERPROFILE")) != NULL) - if (asprintf(&tmppath, - "%s/Local Settings/Application Data%s", home, - hmagic) < 0) - tmppath = NULL; - } else { - if (asprintf(&tmppath, "%s%s", home, hmagic) < 0) - tmppath = NULL; - } + /* First, try to get a magic file from user-application data */ + if ((home = getenv("LOCALAPPDATA")) != NULL) + _w32_append_path(&hmagicpath, "%s%s", home, hmagic); - APPENDPATH(); + /* Second, try to get a magic file from the user profile data */ + if ((home = getenv("USERPROFILE")) != NULL) + _w32_append_path(&hmagicpath, + "%s/Local Settings/Application Data%s", home, hmagic); - /* Second, try to get a magic file from Common Files */ - if ((home = getenv("COMMONPROGRAMFILES")) != NULL) { - if (asprintf(&tmppath, "%s%s", home, hmagic) >= 0) - APPENDPATH(); - } + /* Third, try to get a magic file from Common Files */ + if ((home = getenv("COMMONPROGRAMFILES")) != NULL) + _w32_append_path(&hmagicpath, "%s%s", home, hmagic); - /* Third, try to get magic file relative to dll location */ - dllpath = malloc(sizeof(*dllpath) * (MAX_PATH + 1)); - dllpath[MAX_PATH] = 0; /* just in case long path gets truncated and not null terminated */ - if (GetModuleFileNameA(NULL, dllpath, MAX_PATH)){ - PathRemoveFileSpecA(dllpath); - if (strlen(dllpath) > 3 && - stricmp(&dllpath[strlen(dllpath) - 3], "bin") == 0) { - if (asprintf(&tmppath, - "%s/../share/misc/magic.mgc", dllpath) >= 0) - APPENDPATH(); - } else { - if (asprintf(&tmppath, - "%s/share/misc/magic.mgc", dllpath) >= 0) - APPENDPATH(); - else if (asprintf(&tmppath, - "%s/magic.mgc", dllpath) >= 0) - APPENDPATH(); - } - } + /* Fourth, try to get magic file relative to exe location */ + _w32_get_magic_relative_to(&hmagicpath, NULL); - /* Don't put MAGIC constant - it likely points to a file within MSys - tree */ + /* Fifth, try to get magic file relative to dll location */ + _w32_get_magic_relative_to(&hmagicpath, _w32_dll_instance); + + /* Avoid MAGIC constant - it likely points to a file within MSys tree */ default_magic = hmagicpath; return default_magic; #endif @@ -363,7 +409,7 @@ file_or_fd(struct magic_set *ms, const char *inname, int fd) int ispipe = 0; off_t pos = (off_t)-1; - if (file_reset(ms) == -1) + if (file_reset(ms, 1) == -1) goto out; /* @@ -371,7 +417,7 @@ file_or_fd(struct magic_set *ms, const char *inname, int fd) * some overlapping space for matches near EOF */ #define SLOP (1 + sizeof(union VALUETYPE)) - if ((buf = CAST(unsigned char *, malloc(HOWMANY + SLOP))) == NULL) + if ((buf = CAST(unsigned char *, malloc(ms->bytes_max + SLOP))) == NULL) return NULL; switch (file_fsmagic(ms, inname, &sb)) { @@ -435,18 +481,18 @@ file_or_fd(struct magic_set *ms, const char *inname, int fd) } /* - * try looking at the first HOWMANY bytes + * try looking at the first ms->bytes_max bytes */ if (ispipe) { ssize_t r = 0; while ((r = sread(fd, (void *)&buf[nbytes], - (size_t)(HOWMANY - nbytes), 1)) > 0) { + (size_t)(ms->bytes_max - nbytes), 1)) > 0) { nbytes += r; if (r < PIPE_BUF) break; } - if (nbytes == 0) { + if (nbytes == 0 && inname) { /* We can not read it, but we were able to stat it. */ if (unreadable_info(ms, sb.st_mode, inname) == -1) goto done; @@ -457,10 +503,10 @@ file_or_fd(struct magic_set *ms, const char *inname, int fd) } else { /* Windows refuses to read from a big console buffer. */ size_t howmany = -#if defined(WIN32) && HOWMANY > 8 * 1024 +#if defined(WIN32) _isatty(fd) ? 8 * 1024 : #endif - HOWMANY; + ms->bytes_max; if ((nbytes = read(fd, (char *)buf, howmany)) == -1) { if (inname == NULL && fd != STDIN_FILENO) file_error(ms, errno, "cannot read fd %d", fd); @@ -477,9 +523,11 @@ file_or_fd(struct magic_set *ms, const char *inname, int fd) rv = 0; done: free(buf); - if (pos != (off_t)-1) - (void)lseek(fd, pos, SEEK_SET); - close_and_restore(ms, inname, fd, &sb); + if (fd != -1) { + if (pos != (off_t)-1) + (void)lseek(fd, pos, SEEK_SET); + close_and_restore(ms, inname, fd, &sb); + } out: return rv == 0 ? file_getbuffer(ms) : NULL; } @@ -490,7 +538,7 @@ magic_buffer(struct magic_set *ms, const void *buf, size_t nb) { if (ms == NULL) return NULL; - if (file_reset(ms) == -1) + if (file_reset(ms, 1) == -1) return NULL; /* * The main work is done here! @@ -520,6 +568,15 @@ magic_errno(struct magic_set *ms) } public int +magic_getflags(struct magic_set *ms) +{ + if (ms == NULL) + return -1; + + return ms->flags; +} + +public int magic_setflags(struct magic_set *ms, int flags) { if (ms == NULL) @@ -543,19 +600,25 @@ magic_setparam(struct magic_set *ms, int param, const void *val) { switch (param) { case MAGIC_PARAM_INDIR_MAX: - ms->indir_max = *(const size_t *)val; + ms->indir_max = (uint16_t)*(const size_t *)val; return 0; case MAGIC_PARAM_NAME_MAX: - ms->name_max = *(const size_t *)val; + ms->name_max = (uint16_t)*(const size_t *)val; return 0; case MAGIC_PARAM_ELF_PHNUM_MAX: - ms->elf_phnum_max = *(const size_t *)val; + ms->elf_phnum_max = (uint16_t)*(const size_t *)val; return 0; case MAGIC_PARAM_ELF_SHNUM_MAX: - ms->elf_shnum_max = *(const size_t *)val; + ms->elf_shnum_max = (uint16_t)*(const size_t *)val; return 0; case MAGIC_PARAM_ELF_NOTES_MAX: - ms->elf_notes_max = *(const size_t *)val; + ms->elf_notes_max = (uint16_t)*(const size_t *)val; + return 0; + case MAGIC_PARAM_REGEX_MAX: + ms->elf_notes_max = (uint16_t)*(const size_t *)val; + return 0; + case MAGIC_PARAM_BYTES_MAX: + ms->bytes_max = *(const size_t *)val; return 0; default: errno = EINVAL; @@ -582,6 +645,12 @@ magic_getparam(struct magic_set *ms, int param, void *val) case MAGIC_PARAM_ELF_NOTES_MAX: *(size_t *)val = ms->elf_notes_max; return 0; + case MAGIC_PARAM_REGEX_MAX: + *(size_t *)val = ms->regex_max; + return 0; + case MAGIC_PARAM_BYTES_MAX: + *(size_t *)val = ms->bytes_max; + return 0; default: errno = EINVAL; return -1; diff --git a/src/magic.h b/src/magic.h deleted file mode 100644 index 721712f..0000000 --- a/src/magic.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (c) Christos Zoulas 2003. - * All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice immediately at the beginning of the file, without modification, - * this list of conditions, and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -#ifndef _MAGIC_H -#define _MAGIC_H - -#include <sys/types.h> - -#define MAGIC_NONE 0x000000 /* No flags */ -#define MAGIC_DEBUG 0x000001 /* Turn on debugging */ -#define MAGIC_SYMLINK 0x000002 /* Follow symlinks */ -#define MAGIC_COMPRESS 0x000004 /* Check inside compressed files */ -#define MAGIC_DEVICES 0x000008 /* Look at the contents of devices */ -#define MAGIC_MIME_TYPE 0x000010 /* Return the MIME type */ -#define MAGIC_CONTINUE 0x000020 /* Return all matches */ -#define MAGIC_CHECK 0x000040 /* Print warnings to stderr */ -#define MAGIC_PRESERVE_ATIME 0x000080 /* Restore access time on exit */ -#define MAGIC_RAW 0x000100 /* Don't translate unprintable chars */ -#define MAGIC_ERROR 0x000200 /* Handle ENOENT etc as real errors */ -#define MAGIC_MIME_ENCODING 0x000400 /* Return the MIME encoding */ -#define MAGIC_MIME (MAGIC_MIME_TYPE|MAGIC_MIME_ENCODING) -#define MAGIC_APPLE 0x000800 /* Return the Apple creator and type */ - -#define MAGIC_NO_CHECK_COMPRESS 0x001000 /* Don't check for compressed files */ -#define MAGIC_NO_CHECK_TAR 0x002000 /* Don't check for tar files */ -#define MAGIC_NO_CHECK_SOFT 0x004000 /* Don't check magic entries */ -#define MAGIC_NO_CHECK_APPTYPE 0x008000 /* Don't check application type */ -#define MAGIC_NO_CHECK_ELF 0x010000 /* Don't check for elf details */ -#define MAGIC_NO_CHECK_TEXT 0x020000 /* Don't check for text files */ -#define MAGIC_NO_CHECK_CDF 0x040000 /* Don't check for cdf files */ -#define MAGIC_NO_CHECK_TOKENS 0x100000 /* Don't check tokens */ -#define MAGIC_NO_CHECK_ENCODING 0x200000 /* Don't check text encodings */ - -/* No built-in tests; only consult the magic file */ -#define MAGIC_NO_CHECK_BUILTIN ( \ - MAGIC_NO_CHECK_COMPRESS | \ - MAGIC_NO_CHECK_TAR | \ -/* MAGIC_NO_CHECK_SOFT | */ \ - MAGIC_NO_CHECK_APPTYPE | \ - MAGIC_NO_CHECK_ELF | \ - MAGIC_NO_CHECK_TEXT | \ - MAGIC_NO_CHECK_CDF | \ - MAGIC_NO_CHECK_TOKENS | \ - MAGIC_NO_CHECK_ENCODING | \ - 0 \ -) - -/* Defined for backwards compatibility (renamed) */ -#define MAGIC_NO_CHECK_ASCII MAGIC_NO_CHECK_TEXT - -/* Defined for backwards compatibility; do nothing */ -#define MAGIC_NO_CHECK_FORTRAN 0x000000 /* Don't check ascii/fortran */ -#define MAGIC_NO_CHECK_TROFF 0x000000 /* Don't check ascii/troff */ - -#define MAGIC_VERSION 521 /* This implementation */ - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct magic_set *magic_t; -magic_t magic_open(int); -void magic_close(magic_t); - -const char *magic_getpath(const char *, int); -const char *magic_file(magic_t, const char *); -const char *magic_descriptor(magic_t, int); -const char *magic_buffer(magic_t, const void *, size_t); - -const char *magic_error(magic_t); -int magic_setflags(magic_t, int); - -int magic_version(void); -int magic_load(magic_t, const char *); -int magic_load_buffers(magic_t, void **, size_t *, size_t); - -int magic_compile(magic_t, const char *); -int magic_check(magic_t, const char *); -int magic_list(magic_t, const char *); -int magic_errno(magic_t); - -#define MAGIC_PARAM_INDIR_MAX 0 -#define MAGIC_PARAM_NAME_MAX 1 -#define MAGIC_PARAM_ELF_PHNUM_MAX 2 -#define MAGIC_PARAM_ELF_SHNUM_MAX 3 -#define MAGIC_PARAM_ELF_NOTES_MAX 4 - -int magic_setparam(magic_t, int, const void *); -int magic_getparam(magic_t, int, void *); - -#ifdef __cplusplus -}; -#endif - -#endif /* _MAGIC_H */ diff --git a/src/magic.h.in b/src/magic.h.in index 0d4c5ce..1134bdc 100644 --- a/src/magic.h.in +++ b/src/magic.h.in @@ -29,30 +29,35 @@ #include <sys/types.h> -#define MAGIC_NONE 0x000000 /* No flags */ -#define MAGIC_DEBUG 0x000001 /* Turn on debugging */ -#define MAGIC_SYMLINK 0x000002 /* Follow symlinks */ -#define MAGIC_COMPRESS 0x000004 /* Check inside compressed files */ -#define MAGIC_DEVICES 0x000008 /* Look at the contents of devices */ -#define MAGIC_MIME_TYPE 0x000010 /* Return the MIME type */ -#define MAGIC_CONTINUE 0x000020 /* Return all matches */ -#define MAGIC_CHECK 0x000040 /* Print warnings to stderr */ -#define MAGIC_PRESERVE_ATIME 0x000080 /* Restore access time on exit */ -#define MAGIC_RAW 0x000100 /* Don't translate unprintable chars */ -#define MAGIC_ERROR 0x000200 /* Handle ENOENT etc as real errors */ -#define MAGIC_MIME_ENCODING 0x000400 /* Return the MIME encoding */ +#define MAGIC_NONE 0x0000000 /* No flags */ +#define MAGIC_DEBUG 0x0000001 /* Turn on debugging */ +#define MAGIC_SYMLINK 0x0000002 /* Follow symlinks */ +#define MAGIC_COMPRESS 0x0000004 /* Check inside compressed files */ +#define MAGIC_DEVICES 0x0000008 /* Look at the contents of devices */ +#define MAGIC_MIME_TYPE 0x0000010 /* Return the MIME type */ +#define MAGIC_CONTINUE 0x0000020 /* Return all matches */ +#define MAGIC_CHECK 0x0000040 /* Print warnings to stderr */ +#define MAGIC_PRESERVE_ATIME 0x0000080 /* Restore access time on exit */ +#define MAGIC_RAW 0x0000100 /* Don't convert unprintable chars */ +#define MAGIC_ERROR 0x0000200 /* Handle ENOENT etc as real errors */ +#define MAGIC_MIME_ENCODING 0x0000400 /* Return the MIME encoding */ #define MAGIC_MIME (MAGIC_MIME_TYPE|MAGIC_MIME_ENCODING) -#define MAGIC_APPLE 0x000800 /* Return the Apple creator and type */ - -#define MAGIC_NO_CHECK_COMPRESS 0x001000 /* Don't check for compressed files */ -#define MAGIC_NO_CHECK_TAR 0x002000 /* Don't check for tar files */ -#define MAGIC_NO_CHECK_SOFT 0x004000 /* Don't check magic entries */ -#define MAGIC_NO_CHECK_APPTYPE 0x008000 /* Don't check application type */ -#define MAGIC_NO_CHECK_ELF 0x010000 /* Don't check for elf details */ -#define MAGIC_NO_CHECK_TEXT 0x020000 /* Don't check for text files */ -#define MAGIC_NO_CHECK_CDF 0x040000 /* Don't check for cdf files */ -#define MAGIC_NO_CHECK_TOKENS 0x100000 /* Don't check tokens */ -#define MAGIC_NO_CHECK_ENCODING 0x200000 /* Don't check text encodings */ +#define MAGIC_APPLE 0x0000800 /* Return the Apple creator/type */ +#define MAGIC_EXTENSION 0x1000000 /* Return a /-separated list of + * extensions */ +#define MAGIC_COMPRESS_TRANSP 0x2000000 /* Check inside compressed files + * but not report compression */ +#define MAGIC_NODESC (MAGIC_EXTENSION|MAGIC_MIME|MAGIC_APPLE) + +#define MAGIC_NO_CHECK_COMPRESS 0x0001000 /* Don't check for compressed files */ +#define MAGIC_NO_CHECK_TAR 0x0002000 /* Don't check for tar files */ +#define MAGIC_NO_CHECK_SOFT 0x0004000 /* Don't check magic entries */ +#define MAGIC_NO_CHECK_APPTYPE 0x0008000 /* Don't check application type */ +#define MAGIC_NO_CHECK_ELF 0x0010000 /* Don't check for elf details */ +#define MAGIC_NO_CHECK_TEXT 0x0020000 /* Don't check for text files */ +#define MAGIC_NO_CHECK_CDF 0x0040000 /* Don't check for cdf files */ +#define MAGIC_NO_CHECK_TOKENS 0x0100000 /* Don't check tokens */ +#define MAGIC_NO_CHECK_ENCODING 0x0200000 /* Don't check text encodings */ /* No built-in tests; only consult the magic file */ #define MAGIC_NO_CHECK_BUILTIN ( \ @@ -68,6 +73,35 @@ 0 \ ) +#define MAGIC_SNPRINTB "\177\020\ +b\0debug\0\ +b\1symlink\0\ +b\2compress\0\ +b\3devices\0\ +b\4mime_type\0\ +b\5continue\0\ +b\6check\0\ +b\7preserve_atime\0\ +b\10raw\0\ +b\11error\0\ +b\12mime_encoding\0\ +b\13apple\0\ +b\14no_check_compress\0\ +b\15no_check_tar\0\ +b\16no_check_soft\0\ +b\17no_check_sapptype\0\ +b\20no_check_elf\0\ +b\21no_check_text\0\ +b\22no_check_cdf\0\ +b\23no_check_reserved0\0\ +b\24no_check_tokens\0\ +b\25no_check_encoding\0\ +b\26no_check_reserved1\0\ +b\27no_check_reserved2\0\ +b\30extension\0\ +b\31transp_compression\0\ +" + /* Defined for backwards compatibility (renamed) */ #define MAGIC_NO_CHECK_ASCII MAGIC_NO_CHECK_TEXT @@ -92,6 +126,7 @@ const char *magic_descriptor(magic_t, int); const char *magic_buffer(magic_t, const void *, size_t); const char *magic_error(magic_t); +int magic_getflags(magic_t); int magic_setflags(magic_t, int); int magic_version(void); @@ -108,6 +143,8 @@ int magic_errno(magic_t); #define MAGIC_PARAM_ELF_PHNUM_MAX 2 #define MAGIC_PARAM_ELF_SHNUM_MAX 3 #define MAGIC_PARAM_ELF_NOTES_MAX 4 +#define MAGIC_PARAM_REGEX_MAX 5 +#define MAGIC_PARAM_BYTES_MAX 6 int magic_setparam(magic_t, int, const void *); int magic_getparam(magic_t, int, void *); diff --git a/src/print.c b/src/print.c index fa81798..0b91863 100644 --- a/src/print.c +++ b/src/print.c @@ -32,7 +32,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: print.c,v 1.76 2013/02/26 18:25:00 christos Exp $") +FILE_RCSID("@(#)$File: print.c,v 1.82 2017/02/10 18:14:01 christos Exp $") #endif /* lint */ #include <string.h> @@ -156,25 +156,26 @@ file_mdump(struct magic *m) case FILE_BEDATE: case FILE_MEDATE: (void)fprintf(stderr, "%s,", - file_fmttime(m->value.l, FILE_T_LOCAL, tbuf)); + file_fmttime(m->value.l, 0, tbuf)); break; case FILE_LDATE: case FILE_LELDATE: case FILE_BELDATE: case FILE_MELDATE: (void)fprintf(stderr, "%s,", - file_fmttime(m->value.l, 0, tbuf)); + file_fmttime(m->value.l, FILE_T_LOCAL, tbuf)); + break; case FILE_QDATE: case FILE_LEQDATE: case FILE_BEQDATE: (void)fprintf(stderr, "%s,", - file_fmttime(m->value.q, FILE_T_LOCAL, tbuf)); + file_fmttime(m->value.q, 0, tbuf)); break; case FILE_QLDATE: case FILE_LEQLDATE: case FILE_BEQLDATE: (void)fprintf(stderr, "%s,", - file_fmttime(m->value.q, 0, tbuf)); + file_fmttime(m->value.q, FILE_T_LOCAL, tbuf)); break; case FILE_QWDATE: case FILE_LEQWDATE: @@ -197,6 +198,7 @@ file_mdump(struct magic *m) break; case FILE_USE: case FILE_NAME: + case FILE_DER: (void) fprintf(stderr, "'%s'", m->value.s); break; default: @@ -231,40 +233,27 @@ protected const char * file_fmttime(uint64_t v, int flags, char *buf) { char *pp; - time_t t = (time_t)v; - struct tm *tm; + time_t t; + struct tm *tm, tmz; if (flags & FILE_T_WINDOWS) { struct timespec ts; - cdf_timestamp_to_timespec(&ts, t); + cdf_timestamp_to_timespec(&ts, CAST(cdf_timestamp_t, v)); t = ts.tv_sec; + } else { + // XXX: perhaps detect and print something if overflow + // on 32 bit time_t? + t = (time_t)v; } if (flags & FILE_T_LOCAL) { - pp = ctime_r(&t, buf); + tm = localtime_r(&t, &tmz); } else { -#ifndef HAVE_DAYLIGHT - private int daylight = 0; -#ifdef HAVE_TM_ISDST - private time_t now = (time_t)0; - - if (now == (time_t)0) { - struct tm *tm1; - (void)time(&now); - tm1 = localtime(&now); - if (tm1 == NULL) - goto out; - daylight = tm1->tm_isdst; - } -#endif /* HAVE_TM_ISDST */ -#endif /* HAVE_DAYLIGHT */ - if (daylight) - t += 3600; - tm = gmtime(&t); - if (tm == NULL) - goto out; - pp = asctime_r(tm, buf); + tm = gmtime_r(&t, &tmz); } + if (tm == NULL) + goto out; + pp = asctime_r(tm, buf); if (pp == NULL) goto out; diff --git a/src/readcdf.c b/src/readcdf.c index 635a926..80c8d26 100644 --- a/src/readcdf.c +++ b/src/readcdf.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2008 Christos Zoulas + * Copyright (c) 2008, 2016 Christos Zoulas * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,7 +26,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: readcdf.c,v 1.49 2014/12/04 15:56:46 christos Exp $") +FILE_RCSID("@(#)$File: readcdf.c,v 1.65 2017/04/08 20:58:03 christos Exp $") #endif #include <assert.h> @@ -39,6 +39,10 @@ FILE_RCSID("@(#)$File: readcdf.c,v 1.49 2014/12/04 15:56:46 christos Exp $") #include "cdf.h" #include "magic.h" +#ifndef __arraycount +#define __arraycount(a) (sizeof(a) / sizeof(a[0])) +#endif + #define NOTMIME(ms) (((ms)->flags & MAGIC_MIME) == 0) static const struct nv { @@ -56,12 +60,16 @@ static const struct nv { { "Windows Installer", "vnd.ms-msi", }, { NULL, NULL, }, }, name2mime[] = { + { "Book", "vnd.ms-excel", }, + { "Workbook", "vnd.ms-excel", }, { "WordDocument", "msword", }, { "PowerPoint", "vnd.ms-powerpoint", }, { "DigitalSignature", "vnd.ms-msi", }, { NULL, NULL, }, }, name2desc[] = { - { "WordDocument", "Microsoft Office Word",}, + { "Book", "Microsoft Excel", }, + { "Workbook", "Microsoft Excel", }, + { "WordDocument", "Microsoft Word", }, { "PowerPoint", "Microsoft PowerPoint", }, { "DigitalSignature", "Microsoft Installer", }, { NULL, NULL, }, @@ -96,6 +104,10 @@ cdf_clsid_to_mime(const uint64_t clsid[2], const struct cv *cv) if (clsid[0] == cv[i].clsid[0] && clsid[1] == cv[i].clsid[1]) return cv[i].mime; } +#ifdef CDF_DEBUG + fprintf(stderr, "unknown mime %" PRIx64 ", %" PRIx64 "\n", clsid[0], + clsid[1]); +#endif return NULL; } @@ -111,15 +123,22 @@ cdf_app_to_mime(const char *vbuf, const struct nv *nv) assert(c_lc_ctype != NULL); old_lc_ctype = uselocale(c_lc_ctype); assert(old_lc_ctype != NULL); +#else + char *old_lc_ctype = setlocale(LC_CTYPE, "C"); #endif for (i = 0; nv[i].pattern != NULL; i++) if (strcasestr(vbuf, nv[i].pattern) != NULL) { rv = nv[i].mime; break; } +#ifdef CDF_DEBUG + fprintf(stderr, "unknown app %s\n", vbuf); +#endif #ifdef USE_C_LOCALE (void)uselocale(old_lc_ctype); freelocale(c_lc_ctype); +#else + setlocale(LC_CTYPE, old_lc_ctype); #endif return rv; } @@ -133,7 +152,7 @@ cdf_file_property_info(struct magic_set *ms, const cdf_property_info_t *info, struct timespec ts; char buf[64]; const char *str = NULL; - const char *s; + const char *s, *e; int len; if (!NOTMIME(ms) && root_storage) @@ -180,7 +199,9 @@ cdf_file_property_info(struct magic_set *ms, const cdf_property_info_t *info, if (info[i].pi_type == CDF_LENGTH32_WSTRING) k++; s = info[i].pi_str.s_buf; - for (j = 0; j < sizeof(vbuf) && len--; s += k) { + e = info[i].pi_str.s_buf + len; + for (j = 0; s < e && j < sizeof(vbuf) + && len--; s += k) { if (*s == '\0') break; if (isprint((unsigned char)*s)) @@ -343,6 +364,178 @@ format_clsid(char *buf, size_t len, const uint64_t uuid[2]) { } #endif +private int +cdf_file_catalog_info(struct magic_set *ms, const cdf_info_t *info, + const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat, + const cdf_stream_t *sst, const cdf_dir_t *dir, cdf_stream_t *scn) +{ + int i; + + if ((i = cdf_read_user_stream(info, h, sat, ssat, sst, + dir, "Catalog", scn)) == -1) + return i; +#ifdef CDF_DEBUG + cdf_dump_catalog(h, scn); +#endif + if ((i = cdf_file_catalog(ms, h, scn)) == -1) + return -1; + return i; +} + +private int +cdf_check_summary_info(struct magic_set *ms, const cdf_info_t *info, + const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat, + const cdf_stream_t *sst, const cdf_dir_t *dir, cdf_stream_t *scn, + const cdf_directory_t *root_storage, const char **expn) +{ + int i; + const char *str = NULL; + cdf_directory_t *d; + char name[__arraycount(d->d_name)]; + size_t j, k; + +#ifdef CDF_DEBUG + cdf_dump_summary_info(h, scn); +#endif + if ((i = cdf_file_summary_info(ms, h, scn, root_storage)) < 0) { + *expn = "Can't expand summary_info"; + return i; + } + if (i == 1) + return i; + for (j = 0; str == NULL && j < dir->dir_len; j++) { + d = &dir->dir_tab[j]; + for (k = 0; k < sizeof(name); k++) + name[k] = (char)cdf_tole2(d->d_name[k]); + str = cdf_app_to_mime(name, + NOTMIME(ms) ? name2desc : name2mime); + } + if (NOTMIME(ms)) { + if (str != NULL) { + if (file_printf(ms, "%s", str) == -1) + return -1; + i = 1; + } + } else { + if (str == NULL) + str = "vnd.ms-office"; + if (file_printf(ms, "application/%s", str) == -1) + return -1; + i = 1; + } + if (i <= 0) { + i = cdf_file_catalog_info(ms, info, h, sat, ssat, sst, + dir, scn); + } + return i; +} + +private struct sinfo { + const char *name; + const char *mime; + const char *sections[5]; + const int types[5]; +} sectioninfo[] = { + { "Encrypted", "encrypted", + { + "EncryptedPackage", "EncryptedSummary", + NULL, NULL, NULL, + }, + { + CDF_DIR_TYPE_USER_STREAM, + CDF_DIR_TYPE_USER_STREAM, + 0, 0, 0, + + }, + }, + { "QuickBooks", "quickbooks", + { +#if 0 + "TaxForms", "PDFTaxForms", "modulesInBackup", +#endif + "mfbu_header", NULL, NULL, NULL, NULL, + }, + { +#if 0 + CDF_DIR_TYPE_USER_STORAGE, + CDF_DIR_TYPE_USER_STORAGE, + CDF_DIR_TYPE_USER_STREAM, +#endif + CDF_DIR_TYPE_USER_STREAM, + 0, 0, 0, 0 + }, + }, + { "Microsoft Excel", "vnd.ms-excel", + { + "Book", "Workbook", NULL, NULL, NULL, + }, + { + CDF_DIR_TYPE_USER_STREAM, + CDF_DIR_TYPE_USER_STREAM, + 0, 0, 0, + }, + }, + { "Microsoft Word", "msword", + { + "WordDocument", NULL, NULL, NULL, NULL, + }, + { + CDF_DIR_TYPE_USER_STREAM, + 0, 0, 0, 0, + }, + }, + { "Microsoft PowerPoint", "vnd.ms-powerpoint", + { + "PowerPoint", NULL, NULL, NULL, NULL, + }, + { + CDF_DIR_TYPE_USER_STREAM, + 0, 0, 0, 0, + }, + }, + { "Microsoft Outlook Message", "vnd.ms-outlook", + { + "__properties_version1.0", + "__recip_version1.0_#00000000", + NULL, NULL, NULL, + }, + { + CDF_DIR_TYPE_USER_STREAM, + CDF_DIR_TYPE_USER_STORAGE, + 0, 0, 0, + }, + }, +}; + +private int +cdf_file_dir_info(struct magic_set *ms, const cdf_dir_t *dir) +{ + size_t sd, j; + + for (sd = 0; sd < __arraycount(sectioninfo); sd++) { + const struct sinfo *si = §ioninfo[sd]; + for (j = 0; si->sections[j]; j++) { + if (cdf_find_stream(dir, si->sections[j], si->types[j]) + > 0) + break; +#ifdef CDF_DEBUG + fprintf(stderr, "Can't read %s\n", si->sections[j]); +#endif + } + if (si->sections[j] == NULL) + continue; + if (NOTMIME(ms)) { + if (file_printf(ms, "CDFV2 %s", si->name) == -1) + return -1; + } else { + if (file_printf(ms, "application/%s", si->mime) == -1) + return -1; + } + return 1; + } + return -1; +} + protected int file_trycdf(struct magic_set *ms, int fd, const unsigned char *buf, size_t nbytes) @@ -354,13 +547,13 @@ file_trycdf(struct magic_set *ms, int fd, const unsigned char *buf, cdf_dir_t dir; int i; const char *expn = ""; - const char *corrupt = "corrupt: "; const cdf_directory_t *root_storage; + scn.sst_tab = NULL; info.i_fd = fd; info.i_buf = buf; info.i_len = nbytes; - if (ms->flags & MAGIC_APPLE) + if (ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION)) return 0; if (cdf_read_header(&info, &h) == -1) return 0; @@ -412,7 +605,7 @@ file_trycdf(struct magic_set *ms, int fd, const unsigned char *buf, if ((i = cdf_read_user_stream(&info, &h, &sat, &ssat, &sst, &dir, "FileHeader", &scn)) != -1) { #define HWP5_SIGNATURE "HWP Document File" - if (scn.sst_dirlen >= sizeof(HWP5_SIGNATURE) - 1 + if (scn.sst_len * scn.sst_ss >= sizeof(HWP5_SIGNATURE) - 1 && memcmp(scn.sst_tab, HWP5_SIGNATURE, sizeof(HWP5_SIGNATURE) - 1) == 0) { if (NOTMIME(ms)) { @@ -426,76 +619,39 @@ file_trycdf(struct magic_set *ms, int fd, const unsigned char *buf, i = 1; goto out5; } else { - free(scn.sst_tab); - scn.sst_tab = NULL; - scn.sst_len = 0; - scn.sst_dirlen = 0; + cdf_zero_stream(&scn); } } if ((i = cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir, &scn)) == -1) { - if (errno == ESRCH) { - if ((i = cdf_read_catalog(&info, &h, &sat, &ssat, &sst, - &dir, &scn)) == -1) { - corrupt = expn; - if ((i = cdf_read_encrypted_package(&info, &h, - &sat, &ssat, &sst, &dir, &scn)) == -1) - expn = "No summary info"; - else { - expn = "Encrypted"; - i = -1; - } - goto out4; - } -#ifdef CDF_DEBUG - cdf_dump_catalog(&h, &scn); -#endif - if ((i = cdf_file_catalog(ms, &h, &scn)) - < 0) - expn = "Can't expand catalog"; - } else { + if (errno != ESRCH) { expn = "Cannot read summary info"; - } - goto out4; - } -#ifdef CDF_DEBUG - cdf_dump_summary_info(&h, &scn); -#endif - if ((i = cdf_file_summary_info(ms, &h, &scn, root_storage)) < 0) - expn = "Can't expand summary_info"; - - if (i == 0) { - const char *str = NULL; - cdf_directory_t *d; - char name[__arraycount(d->d_name)]; - size_t j, k; - - for (j = 0; str == NULL && j < dir.dir_len; j++) { - d = &dir.dir_tab[j]; - for (k = 0; k < sizeof(name); k++) - name[k] = (char)cdf_tole2(d->d_name[k]); - str = cdf_app_to_mime(name, - NOTMIME(ms) ? name2desc : name2mime); } - if (NOTMIME(ms)) { - if (str != NULL) { - if (file_printf(ms, "%s", str) == -1) - return -1; - i = 1; + } else { + i = cdf_check_summary_info(ms, &info, &h, + &sat, &ssat, &sst, &dir, &scn, root_storage, &expn); + cdf_zero_stream(&scn); + } + if (i <= 0) { + if ((i = cdf_read_doc_summary_info(&info, &h, &sat, &ssat, + &sst, &dir, &scn)) == -1) { + if (errno != ESRCH) { + expn = "Cannot read summary info"; } } else { - if (str == NULL) - str = "vnd.ms-office"; - if (file_printf(ms, "application/%s", str) == -1) - return -1; - i = 1; + i = cdf_check_summary_info(ms, &info, &h, &sat, &ssat, + &sst, &dir, &scn, root_storage, &expn); } } + if (i <= 0) { + i = cdf_file_dir_info(ms, &dir); + if (i < 0) + expn = "Cannot read section info"; + } out5: - free(scn.sst_tab); -out4: - free(sst.sst_tab); + cdf_zero_stream(&scn); + cdf_zero_stream(&sst); out3: free(dir.dir_tab); out2: @@ -509,11 +665,10 @@ out0: "Composite Document File V2 Document") == -1) return -1; if (*expn) - if (file_printf(ms, ", %s%s", corrupt, expn) == -1) + if (file_printf(ms, ", %s", expn) == -1) return -1; } else { - if (file_printf(ms, "application/CDFV2-%s", - *corrupt ? "corrupt" : "encrypted") == -1) + if (file_printf(ms, "application/CDFV2") == -1) return -1; } i = 1; diff --git a/src/readelf.c b/src/readelf.c index d159620..5f425c9 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -27,7 +27,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: readelf.c,v 1.117 2014/12/16 23:29:42 christos Exp $") +FILE_RCSID("@(#)$File: readelf.c,v 1.138 2017/08/27 07:55:02 christos Exp $") #endif #ifdef BUILTIN_ELF @@ -50,7 +50,7 @@ private int dophn_exec(struct magic_set *, int, int, int, off_t, int, size_t, private int doshn(struct magic_set *, int, int, int, off_t, int, size_t, off_t, int, int, int *, uint16_t *); private size_t donote(struct magic_set *, void *, size_t, size_t, int, - int, size_t, int *, uint16_t *); + int, size_t, int *, uint16_t *, int, off_t, int, off_t); #define ELF_ALIGN(a) ((((a) + align - 1) / align) * align) @@ -177,6 +177,11 @@ getu64(int swap, uint64_t value) elf_getu32(swap, ph32.p_align) : 4) \ : (off_t) (ph64.p_align ? \ elf_getu64(swap, ph64.p_align) : 4))) +#define xph_vaddr (size_t)((clazz == ELFCLASS32 \ + ? (off_t) (ph32.p_vaddr ? \ + elf_getu32(swap, ph32.p_vaddr) : 4) \ + : (off_t) (ph64.p_vaddr ? \ + elf_getu64(swap, ph64.p_vaddr) : 4))) #define xph_filesz (size_t)((clazz == ELFCLASS32 \ ? elf_getu32(swap, ph32.p_filesz) \ : elf_getu64(swap, ph64.p_filesz))) @@ -187,8 +192,8 @@ getu64(int swap, uint64_t value) ? elf_getu32(swap, ph32.p_memsz) \ : elf_getu64(swap, ph64.p_memsz))) #define xnh_sizeof (clazz == ELFCLASS32 \ - ? sizeof nh32 \ - : sizeof nh64) + ? sizeof(nh32) \ + : sizeof(nh64)) #define xnh_type (clazz == ELFCLASS32 \ ? elf_getu32(swap, nh32.n_type) \ : elf_getu32(swap, nh64.n_type)) @@ -213,6 +218,18 @@ getu64(int swap, uint64_t value) #define xcap_val (clazz == ELFCLASS32 \ ? elf_getu32(swap, cap32.c_un.c_val) \ : elf_getu64(swap, cap64.c_un.c_val)) +#define xauxv_addr (clazz == ELFCLASS32 \ + ? (void *)&auxv32 \ + : (void *)&auxv64) +#define xauxv_sizeof (clazz == ELFCLASS32 \ + ? sizeof(auxv32) \ + : sizeof(auxv64)) +#define xauxv_type (clazz == ELFCLASS32 \ + ? elf_getu32(swap, auxv32.a_type) \ + : elf_getu64(swap, auxv64.a_type)) +#define xauxv_val (clazz == ELFCLASS32 \ + ? elf_getu32(swap, auxv32.a_v) \ + : elf_getu64(swap, auxv64.a_v)) #ifdef ELFCORE /* @@ -293,15 +310,18 @@ private const char os_style_names[][8] = { "NetBSD", }; -#define FLAGS_DID_CORE 0x001 -#define FLAGS_DID_OS_NOTE 0x002 -#define FLAGS_DID_BUILD_ID 0x004 -#define FLAGS_DID_CORE_STYLE 0x008 -#define FLAGS_DID_NETBSD_PAX 0x010 -#define FLAGS_DID_NETBSD_MARCH 0x020 -#define FLAGS_DID_NETBSD_CMODEL 0x040 -#define FLAGS_DID_NETBSD_UNKNOWN 0x080 -#define FLAGS_IS_CORE 0x100 +#define FLAGS_CORE_STYLE 0x003 + +#define FLAGS_DID_CORE 0x004 +#define FLAGS_DID_OS_NOTE 0x008 +#define FLAGS_DID_BUILD_ID 0x010 +#define FLAGS_DID_CORE_STYLE 0x020 +#define FLAGS_DID_NETBSD_PAX 0x040 +#define FLAGS_DID_NETBSD_MARCH 0x080 +#define FLAGS_DID_NETBSD_CMODEL 0x100 +#define FLAGS_DID_NETBSD_UNKNOWN 0x200 +#define FLAGS_IS_CORE 0x400 +#define FLAGS_DID_AUXV 0x800 private int dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off, @@ -312,6 +332,8 @@ dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off, size_t offset, len; unsigned char nbuf[BUFSIZ]; ssize_t bufsize; + off_t ph_off = off; + int ph_num = num; if (size != xph_sizeof) { if (file_printf(ms, ", corrupted program header size") == -1) @@ -351,7 +373,8 @@ dophn_core(struct magic_set *ms, int clazz, int swap, int fd, off_t off, if (offset >= (size_t)bufsize) break; offset = donote(ms, nbuf, offset, (size_t)bufsize, - clazz, swap, 4, flags, notecount); + clazz, swap, 4, flags, notecount, fd, ph_off, + ph_num, fsize); if (offset == 0) break; @@ -482,17 +505,32 @@ do_note_freebsd_version(struct magic_set *ms, int swap, void *v) } private int +/*ARGSUSED*/ do_bid_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type, int swap __attribute__((__unused__)), uint32_t namesz, uint32_t descsz, size_t noff, size_t doff, int *flags) { if (namesz == 4 && strcmp((char *)&nbuf[noff], "GNU") == 0 && - type == NT_GNU_BUILD_ID && (descsz == 16 || descsz == 20)) { + type == NT_GNU_BUILD_ID && (descsz >= 4 && descsz <= 20)) { uint8_t desc[20]; + const char *btype; uint32_t i; *flags |= FLAGS_DID_BUILD_ID; - if (file_printf(ms, ", BuildID[%s]=", descsz == 16 ? "md5/uuid" : - "sha1") == -1) + switch (descsz) { + case 8: + btype = "xxHash"; + break; + case 16: + btype = "md5/uuid"; + break; + case 20: + btype = "sha1"; + break; + default: + btype = "unknown"; + break; + } + if (file_printf(ms, ", BuildID[%s]=", btype) == -1) return 1; (void)memcpy(desc, &nbuf[doff], descsz); for (i = 0; i < descsz; i++) @@ -622,7 +660,7 @@ do_pax_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type, return 1; for (i = 0; i < __arraycount(pax); i++) { - if (((1 << i) & desc) == 0) + if (((1 << (int)i) & desc) == 0) continue; if (file_printf(ms, "%s%s", did++ ? "," : "", pax[i]) == -1) @@ -673,32 +711,30 @@ do_core_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type, == -1) return 1; *flags |= FLAGS_DID_CORE_STYLE; + *flags |= os_style; } switch (os_style) { case OS_STYLE_NETBSD: if (type == NT_NETBSD_CORE_PROCINFO) { char sbuf[512]; - uint32_t signo; - /* - * Extract the program name. It is at - * offset 0x7c, and is up to 32-bytes, - * including the terminating NUL. - */ - if (file_printf(ms, ", from '%.31s'", + struct NetBSD_elfcore_procinfo pi; + memset(&pi, 0, sizeof(pi)); + memcpy(&pi, nbuf + doff, descsz); + + if (file_printf(ms, ", from '%.31s', pid=%u, uid=%u, " + "gid=%u, nlwps=%u, lwp=%u (signal %u/code %u)", file_printable(sbuf, sizeof(sbuf), - (const char *)&nbuf[doff + 0x7c])) == -1) - return 1; - - /* - * Extract the signal number. It is at - * offset 0x08. - */ - (void)memcpy(&signo, &nbuf[doff + 0x08], - sizeof(signo)); - if (file_printf(ms, " (signal %u)", - elf_getu32(swap, signo)) == -1) + CAST(char *, pi.cpi_name)), + elf_getu32(swap, pi.cpi_pid), + elf_getu32(swap, pi.cpi_euid), + elf_getu32(swap, pi.cpi_egid), + elf_getu32(swap, pi.cpi_nlwps), + elf_getu32(swap, pi.cpi_siglwp), + elf_getu32(swap, pi.cpi_signo), + elf_getu32(swap, pi.cpi_sigcode)) == -1) return 1; + *flags |= FLAGS_DID_CORE; return 1; } @@ -812,9 +848,177 @@ do_core_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type, return 0; } +private off_t +get_offset_from_virtaddr(struct magic_set *ms, int swap, int clazz, int fd, + off_t off, int num, off_t fsize, uint64_t virtaddr) +{ + Elf32_Phdr ph32; + Elf64_Phdr ph64; + + /* + * Loop through all the program headers and find the header with + * virtual address in which the "virtaddr" belongs to. + */ + for ( ; num; num--) { + if (pread(fd, xph_addr, xph_sizeof, off) < (ssize_t)xph_sizeof) { + file_badread(ms); + return -1; + } + off += xph_sizeof; + + if (fsize != SIZE_UNKNOWN && xph_offset > fsize) { + /* Perhaps warn here */ + continue; + } + + if (virtaddr >= xph_vaddr && virtaddr < xph_vaddr + xph_filesz) + return xph_offset + (virtaddr - xph_vaddr); + } + return 0; +} + +private size_t +get_string_on_virtaddr(struct magic_set *ms, + int swap, int clazz, int fd, off_t ph_off, int ph_num, + off_t fsize, uint64_t virtaddr, char *buf, ssize_t buflen) +{ + char *bptr; + off_t offset; + + if (buflen == 0) + return 0; + + offset = get_offset_from_virtaddr(ms, swap, clazz, fd, ph_off, ph_num, + fsize, virtaddr); + if ((buflen = pread(fd, buf, CAST(size_t, buflen), offset)) <= 0) { + file_badread(ms); + return 0; + } + + buf[buflen - 1] = '\0'; + + /* We expect only printable characters, so return if buffer contains + * non-printable character before the '\0' or just '\0'. */ + for (bptr = buf; *bptr && isprint((unsigned char)*bptr); bptr++) + continue; + if (*bptr != '\0') + return 0; + + return bptr - buf; +} + + +private int +do_auxv_note(struct magic_set *ms, unsigned char *nbuf, uint32_t type, + int swap, uint32_t namesz __attribute__((__unused__)), + uint32_t descsz __attribute__((__unused__)), + size_t noff __attribute__((__unused__)), size_t doff, + int *flags, size_t size __attribute__((__unused__)), int clazz, + int fd, off_t ph_off, int ph_num, off_t fsize) +{ +#ifdef ELFCORE + Aux32Info auxv32; + Aux64Info auxv64; + size_t elsize = xauxv_sizeof; + const char *tag; + int is_string; + size_t nval; + + if ((*flags & (FLAGS_IS_CORE|FLAGS_DID_CORE_STYLE)) != + (FLAGS_IS_CORE|FLAGS_DID_CORE_STYLE)) + return 0; + + switch (*flags & FLAGS_CORE_STYLE) { + case OS_STYLE_SVR4: + if (type != NT_AUXV) + return 0; + break; +#ifdef notyet + case OS_STYLE_NETBSD: + if (type != NT_NETBSD_CORE_AUXV) + return 0; + break; + case OS_STYLE_FREEBSD: + if (type != NT_FREEBSD_PROCSTAT_AUXV) + return 0; + break; +#endif + default: + return 0; + } + + *flags |= FLAGS_DID_AUXV; + + nval = 0; + for (size_t off = 0; off + elsize <= descsz; off += elsize) { + (void)memcpy(xauxv_addr, &nbuf[doff + off], xauxv_sizeof); + /* Limit processing to 50 vector entries to prevent DoS */ + if (nval++ >= 50) { + file_error(ms, 0, "Too many ELF Auxv elements"); + return 1; + } + + switch(xauxv_type) { + case AT_LINUX_EXECFN: + is_string = 1; + tag = "execfn"; + break; + case AT_LINUX_PLATFORM: + is_string = 1; + tag = "platform"; + break; + case AT_LINUX_UID: + is_string = 0; + tag = "real uid"; + break; + case AT_LINUX_GID: + is_string = 0; + tag = "real gid"; + break; + case AT_LINUX_EUID: + is_string = 0; + tag = "effective uid"; + break; + case AT_LINUX_EGID: + is_string = 0; + tag = "effective gid"; + break; + default: + is_string = 0; + tag = NULL; + break; + } + + if (tag == NULL) + continue; + + if (is_string) { + char buf[256]; + ssize_t buflen; + buflen = get_string_on_virtaddr(ms, swap, clazz, fd, + ph_off, ph_num, fsize, xauxv_val, buf, sizeof(buf)); + + if (buflen == 0) + continue; + + if (file_printf(ms, ", %s: '%s'", tag, buf) == -1) + return 0; + } else { + if (file_printf(ms, ", %s: %d", tag, (int) xauxv_val) + == -1) + return 0; + } + } + return 1; +#else + return 0; +#endif +} + private size_t donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size, - int clazz, int swap, size_t align, int *flags, uint16_t *notecount) + int clazz, int swap, size_t align, int *flags, uint16_t *notecount, + int fd, off_t ph_off, int ph_num, off_t fsize) { Elf32_Nhdr nh32; Elf64_Nhdr nh64; @@ -838,6 +1042,7 @@ donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size, namesz = xnh_namesz; descsz = xnh_descsz; + if ((namesz == 0) && (descsz == 0)) { /* * We're out of note headers. @@ -846,13 +1051,13 @@ donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size, } if (namesz & 0x80000000) { - (void)file_printf(ms, ", bad note name size 0x%lx", + (void)file_printf(ms, ", bad note name size %#lx", (unsigned long)namesz); return 0; } if (descsz & 0x80000000) { - (void)file_printf(ms, ", bad note description size 0x%lx", + (void)file_printf(ms, ", bad note description size %#lx", (unsigned long)descsz); return 0; } @@ -875,28 +1080,36 @@ donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size, return (offset >= size) ? offset : size; } + if ((*flags & FLAGS_DID_OS_NOTE) == 0) { if (do_os_note(ms, nbuf, xnh_type, swap, namesz, descsz, noff, doff, flags)) - return size; + return offset; } if ((*flags & FLAGS_DID_BUILD_ID) == 0) { if (do_bid_note(ms, nbuf, xnh_type, swap, namesz, descsz, noff, doff, flags)) - return size; + return offset; } if ((*flags & FLAGS_DID_NETBSD_PAX) == 0) { if (do_pax_note(ms, nbuf, xnh_type, swap, namesz, descsz, noff, doff, flags)) - return size; + return offset; } if ((*flags & FLAGS_DID_CORE) == 0) { if (do_core_note(ms, nbuf, xnh_type, swap, namesz, descsz, noff, doff, flags, size, clazz)) - return size; + return offset; + } + + if ((*flags & FLAGS_DID_AUXV) == 0) { + if (do_auxv_note(ms, nbuf, xnh_type, swap, + namesz, descsz, noff, doff, flags, size, clazz, + fd, ph_off, ph_num, fsize)) + return offset; } if (namesz == 7 && strcmp((char *)&nbuf[noff], "NetBSD") == 0) { @@ -904,32 +1117,32 @@ donote(struct magic_set *ms, void *vbuf, size_t offset, size_t size, descsz = 100; switch (xnh_type) { case NT_NETBSD_VERSION: - return size; + return offset; case NT_NETBSD_MARCH: if (*flags & FLAGS_DID_NETBSD_MARCH) - return size; + return offset; *flags |= FLAGS_DID_NETBSD_MARCH; if (file_printf(ms, ", compiled for: %.*s", (int)descsz, (const char *)&nbuf[doff]) == -1) - return size; + return offset; break; case NT_NETBSD_CMODEL: if (*flags & FLAGS_DID_NETBSD_CMODEL) - return size; + return offset; *flags |= FLAGS_DID_NETBSD_CMODEL; if (file_printf(ms, ", compiler model: %.*s", (int)descsz, (const char *)&nbuf[doff]) == -1) - return size; + return offset; break; default: if (*flags & FLAGS_DID_NETBSD_UNKNOWN) - return size; + return offset; *flags |= FLAGS_DID_NETBSD_UNKNOWN; if (file_printf(ms, ", note=%u", xnh_type) == -1) - return size; + return offset; break; } - return size; + return offset; } return offset; @@ -992,12 +1205,12 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, { Elf32_Shdr sh32; Elf64_Shdr sh64; - int stripped = 1; + int stripped = 1, has_debug_info = 0; size_t nbadcap = 0; void *nbuf; off_t noff, coff, name_off; - uint64_t cap_hw1 = 0; /* SunOS 5.x hardware capabilites */ - uint64_t cap_sf1 = 0; /* SunOS 5.x software capabilites */ + uint64_t cap_hw1 = 0; /* SunOS 5.x hardware capabilities */ + uint64_t cap_sf1 = 0; /* SunOS 5.x software capabilities */ char name[50]; ssize_t namesize; @@ -1008,9 +1221,11 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, } /* Read offset of name section to be able to read section names later */ - if (pread(fd, xsh_addr, xsh_sizeof, off + size * strtab) < (ssize_t)xsh_sizeof) { - file_badread(ms); - return -1; + if (pread(fd, xsh_addr, xsh_sizeof, CAST(off_t, (off + size * strtab))) + < (ssize_t)xsh_sizeof) { + if (file_printf(ms, ", missing section headers") == -1) + return -1; + return 0; } name_off = xsh_offset; @@ -1021,8 +1236,10 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, return -1; } name[namesize] = '\0'; - if (strcmp(name, ".debug_info") == 0) + if (strcmp(name, ".debug_info") == 0) { + has_debug_info = 1; stripped = 0; + } if (pread(fd, xsh_addr, xsh_sizeof, off) < (ssize_t)xsh_sizeof) { file_badread(ms); @@ -1046,15 +1263,28 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, break; } + /* Things we can determine when we seek */ switch (xsh_type) { case SHT_NOTE: + if ((uintmax_t)(xsh_size + xsh_offset) > + (uintmax_t)fsize) { + if (file_printf(ms, + ", note offset/size %#" INTMAX_T_FORMAT + "x+%#" INTMAX_T_FORMAT "x exceeds" + " file size %#" INTMAX_T_FORMAT "x", + (uintmax_t)xsh_offset, (uintmax_t)xsh_size, + (uintmax_t)fsize) == -1) + return -1; + return 0; + } if ((nbuf = malloc(xsh_size)) == NULL) { file_error(ms, errno, "Cannot allocate memory" " for note"); return -1; } - if (pread(fd, nbuf, xsh_size, xsh_offset) < (ssize_t)xsh_size) { + if (pread(fd, nbuf, xsh_size, xsh_offset) < + (ssize_t)xsh_size) { file_badread(ms); free(nbuf); return -1; @@ -1065,7 +1295,8 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, if (noff >= (off_t)xsh_size) break; noff = donote(ms, nbuf, (size_t)noff, - xsh_size, clazz, swap, 4, flags, notecount); + xsh_size, clazz, swap, 4, flags, notecount, + fd, 0, 0, 0); if (noff == 0) break; } @@ -1145,7 +1376,7 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, default: if (file_printf(ms, ", with unknown capability " - "0x%" INT64_T_FORMAT "x = 0x%" + "%#" INT64_T_FORMAT "x = %#" INT64_T_FORMAT "x", (unsigned long long)xcap_tag, (unsigned long long)xcap_val) == -1) @@ -1162,6 +1393,10 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, } } + if (has_debug_info) { + if (file_printf(ms, ", with debug_info") == -1) + return -1; + } if (file_printf(ms, ", %sstripped", stripped ? "" : "not ") == -1) return -1; if (cap_hw1) { @@ -1195,13 +1430,13 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, } if (cap_hw1) if (file_printf(ms, - " unknown hardware capability 0x%" + " unknown hardware capability %#" INT64_T_FORMAT "x", (unsigned long long)cap_hw1) == -1) return -1; } else { if (file_printf(ms, - " hardware capability 0x%" INT64_T_FORMAT "x", + " hardware capability %#" INT64_T_FORMAT "x", (unsigned long long)cap_hw1) == -1) return -1; } @@ -1217,7 +1452,7 @@ doshn(struct magic_set *ms, int clazz, int swap, int fd, off_t off, int num, cap_sf1 &= ~SF1_SUNW_MASK; if (cap_sf1) if (file_printf(ms, - ", with unknown software capability 0x%" + ", with unknown software capability %#" INT64_T_FORMAT "x", (unsigned long long)cap_sf1) == -1) return -1; @@ -1271,7 +1506,7 @@ dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off, if (((align = xph_align) & 0x80000000UL) != 0 || align < 4) { if (file_printf(ms, - ", invalid note alignment 0x%lx", + ", invalid note alignment %#lx", (unsigned long)align) == -1) return -1; align = 4; @@ -1314,7 +1549,7 @@ dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off, break; offset = donote(ms, nbuf, offset, (size_t)bufsize, clazz, swap, align, - flags, notecount); + flags, notecount, fd, 0, 0, 0); if (offset == 0) break; } @@ -1351,7 +1586,7 @@ file_tryelf(struct magic_set *ms, int fd, const unsigned char *buf, Elf64_Ehdr elf64hdr; uint16_t type, phnum, shnum, notecount; - if (ms->flags & (MAGIC_MIME|MAGIC_APPLE)) + if (ms->flags & (MAGIC_MIME|MAGIC_APPLE|MAGIC_EXTENSION)) return 0; /* * ELF executables have multiple section headers in arbitrary diff --git a/src/readelf.h b/src/readelf.h index 732b20c..ef880b9 100644 --- a/src/readelf.h +++ b/src/readelf.h @@ -54,6 +54,42 @@ typedef uint8_t Elf64_Char; #define EI_NIDENT 16 typedef struct { + Elf32_Word a_type; /* 32-bit id */ + Elf32_Word a_v; /* 32-bit id */ +} Aux32Info; + +typedef struct { + Elf64_Xword a_type; /* 64-bit id */ + Elf64_Xword a_v; /* 64-bit id */ +} Aux64Info; + +#define AT_NULL 0 /* end of vector */ +#define AT_IGNORE 1 /* entry should be ignored */ +#define AT_EXECFD 2 /* file descriptor of program */ +#define AT_PHDR 3 /* program headers for program */ +#define AT_PHENT 4 /* size of program header entry */ +#define AT_PHNUM 5 /* number of program headers */ +#define AT_PAGESZ 6 /* system page size */ +#define AT_BASE 7 /* base address of interpreter */ +#define AT_FLAGS 8 /* flags */ +#define AT_ENTRY 9 /* entry point of program */ +#define AT_LINUX_NOTELF 10 /* program is not ELF */ +#define AT_LINUX_UID 11 /* real uid */ +#define AT_LINUX_EUID 12 /* effective uid */ +#define AT_LINUX_GID 13 /* real gid */ +#define AT_LINUX_EGID 14 /* effective gid */ +#define AT_LINUX_PLATFORM 15 /* string identifying CPU for optimizations */ +#define AT_LINUX_HWCAP 16 /* arch dependent hints at CPU capabilities */ +#define AT_LINUX_CLKTCK 17 /* frequency at which times() increments */ +/* AT_* values 18 through 22 are reserved */ +#define AT_LINUX_SECURE 23 /* secure mode boolean */ +#define AT_LINUX_BASE_PLATFORM 24 /* string identifying real platform, may + * differ from AT_PLATFORM. */ +#define AT_LINUX_RANDOM 25 /* address of 16 random bytes */ +#define AT_LINUX_HWCAP2 26 /* extension of AT_HWCAP */ +#define AT_LINUX_EXECFN 31 /* filename of program */ + +typedef struct { Elf32_Char e_ident[EI_NIDENT]; Elf32_Half e_type; Elf32_Half e_machine; @@ -105,7 +141,7 @@ typedef struct { #define SHT_SYMTAB 2 #define SHT_NOTE 7 #define SHT_DYNSYM 11 -#define SHT_SUNW_cap 0x6ffffff5 /* SunOS 5.x hw/sw capabilites */ +#define SHT_SUNW_cap 0x6ffffff5 /* SunOS 5.x hw/sw capabilities */ /* elf type */ #define ELFDATANONE 0 /* e_ident[EI_DATA] */ @@ -194,6 +230,33 @@ typedef struct { } Elf64_Shdr; #define NT_NETBSD_CORE_PROCINFO 1 +#define NT_NETBSD_CORE_AUXV 2 + +struct NetBSD_elfcore_procinfo { + /* Version 1 fields start here. */ + uint32_t cpi_version; /* our version */ + uint32_t cpi_cpisize; /* sizeof(this struct) */ + uint32_t cpi_signo; /* killing signal */ + uint32_t cpi_sigcode; /* signal code */ + uint32_t cpi_sigpend[4]; /* pending signals */ + uint32_t cpi_sigmask[4]; /* blocked signals */ + uint32_t cpi_sigignore[4]; /* ignored signals */ + uint32_t cpi_sigcatch[4]; /* caught signals */ + int32_t cpi_pid; /* process ID */ + int32_t cpi_ppid; /* parent process ID */ + int32_t cpi_pgrp; /* process group ID */ + int32_t cpi_sid; /* session ID */ + uint32_t cpi_ruid; /* real user ID */ + uint32_t cpi_euid; /* effective user ID */ + uint32_t cpi_svuid; /* saved user ID */ + uint32_t cpi_rgid; /* real group ID */ + uint32_t cpi_egid; /* effective group ID */ + uint32_t cpi_svgid; /* saved group ID */ + uint32_t cpi_nlwps; /* number of LWPs */ + int8_t cpi_name[32]; /* copy of p->p_comm */ + /* Add version 2 fields below here. */ + int32_t cpi_siglwp; /* LWP target of killing signal */ +}; /* Note header in a PT_NOTE section */ typedef struct elf_note { @@ -292,6 +355,11 @@ typedef struct { */ #define NT_NETBSD_CMODEL 6 +/* + * FreeBSD specific notes + */ +#define NT_FREEBSD_PROCSTAT_AUXV 16 + #if !defined(ELFSIZE) && defined(ARCH_ELFSIZE) #define ELFSIZE ARCH_ELFSIZE #endif diff --git a/src/softmagic.c b/src/softmagic.c index 5e277e3..b9e9753 100644 --- a/src/softmagic.c +++ b/src/softmagic.c @@ -32,7 +32,7 @@ #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: softmagic.c,v 1.206 2015/01/01 17:07:34 christos Exp $") +FILE_RCSID("@(#)$File: softmagic.c,v 1.249 2017/06/19 18:30:25 christos Exp $") #endif /* lint */ #include "magic.h" @@ -41,28 +41,46 @@ FILE_RCSID("@(#)$File: softmagic.c,v 1.206 2015/01/01 17:07:34 christos Exp $") #include <ctype.h> #include <stdlib.h> #include <time.h> +#include "der.h" private int match(struct magic_set *, struct magic *, uint32_t, - const unsigned char *, size_t, size_t, int, int, int, uint16_t, + const unsigned char *, size_t, size_t, int, int, int, uint16_t *, uint16_t *, int *, int *, int *); private int mget(struct magic_set *, const unsigned char *, - struct magic *, size_t, size_t, unsigned int, int, int, int, uint16_t, + struct magic *, size_t, size_t, unsigned int, int, int, int, uint16_t *, uint16_t *, int *, int *, int *); private int magiccheck(struct magic_set *, struct magic *); private int32_t mprint(struct magic_set *, struct magic *); -private int32_t moffset(struct magic_set *, struct magic *); +private int moffset(struct magic_set *, struct magic *, size_t, int32_t *); private void mdebug(uint32_t, const char *, size_t); private int mcopy(struct magic_set *, union VALUETYPE *, int, int, const unsigned char *, uint32_t, size_t, struct magic *); private int mconvert(struct magic_set *, struct magic *, int); private int print_sep(struct magic_set *, int); -private int handle_annotation(struct magic_set *, struct magic *); -private void cvt_8(union VALUETYPE *, const struct magic *); -private void cvt_16(union VALUETYPE *, const struct magic *); -private void cvt_32(union VALUETYPE *, const struct magic *); -private void cvt_64(union VALUETYPE *, const struct magic *); - -#define OFFSET_OOB(n, o, i) ((n) < (o) || (i) > ((n) - (o))) +private int handle_annotation(struct magic_set *, struct magic *, int); +private int cvt_8(union VALUETYPE *, const struct magic *); +private int cvt_16(union VALUETYPE *, const struct magic *); +private int cvt_32(union VALUETYPE *, const struct magic *); +private int cvt_64(union VALUETYPE *, const struct magic *); + +#define OFFSET_OOB(n, o, i) ((n) < (uint32_t)(o) || (i) > ((n) - (o))) +#define BE64(p) (((uint64_t)(p)->hq[0]<<56)|((uint64_t)(p)->hq[1]<<48)| \ + ((uint64_t)(p)->hq[2]<<40)|((uint64_t)(p)->hq[3]<<32)| \ + ((uint64_t)(p)->hq[4]<<24)|((uint64_t)(p)->hq[5]<<16)| \ + ((uint64_t)(p)->hq[6]<<8)|((uint64_t)(p)->hq[7])) +#define LE64(p) (((uint64_t)(p)->hq[7]<<56)|((uint64_t)(p)->hq[6]<<48)| \ + ((uint64_t)(p)->hq[5]<<40)|((uint64_t)(p)->hq[4]<<32)| \ + ((uint64_t)(p)->hq[3]<<24)|((uint64_t)(p)->hq[2]<<16)| \ + ((uint64_t)(p)->hq[1]<<8)|((uint64_t)(p)->hq[0])) +#define LE32(p) (((uint32_t)(p)->hl[3]<<24)|((uint32_t)(p)->hl[2]<<16)| \ + ((uint32_t)(p)->hl[1]<<8)|((uint32_t)(p)->hl[0])) +#define BE32(p) (((uint32_t)(p)->hl[0]<<24)|((uint32_t)(p)->hl[1]<<16)| \ + ((uint32_t)(p)->hl[2]<<8)|((uint32_t)(p)->hl[3])) +#define ME32(p) (((uint32_t)(p)->hl[1]<<24)|((uint32_t)(p)->hl[0]<<16)| \ + ((uint32_t)(p)->hl[3]<<8)|((uint32_t)(p)->hl[2])) +#define BE16(p) (((uint16_t)(p)->hs[0]<<8)|((uint16_t)(p)->hs[1])) +#define LE16(p) (((uint16_t)(p)->hs[1]<<8)|((uint16_t)(p)->hs[0])) +#define SEXT(s,v,p) ((s)?(intmax_t)(int##v##_t)(p):(intmax_t)(uint##v##_t)(p)) /* * softmagic - lookup one file in parsed, in-memory copy of database @@ -71,20 +89,24 @@ private void cvt_64(union VALUETYPE *, const struct magic *); /*ARGSUSED1*/ /* nbytes passed for regularity, maybe need later */ protected int file_softmagic(struct magic_set *ms, const unsigned char *buf, size_t nbytes, - uint16_t indir_level, uint16_t *name_count, int mode, int text) + uint16_t *indir_count, uint16_t *name_count, int mode, int text) { struct mlist *ml; int rv, printed_something = 0, need_separator = 0; - uint16_t nc; + uint16_t nc, ic; if (name_count == NULL) { nc = 0; name_count = &nc; } + if (indir_count == NULL) { + ic = 0; + indir_count = ⁣ + } for (ml = ms->mlist[0]->next; ml != ms->mlist[0]; ml = ml->next) if ((rv = match(ms, ml->magic, ml->nmagic, buf, nbytes, 0, mode, - text, 0, indir_level, name_count, + text, 0, indir_count, name_count, &printed_something, &need_separator, NULL)) != 0) return rv; @@ -140,14 +162,14 @@ file_fmtcheck(struct magic_set *ms, const struct magic *m, const char *def, private int match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, const unsigned char *s, size_t nbytes, size_t offset, int mode, int text, - int flip, uint16_t indir_level, uint16_t *name_count, + int flip, uint16_t *indir_count, uint16_t *name_count, int *printed_something, int *need_separator, int *returnval) { uint32_t magindex = 0; unsigned int cont_level = 0; int returnvalv = 0, e; /* if a match is found it is set to 1*/ int firstline = 1; /* a flag to print X\n X\n- X */ - int print = (ms->flags & (MAGIC_MIME|MAGIC_APPLE)) == 0; + int print = (ms->flags & MAGIC_NODESC) == 0; if (returnval == NULL) returnval = &returnvalv; @@ -165,11 +187,12 @@ match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, ((text && (m->str_flags & FLT) == STRING_BINTEST) || (!text && (m->str_flags & FLT) == STRING_TEXTTEST))) || (m->flag & mode) != mode) { +flush: /* Skip sub-tests */ - while (magindex + 1 < nmagic && - magic[magindex + 1].cont_level != 0 && - ++magindex) - continue; + while (magindex < nmagic - 1 && + magic[magindex + 1].cont_level != 0) + magindex++; + cont_level = 0; continue; /* Skip to next top-level test*/ } @@ -178,7 +201,7 @@ match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, /* if main entry matches, print it... */ switch (mget(ms, s, m, nbytes, offset, cont_level, mode, text, - flip, indir_level, name_count, + flip, indir_count, name_count, printed_something, need_separator, returnval)) { case -1: return -1; @@ -206,18 +229,16 @@ match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, * main entry didn't match, * flush its continuations */ - while (magindex < nmagic - 1 && - magic[magindex + 1].cont_level != 0) - magindex++; - continue; + goto flush; } - if ((e = handle_annotation(ms, m)) != 0) { + if ((e = handle_annotation(ms, m, firstline)) != 0) { *need_separator = 1; *printed_something = 1; *returnval = 1; return e; } + /* * If we are going to print something, we'll need to print * a blank before we print something else. @@ -233,7 +254,13 @@ match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, if (print && mprint(ms, m) == -1) return -1; - ms->c.li[cont_level].off = moffset(ms, m); + switch (moffset(ms, m, nbytes, &ms->c.li[cont_level].off)) { + case -1: + case 0: + goto flush; + default: + break; + } /* and any continuations that match */ if (file_check_mem(ms, ++cont_level) == -1) @@ -267,7 +294,7 @@ match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, } #endif switch (mget(ms, s, m, nbytes, offset, cont_level, mode, - text, flip, indir_level, name_count, + text, flip, indir_count, name_count, printed_something, need_separator, returnval)) { case -1: return -1; @@ -302,7 +329,8 @@ match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, break; } else ms->c.li[cont_level].got_match = 1; - if ((e = handle_annotation(ms, m)) != 0) { + + if ((e = handle_annotation(ms, m, firstline)) != 0) { *need_separator = 1; *printed_something = 1; *returnval = 1; @@ -338,7 +366,16 @@ match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, if (print && mprint(ms, m) == -1) return -1; - ms->c.li[cont_level].off = moffset(ms, m); + switch (moffset(ms, m, nbytes, + &ms->c.li[cont_level].off)) { + case -1: + case 0: + flush = 1; + cont_level--; + break; + default: + break; + } if (*m->desc) *need_separator = 1; @@ -361,6 +398,7 @@ match(struct magic_set *ms, struct magic *magic, uint32_t nmagic, if ((ms->flags & MAGIC_CONTINUE) == 0 && *printed_something) { return *returnval; /* don't keep searching */ } + cont_level = 0; } return *returnval; /* This is hit if -k is set or there is no match */ } @@ -518,7 +556,7 @@ mprint(struct magic_set *ms, struct magic *m) t = ms->offset + strlen(str); if (*m->value.s == '\0') - str[strcspn(str, "\n")] = '\0'; + str[strcspn(str, "\r\n")] = '\0'; if (m->str_flags & STRING_TRIM) { char *last; @@ -547,7 +585,7 @@ mprint(struct magic_set *ms, struct magic *m) case FILE_LEDATE: case FILE_MEDATE: if (file_printf(ms, F(ms, m, "%s"), - file_fmttime(p->l + m->num_mask, FILE_T_LOCAL, tbuf)) == -1) + file_fmttime(p->l, 0, tbuf)) == -1) return -1; t = ms->offset + sizeof(uint32_t); break; @@ -557,7 +595,7 @@ mprint(struct magic_set *ms, struct magic *m) case FILE_LELDATE: case FILE_MELDATE: if (file_printf(ms, F(ms, m, "%s"), - file_fmttime(p->l + m->num_mask, 0, tbuf)) == -1) + file_fmttime(p->l, FILE_T_LOCAL, tbuf)) == -1) return -1; t = ms->offset + sizeof(uint32_t); break; @@ -566,7 +604,7 @@ mprint(struct magic_set *ms, struct magic *m) case FILE_BEQDATE: case FILE_LEQDATE: if (file_printf(ms, F(ms, m, "%s"), - file_fmttime(p->q + m->num_mask, FILE_T_LOCAL, tbuf)) == -1) + file_fmttime(p->q, 0, tbuf)) == -1) return -1; t = ms->offset + sizeof(uint64_t); break; @@ -575,7 +613,7 @@ mprint(struct magic_set *ms, struct magic *m) case FILE_BEQLDATE: case FILE_LEQLDATE: if (file_printf(ms, F(ms, m, "%s"), - file_fmttime(p->q + m->num_mask, 0, tbuf)) == -1) + file_fmttime(p->q, FILE_T_LOCAL, tbuf)) == -1) return -1; t = ms->offset + sizeof(uint64_t); break; @@ -584,14 +622,14 @@ mprint(struct magic_set *ms, struct magic *m) case FILE_BEQWDATE: case FILE_LEQWDATE: if (file_printf(ms, F(ms, m, "%s"), - file_fmttime(p->q + m->num_mask, FILE_T_WINDOWS, tbuf)) == -1) + file_fmttime(p->q, FILE_T_WINDOWS, tbuf)) == -1) return -1; t = ms->offset + sizeof(uint64_t); break; - case FILE_FLOAT: - case FILE_BEFLOAT: - case FILE_LEFLOAT: + case FILE_FLOAT: + case FILE_BEFLOAT: + case FILE_LEFLOAT: vf = p->f; switch (check_fmt(ms, m)) { case -1: @@ -609,9 +647,9 @@ mprint(struct magic_set *ms, struct magic *m) t = ms->offset + sizeof(float); break; - case FILE_DOUBLE: - case FILE_BEDOUBLE: - case FILE_LEDOUBLE: + case FILE_DOUBLE: + case FILE_BEDOUBLE: + case FILE_LEDOUBLE: vd = p->d; switch (check_fmt(ms, m)) { case -1: @@ -629,6 +667,7 @@ mprint(struct magic_set *ms, struct magic *m) t = ms->offset + sizeof(double); break; + case FILE_SEARCH: case FILE_REGEX: { char *cp; int rval; @@ -652,16 +691,6 @@ mprint(struct magic_set *ms, struct magic *m) break; } - case FILE_SEARCH: - if (file_printf(ms, F(ms, m, "%s"), - file_printable(sbuf, sizeof(sbuf), m->value.s)) == -1) - return -1; - if ((m->str_flags & REGEX_OFFSET_START)) - t = ms->search.offset; - else - t = ms->search.offset + m->vallen; - break; - case FILE_DEFAULT: case FILE_CLEAR: if (file_printf(ms, "%s", m->desc) == -1) @@ -674,7 +703,12 @@ mprint(struct magic_set *ms, struct magic *m) case FILE_NAME: t = ms->offset; break; - + case FILE_DER: + if (file_printf(ms, F(ms, m, "%s"), + file_printable(sbuf, sizeof(sbuf), ms->ms_value.s)) == -1) + return -1; + t = ms->offset; + break; default: file_magerror(ms, "invalid m->type (%d) in mprint()", m->type); return -1; @@ -682,100 +716,152 @@ mprint(struct magic_set *ms, struct magic *m) return (int32_t)t; } -private int32_t -moffset(struct magic_set *ms, struct magic *m) +private int +moffset(struct magic_set *ms, struct magic *m, size_t nbytes, int32_t *op) { + int32_t o; + switch (m->type) { case FILE_BYTE: - return CAST(int32_t, (ms->offset + sizeof(char))); + o = CAST(int32_t, (ms->offset + sizeof(char))); + break; case FILE_SHORT: case FILE_BESHORT: case FILE_LESHORT: - return CAST(int32_t, (ms->offset + sizeof(short))); + o = CAST(int32_t, (ms->offset + sizeof(short))); + break; case FILE_LONG: case FILE_BELONG: case FILE_LELONG: case FILE_MELONG: - return CAST(int32_t, (ms->offset + sizeof(int32_t))); + o = CAST(int32_t, (ms->offset + sizeof(int32_t))); + break; case FILE_QUAD: case FILE_BEQUAD: case FILE_LEQUAD: - return CAST(int32_t, (ms->offset + sizeof(int64_t))); + o = CAST(int32_t, (ms->offset + sizeof(int64_t))); + break; case FILE_STRING: case FILE_PSTRING: case FILE_BESTRING16: case FILE_LESTRING16: - if (m->reln == '=' || m->reln == '!') - return ms->offset + m->vallen; - else { + if (m->reln == '=' || m->reln == '!') { + o = ms->offset + m->vallen; + } else { union VALUETYPE *p = &ms->ms_value; - uint32_t t; if (*m->value.s == '\0') - p->s[strcspn(p->s, "\n")] = '\0'; - t = CAST(uint32_t, (ms->offset + strlen(p->s))); + p->s[strcspn(p->s, "\r\n")] = '\0'; + o = CAST(uint32_t, (ms->offset + strlen(p->s))); if (m->type == FILE_PSTRING) - t += (uint32_t)file_pstring_length_size(m); - return t; + o += (uint32_t)file_pstring_length_size(m); } + break; case FILE_DATE: case FILE_BEDATE: case FILE_LEDATE: case FILE_MEDATE: - return CAST(int32_t, (ms->offset + sizeof(uint32_t))); + o = CAST(int32_t, (ms->offset + sizeof(uint32_t))); + break; case FILE_LDATE: case FILE_BELDATE: case FILE_LELDATE: case FILE_MELDATE: - return CAST(int32_t, (ms->offset + sizeof(uint32_t))); + o = CAST(int32_t, (ms->offset + sizeof(uint32_t))); + break; case FILE_QDATE: case FILE_BEQDATE: case FILE_LEQDATE: - return CAST(int32_t, (ms->offset + sizeof(uint64_t))); + o = CAST(int32_t, (ms->offset + sizeof(uint64_t))); + break; case FILE_QLDATE: case FILE_BEQLDATE: case FILE_LEQLDATE: - return CAST(int32_t, (ms->offset + sizeof(uint64_t))); + o = CAST(int32_t, (ms->offset + sizeof(uint64_t))); + break; case FILE_FLOAT: case FILE_BEFLOAT: case FILE_LEFLOAT: - return CAST(int32_t, (ms->offset + sizeof(float))); + o = CAST(int32_t, (ms->offset + sizeof(float))); + break; case FILE_DOUBLE: case FILE_BEDOUBLE: case FILE_LEDOUBLE: - return CAST(int32_t, (ms->offset + sizeof(double))); + o = CAST(int32_t, (ms->offset + sizeof(double))); + break; case FILE_REGEX: if ((m->str_flags & REGEX_OFFSET_START) != 0) - return CAST(int32_t, ms->search.offset); + o = CAST(int32_t, ms->search.offset); else - return CAST(int32_t, (ms->search.offset + - ms->search.rm_len)); + o = CAST(int32_t, + (ms->search.offset + ms->search.rm_len)); + break; case FILE_SEARCH: if ((m->str_flags & REGEX_OFFSET_START) != 0) - return CAST(int32_t, ms->search.offset); + o = CAST(int32_t, ms->search.offset); else - return CAST(int32_t, (ms->search.offset + m->vallen)); + o = CAST(int32_t, (ms->search.offset + m->vallen)); + break; case FILE_CLEAR: case FILE_DEFAULT: case FILE_INDIRECT: - return ms->offset; + o = ms->offset; + break; + + case FILE_DER: + { + o = der_offs(ms, m, nbytes); + if (o == -1 || (size_t)o > nbytes) { + if ((ms->flags & MAGIC_DEBUG) != 0) { + (void)fprintf(stderr, + "Bad DER offset %d nbytes=%zu", + o, nbytes); + } + *op = 0; + return 0; + } + break; + } default: - return 0; + o = 0; + break; + } + + if ((size_t)o > nbytes) { +#if 0 + file_error(ms, 0, "Offset out of range %zu > %zu", + (size_t)o, nbytes); +#endif + return -1; } + *op = o; + return 1; +} + +private uint32_t +cvt_id3(struct magic_set *ms, uint32_t v) +{ + v = ((((v >> 0) & 0x7f) << 0) | + (((v >> 8) & 0x7f) << 7) | + (((v >> 16) & 0x7f) << 14) | + (((v >> 24) & 0x7f) << 21)); + if ((ms->flags & MAGIC_DEBUG) != 0) + fprintf(stderr, "id3 offs=%u\n", v); + return v; } private int @@ -850,37 +936,45 @@ cvt_flip(int type, int flip) p->fld *= cast m->num_mask; \ break; \ case FILE_OPDIVIDE: \ + if (cast m->num_mask == 0) \ + return -1; \ p->fld /= cast m->num_mask; \ break; \ case FILE_OPMODULO: \ + if (cast m->num_mask == 0) \ + return -1; \ p->fld %= cast m->num_mask; \ break; \ } \ if (m->mask_op & FILE_OPINVERSE) \ p->fld = ~p->fld \ -private void +private int cvt_8(union VALUETYPE *p, const struct magic *m) { DO_CVT(b, (uint8_t)); + return 0; } -private void +private int cvt_16(union VALUETYPE *p, const struct magic *m) { DO_CVT(h, (uint16_t)); + return 0; } -private void +private int cvt_32(union VALUETYPE *p, const struct magic *m) { DO_CVT(l, (uint32_t)); + return 0; } -private void +private int cvt_64(union VALUETYPE *p, const struct magic *m) { DO_CVT(q, (uint64_t)); + return 0; } #define DO_CVT2(fld, cast) \ @@ -896,20 +990,24 @@ cvt_64(union VALUETYPE *p, const struct magic *m) p->fld *= cast m->num_mask; \ break; \ case FILE_OPDIVIDE: \ + if (cast m->num_mask == 0) \ + return -1; \ p->fld /= cast m->num_mask; \ break; \ } \ -private void +private int cvt_float(union VALUETYPE *p, const struct magic *m) { DO_CVT2(f, (float)); + return 0; } -private void +private int cvt_double(union VALUETYPE *p, const struct magic *m) { DO_CVT2(d, (double)); + return 0; } /* @@ -921,25 +1019,28 @@ private int mconvert(struct magic_set *ms, struct magic *m, int flip) { union VALUETYPE *p = &ms->ms_value; - uint8_t type; - switch (type = cvt_flip(m->type, flip)) { + switch (cvt_flip(m->type, flip)) { case FILE_BYTE: - cvt_8(p, m); + if (cvt_8(p, m) == -1) + goto out; return 1; case FILE_SHORT: - cvt_16(p, m); + if (cvt_16(p, m) == -1) + goto out; return 1; case FILE_LONG: case FILE_DATE: case FILE_LDATE: - cvt_32(p, m); + if (cvt_32(p, m) == -1) + goto out; return 1; case FILE_QUAD: case FILE_QDATE: case FILE_QLDATE: case FILE_QWDATE: - cvt_64(p, m); + if (cvt_64(p, m) == -1) + goto out; return 1; case FILE_STRING: case FILE_BESTRING16: @@ -970,90 +1071,79 @@ mconvert(struct magic_set *ms, struct magic *m, int flip) return 1; } case FILE_BESHORT: - p->h = (short)((p->hs[0]<<8)|(p->hs[1])); - cvt_16(p, m); + p->h = (short)BE16(p); + if (cvt_16(p, m) == -1) + goto out; return 1; case FILE_BELONG: case FILE_BEDATE: case FILE_BELDATE: - p->l = (int32_t) - ((p->hl[0]<<24)|(p->hl[1]<<16)|(p->hl[2]<<8)|(p->hl[3])); - if (type == FILE_BELONG) - cvt_32(p, m); + p->l = (int32_t)BE32(p); + if (cvt_32(p, m) == -1) + goto out; return 1; case FILE_BEQUAD: case FILE_BEQDATE: case FILE_BEQLDATE: case FILE_BEQWDATE: - p->q = (uint64_t) - (((uint64_t)p->hq[0]<<56)|((uint64_t)p->hq[1]<<48)| - ((uint64_t)p->hq[2]<<40)|((uint64_t)p->hq[3]<<32)| - ((uint64_t)p->hq[4]<<24)|((uint64_t)p->hq[5]<<16)| - ((uint64_t)p->hq[6]<<8)|((uint64_t)p->hq[7])); - if (type == FILE_BEQUAD) - cvt_64(p, m); + p->q = (uint64_t)BE64(p); + if (cvt_64(p, m) == -1) + goto out; return 1; case FILE_LESHORT: - p->h = (short)((p->hs[1]<<8)|(p->hs[0])); - cvt_16(p, m); + p->h = (short)LE16(p); + if (cvt_16(p, m) == -1) + goto out; return 1; case FILE_LELONG: case FILE_LEDATE: case FILE_LELDATE: - p->l = (int32_t) - ((p->hl[3]<<24)|(p->hl[2]<<16)|(p->hl[1]<<8)|(p->hl[0])); - if (type == FILE_LELONG) - cvt_32(p, m); + p->l = (int32_t)LE32(p); + if (cvt_32(p, m) == -1) + goto out; return 1; case FILE_LEQUAD: case FILE_LEQDATE: case FILE_LEQLDATE: case FILE_LEQWDATE: - p->q = (uint64_t) - (((uint64_t)p->hq[7]<<56)|((uint64_t)p->hq[6]<<48)| - ((uint64_t)p->hq[5]<<40)|((uint64_t)p->hq[4]<<32)| - ((uint64_t)p->hq[3]<<24)|((uint64_t)p->hq[2]<<16)| - ((uint64_t)p->hq[1]<<8)|((uint64_t)p->hq[0])); - if (type == FILE_LEQUAD) - cvt_64(p, m); + p->q = (uint64_t)LE64(p); + if (cvt_64(p, m) == -1) + goto out; return 1; case FILE_MELONG: case FILE_MEDATE: case FILE_MELDATE: - p->l = (int32_t) - ((p->hl[1]<<24)|(p->hl[0]<<16)|(p->hl[3]<<8)|(p->hl[2])); - if (type == FILE_MELONG) - cvt_32(p, m); + p->l = (int32_t)ME32(p); + if (cvt_32(p, m) == -1) + goto out; return 1; case FILE_FLOAT: - cvt_float(p, m); + if (cvt_float(p, m) == -1) + goto out; return 1; case FILE_BEFLOAT: - p->l = ((uint32_t)p->hl[0]<<24)|((uint32_t)p->hl[1]<<16)| - ((uint32_t)p->hl[2]<<8) |((uint32_t)p->hl[3]); - cvt_float(p, m); + p->l = BE32(p); + if (cvt_float(p, m) == -1) + goto out; return 1; case FILE_LEFLOAT: - p->l = ((uint32_t)p->hl[3]<<24)|((uint32_t)p->hl[2]<<16)| - ((uint32_t)p->hl[1]<<8) |((uint32_t)p->hl[0]); - cvt_float(p, m); + p->l = LE32(p); + if (cvt_float(p, m) == -1) + goto out; return 1; case FILE_DOUBLE: - cvt_double(p, m); + if (cvt_double(p, m) == -1) + goto out; return 1; case FILE_BEDOUBLE: - p->q = ((uint64_t)p->hq[0]<<56)|((uint64_t)p->hq[1]<<48)| - ((uint64_t)p->hq[2]<<40)|((uint64_t)p->hq[3]<<32)| - ((uint64_t)p->hq[4]<<24)|((uint64_t)p->hq[5]<<16)| - ((uint64_t)p->hq[6]<<8) |((uint64_t)p->hq[7]); - cvt_double(p, m); + p->q = BE64(p); + if (cvt_double(p, m) == -1) + goto out; return 1; case FILE_LEDOUBLE: - p->q = ((uint64_t)p->hq[7]<<56)|((uint64_t)p->hq[6]<<48)| - ((uint64_t)p->hq[5]<<40)|((uint64_t)p->hq[4]<<32)| - ((uint64_t)p->hq[3]<<24)|((uint64_t)p->hq[2]<<16)| - ((uint64_t)p->hq[1]<<8) |((uint64_t)p->hq[0]); - cvt_double(p, m); + p->q = LE64(p); + if (cvt_double(p, m) == -1) + goto out; return 1; case FILE_REGEX: case FILE_SEARCH: @@ -1061,11 +1151,15 @@ mconvert(struct magic_set *ms, struct magic *m, int flip) case FILE_CLEAR: case FILE_NAME: case FILE_USE: + case FILE_DER: return 1; default: file_magerror(ms, "invalid type %d in mconvert()", m->type); return 0; } +out: + file_magerror(ms, "zerodivide in mconvert()"); + return 0; } @@ -1088,7 +1182,10 @@ mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir, */ if (indir == 0) { switch (type) { + case FILE_DER: case FILE_SEARCH: + if (offset > nbytes) + offset = CAST(uint32_t, nbytes); ms->search.s = RCAST(const char *, s) + offset; ms->search.s_len = nbytes - offset; ms->search.offset = offset; @@ -1102,7 +1199,7 @@ mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir, const char *end; size_t lines, linecnt, bytecnt; - if (s == NULL) { + if (s == NULL || nbytes < offset) { ms->search.s_len = 0; ms->search.s = NULL; return 0; @@ -1116,13 +1213,13 @@ mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir, bytecnt = m->str_range; } - if (bytecnt == 0) - bytecnt = 8192; - if (bytecnt > nbytes) - bytecnt = nbytes; + if (bytecnt == 0 || bytecnt > nbytes - offset) + bytecnt = nbytes - offset; + if (bytecnt > ms->regex_max) + bytecnt = ms->regex_max; buf = RCAST(const char *, s) + offset; - end = last = RCAST(const char *, s) + bytecnt; + end = last = RCAST(const char *, s) + bytecnt + offset; /* mget() guarantees buf <= last */ for (lines = linecnt, b = buf; lines && b < end && ((b = CAST(const char *, @@ -1131,7 +1228,7 @@ mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir, memchr(c, '\r', CAST(size_t, (end - c)))))); lines--, b++) { last = b; - if (b[0] == '\r' && b[1] == '\n') + if (b < end - 1 && b[0] == '\r' && b[1] == '\n') b++; } if (lines) @@ -1164,7 +1261,8 @@ mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir, if (*dst == '\0') { if (type == FILE_BESTRING16 ? *(src - 1) != '\0' : - *(src + 1) != '\0') + ((src + 1 < esrc) && + *(src + 1) != '\0')) *dst = ' '; } } @@ -1199,23 +1297,62 @@ mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir, return 0; } +private uint32_t +do_ops(struct magic *m, intmax_t lhs, intmax_t off) +{ + intmax_t offset; + if (off) { + switch (m->in_op & FILE_OPS_MASK) { + case FILE_OPAND: + offset = lhs & off; + break; + case FILE_OPOR: + offset = lhs | off; + break; + case FILE_OPXOR: + offset = lhs ^ off; + break; + case FILE_OPADD: + offset = lhs + off; + break; + case FILE_OPMINUS: + offset = lhs - off; + break; + case FILE_OPMULTIPLY: + offset = lhs * off; + break; + case FILE_OPDIVIDE: + offset = lhs / off; + break; + case FILE_OPMODULO: + offset = lhs % off; + break; + } + } else + offset = lhs; + if (m->in_op & FILE_OPINVERSE) + offset = ~offset; + + return (uint32_t)offset; +} + private int mget(struct magic_set *ms, const unsigned char *s, struct magic *m, size_t nbytes, size_t o, unsigned int cont_level, int mode, int text, - int flip, uint16_t indir_level, uint16_t *name_count, + int flip, uint16_t *indir_count, uint16_t *name_count, int *printed_something, int *need_separator, int *returnval) { uint32_t offset = ms->offset; - uint32_t lhs; + intmax_t lhs; file_pushbuf_t *pb; int rv, oneed_separator, in_type; char *rbuf; union VALUETYPE *p = &ms->ms_value; struct mlist ml; - if (indir_level >= ms->indir_max) { - file_error(ms, 0, "indirect recursion nesting (%hu) exceeded", - indir_level); + if (*indir_count >= ms->indir_max) { + file_error(ms, 0, "indirect count (%hu) exceeded", + *indir_count); return -1; } @@ -1230,11 +1367,11 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m, return -1; if ((ms->flags & MAGIC_DEBUG) != 0) { - fprintf(stderr, "mget(type=%d, flag=%x, offset=%u, o=%" + fprintf(stderr, "mget(type=%d, flag=%#x, offset=%u, o=%" SIZE_T_FORMAT "u, " "nbytes=%" SIZE_T_FORMAT "u, il=%hu, nc=%hu)\n", m->type, m->flag, offset, o, nbytes, - indir_level, *name_count); + *indir_count, *name_count); mdebug(offset, (char *)(void *)p, sizeof(union VALUETYPE)); #ifndef COMPILE_ONLY file_mdump(m); @@ -1242,347 +1379,92 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m, } if (m->flag & INDIR) { - int off = m->in_offset; + intmax_t off = m->in_offset; + const int sgn = m->in_op & FILE_OPSIGNED; if (m->in_op & FILE_OPINDIRECT) { const union VALUETYPE *q = CAST(const union VALUETYPE *, ((const void *)(s + offset + off))); + if (OFFSET_OOB(nbytes, offset + off, sizeof(*q))) + return 0; switch (cvt_flip(m->in_type, flip)) { case FILE_BYTE: - off = q->b; + off = SEXT(sgn,8,q->b); break; case FILE_SHORT: - off = q->h; + off = SEXT(sgn,16,q->h); break; case FILE_BESHORT: - off = (short)((q->hs[0]<<8)|(q->hs[1])); + off = SEXT(sgn,16,BE16(q)); break; case FILE_LESHORT: - off = (short)((q->hs[1]<<8)|(q->hs[0])); + off = SEXT(sgn,16,LE16(q)); break; case FILE_LONG: - off = q->l; + off = SEXT(sgn,32,q->l); break; case FILE_BELONG: case FILE_BEID3: - off = (int32_t)((q->hl[0]<<24)|(q->hl[1]<<16)| - (q->hl[2]<<8)|(q->hl[3])); + off = SEXT(sgn,32,BE32(q)); break; case FILE_LEID3: case FILE_LELONG: - off = (int32_t)((q->hl[3]<<24)|(q->hl[2]<<16)| - (q->hl[1]<<8)|(q->hl[0])); + off = SEXT(sgn,32,LE32(q)); break; case FILE_MELONG: - off = (int32_t)((q->hl[1]<<24)|(q->hl[0]<<16)| - (q->hl[3]<<8)|(q->hl[2])); + off = SEXT(sgn,32,ME32(q)); break; } if ((ms->flags & MAGIC_DEBUG) != 0) - fprintf(stderr, "indirect offs=%u\n", off); + fprintf(stderr, "indirect offs=%jd\n", off); } switch (in_type = cvt_flip(m->in_type, flip)) { case FILE_BYTE: if (OFFSET_OOB(nbytes, offset, 1)) return 0; - if (off) { - switch (m->in_op & FILE_OPS_MASK) { - case FILE_OPAND: - offset = p->b & off; - break; - case FILE_OPOR: - offset = p->b | off; - break; - case FILE_OPXOR: - offset = p->b ^ off; - break; - case FILE_OPADD: - offset = p->b + off; - break; - case FILE_OPMINUS: - offset = p->b - off; - break; - case FILE_OPMULTIPLY: - offset = p->b * off; - break; - case FILE_OPDIVIDE: - offset = p->b / off; - break; - case FILE_OPMODULO: - offset = p->b % off; - break; - } - } else - offset = p->b; - if (m->in_op & FILE_OPINVERSE) - offset = ~offset; + offset = do_ops(m, SEXT(sgn,8,p->b), off); break; case FILE_BESHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; - lhs = (p->hs[0] << 8) | p->hs[1]; - if (off) { - switch (m->in_op & FILE_OPS_MASK) { - case FILE_OPAND: - offset = lhs & off; - break; - case FILE_OPOR: - offset = lhs | off; - break; - case FILE_OPXOR: - offset = lhs ^ off; - break; - case FILE_OPADD: - offset = lhs + off; - break; - case FILE_OPMINUS: - offset = lhs - off; - break; - case FILE_OPMULTIPLY: - offset = lhs * off; - break; - case FILE_OPDIVIDE: - offset = lhs / off; - break; - case FILE_OPMODULO: - offset = lhs % off; - break; - } - } else - offset = lhs; - if (m->in_op & FILE_OPINVERSE) - offset = ~offset; + offset = do_ops(m, SEXT(sgn,16,BE16(p)), off); break; case FILE_LESHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; - lhs = (p->hs[1] << 8) | p->hs[0]; - if (off) { - switch (m->in_op & FILE_OPS_MASK) { - case FILE_OPAND: - offset = lhs & off; - break; - case FILE_OPOR: - offset = lhs | off; - break; - case FILE_OPXOR: - offset = lhs ^ off; - break; - case FILE_OPADD: - offset = lhs + off; - break; - case FILE_OPMINUS: - offset = lhs - off; - break; - case FILE_OPMULTIPLY: - offset = lhs * off; - break; - case FILE_OPDIVIDE: - offset = lhs / off; - break; - case FILE_OPMODULO: - offset = lhs % off; - break; - } - } else - offset = lhs; - if (m->in_op & FILE_OPINVERSE) - offset = ~offset; + offset = do_ops(m, SEXT(sgn,16,LE16(p)), off); break; case FILE_SHORT: if (OFFSET_OOB(nbytes, offset, 2)) return 0; - if (off) { - switch (m->in_op & FILE_OPS_MASK) { - case FILE_OPAND: - offset = p->h & off; - break; - case FILE_OPOR: - offset = p->h | off; - break; - case FILE_OPXOR: - offset = p->h ^ off; - break; - case FILE_OPADD: - offset = p->h + off; - break; - case FILE_OPMINUS: - offset = p->h - off; - break; - case FILE_OPMULTIPLY: - offset = p->h * off; - break; - case FILE_OPDIVIDE: - offset = p->h / off; - break; - case FILE_OPMODULO: - offset = p->h % off; - break; - } - } - else - offset = p->h; - if (m->in_op & FILE_OPINVERSE) - offset = ~offset; + offset = do_ops(m, SEXT(sgn,16,p->h), off); break; case FILE_BELONG: case FILE_BEID3: if (OFFSET_OOB(nbytes, offset, 4)) return 0; - lhs = (p->hl[0] << 24) | (p->hl[1] << 16) | - (p->hl[2] << 8) | p->hl[3]; - if (off) { - switch (m->in_op & FILE_OPS_MASK) { - case FILE_OPAND: - offset = lhs & off; - break; - case FILE_OPOR: - offset = lhs | off; - break; - case FILE_OPXOR: - offset = lhs ^ off; - break; - case FILE_OPADD: - offset = lhs + off; - break; - case FILE_OPMINUS: - offset = lhs - off; - break; - case FILE_OPMULTIPLY: - offset = lhs * off; - break; - case FILE_OPDIVIDE: - offset = lhs / off; - break; - case FILE_OPMODULO: - offset = lhs % off; - break; - } - } else - offset = lhs; - if (m->in_op & FILE_OPINVERSE) - offset = ~offset; + lhs = BE32(p); + if (in_type == FILE_BEID3) + lhs = cvt_id3(ms, (uint32_t)lhs); + offset = do_ops(m, SEXT(sgn,32,lhs), off); break; case FILE_LELONG: case FILE_LEID3: if (OFFSET_OOB(nbytes, offset, 4)) return 0; - lhs = (p->hl[3] << 24) | (p->hl[2] << 16) | - (p->hl[1] << 8) | p->hl[0]; - if (off) { - switch (m->in_op & FILE_OPS_MASK) { - case FILE_OPAND: - offset = lhs & off; - break; - case FILE_OPOR: - offset = lhs | off; - break; - case FILE_OPXOR: - offset = lhs ^ off; - break; - case FILE_OPADD: - offset = lhs + off; - break; - case FILE_OPMINUS: - offset = lhs - off; - break; - case FILE_OPMULTIPLY: - offset = lhs * off; - break; - case FILE_OPDIVIDE: - offset = lhs / off; - break; - case FILE_OPMODULO: - offset = lhs % off; - break; - } - } else - offset = lhs; - if (m->in_op & FILE_OPINVERSE) - offset = ~offset; + lhs = LE32(p); + if (in_type == FILE_LEID3) + lhs = cvt_id3(ms, (uint32_t)lhs); + offset = do_ops(m, SEXT(sgn,32,lhs), off); break; case FILE_MELONG: if (OFFSET_OOB(nbytes, offset, 4)) return 0; - lhs = (p->hl[1] << 24) | (p->hl[0] << 16) | - (p->hl[3] << 8) | p->hl[2]; - if (off) { - switch (m->in_op & FILE_OPS_MASK) { - case FILE_OPAND: - offset = lhs & off; - break; - case FILE_OPOR: - offset = lhs | off; - break; - case FILE_OPXOR: - offset = lhs ^ off; - break; - case FILE_OPADD: - offset = lhs + off; - break; - case FILE_OPMINUS: - offset = lhs - off; - break; - case FILE_OPMULTIPLY: - offset = lhs * off; - break; - case FILE_OPDIVIDE: - offset = lhs / off; - break; - case FILE_OPMODULO: - offset = lhs % off; - break; - } - } else - offset = lhs; - if (m->in_op & FILE_OPINVERSE) - offset = ~offset; + offset = do_ops(m, SEXT(sgn,32,ME32(p)), off); break; case FILE_LONG: if (OFFSET_OOB(nbytes, offset, 4)) return 0; - if (off) { - switch (m->in_op & FILE_OPS_MASK) { - case FILE_OPAND: - offset = p->l & off; - break; - case FILE_OPOR: - offset = p->l | off; - break; - case FILE_OPXOR: - offset = p->l ^ off; - break; - case FILE_OPADD: - offset = p->l + off; - break; - case FILE_OPMINUS: - offset = p->l - off; - break; - case FILE_OPMULTIPLY: - offset = p->l * off; - break; - case FILE_OPDIVIDE: - offset = p->l / off; - break; - case FILE_OPMODULO: - offset = p->l % off; - break; - } - } else - offset = p->l; - if (m->in_op & FILE_OPINVERSE) - offset = ~offset; - break; - default: - break; - } - - switch (in_type) { - case FILE_LEID3: - case FILE_BEID3: - offset = ((((offset >> 0) & 0x7f) << 0) | - (((offset >> 8) & 0x7f) << 7) | - (((offset >> 16) & 0x7f) << 14) | - (((offset >> 24) & 0x7f) << 21)) + 10; + offset = do_ops(m, SEXT(sgn,32,p->l), off); break; default: break; @@ -1666,7 +1548,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m, case FILE_INDIRECT: if (m->str_flags & INDIRECT_RELATIVE) - offset += o; + offset += CAST(uint32_t, o); if (offset == 0) return 0; @@ -1676,8 +1558,9 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m, if ((pb = file_push_buffer(ms)) == NULL) return -1; + (*indir_count)++; rv = file_softmagic(ms, s + offset, nbytes - offset, - indir_level + 1, name_count, BINTEST, text); + indir_count, name_count, BINTEST, text); if ((ms->flags & MAGIC_DEBUG) != 0) fprintf(stderr, "indirect @offs=%u[%d]\n", offset, rv); @@ -1687,7 +1570,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m, return -1; if (rv == 1) { - if ((ms->flags & (MAGIC_MIME|MAGIC_APPLE)) == 0 && + if ((ms->flags & MAGIC_NODESC) == 0 && file_printf(ms, F(ms, m, "%u"), offset) == -1) { free(rbuf); return -1; @@ -1717,16 +1600,19 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m, if (m->flag & NOSPACE) *need_separator = 0; rv = match(ms, ml.magic, ml.nmagic, s, nbytes, offset + o, - mode, text, flip, indir_level, name_count, + mode, text, flip, indir_count, name_count, printed_something, need_separator, returnval); if (rv != 1) *need_separator = oneed_separator; - return rv; + return 1; case FILE_NAME: + if (ms->flags & MAGIC_NODESC) + return 1; if (file_printf(ms, "%s", m->desc) == -1) return -1; return 1; + case FILE_DER: case FILE_DEFAULT: /* nothing to check */ case FILE_CLEAR: default: @@ -1748,6 +1634,7 @@ file_strncmp(const char *s1, const char *s2, size_t len, uint32_t flags) */ const unsigned char *a = (const unsigned char *)s1; const unsigned char *b = (const unsigned char *)s2; + const unsigned char *eb = b + len; uint64_t v; /* @@ -1762,6 +1649,10 @@ file_strncmp(const char *s1, const char *s2, size_t len, uint32_t flags) } else { /* combine the others */ while (len-- > 0) { + if (b >= eb) { + v = 1; + break; + } if ((flags & STRING_IGNORE_LOWERCASE) && islower(*a)) { if ((v = tolower(*b++) - *a++) != '\0') @@ -1777,7 +1668,7 @@ file_strncmp(const char *s1, const char *s2, size_t len, uint32_t flags) a++; if (isspace(*b++)) { if (!isspace(*a)) - while (isspace(*b)) + while (b < eb && isspace(*b)) b++; } else { @@ -1788,7 +1679,7 @@ file_strncmp(const char *s1, const char *s2, size_t len, uint32_t flags) else if ((flags & STRING_COMPACT_OPTIONAL_WHITESPACE) && isspace(*a)) { a++; - while (isspace(*b)) + while (b < eb && isspace(*b)) b++; } else { @@ -1959,12 +1850,13 @@ magiccheck(struct magic_set *ms, struct magic *m) for (idx = 0; m->str_range == 0 || idx < m->str_range; idx++) { if (slen + idx > ms->search.s_len) - break; + return 0; v = file_strncmp(m->value.s, ms->search.s + idx, slen, m->str_flags); if (v == 0) { /* found match */ ms->search.offset += idx; + ms->search.rm_len = ms->search.s_len - idx; break; } } @@ -1986,14 +1878,13 @@ magiccheck(struct magic_set *ms, struct magic *m) file_regerror(&rx, rc, ms); v = (uint64_t)-1; } else { - regmatch_t pmatch[1]; + regmatch_t pmatch; size_t slen = ms->search.s_len; -#ifndef REG_STARTEND -#define REG_STARTEND 0 char *copy; if (slen != 0) { - copy = malloc(slen); + copy = CAST(char *, malloc(slen)); if (copy == NULL) { + file_regfree(&rx); file_error(ms, errno, "can't allocate %" SIZE_T_FORMAT "u bytes", slen); @@ -2003,25 +1894,18 @@ magiccheck(struct magic_set *ms, struct magic *m) copy[--slen] = '\0'; search = copy; } else { - search = ms->search.s; + search = CCAST(char *, ""); copy = NULL; } -#else - search = ms->search.s; - pmatch[0].rm_so = 0; - pmatch[0].rm_eo = slen; -#endif rc = file_regexec(&rx, (const char *)search, - 1, pmatch, REG_STARTEND); -#if REG_STARTEND == 0 + 1, &pmatch, 0); free(copy); -#endif switch (rc) { case 0: - ms->search.s += (int)pmatch[0].rm_so; - ms->search.offset += (size_t)pmatch[0].rm_so; + ms->search.s += (int)pmatch.rm_so; + ms->search.offset += (size_t)pmatch.rm_so; ms->search.rm_len = - (size_t)(pmatch[0].rm_eo - pmatch[0].rm_so); + (size_t)(pmatch.rm_eo - pmatch.rm_so); v = 0; break; @@ -2044,6 +1928,16 @@ magiccheck(struct magic_set *ms, struct magic *m) case FILE_USE: case FILE_NAME: return 1; + case FILE_DER: + matched = der_cmp(ms, m); + if (matched == -1) { + if ((ms->flags & MAGIC_DEBUG) != 0) { + (void) fprintf(stderr, + "EOF comparing DER entries"); + } + return 0; + } + return matched; default: file_magerror(ms, "invalid type %d in magiccheck()", m->type); return -1; @@ -2141,14 +2035,25 @@ magiccheck(struct magic_set *ms, struct magic *m) } private int -handle_annotation(struct magic_set *ms, struct magic *m) +handle_annotation(struct magic_set *ms, struct magic *m, int firstline) { - if (ms->flags & MAGIC_APPLE) { + if ((ms->flags & MAGIC_APPLE) && m->apple[0]) { + if (!firstline && file_printf(ms, "\n- ") == -1) + return -1; if (file_printf(ms, "%.8s", m->apple) == -1) return -1; return 1; } + if ((ms->flags & MAGIC_EXTENSION) && m->ext[0]) { + if (!firstline && file_printf(ms, "\n- ") == -1) + return -1; + if (file_printf(ms, "%s", m->ext) == -1) + return -1; + return 1; + } if ((ms->flags & MAGIC_MIME_TYPE) && m->mimetype[0]) { + if (!firstline && file_printf(ms, "\n- ") == -1) + return -1; if (file_printf(ms, "%s", m->mimetype) == -1) return -1; return 1; @@ -2159,8 +2064,8 @@ handle_annotation(struct magic_set *ms, struct magic *m) private int print_sep(struct magic_set *ms, int firstline) { - if (ms->flags & MAGIC_MIME) - return 0; +// if (ms->flags & MAGIC_NODESC) +// return 0; if (firstline) return 0; /* diff --git a/src/vasprintf.c b/src/vasprintf.c index 7a18bed..ad1d316 100644 --- a/src/vasprintf.c +++ b/src/vasprintf.c @@ -88,7 +88,7 @@ type: d i o u x X f e g E G c s p n The function needs to allocate memory to store the full text before to -actually writting it. i.e if you want to fnprintf() 1000 characters, the +actually writing it. i.e if you want to fnprintf() 1000 characters, the functions will allocate 1000 bytes. This behaviour can be modified: you have to customise the code to flush the internal buffer (writing to screen or file) when it reach a given size. Then @@ -108,7 +108,7 @@ you use strange formats. #include "file.h" #ifndef lint -FILE_RCSID("@(#)$File: vasprintf.c,v 1.13 2014/12/04 15:56:46 christos Exp $") +FILE_RCSID("@(#)$File: vasprintf.c,v 1.14 2017/08/13 00:21:47 christos Exp $") #endif /* lint */ #include <assert.h> |