Ted Wynnychenko
- Helped test fwknop PF support on OpenBSD.
+
+Andy Rowland
+ - Reported a bug where the same encryption key used for two stanzas in the
+ access.conf file would result in access requests that matched the second
+ stanza to always be treated as a replay attack. This has been fixed for
+ the fwknop-2.0.1 release.
DLL_API int fko_set_spa_client_timeout(fko_ctx_t ctx, const int timeout);
DLL_API int fko_set_spa_digest_type(fko_ctx_t ctx, const short digest_type);
DLL_API int fko_set_spa_digest(fko_ctx_t ctx);
+DLL_API int fko_set_raw_spa_digest_type(fko_ctx_t ctx, const short raw_digest_type);
+DLL_API int fko_set_raw_spa_digest(fko_ctx_t ctx);
DLL_API int fko_set_spa_encryption_type(fko_ctx_t ctx, const short encrypt_type);
DLL_API int fko_set_spa_encryption_mode(fko_ctx_t ctx, const int encrypt_mode);
DLL_API int fko_set_spa_data(fko_ctx_t ctx, const char *enc_msg);
DLL_API int fko_get_spa_server_auth(fko_ctx_t ctx, char **server_auth);
DLL_API int fko_get_spa_client_timeout(fko_ctx_t ctx, int *client_timeout);
DLL_API int fko_get_spa_digest_type(fko_ctx_t ctx, short *spa_digest_type);
+DLL_API int fko_get_raw_spa_digest_type(fko_ctx_t ctx, short *raw_spa_digest_type);
DLL_API int fko_get_spa_digest(fko_ctx_t ctx, char **spa_digest);
+DLL_API int fko_get_raw_spa_digest(fko_ctx_t ctx, char **raw_spa_digest);
DLL_API int fko_get_spa_encryption_type(fko_ctx_t ctx, short *spa_enc_type);
DLL_API int fko_get_spa_encryption_mode(fko_ctx_t ctx, int *spa_enc_mode);
DLL_API int fko_get_spa_data(fko_ctx_t ctx, char **spa_data);
char *version;
char *digest;
+ /* Digest of raw encrypted/base64 data - this is used
+ * for replay attack detection
+ */
+ char *raw_digest;
+ short raw_digest_type;
+
/* Computed processed data (encodings, etc.) */
char *encoded_msg;
char *encrypted_msg;
/* Set the SPA digest type.
*/
-int
-fko_set_spa_digest_type(fko_ctx_t ctx, const short digest_type)
+static int
+set_spa_digest_type(fko_ctx_t ctx,
+ short *digest_type_field, const short digest_type)
{
/* Must be initialized
*/
if(digest_type < 1 || digest_type >= FKO_LAST_DIGEST_TYPE)
return(FKO_ERROR_INVALID_DATA);
- ctx->digest_type = digest_type;
+ *digest_type_field = digest_type;
ctx->state |= FKO_DIGEST_TYPE_MODIFIED;
return(FKO_SUCCESS);
}
+int
+fko_set_spa_digest_type(fko_ctx_t ctx, const short digest_type)
+{
+ return set_spa_digest_type(ctx, &ctx->digest_type, digest_type);
+}
+
+int
+fko_set_raw_spa_digest_type(fko_ctx_t ctx, const short raw_digest_type)
+{
+ return set_spa_digest_type(ctx, &ctx->raw_digest_type, raw_digest_type);
+}
+
/* Return the SPA digest type.
*/
int
return(FKO_SUCCESS);
}
+/* Return the SPA digest type.
+*/
int
-fko_set_spa_digest(fko_ctx_t ctx)
+fko_get_raw_spa_digest_type(fko_ctx_t ctx, short *raw_digest_type)
{
- char *md = NULL;
-
/* Must be initialized
*/
if(!CTX_INITIALIZED(ctx))
return(FKO_ERROR_CTX_NOT_INITIALIZED);
- /* Must have encoded message data to start with.
- */
- if(ctx->encoded_msg == NULL)
- return(FKO_ERROR_MISSING_ENCODED_DATA);
+ *raw_digest_type = ctx->raw_digest_type;
+
+ return(FKO_SUCCESS);
+}
+
+static int
+set_digest(char *data, char **digest, short digest_type)
+{
+ char *md = NULL;
- switch(ctx->digest_type)
+ switch(digest_type)
{
case FKO_DIGEST_MD5:
md = malloc(MD_HEX_SIZE(MD5_DIGEST_LENGTH)+1);
return(FKO_ERROR_MEMORY_ALLOCATION);
md5_base64(md,
- (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
+ (unsigned char*)data, strlen(data));
break;
case FKO_DIGEST_SHA1:
return(FKO_ERROR_MEMORY_ALLOCATION);
sha1_base64(md,
- (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
+ (unsigned char*)data, strlen(data));
break;
case FKO_DIGEST_SHA256:
return(FKO_ERROR_MEMORY_ALLOCATION);
sha256_base64(md,
- (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
+ (unsigned char*)data, strlen(data));
break;
case FKO_DIGEST_SHA384:
return(FKO_ERROR_MEMORY_ALLOCATION);
sha384_base64(md,
- (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
+ (unsigned char*)data, strlen(data));
break;
case FKO_DIGEST_SHA512:
return(FKO_ERROR_MEMORY_ALLOCATION);
sha512_base64(md,
- (unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
+ (unsigned char*)data, strlen(data));
break;
default:
/* Just in case this is a subsquent call to this function. We
* do not want to be leaking memory.
*/
- if(ctx->digest != NULL)
- free(ctx->digest);
+ if(*digest != NULL)
+ free(*digest);
- ctx->digest = md;
+ *digest = md;
return(FKO_SUCCESS);
}
int
+fko_set_spa_digest(fko_ctx_t ctx)
+{
+ /* Must be initialized
+ */
+ if(!CTX_INITIALIZED(ctx))
+ return(FKO_ERROR_CTX_NOT_INITIALIZED);
+
+ /* Must have encoded message data to start with.
+ */
+ if(ctx->encoded_msg == NULL)
+ return(FKO_ERROR_MISSING_ENCODED_DATA);
+
+ return set_digest(ctx->encoded_msg,
+ &ctx->digest, ctx->digest_type);
+}
+
+int
+fko_set_raw_spa_digest(fko_ctx_t ctx)
+{
+ /* Must be initialized
+ */
+ if(!CTX_INITIALIZED(ctx))
+ return(FKO_ERROR_CTX_NOT_INITIALIZED);
+
+ /* Must have encoded message data to start with.
+ */
+ if(ctx->encrypted_msg == NULL)
+ return(FKO_ERROR_MISSING_ENCODED_DATA);
+
+ return set_digest(ctx->encrypted_msg,
+ &ctx->raw_digest, ctx->raw_digest_type);
+}
+
+int
fko_get_spa_digest(fko_ctx_t ctx, char **md)
{
/* Must be initialized
return(FKO_SUCCESS);
}
+int
+fko_get_raw_spa_digest(fko_ctx_t ctx, char **md)
+{
+ /* Must be initialized
+ */
+ if(!CTX_INITIALIZED(ctx))
+ return(FKO_ERROR_CTX_NOT_INITIALIZED);
+
+ *md = ctx->raw_digest;
+
+ return(FKO_SUCCESS);
+}
+
/***EOF***/
if(ctx->digest != NULL)
free(ctx->digest);
+ if(ctx->raw_digest != NULL)
+ free(ctx->raw_digest);
+
if(ctx->encoded_msg != NULL)
free(ctx->encoded_msg);
return(FKO_SUCCESS);
}
+/* For replay attack detection
+*/
+static int
+get_raw_digest(char **digest, char *pkt_data)
+{
+ fko_ctx_t ctx = NULL;
+ char *tmp_digest = NULL;
+ int res = FKO_SUCCESS;
+
+ /* initialize an FKO context with no decryption key just so
+ * we can get the outer message digest
+ */
+ res = fko_new_with_data(&ctx, (char *)pkt_data, NULL);
+ if(res != FKO_SUCCESS)
+ {
+ log_msg(LOG_WARNING, "Error initializing FKO context from SPA data: %s",
+ fko_errstr(res));
+ fko_destroy(ctx);
+ return(SPA_MSG_FKO_CTX_ERROR);
+ }
+
+ res = fko_set_raw_spa_digest_type(ctx, FKO_DEFAULT_DIGEST);
+ if(res != FKO_SUCCESS)
+ {
+ log_msg(LOG_WARNING, "Error setting digest type for SPA data: %s",
+ fko_errstr(res));
+ fko_destroy(ctx);
+ return(SPA_MSG_DIGEST_ERROR);
+ }
+
+ res = fko_set_raw_spa_digest(ctx);
+ if(res != FKO_SUCCESS)
+ {
+ log_msg(LOG_WARNING, "Error setting digest for SPA data: %s",
+ fko_errstr(res));
+ fko_destroy(ctx);
+ return(SPA_MSG_DIGEST_ERROR);
+ }
+
+ res = fko_get_raw_spa_digest(ctx, &tmp_digest);
+ if(res != FKO_SUCCESS)
+ {
+ log_msg(LOG_WARNING, "Error getting digest from SPA data: %s",
+ fko_errstr(res));
+ fko_destroy(ctx);
+ return(SPA_MSG_DIGEST_ERROR);
+ }
+
+ *digest = strdup(tmp_digest);
+
+ if (digest == NULL)
+ return SPA_MSG_ERROR;
+
+ fko_destroy(ctx);
+ return res;
+}
+
+
/* Popluate a spa_data struct from an initialized (and populated) FKO context.
*/
static int
return(res);
}
+/* Check for access.conf stanza SOURCE match based on SPA packet
+ * source IP
+*/
+static int
+is_src_match(acc_stanza_t *acc, const uint32_t ip)
+{
+ while (acc)
+ {
+ if(compare_addr_list(acc->source_list, ip))
+ return 1;
+
+ acc = acc->next;
+ }
+ return 0;
+}
+
/* Process the SPA packet data
*/
void
*/
fko_ctx_t ctx = NULL;
- char *spa_ip_demark, *gpg_id;
+ char *spa_ip_demark, *gpg_id, *raw_digest = NULL;
time_t now_ts;
- int res, status, ts_diff, enc_type, found_acc_sip=0, stanza_num=0;
+ int res, status, ts_diff, enc_type, stanza_num=0;
+ int added_replay_digest = 0;
spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
return;
}
+ if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip)))
+ {
+ if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
+ /* Check for a replay attack
+ */
+ res = get_raw_digest(&raw_digest, (char *)spa_pkt->packet_data);
+ if(res != FKO_SUCCESS)
+ {
+ if (raw_digest != NULL)
+ free(raw_digest);
+ return;
+ }
+ if (raw_digest == NULL)
+ return;
+
+ if (is_replay(opts, raw_digest) != SPA_MSG_SUCCESS)
+ return;
+ }
+ else
+ {
+ log_msg(LOG_WARNING,
+ "No access data found for source IP: %s", spadat.pkt_source_ip
+ );
+ return;
+ }
+
+ /* Now that we know there is a matching access.conf stanza and the
+ * incoming SPA packet is not a replay, see if we should grant any
+ * access
+ */
while(acc)
{
stanza_num++;
continue;
}
- found_acc_sip = 1;
-
log_msg(LOG_INFO, "(stanza #%d) SPA Packet from IP: %s received with access source match",
stanza_num, spadat.pkt_source_ip);
continue;
}
- /* Do we have a valid FKO context?
+ /* Do we have a valid FKO context? Did the SPA decrypt properly?
*/
if(res != FKO_SUCCESS)
{
continue;
}
+ /* Add this SPA packet into the replay detection cache
+ */
+ if (! added_replay_digest)
+ {
+ res = add_replay(opts, raw_digest);
+ if (res != SPA_MSG_SUCCESS)
+ {
+ log_msg(LOG_WARNING, "(stanza #%d) Could not add digest to replay cache",
+ stanza_num);
+ if(ctx != NULL)
+ fko_destroy(ctx);
+ acc = acc->next;
+ continue;
+ }
+ added_replay_digest = 1;
+ }
+
/* At this point, we assume the SPA data is valid. Now we need to see
* if it meets our access criteria.
*/
}
}
- /* Check for replays if so configured.
- */
- if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
- {
- res = replay_check(opts, ctx);
- if(res != 0) /* non-zero means we have seen this packet before. */
- {
- if(ctx != NULL)
- fko_destroy(ctx);
- acc = acc->next;
- continue;
- }
- }
-
/* Populate our spa data struct for future reference.
*/
res = get_spa_data_fields(ctx, &spadat);
break;
}
- if(! found_acc_sip)
- {
- log_msg(LOG_WARNING,
- "No access data found for source IP: %s", spadat.pkt_source_ip
- );
- }
+ if (raw_digest != NULL)
+ free(raw_digest);
return;
}
#endif /* USE_FILE_CACHE */
/* Take an fko context, pull the digest and use it as the key to check the
- * replay db (digest cache). Returns 1 if there was a match (a replay),
- * 0 for no match, and -1 on error.
+ * replay db (digest cache).
*/
int
-replay_check(fko_srv_options_t *opts, fko_ctx_t ctx)
+is_replay(fko_srv_options_t *opts, char *digest)
{
#ifdef NO_DIGEST_CACHE
return(-1);
#else
#if USE_FILE_CACHE
- return replay_check_file_cache(opts, ctx);
+ return is_replay_file_cache(opts, digest);
#else
- return replay_check_dbm_cache(opts, ctx);
+ return is_replay_dbm_cache(opts, digest);
#endif
#endif /* NO_DIGEST_CACHE */
}
-#if USE_FILE_CACHE
int
-replay_check_file_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
+add_replay(fko_srv_options_t *opts, char *digest)
{
- char *digest = NULL;
- char src_ip[INET_ADDRSTRLEN+1] = {0};
- char dst_ip[INET_ADDRSTRLEN+1] = {0};
- int res = 0, digest_len = 0;
- FILE *digest_file_ptr = NULL;
+#ifdef NO_DIGEST_CACHE
+ return(-1);
+#else
- struct digest_cache_list *digest_list_ptr = NULL, *digest_elm = NULL;
+#if USE_FILE_CACHE
+ return add_replay_file_cache(opts, digest);
+#else
+ return add_replay_dbm_cache(opts, digest);
+#endif
+#endif /* NO_DIGEST_CACHE */
+}
- res = fko_get_spa_digest(ctx, &digest);
- if(res != FKO_SUCCESS)
- {
- log_msg(LOG_WARNING, "Error getting digest from SPA data: %s",
- fko_errstr(res));
+#if USE_FILE_CACHE
+int
+is_replay_file_cache(fko_srv_options_t *opts, char *digest)
+{
+ int digest_len = 0;
- return(SPA_MSG_DIGEST_ERROR);
- }
+ struct digest_cache_list *digest_list_ptr = NULL;
digest_len = strlen(digest);
return(SPA_MSG_REPLAY);
}
}
+ return(SPA_MSG_SUCCESS);
+}
+
+int
+add_replay_file_cache(fko_srv_options_t *opts, char *digest)
+{
+ FILE *digest_file_ptr = NULL;
+ int digest_len = 0;
+ char src_ip[INET_ADDRSTRLEN+1] = {0};
+ char dst_ip[INET_ADDRSTRLEN+1] = {0};
+
+ struct digest_cache_list *digest_elm = NULL;
+
+ digest_len = strlen(digest);
- /* If we make it here, then this is a new SPA packet that needs to be
- * added to the cache. We've already decrypted the data, so we know that
- * the contents are valid.
- */
if ((digest_elm = calloc(1, sizeof(struct digest_cache_list))) == NULL)
{
log_msg(LOG_WARNING, "Error calloc() returned NULL for digest cache element",
#if !USE_FILE_CACHE
int
-replay_check_dbm_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
+is_replay_dbm_cache(fko_srv_options_t *opts, char *digest)
{
#ifdef NO_DIGEST_CACHE
return 0;
#endif
datum db_key, db_ent;
- char *digest;
- int digest_len, res;
+ char *digest = NULL;
+ int digest_len, res = SPA_MSG_SUCCESS;
digest_cache_info_t dc_info;
- res = fko_get_spa_digest(ctx, &digest);
- if(res != FKO_SUCCESS)
- {
- log_msg(LOG_WARNING, "Error getting digest from SPA data: %s",
- fko_errstr(res));
-
- return(SPA_MSG_DIGEST_ERROR);
- }
-
digest_len = strlen(digest);
db_key.dptr = digest;
#ifdef HAVE_LIBGDBM
free(db_ent.dptr);
#endif
-
res = SPA_MSG_REPLAY;
- } else {
+ }
+
+ MY_DBM_CLOSE(rpdb);
+
+ return(res);
+#endif /* NO_DIGEST_CACHE */
+}
+
+int
+add_replay_dbm_cache(fko_srv_options_t *opts, char *digest)
+{
+#ifdef NO_DIGEST_CACHE
+ return 0;
+#else
+
+#ifdef HAVE_LIBGDBM
+ GDBM_FILE rpdb;
+#elif HAVE_LIBNDBM
+ DBM *rpdb;
+#endif
+ datum db_key, db_ent;
+
+ char *digest = NULL;
+ int digest_len, res = SPA_MSG_SUCCESS;
+
+ digest_cache_info_t dc_info;
+
+ digest_len = strlen(digest);
+
+ db_key.dptr = digest;
+ db_key.dsize = digest_len;
+
+ /* Check the db for the key
+ */
+#ifdef HAVE_LIBGDBM
+ rpdb = gdbm_open(
+ opts->config[CONF_DIGEST_DB_FILE], 512, GDBM_WRCREAT, S_IRUSR|S_IWUSR, 0
+ );
+#elif HAVE_LIBNDBM
+ rpdb = dbm_open(opts->config[CONF_DIGEST_DB_FILE], O_RDWR, 0);
+#endif
+
+ if(!rpdb)
+ {
+ log_msg(LOG_WARNING, "Error opening digest_cache: '%s': %s",
+ opts->config[CONF_DIGEST_DB_FILE],
+ MY_DBM_STRERROR(errno)
+ );
+
+ return(SPA_MSG_DIGEST_CACHE_ERROR);
+ }
+
+ db_ent = MY_DBM_FETCH(rpdb, db_key);
+
+ /* If the datum is null, we have a new entry.
+ */
+ if(db_ent.dptr == NULL)
+ {
/* This is a new SPA packet that needs to be added to the cache.
*/
dc_info.src_ip = opts->spa_pkt.packet_src_ip;
res = SPA_MSG_SUCCESS;
}
+ else
+ res = SPA_MSG_DIGEST_CACHE_ERROR;
MY_DBM_CLOSE(rpdb);
return(res);
#endif /* NO_DIGEST_CACHE */
-}
+
#endif /* USE_FILE_CACHE */
#if USE_FILE_CACHE
/* Prototypes
*/
int replay_cache_init(fko_srv_options_t *opts);
-int replay_check(fko_srv_options_t *opts, fko_ctx_t ctx);
+int is_replay(fko_srv_options_t *opts, char *digest);
+int add_replay(fko_srv_options_t *opts, char *digest);
#ifdef USE_FILE_CACHE
int replay_file_cache_init(fko_srv_options_t *opts);
-int replay_check_file_cache(fko_srv_options_t *opts, fko_ctx_t ctx);
+int is_replay_file_cache(fko_srv_options_t *opts, char *digest);
+int add_replay_file_cache(fko_srv_options_t *opts, char *digest);
void free_replay_list(fko_srv_options_t *opts);
#else
int replay_db_cache_init(fko_srv_options_t *opts);
-int replay_check_dbm_cache(fko_srv_options_t *opts, fko_ctx_t ctx);
+int is_replay_dbm_cache(fko_srv_options_t *opts, char *digest);
+int add_replay_dbm_cache(fko_srv_options_t *opts, char *digest);
#endif
#endif /* REPLAY_CACHE_H */
--- /dev/null
+SOURCE: ANY;
+KEY: fwknoptest;
+OPEN_PORTS: tcp/22;
+FW_ACCESS_TIMEOUT: 2;
+
+SOURCE: ANY;
+KEY: fwknoptest;
+OPEN_PORTS: tcp/80;
+FW_ACCESS_TIMEOUT: 3;
my $invalid_expire_access_conf = "$conf_dir/invalid_expire_access.conf";
my $invalid_source_access_conf = "$conf_dir/invalid_source_access.conf";
my $force_nat_access_conf = "$conf_dir/force_nat_access.conf";
+my $dual_key_usage_access_conf = "$conf_dir/dual_key_usage_access.conf";
my $gpg_access_conf = "$conf_dir/gpg_access.conf";
my $default_digest_file = "$run_dir/digest.cache";
my $default_pid_file = "$run_dir/fwknopd.pid";
my @tests_to_include = ();
my $test_exclude = '';
my @tests_to_exclude = ();
+my %valgrind_flagged_fcns = ();
+my %valgrind_flagged_fcns_unique = ();
my $list_mode = 0;
+my $diff_dir1 = '';
+my $diff_dir2 = '';
my $loopback_intf = '';
my $anonymize_results = 0;
my $current_test_file = "$output_dir/init";
'List-mode' => \$list_mode,
'enable-valgrind' => \$use_valgrind,
'valgrind-path=s' => \$valgrindCmd,
+ 'output-dir=s' => \$output_dir,
'diff' => \$diff_mode,
+ 'diff-dir1=s' => \$diff_dir1,
+ 'diff-dir2=s' => \$diff_dir2,
'help' => \$help
);
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
+ 'detail' => 'dual usage access key (tcp/80 http)',
+ 'err_msg' => 'could not complete SPA cycle',
+ 'function' => \&spa_cycle,
+ 'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+ "$fwknopCmd -A tcp/80 -a $fake_ip -D $loopback_ip --get-key " .
+ "$local_key_file --verbose --verbose",
+ 'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
+ "$fwknopdCmd -c $default_conf -a $dual_key_usage_access_conf " .
+ "-d $default_digest_file -p $default_pid_file $intf_str",
+ ### check for the first stanza that does not allow tcp/80 - the
+ ### second stanza allows this
+ 'server_positive_output_matches' => [qr/stanza #1\)\sOne\sor\smore\srequested\sprotocol\/ports\swas\sdenied/],
+ 'fw_rule_created' => $NEW_RULE_REQUIRED,
+ 'fw_rule_removed' => $NEW_RULE_REMOVED,
+ 'fatal' => $NO
+ },
+ {
+ 'category' => 'Rijndael SPA',
+ 'subcategory' => 'client+server',
'detail' => 'packet aging (past) (tcp/22 ssh)',
'err_msg' => 'old SPA packet accepted',
'function' => \&spa_cycle,
'fwknopd_cmdline' => $default_server_gpg_args,
'fatal' => $NO
},
-
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'server',
},
);
+if ($use_valgrind) {
+ push @tests,
+ {
+ 'category' => 'valgrind output',
+ 'subcategory' => 'flagged functions',
+ 'detail' => '',
+ 'err_msg' => 'could not parse flagged functions',
+ 'function' => \&parse_valgrind_flagged_functions,
+ 'fatal' => $NO
+ };
+}
+
my %test_keys = (
'category' => $REQUIRED,
'subcategory' => $OPTIONAL,
if (@tests_to_include) {
my $found = 0;
for my $test (@tests_to_include) {
- if ($msg =~ /$test/) {
+ if ($msg =~ /$test/ or ($use_valgrind
+ and $msg =~ /valgrind\soutput/)) {
$found = 1;
last;
}
}
sub diff_test_results() {
+
+ $diff_dir1 = "${output_dir}.last" unless $diff_dir2;
+ $diff_dir2 = $output_dir unless $diff_dir1;
+
die "[*] Need results from a previous run before running --diff"
- unless -d "${output_dir}.last";
- die "[*] Current results set does not exist." unless -d $output_dir;
+ unless -d $diff_dir2;
+ die "[*] Current results set does not exist." unless -d $diff_dir1;
my %current_tests = ();
my %previous_tests = ();
### Only diff results for matching tests (parse the logfile to see which
### test numbers match across the two test cycles).
- &build_results_hash(\%current_tests, $output_dir);
- &build_results_hash(\%previous_tests, "${output_dir}.last");
+ &build_results_hash(\%current_tests, $diff_dir1);
+ &build_results_hash(\%previous_tests, $diff_dir2);
for my $test_msg (sort {$current_tests{$a}{'num'} <=> $current_tests{$b}{'num'}}
keys %current_tests) {
### remove CMD timestamps
my $cmd_search_re = qr/^\S+\s.*?\s\d{4}\sCMD\:/;
- for my $file ("${output_dir}.last/${previous_num}.test",
- "${output_dir}.last/${previous_num}_fwknopd.test",
- "${output_dir}/${current_num}.test",
- "${output_dir}/${current_num}_fwknopd.test",
+ for my $file ("$diff_dir1/${previous_num}.test",
+ "$diff_dir1/${previous_num}_fwknopd.test",
+ "$diff_dir2/${current_num}.test",
+ "$diff_dir2/${current_num}_fwknopd.test",
) {
system qq{perl -p -i -e 's|$valgrind_search_re||' $file} if -e $file;
system qq{perl -p -i -e 's|$cmd_search_re|CMD:|' $file} if -e $file;
}
- if (-e "${output_dir}.last/${previous_num}.test"
- and -e "${output_dir}/${current_num}.test") {
- system "diff -u ${output_dir}.last/${previous_num}.test " .
- "${output_dir}/${current_num}.test";
+ if (-e "$diff_dir1/${previous_num}.test"
+ and -e "$diff_dir2/${current_num}.test") {
+ system "diff -u $diff_dir1/${previous_num}.test " .
+ "$diff_dir2/${current_num}.test";
}
- if (-e "${output_dir}.last/${previous_num}_fwknopd.test"
- and -e "${output_dir}/${current_num}_fwknopd.test") {
- system "diff -u ${output_dir}.last/${previous_num}_fwknopd.test " .
- "${output_dir}/${current_num}_fwknopd.test";
+ if (-e "$diff_dir1/${previous_num}_fwknopd.test"
+ and -e "$diff_dir2/${current_num}_fwknopd.test") {
+ system "diff -u $diff_dir1/${previous_num}_fwknopd.test " .
+ "$diff_dir2/${current_num}_fwknopd.test";
}
return;
return;
}
+sub parse_valgrind_flagged_functions() {
+ for my $file (glob("$output_dir/*.test")) {
+ my $type = 'server';
+ $type = 'client' if $file =~ /\d\.test/;
+ open F, "< $file" or die $!;
+ while (<F>) {
+ ### ==30969== by 0x4E3983A: fko_set_username (fko_user.c:65)
+ if (/^==.*\sby\s\S+\:\s(\S+)\s(.*)/) {
+ $valgrind_flagged_fcns{$type}{"$1 $2"}++;
+ $valgrind_flagged_fcns_unique{$type}{$1}++;
+ }
+ }
+ close F;
+ }
+
+ open F, ">> $current_test_file" or die $!;
+ for my $type ('client', 'server') {
+ print F "\n[+] fwknop $type functions (unique view):\n";
+ next unless defined $valgrind_flagged_fcns_unique{$type};
+ for my $fcn (sort {$valgrind_flagged_fcns_unique{$type}{$b}
+ <=> $valgrind_flagged_fcns_unique{$type}{$a}}
+ keys %{$valgrind_flagged_fcns_unique{$type}}) {
+ printf F " %5d : %s\n", $valgrind_flagged_fcns_unique{$type}{$fcn}, $fcn;
+ }
+ print F "\n[+] fwknop $type functions (with call line numbers):\n";
+ for my $fcn (sort {$valgrind_flagged_fcns{$type}{$b}
+ <=> $valgrind_flagged_fcns{$type}{$a}} keys %{$valgrind_flagged_fcns{$type}}) {
+ printf F " %5d : %s\n", $valgrind_flagged_fcns{$type}{$fcn}, $fcn;
+ }
+ next unless defined $valgrind_flagged_fcns{$type};
+
+ }
+ close F;
+ return 1;
+}
+
sub is_fw_rule_active() {
my $test_hr = shift;
}
sub usage() {
- return;
+ print <<_HELP_;
+
+[+] $0 <options>
+
+ -A --Anonymize-results - Prepare anonymized results at:
+ $tarfile
+ --diff - Compare the results of one test run to
+ another. By default this compares output
+ in ${output_dir}.last to $output_dir
+ --diff-dir1=<path> - Left hand side of diff directory path,
+ default is: ${output_dir}.last
+ --diff-dir2=<path> - Right hand side of diff directory path,
+ default is: $output_dir
+ --include=<regex> - Specify a regex to be used over test
+ names that must match.
+ --exclude=<regex> - Specify a regex to be used over test
+ names that must not match.
+ --enable-recompile - Recompile fwknop sources and look for
+ compilation warnings.
+ --enable-valgrind - Run every test underneath valgrind.
+ --List - List test names.
+ --loopback-intf=<intf> - Specify loopback interface name (default
+ depends on the OS where the test suite
+ is executed).
+ --output-dir=<path> - Path to output directory, default is:
+ $output_dir
+ --fwknop-path=<path> - Path to fwknop binary, default is:
+ $fwknopCmd
+ --fwknopd-path=<path> - Path to fwknopd binary, default is:
+ $fwknopdCmd
+ --libfko-path=<path> - Path to libfko, default is:
+ $libfko_bin
+ --valgrind-path=<path> - Path to valgrind, default is:
+ $valgrindCmd
+ -h --help - Display usage on STDOUT and exit.
+
+_HELP_
+ exit 0;
}