Added additional sanity checks and clean-up of access.conf processing and functionality. Fixes require source and added check for required username. Added fallback to use GPG_DECRYPT_PW if it was set and the normal KEY failed with a decyption error. Fixed packet count checks to allow a limit of 0 to mean unlimited number of packets.

git-svn-id: file:///home/mbr/svn/fwknop/trunk@203 510a4753-2344-4c79-9c09-4d669213fbeb
This commit is contained in:
Damien Stuart
2010-01-30 20:22:53 +00:00
parent 903f5f466c
commit e3bd3b703e
7 changed files with 172 additions and 52 deletions
+1
View File
@@ -103,6 +103,7 @@ AC_TYPE_UINT64_T
AC_TYPE_OFF_T
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_CHECK_SIZEOF(unsigned int)
dnl AC_CHECK_TYPES([uint8_t, uint32_t])
+6 -4
View File
@@ -406,16 +406,18 @@ the correct message format and content. These message types are:
@table @code
@item FKO_COMMAND_MSG
A request to have the fwknop server execute the given command. The format
for this type is: @samp{<ip of requestor>:<command text>}
for this type is: @samp{<ip of requestor>,<command text>}.
@example
"192.168.1.2:uname -a"
"192.168.1.2,uname -a"
@end example
@item FKO_ACCESS_MSG
A basic access request. This is the most common type in use. The format
for this type is: @samp{<ip of requestor>:<protocol>/<port>}.
for this type is: @samp{<ip of requestor>,<protocol>/<port>}. Note that
mulitple protocol/port entries are allowed.
@example
"192.168.1.2:tcp/22"
"192.168.1.2,tcp/22"
"192.168.1.2,tcp/22,udp/5005"
@end example
@item FKO_NAT_ACCESS_MSG
+61 -9
View File
@@ -578,6 +578,23 @@ set_acc_defaults(fko_srv_options_t *opts)
}
}
/* Perform some sanity checks on an acc stanza data.
*/
int
acc_data_is_valid(acc_stanza_t *acc)
{
if((acc->key == NULL || !strlen(acc->key))
&& (acc->gpg_decrypt_pw == NULL || !strlen(acc->gpg_decrypt_pw)))
{
fprintf(stderr,
"[*] No keys found for access stanza source: '%s'\n", acc->source
);
return(0);
}
return(1);
}
/* Read and parse the access file, popluating the access data as we go.
*/
void
@@ -585,7 +602,7 @@ parse_access_file(fko_srv_options_t *opts)
{
FILE *file_ptr;
char *ndx;
int got_source = 0, got_open_ports = 0, got_key = 0;
int got_source = 0;
unsigned int num_lines = 0;
char access_line_buf[MAX_LINE_LEN] = {0};
@@ -665,11 +682,25 @@ parse_access_file(fko_srv_options_t *opts)
if(CONF_VAR_IS(var, "SOURCE"))
{
/* If this is not the first stanza, sanity check the previous
* stanza for the minimum required data.
*/
if(curr_acc != NULL) {
if(!acc_data_is_valid(curr_acc))
{
fprintf(stderr,
"[*] Data error in access file: '%s'\n",
opts->config[CONF_ACCESS_FILE]);
exit(EXIT_FAILURE);
}
}
/* Start new stanza.
*/
curr_acc = acc_stanza_add(opts);
add_acc_string(&(curr_acc->source), val);
got_source++;
}
else if (curr_acc == NULL)
@@ -681,7 +712,6 @@ parse_access_file(fko_srv_options_t *opts)
else if(CONF_VAR_IS(var, "OPEN_PORTS"))
{
add_acc_string(&(curr_acc->open_ports), val);
got_open_ports++;
}
else if(CONF_VAR_IS(var, "RESTRICT_PORTS"))
{
@@ -689,8 +719,14 @@ parse_access_file(fko_srv_options_t *opts)
}
else if(CONF_VAR_IS(var, "KEY"))
{
if(strcasecmp(val, "__CHANGEME__") == 0)
{
fprintf(stderr,
"[*] KEY value is not properly set in stanza source '%s' in access file: '%s'\n",
curr_acc->source, opts->config[CONF_ACCESS_FILE]);
exit(EXIT_FAILURE);
}
add_acc_string(&(curr_acc->key), val);
got_key++;
}
else if(CONF_VAR_IS(var, "FW_ACCESS_TIMEOUT"))
{
@@ -706,7 +742,7 @@ parse_access_file(fko_srv_options_t *opts)
}
else if(CONF_VAR_IS(var, "REQUIRE_USERNAME"))
{
add_acc_bool(&(curr_acc->require_username), val);
add_acc_string(&(curr_acc->require_username), val);
}
else if(CONF_VAR_IS(var, "REQUIRE_SOURCE_ADDRESS"))
{
@@ -722,6 +758,13 @@ parse_access_file(fko_srv_options_t *opts)
}
else if(CONF_VAR_IS(var, "GPG_DECRYPT_PW"))
{
if(strcasecmp(val, "__CHANGEME__") == 0)
{
fprintf(stderr,
"[*] GPG_DECRYPT_PW value is not properly set in stanza source '%s' in access file: '%s'\n",
curr_acc->source, opts->config[CONF_ACCESS_FILE]);
exit(EXIT_FAILURE);
}
add_acc_string(&(curr_acc->gpg_decrypt_pw), val);
}
else if(CONF_VAR_IS(var, "GPG_REMOTE_ID"))
@@ -740,11 +783,10 @@ parse_access_file(fko_srv_options_t *opts)
fclose(file_ptr);
/* Basic check to ensure that we got at least one SOURCE stanza with
* the OPEN_PORTS and KEY variables defined.
* a valid KEY defined (valid meaning it has a value that is not
* "__CHANGEME__".
*/
if (got_source == 0
|| got_open_ports == 0
|| got_key == 0)
if (got_source == 0)
{
fprintf(stderr,
"[*] Could not find valid SOURCE stanza in access file: '%s'\n",
@@ -752,6 +794,16 @@ parse_access_file(fko_srv_options_t *opts)
exit(EXIT_FAILURE);
}
/* Sanity check the last stanza
*/
if(!acc_data_is_valid(curr_acc))
{
fprintf(stderr,
"[*] Data error in access file: '%s'\n",
opts->config[CONF_ACCESS_FILE]);
exit(EXIT_FAILURE);
}
/* Expand our the expandable fields into their respective data buckets.
*/
expand_acc_ent_lists(opts);
@@ -933,7 +985,7 @@ dump_access_list(fko_srv_options_t *opts)
acc->fw_access_timeout,
acc->enable_cmd_exec ? "Yes" : "No",
(acc->cmd_regex == NULL) ? "<not set>" : acc->cmd_regex,
acc->require_username ? "Yes" : "No",
(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,
(acc->gpg_decrypt_id == NULL) ? "<not set>" : acc->gpg_decrypt_id,
+1 -1
View File
@@ -155,5 +155,5 @@ FW_ACCESS_TIMEOUT: 30;
#
#GPG_HOME_DIR: /root/.gnupg;
#GPG_DECRYPT_ID: ABCD1234;
#GPG_DECRYPT_PW: myGpgPassword;
#GPG_DECRYPT_PW: __CHANGEME__;
#GPG_REMOTE_ID: 1234ABCD;
+1 -1
View File
@@ -308,7 +308,7 @@ typedef struct acc_stanza
int fw_access_timeout;
unsigned char enable_cmd_exec;
char *cmd_regex;
unsigned char require_username;
char *require_username;
unsigned char require_source_address;
char *gpg_home_dir;
char *gpg_decrypt_id;
+87 -24
View File
@@ -34,9 +34,9 @@ int
incoming_spa(fko_srv_options_t *opts)
{
fko_ctx_t ctx;
char *ndx;
char spa_src_ip[16];
char spa_protoport[128];
char *spa_ip_demark;
char spa_msg_src_ip[16];
char spa_msg_remain[1024]; /* --DSS should not have arbitrary limit */
time_t now_ts;
int res;
int ts_diff;
@@ -48,6 +48,7 @@ incoming_spa(fko_srv_options_t *opts)
*/
time_t spa_ts;
short spa_msg_type;
char *spa_username;
char *spa_msg;
char *spa_nat_access;
int spa_client_timeout;
@@ -75,14 +76,35 @@ incoming_spa(fko_srv_options_t *opts)
fprintf(stderr, "SPA Packet: '%s'\n", spa_pkt->packet_data);
/* --DSS temp */
/* Decode the packet data
/* Decode the packet data. Try the plain key, then fallback to the gpg
* decrypt pw.
*/
res = fko_new_with_data(&ctx, spa_pkt->packet_data, acc->key);
if(acc->key != NULL)
{
res = fko_new_with_data(&ctx, spa_pkt->packet_data, acc->key);
/* Reset the packet data length to 0.
/* If we had a decryption failure, fallback to gpg if we have a
* decryption key to try.
*/
if(res == FKO_ERROR_DECRYPTION_FAILURE && acc->gpg_decrypt_pw != NULL)
res = fko_new_with_data(&ctx, spa_pkt->packet_data, acc->gpg_decrypt_pw);
}
else if(acc->gpg_decrypt_pw != NULL)
{
/* Otherwise this is probably a GPG-only stanza...
*/
res = fko_new_with_data(&ctx, spa_pkt->packet_data, acc->gpg_decrypt_pw);
}
/* Reset the packet data length to 0. This our indicator to the rest of
* the program that we do not have a current spa packet to process
* (whcih we won't be the time we return from this function for whatever
* reason.
*/
spa_pkt->packet_data_len = 0;
/* Do we have a valid FKO context?
*/
if(res != FKO_SUCCESS)
{
log_msg(LOG_WARNING|LOG_STDERR, "Error creating fko context: %s",
@@ -141,6 +163,60 @@ display_ctx(ctx);
fko_errstr(res));
got_spa_error++;
}
/* At this point, we have enough to check the embedded (or packet source)
* IP address against the defined access rights. We start by splitting
* the spa msg source IP from the remainder of the message.
*/
spa_ip_demark = strchr(spa_msg, ',');
if(spa_ip_demark == NULL)
{
log_msg(LOG_WARNING|LOG_STDERR, "Error parsing SPA message string: %s",
fko_errstr(res));
res = SPA_MSG_ERROR;
goto clean_and_bail;
}
strlcpy(spa_msg_src_ip, spa_msg, (spa_ip_demark-spa_msg)+1);
strlcpy(spa_msg_remain, spa_ip_demark+1, 1024);
/* If use source IP was requested (embedded IP of 0.0.0.0), make sure it
* is allowed.
*/
if((strcmp(spa_msg_src_ip, "0.0.0.0") == 0) && acc->require_source_address)
{
log_msg(LOG_WARNING|LOG_STDERR,
"Got 0.0.0.0 when valid source IP was required."
);
res = SPA_MSG_ACCESS_DENIED;
goto clean_and_bail;
}
/* If REQUIRE_USERNAME is set, make sure the username in this SPA data
* matches.
*/
if(acc->require_username != NULL)
{
if(fko_get_username(ctx, &spa_username) != FKO_SUCCESS)
{
log_msg(LOG_WARNING|LOG_STDERR, "Error getting SPA username string: %s",
fko_errstr(res));
got_spa_error++;
}
if(strcmp(spa_username, acc->require_username) != 0)
{
log_msg(LOG_WARNING|LOG_STDERR,
"Username in SPA data (%s) does not match required username: %s",
spa_username, acc->require_username
);
res = SPA_MSG_ACCESS_DENIED;
goto clean_and_bail;
}
}
/* Get the rest of our SPA data fields.
*/
if(fko_get_spa_nat_access(ctx, &spa_nat_access) != FKO_SUCCESS)
{
log_msg(LOG_WARNING|LOG_STDERR, "Error getting SPA nat access string: %s",
@@ -170,7 +246,7 @@ display_ctx(ctx);
if(!acc->enable_cmd_exec)
{
log_msg(LOG_WARNING|LOG_STDERR,
"SPA Command message are not allowed or supported."
"SPA Command message are not yet allowed or supported."
);
res = SPA_MSG_ACCESS_DENIED;
}
@@ -210,29 +286,16 @@ display_ctx(ctx);
*/
case FKO_ACCESS_MSG:
case FKO_CLIENT_TIMEOUT_ACCESS_MSG:
/* Parse it into its components (source IP and proto/port list).
*/
ndx = strchr(spa_msg, ',');
if(ndx == NULL)
{
log_msg(LOG_WARNING|LOG_STDERR, "Error parsing SPA message string: %s",
fko_errstr(res));
res = SPA_MSG_ERROR;
break;
}
strlcpy(spa_src_ip, spa_msg, (ndx-spa_msg)+1);
strlcpy(spa_protoport, ndx+1, 128);
/* Check access against restrict_ports and open_ports.
*/
res = acc_check_port_access(acc, spa_protoport);
res = acc_check_port_access(acc, spa_msg_remain);
// --DSS temp
log_msg(LOG_WARNING|LOG_STDERR,
"This SPA access msg would%s be allowed.\n Either way we need code to implement it. :)",
res ? "":" *NOT*"
"<<<< This SPA access msg would be %s >>>>",
res ? "allowed":"DENIED due to port restictions"
);
res = SPA_MSG_NOT_SUPPORTED;
/* --DSS TODO: Finish Me */
+15 -13
View File
@@ -31,6 +31,7 @@
#include "incoming_spa.h"
#include "config_init.h"
#include "sig_handler.h"
#include "log_msg.h"
/* The pcap capture routine.
*/
@@ -61,18 +62,18 @@ pcap_capture(fko_srv_options_t *opts)
if(pcap == NULL)
{
fprintf(stderr, "* pcap_open_live error: %s\n", errstr);
log_msg(LOG_ERR|LOG_STDERR, "* pcap_open_live error: %s\n", errstr);
exit(EXIT_FAILURE);
}
/* We are only interested on seeing packets coming into the interface.
*/
if (pcap_setdirection(pcap, PCAP_D_IN) < 0)
fprintf(stderr, "* Warning: pcap error on setdirection\n");
log_msg(LOG_WARNING|LOG_STDERR, "* Warning: pcap error on setdirection");
if (pcap == NULL)
{
fprintf(stderr, "[*] pcap error: %s\n", errstr);
log_msg(LOG_ERR|LOG_STDERR, "[*] pcap error: %s", errstr);
exit(EXIT_FAILURE);
}
@@ -83,7 +84,7 @@ pcap_capture(fko_srv_options_t *opts)
{
if(pcap_compile(pcap, &fp, opts->config[CONF_PCAP_FILTER], 1, 0) == -1)
{
fprintf(stderr, "[*] Error compiling pcap filter: %s\n",
log_msg(LOG_ERR|LOG_STDERR, "[*] Error compiling pcap filter: %s",
pcap_geterr(pcap)
);
exit(EXIT_FAILURE);
@@ -91,7 +92,7 @@ pcap_capture(fko_srv_options_t *opts)
if(pcap_setfilter(pcap, &fp) == -1)
{
fprintf(stderr, "[*] Error setting pcap filter: %s\n",
log_msg(LOG_ERR|LOG_STDERR, "[*] Error setting pcap filter: %s",
pcap_geterr(pcap)
);
exit(EXIT_FAILURE);
@@ -121,7 +122,7 @@ pcap_capture(fko_srv_options_t *opts)
*/
if((pcap_setnonblock(pcap, 1, errstr)) == -1)
{
fprintf(stderr, "[*] Error setting pcap to non-blocking: %s\n",
log_msg(LOG_ERR|LOG_STDERR, "[*] Error setting pcap to non-blocking: %s",
errstr
);
exit(EXIT_FAILURE);
@@ -159,28 +160,29 @@ pcap_capture(fko_srv_options_t *opts)
* of SPA packet validity at this point.
*/
opts->packet_ctr++;
if (opts->packet_ctr >= opts->packet_ctr_limit)
if (opts->packet_ctr_limit && opts->packet_ctr >= opts->packet_ctr_limit)
{
log_msg(LOG_WARNING|LOG_STDERR,
"* Incoming packet count limit of %i reached",
opts->packet_ctr_limit
);
pcap_breakloop(pcap);
pending_break = 1;
}
pcap_errcnt = 0;
continue;
}
/* If there was an error, complain and go on (to an extent
* before giving up).
*/
else if(res == -1)
{
fprintf(stderr, "[*] Error from pcap_dispatch: %s\n",
log_msg(LOG_ERR|LOG_STDERR, "[*] Error from pcap_dispatch: %s",
pcap_geterr(pcap)
);
if(pcap_errcnt++ > MAX_PCAP_ERRORS_BEFORE_BAIL)
{
fprintf(stderr, "[*] %i consecutive pcap errors. Giving up\n",
log_msg(LOG_ERR|LOG_STDERR, "[*] %i consecutive pcap errors. Giving up",
pcap_errcnt
);
exit(EXIT_FAILURE);