Browse files

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.
  • Loading branch information...
1 parent 6b3e5ef commit be4193d734850fe60f14a26b547525ea0b9ce1e9 @mrash committed Jul 8, 2012
Showing with 196 additions and 121 deletions.
  1. +92 −12 server/incoming_spa.c
  2. +98 −106 server/replay_cache.c
  3. +6 −3 server/replay_cache.h
View
104 server/incoming_spa.c
@@ -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;
}
View
204 server/replay_cache.c
@@ -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 */
}
-#if USE_FILE_CACHE
int
-is_replay_file_cache(fko_srv_options_t *opts, unsigned char *pkt_data)
+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;
-
- struct digest_cache_list *digest_list_ptr = NULL, *digest_elm = NULL;
+#ifdef NO_DIGEST_CACHE
+ return(-1);
+#else
- res = get_raw_digest(&digest, (char *)pkt_data);
+#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(res != FKO_SUCCESS)
- {
- if (digest != NULL)
- free(digest);
- return res;
- }
+#if USE_FILE_CACHE
+int
+is_replay_file_cache(fko_srv_options_t *opts, char *digest)
+{
+ int digest_len = 0;
- if (digest == NULL)
- return SPA_MSG_ERROR;
+ struct digest_cache_list *digest_list_ptr = NULL;
digest_len = strlen(digest);
@@ -529,29 +472,36 @@ 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)
{
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
View
9 server/replay_cache.h
@@ -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 */

0 comments on commit be4193d

Please sign in to comment.