merged master 2.0.3 changes

This commit is contained in:
Michael Rash
2012-08-31 21:43:55 -04:00
27 changed files with 846 additions and 133 deletions
+7
View File
@@ -53,3 +53,10 @@ Hank Leininger
- For iptables firewalls, suggested a check for the 'comment' match to
ensure the local environment will properly support fwknopd operations.
The result is the new ENABLE_IPT_COMMENT_CHECK functionality.
Fernando Arnaboldi (IOActive)
- Found important buffer overflow conditions for authenticated SPA clients
in the fwknopd server (pre-2.0.3). These findings enabled fixes to be
developed along with a new fuzzing capability in the test suite.
- Found a condition in which an overly long IP from malicious authenticated
clients is not properly validated by the fwknopd server (pre-2.0.3).
+27
View File
@@ -1,3 +1,30 @@
fwknop-2.0.3 (08//2012):
- Fixed RPM builds by including the $(DESTDIR) prefix for uninstall-local
and install-exec-hook stages in Makefile.am.
- [server] Fernando Arnaboldi from IOActive found several DoS/code
execution vulnerabilities for malicious fwknop clients that manage to
get past the authentication stage (so a such a client must be in
possession of a valid access.conf encryption key). These vulnerbilities
manifested themselves in the handling of malformed access requests, and
both the fwknopd server code along with libfko now perform stronger input
validation of access request data. These vulnerabilities affect
pre-2.0.3 fwknop releases.
- [server] Fernando Arnaboldi from IOActive found a condition in which
the server did not properly validate allow IP addresses from malicious
authenticated clients. This has been fixed with stronger allow IP
validation.
- [client+server] Fernando Arnaboldi from IOActive found that strict
filesystem permissions for various fwknop files are not verified. Added
warnings whenever permissions are not strict enough, and ensured that
files created by the fwknop client and server are only set to user
read/write.
- [client] Fernando Arnaboldi from IOActive found a local buffer overflow
in --last processing with a maliciously constructed ~/.fwknop.run file.
This has been fixed with proper validation of .fwknop.run arguments.
- [test suite] Added a new fuzzing capability to ensure proper server-side
input validation. Fuzzing data is constructed with modified fwknop
client code that is designed to emulate malicious behavior.
fwknop-2.0.2 (08/18/2012):
- [server] For GPG mode, added a new access.conf variable
"GPG_ALLOW_NO_PW" to make it possible to leverage a server-side GPG key
+22 -22
View File
@@ -13,7 +13,7 @@ SUBDIRS = \
common \
$(CLIENT_DIR) \
$(SERVER_DIR) \
doc
doc
EXTRA_DIST = \
android/COPYING \
@@ -149,6 +149,7 @@ EXTRA_DIST = \
test/conf/require_user_access.conf \
test/conf/subnet_source_match_access.conf \
test/conf/local_nat_fwknopd.conf \
test/conf/disable_aging_fwknopd.conf \
test/hardening-check \
test/local_spa.key \
test/test-fwknop.pl \
@@ -173,39 +174,38 @@ dist-hook:
rm -f $(distdir)/server/fwknopd.8
uninstall-local:
if test -f $(sysconfdir)/fwknop/fwknopd.conf; then \
rm -f $(sysconfdir)/fwknop/fwknopd.conf; \
if test -f $(DESTDIR)$(sysconfdir)/fwknop/fwknopd.conf; then \
rm -f $(DESTDIR)$(sysconfdir)/fwknop/fwknopd.conf; \
fi
if test -f $(sysconfdir)/fwknop/fwknopd.conf.inst; then \
rm -f $(sysconfdir)/fwknop/fwknopd.conf.inst; \
if test -f $(DESTDIR)$(sysconfdir)/fwknop/fwknopd.conf.inst; then \
rm -f $(DESTDIR)$(sysconfdir)/fwknop/fwknopd.conf.inst; \
fi
if test -f $(sysconfdir)/fwknop/access.conf; then \
rm -f $(sysconfdir)/fwknop/access.conf; \
if test -f $(DESTDIR)$(sysconfdir)/fwknop/access.conf; then \
rm -f $(DESTDIR)$(sysconfdir)/fwknop/access.conf; \
fi
if test -f $(sysconfdir)/fwknop/access.conf.inst; then \
rm -f $(sysconfdir)/fwknop/access.conf.inst; \
if test -f $(DESTDIR)$(sysconfdir)/fwknop/access.conf.inst; then \
rm -f $(DESTDIR)$(sysconfdir)/fwknop/access.conf.inst; \
fi
install-exec-hook:
if test -d $(sysconfdir)/fwknop; then \
chmod 700 $(sysconfdir)/fwknop; \
if test -d $(DESTDIR)$(sysconfdir)/fwknop; then \
chmod 700 $(DESTDIR)$(sysconfdir)/fwknop; \
fi
if test -f $(sysconfdir)/fwknop/fwknopd.conf; then :; \
if test -f $(DESTDIR)$(sysconfdir)/fwknop/fwknopd.conf; then :; \
else \
if test -f $(sysconfdir)/fwknop/fwknopd.conf.inst; then \
mv $(sysconfdir)/fwknop/fwknopd.conf.inst $(sysconfdir)/fwknop/fwknopd.conf; \
if test -f $(DESTDIR)$(sysconfdir)/fwknop/fwknopd.conf.inst; then \
mv $(DESTDIR)$(sysconfdir)/fwknop/fwknopd.conf.inst $(DESTDIR)$(sysconfdir)/fwknop/fwknopd.conf; \
fi \
fi
if test -f $(sysconfdir)/fwknop/access.conf; then :; \
if test -f $(DESTDIR)$(sysconfdir)/fwknop/access.conf; then :; \
else \
if test -f $(sysconfdir)/fwknop/access.conf.inst; then \
mv $(sysconfdir)/fwknop/access.conf.inst $(sysconfdir)/fwknop/access.conf; \
if test -f $(DESTDIR)$(sysconfdir)/fwknop/access.conf.inst; then \
mv $(DESTDIR)$(sysconfdir)/fwknop/access.conf.inst $(DESTDIR)$(sysconfdir)/fwknop/access.conf; \
fi \
fi
if test -f $(sysconfdir)/fwknop/fwknopd.conf; then \
chmod 600 $(sysconfdir)/fwknop/fwknopd.conf; \
if test -f $(DESTDIR)$(sysconfdir)/fwknop/fwknopd.conf; then \
chmod 600 $(DESTDIR)$(sysconfdir)/fwknop/fwknopd.conf; \
fi
if test -f $(sysconfdir)/fwknop/access.conf; then \
chmod 600 $(sysconfdir)/fwknop/access.conf; \
if test -f $(DESTDIR)$(sysconfdir)/fwknop/access.conf; then \
chmod 600 $(DESTDIR)$(sysconfdir)/fwknop/access.conf; \
fi
-41
View File
@@ -1,41 +0,0 @@
To whom it may concern, this is -*- outline -*- mode.
* The library - libfko:
** C-based test suite.
** Get it out there and get feedback.
* The fwknop C client:
** Config file support (~/.fwknoprc ?).
** Server Auth support (maybe).
* The fwknopd server:
** Sniffer support to acquire SPA packet data ala the fwknopd Perl server:
** SPA packet decryption:
*** Add support for ipfw and pf support eventually.
** Test on embedded platforms - especially OpenWRT on a Linksys router.
* Nice to haves:
** Binary packages:
*** Redhat RPMS
*** Debian .deb
** Linux/Unix platform:
*** A GNOME or KDE GUI app for the client.
** Windows platform:
*** VB and/or C# class wrappers around libfko.dll.
*** A Windows GUI app that uses the dll or wrapper classes.
** Misc:
*** Python module wrapping libfko.
*** Ruby module wrapping libfko.
*** PHP module wrapping libfko.
Copyright 2009-2010 - Damien Stuart
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without
modifications, as long as this notice is preserved.
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.
+17 -3
View File
@@ -109,6 +109,11 @@ parse_time_offset(const char *offset_str)
if (isdigit(offset_str[i])) {
offset_digits[j] = offset_str[i];
j++;
if(j >= MAX_TIME_STR_LEN)
{
fprintf(stderr, "Invalid time offset: %s", offset_str);
exit(EXIT_FAILURE);
}
} else if (offset_str[i] == 'm' || offset_str[i] == 'M') {
offset_type = TIME_OFFSET_MINUTES;
break;
@@ -145,9 +150,9 @@ parse_time_offset(const char *offset_str)
static int
create_fwknoprc(const char *rcfile)
{
FILE *rc;
FILE *rc = NULL;
fprintf(stderr, "Creating initial rc file: %s.\n", rcfile);
fprintf(stdout, "[*] Creating initial rc file: %s.\n", rcfile);
if ((rc = fopen(rcfile, "w")) == NULL)
{
@@ -209,7 +214,7 @@ create_fwknoprc(const char *rcfile)
"# User-provided named stanzas:\n"
"\n"
"# Example for a destination server of 192.168.1.20 to open access to \n"
"# SSH for an IP that is resoved exteranlly, and one with a NAT request\n"
"# SSH for an IP that is resolved externally, and one with a NAT request\n"
"# for a specific source IP that maps port 8088 on the server\n"
"# to port 88 on 192.168.1.55 with timeout.\n"
"#\n"
@@ -231,6 +236,8 @@ create_fwknoprc(const char *rcfile)
fclose(rc);
set_file_perms(rcfile);
return(0);
}
@@ -510,6 +517,13 @@ process_rc(fko_cli_options_t *options)
strlcpy(rcfile, options->rc_file, MAX_PATH_LEN);
}
/* Check rc file permissions - if anything other than user read/write,
* then don't process it. This change was made to help ensure that the
* client consumes a proper rc file with strict permissions set (thanks
* to Fernando Arnaboldi from IOActive for pointing this out).
*/
verify_file_perms_ownership(rcfile);
/* Open the rc file for reading, if it does not exist, then create
* an initial .fwknoprc file with defaults and go on.
*/
+15 -3
View File
@@ -51,6 +51,8 @@ int resolve_ip_http(fko_cli_options_t *options);
static void clean_exit(fko_ctx_t ctx, fko_cli_options_t *opts,
unsigned int exit_status);
#define MAX_CMDLINE_ARGS 50 /* should be way more than enough */
int
main(int argc, char **argv)
{
@@ -640,6 +642,7 @@ show_last_command(void)
args_save_file);
exit(EXIT_FAILURE);
}
verify_file_perms_ownership(args_save_file);
if ((fgets(args_str, MAX_LINE_LEN, args_file_ptr)) != NULL) {
printf("Last fwknop client command line: %s", args_str);
} else {
@@ -665,7 +668,7 @@ run_last_args(fko_cli_options_t *options)
char args_save_file[MAX_PATH_LEN] = {0};
char args_str[MAX_LINE_LEN] = {0};
char arg_tmp[MAX_LINE_LEN] = {0};
char *argv_new[200]; /* should be way more than enough */
char *argv_new[MAX_CMDLINE_ARGS]; /* should be way more than enough */
#ifdef WIN32
@@ -677,6 +680,8 @@ run_last_args(fko_cli_options_t *options)
if (get_save_file(args_save_file))
{
verify_file_perms_ownership(args_save_file);
if ((args_file_ptr = fopen(args_save_file, "r")) == NULL)
{
fprintf(stderr, "Could not open args file: %s\n",
@@ -701,12 +706,17 @@ run_last_args(fko_cli_options_t *options)
argv_new[argc_new] = malloc(strlen(arg_tmp)+1);
if (argv_new[argc_new] == NULL)
{
fprintf(stderr, "malloc failure for cmd line arg.\n");
fprintf(stderr, "[*] malloc failure for cmd line arg.\n");
exit(EXIT_FAILURE);
}
strlcpy(argv_new[argc_new], arg_tmp, strlen(arg_tmp)+1);
current_arg_ctr = 0;
argc_new++;
if(argc_new >= MAX_CMDLINE_ARGS)
{
fprintf(stderr, "[*] max command line args exceeded.\n");
exit(EXIT_FAILURE);
}
}
}
}
@@ -739,7 +749,6 @@ save_args(int argc, char **argv)
return;
#endif
if (get_save_file(args_save_file)) {
if ((args_file_ptr = fopen(args_save_file, "w")) == NULL) {
fprintf(stderr, "Could not open args file: %s\n",
@@ -758,6 +767,9 @@ save_args(int argc, char **argv)
fprintf(args_file_ptr, "%s\n", args_str);
fclose(args_file_ptr);
}
set_file_perms(args_save_file);
return;
}
+3 -3
View File
@@ -46,9 +46,9 @@
struct url
{
char port[6];
char host[256];
char path[1024];
char port[MAX_PORT_STR_LEN];
char host[MAX_URL_HOST_LEN+1];
char path[MAX_URL_PATH_LEN+1];
};
static int
+1 -1
View File
@@ -147,7 +147,7 @@ send_spa_packet_tcp_or_udp(const char *spa_data, const int sd_len,
hints.ai_protocol = IPPROTO_TCP;
}
sprintf(port_str, "%d", options->spa_dst_port);
snprintf(port_str, MAX_PORT_STR_LEN, "%d", options->spa_dst_port);
error = getaddrinfo(options->spa_server_str, port_str, &hints, &result);
+65
View File
@@ -89,4 +89,69 @@ is_base64(const unsigned char *buf, const unsigned short int len)
return rv;
}
int
set_file_perms(const char *file)
{
int res = 0;
res = chmod(file, S_IRUSR | S_IWUSR);
if(res != 0)
{
fprintf(stderr,
"[-] unable to chmod file %s to user read/write (0600, -rw-------): %s\n",
file,
strerror(errno)
);
}
return res;
}
int
verify_file_perms_ownership(const char *file)
{
#if HAVE_STAT
struct stat st;
/* Every file that the fwknop client deals with should be owned
* by the user and permissions set to 600 (user read/write)
*/
if((stat(file, &st)) != 0)
{
fprintf(stderr, "[-] unable to run stat() against file: %s: %s\n",
file, strerror(errno));
exit(EXIT_FAILURE);
}
/* Make sure it is a regular file or symbolic link
*/
if(S_ISREG(st.st_mode) != 1 && S_ISLNK(st.st_mode) != 1)
{
fprintf(stderr,
"[-] file: %s is not a regular file or symbolic link.\n",
file
);
return 0;
}
if((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != (S_IRUSR|S_IWUSR))
{
fprintf(stderr,
"[-] file: %s permissions should only be user read/write (0600, -rw-------)\n",
file
);
return 0;
}
if(st.st_uid != getuid())
{
fprintf(stderr, "[-] file: %s not owned by current effective user id.\n",
file);
return 0;
}
#endif
return 1;
}
/***EOF***/
+13
View File
@@ -31,11 +31,24 @@
#ifndef UTILS_H
#define UTILS_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#if HAVE_CONFIG_H
#include "config.h"
#endif
/* Prototypes
*/
void hex_dump(const unsigned char *data, const int size);
int is_base64(const unsigned char *buf, const unsigned short int len);
int set_file_perms(const char *file);
int verify_file_perms_ownership(const char *file);
size_t strlcat(char *dst, const char *src, size_t siz);
size_t strlcpy(char *dst, const char *src, size_t siz);
-2
View File
@@ -102,8 +102,6 @@ enum {
#define DEFAULT_NAT_PORT 55000
#define MIN_HIGH_PORT 10000 /* sensible minimum for SPA dest port */
#define MAX_PORT 65535
#define MAX_PORT_STR_LEN 6
#define MAX_PROTO_STR_LEN 6
#define MAX_SERVER_STR_LEN 50
#define MAX_LINE_LEN 1024
+1 -1
View File
@@ -256,7 +256,7 @@ AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_FUNC_STAT
AC_CHECK_FUNCS([bzero gettimeofday memmove memset socket strchr strcspn strdup strncasecmp strndup strrchr strspn])
AC_CHECK_FUNCS([bzero gettimeofday memmove memset socket strchr strcspn strdup strncasecmp strndup strrchr strspn strnlen stat chmod chown])
AC_SEARCH_LIBS([socket], [socket])
AC_SEARCH_LIBS([inet_addr], [nsl])
+27 -12
View File
@@ -200,6 +200,8 @@ validate_access_msg(const char *msg)
do {
ndx++;
res = validate_proto_port_spec(ndx);
if(res != FKO_SUCCESS)
break;
} while((ndx = strchr(ndx, ',')));
return(res);
@@ -208,14 +210,13 @@ validate_access_msg(const char *msg)
int
validate_proto_port_spec(const char *msg)
{
int startlen = strnlen(msg, MAX_SPA_MESSAGE_SIZE);
int startlen = strnlen(msg, MAX_SPA_MESSAGE_SIZE), port_str_len = 0;
const char *ndx = msg;
if(startlen == MAX_SPA_MESSAGE_SIZE)
return(FKO_ERROR_INVALID_DATA);
/* Now check for proto/port string. Currenly we only allow protos
* 'tcp', 'udp', and 'icmp'.
/* Now check for proto/port string.
*/
if(strncmp(ndx, "tcp", 3)
&& strncmp(ndx, "udp", 3)
@@ -224,19 +225,25 @@ validate_proto_port_spec(const char *msg)
return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
ndx = strchr(ndx, '/');
if(ndx == NULL || (1+(ndx - msg)) >= startlen)
if(ndx == NULL || ((1+(ndx - msg)) > MAX_PROTO_STR_LEN))
return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
/* Skip over the ',' and make sure we only have digits.
/* Skip over the '/' and make sure we only have digits.
*/
ndx++;
while(*ndx != '\0')
/* Must have at least one digit for the port number
*/
if(isdigit(*ndx) == 0)
return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
while(*ndx != '\0' && *ndx != ',')
{
if(isdigit(*ndx) == 0)
port_str_len++;
if((isdigit(*ndx) == 0) || (port_str_len > MAX_PORT_STR_LEN))
return(FKO_ERROR_INVALID_SPA_ACCESS_MSG);
ndx++;
}
return(FKO_SUCCESS);
}
@@ -259,23 +266,31 @@ int
got_allow_ip(const char *msg)
{
const char *ndx = msg;
int dot_cnt = 0;
int dot_ctr = 0, char_ctr = 0;
int res = FKO_SUCCESS;
while(*ndx != ',' && *ndx != '\0')
{
char_ctr++;
if(char_ctr >= MAX_IPV4_STR_LEN)
{
res = FKO_ERROR_INVALID_ALLOW_IP;
break;
}
if(*ndx == '.')
dot_cnt++;
dot_ctr++;
else if(isdigit(*ndx) == 0)
{
res = FKO_ERROR_INVALID_ALLOW_IP;
break;
}
ndx++;
}
if(dot_cnt != 3)
if (char_ctr < MIN_IPV4_STR_LEN)
res = FKO_ERROR_INVALID_ALLOW_IP;
if(dot_ctr != 3)
res = FKO_ERROR_INVALID_ALLOW_IP;
return(res);
+3
View File
@@ -32,6 +32,9 @@
#ifndef FKO_MESSAGE_H
#define FKO_MESSAGE_H 1
#define MAX_PROTO_STR_LEN 5 /* tcp, udp, icmp for now */
#define MAX_PORT_STR_LEN 6
/* SPA message format validation functions.
*/
int validate_cmd_msg(const char *msg);
+96 -22
View File
@@ -213,7 +213,7 @@ add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *v
/* Take an IP or Subnet/Mask and convert it to mask for later
* comparisons of incoming source IPs against this mask.
*/
static void
static int
add_source_mask(fko_srv_options_t *opts, acc_stanza_t *acc, const char *ip)
{
char *ndx;
@@ -278,7 +278,11 @@ add_source_mask(fko_srv_options_t *opts, acc_stanza_t *acc, const char *ip)
log_msg(LOG_ERR,
"Fatal error parsing IP to int for: %s", ip_str
);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
free(new_sle);
new_sle = NULL;
return 0;
}
/* Store our mask converted from CIDR to a 32-bit value.
@@ -290,15 +294,17 @@ add_source_mask(fko_srv_options_t *opts, acc_stanza_t *acc, const char *ip)
*/
new_sle->maddr = ntohl(in.s_addr) & new_sle->mask;
}
return 1;
}
/* Expand the access SOURCE string to a list of masks.
*/
void
static int
expand_acc_source(fko_srv_options_t *opts, acc_stanza_t *acc)
{
char *ndx, *start;
char buf[32];
char buf[ACCESS_BUF_LEN];
int res = 1;
start = acc->source;
@@ -311,8 +317,15 @@ expand_acc_source(fko_srv_options_t *opts, acc_stanza_t *acc)
while(isspace(*start))
start++;
if(((ndx-start)+1) >= ACCESS_BUF_LEN)
return 0;
strlcpy(buf, start, (ndx-start)+1);
add_source_mask(opts, acc, buf);
res = add_source_mask(opts, acc, buf);
if(res == 0)
return res;
start = ndx+1;
}
}
@@ -322,15 +335,21 @@ expand_acc_source(fko_srv_options_t *opts, acc_stanza_t *acc)
while(isspace(*start))
start++;
if(((ndx-start)+1) >= ACCESS_BUF_LEN)
return 0;
strlcpy(buf, start, (ndx-start)+1);
add_source_mask(opts, acc, buf);
res = add_source_mask(opts, acc, buf);
return res;
}
static int
parse_proto_and_port(char *pstr, int *proto, int *port)
{
char *ndx;
char proto_str[32];
char proto_str[ACCESS_BUF_LEN];
/* Parse the string into its components.
*/
@@ -342,10 +361,24 @@ parse_proto_and_port(char *pstr, int *proto, int *port)
return(-1);
}
strlcpy(proto_str, pstr, (ndx - pstr)+1);
if(((ndx - pstr)+1) >= ACCESS_BUF_LEN)
{
log_msg(LOG_ERR,
"Parse error on access port entry: %s", pstr);
return(-1);
}
strlcpy(proto_str, pstr, (ndx - pstr)+1);
*port = atoi(ndx+1);
if((*port < 0) || (*port > MAX_PORT))
{
log_msg(LOG_ERR,
"Invalid port in access request: %s", pstr);
return(-1);
}
if(strcasecmp(proto_str, "tcp") == 0)
*proto = PROTO_TCP;
else if(strcasecmp(proto_str, "udp") == 0)
@@ -354,7 +387,6 @@ parse_proto_and_port(char *pstr, int *proto, int *port)
{
log_msg(LOG_ERR,
"Invalid protocol in access port entry: %s", pstr);
return(-1);
}
@@ -457,15 +489,15 @@ add_string_list_ent(acc_string_list_t **stlist, const char *str_str)
/* Expand a proto/port access string to a list of access proto-port struct.
*/
void
int
expand_acc_port_list(acc_port_list_t **plist, char *plist_str)
{
char *ndx, *start;
char buf[32];
char buf[ACCESS_BUF_LEN];
start = plist_str;
for(ndx = start; *ndx; ndx++)
for(ndx = start; *ndx != '\0'; ndx++)
{
if(*ndx == ',')
{
@@ -474,6 +506,9 @@ expand_acc_port_list(acc_port_list_t **plist, char *plist_str)
while(isspace(*start))
start++;
if(((ndx-start)+1) >= ACCESS_BUF_LEN)
return 0;
strlcpy(buf, start, (ndx-start)+1);
add_port_list_ent(plist, buf);
start = ndx+1;
@@ -485,9 +520,14 @@ expand_acc_port_list(acc_port_list_t **plist, char *plist_str)
while(isspace(*start))
start++;
if(((ndx-start)+1) >= ACCESS_BUF_LEN)
return 0;
strlcpy(buf, start, (ndx-start)+1);
add_port_list_ent(plist, buf);
return 1;
}
/* Expand a comma-separated string into a simple acc_string_list.
@@ -652,7 +692,11 @@ expand_acc_ent_lists(fko_srv_options_t *opts)
{
/* Expand the source string to 32-bit integer masks foreach entry.
*/
expand_acc_source(opts, acc);
if(expand_acc_source(opts, acc) == 0)
{
acc = acc->next;
continue;
}
/* Now expand the open_ports string.
*/
@@ -782,8 +826,9 @@ set_acc_defaults(fko_srv_options_t *opts)
static int
acc_data_is_valid(const acc_stanza_t *acc)
{
if(acc->key_len < 0 || ((acc->key == NULL && acc->key_base64 == NULL)
&& (acc->gpg_decrypt_pw == NULL || !strlen(acc->gpg_decrypt_pw))))
if(((acc->key == NULL || !strlen(acc->key))
&& (acc->gpg_decrypt_pw == NULL || !strlen(acc->gpg_decrypt_pw)))
|| (acc->use_rijndael == 0 && acc->use_gpg == 0 && acc->gpg_allow_no_pw == 0))
{
fprintf(stderr,
"[*] No keys found for access stanza source: '%s'\n", acc->source
@@ -824,6 +869,8 @@ parse_access_file(fko_srv_options_t *opts)
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
verify_file_perms_ownership(opts->config[CONF_ACCESS_FILE]);
if ((file_ptr = fopen(opts->config[CONF_ACCESS_FILE], "r")) == NULL)
{
fprintf(stderr, "[*] Could not open access file: %s\n",
@@ -934,6 +981,7 @@ parse_access_file(fko_srv_options_t *opts)
}
add_acc_string(&(curr_acc->key), val);
curr_acc->key_len = strlen(curr_acc->key);
add_acc_bool(&(curr_acc->use_rijndael), "Y");
}
else if(CONF_VAR_IS(var, "KEY_BASE64"))
{
@@ -954,6 +1002,7 @@ parse_access_file(fko_srv_options_t *opts)
add_acc_string(&(curr_acc->key_base64), val);
add_acc_b64_string(&(curr_acc->key),
&(curr_acc->key_len), curr_acc->key_base64);
add_acc_bool(&(curr_acc->use_rijndael), "Y");
}
else if(CONF_VAR_IS(var, "HMAC_KEY_BASE64"))
{
@@ -1049,13 +1098,18 @@ parse_access_file(fko_srv_options_t *opts)
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
add_acc_string(&(curr_acc->gpg_decrypt_pw), val);
add_acc_bool(&(curr_acc->use_gpg), "Y");
}
else if(CONF_VAR_IS(var, "GPG_ALLOW_NO_PW"))
{
if(curr_acc->gpg_decrypt_pw != NULL && curr_acc->gpg_decrypt_pw[0] != '\0')
free(curr_acc->gpg_decrypt_pw);
add_acc_string(&(curr_acc->gpg_decrypt_pw), "");
add_acc_bool(&(curr_acc->gpg_allow_no_pw), val);
if(curr_acc->gpg_allow_no_pw == 1)
{
add_acc_bool(&(curr_acc->use_gpg), "Y");
if(curr_acc->gpg_decrypt_pw != NULL && curr_acc->gpg_decrypt_pw[0] != '\0')
free(curr_acc->gpg_decrypt_pw);
add_acc_string(&(curr_acc->gpg_decrypt_pw), "");
}
}
else if(CONF_VAR_IS(var, "GPG_REQUIRE_SIG"))
{
@@ -1199,9 +1253,9 @@ compare_port_list(acc_port_list_t *in, acc_port_list_t *ac, const int match_any)
int
acc_check_port_access(acc_stanza_t *acc, char *port_str)
{
int res = 1;
int res = 1, ctr = 0;
char buf[32];
char buf[ACCESS_BUF_LEN];
char *ndx, *start;
acc_port_list_t *o_pl = acc->oport_list;
@@ -1214,14 +1268,34 @@ acc_check_port_access(acc_stanza_t *acc, char *port_str)
/* Create our own internal port_list from the incoming SPA data
* for comparison.
*/
for(ndx = start; *ndx; ndx++)
for(ndx = start; *ndx != '\0'; ndx++)
{
if(*ndx == ',')
{
if((ctr >= ACCESS_BUF_LEN)
|| (((ndx-start)+1) >= ACCESS_BUF_LEN))
{
log_msg(LOG_ERR,
"Unable to create acc_port_list from incoming data: %s",
port_str
);
return(0);
}
strlcpy(buf, start, (ndx-start)+1);
add_port_list_ent(&in_pl, buf);
start = ndx+1;
ctr = 0;
}
ctr++;
}
if((ctr >= ACCESS_BUF_LEN)
|| (((ndx-start)+1) >= ACCESS_BUF_LEN))
{
log_msg(LOG_ERR,
"Unable to create acc_port_list from incoming data: %s",
port_str
);
return(0);
}
strlcpy(buf, start, (ndx-start)+1);
add_port_list_ent(&in_pl, buf);
+3 -1
View File
@@ -34,6 +34,8 @@
#define PROTO_TCP 6
#define PROTO_UDP 17
#define ACCESS_BUF_LEN 32
/* Function Prototypes
*/
void parse_access_file(fko_srv_options_t *opts);
@@ -41,7 +43,7 @@ int compare_addr_list(acc_int_list_t *source_list, const uint32_t ip);
int acc_check_port_access(acc_stanza_t *acc, char *port_str);
int acc_check_gpg_remote_id(acc_stanza_t *acc, const char *gpg_id);
void dump_access_list(const fko_srv_options_t *opts);
void expand_acc_port_list(acc_port_list_t **plist, char *plist_str);
int expand_acc_port_list(acc_port_list_t **plist, char *plist_str);
void free_acc_stanzas(fko_srv_options_t *opts);
void free_acc_port_list(acc_port_list_t *plist);
+2
View File
@@ -200,6 +200,8 @@ parse_config_file(fko_srv_options_t *opts, const char *config_file)
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
verify_file_perms_ownership(config_file);
if ((cfile_ptr = fopen(config_file, "r")) == NULL)
{
fprintf(stderr, "[*] Could not open config file: %s\n",
+2 -1
View File
@@ -582,7 +582,8 @@ process_spa_request(const fko_srv_options_t *opts, const acc_stanza_t *acc, spa_
/* Parse and expand our access message.
*/
expand_acc_port_list(&port_list, spadat->spa_message_remain);
if(expand_acc_port_list(&port_list, spadat->spa_message_remain) != 1)
return res;
/* Start at the top of the proto-port list...
*/
+16 -2
View File
@@ -496,10 +496,22 @@ make_dir_path(const char *run_dir)
if(stat(tmp_path, &st) != 0)
{
if(errno == ENOENT)
{
res = mkdir(tmp_path, S_IRWXU);
if(res != 0)
return res;
if(res != 0)
return res;
/* run stat() against the component since we just
* created it
*/
if(stat(tmp_path, &st) != 0)
{
log_msg(LOG_ERR,
"Could not create component: %s of %s\n\n", tmp_path, run_dir
);
return(ENOTDIR);
}
}
}
if(! S_ISDIR(st.st_mode))
@@ -664,10 +676,12 @@ get_running_pid(const fko_srv_options_t *opts)
char buf[PID_BUFLEN] = {0};
pid_t rpid = 0;
op_fd = open(opts->config[CONF_FWKNOP_PID_FILE], O_RDONLY);
if(op_fd > 0)
{
verify_file_perms_ownership(opts->config[CONF_FWKNOP_PID_FILE]);
if (read(op_fd, buf, PID_BUFLEN) > 0)
{
buf[PID_BUFLEN-1] = '\0';
+3
View File
@@ -278,6 +278,7 @@ typedef struct acc_stanza
char *hmac_key;
int hmac_key_len;
char *hmac_key_base64;
unsigned char use_rijndael;
int fw_access_timeout;
unsigned char enable_cmd_exec;
char *cmd_exec_user;
@@ -289,6 +290,8 @@ typedef struct acc_stanza
char *gpg_decrypt_pw;
unsigned char gpg_require_sig;
unsigned char gpg_ignore_sig_error;
unsigned char use_gpg;
unsigned char gpg_allow_no_pw;
char *gpg_remote_id;
acc_string_list_t *gpg_remote_id_list;
time_t access_expire_time;
+5 -13
View File
@@ -363,7 +363,7 @@ incoming_spa(fko_srv_options_t *opts)
*/
enc_type = fko_encryption_type((char *)spa_pkt->packet_data);
if(enc_type == FKO_ENCRYPTION_RIJNDAEL)
if(acc->use_rijndael && enc_type == FKO_ENCRYPTION_RIJNDAEL)
{
if (acc->key == NULL)
{
@@ -379,12 +379,12 @@ incoming_spa(fko_srv_options_t *opts)
acc->key, acc->key_len, acc->encryption_mode, acc->hmac_key,
acc->hmac_key_len);
}
else if(enc_type == FKO_ENCRYPTION_GPG)
else if(acc->use_gpg && enc_type == FKO_ENCRYPTION_GPG)
{
/* For GPG we create the new context without decrypting on the fly
* so we can set some GPG parameters first.
*/
if(acc->gpg_decrypt_pw != NULL)
if(acc->gpg_decrypt_pw != NULL || acc->gpg_allow_no_pw)
{
res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, NULL,
0, acc->encryption_mode, NULL, 0);
@@ -443,19 +443,11 @@ incoming_spa(fko_srv_options_t *opts)
res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw, 0);
}
else
{
log_msg(LOG_ERR,
"(stanza #%d) No GPG_DECRYPT_PW for GPG encrypted messages, set GPG_ALLOW_NO_PW",
stanza_num
);
acc = acc->next;
continue;
}
}
else
{
log_msg(LOG_ERR, "(stanza #%d) Unable to determing encryption type. Got type=%i.",
log_msg(LOG_ERR,
"(stanza #%d) No stanza encryption mode match for encryption type: %i.",
stanza_num, enc_type);
acc = acc->next;
continue;
+5 -1
View File
@@ -261,10 +261,14 @@ replay_file_cache_init(fko_srv_options_t *opts)
fprintf(digest_file_ptr,
"# <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>\n");
fclose(digest_file_ptr);
set_file_perms(opts->config[CONF_DIGEST_FILE]);
return(0);
}
/* File exist, and we have access - create in-memory digest cache
verify_file_perms_ownership(opts->config[CONF_DIGEST_FILE]);
/* File exists, and we have access - create in-memory digest cache
*/
if ((digest_file_ptr = fopen(opts->config[CONF_DIGEST_FILE], "r")) == NULL)
{
+70 -2
View File
@@ -147,19 +147,87 @@ dump_ctx(fko_ctx_t ctx)
int
is_valid_dir(const char *path)
{
struct stat st;
#if HAVE_STAT
struct stat st;
/* If we are unable to stat the given dir, then return with error.
*/
if(stat(path, &st) != 0)
return(0);
{
fprintf(stderr, "[-] unable to stat() directory: %s: %s\n",
path, strerror(errno));
exit(EXIT_FAILURE);
}
if(!S_ISDIR(st.st_mode))
return(0);
#endif /* HAVE_STAT */
return(1);
}
int
set_file_perms(const char *file)
{
int res = 0;
res = chmod(file, S_IRUSR | S_IWUSR);
if(res != 0)
{
fprintf(stderr, "[-] unable to chmod file %s to user read/write: %s\n",
file, strerror(errno));
}
return res;
}
int
verify_file_perms_ownership(const char *file)
{
#if HAVE_STAT
struct stat st;
/* Every file that the fwknop client deals with should be owned
* by the user and permissions set to 600 (user read/write)
*/
if((stat(file, &st)) != 0)
{
fprintf(stderr, "[-] unable to stat() file: %s: %s\n",
file, strerror(errno));
exit(EXIT_FAILURE);
}
/* Make sure it is a regular file
*/
if(S_ISREG(st.st_mode) != 1 && S_ISLNK(st.st_mode) != 1)
{
fprintf(stderr,
"[-] file: %s is not a regular file or symbolic link.\n",
file
);
return 0;
}
if((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != (S_IRUSR|S_IWUSR))
{
fprintf(stderr,
"[-] file: %s permissions should only be user read/write (0600, -rw-------)\n",
file
);
return 0;
}
if(st.st_uid != getuid())
{
fprintf(stderr, "[-] file: %s not owned by current effective user id\n",
file);
return 0;
}
#endif
return 1;
}
/* Determine if a buffer contains only characters from the base64
* encoding set
*/
+2
View File
@@ -62,6 +62,8 @@ void hex_dump(const unsigned char *data, const int size);
char* dump_ctx(fko_ctx_t ctx);
int is_base64(const unsigned char *buf, const unsigned short int len);
int is_valid_dir(const char *path);
int set_file_perms(const char *file);
int verify_file_perms_ownership(const char *file);
size_t strlcat(char *dst, const char *src, size_t siz);
size_t strlcpy(char *dst, const char *src, size_t siz);
+5
View File
@@ -0,0 +1,5 @@
#
# The default fwknopd.conf contains only comments since defaults are defined in
# code and modified via the config file
#
ENABLE_SPA_PACKET_AGING N;
+419 -3
View File
@@ -61,6 +61,7 @@ my %cf = (
'rc_file_invalid_b64_key' => "$conf_dir/fwknoprc_invalid_base64_key",
'rc_file_hmac_b64_key' => "$conf_dir/fwknoprc_default_hmac_base64_key",
'base64_key_access' => "$conf_dir/base64_key_access.conf",
'disable_aging' => "$conf_dir/disable_aging_fwknopd.conf",
);
my $default_digest_file = "$run_dir/digest.cache";
@@ -562,7 +563,7 @@ my @tests = (
{
'category' => 'basic operations',
'subcategory' => 'client',
'detail' => '-A <proto>/<port> specification',
'detail' => '-A <proto>/<port> specification (proto)',
'err_msg' => 'permitted invalid -A <proto>/<port>',
'function' => \&generic_exec,
'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
@@ -571,6 +572,19 @@ my @tests = (
"$fwknopCmd -A invalid/22 -a $fake_ip -D $loopback_ip",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'client',
'detail' => '-A <proto>/<port> specification (port)',
'err_msg' => 'permitted invalid -A <proto>/<port>',
'function' => \&generic_exec,
'positive_output_matches' => [qr/Invalid\sSPA\saccess\smessage/i],
'exec_err' => $YES,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/600001 -a $fake_ip -D $loopback_ip",
'fatal' => $NO
},
{
'category' => 'basic operations',
'subcategory' => 'client',
@@ -689,6 +703,20 @@ my @tests = (
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'permissions check cycle (tcp/22)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&permissions_check,
'cmdline' => $default_client_args,
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'server_positive_output_matches' => [qr/permissions\sshould\sonly\sbe\suser/],
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
@@ -1464,6 +1492,52 @@ my @tests = (
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (tcp/60001)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/60001 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'multi port (tcp/60001,udp/60001)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/60001,udp/60001 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
'detail' => 'multi port (tcp/22,udp/53,tcp/1234)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/22,udp/53,tcp/1234 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose",
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd $default_server_conf_args $intf_str",
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
@@ -1549,6 +1623,227 @@ my @tests = (
'replay_positive_output_matches' => [qr/Data\sis\snot\sa\svalid\sSPA\smessage\sformat/],
'fatal' => $NO
},
### fuzzing tests
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'overly long port value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A \
# "tcp/`perl -e '{print "1"x"40"}'`" -a 127.0.0.2 -D 127.0.0.1 \
# --get-key local_spa.key --verbose --verbose
#
# This problem was found by Fernando Arnaboldi of IOActive and exploits
# a buffer overflow in the fwknopd servers prior to 2.0.3 from
# authenticated clients.
#
'fuzzing_pkt' =>
'+JzxeTGlc6lwwzbJSrYChKx8bonWBIPajwGfEtGOaoglcMLbTY/GGXo/nxqiN1LykFS' .
'lDFXgrkyx2emJ7NGzYqQPUYZxLdZRocR9aRIptvXLLIPBcIpJASi/TUiJlw7CDFMcj0' .
'ptSBJJUZi0tozpKHETp3AgqfzyOy5FNs38aZsV5/sDl3Pt+kF7fTZJ+YLbmYY4yCUz2' .
'ZUYoCaJ7X78ULyJTi5eT7nug',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'overly long proto value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A \
# "tcp`perl -e '{print "A"x"28"}'`/1" -a 127.0.0.2 -D 127.0.0.1 \
# --get-key local_spa.key --verbose --verbose
#
# This problem was found by Fernando Arnaboldi of IOActive and exploits
# a buffer overflow in the fwknopd servers prior to 2.0.3 from
# authenticated clients.
#
'fuzzing_pkt' =>
'/im5MiJQmOdzqrdWXv+AjEtAm/HsLrdaTFcSw3ZskqpGOdDIrSCz3VXbFfv7qDkc5Y4' .
'q/k1mRXl9SGzpug87U5dZSyCdAr30z7/2kUFEPTGOQBi/x+L1t1pvdkm4xg13t09ldm' .
'5OD8KiV6qzqLOvN4ULJjvvJJWBZ9qvo/f2Q9Wf67g2KHiwS6EeCINAuMoUw/mNRQMa4' .
'oGnOXu3/DeWHJAwtSeh7EAr4',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'overly long IP value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A tcp/22 \
# -a `perl -e '{print "1"x"136"}'`.0.0.1 -D 127.0.0.1 \
# --get-key local_spa.key --verbose --verbose
#
# This problem was found by Fernando Arnaboldi of IOActive and exploits
# a condition in which pre-2.0.3 fwknopd servers fail to properly validate
# allow IP addresses from malicious authenticated clients.
#
'fuzzing_pkt' =>
'93f2rhsXLmBoPicWvYTqrbp+6lNqvWDc8dzmX2s3settwjBGRAXm33TB9agibEphrBu' .
'3d+7DEsivZLDS6Kz0JwdjX7t0J9c8es+DVNjlLnPtVNcxhs+2kUzimNrgysIXQRJ+GF' .
'GbhdxiXCqdy1vWxWpdoaZmY/CeGIkpoFJFPbJhCRLLX25UMvMF2wXj02MpI4d3t1/6W' .
'DM3taM3kZsiFv6HxFjAhIEuQ1oAg2OgRGXkDmT3jDNZMHUm0d4Ahm9LonG7RbOxq/B0' .
'qUvY8lkymbwvjelVok7Lvlc06cRhN4zm32D4V05g0vQS3PlX9C+mgph9DeAPVX+D8iZ' .
'8lGrxcPSfbCOW61k0MP+q1EhLZkc1qAm5g2+2cLNZcoBNEdh3yj8OTPZJyBVw',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'negative port value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A \
# tcp/-33 -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
# --verbose --verbose
#
'fuzzing_pkt' =>
'/weoc+pEuQknZo8ImWTQBB+/PwSJ2/TcrmFoSkxpRXX4+jlUxoJakHrioxh8rhLmAD9' .
'8E4lMnq+EbM2XYdhs2alpZ5bovAFojMsYRWwr/BvRO4Um4Fmo9z9sY3DR477TXNYXBR' .
'iGXWxSL4u+AWSSePK3qiiYoRQVw',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'null port value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A tcp/ \
# -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
# --verbose --verbose
#
'fuzzing_pkt' =>
'94nu7hvq6V/3A27GzjHwfPnPCQfs44ySlraIFYHOAqy5YqjkrBS67nH35tX55N1BrYZ' .
'07zvcT03keUhLE1Uo7Wme1nE7BfTOG5stmIK1UQI85sL52//lDHu+xCqNcL7GUKbVRz' .
'ekw+EUscVvUkrsRcVtSvOm+fCNo',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'long FKO protocol value (enc mode trigger)',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A tcp/22 \
# -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key --verbose --verbose
#
# This problem was found by Fernando Arnaboldi of IOActive and is designed
# to have fwknopd look for a mode decryption mode for a long Rijndael-
# encrypted SPA packet
#
'fuzzing_pkt' =>
'/ewH/k1XsDX+VQ8NlNvCZ4P2QOl/4IpJYXkq4TtAe3899OtApXJiTtPCuYW70XPuxge' .
'MtFjc4UfslK/r9v+FYfyd3fIIHCz0Q0M4+nM3agTLmJj8nOxk6ZeBj82SDQWhHAxGdJ' .
'IQALPve0ug4cuGxS3b4M+2Q/Av9i2tU3Lzlogw3sY0tk6wGf4zZk4UsviVXYpINniGT' .
'RhYSIQ1dfdkng7hKiHMDaObYY1GFp4nxEt/QjasAwvE+7/iFyoKN+IRpGG4v4hGEPh2' .
'vTDqmvfRuIHtgFD7NxZjt+m/jjcu0gkdWEoD4fenwGU35FlvchyM2AiAEw7yRzSABfn' .
'R9d3sYZGMtyASw2O1vSluwIxUUnDop3gxEIhJEj8h+01pA3K+klSpALeY9EZgHqYC7E' .
'ETuPS6dZ3764nWohtCY67JvNUX7TtNDNc2qrhrapdRP17+PT2Vh4s9m38V3WwVWC3uH' .
'X/klLZcHIt+aRDV+uekw9GOKSgwFL2ekPpr3gXxigc3zrxel5hcsqLOpVUa4CP/0HkG' .
'F0NPQvOT3ZvpeIJnirKP1ZX9gDFinqhuzL7oqktW61e1iwe7KZEdrZV0k2KZwyb8qU5' .
'rPAEnw',
'server_positive_output_matches' => [qr/No\sstanza\sencryption\smode\smatch/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'long FKO protocol value (Rijndael trigger)',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A tcp/22 \
# -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key --verbose --verbose
#
# This problem was found by Fernando Arnaboldi of IOActive and is designed
# to have fwknopd look for a mode decryption mode for a long Rijndael-
# encrypted SPA packet
#
'fuzzing_pkt' =>
'+YQNu4BFgiNeu8HeiBiNKriqCFSseALt9vJaKzkzK/OF4pjkJcvhGEOi7fEVXqn3VIdlGR' .
'DmBul2I7H3z18U9E97bWGgT9NexKgEPCuekL18ZEPf5xR3JleNsNWatqYgAOkgN8ZWE69Q' .
'qQUYYhxTvJHS6R+5JqFKB3A44hMXoICdYNkn9MAktHxk3PbbpQ+nA+jESwVCra2doAiLiM' .
'ucvGIZZiTv0Mc1blFYIE2zqZ/C7ct1V+ukwSkUv0r87eA7uJhmlpThRsL0dN6iekJ6i87B' .
'tE8QyuOXzOMftI11SUn/LwqD4RMdR21rvLrzR6ZB5eUX2UBpODyzX6n+PJJkTWCuFVT4z1' .
'MKY',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'FUZZING',
'detail' => 'null proto value',
'err_msg' => 'server crashed or did not detect error condition',
'function' => \&fuzzer,
### this packet was generated with a modified fwknop client via the
### following command line:
#
# LD_LIBRARY_PATH=../lib/.libs ../client/.libs/fwknop -A /22 \
# -a 127.0.0.2 -D 127.0.0.1 --get-key local_spa.key \
# --verbose --verbose
#
'fuzzing_pkt' =>
'/JT14qxh9P4iy+CuUZahThaQjoEuL2zd46a+jL6sTrBZJSa6faUX4dH5fte/4ZJv+9f' .
'd/diWYKAUvdQ4DydPGlR7mwQa2W+obKpqrsTBz7D4054z6ATAOGpCtifakEVl1XRc2+' .
'hW04WpY8mdUNu9i+PrfPr7/KxqU',
'server_positive_output_matches' => [qr/Args\scontain\sinvalid\sdata/],
'fwknopd_cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopdCmd -c $cf{'disable_aging'} -a $cf{'def_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fatal' => $NO
},
{
'category' => 'Rijndael SPA',
'subcategory' => 'client+server',
@@ -1694,6 +1989,24 @@ my @tests = (
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (tcp/60001)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/60001 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose " .
"--gpg-recipient-key $gpg_server_key " .
"--gpg-signer-key $gpg_client_key " .
"--gpg-home-dir $gpg_client_home_dir_no_pw",
'fwknopd_cmdline' => $default_server_gpg_args_no_pw,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GPG (no pw) SPA',
'subcategory' => 'client+server',
@@ -1883,6 +2196,24 @@ my @tests = (
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
'detail' => 'complete cycle (tcp/60001)',
'err_msg' => 'could not complete SPA cycle',
'function' => \&spa_cycle,
'cmdline' => "LD_LIBRARY_PATH=$lib_dir $valgrind_str " .
"$fwknopCmd -A tcp/60001 -a $fake_ip -D $loopback_ip --get-key " .
"$local_key_file --verbose --verbose " .
"--gpg-recipient-key $gpg_server_key " .
"--gpg-signer-key $gpg_client_key " .
"--gpg-home-dir $gpg_client_home_dir",
'fwknopd_cmdline' => $default_server_gpg_args,
'fw_rule_created' => $NEW_RULE_REQUIRED,
'fw_rule_removed' => $NEW_RULE_REMOVED,
'fatal' => $NO
},
{
'category' => 'GnuPG (GPG) SPA',
'subcategory' => 'client+server',
@@ -2398,6 +2729,26 @@ sub client_send_spa_packet() {
return 1;
}
sub permissions_check() {
my $test_hr = shift;
my $rv = 0;
chmod 0777, $cf{'def'} or die $!;
chmod 0777, $cf{'def_access'} or die $!;
$rv = &spa_cycle($test_hr);
chmod 0600, $cf{'def'} or die $!;
chmod 0600, $cf{'def_access'} or die $!;
if ($test_hr->{'server_positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'server_positive_output_matches'},
$MATCH_ALL, $server_test_file);
}
return $rv;
}
sub spa_cycle() {
my $test_hr = shift;
@@ -2636,6 +2987,44 @@ sub altered_non_base64_spa_data() {
return $rv;
}
sub fuzzer() {
my $test_hr = shift;
my $rv = 1;
my $server_was_stopped = 0;
my $fw_rule_created = 0;
my $fw_rule_removed = 0;
my @packets = (
{
'proto' => 'udp',
'port' => $default_spa_port,
'dst_ip' => $loopback_ip,
'data' => $test_hr->{'fuzzing_pkt'},
},
);
($rv, $server_was_stopped, $fw_rule_created, $fw_rule_removed)
= &client_server_interaction($test_hr, \@packets, $USE_PREDEF_PKTS);
$rv = 0 unless $server_was_stopped;
if ($fw_rule_created) {
&write_test_file("[-] new fw rule created.\n", $curr_test_file);
$rv = 0;
} else {
&write_test_file("[+] new fw rule not created.\n", $curr_test_file);
}
if ($test_hr->{'server_positive_output_matches'}) {
$rv = 0 unless &file_find_regex(
$test_hr->{'server_positive_output_matches'},
$MATCH_ALL, $server_test_file);
}
return $rv;
}
sub altered_base64_spa_data() {
my $test_hr = shift;
@@ -2989,7 +3378,7 @@ sub server_ignore_small_packets() {
}
sub client_server_interaction() {
my ($test_hr, $pkts_hr, $spa_client_flag, $fw_rules_flag) = @_;
my ($test_hr, $pkts_hr, $spa_client_flag) = @_;
my $rv = 1;
my $server_was_stopped = 1;
@@ -3417,7 +3806,26 @@ sub run_cmd() {
close F;
}
my $rv = ((system "$cmd > $cmd_out 2>&1") >> 8);
### copy original file descriptors (credit: Perl Cookbook)
open OLDOUT, ">&STDOUT";
open OLDERR, ">&STDERR";
### redirect command output
open STDOUT, "> $cmd_out" or die "[*] Could not redirect stdout: $!";
open STDERR, ">&STDOUT" or die "[*] Could not dup stdout: $!";
my $rv = ((system $cmd) >> 8);
close STDOUT or die "[*] Could not close STDOUT: $!";
close STDERR or die "[*] Could not close STDERR: $!";
### restore original filehandles
open STDERR, ">&OLDERR" or die "[*] Could not restore stderr: $!";
open STDOUT, ">&OLDOUT" or die "[*] Could not restore stdout: $!";
### close the old copies
close OLDOUT or die "[*] Could not close OLDOUT: $!";
close OLDERR or die "[*] Could not close OLDERR: $!";
open C, "< $cmd_out" or die "[*] Could not open $cmd_out: $!";
my @cmd_lines = <C>;
@@ -3476,6 +3884,7 @@ sub init() {
for my $name (keys %cf) {
die "[*] $cf{$name} does not exist" unless -e $cf{$name};
chmod 0600, $cf{$name} or die "[*] Could not chmod 0600 $cf{$name}";
}
if (-d $output_dir) {
@@ -3491,8 +3900,15 @@ sub init() {
copy $logfile, "${output_dir}.last/$logfile" or die $!;
}
$saved_last_results = 1;
} else {
mkdir $output_dir or die "[*] Could not mkdir $output_dir: $!";
}
if (-d $run_dir) {
rmtree $run_dir or die $!;
}
mkdir $run_dir or die "[*] Could not mkdir $run_dir: $!";
for my $dir ($output_dir, $run_dir) {
next if -d $dir;
mkdir $dir or die "[*] Could not mkdir $dir: $!";
+17
View File
@@ -50,3 +50,20 @@
** [test suite] backwards compatibility tests
The test suite should have the ability to test backwards compatibility
between fwknop versions.
** For Linux/Unix - a GNOME or KDE GUI app for the fwknop client.
Although there is currently a functioning web proxy that can serve as a
UI via a browser, it would be nice to have native GNOME and KDE GUI
wrappers for the fwknop client.
** For Windows - VB and/or C# class wrappers around libfko.dll
Extend Windows support with VB and/or C# class wrappers around the
libfko.dll
** Ruby bindings to libfko
Perl and Python bindings already exist for libfko, so add Ruby to this list
as well.
** [client] Update to not send SPA packet if Ctrl-C is used
The client currently sends an SPA packet when an encryption key is
requested but the user tries to exit out with Ctrl-C.
** Add --disable-gpg arg to the autoconf configure script
There needs to be a way to easily disable libgpgme usage even if it is
installed - this could be done with a new --disablegpg argument to the
configure script.