From f4a8c0fda84ec5ebafb68506ff0059f3dbeae396 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Fri, 18 Apr 2014 21:39:54 -0400 Subject: [PATCH 01/13] [libfko] for fuzzing purposes, added fko_set_encoded_data() to bypass encryption and authentication for SPA payloads --- lib/fko.h | 2 ++ lib/fko_encode.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/lib/fko.h b/lib/fko.h index 4c301b07..f4e5f0b4 100644 --- a/lib/fko.h +++ b/lib/fko.h @@ -381,6 +381,8 @@ DLL_API int fko_set_spa_hmac(fko_ctx_t ctx, const char * const hmac_key, DLL_API int fko_get_spa_hmac(fko_ctx_t ctx, char **enc_data); DLL_API int fko_get_encoded_data(fko_ctx_t ctx, char **enc_data); +DLL_API int fko_set_encoded_data(fko_ctx_t ctx, const char * const encoded_msg, + const int msg_len, const int do_digest, const int digest_type); /* Get context data functions */ diff --git a/lib/fko_encode.c b/lib/fko_encode.c index 889efeb5..06128cfe 100644 --- a/lib/fko_encode.c +++ b/lib/fko_encode.c @@ -241,4 +241,74 @@ fko_get_encoded_data(fko_ctx_t ctx, char **enc_msg) return(FKO_SUCCESS); } +/* Set the fko SPA encoded data (this is a convenience + * function mostly used for tests that involve fuzzing). +*/ +int +fko_set_encoded_data(fko_ctx_t ctx, + const char * const encoded_msg, const int msg_len, + const int require_digest, const int digest_type) +{ + char *tbuf = NULL; + int res = FKO_SUCCESS, mlen; + + /* Must be initialized + */ + if(!CTX_INITIALIZED(ctx)) + return(FKO_ERROR_CTX_NOT_INITIALIZED); + + if(encoded_msg == NULL) + return(FKO_ERROR_INVALID_DATA); + + ctx->encoded_msg = strdup(encoded_msg); + + ctx->state |= FKO_DATA_MODIFIED; + + if(ctx->encoded_msg == NULL) + return(FKO_ERROR_MEMORY_ALLOCATION); + + /* allow arbitrary length (i.e. let the decode routines validate + * SPA message length). + */ + ctx->encoded_msg_len = msg_len; + + if(require_digest) + { + fko_set_spa_digest_type(ctx, FKO_DIGEST_SHA256); + if((res = fko_set_spa_digest(ctx)) != FKO_SUCCESS) + { + return res; + } + + /* append the digest to the encoded message buffer + */ + mlen = ctx->encoded_msg_len + ctx->digest_len + 2; + tbuf = calloc(1, mlen); + if(tbuf == NULL) + return(FKO_ERROR_MEMORY_ALLOCATION); + + /* memcpy since the provided encoded buffer might + * have an embedded NULL? + */ + mlen = snprintf(tbuf, mlen, "%s:%s", ctx->encoded_msg, ctx->digest); + + if(ctx->encoded_msg != NULL) + free(ctx->encoded_msg); + + ctx->encoded_msg = strdup(tbuf); + if(ctx->encoded_msg == NULL) + { + free(tbuf); + return(FKO_ERROR_MEMORY_ALLOCATION); + } + + ctx->encoded_msg_len = mlen; + free(tbuf); + } + + FKO_CLEAR_SPA_DATA_MODIFIED(ctx); + + return(FKO_SUCCESS); +} + /***EOF***/ From 63a59bf48b2cbea3755bb774b2007ffd8d881c54 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Tue, 22 Apr 2014 20:58:03 -0400 Subject: [PATCH 02/13] [test suite] add --enable-fuzzing-interfaces, fix profile coverage file handling --- test/test-fwknop.pl | 141 +++++++++++++++++++------------------------- 1 file changed, 62 insertions(+), 79 deletions(-) diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index 36fb3376..5a2f8ac2 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -28,6 +28,7 @@ my $data_tmp = 'data.tmp'; my $key_tmp = 'key.tmp'; my $enc_save_tmp = 'openssl_save.enc'; my $test_suite_path = 'test-fwknop.pl'; +my $username = ''; my $gpg_dir_orig_tar = 'gpg_dirs_orig.tar.gz'; our $gpg_client_home_dir = "$conf_dir/client-gpg"; our $gpg_client_home_dir_no_pw = "$conf_dir/client-gpg-no-pw"; @@ -322,6 +323,7 @@ my $perl_libfko_constants_file = '../perl/FKO/lib/FKO_Constants.pl'; my $python_libfko_constants_file = '../python/fko.py'; my $fko_wrapper_dir = 'fko-wrapper'; my $python_spa_packet = ''; +my $enable_fuzzing_interfaces_tests = 0; my $enable_client_ip_resolve_test = 0; my $enable_all = 0; my $saved_last_results = 0; @@ -431,6 +433,7 @@ exit 1 unless GetOptions( 'valgrind-disable-suppressions' => \$valgrind_disable_suppressions, 'valgrind-disable-child-silent' => \$valgrind_disable_child_silent, 'enable-all' => \$enable_all, + 'enable-fuzzing-interfaces-tests' => \$enable_fuzzing_interfaces_tests, 'valgrind-path=s' => \$valgrind_path, 'valgrind-suppression-file' => \$valgrind_suppressions_file, ### can set the following to "output.last/valgrind-coverage" if @@ -1123,6 +1126,15 @@ sub compile_warnings() { my $curr_pwd = cwd() or die $!; + if ($enable_profile_coverage_check) { + ### we're recompiling, so remove any existing profile coverage + ### files since they will be invalidated by the recompile + for my $extension ('*.gcno', '*.gcda', '*.gcov') { + ### remove profile output from any previous run + system qq{find .. -name $extension | xargs rm 2> /dev/null}; + } + } + chdir '..' or die $!; ### 'make clean' as root @@ -1133,10 +1145,6 @@ sub compile_warnings() { } if ($sudo_path) { - my $username = getpwuid((stat("test/$test_suite_path"))[4]); - die "[*] Could not determine test/$test_suite_path owner" - unless $username; - unless (&run_cmd("$sudo_path -u $username make", $cmd_out_tmp, "test/$curr_test_file")) { unless (&run_cmd('make', $cmd_out_tmp, @@ -1220,6 +1228,13 @@ sub profile_coverage() { $cmd_out_tmp, $curr_test_file); } + if ($username) { + for my $extension ('*.gcno', '*.gcda', '*.gcov') { + ### remove profile output from any previous run + system qq{find .. -name $extension | xargs chown $username}; + } + } + return 1; } @@ -5993,6 +6008,12 @@ sub init() { $git_path = &find_command('git') unless $git_path; $perl_path = &find_command('perl') unless $perl_path; + if ($sudo_path) { + $username = getpwuid((stat($test_suite_path))[4]); + die "[*] Could not determine $test_suite_path owner" + unless $username; + } + ### On Mac OS X look for otool instead of ldd unless ($lib_view_cmd) { $lib_view_cmd = &find_command('otool'); @@ -6015,18 +6036,11 @@ sub init() { if ($gcov_path) { if ($enable_profile_coverage_check - and not $preserve_previous_coverage_files and not $list_mode) { if ($profile_coverage_init) { - print "[+] --profile-coverage-init mode, ", - "removing existing .gcda, .gcno, and .gcov files...\n"; - } - for my $extension ('*.gcno', '*.gcda', '*.gcov') { - ### remove profile output from any previous run - system qq{find .. -name $extension | xargs rm 2> /dev/null}; - } - if ($profile_coverage_init) { - print "[+] Recompiling fwknop...\n"; + print "[+] Recompiling fwknop and removing previous coverage files...\n"; + ### if we recompile then remove the .gcno files (which are + ### generated at compile time) &compile_warnings(); if (&file_find_regex([qr/profile\-arcs.*test\-coverage/], $MATCH_ALL, $APPEND_RESULTS, $curr_test_file)) { @@ -6250,8 +6264,7 @@ sub import_previous_valgrind_coverage_info() { return; } -sub compile_execute_fko_wrapper() { - my $rv = 1; +sub compile_wrapper() { unless (-d $fko_wrapper_dir) { &write_test_file("[-] fko wrapper directory " . @@ -6268,14 +6281,13 @@ sub compile_execute_fko_wrapper() { return 0; } - if ($sudo_path) { - my $username = getpwuid((stat("../$test_suite_path"))[4]); - die "[*] Could not determine ../$test_suite_path owner" - unless $username; + my $make_str = 'make'; + $make_str .= ' fuzzing' if $enable_fuzzing_interfaces_tests; - unless (&run_cmd("$sudo_path -u $username make", + if ($sudo_path) { + unless (&run_cmd("$sudo_path -u $username $make_str", "../$cmd_out_tmp", "../$curr_test_file")) { - unless (&run_cmd('make', "../$cmd_out_tmp", + unless (&run_cmd($make_str, "../$cmd_out_tmp", "../$curr_test_file")) { chdir '..' or die $!; return 0; @@ -6284,7 +6296,7 @@ sub compile_execute_fko_wrapper() { } else { - unless (&run_cmd('make', "../$cmd_out_tmp", + unless (&run_cmd($make_str, "../$cmd_out_tmp", "../$curr_test_file")) { chdir '..' or die $!; return 0; @@ -6298,74 +6310,45 @@ sub compile_execute_fko_wrapper() { return 0; } - &run_cmd('./run_valgrind.sh', "../$cmd_out_tmp", "../$curr_test_file"); - - $rv = 0 unless &file_find_regex([qr/no\sleaks\sare\spossible/], - $MATCH_ALL, $APPEND_RESULTS, "../$curr_test_file"); - chdir '..' or die $!; + return 1; +} + +sub compile_execute_fko_wrapper() { + + my $rv = &compile_wrapper(); + + if ($rv) { + + chdir $fko_wrapper_dir or die $!; + &run_cmd('./run_valgrind.sh', "../$cmd_out_tmp", "../$curr_test_file"); + + $rv = 0 unless &file_find_regex([qr/no\sleaks\sare\spossible/], + $MATCH_ALL, $APPEND_RESULTS, "../$curr_test_file"); + + chdir '..' or die $!; + } return $rv; } sub compile_execute_fko_wrapper_no_valgrind() { - my $rv = 1; - unless (-d $fko_wrapper_dir) { - &write_test_file("[-] fko wrapper directory " . - "$fko_wrapper_dir does not exist.\n", $curr_test_file); - return 0; - } + my $rv = &compile_wrapper(); - chdir $fko_wrapper_dir or die $!; + if ($rv) { + + chdir $fko_wrapper_dir or die $!; + &run_cmd('./run_no_valgrind.sh', "../$cmd_out_tmp", "../$curr_test_file"); - ### 'make clean' as root - unless (&run_cmd('make clean', "../$cmd_out_tmp", - "../$curr_test_file")) { chdir '..' or die $!; - return 0; - } - if ($sudo_path) { - my $username = getpwuid((stat("../$test_suite_path"))[4]); - die "[*] Could not determine ../$test_suite_path owner" - unless $username; - - unless (&run_cmd("$sudo_path -u $username make", - "../$cmd_out_tmp", "../$curr_test_file")) { - unless (&run_cmd('make', "../$cmd_out_tmp", - "../$curr_test_file")) { - chdir '..' or die $!; - return 0; - } + if (&file_find_regex([qr/segmentation\sfault/i, qr/core\sdumped/i], + $MATCH_ANY, $NO_APPEND_RESULTS, $curr_test_file)) { + &write_test_file("[-] crash message found in: $curr_test_file\n", + $curr_test_file); + $rv = 0; } - - } else { - - unless (&run_cmd('make', "../$cmd_out_tmp", - "../$curr_test_file")) { - chdir '..' or die $!; - return 0; - } - } - - unless (-e 'fko_wrapper' and -e 'run_no_valgrind.sh') { - &write_test_file("[-] fko_wrapper or run_valgrind.sh does not exist.\n", - "../$curr_test_file"); - chdir '..' or die $!; - return 0; - } - - &run_cmd('./run_no_valgrind.sh', - "../$cmd_out_tmp", "../$curr_test_file"); - - chdir '..' or die $!; - - if (&file_find_regex([qr/segmentation\sfault/i, qr/core\sdumped/i], - $MATCH_ANY, $NO_APPEND_RESULTS, $curr_test_file)) { - &write_test_file("[-] crash message found in: $curr_test_file\n", - $curr_test_file); - $rv = 0; } return $rv; From beb8df46432d46afe1b60bed132b03285fd86f0e Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Tue, 22 Apr 2014 21:00:16 -0400 Subject: [PATCH 03/13] [test suite] add python SPA packet payload fuzzer --- test/spa_fuzzing.py | 61 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100755 test/spa_fuzzing.py diff --git a/test/spa_fuzzing.py b/test/spa_fuzzing.py new file mode 100755 index 00000000..613ec708 --- /dev/null +++ b/test/spa_fuzzing.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# +# Purpose: This script generates SPA packet payloads that are designed to +# act as fuzzer against libfko SPA decoding routines. +# +# Fuzzing file format: +# +# +# +# SPA payload format (before: +# +# ::::: " + return + +def parse_cmdline(): + + ### parse command line args + parser = argparse.ArgumentParser() + + parser.add_argument("-c", "--max-packet-count", type=int, help="packet count", default=1000000) + + args = parser.parse_args() + return args + +if __name__ == "__main__": + main() From b28b8b5de124828f6987f26fc824a0a989c4f5b7 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Tue, 22 Apr 2014 21:58:09 -0400 Subject: [PATCH 04/13] [libfko] fix double free bug in SPA parser This commit fixes a double free condition discovered through the new python SPA payload fuzzer. This bug could be triggered in fwknopd with a malicious SPA payload but only when GnuPG is used. When Rijndael is used for SPA packet encryption, this bug cannot be triggered due to an length/format check towards the end of _rijndael_decrypt(). It should be noted that only a person in possession of the correct encryption and authentication GnuPG keys could trigger this bug. --- lib/fko_decode.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/fko_decode.c b/lib/fko_decode.c index 03d85969..778e1e35 100644 --- a/lib/fko_decode.c +++ b/lib/fko_decode.c @@ -455,20 +455,15 @@ static int parse_rand_val(char *tbuf, char **ndx, int *t_size, fko_ctx_t ctx) { if((*t_size = strcspn(*ndx, ":")) < FKO_RAND_VAL_SIZE) - { - free(tbuf); return(FKO_ERROR_INVALID_DATA_DECODE_RAND_MISSING); - } if(ctx->rand_val != NULL) free(ctx->rand_val); ctx->rand_val = calloc(1, FKO_RAND_VAL_SIZE+1); if(ctx->rand_val == NULL) - { - free(tbuf); return(FKO_ERROR_MEMORY_ALLOCATION); - } + ctx->rand_val = strncpy(ctx->rand_val, *ndx, FKO_RAND_VAL_SIZE); *ndx += *t_size + 1; From cd8a2493a7d0679bc2c7e02d49ed46c3831972bf Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Tue, 22 Apr 2014 23:20:06 -0400 Subject: [PATCH 05/13] [test suite] python fuzzer additional tests --- test/spa_fuzzing.py | 65 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/test/spa_fuzzing.py b/test/spa_fuzzing.py index 613ec708..4e1a7586 100755 --- a/test/spa_fuzzing.py +++ b/test/spa_fuzzing.py @@ -21,6 +21,13 @@ import argparse def main(): + ### a few constants + spa_success = 1 + spa_failure = 0 + spa_sha256 = 3 + do_digest = 1 + no_digest = 0 + args = parse_cmdline() print_hdr() @@ -29,18 +36,66 @@ def main(): pkt_id = 1 - #### non-ascii char tests + ### valid payload tests - all digest types + print "# valid payloads..." + for digest_type in range(0, 6): + print str(pkt_id), str(spa_success), str(do_digest), \ + str(digest_type), base64.b64encode(spa_payload) + pkt_id += 1 + + ### invalid digest types + print "# invalid digest types..." + for digest_type in [-1, 6, 7]: + print str(pkt_id), str(spa_success), str(do_digest), \ + str(digest_type), base64.b64encode(spa_payload) + pkt_id += 1 + + ### truncated lengths + print "# truncated lengths..." + for l in range(1, len(spa_payload)): + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(spa_payload[:l]) + pkt_id += 1 + for l in range(1, len(spa_payload)): + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(spa_payload[l:]) + pkt_id += 1 + + ### SPA payloads that are too long + print "# payloads too long..." + for l in [1, 10, 50, 127, 128, 129, 200, 399, \ + 400, 401, 500, 800, 1000, 1023, 1024, 1025, \ + 1200, 1499, 1500, 1501, 2000]: + for non_ascii in range(0, 5) + range(127, 130) + range(252, 255): + new_payload = spa_payload + for p in range(0, l): + new_payload += chr(non_ascii) + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(new_payload) + pkt_id += 1 + + ### additional embedded ':' chars + print "# additional embedded : chars..." + for pos in range(0, len(spa_payload)): + if spa_payload[pos] == ':': + continue + new_payload = list(spa_payload) + new_payload[pos] = ':' + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(''.join(new_payload)) + pkt_id += 1 + + ### non-ascii char tests + print "# non-ascii char tests..." for pos in range(0, len(spa_payload)): for non_ascii in range(0, 31) + range(127, 255): new_payload = list(spa_payload) new_payload[pos] = chr(non_ascii) ### write out the fuzzing line - print str(pkt_id) + " 0 1 3 " + base64.b64encode(''.join(new_payload)) + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(''.join(new_payload)) pkt_id += 1 - # for c in list(spa_payload): - # print c - return def print_hdr(): From b9e2a42c5c55286017020d5048e76f375aac060f Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Tue, 22 Apr 2014 23:48:13 -0400 Subject: [PATCH 06/13] [test suite] support multiple initial SPA payloads in the python fuzzer --- test/spa_fuzzing.py | 115 +++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/test/spa_fuzzing.py b/test/spa_fuzzing.py index 4e1a7586..b044abc4 100755 --- a/test/spa_fuzzing.py +++ b/test/spa_fuzzing.py @@ -32,70 +32,85 @@ def main(): print_hdr() - spa_payload = "1716411011200157:cm9vdA:1397329899:2.0.1:1:MTI3LjAuMC4yLHRjcC8yMw" + spa_payloads = [ + "1716411011200157:cm9vdA:1397329899:2.0.1:1:MTI3LjAuMC4yLHRjcC8yMw", + "1642197848921959:cm9vdA:1397329740:2.0.1:2:MTI3LjAuMC4yLHRjcC8yMg:MTkyLjE2OC4xLjIsMjI", + "1548062350109656:cm9vdA:1397330450:2.0.1:3:MTI3LjAuMC4yLHRjcC8yMg:2", + "1414212790438062:cm9vdA:1397329054:2.0.1:4:MTI3LjAuMC4yLHRjcC8yMg:MTkyLjE2OC4xMC4xLDEyMzQ1:1234", + "3184260168681452:c29tZXVzZXI:1397330288:2.0.1:4:MS4xLjEuMSx0Y3AvMjI:MS4yLjMuNCwxMjM0:10:GboVlHuyiwjxmHbH16vGvlKF", + "8148229791462660:cm9vdA:1397331007:2.0.1:5:MTI3LjAuMC4yLHRjcC8zNzE3Mg:MTI3LjAuMC4xLDIy", + "1918702109191551:cm9vdA:1397329052:2.0.1:6:MTI3LjAuMC4yLHRjcC8yMg:MTI3LjAuMC4xLDIy:1234" + ] pkt_id = 1 + payload_num = 0 - ### valid payload tests - all digest types - print "# valid payloads..." - for digest_type in range(0, 6): - print str(pkt_id), str(spa_success), str(do_digest), \ - str(digest_type), base64.b64encode(spa_payload) - pkt_id += 1 + for spa_payload in spa_payloads: - ### invalid digest types - print "# invalid digest types..." - for digest_type in [-1, 6, 7]: - print str(pkt_id), str(spa_success), str(do_digest), \ - str(digest_type), base64.b64encode(spa_payload) - pkt_id += 1 + payload_num += 1 - ### truncated lengths - print "# truncated lengths..." - for l in range(1, len(spa_payload)): - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(spa_payload[:l]) - pkt_id += 1 - for l in range(1, len(spa_payload)): - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(spa_payload[l:]) - pkt_id += 1 + print "# start tests with payload: " + spa_payload - ### SPA payloads that are too long - print "# payloads too long..." - for l in [1, 10, 50, 127, 128, 129, 200, 399, \ - 400, 401, 500, 800, 1000, 1023, 1024, 1025, \ - 1200, 1499, 1500, 1501, 2000]: - for non_ascii in range(0, 5) + range(127, 130) + range(252, 255): - new_payload = spa_payload - for p in range(0, l): - new_payload += chr(non_ascii) - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(new_payload) + ### valid payload tests - all digest types + print "# payload " + str(payload_num) + " valid payload + valid digest types..." + for digest_type in range(0, 6): + print str(pkt_id), str(spa_success), str(do_digest), \ + str(digest_type), base64.b64encode(spa_payload) pkt_id += 1 - ### additional embedded ':' chars - print "# additional embedded : chars..." - for pos in range(0, len(spa_payload)): - if spa_payload[pos] == ':': - continue - new_payload = list(spa_payload) - new_payload[pos] = ':' - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(''.join(new_payload)) - pkt_id += 1 + ### invalid digest types + print "# payload " + str(payload_num) + " invalid digest types..." + for digest_type in [-1, 6, 7]: + print str(pkt_id), str(spa_success), str(do_digest), \ + str(digest_type), base64.b64encode(spa_payload) + pkt_id += 1 - ### non-ascii char tests - print "# non-ascii char tests..." - for pos in range(0, len(spa_payload)): - for non_ascii in range(0, 31) + range(127, 255): + ### truncated lengths + print "# payload " + str(payload_num) + " truncated lengths..." + for l in range(1, len(spa_payload)): + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(spa_payload[:l]) + pkt_id += 1 + for l in range(1, len(spa_payload)): + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(spa_payload[l:]) + pkt_id += 1 + + ### SPA payloads that are too long + print "# payload " + str(payload_num) + " payloads too long..." + for l in [1, 10, 50, 127, 128, 129, 200, 399, \ + 400, 401, 500, 800, 1000, 1023, 1024, 1025, \ + 1200, 1499, 1500, 1501, 2000]: + for non_ascii in range(0, 5) + range(127, 130) + range(252, 255): + new_payload = spa_payload + for p in range(0, l): + new_payload += chr(non_ascii) + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(new_payload) + pkt_id += 1 + + ### additional embedded ':' chars + print "# payload " + str(payload_num) + " additional embedded : chars..." + for pos in range(0, len(spa_payload)): + if spa_payload[pos] == ':': + continue new_payload = list(spa_payload) - new_payload[pos] = chr(non_ascii) - ### write out the fuzzing line + new_payload[pos] = ':' print str(pkt_id), str(spa_failure), str(do_digest), \ str(spa_sha256), base64.b64encode(''.join(new_payload)) pkt_id += 1 + ### non-ascii char tests + print "# payload " + str(payload_num) + " non-ascii char tests..." + for pos in range(0, len(spa_payload)): + for non_ascii in range(0, 31) + range(127, 255): + new_payload = list(spa_payload) + new_payload[pos] = chr(non_ascii) + ### write out the fuzzing line + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(''.join(new_payload)) + pkt_id += 1 + return def print_hdr(): From 4b11232249a89e4b917779546f6beee2d9e17a91 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Wed, 23 Apr 2014 23:31:37 -0400 Subject: [PATCH 07/13] [test suite] add command mode SPA payload and splicing tests to python fuzzer --- test/spa_fuzzing.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/spa_fuzzing.py b/test/spa_fuzzing.py index b044abc4..3ac56f78 100755 --- a/test/spa_fuzzing.py +++ b/test/spa_fuzzing.py @@ -34,6 +34,7 @@ def main(): spa_payloads = [ "1716411011200157:cm9vdA:1397329899:2.0.1:1:MTI3LjAuMC4yLHRjcC8yMw", + "3145808919615481:cm9vdA:1397329998:2.0.1:0:MTI3LjAuMC4yLGVjaG8gZndrbm9wdGVzdCA+IC90bXAvZndrbm9wdGVzdA", "1642197848921959:cm9vdA:1397329740:2.0.1:2:MTI3LjAuMC4yLHRjcC8yMg:MTkyLjE2OC4xLjIsMjI", "1548062350109656:cm9vdA:1397330450:2.0.1:3:MTI3LjAuMC4yLHRjcC8yMg:2", "1414212790438062:cm9vdA:1397329054:2.0.1:4:MTI3LjAuMC4yLHRjcC8yMg:MTkyLjE2OC4xMC4xLDEyMzQ1:1234", @@ -76,6 +77,15 @@ def main(): str(spa_sha256), base64.b64encode(spa_payload[l:]) pkt_id += 1 + ### blocks of spliced chars out of the original SPA payload + print "# payload " + str(payload_num) + " splice blocks of chars..." + for bl in range(1, 20): + for l in range(0, len(spa_payload)): + new_payload = spa_payload[:l] + spa_payload[l+bl:] + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(new_payload) + pkt_id += 1 + ### SPA payloads that are too long print "# payload " + str(payload_num) + " payloads too long..." for l in [1, 10, 50, 127, 128, 129, 200, 399, \ From 1deccfd0053f5e4649dce697de7cd662a4cb47ec Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Thu, 24 Apr 2014 22:11:04 -0400 Subject: [PATCH 08/13] [test suite] python fuzzer - break out fuzzing sections into dedicated functions --- test/spa_fuzzing.py | 198 ++++++++++++++++++++++++++++++-------------- 1 file changed, 134 insertions(+), 64 deletions(-) diff --git a/test/spa_fuzzing.py b/test/spa_fuzzing.py index 3ac56f78..ba395fca 100755 --- a/test/spa_fuzzing.py +++ b/test/spa_fuzzing.py @@ -19,14 +19,14 @@ import base64 import argparse -def main(): +### a few constants +spa_success = 1 +spa_failure = 0 +spa_sha256 = 3 +do_digest = 1 +no_digest = 0 - ### a few constants - spa_success = 1 - spa_failure = 0 - spa_sha256 = 3 - do_digest = 1 - no_digest = 0 +def main(): args = parse_cmdline() @@ -50,78 +50,148 @@ def main(): payload_num += 1 - print "# start tests with payload: " + spa_payload + print "# start tests with payload: ", spa_payload + "\n" \ + "# base64 encoded original payload:", base64.b64encode(spa_payload) + + ### fuzz individual payload fields + pkt_id = field_fuzzing(args, spa_payload, payload_num, pkt_id) ### valid payload tests - all digest types - print "# payload " + str(payload_num) + " valid payload + valid digest types..." - for digest_type in range(0, 6): - print str(pkt_id), str(spa_success), str(do_digest), \ - str(digest_type), base64.b64encode(spa_payload) - pkt_id += 1 + pkt_id = valid_payloads(args, spa_payload, payload_num, pkt_id) ### invalid digest types - print "# payload " + str(payload_num) + " invalid digest types..." - for digest_type in [-1, 6, 7]: - print str(pkt_id), str(spa_success), str(do_digest), \ - str(digest_type), base64.b64encode(spa_payload) - pkt_id += 1 + pkt_id = invalid_digest_types(args, spa_payload, payload_num, pkt_id) ### truncated lengths - print "# payload " + str(payload_num) + " truncated lengths..." - for l in range(1, len(spa_payload)): - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(spa_payload[:l]) - pkt_id += 1 - for l in range(1, len(spa_payload)): - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(spa_payload[l:]) - pkt_id += 1 + pkt_id = truncated_lengths(args, spa_payload, payload_num, pkt_id) - ### blocks of spliced chars out of the original SPA payload - print "# payload " + str(payload_num) + " splice blocks of chars..." - for bl in range(1, 20): - for l in range(0, len(spa_payload)): - new_payload = spa_payload[:l] + spa_payload[l+bl:] - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(new_payload) - pkt_id += 1 + ### remove chunks of chars out of the original SPA payload + pkt_id = rm_chunks(args, spa_payload, payload_num, pkt_id) ### SPA payloads that are too long - print "# payload " + str(payload_num) + " payloads too long..." - for l in [1, 10, 50, 127, 128, 129, 200, 399, \ - 400, 401, 500, 800, 1000, 1023, 1024, 1025, \ - 1200, 1499, 1500, 1501, 2000]: - for non_ascii in range(0, 5) + range(127, 130) + range(252, 255): - new_payload = spa_payload - for p in range(0, l): - new_payload += chr(non_ascii) - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(new_payload) - pkt_id += 1 + pkt_id = append_data_to_end(args, spa_payload, payload_num, pkt_id) ### additional embedded ':' chars - print "# payload " + str(payload_num) + " additional embedded : chars..." - for pos in range(0, len(spa_payload)): - if spa_payload[pos] == ':': - continue + pkt_id = embedded_separators(args, spa_payload, payload_num, pkt_id) + + ### non-ascii char tests + pkt_id = embedded_non_ascii(args, spa_payload, payload_num, pkt_id) + + return + +def field_fuzzing(args, spa_payload, payload_num, pkt_id): + repl_start = 0 + repl_end = 0 + field_num = 0 + for idx in range(0, len(spa_payload)): + if spa_payload[idx] == ':' or idx == len(spa_payload)-1: + field_num += 1 + repl_start = repl_end + repl_end = idx + + ### now generate fuzzing data for this field + for c in range(1, 255): + for l in [1, 2, 3, 4, 5, 6, 10, 14, 15, 16, 17, 24, 31, 32, 33, \ + 63, 64, 127, 128, 129, 250]: + + fuzzing_field = '' + + for n in range(0, l): + fuzzing_field += chr(c) + + if idx == len(spa_payload)-1: + new_payload = spa_payload[:repl_start] + ":" + base64.b64encode(fuzzing_field) + else: + if field_num == 1: + new_payload = spa_payload[:repl_start] + fuzzing_field + spa_payload[repl_end:] + elif field_num == 2 or field_num >= 6: ### user or access request + new_payload = spa_payload[:repl_start] + ":" + base64.b64encode(fuzzing_field) + spa_payload[repl_end:] + else: + ### time stamp, version, and SPA type fields aren't base64 encoded + new_payload = spa_payload[:repl_start] + ":" + fuzzing_field + spa_payload[repl_end:] + + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(new_payload) + pkt_id += 1 + + return pkt_id + +def valid_payloads(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " valid payload + valid digest types..." + for digest_type in range(0, 6): + print str(pkt_id), str(spa_success), str(do_digest), \ + str(digest_type), base64.b64encode(spa_payload) + pkt_id += 1 + return pkt_id + +def invalid_digest_types(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " invalid digest types..." + for digest_type in [-1, 6, 7]: + print str(pkt_id), str(spa_success), str(do_digest), \ + str(digest_type), base64.b64encode(spa_payload) + pkt_id += 1 + return pkt_id + +def truncated_lengths(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " truncated lengths..." + for l in range(1, len(spa_payload)): + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(spa_payload[:l]) + pkt_id += 1 + for l in range(1, len(spa_payload)): + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(spa_payload[l:]) + pkt_id += 1 + + return pkt_id + +def rm_chunks(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " splice blocks of chars..." + for bl in range(1, 20): + for l in range(0, len(spa_payload)): + new_payload = spa_payload[:l] + spa_payload[l+bl:] + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(new_payload) + pkt_id += 1 + return pkt_id + +def append_data_to_end(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " payloads too long..." + for l in [1, 10, 50, 127, 128, 129, 200, 399, \ + 400, 401, 500, 800, 1000, 1023, 1024, 1025, \ + 1200, 1499, 1500, 1501, 2000]: + for non_ascii in range(0, 5) + range(127, 130) + range(252, 256): + new_payload = spa_payload + for p in range(0, l): + new_payload += chr(non_ascii) + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(new_payload) + pkt_id += 1 + return pkt_id + +def embedded_separators(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " additional embedded : chars..." + for pos in range(0, len(spa_payload)): + if spa_payload[pos] == ':': + continue + new_payload = list(spa_payload) + new_payload[pos] = ':' + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(''.join(new_payload)) + pkt_id += 1 + return pkt_id + +def embedded_non_ascii(args, spa_payload, payload_num, pkt_id): + print "# payload " + str(payload_num) + " non-ascii char tests..." + for pos in range(0, len(spa_payload)): + for non_ascii in range(0, 31) + range(127, 256): new_payload = list(spa_payload) - new_payload[pos] = ':' + new_payload[pos] = chr(non_ascii) + ### write out the fuzzing line print str(pkt_id), str(spa_failure), str(do_digest), \ str(spa_sha256), base64.b64encode(''.join(new_payload)) pkt_id += 1 - - ### non-ascii char tests - print "# payload " + str(payload_num) + " non-ascii char tests..." - for pos in range(0, len(spa_payload)): - for non_ascii in range(0, 31) + range(127, 255): - new_payload = list(spa_payload) - new_payload[pos] = chr(non_ascii) - ### write out the fuzzing line - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(''.join(new_payload)) - pkt_id += 1 - - return + return pkt_id def print_hdr(): print "# " From e00add778ed7f04791d8f9380da766deaa8e5874 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Sat, 26 Apr 2014 17:03:47 -0400 Subject: [PATCH 09/13] [test suite] python fuzzer - add fuzzing fields to original fields (interim commit) --- test/spa_fuzzing.py | 51 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/test/spa_fuzzing.py b/test/spa_fuzzing.py index ba395fca..820b847a 100755 --- a/test/spa_fuzzing.py +++ b/test/spa_fuzzing.py @@ -75,7 +75,7 @@ def main(): pkt_id = embedded_separators(args, spa_payload, payload_num, pkt_id) ### non-ascii char tests - pkt_id = embedded_non_ascii(args, spa_payload, payload_num, pkt_id) + pkt_id = embedded_chars(args, spa_payload, payload_num, pkt_id) return @@ -86,11 +86,16 @@ def field_fuzzing(args, spa_payload, payload_num, pkt_id): for idx in range(0, len(spa_payload)): if spa_payload[idx] == ':' or idx == len(spa_payload)-1: field_num += 1 + if repl_end > 0: + orig_field = spa_payload[repl_end+1:idx] + else: + orig_field = spa_payload[repl_end:idx] + repl_start = repl_end repl_end = idx ### now generate fuzzing data for this field - for c in range(1, 255): + for c in range(0, 3) + range(33, 47) + range(65, 67) + range(127, 130) + range(252, 256): for l in [1, 2, 3, 4, 5, 6, 10, 14, 15, 16, 17, 24, 31, 32, 33, \ 63, 64, 127, 128, 129, 250]: @@ -100,18 +105,46 @@ def field_fuzzing(args, spa_payload, payload_num, pkt_id): fuzzing_field += chr(c) if idx == len(spa_payload)-1: - new_payload = spa_payload[:repl_start] + ":" + base64.b64encode(fuzzing_field) + new_payload1 = spa_payload[:repl_start] + ":" + \ + base64.b64encode(fuzzing_field) + new_payload2 = spa_payload[:repl_start] + ":" + \ + base64.b64encode(fuzzing_field+base64.b64decode(orig_field)) + new_payload3 = spa_payload[:repl_start] + ":" + \ + base64.b64encode(base64.b64decode(orig_field)+fuzzing_field) else: if field_num == 1: - new_payload = spa_payload[:repl_start] + fuzzing_field + spa_payload[repl_end:] + new_payload1 = spa_payload[:repl_start] + \ + fuzzing_field + spa_payload[repl_end:] + new_payload2 = spa_payload[:repl_start] + \ + fuzzing_field+orig_field + spa_payload[repl_end:] + new_payload3 = spa_payload[:repl_start] + \ + orig_field+fuzzing_field + spa_payload[repl_end:] elif field_num == 2 or field_num >= 6: ### user or access request - new_payload = spa_payload[:repl_start] + ":" + base64.b64encode(fuzzing_field) + spa_payload[repl_end:] + new_payload1 = spa_payload[:repl_start] + ":" + \ + base64.b64encode(fuzzing_field) + spa_payload[repl_end:] + new_payload2 = spa_payload[:repl_start] + ":" + \ + base64.b64encode(fuzzing_field+base64.b64decode(orig_field)) \ + + spa_payload[repl_end:] + new_payload3 = spa_payload[:repl_start] + ":" + \ + base64.b64encode(base64.b64decode(orig_field)+fuzzing_field) \ + + spa_payload[repl_end:] else: ### time stamp, version, and SPA type fields aren't base64 encoded - new_payload = spa_payload[:repl_start] + ":" + fuzzing_field + spa_payload[repl_end:] + new_payload1 = spa_payload[:repl_start] + ":" + \ + fuzzing_field + spa_payload[repl_end:] + new_payload2 = spa_payload[:repl_start] + ":" + \ + fuzzing_field+orig_field + spa_payload[repl_end:] + new_payload3 = spa_payload[:repl_start] + ":" + \ + orig_field+fuzzing_field + spa_payload[repl_end:] print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(new_payload) + str(spa_sha256), base64.b64encode(new_payload1) + pkt_id += 1 + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(new_payload2) + pkt_id += 1 + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(new_payload3) pkt_id += 1 return pkt_id @@ -181,10 +214,10 @@ def embedded_separators(args, spa_payload, payload_num, pkt_id): pkt_id += 1 return pkt_id -def embedded_non_ascii(args, spa_payload, payload_num, pkt_id): +def embedded_chars(args, spa_payload, payload_num, pkt_id): print "# payload " + str(payload_num) + " non-ascii char tests..." for pos in range(0, len(spa_payload)): - for non_ascii in range(0, 31) + range(127, 256): + for non_ascii in range(0, 31) + range(44, 48) + range(127, 131) + range(253, 255): new_payload = list(spa_payload) new_payload[pos] = chr(non_ascii) ### write out the fuzzing line From 367424ece5aaf0b0f4c9926e32b36b6d53e36d3a Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Sat, 26 Apr 2014 22:03:32 -0400 Subject: [PATCH 10/13] [test suite] python fuzzer - account for base64 strings that have stripped '=' chars --- test/spa_fuzzing.py | 90 ++++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/test/spa_fuzzing.py b/test/spa_fuzzing.py index 820b847a..f4de5ae6 100755 --- a/test/spa_fuzzing.py +++ b/test/spa_fuzzing.py @@ -100,55 +100,63 @@ def field_fuzzing(args, spa_payload, payload_num, pkt_id): 63, 64, 127, 128, 129, 250]: fuzzing_field = '' + require_b64 = orig_field.isdigit() for n in range(0, l): fuzzing_field += chr(c) - if idx == len(spa_payload)-1: - new_payload1 = spa_payload[:repl_start] + ":" + \ - base64.b64encode(fuzzing_field) - new_payload2 = spa_payload[:repl_start] + ":" + \ - base64.b64encode(fuzzing_field+base64.b64decode(orig_field)) - new_payload3 = spa_payload[:repl_start] + ":" + \ - base64.b64encode(base64.b64decode(orig_field)+fuzzing_field) - else: - if field_num == 1: - new_payload1 = spa_payload[:repl_start] + \ - fuzzing_field + spa_payload[repl_end:] - new_payload2 = spa_payload[:repl_start] + \ - fuzzing_field+orig_field + spa_payload[repl_end:] - new_payload3 = spa_payload[:repl_start] + \ - orig_field+fuzzing_field + spa_payload[repl_end:] - elif field_num == 2 or field_num >= 6: ### user or access request - new_payload1 = spa_payload[:repl_start] + ":" + \ - base64.b64encode(fuzzing_field) + spa_payload[repl_end:] - new_payload2 = spa_payload[:repl_start] + ":" + \ - base64.b64encode(fuzzing_field+base64.b64decode(orig_field)) \ - + spa_payload[repl_end:] - new_payload3 = spa_payload[:repl_start] + ":" + \ - base64.b64encode(base64.b64decode(orig_field)+fuzzing_field) \ - + spa_payload[repl_end:] - else: - ### time stamp, version, and SPA type fields aren't base64 encoded - new_payload1 = spa_payload[:repl_start] + ":" + \ - fuzzing_field + spa_payload[repl_end:] - new_payload2 = spa_payload[:repl_start] + ":" + \ - fuzzing_field+orig_field + spa_payload[repl_end:] - new_payload3 = spa_payload[:repl_start] + ":" + \ - orig_field+fuzzing_field + spa_payload[repl_end:] + new_payloads = [ + spa_payload[:repl_start], ### for payloads without original field + spa_payload[:repl_start], ### prepend fuzzing field with original + spa_payload[:repl_start] ### append fuzzing field to original + ] - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(new_payload1) - pkt_id += 1 - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(new_payload2) - pkt_id += 1 - print str(pkt_id), str(spa_failure), str(do_digest), \ - str(spa_sha256), base64.b64encode(new_payload3) - pkt_id += 1 + if field_num > 1: + for i in range(0, len(new_payloads)): + new_payloads[i] += ':' + + if idx == len(spa_payload)-1: + field_variants(new_payloads, fuzzing_field, orig_field, require_b64) + else: + if field_num == 1 or field_num in range(3, 6): + ### fields: rand val, time stamp, version, and SPA type + field_variants(new_payloads, fuzzing_field, orig_field, False) + elif field_num == 2: ### user field + field_variants(new_payloads, fuzzing_field, orig_field, True) + else: + field_variants(new_payloads, fuzzing_field, orig_field, require_b64) + + for i in range(0, len(new_payloads)): + new_payloads[i] += spa_payload[repl_end:] + + for s in new_payloads: + print str(pkt_id), str(spa_failure), str(do_digest), \ + str(spa_sha256), base64.b64encode(s) + pkt_id += 1 return pkt_id +def field_variants(new_payloads, fuzzing_field, orig_field, require_b64): + if require_b64: + decoded_orig_field = spa_base64_decode(orig_field) + new_payloads[0] += base64.b64encode(fuzzing_field) + new_payloads[1] += base64.b64encode(fuzzing_field+decoded_orig_field) + new_payloads[2] += base64.b64encode(decoded_orig_field+fuzzing_field) + else: + new_payloads[0] += fuzzing_field + new_payloads[1] += fuzzing_field+orig_field + new_payloads[2] += orig_field+fuzzing_field + return + +def spa_base64_decode(b64str): + ### account for how fwknop strips '=' chars + remainder = len(b64str) % 4 + if remainder != 0: + for i in range(0, remainder): + b64str += '=' + + return base64.b64decode(b64str) + def valid_payloads(args, spa_payload, payload_num, pkt_id): print "# payload " + str(payload_num) + " valid payload + valid digest types..." for digest_type in range(0, 6): From 91a60b8d91afd7bc11902151a0ea8995ead31a70 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Sat, 26 Apr 2014 22:35:57 -0400 Subject: [PATCH 11/13] [test suite] libfko wrapper is already called in Rijndael tests --- test/test-fwknop.pl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/test-fwknop.pl b/test/test-fwknop.pl index 5a2f8ac2..391327dc 100755 --- a/test/test-fwknop.pl +++ b/test/test-fwknop.pl @@ -792,13 +792,6 @@ if ($enable_profile_coverage_check) { } if ($enable_valgrind) { - &run_test({ - 'category' => 'valgrind', - 'subcategory' => 'fko-wrapper', - 'detail' => 'multiple libfko calls', - 'function' => \&compile_execute_fko_wrapper} - ); - &run_test({ 'category' => 'valgrind output', 'subcategory' => 'flagged functions', From e1dde1733a3b7f5512fdb2c104f56e0c45d52589 Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Sat, 26 Apr 2014 23:01:47 -0400 Subject: [PATCH 12/13] [test suite] python fuzzer - more field length variations to hit MAX_SPA_MESSAGE_SIZE --- test/spa_fuzzing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/spa_fuzzing.py b/test/spa_fuzzing.py index f4de5ae6..ee642f94 100755 --- a/test/spa_fuzzing.py +++ b/test/spa_fuzzing.py @@ -34,6 +34,7 @@ def main(): spa_payloads = [ "1716411011200157:cm9vdA:1397329899:2.0.1:1:MTI3LjAuMC4yLHRjcC8yMw", + "1716411011200157:cm9vdA:1397329899:2.0.1:1:MTI3LjAuMC4yLHRjcC8yMw:cGFzc3dk", "3145808919615481:cm9vdA:1397329998:2.0.1:0:MTI3LjAuMC4yLGVjaG8gZndrbm9wdGVzdCA+IC90bXAvZndrbm9wdGVzdA", "1642197848921959:cm9vdA:1397329740:2.0.1:2:MTI3LjAuMC4yLHRjcC8yMg:MTkyLjE2OC4xLjIsMjI", "1548062350109656:cm9vdA:1397330450:2.0.1:3:MTI3LjAuMC4yLHRjcC8yMg:2", @@ -97,7 +98,7 @@ def field_fuzzing(args, spa_payload, payload_num, pkt_id): ### now generate fuzzing data for this field for c in range(0, 3) + range(33, 47) + range(65, 67) + range(127, 130) + range(252, 256): for l in [1, 2, 3, 4, 5, 6, 10, 14, 15, 16, 17, 24, 31, 32, 33, \ - 63, 64, 127, 128, 129, 250]: + 63, 64, 127, 128, 129, 150, 220, 230, 254, 255, 256, 257, 258]: fuzzing_field = '' require_b64 = orig_field.isdigit() From 9901d8a76a75e8d2bb5088fe92cc370f084e85cb Mon Sep 17 00:00:00 2001 From: Michael Rash Date: Sat, 26 Apr 2014 23:04:44 -0400 Subject: [PATCH 13/13] [libfko/test suite] add the FUZZING_INTERFACES macro Add a new fko_set_encoded_data() function gated by #define FUZZING_INTERFACES to allow encryption and authentication to be bypassed for fuzzing purposes (and only fuzzing purposes). The fko-wrapper code has been extended to process data in the test/fko-wrapper/fuzz_spa_payloads file, which is created by the new python fuzzer. Typical workflow is: $ cd test/fko-wrapper $ ../spa_fuzzer.py > fuzz_spa_payloads $ make fuzzing (as root): ./test-fwknop.pl --enable-profile-coverage --enable-fuzzing-interfaces --enable-all --include wrapper [+] Starting the fwknop test suite... args: --enable-profile-coverage --enable-fuzzing-interfaces --enable-all --include wrapper Saved results from previous run to: output.last/ Valgrind mode enabled, will import previous coverage from: output.last/valgrind-coverage/ [+] Total test buckets to execute: 2 [Rijndael] [fko-wrapper] multiple libfko calls (with valgrind)......pass (1) [Rijndael] [fko-wrapper] multiple libfko calls......................pass (2) [profile coverage] gcov profile coverage............................pass (3) [valgrind output] [flagged functions] ..............................pass (4) Run time: 5.85 minutes [+] 0/0/0 OpenSSL tests passed/failed/executed [+] 0/0/0 OpenSSL HMAC tests passed/failed/executed [+] 4/0/4 test buckets passed/failed/executed --- configure.ac | 14 ++++ lib/fko.h | 2 + lib/fko_encode.c | 4 +- test/conf/client-gpg-no-pw/trustdb.gpg | Bin 1360 -> 1360 bytes test/fko-wrapper/Makefile | 3 + test/fko-wrapper/fko_wrapper.c | 90 +++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ec0bc905..2bf653a6 100644 --- a/configure.ac +++ b/configure.ac @@ -135,6 +135,20 @@ if test "x$want_profile_coverage" = "xyes"; then FKO_CHECK_COMPILER_ARG_LDFLAGS_ONLY([-lgcov]) fi +dnl Decide whether or not to compile in certain features that enable fuzzing +dnl of fwknop code - this is for testing purposes only. +dnl +want_fuzzing_interfaces=no +AC_ARG_ENABLE([fuzzing-interfaces], + [AS_HELP_STRING([--enable-fuzzing-interfaces], + [Build fwknop binaries with support for fuzzing interfaces @<:@default is to disable@:>@])], + [want_fuzzing_interfaces=$enableval], + []) + +if test "x$want_fuzzing_interfaces" = "xyes"; then + AC_DEFINE([FUZZING_INTERFACES], [1], [Define for fuzzing interfaces support]) +fi + dnl Decide whether or not to enable all warnings with -Wall dnl use_wall=yes diff --git a/lib/fko.h b/lib/fko.h index f4e5f0b4..aae0884f 100644 --- a/lib/fko.h +++ b/lib/fko.h @@ -381,8 +381,10 @@ DLL_API int fko_set_spa_hmac(fko_ctx_t ctx, const char * const hmac_key, DLL_API int fko_get_spa_hmac(fko_ctx_t ctx, char **enc_data); DLL_API int fko_get_encoded_data(fko_ctx_t ctx, char **enc_data); +#if FUZZING_INTERFACES DLL_API int fko_set_encoded_data(fko_ctx_t ctx, const char * const encoded_msg, const int msg_len, const int do_digest, const int digest_type); +#endif /* Get context data functions */ diff --git a/lib/fko_encode.c b/lib/fko_encode.c index 06128cfe..5a34b352 100644 --- a/lib/fko_encode.c +++ b/lib/fko_encode.c @@ -244,6 +244,7 @@ fko_get_encoded_data(fko_ctx_t ctx, char **enc_msg) /* Set the fko SPA encoded data (this is a convenience * function mostly used for tests that involve fuzzing). */ +#if FUZZING_INTERFACES int fko_set_encoded_data(fko_ctx_t ctx, const char * const encoded_msg, const int msg_len, @@ -274,7 +275,7 @@ fko_set_encoded_data(fko_ctx_t ctx, if(require_digest) { - fko_set_spa_digest_type(ctx, FKO_DIGEST_SHA256); + fko_set_spa_digest_type(ctx, digest_type); if((res = fko_set_spa_digest(ctx)) != FKO_SUCCESS) { return res; @@ -310,5 +311,6 @@ fko_set_encoded_data(fko_ctx_t ctx, return(FKO_SUCCESS); } +#endif /***EOF***/ diff --git a/test/conf/client-gpg-no-pw/trustdb.gpg b/test/conf/client-gpg-no-pw/trustdb.gpg index 4c30150c1afe02a1f4531c2c73dd65ff44f02bd9..58ec2d6dbf204edd880308798a9b72d7f7d66d15 100644 GIT binary patch delta 28 jcmcb>b%9HOF})z2nVFH5k%581y}swh#6aYA(oXdeeU delta 28 jcmcb>b%9HOF})z2nVFH5k%@sJ*t6&O#6aYA(oW*Y|q diff --git a/test/fko-wrapper/Makefile b/test/fko-wrapper/Makefile index e88a5ab2..3e970ba2 100644 --- a/test/fko-wrapper/Makefile +++ b/test/fko-wrapper/Makefile @@ -2,5 +2,8 @@ all : fko_wrapper.c gcc -Wall -g -I../../lib fko_wrapper.c -o fko_wrapper -L../../lib/.libs -lfko +fuzzing: fko_wrapper.c + gcc -Wall -g -DFUZZING_INTERFACES -I../../lib fko_wrapper.c -o fko_wrapper -L../../lib/.libs -lfko + clean: rm -f fko_wrapper diff --git a/test/fko-wrapper/fko_wrapper.c b/test/fko-wrapper/fko_wrapper.c index cbb4944c..0ece661f 100644 --- a/test/fko-wrapper/fko_wrapper.c +++ b/test/fko-wrapper/fko_wrapper.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "fko.h" #define ENABLE_GPG_TESTS 0 @@ -23,12 +24,20 @@ #define NO_DIGEST 0 #define DO_DIGEST 1 #define RAW_DIGEST 2 +#define MAX_LINE_LEN 3000 /* really long for fuzzing tests */ #define ENC_KEY "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /* 32 bytes */ #define HMAC_KEY "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" /* 32 bytes */ +#define IS_EMPTY_LINE(x) ( \ + x == '#' || x == '\n' || x == '\r' || x == ';' || x == '\0' \ +) + static void display_ctx(fko_ctx_t ctx); static void test_loop(int new_ctx_flag, int destroy_ctx_flag); static void test_loop_compounded(void); +#if FUZZING_INTERFACES +static void spa_encoded_msg_fuzzing(void); +#endif static void ctx_update(fko_ctx_t *ctx, int new_ctx_flag, int destroy_ctx_flag, int print_flag); static void spa_default_ctx(fko_ctx_t *ctx); @@ -67,9 +76,90 @@ int main(void) { printf("[+] Total libfko function calls (after compounded tests): %d\n\n", spa_calls); +#if FUZZING_INTERFACES + printf("[+] libfko fuzzing by setting SPA buffer manually...\n"); + spa_encoded_msg_fuzzing(); +#endif + return 0; } +#if FUZZING_INTERFACES +static void +spa_encoded_msg_fuzzing(void) +{ + fko_ctx_t decode_ctx = NULL; + int res = 0, pkt_id, require_success, require_digest, digest_type, msg_len; + int line_ctr = 0, spa_payload_ctr = 0; + FILE *fz = NULL; + char line[MAX_LINE_LEN] = {0}; + char b64_encoded_msg[MAX_LINE_LEN] = {0}; + unsigned char b64_decoded_msg[MAX_LINE_LEN] = {0}; + + /* fuzzing file contents (or from stdin) are formatted like this: + * + * + */ + + if ((fz = fopen("fuzz_spa_payloads", "r")) == NULL) + return; + + while ((fgets(line, MAX_LINE_LEN, fz)) != NULL) + { + line_ctr++; + line[MAX_LINE_LEN-1] = '\0'; + + if (line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + + if(IS_EMPTY_LINE(line[0])) + continue; + + if(sscanf(line, "%d %d %d %d %s", &pkt_id, &require_success, + &require_digest, &digest_type, b64_encoded_msg) != 5) + { + printf("[+] fuzzing parsing error at line: %d\n", line_ctr); + continue; + } + + msg_len = fko_base64_decode(b64_encoded_msg, b64_decoded_msg); + + spa_payload_ctr++; + + fko_new(&decode_ctx); + + if ((res = fko_set_encoded_data(decode_ctx, (char *) b64_decoded_msg, + msg_len, require_digest, digest_type)) != FKO_SUCCESS) { + printf("[-] pkt_id: %d, fko_set_encoded_data(): %s\n", pkt_id, fko_errstr(res)); + } + + res = fko_decode_spa_data(decode_ctx); + if (require_success) { + if (res != FKO_SUCCESS) { + printf("[-] pkt_id: %d, expected decode success but: fko_decode_spa_data(): %s\n", + pkt_id, fko_errstr(res)); + } + } else { + if (res == FKO_SUCCESS) { + printf("[-] pkt_id: %d, expected decode failure but: fko_decode_spa_data(): %s\n", + pkt_id, fko_errstr(res)); + } + } + + fko_destroy(decode_ctx); + + memset(line, 0x0, MAX_LINE_LEN); + memset(b64_encoded_msg, 0x0, MAX_LINE_LEN); + } + + fclose(fz); + + printf("[+] Sent %d SPA payloads through libfko encode/decode cycle...\n", + spa_payload_ctr); + return; +} +#endif + static void test_loop_compounded(void) {