#include "krb5/gsskrb5_locl.h"
#include <err.h>
#include <getarg.h>
#include <gssapi.h>
#include <gssapi_krb5.h>
#include <gssapi_spnego.h>
#include <gssapi_ntlm.h>
#include <gssapi_spi.h>
#include "test_common.h"
#include "fuzzer.h"
static char *type_string;
static char *mech_string;
static char *cred_string;
static char *client_name;
static int max_loops = 20;
static int fuzzer_loops = 1000000;
static int fuzzer_failure = 0;
static int fuzzer_slipped_by = 0;
static int version_flag = 0;
static char *dumpfile_string = NULL;
static int verbose_flag = 0;
static int help_flag = 0;
enum return_value {
NEXT_ITERATION = 0,
NEXT_TARGET = 1,
NEXT_FUZZER = 2
};
static heim_fuzz_type_t fuzzers[] = {
HEIM_FUZZ_RANDOM,
HEIM_FUZZ_BITFLIP,
HEIM_FUZZ_BYTEFLIP,
HEIM_FUZZ_SHORTFLIP,
HEIM_FUZZ_WORDFLIP,
HEIM_FUZZ_INTERESTING8,
HEIM_FUZZ_INTERESTING16,
HEIM_FUZZ_INTERESTING32,
#if 0
HEIM_FUZZ_ASN1,
#endif
NULL
};
static enum return_value
loop(heim_fuzz_type_t fuzzer, gss_OID mechoid, gss_const_OID nameoid, const char *target, gss_cred_id_t init_cred,
unsigned long target_loop,
unsigned long iteration_count,
void **fuzzer_context)
{
int server_done = 0, client_done = 0;
int num_loops = 0;
unsigned long current_target = 0;
OM_uint32 maj_stat, min_stat;
gss_name_t gss_target_name = GSS_C_NO_NAME;
gss_buffer_desc input_token, output_token;
OM_uint32 flags = 0, ret_cflags, ret_sflags;
gss_OID actual_mech_client;
gss_OID actual_mech_server;
gss_ctx_id_t cctx = NULL, sctx = NULL;
int fuzzer_changed = 0;
enum return_value return_value = NEXT_ITERATION;
flags |= GSS_C_INTEG_FLAG;
flags |= GSS_C_CONF_FLAG;
flags |= GSS_C_MUTUAL_FLAG;
input_token.value = rk_UNCONST(target);
input_token.length = strlen(target);
maj_stat = gss_import_name(&min_stat,
&input_token,
nameoid,
&gss_target_name);
if (GSS_ERROR(maj_stat))
err(1, "import name creds failed with: %d", maj_stat);
input_token.length = 0;
input_token.value = NULL;
output_token.length = 0;
output_token.value = NULL;
while (!server_done || !client_done) {
num_loops++;
if (max_loops && max_loops < num_loops) {
errx(1, "num loops %d was larger then max loops %d",
num_loops, max_loops);
}
if (target_loop == current_target) {
uint8_t *data = (uint8_t *)input_token.value;
fuzzer_changed = 1;
if (heim_fuzzer(fuzzer, fuzzer_context, iteration_count, data, input_token.length)) {
heim_fuzzer_free(fuzzer, *fuzzer_context);
*fuzzer_context = NULL;
return_value = NEXT_TARGET;
goto out;
}
if (dumpfile_string)
rk_dumpdata(dumpfile_string, data, input_token.length);
}
current_target++;
maj_stat = gss_init_sec_context(&min_stat,
init_cred,
&cctx,
gss_target_name,
mechoid,
flags,
0,
GSS_C_NO_CHANNEL_BINDINGS,
&input_token,
&actual_mech_client,
&output_token,
&ret_cflags,
NULL);
if (dumpfile_string)
unlink(dumpfile_string);
if (GSS_ERROR(maj_stat)) {
if (verbose_flag)
warnx("init_sec_context: %s",
gssapi_err(maj_stat, min_stat, mechoid));
fuzzer_failure++;
goto out;
}
if (maj_stat & GSS_S_CONTINUE_NEEDED)
;
else
client_done = 1;
if (client_done && server_done)
break;
if (input_token.length != 0)
gss_release_buffer(&min_stat, &input_token);
if (target_loop == current_target) {
uint8_t *data = (uint8_t *)output_token.value;
fuzzer_changed = 1;
if (heim_fuzzer(fuzzer, fuzzer_context, iteration_count, data, output_token.length)) {
heim_fuzzer_free(fuzzer, *fuzzer_context);
*fuzzer_context = NULL;
return_value = NEXT_TARGET;
goto out;
}
if (dumpfile_string)
rk_dumpdata(dumpfile_string, data, input_token.length);
}
current_target++;
maj_stat = gss_accept_sec_context(&min_stat,
&sctx,
GSS_C_NO_CREDENTIAL,
&output_token,
GSS_C_NO_CHANNEL_BINDINGS,
NULL,
&actual_mech_server,
&input_token,
&ret_sflags,
NULL,
NULL);
if (dumpfile_string)
unlink(dumpfile_string);
if (GSS_ERROR(maj_stat)) {
if (verbose_flag)
warnx("accept_sec_context: %s",
gssapi_err(maj_stat, min_stat, actual_mech_server));
fuzzer_failure++;
goto out;
}
if (output_token.length != 0)
gss_release_buffer(&min_stat, &output_token);
if (maj_stat & GSS_S_CONTINUE_NEEDED)
;
else
server_done = 1;
}
if (client_done && server_done) {
if (!fuzzer_changed)
return_value = NEXT_FUZZER;
else
fuzzer_slipped_by++;
}
out:
gss_delete_sec_context(&min_stat, &cctx, NULL);
gss_delete_sec_context(&min_stat, &sctx, NULL);
if (output_token.length != 0)
gss_release_buffer(&min_stat, &output_token);
if (input_token.length != 0)
gss_release_buffer(&min_stat, &input_token);
gss_release_name(&min_stat, &gss_target_name);
return return_value;
}
static struct getargs args[] = {
{"name-type",0, arg_string, &type_string, "type of name", NULL },
{"mech-type",0, arg_string, &mech_string, "type of mech", NULL },
{"cred-type",0, arg_string, &cred_string, "type of cred", NULL },
{"client-name", 0, arg_string, &client_name, "client name", NULL },
{"max-loops", 0, arg_integer, &max_loops, "times", NULL },
{"fuzzer-loops", 0, arg_integer, &fuzzer_loops, "times", NULL },
{"dump-data", 0, arg_string, &dumpfile_string, "file", NULL },
{"version", 0, arg_flag, &version_flag, "print version", NULL },
{"verbose", 'v', arg_flag, &verbose_flag, "verbose", NULL },
{"help", 0, arg_flag, &help_flag, NULL, NULL }
};
static void
usage (int ret)
{
arg_printusage (args, sizeof(args)/sizeof(*args),
NULL, "service@host");
exit (ret);
}
int
main(int argc, char **argv)
{
int optidx = 0;
OM_uint32 min_stat, maj_stat;
gss_const_OID nameoid, mechoid;
gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL;
gss_name_t cname = GSS_C_NO_NAME;
unsigned long current_target, current_iteration, current_fuzzer;
unsigned long n, divN;
int end_of_fuzzer = 0;
int ttyp = isatty(STDIN_FILENO);
void *fuzzer_context = NULL;
setprogname(argv[0]);
if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
usage(1);
if (help_flag)
usage (0);
if(version_flag){
print_version(NULL);
exit(0);
}
argc -= optidx;
argv += optidx;
if (argc != 1)
usage(1);
if (fuzzer_loops < 0)
errx(1, "invalid number of loops");
if (type_string == NULL)
nameoid = GSS_C_NT_HOSTBASED_SERVICE;
else if (strcmp(type_string, "hostbased-service") == 0)
nameoid = GSS_C_NT_HOSTBASED_SERVICE;
else if (strcmp(type_string, "krb5-principal-name") == 0)
nameoid = GSS_KRB5_NT_PRINCIPAL_NAME;
else if (strcmp(type_string, "krb5-principal-name-referral") == 0)
nameoid = GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL;
else
errx(1, "%s not suppported", type_string);
if (mech_string == NULL)
mechoid = GSS_KRB5_MECHANISM;
else {
mechoid = gss_name_to_oid(mech_string);
if (mechoid == GSS_C_NO_OID)
errx(1, "failed to find mech oid: %s", mech_string);
}
if (client_name) {
gss_buffer_desc cn;
gss_const_OID credoid = GSS_C_NO_OID;
gss_OID_set mechs = GSS_C_NULL_OID_SET;
if (cred_string) {
credoid = gss_name_to_oid(cred_string);
if (credoid == GSS_C_NO_OID)
errx(1, "failed to find cred oid: %s", cred_string);
}
cn.value = client_name;
cn.length = strlen(client_name);
maj_stat = gss_import_name(&min_stat, &cn, GSS_C_NT_USER_NAME, &cname);
if (maj_stat)
errx(1, "gss_import_name: %s",
gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
if (credoid != GSS_C_NO_OID) {
maj_stat = gss_create_empty_oid_set(&min_stat, &mechs);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_create_empty_oid_set: %s",
gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
maj_stat = gss_add_oid_set_member(&min_stat, credoid, &mechs);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_add_oid_set_member: %s",
gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
}
maj_stat = gss_acquire_cred(&min_stat, cname, 0, mechs,
GSS_C_INITIATE, &client_cred, NULL, NULL);
if (GSS_ERROR(maj_stat))
errx(1, "gss_import_name: %s",
gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
if (mechs != GSS_C_NULL_OID_SET)
gss_release_oid_set(&min_stat, &mechs);
gss_release_name(&min_stat, &cname);
} else {
maj_stat = gss_acquire_cred(&min_stat,
cname,
GSS_C_INDEFINITE,
GSS_C_NO_OID_SET,
GSS_C_INITIATE,
&client_cred,
NULL,
NULL);
if (GSS_ERROR(maj_stat))
errx(1, "gss_acquire_cred: %s",
gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
}
divN = fuzzer_loops / 10;
if (divN == 0)
divN = 1;
current_fuzzer = current_target = current_iteration = 0;
for (n = 1; n <= (unsigned long)fuzzer_loops && !end_of_fuzzer; n++) {
enum return_value return_value;
return_value = loop(fuzzers[current_fuzzer], rk_UNCONST(mechoid), nameoid,
argv[0], client_cred, current_target, current_iteration, &fuzzer_context);
switch (return_value) {
case NEXT_ITERATION:
current_iteration++;
break;
case NEXT_TARGET:
if (fuzzer_context != NULL)
errx(1, "fuzzer context not NULL at next target state?");
if (ttyp)
printf("\b");
printf("fuzzer %s targets next step (%lu) at: %lu\n",
heim_fuzzer_name(fuzzers[current_fuzzer]),
current_target + 1,
(unsigned long)current_iteration);
current_iteration = 0;
current_target++;
break;
case NEXT_FUZZER:
if (ttyp)
printf("\b");
printf("fuzzer %s done at: %lu\n",
heim_fuzzer_name(fuzzers[current_fuzzer]), n);
current_target = 0;
current_iteration = 0;
current_fuzzer++;
if (fuzzers[current_fuzzer] == NULL)
end_of_fuzzer = 1;
break;
}
if ((n % divN) == 0) {
if (ttyp)
printf("\b");
printf(" try %lu\n", (unsigned long)n);
} else if (ttyp && (n & 0xff) == 0) {
printf("\b%d", (int)((n >> 8) % 10));
}
}
if (ttyp)
printf("\b");
if (!end_of_fuzzer)
printf("fuzzer: %s step: %lu iteration: %lu\n",
heim_fuzzer_name(fuzzers[current_fuzzer]),
current_target,
current_iteration);
printf("fuzzer: tries: %lu failure: %d "
"modified-but-non-failure: %d end-of-fuzzer: %d\n",
(unsigned long)n,
fuzzer_failure,
fuzzer_slipped_by,
end_of_fuzzer);
return 0;
}