1/*
2 * Copyright (c) 2011 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2011 - 2013 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/types.h>
37#include <inttypes.h>
38#include <roken.h>
39#include "asn1-common.h"
40#include "der.h"
41
42#define HEIM_FUZZER_INTERNALS 1
43#include "fuzzer.h"
44
45/*
46 */
47
48struct context {
49    char *interesting;
50    size_t size;
51    unsigned long permutation;
52    unsigned long iteration;
53    size_t current;
54};
55
56typedef int (*ap)(struct context *, unsigned char *, size_t);
57
58/*
59 *
60 */
61
62static int
63tag_permutate(struct context *context, unsigned char *data, size_t len)
64{
65    return 1;
66}
67
68static int
69size_permutate(struct context *context, unsigned char *data, size_t len)
70{
71    return 1;
72}
73static int
74integer_permutate(struct context *context, unsigned char *data, size_t len)
75{
76    return 1;
77}
78static int
79os_permutate(struct context *context, unsigned char *data, size_t len)
80{
81    return 1;
82}
83static int
84string_permutate(struct context *context, unsigned char *data, size_t len)
85{
86    return 1;
87}
88static int
89oid_permutate(struct context *context, unsigned char *data, size_t len)
90{
91    return 1;
92}
93
94struct perm {
95    char type;
96    const char *name;
97    ap perm;
98} permuntations[] = {
99    { 'T', "tag", tag_permutate },
100    { 'S', "size", size_permutate },
101    { 'd', "integer", integer_permutate },
102    { 'o', "octet string", os_permutate },
103    { 's', "string", string_permutate },
104    { 'd', "oid", oid_permutate }
105};
106
107static unsigned long indefinite_form_loop;
108static unsigned long indefinite_form_loop_max = 20;
109
110static void
111find_interesting(unsigned char *buf, size_t len, char *interesting)
112{
113    unsigned char *start_buf = buf;
114
115    memset(interesting, 0, len);
116
117    while (len > 0) {
118	int ret;
119	Der_class cls;
120	Der_type type;
121	unsigned int tag;
122	size_t sz;
123	size_t length;
124	int end_tag = 0;
125
126	ret = der_get_tag (buf, len, &cls, &type, &tag, &sz);
127	if (ret) {
128	    warn("der_get_tag failed at offset %lu: %d", (unsigned long)(buf - start_buf), ret);
129	    return;
130	}
131	if (sz > len) {
132	    warn("unreasonable length (%u) > %u",
133		 (unsigned)sz, (unsigned)len);
134	    return;
135	}
136
137	memset(interesting, 'T', sz);
138	interesting += sz;
139	buf += sz;
140	len -= sz;
141
142	ret = der_get_length (buf, len, &length, &sz);
143	if (ret) {
144	    warnx("der_get_tag: %d", ret);
145	    return;
146	}
147	if (sz > len) {
148	    warnx("unreasonable tag length (%u) > %u",
149		  (unsigned)sz, (unsigned)len);
150	    return;
151	}
152
153	memset(interesting, 'S', sz);
154	interesting += sz;
155	buf += sz;
156	len -= sz;
157
158	if (length == ASN1_INDEFINITE) {
159	    if ((cls == ASN1_C_UNIV && type == PRIM && tag == UT_OctetString) ||
160		(cls == ASN1_C_CONTEXT && type == CONS) ||
161		(cls == ASN1_C_UNIV && type == CONS && tag == UT_Sequence) ||
162		(cls == ASN1_C_UNIV && type == CONS && tag == UT_Set)) {
163
164	    } else {
165		fflush(stdout);
166		warnx("indef form used on unsupported object");
167		return;
168	    }
169	    end_tag = 1;
170	    if (indefinite_form_loop > indefinite_form_loop_max) {
171		warnx("indefinite form used recursively more then %lu "
172		     "times, aborting", indefinite_form_loop_max);
173	    }
174	    indefinite_form_loop++;
175	    length = len;
176	} else if (length > len) {
177	    printf("\n");
178	    fflush(stdout);
179	    warnx("unreasonable inner length (%u) > %u",
180		  (unsigned)length, (unsigned)len);
181	    return;
182	}
183	if (cls == ASN1_C_CONTEXT || cls == ASN1_C_APPL) {
184
185	    if (type == CONS) {
186		size_t offset = buf - start_buf;
187		find_interesting(buf, length, interesting + offset);
188	    }
189	} else if (cls == ASN1_C_UNIV) {
190	    switch (tag) {
191	    case UT_EndOfContent:
192		break;
193	    case UT_Set :
194	    case UT_Sequence : {
195		find_interesting(buf, length, interesting);
196		break;
197	    }
198	    case UT_Integer :
199		memset(interesting, 'i', length);
200		break;
201	    case UT_OctetString : {
202		heim_octet_string str;
203		Der_class cls2;
204		Der_type type2;
205		unsigned int tag2;
206
207		ret = der_get_octet_string (buf, length, &str, NULL);
208		if (ret) {
209		    warnx( "der_get_octet_string: %d", ret);
210		    return;
211		}
212
213		ret = der_get_tag(str.data, str.length,
214				  &cls2, &type2, &tag2, &sz);
215		if (ret || sz > str.length || type2 != CONS || tag2 != UT_Sequence) {
216		    memset(interesting, 'o', length);
217		} else {
218		    find_interesting(str.data, str.length, interesting);
219		}
220		break;
221	    }
222	    case UT_IA5String :
223	    case UT_PrintableString :
224	    case UT_GeneralizedTime :
225	    case UT_GeneralString :
226	    case UT_VisibleString :
227	    case UT_UTF8String : {
228		memset(interesting, 's', length);
229		break;
230	    }
231	    case UT_OID:
232		memset(interesting, 'd', length);
233		break;
234	    case UT_Enumerated:
235		memset(interesting, 'i', length);
236		break;
237	    default :
238		break;
239	    }
240	}
241	if (end_tag) {
242	    if (indefinite_form_loop == 0)
243		errx(1, "internal error in indefinite form loop detection");
244	    indefinite_form_loop--;
245	}
246	interesting += length;
247	buf += length;
248	len -= length;
249    }
250}
251
252
253/*
254 *
255 */
256
257static unsigned long
258asn1_tries(size_t length)
259{
260    return length * 12;
261}
262
263static int
264asn1_fuzz(void **ctx, unsigned long iteration, uint8_t *data, size_t length)
265{
266    struct context *context;
267    int ret;
268
269    if (*ctx == NULL) {
270	context = calloc(1, sizeof(*context));
271
272	context->interesting = calloc(1, length + 1);
273	if (context->interesting == NULL)
274	    abort();
275	context->size = length;
276
277	context->interesting[length] = 'e';
278
279	find_interesting(data, length, context->interesting);
280
281	if (context->interesting[length] != 'e') {
282	    printf("find_interesting corrupted interesting buffer\n");
283	    return 1;
284	}
285
286	if (ctx)
287	    *ctx = context;
288
289#if 1
290	size_t s;
291	for (s = 0; s < length; s++) {
292	    if (context->interesting[s]) {
293		printf("%c", context->interesting[s]);
294	    } else {
295		printf(".");
296	    }
297	}
298	printf("\n");
299#endif
300
301    } else {
302	context = *ctx;
303    }
304
305    context->iteration = iteration;
306
307    do {
308        ret = permuntations[context->permutation].perm(context, data, length);
309	if (ret)
310	    context->permutation++;
311
312    } while (ret != 0 && context->permutation < sizeof(permuntations)/sizeof(permuntations[0]));
313
314    return ret;
315}
316
317static void
318asn1_fuzz_free(void *ctx)
319{
320    struct context *context = (struct context *)ctx;
321    free(context->interesting);
322    free(context);
323}
324
325
326const struct heim_fuzz_type_data __heim_fuzz_asn1 = {
327    "asn1",
328    asn1_tries,
329    asn1_fuzz,
330    asn1_fuzz_free
331};
332
333