Files
fwknop/server/incoming_spa.c

380 lines
11 KiB
C

/* $Id$
*****************************************************************************
*
* File: incoming_spa.c
*
* Author: Damien S. Stuart
*
* Purpose: Process an incoming SPA data packet for fwknopd.
*
* Copyright (C) 2009 Damien Stuart (dstuart@dstuart.org)
*
* License (GNU Public License):
*
* 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 "incoming_spa.h"
#include "access.h"
#include "log_msg.h"
/* Process the SPA packet data
*/
int
incoming_spa(fko_srv_options_t *opts)
{
fko_ctx_t ctx;
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, ts_diff, enc_type;
int got_spa_error = 0;
spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
/* SPA data fields we will want to pull:
*/
time_t spa_ts;
short spa_msg_type;
char *spa_username;
char *spa_msg;
char *spa_nat_access;
int spa_client_timeout;
/* Get the access.conf data for the stanza that matches this incoming
* source IP address.
*/
acc_stanza_t *acc = acc_check_source(opts, spa_pkt->packet_src_ip);
if(acc == NULL)
{
log_msg(LOG_WARNING|LOG_STDERR,
"No access data found for source IP: %u", spa_pkt->packet_src_ip
);
return(SPA_MSG_ACCESS_DENIED);
}
/* Sanity check
*/
if(spa_pkt->packet_data_len <= 0)
return(SPA_MSG_BAD_DATA);
/* --DSS temp */
fprintf(stderr, "SPA Packet: '%s'\n", spa_pkt->packet_data);
/* --DSS temp */
/* 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
* (which we won't by the time we return from this function for whatever
* reason).
*/
spa_pkt->packet_data_len = 0;
/* Get encryption type and try its decoding routine first (if the key
* for that type is set)
*/
enc_type = fko_encryption_type(spa_pkt->packet_data);
if(enc_type == FKO_ENCRYPTION_RIJNDAEL)
{
if(acc->key != NULL)
res = fko_new_with_data(&ctx, spa_pkt->packet_data, acc->key);
else
{
log_msg(LOG_ERR|LOG_STDERR,
"No KEY for RIJNDAEL encrypted messages");
return(SPA_MSG_FKO_CTX_ERROR);
}
}
else if(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)
{
res = fko_new_with_data(&ctx, spa_pkt->packet_data, NULL);
if(res != FKO_SUCCESS)
{
log_msg(LOG_WARNING|LOG_STDERR,
"Error creating fko context (before decryption): %s",
fko_errstr(res)
);
return(SPA_MSG_FKO_CTX_ERROR);
}
/* Set whatever GPG parameters we have.
*/
if(acc->gpg_home_dir != NULL)
fko_set_gpg_home_dir(ctx, acc->gpg_home_dir);
if(acc->gpg_decrypt_id != NULL)
fko_set_gpg_recipient(ctx, acc->gpg_decrypt_id);
/* If REMOTE_ID is set validate the check the signer. Otherwise,
* skip and ignore verify errors.
*
* TODO: At present we are not checking signatures.
*/
if(acc->gpg_remote_id != NULL)
{
/* TODO: Add sig verify code */
/** --DSS replace these with the real code **/
/**/ fko_set_gpg_signature_verify(ctx, 0); /**/
/**/ fko_set_gpg_ignore_verify_error(ctx, 1); /**/
/** --DSS replace these with the real code **/
}
else
{
fko_set_gpg_signature_verify(ctx, 0);
fko_set_gpg_ignore_verify_error(ctx, 1);
}
/* Now decrypt the data.
*/
res = fko_decrypt_spa_data(ctx, acc->gpg_decrypt_pw);
}
else
{
log_msg(LOG_ERR|LOG_STDERR,
"No GPG_DECRYPT_PW for GPG encrypted messages");
return(SPA_MSG_FKO_CTX_ERROR);
}
}
else
{
log_msg(LOG_ERR|LOG_STDERR, "Unable to determing encryption type. Got type=%i.",
enc_type);
return(SPA_MSG_FKO_CTX_ERROR);
}
/* Do we have a valid FKO context?
*/
if(res != FKO_SUCCESS)
{
log_msg(LOG_WARNING|LOG_STDERR, "Error creating fko context: %s",
fko_errstr(res));
if(IS_GPG_ERROR(res))
log_msg(LOG_WARNING|LOG_STDERR, " - GPG ERROR: %s",
fko_gpg_errstr(ctx));
goto clean_and_bail;
}
/* --DSS temp */
fprintf(stderr, "Decode res = %i\n", res);
display_ctx(ctx);
/* --DSS temp */
if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
{
res = replay_check(opts, ctx);
if(res != SPA_MSG_SUCCESS)
goto clean_and_bail;
}
/* Check packet age if so configured.
*/
if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0)
{
if(fko_get_timestamp(ctx, &spa_ts) != FKO_SUCCESS)
{
log_msg(LOG_WARNING|LOG_STDERR, "Error getting SPA timestamp: %s",
fko_errstr(res));
res = SPA_MSG_ERROR;
goto clean_and_bail;
}
time(&now_ts);
ts_diff = now_ts - spa_ts;
if(ts_diff > atoi(opts->config[CONF_MAX_SPA_PACKET_AGE]))
{
log_msg(LOG_WARNING|LOG_STDERR, "SPA data is too old (%i seconds).",
ts_diff);
res = SPA_MSG_TOO_OLD;
goto clean_and_bail;
}
}
/* Ok, we can pull the rest of the spa data we are interested in.
*/
if(fko_get_spa_message_type(ctx, &spa_msg_type) != FKO_SUCCESS)
{
log_msg(LOG_WARNING|LOG_STDERR, "Error getting SPA message type: %s",
fko_errstr(res));
got_spa_error++;
}
if(fko_get_spa_message(ctx, &spa_msg) != FKO_SUCCESS)
{
log_msg(LOG_WARNING|LOG_STDERR, "Error getting SPA message string: %s",
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",
fko_errstr(res));
got_spa_error++;
}
if(fko_get_spa_client_timeout(ctx, &spa_client_timeout) != FKO_SUCCESS)
{
log_msg(LOG_WARNING|LOG_STDERR, "Error getting SPA client_timeout: %s",
fko_errstr(res));
got_spa_error++;
}
if(got_spa_error > 0)
{
res = SPA_MSG_ERROR;
goto clean_and_bail;
}
/* Take action based on SPA message type.
*/
switch(spa_msg_type)
{
/* Command messages.
*/
case FKO_COMMAND_MSG:
if(!acc->enable_cmd_exec)
{
log_msg(LOG_WARNING|LOG_STDERR,
"SPA Command message are not yet allowed or supported."
);
res = SPA_MSG_ACCESS_DENIED;
}
else
{
/* --DSS TODO: Finish Me */
log_msg(LOG_WARNING|LOG_STDERR,
"SPA Command message are not yet supported."
);
res = SPA_MSG_NOT_SUPPORTED;
}
break;
/* NAT access messages.
*/
case FKO_NAT_ACCESS_MSG:
case FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG:
log_msg(LOG_WARNING|LOG_STDERR,
"SPA NAT access messages are not yet supported."
);
res = SPA_MSG_NOT_SUPPORTED;
/* --DSS TODO: Finish Me */
break;
/* Local NAT access messages.
*/
case FKO_LOCAL_NAT_ACCESS_MSG:
case FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG:
log_msg(LOG_WARNING|LOG_STDERR,
"SPA Local NAT access messages are not yet supported."
);
res = SPA_MSG_NOT_SUPPORTED;
/* --DSS TODO: Finish Me */
break;
/* Standard access messages.
*/
case FKO_ACCESS_MSG:
case FKO_CLIENT_TIMEOUT_ACCESS_MSG:
/* Check access against restrict_ports and open_ports.
*/
res = acc_check_port_access(acc, spa_msg_remain);
// --DSS temp
log_msg(LOG_WARNING|LOG_STDERR,
"<<<< This SPA access msg would be %s >>>>",
res ? "allowed":"DENIED due to port restictions"
);
res = SPA_MSG_NOT_SUPPORTED;
/* --DSS TODO: Finish Me */
break;
}
/* Send to the firewall rule processor.
*/
// TODO: Finish me
clean_and_bail:
fko_destroy(ctx);
return(res);
}
/***EOF***/