[server] add sudo support, closes #159

This commit is contained in:
Michael Rash 2015-07-19 20:23:04 -07:00
parent 89b2e8f477
commit d681485e29
9 changed files with 448 additions and 60 deletions

View File

@ -1,7 +1,14 @@
fwknop-2.6.7 (05//2015): fwknop-2.6.7 (07//2015):
- [server] Added sudo support for command execution mode. This was
suggested by Github user 'freegigi' (issue #159) as a means to provide
command filtering using the powerful sudoers syntax. This feature is
implemented by prefixing any incoming command from a valid SPA packet
with the sudo command along with optional user and group requirements
as defined by the following new access.conf variables:
ENABLE_CMD_SUDO_EXEC, CMD_SUDO_EXEC_USER, and CMD_SUDO_EXEC_GROUP.
- [server] Kevin Layer reported a bug to the fwknop mailing list that - [server] Kevin Layer reported a bug to the fwknop mailing list that
simultaneous NAT access for two different access.conf stanza was not simultaneous NAT access for two different access.conf stanza was not
functioning properly. After some diagnosis, this is a result of functioning properly. After some diagnosis, this was a result of
rule_exists() not properly detecting and differentiating existing DNAT rule_exists() not properly detecting and differentiating existing DNAT
rules from new ones with different port numbers when 'iptables -C' rules from new ones with different port numbers when 'iptables -C'
support is not available. This was against iptables-1.4.7, and has been support is not available. This was against iptables-1.4.7, and has been

View File

@ -543,7 +543,7 @@ directive starts a new stanza.
the *fwknopd* server as the user specified by the ``CMD_EXEC_USER'' or the *fwknopd* server as the user specified by the ``CMD_EXEC_USER'' or
as the user that started *fwknopd* if that is not set. as the user that started *fwknopd* if that is not set.
*CMD_EXEC_ALL_SUDO* '<Y/N>':: *ENABLE_CMD_SUDO_EXEC* '<Y/N>'::
*sudo* provides a powerful means of restricting the sets of commands that *sudo* provides a powerful means of restricting the sets of commands that
users can execute via the ``sudoers'' file. By enabling this feature (and users can execute via the ``sudoers'' file. By enabling this feature (and
in ``ENABLE_CMD_EXEC'' mode), all incoming commands from valid SPA packets in ``ENABLE_CMD_EXEC'' mode), all incoming commands from valid SPA packets

View File

@ -76,6 +76,58 @@ add_acc_string(char **var, const char *val)
return SUCCESS; return SUCCESS;
} }
/* Add an access user entry
*/
static int
add_acc_user(char **user_var, uid_t *uid_var, struct passwd *upw,
const char *val, const char *var_name)
{
struct passwd *pw = NULL;
if(add_acc_string(user_var, val) != SUCCESS)
return FATAL_ERR;
errno = 0;
upw = pw = getpwnam(val);
if(pw == NULL)
{
log_msg(LOG_ERR, "[*] Unable to determine UID for %s: %s.",
var_name, errno ? strerror(errno) : "Not a user on this system");
return FATAL_ERR;
}
*uid_var = pw->pw_uid;
return SUCCESS;
}
/* Add an access group entry
*/
static int
add_acc_group(char **group_var, gid_t *gid_var,
const char *val, const char *var_name)
{
struct passwd *pw = NULL;
if(add_acc_string(group_var, val) != SUCCESS)
return FATAL_ERR;
errno = 0;
pw = getpwnam(val);
if(pw == NULL)
{
log_msg(LOG_ERR, "[*] Unable to determine UID for %s: %s.",
var_name, errno ? strerror(errno) : "Not a group on this system");
return FATAL_ERR;
}
*gid_var = pw->pw_gid;
return SUCCESS;
}
/* Decode base64 encoded string into access entry /* Decode base64 encoded string into access entry
*/ */
static int static int
@ -803,6 +855,12 @@ free_acc_stanza_data(acc_stanza_t *acc)
free(acc->hmac_key_base64); free(acc->hmac_key_base64);
} }
if(acc->cmd_sudo_exec_user != NULL)
free(acc->cmd_sudo_exec_user);
if(acc->cmd_sudo_exec_group != NULL)
free(acc->cmd_sudo_exec_group);
if(acc->cmd_exec_user != NULL) if(acc->cmd_exec_user != NULL)
free(acc->cmd_exec_user); free(acc->cmd_exec_user);
@ -1077,7 +1135,9 @@ set_acc_defaults(fko_srv_options_t *opts)
/* Perform some sanity checks on an acc stanza data. /* Perform some sanity checks on an acc stanza data.
*/ */
static int static int
acc_data_is_valid(fko_srv_options_t *opts, struct passwd *user_pw, acc_stanza_t * const acc) acc_data_is_valid(fko_srv_options_t *opts,
struct passwd *user_pw, struct passwd *sudo_user_pw,
acc_stanza_t * const acc)
{ {
if(acc == NULL) if(acc == NULL)
{ {
@ -1176,6 +1236,17 @@ acc_data_is_valid(fko_srv_options_t *opts, struct passwd *user_pw, acc_stanza_t
acc->cmd_exec_gid = user_pw->pw_gid; acc->cmd_exec_gid = user_pw->pw_gid;
} }
if(sudo_user_pw != NULL
&& acc->cmd_sudo_exec_uid != 0 && acc->cmd_sudo_exec_gid == 0)
{
log_msg(LOG_INFO,
"Setting gid to group associated with CMD_SUDO_EXEC_USER '%s' in stanza source: '%s'",
acc->cmd_exec_user,
acc->source
);
acc->cmd_sudo_exec_gid = sudo_user_pw->pw_gid;
}
return(1); return(1);
} }
@ -1193,8 +1264,8 @@ parse_access_file(fko_srv_options_t *opts)
char var[MAX_LINE_LEN] = {0}; char var[MAX_LINE_LEN] = {0};
char val[MAX_LINE_LEN] = {0}; char val[MAX_LINE_LEN] = {0};
struct passwd *pw = NULL;
struct passwd *user_pw = NULL; struct passwd *user_pw = NULL;
struct passwd *sudo_user_pw = NULL;
struct stat st; struct stat st;
acc_stanza_t *curr_acc = NULL; acc_stanza_t *curr_acc = NULL;
@ -1293,7 +1364,7 @@ parse_access_file(fko_srv_options_t *opts)
* stanza for the minimum required data. * stanza for the minimum required data.
*/ */
if(curr_acc != NULL) { if(curr_acc != NULL) {
if(!acc_data_is_valid(opts, user_pw, curr_acc)) if(!acc_data_is_valid(opts, user_pw, sudo_user_pw, curr_acc))
{ {
log_msg(LOG_ERR, "[*] Data error in access file: '%s'", log_msg(LOG_ERR, "[*] Data error in access file: '%s'",
opts->config[CONF_ACCESS_FILE]); opts->config[CONF_ACCESS_FILE]);
@ -1484,47 +1555,45 @@ parse_access_file(fko_srv_options_t *opts)
{ {
add_acc_bool(&(curr_acc->enable_cmd_sudo_exec), val); add_acc_bool(&(curr_acc->enable_cmd_sudo_exec), val);
} }
else if(CONF_VAR_IS(var, "CMD_SUDO_EXEC_USER"))
{
if(add_acc_user(&(curr_acc->cmd_sudo_exec_user),
&(curr_acc->cmd_sudo_exec_uid), sudo_user_pw,
val, "CMD_SUDO_EXEC_USER") != SUCCESS)
{
fclose(file_ptr);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
}
else if(CONF_VAR_IS(var, "CMD_SUDO_EXEC_GROUP"))
{
if(add_acc_group(&(curr_acc->cmd_sudo_exec_group),
&(curr_acc->cmd_sudo_exec_gid), val,
"CMD_SUDO_EXEC_GROUP") != SUCCESS)
{
fclose(file_ptr);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
}
else if(CONF_VAR_IS(var, "CMD_EXEC_USER")) else if(CONF_VAR_IS(var, "CMD_EXEC_USER"))
{ {
if(add_acc_string(&(curr_acc->cmd_exec_user), val) != SUCCESS) if(add_acc_user(&(curr_acc->cmd_exec_user),
&(curr_acc->cmd_exec_uid), user_pw,
val, "CMD_EXEC_USER") != SUCCESS)
{ {
fclose(file_ptr); fclose(file_ptr);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
} }
errno = 0;
user_pw = pw = getpwnam(val);
if(pw == NULL)
{
log_msg(LOG_ERR, "[*] Unable to determine UID for CMD_EXEC_USER: %s.",
errno ? strerror(errno) : "Not a user on this system");
fclose(file_ptr);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
curr_acc->cmd_exec_uid = pw->pw_uid;
} }
else if(CONF_VAR_IS(var, "CMD_EXEC_GROUP")) else if(CONF_VAR_IS(var, "CMD_EXEC_GROUP"))
{ {
if(add_acc_string(&(curr_acc->cmd_exec_group), val) != SUCCESS) if(add_acc_group(&(curr_acc->cmd_exec_group),
&(curr_acc->cmd_exec_gid), val,
"CMD_SUDO_EXEC_GROUP") != SUCCESS)
{ {
fclose(file_ptr); fclose(file_ptr);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE); clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
} }
errno = 0;
pw = getpwnam(val);
if(pw == NULL)
{
log_msg(LOG_ERR, "[*] Unable to determine GID for CMD_EXEC_GROUP: %s.",
errno ? strerror(errno) : "Not a group on this system");
fclose(file_ptr);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
curr_acc->cmd_exec_gid = pw->pw_gid;
} }
else if(CONF_VAR_IS(var, "REQUIRE_USERNAME")) else if(CONF_VAR_IS(var, "REQUIRE_USERNAME"))
{ {
@ -1759,7 +1828,7 @@ parse_access_file(fko_srv_options_t *opts)
/* Sanity check the last stanza /* Sanity check the last stanza
*/ */
if(!acc_data_is_valid(opts, user_pw, curr_acc)) if(!acc_data_is_valid(opts, user_pw, sudo_user_pw, curr_acc))
{ {
log_msg(LOG_ERR, log_msg(LOG_ERR,
"[*] Data error in access file: '%s'", "[*] Data error in access file: '%s'",
@ -1961,6 +2030,8 @@ dump_access_list(const fko_srv_options_t *opts)
" FW_ACCESS_TIMEOUT: %i\n" " FW_ACCESS_TIMEOUT: %i\n"
" ENABLE_CMD_EXEC: %s\n" " ENABLE_CMD_EXEC: %s\n"
" ENABLE_CMD_SUDO_EXEC: %s\n" " ENABLE_CMD_SUDO_EXEC: %s\n"
" CMD_SUDO_EXEC_USER: %s\n"
" CMD_SUDO_EXEC_GROUP: %s\n"
" CMD_EXEC_USER: %s\n" " CMD_EXEC_USER: %s\n"
" CMD_EXEC_GROUP: %s\n" " CMD_EXEC_GROUP: %s\n"
" REQUIRE_USERNAME: %s\n" " REQUIRE_USERNAME: %s\n"
@ -1996,6 +2067,8 @@ dump_access_list(const fko_srv_options_t *opts)
acc->fw_access_timeout, acc->fw_access_timeout,
acc->enable_cmd_exec ? "Yes" : "No", acc->enable_cmd_exec ? "Yes" : "No",
acc->enable_cmd_sudo_exec ? "Yes" : "No", acc->enable_cmd_sudo_exec ? "Yes" : "No",
(acc->cmd_sudo_exec_user == NULL) ? "<not set>" : acc->cmd_sudo_exec_user,
(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_user == NULL) ? "<not set>" : acc->cmd_exec_user,
(acc->cmd_exec_group == NULL) ? "<not set>" : acc->cmd_exec_group, (acc->cmd_exec_group == NULL) ? "<not set>" : acc->cmd_exec_group,
(acc->require_username == NULL) ? "<not set>" : acc->require_username, (acc->require_username == NULL) ? "<not set>" : acc->require_username,

View File

@ -1322,6 +1322,19 @@ config_init(fko_srv_options_t *opts, int argc, char **argv)
case 'S': case 'S':
opts->status = 1; opts->status = 1;
break; break;
case SUDO_EXE_PATH:
if (is_valid_exe(optarg))
{
set_config_entry(opts, CONF_SUDO_EXE, optarg);
}
else
{
log_msg(LOG_ERR,
"[*] gpg path '%s' could not stat()/does not exist?",
optarg);
clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
}
break;
case 't': case 't':
opts->test = 1; opts->test = 1;
break; break;

View File

@ -111,7 +111,7 @@ copy_or_search(char *so_read_buf, char *so_buf, const size_t so_buf_sz,
} }
/* Run an external command returning exit status, and optionally filling /* Run an external command returning exit status, and optionally filling
* provided buffer with STDOUT output up to the size provided. * provided buffer with STDOUT output up to the size provided.
* *
* Note: XXX: We are not using the timeout parameter at present. We still need * Note: XXX: We are not using the timeout parameter at present. We still need
* to implement a reliable timeout mechanism. * to implement a reliable timeout mechanism.
@ -136,6 +136,8 @@ _run_extcmd(uid_t uid, gid_t gid, const char *cmd, char *so_buf,
#endif #endif
#if AFL_FUZZING #if AFL_FUZZING
/* Don't allow command execution in AFL fuzzing mode
*/
return 0; return 0;
#endif #endif

View File

@ -378,6 +378,10 @@ typedef struct acc_stanza
int fw_access_timeout; int fw_access_timeout;
unsigned char enable_cmd_exec; unsigned char enable_cmd_exec;
unsigned char enable_cmd_sudo_exec; unsigned char enable_cmd_sudo_exec;
char *cmd_sudo_exec_user;
char *cmd_sudo_exec_group;
uid_t cmd_sudo_exec_uid;
gid_t cmd_sudo_exec_gid;
char *cmd_exec_user; char *cmd_exec_user;
char *cmd_exec_group; char *cmd_exec_group;
uid_t cmd_exec_uid; uid_t cmd_exec_uid;

View File

@ -304,6 +304,7 @@ incoming_spa(fko_srv_options_t *opts)
int is_err, cmd_exec_success = 0, attempted_decrypt = 0; int is_err, cmd_exec_success = 0, attempted_decrypt = 0;
int conf_pkt_age = 0; int conf_pkt_age = 0;
char dump_buf[CTX_DUMP_BUFSIZE]; char dump_buf[CTX_DUMP_BUFSIZE];
char cmd_buf[MAX_SPA_CMD_LEN] = {0};
spa_pkt_info_t *spa_pkt = &(opts->spa_pkt); spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
@ -881,24 +882,54 @@ incoming_spa(fko_srv_options_t *opts)
spadat.pkt_source_ip, stanza_num, spadat.spa_message_remain spadat.pkt_source_ip, stanza_num, spadat.spa_message_remain
); );
/* Do we need to become another user? If so, we call memset(cmd_buf, 0x0, sizeof(cmd_buf));
* run_extcmd_as and pass the cmd_exec_uid. if(acc->enable_cmd_sudo_exec)
*/ {
if(acc->cmd_exec_user != NULL && strncasecmp(acc->cmd_exec_user, "root", 4) != 0) /* Run the command via sudo - this allows sudo filtering
* to apply to the incoming command
*/
strlcpy(cmd_buf, opts->config[CONF_SUDO_EXE],
sizeof(cmd_buf));
if(acc->cmd_sudo_exec_user != NULL
&& strncasecmp(acc->cmd_sudo_exec_user, "root", 4) != 0)
{
strlcat(cmd_buf, " -u ", sizeof(cmd_buf));
strlcat(cmd_buf, acc->cmd_sudo_exec_user, sizeof(cmd_buf));
}
if(acc->cmd_exec_group != NULL
&& strncasecmp(acc->cmd_sudo_exec_group, "root", 4) != 0)
{
strlcat(cmd_buf, " -g ", sizeof(cmd_buf));
strlcat(cmd_buf,
acc->cmd_sudo_exec_group, sizeof(cmd_buf));
}
strlcat(cmd_buf, " ", sizeof(cmd_buf));
strlcat(cmd_buf, spadat.spa_message_remain, sizeof(cmd_buf));
}
else
strlcpy(cmd_buf, spadat.spa_message_remain, sizeof(cmd_buf));
if(acc->cmd_exec_user != NULL
&& strncasecmp(acc->cmd_exec_user, "root", 4) != 0)
{ {
log_msg(LOG_INFO, log_msg(LOG_INFO,
"[%s] (stanza #%d) setuid/setgid user/group to %s/%s (UID=%i,GID=%i) before running command.", "[%s] (stanza #%d) Running command '%s' setuid/setgid user/group to %s/%s (UID=%i,GID=%i)",
spadat.pkt_source_ip, stanza_num, acc->cmd_exec_user, spadat.pkt_source_ip, stanza_num, cmd_buf, acc->cmd_exec_user,
acc->cmd_exec_group == NULL ? acc->cmd_exec_user : acc->cmd_exec_group, acc->cmd_exec_group == NULL ? acc->cmd_exec_user : acc->cmd_exec_group,
acc->cmd_exec_uid, acc->cmd_exec_gid); acc->cmd_exec_uid, acc->cmd_exec_gid);
res = run_extcmd_as(acc->cmd_exec_uid, acc->cmd_exec_gid, res = run_extcmd_as(acc->cmd_exec_uid, acc->cmd_exec_gid,
spadat.spa_message_remain, NULL, 0, cmd_buf, NULL, 0, WANT_STDERR, NO_TIMEOUT,
WANT_STDERR, NO_TIMEOUT, &pid_status, opts); &pid_status, opts);
} }
else /* Just run it as we are (root that is). */ else /* Just run it as we are (root that is). */
res = run_extcmd(spadat.spa_message_remain, NULL, 0, {
WANT_STDERR, 5, &pid_status, opts); log_msg(LOG_INFO,
"[%s] (stanza #%d) Running command '%s'",
spadat.pkt_source_ip, stanza_num, cmd_buf);
res = run_extcmd(cmd_buf, NULL, 0, WANT_STDERR,
5, &pid_status, opts);
}
/* should only call WEXITSTATUS() if WIFEXITED() is true /* should only call WEXITSTATUS() if WIFEXITED() is true
*/ */

View File

@ -201,6 +201,9 @@ my $fuzzing_test_tag = '';
my $fuzzing_class = 'bogus data'; my $fuzzing_class = 'bogus data';
my %fuzzing_spa_packets = (); my %fuzzing_spa_packets = ();
my $total_fuzzing_pkts = 0; my $total_fuzzing_pkts = 0;
our $sudo_access_conf = "$run_dir/sudo_access.conf";
my $sudo_conf = '/etc/sudoers';
my $sudo_conf_testing = '';
my $server_test_file = ''; my $server_test_file = '';
my $client_only_mode = 0; my $client_only_mode = 0;
my $server_only_mode = 0; my $server_only_mode = 0;
@ -267,6 +270,7 @@ our $valgrind_path = '';
our $fiu_run_path = ''; our $fiu_run_path = '';
our $sudo_path = ''; our $sudo_path = '';
our $gcov_path = ''; our $gcov_path = '';
my $touch_path = '';
my $lcov_path = ''; my $lcov_path = '';
my $coverage_diff_path = 'coverage_diff.py'; my $coverage_diff_path = 'coverage_diff.py';
my $genhtml_path = ''; my $genhtml_path = '';
@ -875,6 +879,11 @@ my %test_keys = (
'server_exec_err' => $OPTIONAL, 'server_exec_err' => $OPTIONAL,
'fw_rule_created' => $OPTIONAL, 'fw_rule_created' => $OPTIONAL,
'fw_rule_removed' => $OPTIONAL, 'fw_rule_removed' => $OPTIONAL,
'sudo_test' => $OPTIONAL,
'sudo_conf' => $OPTIONAL,
'sudo_exec_user' => $OPTIONAL,
'sudo_exec_group' => $OPTIONAL,
'exec_user' => $OPTIONAL,
'server_conf' => $OPTIONAL, 'server_conf' => $OPTIONAL,
'client_only' => $OPTIONAL_NUMERIC, 'client_only' => $OPTIONAL_NUMERIC,
'server_only' => $OPTIONAL_NUMERIC, 'server_only' => $OPTIONAL_NUMERIC,
@ -902,6 +911,7 @@ my %test_keys = (
'server_conf_file' => $OPTIONAL, 'server_conf_file' => $OPTIONAL,
'digest_cache_file' => $OPTIONAL, 'digest_cache_file' => $OPTIONAL,
'cmd_exec_file_owner' => $OPTIONAL, 'cmd_exec_file_owner' => $OPTIONAL,
'cmd_exec_file_not_created' => $OPTIONAL,
'rm_rule_mid_cycle' => $OPTIONAL, 'rm_rule_mid_cycle' => $OPTIONAL,
'server_receive_re' => $OPTIONAL, 'server_receive_re' => $OPTIONAL,
'no_exit_intf_down' => $OPTIONAL, 'no_exit_intf_down' => $OPTIONAL,
@ -4672,28 +4682,103 @@ sub get_mod_paths() {
return \@paths; return \@paths;
} }
sub write_sudo_access_conf() {
my $test_hr = shift;
unlink $sudo_access_conf if -e $sudo_access_conf;
# mbr localhost = NOPASSWD: /usr/bin/cat, /usr/bin/touch
# mbr localhost = NOPASSWD: /usr/bin/cat, (root) /usr/bin/touch
# mbr localhost = NOPASSWD: /usr/bin/cat, (mbr : mbr) /usr/bin/touch
if ($test_hr->{'sudo_conf'}) {
open ST, "> $sudo_conf_testing" or die $!;
$test_hr->{'sudo_conf'} =~ s/USER/$username/g;
if ($test_hr->{'sudo_conf'} =~ /TOUCH/) {
if ($touch_path) {
$test_hr->{'sudo_conf'} =~ s/TOUCH/$touch_path/;
} else {
$test_hr->{'sudo_conf'} =~ s|TOUCH|/bin/touch|;
}
}
print ST $test_hr->{'sudo_conf'}, "\n";
close ST;
&write_test_file(
"[+] Setting $sudo_conf_testing file to:\n$test_hr->{'sudo_conf'}\n",
$curr_test_file);
}
copy $cf{'hmac_cmd_access'}, $sudo_access_conf or die $!;
open CA, ">> $sudo_access_conf" or die $!;
print CA "ENABLE_CMD_SUDO_EXEC Y\n";
if ($test_hr->{'exec_user'} eq $YES) {
print CA "CMD_EXEC_USER $username\n";
}
if ($test_hr->{'sudo_exec_user'} eq $YES) {
print CA "CMD_SUDO_EXEC_USER $username\n";
}
if ($test_hr->{'sudo_exec_group'} eq $YES) {
print CA "CMD_SUDO_EXEC_GROUP $username\n";
}
close CA;
return;
}
sub spa_cmd_exec_cycle() { sub spa_cmd_exec_cycle() {
my $test_hr = shift; my $test_hr = shift;
unlink $cmd_exec_test_file if -e $cmd_exec_test_file; unlink $cmd_exec_test_file if -e $cmd_exec_test_file;
if ($test_hr->{'sudo_test'} eq $YES) {
### we need to write the access.conf file based on sudo
### requirements
&write_sudo_access_conf($test_hr);
}
if (-e $cmd_exec_test_file) {
&write_test_file("[-] $cmd_exec_test_file file exists before SPA cycle.\n",
$curr_test_file);
} else {
&write_test_file("[+] $cmd_exec_test_file does not exist before SPA cycle.\n",
$curr_test_file);
}
my $rv = &spa_cycle($test_hr); my $rv = &spa_cycle($test_hr);
if (-e $cmd_exec_test_file) { if (-e $cmd_exec_test_file) {
&run_cmd("ls -l $cmd_exec_test_file", $cmd_out_tmp, $curr_test_file);
if ($test_hr->{'cmd_exec_file_owner'}) { if ($test_hr->{'cmd_exec_file_owner'}) {
$test_hr->{'cmd_exec_file_owner'} =~ s/USER/$username/;
my $user = (getpwuid((stat($cmd_exec_test_file))[4]))[0]; my $user = (getpwuid((stat($cmd_exec_test_file))[4]))[0];
if ($user and $user eq 'nobody') { if ($user and $user eq $test_hr->{'cmd_exec_file_owner'}) {
&write_test_file("[+] $cmd_exec_test_file is owned by user 'nobody'\n", &write_test_file("[+] $cmd_exec_test_file is owned by user: $user\n",
$curr_test_file);
$rv = 1;
} else {
&write_test_file("[+] $cmd_exec_test_file is not " .
"owned by user: $test_hr->{'cmd_exec_file_owner'}\n",
$curr_test_file); $curr_test_file);
&run_cmd("ls -l $cmd_exec_test_file", $cmd_out_tmp, $curr_test_file);
$rv = 1; $rv = 1;
} }
} }
if ($test_hr->{'cmd_exec_file_not_created'}) {
&write_test_file("[-] $cmd_exec_test_file file exists, setting rv=0.\n",
$curr_test_file);
$rv = 0;
}
unlink $cmd_exec_test_file; unlink $cmd_exec_test_file;
} else { } else {
&write_test_file("[-] $cmd_exec_test_file file does not exist, setting rv=0.\n", if ($test_hr->{'cmd_exec_file_not_created'}) {
$curr_test_file); &write_test_file("[+] $cmd_exec_test_file file does not exist.\n",
$rv = 0; $curr_test_file);
$rv = 1;
} else {
&write_test_file("[-] $cmd_exec_test_file file does not exist, setting rv=0.\n",
$curr_test_file);
$rv = 0;
}
} }
return $rv; return $rv;
@ -6936,17 +7021,32 @@ sub init() {
push @tests_to_exclude, qr/perl FKO module.*FUZZING/; push @tests_to_exclude, qr/perl FKO module.*FUZZING/;
} }
$sudo_path = &find_command('sudo') unless $sudo_path; $sudo_path = &find_command('sudo') unless $sudo_path;
$killall_path = &find_command('killall') unless $killall_path; $killall_path = &find_command('killall') unless $killall_path;
$pgrep_path = &find_command('pgrep') unless $pgrep_path; $pgrep_path = &find_command('pgrep') unless $pgrep_path;
$lib_view_cmd = &find_command('ldd') unless $lib_view_cmd; $lib_view_cmd = &find_command('ldd') unless $lib_view_cmd;
$git_path = &find_command('git') unless $git_path; $git_path = &find_command('git') unless $git_path;
$perl_path = &find_command('perl') unless $perl_path; $perl_path = &find_command('perl') unless $perl_path;
$touch_path = &find_command('touch') unless $touch_path;
if ($sudo_path) { if ($sudo_path) {
$username = (getpwuid((stat($test_suite_path))[4]))[0]; $username = (getpwuid((stat($test_suite_path))[4]))[0];
die "[*] Could not determine $test_suite_path owner" die "[*] Could not determine $test_suite_path owner"
unless $username; unless $username;
### see if sudo is configured to accept custom configs
if (-e $sudo_conf) {
open SR, "< $sudo_conf" or die $!;
while (<SR>) {
if (/^#includedir\s+(\/\S+)/) {
$sudo_conf_testing = "$1/fwknop_testing";
last;
}
}
close SR;
}
} else {
push @tests_to_exclude, qr/sudo/;
} }
### see if the 'nobody' user is on the system ### see if the 'nobody' user is on the system

View File

@ -28,6 +28,20 @@
'fw_rule_created' => $REQUIRE_NO_NEW_RULE, 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'server_positive_output_matches' => [qr/Command messages are not allowed/] 'server_positive_output_matches' => [qr/Command messages are not allowed/]
}, },
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => 'command execution invalid path',
'function' => \&spa_cmd_exec_cycle,
'cmdline' => qq|$fwknopCmd --server-cmd "badpath $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} ".
"$verbose_str",
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $cf{'hmac_cmd_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'cmd_exec_file_not_created' => $YES,
},
{ {
'category' => 'Rijndael+HMAC', 'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server', 'subcategory' => 'client+server',
@ -87,12 +101,156 @@
'detail' => "command execution uid/gid 'nobody'", 'detail' => "command execution uid/gid 'nobody'",
'function' => \&spa_cmd_exec_cycle, 'function' => \&spa_cmd_exec_cycle,
'cmdline' => qq|$fwknopCmd --server-cmd "touch $cmd_exec_test_file" | . 'cmdline' => qq|$fwknopCmd --server-cmd "touch $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} ". "-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} " .
"$verbose_str", $verbose_str,
'cmd_exec_file_owner' => 'nobody', 'cmd_exec_file_owner' => 'nobody',
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $cf{'hmac_cmd_giduid_access'} " . 'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $cf{'hmac_cmd_giduid_access'} " .
"-d $default_digest_file -p $default_pid_file $intf_str", "-d $default_digest_file -p $default_pid_file $intf_str",
'fw_rule_created' => $REQUIRE_NO_NEW_RULE, 'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
}, },
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => "command exec sudo current user (1)",
'function' => \&spa_cmd_exec_cycle,
'cmdline' => qq|$fwknopCmd --server-cmd "touch $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} " .
$verbose_str,
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $sudo_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'sudo_test' => $YES,
'sudo_conf' => 'USER localhost = NOPASSWD: /usr/bin/cat, TOUCH',
'cmd_exec_file_owner' => 'root',
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
},
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => "command exec sudo current user (2)",
'function' => \&spa_cmd_exec_cycle,
'cmdline' => qq|$fwknopCmd --server-cmd "touch $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} " .
$verbose_str,
'cmd_exec_file_owner' => 'nobody',
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $sudo_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'sudo_test' => $YES,
'sudo_conf' => 'USER localhost = NOPASSWD: /usr/bin/cat, (root) TOUCH',
'sudo_exec_user' => $YES,
'cmd_exec_file_owner' => 'root',
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
},
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => "command exec sudo current user (3)",
'function' => \&spa_cmd_exec_cycle,
'cmdline' => qq|$fwknopCmd --server-cmd "touch $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} " .
$verbose_str,
'cmd_exec_file_owner' => 'nobody',
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $sudo_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'sudo_test' => $YES,
'exec_user' => $YES,
'sudo_conf' => 'USER localhost = NOPASSWD: /usr/bin/cat, TOUCH',
'sudo_exec_user' => $YES,
'cmd_exec_file_not_created' => $YES,
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'server_positive_output_matches' => [qr/is not allowed to execute/]
},
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => "command exec sudo current user (4)",
'function' => \&spa_cmd_exec_cycle,
'cmdline' => qq|$fwknopCmd --server-cmd "touch $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} " .
$verbose_str,
'cmd_exec_file_owner' => 'nobody',
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $sudo_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'sudo_test' => $YES,
'exec_user' => $YES,
'sudo_conf' => 'USER localhost = NOPASSWD: /usr/bin/cat, (USER) TOUCH',
'sudo_exec_user' => $YES,
'cmd_exec_file_owner' => 'USER',
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
},
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => "command exec sudo current user (5)",
'function' => \&spa_cmd_exec_cycle,
'cmdline' => qq|$fwknopCmd --server-cmd "touch $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} " .
$verbose_str,
'cmd_exec_file_owner' => 'nobody',
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $sudo_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'sudo_test' => $YES,
'exec_user' => $YES,
'sudo_conf' => 'USER localhost = NOPASSWD: /usr/bin/cat, (baduser) TOUCH',
'sudo_exec_user' => $YES,
'cmd_exec_file_not_created' => $YES,
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'server_positive_output_matches' => [qr/is not allowed to execute/]
},
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => "command exec sudo current user (6)",
'function' => \&spa_cmd_exec_cycle,
'cmdline' => qq|$fwknopCmd --server-cmd "touch $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} " .
$verbose_str,
'cmd_exec_file_owner' => 'nobody',
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $sudo_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'sudo_test' => $YES,
'exec_user' => $YES,
'sudo_conf' => 'USER localhost = NOPASSWD: /usr/bin/cat, notouchcmd',
'sudo_exec_user' => $YES,
'cmd_exec_file_not_created' => $YES,
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
'server_positive_output_matches' => [qr/is not allowed to execute/]
},
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => "command exec sudo current user (7)",
'function' => \&spa_cmd_exec_cycle,
'cmdline' => qq|$fwknopCmd --server-cmd "touch $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} " .
$verbose_str,
'cmd_exec_file_owner' => 'nobody',
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $sudo_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'sudo_test' => $YES,
'exec_user' => $YES,
'sudo_conf' => 'USER localhost = NOPASSWD: /usr/bin/cat, (USER : USER) TOUCH',
'sudo_exec_user' => $YES,
'cmd_exec_file_owner' => 'USER',
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
},
{
'category' => 'Rijndael+HMAC',
'subcategory' => 'client+server',
'detail' => "command exec sudo current user (8)",
'function' => \&spa_cmd_exec_cycle,
'cmdline' => qq|$fwknopCmd --server-cmd "touch $cmd_exec_test_file" | .
"-a $fake_ip -D $loopback_ip --rc-file $cf{'rc_hmac_b64_key'} " .
$verbose_str,
'cmd_exec_file_owner' => 'nobody',
'fwknopd_cmdline' => "$fwknopdCmd -c $cf{'def'} -a $sudo_access_conf " .
"-d $default_digest_file -p $default_pid_file $intf_str",
'sudo_test' => $YES,
'exec_user' => $YES,
'sudo_conf' => 'USER localhost = NOPASSWD: /usr/bin/cat, (USER : USER) TOUCH',
'sudo_exec_user' => $YES,
'sudo_exec_group' => $YES,
'cmd_exec_file_owner' => 'USER',
'fw_rule_created' => $REQUIRE_NO_NEW_RULE,
},
); );