[server] start on command open/close cycle support (issue #117)

This commit is contained in:
Michael Rash 2015-10-13 18:00:07 -07:00
parent cac6a3f726
commit d67fbde992
13 changed files with 352 additions and 29 deletions

View File

@ -1,3 +1,6 @@
fwknop-2.6.8 (11//2015):
- [server] open/close functionality...
fwknop-2.6.7 (08/24/2015):
- [server] When command execution is enabled with ENABLE_CMD_EXEC for an
access.conf stanza, added support for running commands via sudo. This was

View File

@ -190,6 +190,7 @@ EXTRA_DIST = \
test/conf/hmac_cmd_access.conf \
test/conf/hmac_cmd_setuid_access.conf \
test/conf/hmac_cmd_giduid_access.conf \
test/conf/hmac_cmd_open_close_cycle_access.conf \
test/conf/hmac_get_key_access.conf \
test/conf/hmac_no_b64_access.conf \
test/conf/hmac_equal_keys_access.conf \

View File

@ -12,7 +12,7 @@ BASE_SOURCE_FILES = fwknopd.h config_init.c config_init.h \
fw_util_iptables.c fw_util_iptables.h \
fw_util_ipfw.c fw_util_ipfw.h \
fw_util_pf.c fw_util_pf.h cmd_opts.h \
extcmd.c extcmd.h
extcmd.c extcmd.h cmd_cycle.c cmd_cycle.h
fwknopd_SOURCES = fwknopd.c $(BASE_SOURCE_FILES)

View File

@ -898,6 +898,12 @@ free_acc_stanza_data(acc_stanza_t *acc)
if(acc->require_username != NULL)
free(acc->require_username);
if(acc->cmd_cycle_open != NULL)
free(acc->cmd_cycle_open);
if(acc->cmd_cycle_close != NULL)
free(acc->cmd_cycle_close);
if(acc->gpg_home_dir != NULL)
free(acc->gpg_home_dir);
@ -1551,6 +1557,22 @@ parse_access_file(fko_srv_options_t *opts)
add_acc_group(&(curr_acc->cmd_exec_group),
&(curr_acc->cmd_exec_gid), val,
"CMD_EXEC_GROUP", file_ptr, opts);
else if(CONF_VAR_IS(var, "CMD_CYCLE_OPEN"))
add_acc_string(&(curr_acc->cmd_cycle_open), val, file_ptr, opts);
else if(CONF_VAR_IS(var, "CMD_CYCLE_CLOSE"))
add_acc_string(&(curr_acc->cmd_cycle_close), val, file_ptr, opts);
else if(CONF_VAR_IS(var, "CMD_CYCLE_TIMER"))
{
curr_acc->cmd_cycle_timer = strtol_wrapper(val, 0,
RCHK_MAX_CMD_CYCLE_TIMER, NO_EXIT_UPON_ERR, &is_err);
if(is_err != FKO_SUCCESS)
{
log_msg(LOG_ERR,
"[*] CMD_CYCLE_TIMER value not in range.");
fclose(file_ptr);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
}
else if(CONF_VAR_IS(var, "REQUIRE_USERNAME"))
add_acc_string(&(curr_acc->require_username), val, file_ptr, opts);
else if(CONF_VAR_IS(var, "REQUIRE_SOURCE_ADDRESS"))
@ -1923,6 +1945,9 @@ dump_access_list(const fko_srv_options_t *opts)
" CMD_SUDO_EXEC_GROUP: %s\n"
" CMD_EXEC_USER: %s\n"
" CMD_EXEC_GROUP: %s\n"
" CMD_CYCLE_OPEN: %s\n"
" CMD_CYCLE_CLOSE: %s\n"
" CMD_CYCLE_TIMER: %i\n"
" REQUIRE_USERNAME: %s\n"
" REQUIRE_SOURCE_ADDRESS: %s\n"
" FORCE_NAT (ip): %s\n"
@ -1960,6 +1985,9 @@ dump_access_list(const fko_srv_options_t *opts)
(acc->cmd_sudo_exec_group == NULL) ? "<not set>" : acc->cmd_sudo_exec_group,
(acc->cmd_exec_user == NULL) ? "<not set>" : acc->cmd_exec_user,
(acc->cmd_exec_group == NULL) ? "<not set>" : acc->cmd_exec_group,
(acc->cmd_cycle_open == NULL) ? "<not set>" : acc->cmd_cycle_open,
(acc->cmd_cycle_close == NULL) ? "<not set>" : acc->cmd_cycle_close,
acc->cmd_cycle_timer,
(acc->require_username == NULL) ? "<not set>" : acc->require_username,
acc->require_source_address ? "Yes" : "No",
acc->force_nat ? acc->force_nat_ip : "<not set>",

189
server/cmd_cycle.c Normal file
View File

@ -0,0 +1,189 @@
/*
* @file cmd_cycle.c
*
* @brief Fwknop routines for managing command cycles as defined via
* access.conf stanzas (CMD_CYCLE_OPEN and CMD_CYCLE_CLOSE).
*
* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
* Copyright (C) 2009-2014 fwknop developers and contributors. For a full
* list of contributors, see the file 'CREDITS'.
*
* License (GNU General Public License):
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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 "fwknopd_common.h"
#include "log_msg.h"
#include "extcmd.h"
#include "cmd_cycle.h"
static char cmd_buf[CMD_CYCLE_BUFSIZE];
static char err_buf[CMD_CYCLE_BUFSIZE];
static void
zero_cmd_buffers(void)
{
memset(cmd_buf, 0x0, CMD_CYCLE_BUFSIZE);
memset(err_buf, 0x0, CMD_CYCLE_BUFSIZE);
return;
}
static int pid_status = 0;
static int
do_var_subs(const char * const str)
{
int i;
/* Look for a '$' char
*/
if (str != NULL && str[0] != 0x0)
{
for (i=0; i<strlen(str); i++)
{
if(str[i] == '$')
return 1;
i++;
}
}
return 0;
}
int
cmd_cycle_open(fko_srv_options_t *opts, acc_stanza_t *acc,
spa_data_t *spadat, const int stanza_num, int *res)
{
cmd_cycle_list_t *last_clist=NULL, *new_clist=NULL, *tmp_clist=NULL;
time_t now;
char *open_cmd=NULL, *close_cmd=NULL;
if(opts->test)
{
log_msg(LOG_WARNING,
"[%s] (stanza #%d) --test mode enabled, skipping CMD_CYCLE_OPEN execution.",
spadat->pkt_source_ip, stanza_num
);
return 0;
}
log_msg(LOG_INFO, "[%s] (stanza #%d) CMD_CYCLE_OPEN: %s",
spadat->pkt_source_ip, stanza_num, acc->cmd_cycle_open);
log_msg(LOG_INFO, "[%s] (stanza #%d) expected CMD_CYCLE_CLOSE: %s",
spadat->pkt_source_ip, stanza_num, acc->cmd_cycle_close);
/* Add the corresponding close command - to be executed after the
* designated timer has expired.
*/
if((new_clist = calloc(1, sizeof(cmd_cycle_list_t))) == NULL)
{
log_msg(LOG_ERR,
"[*] Fatal memory allocation error creating string list entry"
);
return 0; // FIXME: handle this
}
if(opts->cmd_cycle_list == NULL)
{
opts->cmd_cycle_list = new_clist;
}
else
{
tmp_clist = new_clist;
do {
last_clist = tmp_clist;
} while((tmp_clist = tmp_clist->next));
last_clist->next = new_clist;
}
/* Set the source IP, expiration timer, and close command
*/
strlcpy(new_clist->src_ip, spadat->pkt_source_ip,
sizeof(new_clist->src_ip));
time(&now);
new_clist->expire = now + acc->cmd_cycle_timer;
/* Now, execute the open command
*/
if(do_var_subs(acc->cmd_cycle_open))
{
}
else
{
/* Run the open command as-is
*/
open_cmd = acc->cmd_cycle_open;
}
zero_cmd_buffers();
snprintf(cmd_buf, CMD_CYCLE_BUFSIZE-1, "%s", open_cmd);
run_extcmd(cmd_buf, err_buf, CMD_CYCLE_BUFSIZE,
WANT_STDERR, NO_TIMEOUT, &pid_status, opts);
return FKO_SUCCESS;
}
void
cmd_cycle_close(fko_srv_options_t *opts)
{
cmd_cycle_list_t *tmp_clist = NULL;
time_t now;
time(&now);
log_msg(LOG_INFO, "cmd_cycle_close() function");
if(opts->cmd_cycle_list == NULL)
{
return; /* No active command cycles */
}
else
{
tmp_clist = opts->cmd_cycle_list;
do {
log_msg(LOG_INFO, "close command for src IP: %s", tmp_clist->src_ip);
if(tmp_clist->expire <= now)
{
// FIXME: must remove this element from the list
log_msg(LOG_INFO, "EXPIRED!", tmp_clist->src_ip);
}
} while((tmp_clist = tmp_clist->next));
}
return;
}
void
free_cmd_cycle_list(fko_srv_options_t *opts)
{
cmd_cycle_list_t *tmp_clist=NULL, *clist=NULL;
clist = opts->cmd_cycle_list;
while(clist != NULL)
{
tmp_clist = clist->next;
free(clist);
clist = tmp_clist;
}
return;
}

41
server/cmd_cycle.h Normal file
View File

@ -0,0 +1,41 @@
/**
*
* @file cmd_cycle.h
*
* @brief: Function prototypes for managing command cycles as defined via
* access.conf stanzas (CMD_CYCLE_OPEN and CMD_CYCLE_CLOSE).
*
* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
* Copyright (C) 2009-2014 fwknop developers and contributors. For a full
* list of contributors, see the file 'CREDITS'.
*
* License (GNU General Public License):
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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 CMD_CYCLE_H
#define CMD_CYCLE_H
#define CMD_CYCLE_BUFSIZE 256
int cmd_cycle_open(fko_srv_options_t *opts, acc_stanza_t *acc,
spa_data_t *spadat, const int stanza_num, int *res);
void cmd_cycle_close(fko_srv_options_t *opts);
void free_cmd_cycle_list(fko_srv_options_t *opts);
#endif /* CMD_CYCLE_H */

View File

@ -130,7 +130,8 @@
#define RCHK_MAX_UDPSERV_PORT ((2 << 16) - 1)
#define RCHK_MAX_UDPSERV_SELECT_TIMEOUT (2 << 22)
#define RCHK_MAX_PCAP_DISPATCH_COUNT (2 << 22)
#define RCHK_MAX_FW_TIMEOUT (2 << 22)
#define RCHK_MAX_FW_TIMEOUT (2 << 22) /* seconds */
#define RCHK_MAX_CMD_CYCLE_TIMER (2 << 22) /* seconds */
#define RCHK_MAX_RULES_CHECK_THRESHOLD ((2 << 16) - 1)
/* FirewallD-specific defines
@ -384,6 +385,9 @@ typedef struct acc_stanza
gid_t cmd_sudo_exec_gid;
char *cmd_exec_user;
char *cmd_exec_group;
char *cmd_cycle_open;
char *cmd_cycle_close;
int cmd_cycle_timer;
uid_t cmd_exec_uid;
gid_t cmd_exec_gid;
char *require_username;
@ -420,6 +424,15 @@ typedef struct acc_stanza
struct acc_stanza *next;
} acc_stanza_t;
/* A simple linked list of strings for command open/close cycles
*/
typedef struct cmd_cycle_list
{
char src_ip[MAX_IPV4_STR_LEN];
char *close_cmd;
time_t expire;
struct cmd_cycle_list *next;
} cmd_cycle_list_t;
/* Firewall-related data and types. */
@ -661,6 +674,11 @@ typedef struct fko_srv_options
*/
unsigned int check_rules_ctr;
/* Track external command execution cycles (track source IP, access.conf
* stanza number, and instantiation time).
*/
cmd_cycle_list_t *cmd_cycle_list;
/* Set to 1 when messages have to go through syslog, 0 otherwise */
unsigned char syslog_enable;

View File

@ -38,6 +38,7 @@
#include "incoming_spa.h"
#include "access.h"
#include "extcmd.h"
#include "cmd_cycle.h"
#include "log_msg.h"
#include "utils.h"
#include "fw_util.h"
@ -1119,7 +1120,17 @@ incoming_spa(fko_srv_options_t *opts)
/* Command messages.
*/
if(spadat.message_type == FKO_COMMAND_MSG)
if(acc->cmd_cycle_open != NULL)
{
if(cmd_cycle_open(opts, acc, &spadat, stanza_num, &res))
break; /* successfully processed a matching access stanza */
else
{
acc = acc->next;
continue;
}
}
else if(spadat.message_type == FKO_COMMAND_MSG)
{
if(process_cmd_msg(opts, acc, &spadat, stanza_num, &res))
{

View File

@ -38,6 +38,7 @@
#include "pcap_capture.h"
#include "process_packet.h"
#include "fw_util.h"
#include "cmd_cycle.h"
#include "log_msg.h"
#include "fwknopd_errors.h"
#include "sig_handler.h"
@ -331,10 +332,10 @@ pcap_capture(fko_srv_options_t *opts)
else
pcap_errcnt = 0;
/* Check for any expired firewall rules and deal with them.
*/
if(!opts->test)
{
/* Check for any expired firewall rules and deal with them.
*/
if(rules_chk_threshold > 0)
{
opts->check_rules_ctr++;
@ -346,6 +347,10 @@ pcap_capture(fko_srv_options_t *opts)
}
check_firewall_rules(opts, chk_rm_all);
chk_rm_all = 0;
/* See if any CMD_CYCLE_CLOSE commands need to be executed.
*/
cmd_cycle_close(opts);
}
#if FIREWALL_IPFW

View File

@ -377,6 +377,7 @@ clean_exit(fko_srv_options_t *opts, unsigned int fw_cleanup_flag, unsigned int e
#endif
free_logging();
free_cmd_cycle_list(opts);
free_configs(opts);
exit(exit_status);
}

View File

@ -0,0 +1,8 @@
SOURCE ANY
KEY_BASE64 wzNP62oPPgEc+kXDPQLHPOayQBuNbYUTPP+QrErNDmg=
HMAC_KEY_BASE64 Yh+xizBnl6FotC5ec7FanVGClRMlsOAPh2u6eovnerfBVKwaVKzjGoblFMHMc593TNyi0dWn4opLoTIV9q/ttg==
FW_ACCESS_TIMEOUT 3
#CMD_CYCLE_OPEN /some/cmd/to/execute -with -args test test
CMD_CYCLE_OPEN /usr/bin/touch /tmp/cmdcycle.test
CMD_CYCLE_CLOSE /some/closecmd/to/execute -test
CMD_CYCLE_TIMER 2

View File

@ -119,6 +119,7 @@ my @test_files = (
"$tests_dir/rijndael.pl",
"$tests_dir/rijndael_cmd_exec.pl",
"$tests_dir/rijndael_hmac_cmd_exec.pl",
"$tests_dir/rijndael_hmac_cmd_open_close.pl",
"$tests_dir/rijndael_replay_attacks.pl",
"$tests_dir/rijndael_fuzzing.pl",
"$tests_dir/rijndael_backwards_compatibility.pl",
@ -137,30 +138,31 @@ my @test_files = (
);
#================== end config ===================
our @build_security_client = (); ### imported from tests/build_security.pl
our @build_security_server = ();
our @build_security_libfko = ();
our @preliminaries = (); ### from tests/preliminaries.pl
our @code_structure_errstr = (); ### from tests/code_structure.pl (may include Coccinelle matches eventually)
our @configure_args = (); ### from tests/configure_args.pl
our @basic_operations = (); ### from tests/basic_operations.pl
our @rijndael = (); ### from tests/rijndael.pl
our @rijndael_cmd_exec = (); ### from tests/rijndael_cmd_exec.pl
our @rijndael_hmac_cmd_exec = (); ### from tests/rijndael_hmac_cmd_exec.pl
our @rijndael_replay_attacks = (); ### from tests/rijndael_replay_attacks.pl
our @rijndael_hmac = (); ### from tests/rijndael_hmac.pl
our @rijndael_fuzzing = (); ### from tests/rijndael_fuzzing.pl
our @rijndael_hmac_fuzzing = (); ### from tests/rijndael_hmac_fuzzing.pl
our @fault_injection = (); ### from tests/fault_injection.pl
our @afl_fuzzing = (); ### from tests/alf_fuzzing.pl
our @address_sanitizer = (); ### from tests/address_sanitizer.pl
our @gpg_no_pw = (); ### from tests/gpg_now_pw.pl
our @gpg_no_pw_hmac = (); ### from tests/gpg_now_pw_hmac.pl
our @gpg = (); ### from tests/gpg.pl
our @gpg_hmac = (); ### from tests/gpg_hmac.pl
our @perl_FKO_module = (); ### from tests/perl_FKO_module.pl
our @python_fko = (); ### from tests/python_fko.pl
our @os_compatibility = (); ### from tests/os_compatibility.pl
our @build_security_client = (); ### imported from tests/build_security.pl
our @build_security_server = ();
our @build_security_libfko = ();
our @preliminaries = (); ### from tests/preliminaries.pl
our @code_structure_errstr = (); ### from tests/code_structure.pl (may include Coccinelle matches eventually)
our @configure_args = (); ### from tests/configure_args.pl
our @basic_operations = (); ### from tests/basic_operations.pl
our @rijndael = (); ### from tests/rijndael.pl
our @rijndael_cmd_exec = (); ### from tests/rijndael_cmd_exec.pl
our @rijndael_hmac_cmd_exec = (); ### from tests/rijndael_hmac_cmd_exec.pl
our @rijndael_hmac_cmd_open_close = (); ### from tests/rijndael_hmac_cmd_open_close.pl
our @rijndael_replay_attacks = (); ### from tests/rijndael_replay_attacks.pl
our @rijndael_hmac = (); ### from tests/rijndael_hmac.pl
our @rijndael_fuzzing = (); ### from tests/rijndael_fuzzing.pl
our @rijndael_hmac_fuzzing = (); ### from tests/rijndael_hmac_fuzzing.pl
our @fault_injection = (); ### from tests/fault_injection.pl
our @afl_fuzzing = (); ### from tests/alf_fuzzing.pl
our @address_sanitizer = (); ### from tests/address_sanitizer.pl
our @gpg_no_pw = (); ### from tests/gpg_now_pw.pl
our @gpg_no_pw_hmac = (); ### from tests/gpg_now_pw_hmac.pl
our @gpg = (); ### from tests/gpg.pl
our @gpg_hmac = (); ### from tests/gpg_hmac.pl
our @perl_FKO_module = (); ### from tests/perl_FKO_module.pl
our @python_fko = (); ### from tests/python_fko.pl
our @os_compatibility = (); ### from tests/os_compatibility.pl
our @rijndael_backwards_compatibility = (); ### from tests/rijndael_backwards_compatibility.pl
my $passed = 0;
@ -427,6 +429,7 @@ our %cf = (
'hmac_simple_keys_access' => "$conf_dir/hmac_simple_keys_access.conf",
'hmac_invalid_type_access' => "$conf_dir/hmac_invalid_type_access.conf",
'hmac_cygwin_access' => "$conf_dir/hmac_no_b64_cygwin_access.conf",
'hmac_cmd_open_close_cycle_access' => "$conf_dir/hmac_cmd_open_close_cycle_access.conf",
'spa_destination' => "$conf_dir/destination_rule_fwknopd.conf",
"${fw_conf_prefix}_spa_dst_snat" => "$conf_dir/${fw_conf_prefix}_spa_dst_snat_fwknopd.conf",
'hmac_spa_destination_access' => "$conf_dir/hmac_spa_destination_access.conf",
@ -818,6 +821,7 @@ my @tests = (
@rijndael,
@rijndael_cmd_exec,
@rijndael_hmac_cmd_exec,
@rijndael_hmac_cmd_open_close,
@rijndael_replay_attacks,
@rijndael_backwards_compatibility,
@rijndael_fuzzing,

View File

@ -0,0 +1,14 @@
@rijndael_hmac_cmd_open_close = (
### command open/close cycle tests
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => 'cmd open/close cycle',
'function' => \&spa_cycle,
'cmdline' => $default_client_hmac_args,
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $cf{'hmac_cmd_open_close_cycle_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'key_file' => $cf{'rc_hmac_b64_key'},
},
);