merged usage() information from master
This commit is contained in:
6
CREDITS
6
CREDITS
@@ -18,3 +18,9 @@ Max Kastanas
|
||||
|
||||
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.
|
||||
|
||||
@@ -224,6 +224,8 @@ DLL_API int fko_set_spa_server_auth(fko_ctx_t ctx, const char *server_auth);
|
||||
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);
|
||||
@@ -252,7 +254,9 @@ DLL_API int fko_get_spa_nat_access(fko_ctx_t ctx, char **nat_access);
|
||||
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);
|
||||
|
||||
@@ -69,6 +69,12 @@ struct fko_context {
|
||||
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;
|
||||
|
||||
185
lib/fko_digest.c
185
lib/fko_digest.c
@@ -36,8 +36,9 @@
|
||||
|
||||
/* 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
|
||||
*/
|
||||
@@ -47,13 +48,25 @@ fko_set_spa_digest_type(fko_ctx_t ctx, const short digest_type)
|
||||
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
|
||||
@@ -69,11 +82,91 @@ fko_get_spa_digest_type(fko_ctx_t ctx, short *digest_type)
|
||||
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)
|
||||
{
|
||||
/* Must be initialized
|
||||
*/
|
||||
if(!CTX_INITIALIZED(ctx))
|
||||
return(FKO_ERROR_CTX_NOT_INITIALIZED);
|
||||
|
||||
*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(digest_type)
|
||||
{
|
||||
case FKO_DIGEST_MD5:
|
||||
md = malloc(MD_HEX_SIZE(MD5_DIGEST_LENGTH)+1);
|
||||
if(md == NULL)
|
||||
return(FKO_ERROR_MEMORY_ALLOCATION);
|
||||
|
||||
md5_base64(md,
|
||||
(unsigned char*)data, strlen(data));
|
||||
break;
|
||||
|
||||
case FKO_DIGEST_SHA1:
|
||||
md = malloc(MD_HEX_SIZE(SHA1_DIGEST_LENGTH)+1);
|
||||
if(md == NULL)
|
||||
return(FKO_ERROR_MEMORY_ALLOCATION);
|
||||
|
||||
sha1_base64(md,
|
||||
(unsigned char*)data, strlen(data));
|
||||
break;
|
||||
|
||||
case FKO_DIGEST_SHA256:
|
||||
md = malloc(MD_HEX_SIZE(SHA256_DIGEST_LENGTH)+1);
|
||||
if(md == NULL)
|
||||
return(FKO_ERROR_MEMORY_ALLOCATION);
|
||||
|
||||
sha256_base64(md,
|
||||
(unsigned char*)data, strlen(data));
|
||||
break;
|
||||
|
||||
case FKO_DIGEST_SHA384:
|
||||
md = malloc(MD_HEX_SIZE(SHA384_DIGEST_LENGTH)+1);
|
||||
if(md == NULL)
|
||||
return(FKO_ERROR_MEMORY_ALLOCATION);
|
||||
|
||||
sha384_base64(md,
|
||||
(unsigned char*)data, strlen(data));
|
||||
break;
|
||||
|
||||
case FKO_DIGEST_SHA512:
|
||||
md = malloc(MD_HEX_SIZE(SHA512_DIGEST_LENGTH)+1);
|
||||
if(md == NULL)
|
||||
return(FKO_ERROR_MEMORY_ALLOCATION);
|
||||
|
||||
sha512_base64(md,
|
||||
(unsigned char*)data, strlen(data));
|
||||
break;
|
||||
|
||||
default:
|
||||
return(FKO_ERROR_INVALID_DIGEST_TYPE);
|
||||
}
|
||||
|
||||
/* Just in case this is a subsquent call to this function. We
|
||||
* do not want to be leaking memory.
|
||||
*/
|
||||
if(*digest != NULL)
|
||||
free(*digest);
|
||||
|
||||
*digest = md;
|
||||
|
||||
return(FKO_SUCCESS);
|
||||
}
|
||||
|
||||
int
|
||||
fko_set_spa_digest(fko_ctx_t ctx)
|
||||
{
|
||||
/* Must be initialized
|
||||
*/
|
||||
if(!CTX_INITIALIZED(ctx))
|
||||
@@ -84,66 +177,25 @@ fko_set_spa_digest(fko_ctx_t ctx)
|
||||
if(ctx->encoded_msg == NULL)
|
||||
return(FKO_ERROR_MISSING_ENCODED_DATA);
|
||||
|
||||
switch(ctx->digest_type)
|
||||
{
|
||||
case FKO_DIGEST_MD5:
|
||||
md = malloc(MD_HEX_SIZE(MD5_DIGEST_LENGTH)+1);
|
||||
if(md == NULL)
|
||||
return(FKO_ERROR_MEMORY_ALLOCATION);
|
||||
return set_digest(ctx->encoded_msg,
|
||||
&ctx->digest, ctx->digest_type);
|
||||
}
|
||||
|
||||
md5_base64(md,
|
||||
(unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
|
||||
break;
|
||||
|
||||
case FKO_DIGEST_SHA1:
|
||||
md = malloc(MD_HEX_SIZE(SHA1_DIGEST_LENGTH)+1);
|
||||
if(md == NULL)
|
||||
return(FKO_ERROR_MEMORY_ALLOCATION);
|
||||
|
||||
sha1_base64(md,
|
||||
(unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
|
||||
break;
|
||||
|
||||
case FKO_DIGEST_SHA256:
|
||||
md = malloc(MD_HEX_SIZE(SHA256_DIGEST_LENGTH)+1);
|
||||
if(md == NULL)
|
||||
return(FKO_ERROR_MEMORY_ALLOCATION);
|
||||
|
||||
sha256_base64(md,
|
||||
(unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
|
||||
break;
|
||||
|
||||
case FKO_DIGEST_SHA384:
|
||||
md = malloc(MD_HEX_SIZE(SHA384_DIGEST_LENGTH)+1);
|
||||
if(md == NULL)
|
||||
return(FKO_ERROR_MEMORY_ALLOCATION);
|
||||
|
||||
sha384_base64(md,
|
||||
(unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
|
||||
break;
|
||||
|
||||
case FKO_DIGEST_SHA512:
|
||||
md = malloc(MD_HEX_SIZE(SHA512_DIGEST_LENGTH)+1);
|
||||
if(md == NULL)
|
||||
return(FKO_ERROR_MEMORY_ALLOCATION);
|
||||
|
||||
sha512_base64(md,
|
||||
(unsigned char*)ctx->encoded_msg, strlen(ctx->encoded_msg));
|
||||
break;
|
||||
|
||||
default:
|
||||
return(FKO_ERROR_INVALID_DIGEST_TYPE);
|
||||
}
|
||||
|
||||
/* Just in case this is a subsquent call to this function. We
|
||||
* do not want to be leaking memory.
|
||||
int
|
||||
fko_set_raw_spa_digest(fko_ctx_t ctx)
|
||||
{
|
||||
/* Must be initialized
|
||||
*/
|
||||
if(ctx->digest != NULL)
|
||||
free(ctx->digest);
|
||||
if(!CTX_INITIALIZED(ctx))
|
||||
return(FKO_ERROR_CTX_NOT_INITIALIZED);
|
||||
|
||||
ctx->digest = md;
|
||||
/* Must have encoded message data to start with.
|
||||
*/
|
||||
if(ctx->encrypted_msg == NULL)
|
||||
return(FKO_ERROR_MISSING_ENCODED_DATA);
|
||||
|
||||
return(FKO_SUCCESS);
|
||||
return set_digest(ctx->encrypted_msg,
|
||||
&ctx->raw_digest, ctx->raw_digest_type);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -159,4 +211,17 @@ fko_get_spa_digest(fko_ctx_t ctx, char **md)
|
||||
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***/
|
||||
|
||||
@@ -261,6 +261,9 @@ fko_destroy(fko_ctx_t ctx)
|
||||
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);
|
||||
|
||||
|
||||
@@ -110,6 +110,64 @@ preprocess_spa_data(fko_srv_options_t *opts, const char *src_ip)
|
||||
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
|
||||
@@ -152,6 +210,22 @@ get_spa_data_fields(fko_ctx_t ctx, spa_data_t *spdat)
|
||||
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
|
||||
@@ -162,9 +236,10 @@ incoming_spa(fko_srv_options_t *opts)
|
||||
*/
|
||||
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);
|
||||
|
||||
@@ -192,6 +267,36 @@ incoming_spa(fko_srv_options_t *opts)
|
||||
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++;
|
||||
@@ -204,8 +309,6 @@ incoming_spa(fko_srv_options_t *opts)
|
||||
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);
|
||||
|
||||
@@ -332,7 +435,7 @@ incoming_spa(fko_srv_options_t *opts)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do we have a valid FKO context?
|
||||
/* Do we have a valid FKO context? Did the SPA decrypt properly?
|
||||
*/
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
@@ -349,6 +452,23 @@ incoming_spa(fko_srv_options_t *opts)
|
||||
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.
|
||||
*/
|
||||
@@ -389,20 +509,6 @@ incoming_spa(fko_srv_options_t *opts)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
@@ -636,12 +742,8 @@ incoming_spa(fko_srv_options_t *opts)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -420,44 +420,45 @@ replay_db_cache_init(fko_srv_options_t *opts)
|
||||
#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 */
|
||||
}
|
||||
|
||||
int
|
||||
add_replay(fko_srv_options_t *opts, char *digest)
|
||||
{
|
||||
#ifdef NO_DIGEST_CACHE
|
||||
return(-1);
|
||||
#else
|
||||
|
||||
#if USE_FILE_CACHE
|
||||
return add_replay_file_cache(opts, digest);
|
||||
#else
|
||||
return add_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)
|
||||
is_replay_file_cache(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;
|
||||
int digest_len = 0;
|
||||
|
||||
struct digest_cache_list *digest_list_ptr = NULL, *digest_elm = NULL;
|
||||
|
||||
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);
|
||||
}
|
||||
struct digest_cache_list *digest_list_ptr = NULL;
|
||||
|
||||
digest_len = strlen(digest);
|
||||
|
||||
@@ -474,11 +475,21 @@ replay_check_file_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
|
||||
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",
|
||||
@@ -537,7 +548,7 @@ replay_check_file_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
|
||||
|
||||
#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;
|
||||
@@ -550,20 +561,11 @@ replay_check_dbm_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
|
||||
#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;
|
||||
@@ -609,9 +611,65 @@ replay_check_dbm_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
|
||||
#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;
|
||||
@@ -636,12 +694,14 @@ replay_check_dbm_cache(fko_srv_options_t *opts, fko_ctx_t ctx)
|
||||
|
||||
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
|
||||
|
||||
@@ -59,14 +59,17 @@ struct digest_cache_list {
|
||||
/* 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 */
|
||||
|
||||
9
test/conf/dual_key_usage_access.conf
Normal file
9
test/conf/dual_key_usage_access.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
SOURCE: ANY;
|
||||
KEY: fwknoptest;
|
||||
OPEN_PORTS: tcp/22;
|
||||
FW_ACCESS_TIMEOUT: 2;
|
||||
|
||||
SOURCE: ANY;
|
||||
KEY: fwknoptest;
|
||||
OPEN_PORTS: tcp/80;
|
||||
FW_ACCESS_TIMEOUT: 3;
|
||||
@@ -33,6 +33,7 @@ my $expired_epoch_access_conf = "$conf_dir/expired_epoch_stanza_access.conf";
|
||||
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";
|
||||
@@ -76,7 +77,11 @@ my $test_include = '';
|
||||
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";
|
||||
@@ -123,7 +128,10 @@ exit 1 unless GetOptions(
|
||||
'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
|
||||
);
|
||||
|
||||
@@ -599,6 +607,25 @@ my @tests = (
|
||||
'fw_rule_removed' => $NEW_RULE_REMOVED,
|
||||
'fatal' => $NO
|
||||
},
|
||||
{
|
||||
'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',
|
||||
@@ -1317,7 +1344,6 @@ my @tests = (
|
||||
'fwknopd_cmdline' => $default_server_gpg_args,
|
||||
'fatal' => $NO
|
||||
},
|
||||
|
||||
{
|
||||
'category' => 'GnuPG (GPG) SPA',
|
||||
'subcategory' => 'server',
|
||||
@@ -1336,6 +1362,18 @@ my @tests = (
|
||||
},
|
||||
);
|
||||
|
||||
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,
|
||||
@@ -1431,7 +1469,8 @@ sub process_include_exclude() {
|
||||
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;
|
||||
}
|
||||
@@ -1452,17 +1491,21 @@ sub process_include_exclude() {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -1493,25 +1536,25 @@ sub diff_results() {
|
||||
### 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;
|
||||
@@ -2722,6 +2765,42 @@ sub identify_loopback_intf() {
|
||||
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;
|
||||
|
||||
@@ -2839,5 +2918,42 @@ sub logr() {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user