asn1: Make asn1_print a fuzzing tool
This commit is contained in:
@@ -60,6 +60,9 @@
|
|||||||
.Fl Fl raw-sequence
|
.Fl Fl raw-sequence
|
||||||
.Xc
|
.Xc
|
||||||
.Oc
|
.Oc
|
||||||
|
.Oo Fl n \*(Ba Xo Fl Fl no-print Xc Oc
|
||||||
|
.Oo Xo Fl Fl test-encode Xc Oc
|
||||||
|
.Oo Xo Fl Fl test-copy Xc Oc
|
||||||
.Oo Fl l v \*(Ba Xo
|
.Oo Fl l v \*(Ba Xo
|
||||||
.Fl Fl version
|
.Fl Fl version
|
||||||
.Xc
|
.Xc
|
||||||
@@ -113,6 +116,22 @@ If a value parses as a given
|
|||||||
.Ar TypeName
|
.Ar TypeName
|
||||||
but any bytes are left over, try to parse those separately as
|
but any bytes are left over, try to parse those separately as
|
||||||
well until all bytes are consumed or an error occurs.
|
well until all bytes are consumed or an error occurs.
|
||||||
|
.It Fl n, Fl Fl no-print
|
||||||
|
For the case where
|
||||||
|
.Fl A
|
||||||
|
or
|
||||||
|
.Fl Fl try-all-types
|
||||||
|
or where a
|
||||||
|
.Ar TypeName
|
||||||
|
is given, do not output a JSON representation of the value, just
|
||||||
|
attempt to decode it.
|
||||||
|
This is useful for fuzzing.
|
||||||
|
.It Fl Fl test-encode
|
||||||
|
Check that encoding produces the same value as decoding.
|
||||||
|
Useful for fuzzing.
|
||||||
|
.It Fl Fl test-copy
|
||||||
|
Test copy functions.
|
||||||
|
Useful for fuzzing.
|
||||||
.It Fl v, Fl Fl version
|
.It Fl v, Fl Fl version
|
||||||
.It Fl h, Fl Fl help
|
.It Fl h, Fl Fl help
|
||||||
.El
|
.El
|
||||||
|
@@ -55,33 +55,45 @@
|
|||||||
#include "rfc4108_asn1.h"
|
#include "rfc4108_asn1.h"
|
||||||
#include "x690sample_asn1.h"
|
#include "x690sample_asn1.h"
|
||||||
|
|
||||||
static int sequence_flag = 0;
|
static int print_flag = 1;
|
||||||
static int try_all_flag = 0;
|
static int test_copy_flag;
|
||||||
|
static int test_encode_flag;
|
||||||
|
static int sequence_flag;
|
||||||
|
static int try_all_flag;
|
||||||
static int indent_flag = 1;
|
static int indent_flag = 1;
|
||||||
static int inner_flag = 0;
|
static int inner_flag;
|
||||||
|
|
||||||
static unsigned long indefinite_form_loop;
|
static unsigned long indefinite_form_loop;
|
||||||
static unsigned long indefinite_form_loop_max = 10000;
|
static unsigned long indefinite_form_loop_max = 10000;
|
||||||
|
|
||||||
|
typedef size_t (*lengther)(void *);
|
||||||
|
typedef size_t (*copyer)(const void *, void *);
|
||||||
|
typedef int (*encoder)(unsigned char *, size_t, void *, size_t *);
|
||||||
typedef int (*decoder)(const unsigned char *, size_t, void *, size_t *);
|
typedef int (*decoder)(const unsigned char *, size_t, void *, size_t *);
|
||||||
typedef char *(*printer)(const void *, int);
|
typedef char *(*printer)(const void *, int);
|
||||||
typedef void (*releaser)(void *);
|
typedef void (*releaser)(void *);
|
||||||
const struct types {
|
const struct types {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
size_t sz;
|
||||||
|
copyer cpy;
|
||||||
|
lengther len;
|
||||||
decoder decode;
|
decoder decode;
|
||||||
|
encoder encode;
|
||||||
printer print;
|
printer print;
|
||||||
releaser release;
|
releaser release;
|
||||||
size_t sz;
|
|
||||||
} types[] = {
|
} types[] = {
|
||||||
#define ASN1_SYM_INTVAL(n, gn, gns, i)
|
#define ASN1_SYM_INTVAL(n, gn, gns, i)
|
||||||
#define ASN1_SYM_OID(n, gn, gns)
|
#define ASN1_SYM_OID(n, gn, gns)
|
||||||
#define ASN1_SYM_TYPE(n, gn, gns) \
|
#define ASN1_SYM_TYPE(n, gn, gns) \
|
||||||
{ \
|
{ \
|
||||||
n, \
|
n, \
|
||||||
|
sizeof(gns), \
|
||||||
|
(copyer)copy_ ## gns, \
|
||||||
|
(lengther)length_ ## gns, \
|
||||||
(decoder)decode_ ## gns, \
|
(decoder)decode_ ## gns, \
|
||||||
|
(encoder)encode_ ## gns, \
|
||||||
(printer)print_ ## gns, \
|
(printer)print_ ## gns, \
|
||||||
(releaser)free_ ## gns, \
|
(releaser)free_ ## gns, \
|
||||||
sizeof(gns) \
|
|
||||||
},
|
},
|
||||||
#include "cms_asn1_syms.x"
|
#include "cms_asn1_syms.x"
|
||||||
#include "digest_asn1_syms.x"
|
#include "digest_asn1_syms.x"
|
||||||
@@ -356,8 +368,8 @@ dotype(unsigned char *buf, size_t len, char **argv, size_t *size)
|
|||||||
const char *typename = "";
|
const char *typename = "";
|
||||||
size_t matches = 0;
|
size_t matches = 0;
|
||||||
size_t sz;
|
size_t sz;
|
||||||
|
size_t tried = 0;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
char *s = NULL;
|
|
||||||
void *v;
|
void *v;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@@ -397,6 +409,7 @@ dotype(unsigned char *buf, size_t len, char **argv, size_t *size)
|
|||||||
v = ecalloc(1, sorted_types[i].sz);
|
v = ecalloc(1, sorted_types[i].sz);
|
||||||
ret = sorted_types[i].decode(buf, len, v, &sz);
|
ret = sorted_types[i].decode(buf, len, v, &sz);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
|
matches++;
|
||||||
if (sz == len) {
|
if (sz == len) {
|
||||||
fprintf(stderr, "Match: %s\n", typename);
|
fprintf(stderr, "Match: %s\n", typename);
|
||||||
} else if (sequence_flag) {
|
} else if (sequence_flag) {
|
||||||
@@ -404,36 +417,70 @@ dotype(unsigned char *buf, size_t len, char **argv, size_t *size)
|
|||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Prefix match: %s\n", typename);
|
fprintf(stderr, "Prefix match: %s\n", typename);
|
||||||
}
|
}
|
||||||
s = sorted_types[i].print(v, indent_flag ? ASN1_PRINT_INDENT : 0);
|
if (print_flag) {
|
||||||
sorted_types[i].release(v);
|
char *s = NULL;
|
||||||
if (!s) {
|
|
||||||
ret = errno;
|
s = sorted_types[i].print(v, indent_flag ? ASN1_PRINT_INDENT : 0);
|
||||||
err(1, "Could not print %s\n", typename);
|
if (!s) {
|
||||||
|
ret = errno;
|
||||||
|
err(1, "Could not print %s\n", typename);
|
||||||
|
}
|
||||||
|
fprintf(stdout, "%s\n", s);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
if (test_encode_flag) {
|
||||||
|
unsigned char *der = emalloc(sz);
|
||||||
|
size_t wants = sorted_types[i].len(v);
|
||||||
|
|
||||||
|
if (wants != sz)
|
||||||
|
errx(1, "Encoding will not round trip");
|
||||||
|
ret = sorted_types[i].encode(der, sz, v, &sz);
|
||||||
|
if (ret != 0)
|
||||||
|
errx(1, "Encoding failed");
|
||||||
|
if (memcmp(buf, der, sz))
|
||||||
|
errx(1, "Encoding did not round trip");
|
||||||
|
}
|
||||||
|
if (test_copy_flag) {
|
||||||
|
void *vcpy = ecalloc(1, sorted_types[i].sz);
|
||||||
|
|
||||||
|
ret = sorted_types[i].cpy(v, vcpy);
|
||||||
|
if (ret != 0)
|
||||||
|
errx(1, "Copy function failed");
|
||||||
|
if (test_encode_flag) {
|
||||||
|
unsigned char *der = emalloc(sz);
|
||||||
|
size_t wants = sorted_types[i].len(vcpy);
|
||||||
|
|
||||||
|
if (wants != sz)
|
||||||
|
errx(1, "Encoding of copy will not round trip");
|
||||||
|
ret = sorted_types[i].encode(der, sz, vcpy, &sz);
|
||||||
|
if (ret != 0)
|
||||||
|
errx(1, "Encoding of copy failed");
|
||||||
|
if (memcmp(buf, der, sz))
|
||||||
|
errx(1, "Encoding of copy did not round trip");
|
||||||
|
}
|
||||||
|
sorted_types[i].release(vcpy);
|
||||||
|
free(vcpy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sorted_types[i].release(v);
|
||||||
free(v);
|
free(v);
|
||||||
if (ret == 0) {
|
tried++;
|
||||||
fprintf(stdout, "%s\n", s);
|
i++;
|
||||||
|
|
||||||
matches++;
|
if (ret == 0 && !try_all_flag && !argv[0])
|
||||||
i++;
|
|
||||||
if (try_all_flag)
|
|
||||||
continue;
|
|
||||||
free(s);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (argv[0])
|
if (!try_all_flag && argv[0])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (try_all_flag) {
|
if (try_all_flag) {
|
||||||
i++;
|
|
||||||
if (i < sizeof(types)/sizeof(types[0]))
|
if (i < sizeof(types)/sizeof(types[0]))
|
||||||
continue;
|
continue;
|
||||||
if (matches)
|
if (matches)
|
||||||
break;
|
break;
|
||||||
errx(1, "No type matched the input value");
|
|
||||||
}
|
}
|
||||||
|
if (tried > 1)
|
||||||
|
errx(1, "No type matched the input value");
|
||||||
|
|
||||||
/* XXX Use com_err */
|
/* XXX Use com_err */
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
@@ -493,10 +540,9 @@ dotype(unsigned char *buf, size_t len, char **argv, size_t *size)
|
|||||||
errx(1, "Could not decode and print data as type %s: "
|
errx(1, "Could not decode and print data as type %s: "
|
||||||
"End-of-contents tag contains data", typename);
|
"End-of-contents tag contains data", typename);
|
||||||
default:
|
default:
|
||||||
err(1, "Could not decode and print data as type %s", typename);
|
errx(1, "Could not decode and print data as type %s", typename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(s);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,7 +582,7 @@ doit(char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int list_types_flag = 0;
|
static int list_types_flag;
|
||||||
static int version_flag;
|
static int version_flag;
|
||||||
static int help_flag;
|
static int help_flag;
|
||||||
struct getargs args[] = {
|
struct getargs args[] = {
|
||||||
@@ -550,6 +596,12 @@ struct getargs args[] = {
|
|||||||
"\ttry all known types", NULL },
|
"\ttry all known types", NULL },
|
||||||
{ "raw-sequence", 'S', arg_flag, &sequence_flag,
|
{ "raw-sequence", 'S', arg_flag, &sequence_flag,
|
||||||
"\ttry parsing leftover data", NULL },
|
"\ttry parsing leftover data", NULL },
|
||||||
|
{ "test-encode", 0, arg_flag, &test_encode_flag,
|
||||||
|
"\ttest encode round trip (for memory debugging and fuzzing)", NULL },
|
||||||
|
{ "test-copy", 0, arg_flag, &test_copy_flag,
|
||||||
|
"\ttest copy operation (for memory debugging and fuzzing)", NULL },
|
||||||
|
{ "print", 'n', arg_negative_flag, &print_flag,
|
||||||
|
"\ttest copy operation (for memory debugging and fuzzing)", NULL },
|
||||||
{ "version", 'v', arg_flag, &version_flag, NULL, NULL },
|
{ "version", 'v', arg_flag, &version_flag, NULL, NULL },
|
||||||
{ "help", 'h', arg_flag, &help_flag, NULL, NULL }
|
{ "help", 'h', arg_flag, &help_flag, NULL, NULL }
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user