Added support for COMMAND_MSG requests. Also added CMD_EXEC_USER to access.conf to allow for fwknopd to setuid to the specified user before running the command. Other minor tweaks.

git-svn-id: file:///home/mbr/svn/fwknop/trunk@226 510a4753-2344-4c79-9c09-4d669213fbeb
This commit is contained in:
Damien Stuart
2010-06-29 01:00:11 +00:00
parent b95d222d3c
commit b7ede1625d
7 changed files with 99 additions and 57 deletions

View File

@@ -29,6 +29,7 @@
#include <sys/socket.h>
#endif
#include <arpa/inet.h>
#include "pwd.h"
#include "fwknopd_common.h"
#include "access.h"
@@ -434,8 +435,8 @@ free_acc_stanza_data(acc_stanza_t *acc)
if(acc->key != NULL)
free(acc->key);
if(acc->cmd_regex != NULL)
free(acc->cmd_regex);
if(acc->cmd_exec_user != NULL)
free(acc->cmd_exec_user);
if(acc->gpg_home_dir != NULL)
free(acc->gpg_home_dir);
@@ -611,6 +612,7 @@ parse_access_file(fko_srv_options_t *opts)
char tmp1[MAX_LINE_LEN] = {0};
char tmp2[MAX_LINE_LEN] = {0};
struct passwd *pw;
struct stat st;
acc_stanza_t *curr_acc = NULL;
@@ -736,9 +738,21 @@ parse_access_file(fko_srv_options_t *opts)
{
add_acc_bool(&(curr_acc->enable_cmd_exec), val);
}
else if(CONF_VAR_IS(var, "CMD_REGEX"))
else if(CONF_VAR_IS(var, "CMD_EXEC_USER"))
{
add_acc_string(&(curr_acc->cmd_regex), val);
add_acc_string(&(curr_acc->cmd_exec_user), val);
errno = 0;
pw = getpwnam(val);
if(pw == NULL)
{
fprintf(stderr, "Unable to determine UID for CMD_EXEC_USER: %s.\n",
errno ? strerror(errno) : "Not a user on this system");
exit(EXIT_FAILURE);
}
curr_acc->cmd_exec_uid = pw->pw_uid;
}
else if(CONF_VAR_IS(var, "REQUIRE_USERNAME"))
{
@@ -970,7 +984,7 @@ dump_access_list(fko_srv_options_t *opts)
" KEY: %s\n"
" FW_ACCESS_TIMEOUT: %i\n"
" ENABLE_CMD_EXEC: %s\n"
" CMD_REGEX: %s\n"
" CMD_EXEC_USER: %s\n"
" REQUIRE_USERNAME: %s\n"
" REQUIRE_SOURCE_ADDRESS: %s\n"
" GPG_HOME_DIR: %s\n"
@@ -984,7 +998,7 @@ dump_access_list(fko_srv_options_t *opts)
(acc->key == NULL) ? "<not set>" : acc->key,
acc->fw_access_timeout,
acc->enable_cmd_exec ? "Yes" : "No",
(acc->cmd_regex == NULL) ? "<not set>" : acc->cmd_regex,
(acc->cmd_exec_user == NULL) ? "<not set>" : acc->cmd_exec_user,
(acc->require_username == NULL) ? "<not set>" : acc->require_username,
acc->require_source_address ? "Yes" : "No",
(acc->gpg_home_dir == NULL) ? "<not set>" : acc->gpg_home_dir,

View File

@@ -46,7 +46,7 @@ set_nonblock(int fd)
* buffers with STDOUT an STDERR up to the size provided.
*/
int
run_extcmd(char *cmd, char *so_buf, char *se_buf, size_t so_buf_sz, size_t se_buf_sz, int *status)
_run_extcmd(uid_t user_uid, char *cmd, char *so_buf, char *se_buf, size_t so_buf_sz, size_t se_buf_sz, int *status)
{
pid_t pid;
@@ -102,9 +102,20 @@ run_extcmd(char *cmd, char *so_buf, char *se_buf, size_t so_buf_sz, size_t se_bu
close(so[0]);
close(se[0]);
/* --DSS XXX: It would be more efficient to use one of the exec()
* calls. (i.e. 'return(execvp(ext_cmd, &argv[1]));' ).
* but for now, we use system() and exit with the external
/* If user is not null, then we setuid to that user before running the
* command.
*/
if(user_uid > 0)
{
if(setuid(user_uid) < 0)
{
exit(EXTCMD_SETUID_ERROR);
}
}
/* --DSS XXX: Would it be more efficient to use one of the exec()
* calls (i.e. 'return(execvp(ext_cmd, &argv[1]));')?
* For now, we use system() and exit with the external
* command exit status.
*/
exit(WEXITSTATUS(system(cmd)));
@@ -267,3 +278,18 @@ run_extcmd(char *cmd, char *so_buf, char *se_buf, size_t so_buf_sz, size_t se_bu
*/
return(retval);
}
/* Run an external command. This is wrapper around _run_extcmd()
*/
run_extcmd(char *cmd, char *so_buf, char *se_buf, size_t so_buf_sz, size_t se_buf_sz, int *status)
{
return _run_extcmd(0, cmd, so_buf, se_buf, so_buf_sz, se_buf_sz, status);
}
/* Run an external command as the specified user. This is wrapper around _run_extcmd()
*/
run_extcmd_as(uid_t user_uid, char *cmd, char *so_buf, char *se_buf, size_t so_buf_sz, size_t se_buf_sz, int *status)
{
return _run_extcmd(user_uid, cmd, so_buf, se_buf, so_buf_sz, se_buf_sz, status);
}

View File

@@ -33,6 +33,7 @@
* may end up in.
*/
enum {
EXTCMD_SETUID_ERROR = -4,
EXTCMD_SELECT_ERROR = -3,
EXTCMD_PIPE_ERROR = -2,
EXTCMD_FORK_ERROR = -1,
@@ -66,6 +67,7 @@ enum {
/* Function prototypes
*/
int run_extcmd(char *cmd, char *so_buf, char *se_buf, size_t so_buf_sz, size_t se_buf_sz, int *status);
int run_extcmd_as(uid_t uid, char *cmd, char *so_buf, char *se_buf, size_t so_buf_sz, size_t se_buf_sz, int *status);
#endif /* EXTCMD_H */

View File

@@ -372,7 +372,7 @@ fw_initialize(fko_srv_options_t *opts)
*
*/
if(opts->config[CONF_SNAT_TRANSLATE_IP] != NULL
&& strncasecmp(opts->config[CONF_SNAT_TRANSLATE_IP], "_CHANGEME_", 10)!=0)
&& strncasecmp(opts->config[CONF_SNAT_TRANSLATE_IP], "__CHANGEME__", 10)!=0)
{
if(opts->config[CONF_IPT_SNAT_ACCESS] != NULL)
set_fw_chain_conf(IPT_SNAT_ACCESS, opts->config[CONF_IPT_SNAT_ACCESS]);
@@ -661,7 +661,7 @@ process_spa_request(fko_srv_options_t *opts, spa_data_t *spadat)
* or MASQUERADE.
*/
if(opts->config[CONF_SNAT_TRANSLATE_IP] != NULL
&& strncasecmp(opts->config[CONF_SNAT_TRANSLATE_IP], "_CHANGEME_", 10)!=0)
&& strncasecmp(opts->config[CONF_SNAT_TRANSLATE_IP], "__CHANGEME__", 10)!=0)
{
/* Using static SNAT */
snat_chain = &(opts->fw_config->chain[IPT_SNAT_ACCESS]);

View File

@@ -19,7 +19,7 @@
# Machine hostname. If not set, fwknopd will attempt to use gethostname()
# to determine the local hostname and use that.
#
#HOSTNAME _CHANGEME_;
#HOSTNAME __CHANGEME__;
# Define the firewall type. The default is "iptables" for Linux systems,
# but this can be set to "ipfw" for *BSD systems. Also supported is
@@ -108,7 +108,7 @@ ENABLE_IPT_LOCAL_NAT Y;
# variable.
#
ENABLE_IPT_SNAT N;
#SNAT_TRANSLATE_IP _CHANGEME_;
#SNAT_TRANSLATE_IP __CHANGEME__;
# Add ACCEPT rules to the FWKNOP_OUTPUT chain. This is usually only useful
# if there are no state tracking rules to allow connection responses out and

View File

@@ -102,6 +102,7 @@ enum {
SPA_MSG_REPLAY,
SPA_MSG_TOO_OLD,
SPA_MSG_ACCESS_DENIED,
SPA_MSG_COMMAND_ERROR,
SPA_MSG_NOT_SUPPORTED,
SPA_MSG_ERROR
};
@@ -315,7 +316,8 @@ typedef struct acc_stanza
char *key;
int fw_access_timeout;
unsigned char enable_cmd_exec;
char *cmd_regex;
char *cmd_exec_user;
uid_t cmd_exec_uid;
char *require_username;
unsigned char require_source_address;
char *gpg_home_dir;

View File

@@ -26,6 +26,7 @@
#include "fwknopd_common.h"
#include "incoming_spa.h"
#include "access.h"
#include "extcmd.h"
#include "log_msg.h"
/* Validate and in some cases preprocess/reformat the SPA data. Return an
@@ -36,7 +37,7 @@ preprocess_spa_data(fko_srv_options_t *opts, char *src_ip)
{
spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
unsigned char *ndx = &(spa_pkt->packet_data);
unsigned char *ndx = (unsigned char *)&(spa_pkt->packet_data);
int pkt_data_len = spa_pkt->packet_data_len;
int tc, i;
@@ -170,7 +171,8 @@ incoming_spa(fko_srv_options_t *opts)
char *spa_ip_demark;
time_t now_ts;
int res, ts_diff, enc_type;
int res, status, ts_diff, enc_type;
uid_t myuid;
spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
@@ -423,11 +425,42 @@ incoming_spa(fko_srv_options_t *opts)
}
else
{
/* --DSS TODO: Finish Me */
log_msg(LOG_WARNING,
"SPA Command message are not yet supported."
log_msg(LOG_INFO,
"Processing SPA Command message: command='%s'.",
spadat.spa_message_remain
);
res = SPA_MSG_NOT_SUPPORTED;
/* Do we need to become another user? If so, we call
* run_extcmd_as and pass the cmd_exec_uid.
*/
if(acc->cmd_exec_user != NULL && strncasecmp(acc->cmd_exec_user, "root", 4) != 0)
{
if(opts->verbose)
log_msg(LOG_INFO, "Setting effective user to %s (UID=%i) before running command.",
acc->cmd_exec_user, acc->cmd_exec_uid);
res = run_extcmd_as(acc->cmd_exec_uid,
spadat.spa_message_remain,
NULL, NULL, 0, 0, &status);
}
else /* Just run it as we are (root that is). */
res = run_extcmd(spadat.spa_message_remain,
NULL, NULL, 0, 0, &status);
/* --DSS XXX: I have found that the status (and res for that
* matter) have been unreliable indicators of the
* actual exit status of some commands. Not sure
* why yet. For now, we will take what we get.
*/
status = WEXITSTATUS(status);
if(opts->verbose > 1)
log_msg(LOG_WARNING,
"CMD_EXEC: result: %i, command returned %i", res, status);
if(res != 0 || status != 0)
res = SPA_MSG_COMMAND_ERROR;
}
goto clean_and_bail;
@@ -450,45 +483,10 @@ incoming_spa(fko_srv_options_t *opts)
goto clean_and_bail;
}
/* If we are here we will at least need to have the base access. So we
* can go ahead an generate that now.
/* At this point, we can process the SPA request.
*/
res = process_spa_request(opts, &spadat);
#if 0
if(res != FKO_SUCCESS)
goto clean_and_bail;
/* Now see if we are looking for a local NAT access of some sort.
*/
if(spadat.message_type == FKO_LOCAL_NAT_ACCESS_MSG
|| spadat.message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
{
log_msg(LOG_WARNING,
"SPA Local NAT access messages are not yet supported."
);
/* --DSS TODO: Finish Me */
res = SPA_MSG_NOT_SUPPORTED;
goto clean_and_bail;
}
/* NAT access messages.
*/
if(spadat.message_type == FKO_NAT_ACCESS_MSG
|| spadat.message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG)
{
log_msg(LOG_WARNING,
"SPA NAT access messages are not yet supported."
);
res = SPA_MSG_NOT_SUPPORTED;
/* --DSS TODO: Finish Me */
}
#endif
clean_and_bail:
if(ctx != NULL)
fko_destroy(ctx);