1/*
2 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 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 KTH nor the names of its contributors may be
20 *    used to endorse or promote products derived from this software without
21 *    specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
30 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#include "krb5/gsskrb5_locl.h"
37#include <err.h>
38#include <getarg.h>
39#include <gssapi.h>
40#include <gssapi_krb5.h>
41#include <gssapi_spnego.h>
42#include <gssapi_ntlm.h>
43#include <gssapi_spi.h>
44#include "test_common.h"
45#include "fuzzer.h"
46
47static char *type_string;
48static char *mech_string;
49static char *cred_string;
50static char *client_name;
51
52static int max_loops = 20;
53static int fuzzer_loops = 1000000;
54static int fuzzer_failure = 0;
55static int fuzzer_slipped_by = 0;
56static int version_flag = 0;
57static char *dumpfile_string = NULL;
58static int verbose_flag = 0;
59static int help_flag	= 0;
60
61enum return_value {
62    NEXT_ITERATION = 0,
63    NEXT_TARGET = 1,
64    NEXT_FUZZER = 2
65};
66
67static heim_fuzz_type_t fuzzers[] = {
68    HEIM_FUZZ_RANDOM,
69    HEIM_FUZZ_BITFLIP,
70    HEIM_FUZZ_BYTEFLIP,
71    HEIM_FUZZ_SHORTFLIP,
72    HEIM_FUZZ_WORDFLIP,
73    HEIM_FUZZ_INTERESTING8,
74    HEIM_FUZZ_INTERESTING16,
75    HEIM_FUZZ_INTERESTING32,
76#if 0
77    HEIM_FUZZ_ASN1,
78#endif
79    NULL
80};
81
82static enum return_value
83loop(heim_fuzz_type_t fuzzer, gss_OID mechoid, gss_const_OID nameoid, const char *target, gss_cred_id_t init_cred,
84     unsigned long target_loop,
85     unsigned long iteration_count,
86     void **fuzzer_context)
87{
88    int server_done = 0, client_done = 0;
89    int num_loops = 0;
90    unsigned long current_target = 0;
91    OM_uint32 maj_stat, min_stat;
92    gss_name_t gss_target_name = GSS_C_NO_NAME;
93    gss_buffer_desc input_token, output_token;
94    OM_uint32 flags = 0, ret_cflags, ret_sflags;
95    gss_OID actual_mech_client;
96    gss_OID actual_mech_server;
97    gss_ctx_id_t cctx = NULL, sctx = NULL;
98    int fuzzer_changed = 0;
99    enum return_value return_value = NEXT_ITERATION;
100
101    flags |= GSS_C_INTEG_FLAG;
102    flags |= GSS_C_CONF_FLAG;
103    flags |= GSS_C_MUTUAL_FLAG;
104
105    input_token.value = rk_UNCONST(target);
106    input_token.length = strlen(target);
107
108    maj_stat = gss_import_name(&min_stat,
109			       &input_token,
110			       nameoid,
111			       &gss_target_name);
112    if (GSS_ERROR(maj_stat))
113	err(1, "import name creds failed with: %d", maj_stat);
114
115    input_token.length = 0;
116    input_token.value = NULL;
117
118    output_token.length = 0;
119    output_token.value = NULL;
120
121    while (!server_done || !client_done) {
122
123	num_loops++;
124
125	if (max_loops && max_loops < num_loops) {
126	    errx(1, "num loops %d was larger then max loops %d",
127		 num_loops, max_loops);
128	}
129
130	if (target_loop == current_target) {
131	    uint8_t *data = (uint8_t *)input_token.value;
132
133	    fuzzer_changed = 1;
134
135	    if (heim_fuzzer(fuzzer, fuzzer_context, iteration_count,  data, input_token.length)) {
136		heim_fuzzer_free(fuzzer, *fuzzer_context);
137		*fuzzer_context = NULL;
138		return_value = NEXT_TARGET;
139		goto out;
140	    }
141
142	    if (dumpfile_string)
143		rk_dumpdata(dumpfile_string, data, input_token.length);
144	}
145	current_target++;
146
147	maj_stat = gss_init_sec_context(&min_stat,
148					init_cred,
149					&cctx,
150					gss_target_name,
151					mechoid,
152					flags,
153					0,
154					GSS_C_NO_CHANNEL_BINDINGS,
155					&input_token,
156					&actual_mech_client,
157					&output_token,
158					&ret_cflags,
159					NULL);
160	if (dumpfile_string)
161	    unlink(dumpfile_string);
162
163	if (GSS_ERROR(maj_stat)) {
164	    if (verbose_flag)
165		warnx("init_sec_context: %s",
166		      gssapi_err(maj_stat, min_stat, mechoid));
167	    fuzzer_failure++;
168	    goto out;
169	}
170
171	if (maj_stat & GSS_S_CONTINUE_NEEDED)
172	    ;
173	else
174	    client_done = 1;
175
176
177	if (client_done && server_done)
178	    break;
179
180	if (input_token.length != 0)
181	    gss_release_buffer(&min_stat, &input_token);
182
183	if (target_loop == current_target) {
184	    uint8_t *data = (uint8_t *)output_token.value;
185
186	    fuzzer_changed = 1;
187
188	    if (heim_fuzzer(fuzzer, fuzzer_context, iteration_count,  data, output_token.length)) {
189		heim_fuzzer_free(fuzzer, *fuzzer_context);
190		*fuzzer_context = NULL;
191		return_value = NEXT_TARGET;
192		goto out;
193	    }
194
195	    if (dumpfile_string)
196		rk_dumpdata(dumpfile_string, data, input_token.length);
197	}
198	current_target++;
199
200	maj_stat = gss_accept_sec_context(&min_stat,
201					  &sctx,
202					  GSS_C_NO_CREDENTIAL,
203					  &output_token,
204					  GSS_C_NO_CHANNEL_BINDINGS,
205					  NULL,
206					  &actual_mech_server,
207					  &input_token,
208					  &ret_sflags,
209					  NULL,
210					  NULL);
211	if (dumpfile_string)
212	    unlink(dumpfile_string);
213
214	if (GSS_ERROR(maj_stat)) {
215	    if (verbose_flag)
216		warnx("accept_sec_context: %s",
217		      gssapi_err(maj_stat, min_stat, actual_mech_server));
218	    fuzzer_failure++;
219	    goto out;
220	}
221
222
223	if (output_token.length != 0)
224	    gss_release_buffer(&min_stat, &output_token);
225
226	if (maj_stat & GSS_S_CONTINUE_NEEDED)
227	    ;
228	else
229	    server_done = 1;
230
231    }
232
233    if (client_done && server_done) {
234	if (!fuzzer_changed)
235	    return_value = NEXT_FUZZER;
236	else
237	    fuzzer_slipped_by++;
238    }
239
240 out:
241    gss_delete_sec_context(&min_stat, &cctx, NULL);
242    gss_delete_sec_context(&min_stat, &sctx, NULL);
243
244    if (output_token.length != 0)
245	gss_release_buffer(&min_stat, &output_token);
246    if (input_token.length != 0)
247	gss_release_buffer(&min_stat, &input_token);
248    gss_release_name(&min_stat, &gss_target_name);
249
250    return return_value;
251}
252
253
254/*
255 *
256 */
257
258static struct getargs args[] = {
259    {"name-type",0,	arg_string, &type_string,  "type of name", NULL },
260    {"mech-type",0,	arg_string, &mech_string,  "type of mech", NULL },
261
262    {"cred-type",0,	arg_string,     &cred_string,  "type of cred", NULL },
263    {"client-name", 0,  arg_string,     &client_name, "client name", NULL },
264
265    {"max-loops",	0, arg_integer,	&max_loops, "times", NULL },
266    {"fuzzer-loops",	0, arg_integer,	&fuzzer_loops, "times", NULL },
267    {"dump-data",	0, arg_string,	&dumpfile_string, "file", NULL },
268    {"version",	0,	arg_flag,	&version_flag, "print version", NULL },
269    {"verbose",	'v',	arg_flag,	&verbose_flag, "verbose", NULL },
270    {"help",	0,	arg_flag,	&help_flag,  NULL, NULL }
271};
272
273static void
274usage (int ret)
275{
276    arg_printusage (args, sizeof(args)/sizeof(*args),
277		    NULL, "service@host");
278    exit (ret);
279}
280
281int
282main(int argc, char **argv)
283{
284    int optidx = 0;
285    OM_uint32 min_stat, maj_stat;
286    gss_const_OID nameoid, mechoid;
287    gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL;
288    gss_name_t cname = GSS_C_NO_NAME;
289    unsigned long current_target, current_iteration, current_fuzzer;
290    unsigned long n, divN;
291    int end_of_fuzzer = 0;
292    int ttyp = isatty(STDIN_FILENO);
293    void *fuzzer_context = NULL;
294
295    setprogname(argv[0]);
296
297    if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
298	usage(1);
299
300    if (help_flag)
301	usage (0);
302
303    if(version_flag){
304	print_version(NULL);
305	exit(0);
306    }
307
308    argc -= optidx;
309    argv += optidx;
310
311    if (argc != 1)
312	usage(1);
313
314    if (fuzzer_loops < 0)
315	errx(1, "invalid number of loops");
316
317    if (type_string == NULL)
318	nameoid = GSS_C_NT_HOSTBASED_SERVICE;
319    else if (strcmp(type_string, "hostbased-service") == 0)
320	nameoid = GSS_C_NT_HOSTBASED_SERVICE;
321    else if (strcmp(type_string, "krb5-principal-name") == 0)
322	nameoid = GSS_KRB5_NT_PRINCIPAL_NAME;
323    else if (strcmp(type_string, "krb5-principal-name-referral") == 0)
324	nameoid = GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL;
325    else
326	errx(1, "%s not suppported", type_string);
327
328    if (mech_string == NULL)
329	mechoid = GSS_KRB5_MECHANISM;
330    else {
331	mechoid = gss_name_to_oid(mech_string);
332	if (mechoid == GSS_C_NO_OID)
333	    errx(1, "failed to find mech oid: %s", mech_string);
334    }
335
336    if (client_name) {
337	gss_buffer_desc cn;
338	gss_const_OID credoid = GSS_C_NO_OID;
339	gss_OID_set mechs = GSS_C_NULL_OID_SET;
340
341	if (cred_string) {
342	    credoid = gss_name_to_oid(cred_string);
343	    if (credoid == GSS_C_NO_OID)
344		errx(1, "failed to find cred oid: %s", cred_string);
345	}
346
347	cn.value = client_name;
348	cn.length = strlen(client_name);
349
350	maj_stat = gss_import_name(&min_stat, &cn, GSS_C_NT_USER_NAME, &cname);
351	if (maj_stat)
352	    errx(1, "gss_import_name: %s",
353		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
354
355
356
357	if (credoid != GSS_C_NO_OID) {
358	    maj_stat = gss_create_empty_oid_set(&min_stat, &mechs);
359	    if (maj_stat != GSS_S_COMPLETE)
360		errx(1, "gss_create_empty_oid_set: %s",
361		     gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
362
363	    maj_stat = gss_add_oid_set_member(&min_stat, credoid, &mechs);
364	    if (maj_stat != GSS_S_COMPLETE)
365		errx(1, "gss_add_oid_set_member: %s",
366		     gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
367	}
368
369	maj_stat = gss_acquire_cred(&min_stat, cname, 0, mechs,
370				    GSS_C_INITIATE, &client_cred, NULL, NULL);
371	if (GSS_ERROR(maj_stat))
372	    errx(1, "gss_import_name: %s",
373		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
374
375	if (mechs != GSS_C_NULL_OID_SET)
376	    gss_release_oid_set(&min_stat, &mechs);
377
378	gss_release_name(&min_stat, &cname);
379
380    } else {
381	maj_stat = gss_acquire_cred(&min_stat,
382				    cname,
383				    GSS_C_INDEFINITE,
384				    GSS_C_NO_OID_SET,
385				    GSS_C_INITIATE,
386				    &client_cred,
387				    NULL,
388				    NULL);
389	if (GSS_ERROR(maj_stat))
390	    errx(1, "gss_acquire_cred: %s",
391		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
392    }
393
394    /*
395     *
396     */
397
398    divN = fuzzer_loops / 10;
399    if (divN == 0)
400	divN = 1;
401
402    current_fuzzer = current_target = current_iteration = 0;
403
404    for (n = 1; n <= (unsigned long)fuzzer_loops && !end_of_fuzzer; n++) {
405	enum return_value return_value;
406
407	return_value = loop(fuzzers[current_fuzzer], rk_UNCONST(mechoid), nameoid,
408			    argv[0], client_cred, current_target, current_iteration, &fuzzer_context);
409	switch (return_value) {
410	case NEXT_ITERATION:
411	    current_iteration++;
412	    break;
413
414	case NEXT_TARGET:
415	    if (fuzzer_context != NULL)
416		errx(1, "fuzzer context not NULL at next target state?");
417	    if (ttyp)
418		printf("\b");
419	    printf("fuzzer %s targets next step (%lu) at: %lu\n",
420		   heim_fuzzer_name(fuzzers[current_fuzzer]),
421		   current_target + 1,
422		   (unsigned long)current_iteration);
423	    current_iteration = 0;
424	    current_target++;
425	    break;
426
427	case NEXT_FUZZER:
428	    if (ttyp)
429		printf("\b");
430	    printf("fuzzer %s done at: %lu\n",
431		   heim_fuzzer_name(fuzzers[current_fuzzer]), n);
432	    current_target = 0;
433	    current_iteration = 0;
434
435	    current_fuzzer++;
436	    if (fuzzers[current_fuzzer] == NULL)
437		end_of_fuzzer = 1;
438	    break;
439	}
440
441	if ((n % divN) == 0) {
442	    if (ttyp)
443		printf("\b");
444	    printf(" try %lu\n", (unsigned long)n);
445	} else if (ttyp && (n & 0xff) == 0) {
446	    printf("\b%d", (int)((n >> 8) % 10));
447	}
448    }
449
450    if (ttyp)
451	printf("\b");
452
453    if (!end_of_fuzzer)
454	printf("fuzzer: %s step: %lu iteration: %lu\n",
455	       heim_fuzzer_name(fuzzers[current_fuzzer]),
456	       current_target,
457	       current_iteration);
458
459    printf("fuzzer: tries: %lu failure: %d "
460	   "modified-but-non-failure: %d end-of-fuzzer: %d\n",
461	   (unsigned long)n,
462	   fuzzer_failure,
463	   fuzzer_slipped_by,
464	   end_of_fuzzer);
465
466    return 0;
467}
468