Only cache replay digests for SPA packets that decrypt

This change ensures that we only cache replay digests for those SPA packets
that actually decrypt.  Not doing this would have allowed an attacker to
potentially fill up digest cache space with digests for garbage packets.
This commit is contained in:
Michael Rash
2012-07-08 08:36:30 -04:00
parent 6b3e5ef3c2
commit be4193d734
3 changed files with 197 additions and 122 deletions
+92 -12
View File
@@ -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
@@ -178,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);
@@ -213,7 +272,17 @@ incoming_spa(fko_srv_options_t *opts)
if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
/* Check for a replay attack
*/
if (is_replay(opts, spa_pkt->packet_data) != SPA_MSG_SUCCESS)
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
@@ -240,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);
@@ -366,7 +433,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)
{
@@ -383,6 +450,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.
*/
@@ -656,12 +740,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;
}
+99 -107
View File
@@ -77,61 +77,6 @@
#define DATE_LEN 18
#define MAX_DIGEST_SIZE 64
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;
}
/* Rotate the digest file by simply renaming it.
*/
static void
@@ -475,47 +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
is_replay(fko_srv_options_t *opts, unsigned char *pkt_data)
is_replay(fko_srv_options_t *opts, char *digest)
{
#ifdef NO_DIGEST_CACHE
return(-1);
#else
#if USE_FILE_CACHE
return is_replay_file_cache(opts, pkt_data);
return is_replay_file_cache(opts, digest);
#else
return is_replay_dbm_cache(opts, pkt_data);
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
is_replay_file_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
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 = get_raw_digest(&digest, (char *)pkt_data);
if(res != FKO_SUCCESS)
{
if (digest != NULL)
free(digest);
return res;
}
if (digest == NULL)
return SPA_MSG_ERROR;
struct digest_cache_list *digest_list_ptr = NULL;
digest_len = strlen(digest);
@@ -529,21 +472,29 @@ is_replay_file_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
replay_warning(opts, &(digest_list_ptr->cache_info));
free(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",
fko_errstr(SPA_MSG_ERROR));
free(digest);
return(SPA_MSG_ERROR);
}
if ((digest_elm->cache_info.digest = calloc(1, digest_len+1)) == NULL)
@@ -551,7 +502,6 @@ is_replay_file_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
log_msg(LOG_WARNING, "Error calloc() returned NULL for digest cache string",
fko_errstr(SPA_MSG_ERROR));
free(digest_elm);
free(digest);
return(SPA_MSG_ERROR);
}
@@ -574,7 +524,6 @@ is_replay_file_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
{
log_msg(LOG_WARNING, "Could not open digest cache: %s",
opts->config[CONF_DIGEST_FILE]);
free(digest);
return(SPA_MSG_DIGEST_CACHE_ERROR);
}
@@ -593,14 +542,13 @@ is_replay_file_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
fclose(digest_file_ptr);
free(digest);
return(SPA_MSG_SUCCESS);
}
#endif /* USE_FILE_CACHE */
#if !USE_FILE_CACHE
int
is_replay_dbm_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
is_replay_dbm_cache(fko_srv_options_t *opts, char *digest)
{
#ifdef NO_DIGEST_CACHE
return 0;
@@ -614,22 +562,10 @@ is_replay_dbm_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
datum db_key, db_ent;
char *digest = NULL;
int digest_len, res;
int digest_len, res = SPA_MSG_SUCCESS;
digest_cache_info_t dc_info;
res = get_raw_digest(&digest, (char *)pkt_data);
if(res != FKO_SUCCESS)
{
if (digest != NULL)
free(digest);
return res;
}
if (digest == NULL)
return SPA_MSG_ERROR;
digest_len = strlen(digest);
db_key.dptr = digest;
@@ -652,7 +588,6 @@ is_replay_dbm_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
MY_DBM_STRERROR(errno)
);
free(digest);
return(SPA_MSG_DIGEST_CACHE_ERROR);
}
@@ -676,9 +611,65 @@ is_replay_dbm_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
#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;
@@ -703,13 +694,14 @@ is_replay_dbm_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
res = SPA_MSG_SUCCESS;
}
else
res = SPA_MSG_DIGEST_CACHE_ERROR;
MY_DBM_CLOSE(rpdb);
free(digest);
return(res);
#endif /* NO_DIGEST_CACHE */
}
#endif /* USE_FILE_CACHE */
#if USE_FILE_CACHE
+6 -3
View File
@@ -59,14 +59,17 @@ struct digest_cache_list {
/* Prototypes
*/
int replay_cache_init(fko_srv_options_t *opts);
int is_replay(fko_srv_options_t *opts, unsigned char *pkt_data);
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 is_replay_file_cache(fko_srv_options_t *opts, unsigned char *pkt_data);
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 is_replay_dbm_cache(fko_srv_options_t *opts, unsigned char *pkt_data);
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 */