From 8c1261ca39fba47568542b8afdb5ca1b16cadf3e Mon Sep 17 00:00:00 2001 From: Damien Stuart Date: Fri, 16 Oct 2009 02:23:02 +0000 Subject: [PATCH] Fixed memory leak issue in libfko when fko_new_with_data() was called with a bad key. Added autoconf checks for gdbm with fallback to ndbm for server builds. Added digest cache capability using gdbm (in ndbm compatibility mode) or ndbm for replay detection. git-svn-id: file:///home/mbr/svn/fwknop/trunk@153 510a4753-2344-4c79-9c09-4d669213fbeb --- configure.ac | 19 +++-- lib/fko_encryption.c | 1 + lib/fko_funcs.c | 35 ++++++++-- server/Makefile.am | 6 +- server/fwknopd.c | 15 ++-- server/incoming_spa.c | 37 ++++++---- server/replay_dbm.c | 156 ++++++++++++++++++++++++++++++++++++++++++ server/replay_dbm.h | 37 ++++++++++ 8 files changed, 267 insertions(+), 39 deletions(-) create mode 100644 server/replay_dbm.c create mode 100644 server/replay_dbm.h diff --git a/configure.ac b/configure.ac index 3b62c26a..ed412c3e 100644 --- a/configure.ac +++ b/configure.ac @@ -189,15 +189,23 @@ AC_ARG_ENABLE([server], []) AM_CONDITIONAL([WANT_SERVER], [test "$want_server" = yes]) -dnl Check for libpcap if we are building the server component +dnl Check for libpcap, gdbm (or ndbm) if we are building the server component dnl -have_pcap=yes AS_IF([test "$want_server" = yes], - AC_CHECK_LIB([pcap],[pcap_open_live], - AC_DEFINE([HAVE_LIBPCAP], [1], [Define if you have libpcap]), [have_pcap=no] + # Looking for libpcap + # + AC_CHECK_LIB([pcap],[pcap_open_live], [], + [ AC_MSG_ERROR([fwknopd needs libpcap])] + ) + + # Looking for gdbm or fallback to ndbm or bail + # + AC_CHECK_LIB([gdbm],[dbm_open], [], + [ AC_CHECK_LIB([ndbm],[dbm_open], [], + [ AC_MSG_ERROR([fwknopd needs either gdbm or ndbm])] + )] ) ) -AM_CONDITIONAL([HAVE_LIBPCAP],[test "$have_pcap" = yes]) AC_CONFIG_FILES([Makefile lib/Makefile @@ -213,6 +221,5 @@ echo " ============================================ Client build: $want_client Server build: $want_server - - with libpcap: $have_pcap GPG encryption support: $have_gpgme " diff --git a/lib/fko_encryption.c b/lib/fko_encryption.c index 47feea78..e659b6c9 100644 --- a/lib/fko_encryption.c +++ b/lib/fko_encryption.c @@ -111,6 +111,7 @@ _rijndael_decrypt(fko_ctx_t ctx, char *dec_key, int b64_len) return(FKO_ERROR_MEMORY_ALLOCATION); memmove(tbuf+strlen(B64_RIJNDAEL_SALT), tbuf, b64_len); + ctx->encrypted_msg = memcpy(tbuf, B64_RIJNDAEL_SALT, strlen(B64_RIJNDAEL_SALT)); /* Adjust b64_len for added SALT value and Make sure we are still diff --git a/lib/fko_funcs.c b/lib/fko_funcs.c index c71f3d81..0b30e4cc 100644 --- a/lib/fko_funcs.c +++ b/lib/fko_funcs.c @@ -158,12 +158,11 @@ int fko_new_with_data(fko_ctx_t *r_ctx, char *enc_msg, char *dec_key) { fko_ctx_t ctx; + int res = FKO_SUCCESS; /* Are we optimistic or what? */ - int res = fko_new(r_ctx); - if(res != FKO_SUCCESS) - return res; - - ctx = *r_ctx; + ctx = calloc(1, sizeof *ctx); + if(ctx == NULL) + return(FKO_ERROR_MEMORY_ALLOCATION); /* First, add the data to the context. */ @@ -174,13 +173,35 @@ fko_new_with_data(fko_ctx_t *r_ctx, char *enc_msg, char *dec_key) return(FKO_ERROR_MEMORY_ALLOCATION); } + /* Consider it initialized here. + */ + ctx->initval = FKO_CTX_INITIALIZED; + FKO_SET_CTX_INITIALIZED(ctx); + /* If a decryption password is provided, go ahead and decrypt and * decode. */ if(dec_key != NULL) - return(fko_decrypt_spa_data(ctx, dec_key)); + { + res = fko_decrypt_spa_data(ctx, dec_key); - return(FKO_SUCCESS); + if(res != FKO_SUCCESS) + { + fko_destroy(ctx); + return(res); + } + } + +#if HAVE_LIBGPGME + /* Set gpg signature verify on. + */ + ctx->verify_gpg_sigs = 1; +#endif /* HAVE_LIBGPGME */ + + + *r_ctx = ctx; + + return(res); } /* Destroy a context and free its resources diff --git a/server/Makefile.am b/server/Makefile.am index 2286c697..4120262d 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -4,14 +4,10 @@ fwknopd_SOURCES = fwknopd.c fwknopd.h config_init.c config_init.h \ fwknopd_common.h incoming_spa.c incoming_spa.h \ pcap_capture.c pcap_capture.h process_packet.c \ process_packet.h log_msg.c log_msg.h utils.c utils.h \ - sig_handler.c sig_handler.h + sig_handler.c sig_handler.h replay_dbm.c replay_dbm.h fwknopd_LDADD = $(top_builddir)/lib/libfko.la -if HAVE_LIBPCAP - fwknopd_LDADD += -lpcap -endif - fwknopd_CPPFLAGS = -I $(top_srcdir)/lib -I $(top_srcdir)/common -DSYSCONFDIR=\"$(sysconfdir)\" fwknopddir = @sysconfdir@/fwknop diff --git a/server/fwknopd.c b/server/fwknopd.c index 70c88c0a..ec8a37db 100644 --- a/server/fwknopd.c +++ b/server/fwknopd.c @@ -33,6 +33,7 @@ #include "log_msg.h" #include "utils.h" #include "sig_handler.h" +#include "replay_dbm.h" /* Prototypes */ @@ -44,7 +45,7 @@ int main(int argc, char **argv) { fko_ctx_t ctx; - int res, last_sig; + int res, last_sig, rpdb_count; char *spa_data, *version; char access_buf[MAX_LINE_LEN]; pid_t old_pid; @@ -176,12 +177,12 @@ main(int argc, char **argv) exit(EXIT_FAILURE); } -#ifndef HAVE_LIBPCAP - log_msg(LOG_ERR|LOG_STDERR, - "libpcap is not avaiable, I'm hosed (for now)."); - exit(EXIT_FAILURE); -#endif - + /* Initialize the digest cache (replay attack detection dbm). + */ + rpdb_count = replay_db_init(&opts); + +fprintf(stderr, "RPDB Count: %i\n", rpdb_count); + /* Intiate pcap capture mode... */ pcap_capture(&opts); diff --git a/server/incoming_spa.c b/server/incoming_spa.c index 447b8a43..70748ada 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -25,6 +25,7 @@ */ #include "fwknopd_common.h" #include "incoming_spa.h" +#include "log_msg.h" /* The pcap capture routine. */ @@ -45,27 +46,35 @@ fprintf(stderr, "SPA Packet: '%s'\n", spa_pkt->packet_data); /* Get the decryption key */ + // TODO: finish me + /* Decode the packet data + * --DSS TEMP note using the hard-coded "sdf" as the password. + * this is just for dev testing until I get the + * access.conf handling in. + */ res = fko_new_with_data(&ctx, spa_pkt->packet_data, "sdf"); - if(res == FKO_SUCCESS) - { - -fprintf(stderr, "Decode res = %i\n", res); - display_ctx(ctx); - - fko_destroy(ctx); - } - else - { - fprintf(stderr, "Error creating fko context: %s\n", fko_errstr(res)); - } - /* Reset the packet data length to 0. */ spa_pkt->packet_data_len = 0; - return(0); + if(res != FKO_SUCCESS) + { + fprintf(stderr, "Error creating fko context: %s\n", fko_errstr(res)); + return(-1); + } + +fprintf(stderr, "Decode res = %i\n", res); + + + display_ctx(ctx); + + res = replay_check(opts, ctx); + + fko_destroy(ctx); + + return(res); } /***EOF***/ diff --git a/server/replay_dbm.c b/server/replay_dbm.c new file mode 100644 index 00000000..e693d0e4 --- /dev/null +++ b/server/replay_dbm.c @@ -0,0 +1,156 @@ +/* + ***************************************************************************** + * + * File: replay_dbm.c + * + * Author: Damien S. Stuart + * + * Purpose: Provides the functions to check for possible replay attacks + * by using a dbm (ndbm or gdbm in ndbm compatibility mode) file + * to store a digest of previously received SPA packets. + * + * Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org) + * + * License (GNU Public License): + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + ***************************************************************************** +*/ +#include "replay_dbm.h" +#include "log_msg.h" + +#if HAVE_LIBGDBM + /* NOTE: We are using gdbm in ndbm compatibility mode so we grab its + * version of ndbm.h + */ +// #include + #include +#elif HAVE_LIBNDBM + #include +#else + #error "No DBM header file found. WTF?" +#endif + +#if HAVE_SYS_SOCKET_H + #include +#endif +#include + +#include + +#define MAX_DIGEST_SIZE 64 + +/* Check for the existence of the replay dbm file, and create it if it does + * not exist. Returns the number of db entries or -1 on error. +*/ +int +replay_db_init(fko_srv_options_t *opts) +{ + DBM *rpdb; + datum db_ent; + + int db_count = 0; + + rpdb = dbm_open(opts->config[CONF_DIGEST_FILE], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); + + if(!rpdb) + { + perror("Unable to create digest cache file: "); + return(-1); + } + + for (db_ent = dbm_firstkey(rpdb); db_ent.dptr != NULL; db_ent = dbm_nextkey(rpdb)) + db_count++; + + dbm_close(rpdb); + + return(db_count); +} + +/* 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. +*/ +int +replay_check(fko_srv_options_t *opts, fko_ctx_t ctx) +{ + DBM *rpdb; + datum db_key, db_ent; + + char ipaddr[INET_ADDRSTRLEN+1] = {0}; + + char *digest; + int digest_len, res; + + res = fko_get_spa_digest(ctx, &digest); + if(res != FKO_SUCCESS) + { + log_msg(LOG_WARNING|LOG_STDERR, "Error getting digest from SPA data: %s", + fko_errstr(res)); + + return(-1); + } + + digest_len = strlen(digest); + + db_key.dptr = digest; + db_key.dsize = digest_len; + + /* Check the db for the key + */ + rpdb = dbm_open(opts->config[CONF_DIGEST_FILE], O_RDWR, 0); + + if(!rpdb) + { + log_msg(LOG_WARNING|LOG_STDERR, "Error opening digest_cache: %s", + strerror(errno)); + + return(-1); + } + + db_ent = dbm_fetch(rpdb, db_key); + + /* If the datum is not null, we have a match. Otherwise, we add + * this entry to the cache. + */ + if(db_ent.dptr != NULL) + { + /* Convert the IP to a human readable form + */ + inet_ntop(AF_INET, &(opts->spa_pkt.packet_src_ip), + ipaddr, INET_ADDRSTRLEN); + + log_msg(LOG_WARNING|LOG_STDERR, + "Replay detected from source IP: %s", ipaddr); + + res = 1; + } else { + db_ent.dptr = (char*)&(opts->spa_pkt.packet_src_ip); + db_ent.dsize = sizeof(opts->spa_pkt.packet_src_ip); + + if(dbm_store(rpdb, db_key, db_ent, DBM_INSERT) != 0) + { + log_msg(LOG_WARNING|LOG_STDERR, "Error adding entry digest_cache: %s", + strerror(errno)); + + res = -1; + } + + res = 0; + } + + dbm_close(rpdb); + + return(res); +} + +/***EOF***/ diff --git a/server/replay_dbm.h b/server/replay_dbm.h new file mode 100644 index 00000000..73980b1f --- /dev/null +++ b/server/replay_dbm.h @@ -0,0 +1,37 @@ +/* + ***************************************************************************** + * + * File: replay_dbm.h + * + * Author: Damien Stuart (dstuart@dstuart.org) + * + * Purpose: Header file for fwknopd replay_dbm.c functions. + * + * Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org) + * + * License (GNU Public License): + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + ***************************************************************************** +*/ +#ifndef REPLAY_DBM_H +#define REPLAY_DBM_H + +#include "fwknopd_common.h" +#include "fko.h" + +/* Prototypes +*/ +int replay_db_init(fko_srv_options_t *opts); +int replay_check(fko_srv_options_t *opts, fko_ctx_t ctx); + +#endif /* REPLAY_DBM_H */