From 328f058d842de3e6878d160bab5ceaa1e96ec089 Mon Sep 17 00:00:00 2001 From: Christos Zoulas Date: Tue, 19 Jan 2016 04:17:07 +0000 Subject: [PATCH] Add DER parsing (experimental) --- src/Makefile.am | 2 +- src/apprentice.c | 10 +- src/der.c | 335 +++++++++++++++++++++++++++++++++++++++++++++++ src/file.h | 5 +- src/softmagic.c | 11 +- 5 files changed, 358 insertions(+), 5 deletions(-) create mode 100644 src/der.c diff --git a/src/Makefile.am b/src/Makefile.am index e3196ce5..c6f685e9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ 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/apprentice.c b/src/apprentice.c index 823a0d51..9748c289 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.243 2015/11/09 21:21:16 christos Exp $") +FILE_RCSID("@(#)$File: apprentice.c,v 1.244 2015/11/13 16:45:01 christos Exp $") #endif /* lint */ #include "magic.h" @@ -268,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 }, }; @@ -276,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 }, @@ -866,6 +868,9 @@ apprentice_magic_strength(const struct magic *m) case FILE_USE: break; + case FILE_DER: + val += MULT; + default: (void)fprintf(stderr, "Bad type %d\n", m->type); abort(); @@ -1021,6 +1026,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: @@ -1452,6 +1458,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) @@ -2562,6 +2569,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) diff --git a/src/der.c b/src/der.c new file mode 100644 index 00000000..9d167855 --- /dev/null +++ b/src/der.c @@ -0,0 +1,335 @@ +/*- + * 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/ + */ +#include "file.h" + +#ifndef lint +FILE_RCSID("@(#)$File: cdf.c,v 1.76 2015/02/28 00:18:02 christos Exp $") +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "der.h" + +#define DER_CLASS_UNIVERSAL 0 +#define DER_CLASS_APPLICATION 1 +#define DER_CLASS_CONTEXT 2 +#define DER_CLASS_PRIVATE 3 +static const char der_class[] = "UACP"; + +#define DER_TYPE_PRIMITIVE 0 +#define DER_TYPE_CONSTRUCTED 1 +static const char der_type[] = "PC"; + +#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 = c[(*p)++] & 0x1f; + l--; + + if (tag != 0x1f) + return tag; + + while (c[*p] >= 0x80 && l-- > 0) + tag = tag * 128 + c[(*p)++] - 0x80; + return tag; +} + +static uint32_t +getlength(const uint8_t *c, size_t *p, size_t l) +{ + uint8_t digits, i; + size_t len; + + digits = c[(*p)++]; + l--; + if ((digits & 0x80) == 0) + return digits; + + digits &= 0x7f; + len = 0; + + for (i = 0; i < digits && l-- > 0; i++) + len = (len << 8) | c[(*p)++]; + return 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; +} + +static int +der_data(char *buf, size_t blen, 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: + case DER_TAG_IA5_STRING: + 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) +{ + const uint8_t *b = CAST(const void *, ms->search.s); + size_t offs = 0, len = ms->search.rm_len; + (void)gettag(b, &offs, len); + DPRINTF(("%s1: %d %zu %u\n", __func__, ms->offset, offs, m->offset)); + uint32_t tlen = getlength(b, &offs, len); + 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) { + ms->c.li[m->cont_level - 1].off = offs + tlen; + DPRINTF(("cont_level[%u] = %u\n", m->cont_level - 1, + ms->c.li[m->cont_level - 1].off)); + } + return offs; +} + +int +der_cmp(struct magic_set *ms, struct magic *m) +{ + const uint8_t *b = CAST(const void *, ms->search.s); + const char *s = m->value.s; + size_t offs = 0, len = ms->search.s_len; + uint32_t tag = gettag(b, &offs, len); + uint32_t tlen = getlength(b, &offs, len); + char buf[128]; + + der_tag(buf, sizeof(buf), tag); + DPRINTF(("%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)); + DPRINTF(("%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); + DPRINTF(("%s: data %s %s\n", __func__, buf, s)); + if (strcmp(buf, s) != 0 && strcmp("x", s) != 0) + return 0; + strlcpy(m->value.s, buf, sizeof(m->value.s)); + return 1; +} + +#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; + 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 (len == 0x80) { + const uint8_t *e = q; + while (*e && e < ep) + e++; + len = e - q; + } + 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/file.h b/src/file.h index f2d6c564..fcef3174 100644 --- a/src/file.h +++ b/src/file.h @@ -27,7 +27,7 @@ */ /* * file.h - definitions for file(1) program - * @(#)$File: file.h,v 1.173 2015/11/09 21:03:42 christos Exp $ + * @(#)$File: file.h,v 1.174 2015/11/13 15:36:14 christos Exp $ */ #ifndef __file_h__ @@ -227,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 || \ diff --git a/src/softmagic.c b/src/softmagic.c index 1e4619fc..29e56460 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.222 2015/09/17 01:10:00 christos Exp $") +FILE_RCSID("@(#)$File: softmagic.c,v 1.223 2015/11/11 04:25:41 christos Exp $") #endif /* lint */ #include "magic.h" @@ -41,6 +41,7 @@ FILE_RCSID("@(#)$File: softmagic.c,v 1.222 2015/09/17 01:10:00 christos Exp $") #include #include #include +#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, @@ -523,6 +524,7 @@ mprint(struct magic_set *ms, struct magic *m) case FILE_PSTRING: case FILE_BESTRING16: case FILE_LESTRING16: + case FILE_DER: if (m->reln == '=' || m->reln == '!') { if (file_printf(ms, F(ms, m, "%s"), file_printable(sbuf, sizeof(sbuf), m->value.s)) @@ -782,6 +784,8 @@ moffset(struct magic_set *ms, struct magic *m) case FILE_DEFAULT: case FILE_INDIRECT: return ms->offset; + case FILE_DER: + return der_offs(ms, m); default: return 0; @@ -1076,6 +1080,7 @@ 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); @@ -1106,6 +1111,7 @@ mcopy(struct magic_set *ms, union VALUETYPE *p, int type, int indir, */ if (indir == 0) { switch (type) { + case FILE_DER: case FILE_SEARCH: ms->search.s = RCAST(const char *, s) + offset; ms->search.s_len = nbytes - offset; @@ -1745,6 +1751,7 @@ mget(struct magic_set *ms, const unsigned char *s, struct magic *m, 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: @@ -2054,6 +2061,8 @@ magiccheck(struct magic_set *ms, struct magic *m) case FILE_USE: case FILE_NAME: return 1; + case FILE_DER: + return der_cmp(ms, m); default: file_magerror(ms, "invalid type %d in magiccheck()", m->type); return -1; -- 2.40.0