minor update to switch to stdout when exiting with success
This commit is contained in:
parent
41c0be29b7
commit
88d8eb03b3
@ -76,7 +76,7 @@ main(int argc, char **argv)
|
||||
res = kill(old_pid, SIGTERM);
|
||||
if(res == 0)
|
||||
{
|
||||
fprintf(stderr, "Killed fwknopd (pid=%i)\n", old_pid);
|
||||
fprintf(stdout, "Killed fwknopd (pid=%i)\n", old_pid);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
@ -99,9 +99,9 @@ main(int argc, char **argv)
|
||||
old_pid = write_pid_file(&opts);
|
||||
|
||||
if(old_pid > 0)
|
||||
fprintf(stderr, "Detected fwknopd is running (pid=%i).\n", old_pid);
|
||||
fprintf(stdout, "Detected fwknopd is running (pid=%i).\n", old_pid);
|
||||
else
|
||||
fprintf(stderr, "No running fwknopd detected.\n");
|
||||
fprintf(stdout, "No running fwknopd detected.\n");
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
@ -117,7 +117,7 @@ main(int argc, char **argv)
|
||||
res = kill(old_pid, SIGHUP);
|
||||
if(res == 0)
|
||||
{
|
||||
fprintf(stderr, "Sent restart signal to fwknopd (pid=%i)\n", old_pid);
|
||||
fprintf(stdout, "Sent restart signal to fwknopd (pid=%i)\n", old_pid);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
@ -128,7 +128,7 @@ main(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "No running fwknopd detected.\n");
|
||||
fprintf(stdout, "No running fwknopd detected.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
664
server/fwknopd.c.orig
Normal file
664
server/fwknopd.c.orig
Normal file
@ -0,0 +1,664 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: fwknopd.c
|
||||
*
|
||||
* Author: Damien S. Stuart
|
||||
*
|
||||
* Purpose: An implementation of an fwknop server.
|
||||
*
|
||||
* Copyright 2010 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU 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.h"
|
||||
#include "config_init.h"
|
||||
#include "access.h"
|
||||
#include "process_packet.h"
|
||||
#include "pcap_capture.h"
|
||||
#include "log_msg.h"
|
||||
#include "utils.h"
|
||||
#include "fw_util.h"
|
||||
#include "sig_handler.h"
|
||||
#include "replay_cache.h"
|
||||
#include "tcp_server.h"
|
||||
|
||||
/* Prototypes
|
||||
*/
|
||||
static void check_dir_path(const char *path, const char *path_name, unsigned char use_basename);
|
||||
static int make_dir_path(const char *path);
|
||||
static void daemonize_process(fko_srv_options_t *opts);
|
||||
static int write_pid_file(fko_srv_options_t *opts);
|
||||
static pid_t get_running_pid(fko_srv_options_t *opts);
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int res, last_sig, rpdb_count;
|
||||
char *locale;
|
||||
pid_t old_pid;
|
||||
|
||||
fko_srv_options_t opts;
|
||||
|
||||
while(1)
|
||||
{
|
||||
/* Handle command line
|
||||
*/
|
||||
config_init(&opts, argc, argv);
|
||||
|
||||
/* Process any options that do their thing and exit. */
|
||||
|
||||
/* Kill the currently running fwknopd?
|
||||
*/
|
||||
if(opts.kill == 1)
|
||||
{
|
||||
old_pid = get_running_pid(&opts);
|
||||
|
||||
if(old_pid > 0)
|
||||
{
|
||||
res = kill(old_pid, SIGTERM);
|
||||
if(res == 0)
|
||||
{
|
||||
fprintf(stderr, "Killed fwknopd (pid=%i)\n", old_pid);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("Unable to kill fwknop: ");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "No running fwknopd detected.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Status of the currently running fwknopd?
|
||||
*/
|
||||
if(opts.status == 1)
|
||||
{
|
||||
old_pid = write_pid_file(&opts);
|
||||
|
||||
if(old_pid > 0)
|
||||
fprintf(stderr, "Detected fwknopd is running (pid=%i).\n", old_pid);
|
||||
else
|
||||
fprintf(stderr, "No running fwknopd detected.\n");
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* Restart the currently running fwknopd?
|
||||
*/
|
||||
if(opts.restart == 1 || opts.status == 1)
|
||||
{
|
||||
old_pid = get_running_pid(&opts);
|
||||
|
||||
if(old_pid > 0)
|
||||
{
|
||||
res = kill(old_pid, SIGHUP);
|
||||
if(res == 0)
|
||||
{
|
||||
fprintf(stderr, "Sent restart signal to fwknopd (pid=%i)\n", old_pid);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("Unable to send signal to fwknop: ");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "No running fwknopd detected.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize logging.
|
||||
*/
|
||||
init_logging(&opts);
|
||||
|
||||
#if HAVE_LOCALE_H
|
||||
/* Set the locale if specified.
|
||||
*/
|
||||
if(opts.config[CONF_LOCALE] != NULL
|
||||
&& strncasecmp(opts.config[CONF_LOCALE], "NONE", 4) != 0)
|
||||
{
|
||||
locale = setlocale(LC_ALL, opts.config[CONF_LOCALE]);
|
||||
|
||||
if(locale == NULL)
|
||||
{
|
||||
log_msg(LOG_ERR,
|
||||
"WARNING: Unable to set locale to '%s'.",
|
||||
opts.config[CONF_LOCALE]
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(opts.verbose)
|
||||
log_msg(LOG_INFO,
|
||||
"Locale set to '%s'.", opts.config[CONF_LOCALE]
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Make sure we have a valid run dir and path leading to digest file
|
||||
* in case it configured to be somewhere other than the run dir.
|
||||
*/
|
||||
check_dir_path((const char *)opts.config[CONF_FWKNOP_RUN_DIR], "Run", 0);
|
||||
check_dir_path((const char *)opts.config[CONF_DIGEST_FILE], "Run", 1);
|
||||
|
||||
/* Process the access.conf file.
|
||||
*/
|
||||
parse_access_file(&opts);
|
||||
|
||||
/* Show config (including access.conf vars) and exit dump config was
|
||||
* wanted.
|
||||
*/
|
||||
if(opts.dump_config == 1)
|
||||
{
|
||||
dump_config(&opts);
|
||||
dump_access_list(&opts);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* Initialize the firewall rules handler based on the fwknopd.conf
|
||||
* file, but (for iptables firewalls) don't flush any rules or create
|
||||
* any chains yet. This allows us to dump the current firewall rules
|
||||
* via fw_rules_dump() in --fw-list mode before changing around any rules
|
||||
* of an existing fwknopd process.
|
||||
*/
|
||||
fw_config_init(&opts);
|
||||
|
||||
if(opts.fw_list == 1)
|
||||
{
|
||||
fw_dump_rules(&opts);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* If we are a new process (just being started), proceed with normal
|
||||
* start-up. Otherwise, we are here as a result of a signal sent to an
|
||||
* existing process and we want to restart.
|
||||
*/
|
||||
if(get_running_pid(&opts) != getpid())
|
||||
{
|
||||
/* If foreground mode is not set, the fork off and become a daemon.
|
||||
* Otherwise, attempt to get the pid file lock and go on.
|
||||
*/
|
||||
if(opts.foreground == 0)
|
||||
{
|
||||
daemonize_process(&opts);
|
||||
}
|
||||
else
|
||||
{
|
||||
old_pid = write_pid_file(&opts);
|
||||
if(old_pid > 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"* An instance of fwknopd is already running: (PID=%i).\n", old_pid
|
||||
);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else if(old_pid < 0)
|
||||
{
|
||||
fprintf(stderr, "* PID file error. The lock may not be effective.\n");
|
||||
}
|
||||
}
|
||||
|
||||
log_msg(LOG_INFO, "Starting %s", MY_NAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_msg(LOG_INFO, "Re-starting %s", MY_NAME);
|
||||
}
|
||||
|
||||
if(opts.verbose > 1 && opts.foreground)
|
||||
{
|
||||
dump_config(&opts);
|
||||
dump_access_list(&opts);
|
||||
}
|
||||
|
||||
/* Initialize the digest cache for replay attack detection
|
||||
* if so configured.
|
||||
*/
|
||||
if(strncasecmp(opts.config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
|
||||
{
|
||||
rpdb_count = replay_db_init(&opts);
|
||||
|
||||
if(rpdb_count < 0)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"Error opening digest cache file. Incoming digests will not be remembered."
|
||||
);
|
||||
strcpy(opts.config[CONF_ENABLE_DIGEST_PERSISTENCE], "N");
|
||||
}
|
||||
|
||||
if(opts.verbose)
|
||||
log_msg(LOG_ERR,
|
||||
"Using Digest Cache: '%s' (entry count = %i)",
|
||||
opts.config[CONF_DIGEST_FILE], rpdb_count
|
||||
);
|
||||
}
|
||||
|
||||
/* Prepare the firewall - i.e. flush any old rules and (for iptables)
|
||||
* create fwknop chains.
|
||||
*/
|
||||
fw_initialize(&opts);
|
||||
|
||||
/* If the TCP server option was set, fire it up here.
|
||||
*/
|
||||
if(strncasecmp(opts.config[CONF_ENABLE_TCP_SERVER], "Y", 1) == 0)
|
||||
{
|
||||
if(atoi(opts.config[CONF_TCPSERV_PORT]) <= 0
|
||||
|| atoi(opts.config[CONF_TCPSERV_PORT]) > 65535)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"WARNING: ENABLE_TCP_SERVER is set, but TCPSERV_PORT is not valid. TCP server not started!"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
run_tcp_server(&opts);
|
||||
}
|
||||
}
|
||||
|
||||
/* Intiate pcap capture mode...
|
||||
*/
|
||||
pcap_capture(&opts);
|
||||
|
||||
if(got_signal) {
|
||||
last_sig = got_signal;
|
||||
got_signal = 0;
|
||||
|
||||
if(got_sighup)
|
||||
{
|
||||
log_msg(LOG_WARNING, "Got SIGHUP. Re-reading configs.");
|
||||
free_configs(&opts);
|
||||
kill(opts.tcp_server_pid, SIGTERM);
|
||||
usleep(1000000);
|
||||
got_sighup = 0;
|
||||
}
|
||||
else if(got_sigint)
|
||||
{
|
||||
log_msg(LOG_WARNING, "Got SIGINT. Exiting...");
|
||||
got_sigint = 0;
|
||||
break;
|
||||
}
|
||||
else if(got_sigterm)
|
||||
{
|
||||
log_msg(LOG_WARNING, "Got SIGTERM. Exiting...");
|
||||
got_sigterm = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"Got signal %i. No defined action but to exit.", last_sig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (opts.packet_ctr_limit > 0
|
||||
&& opts.packet_ctr >= opts.packet_ctr_limit)
|
||||
{
|
||||
log_msg(LOG_INFO,
|
||||
"Packet count limit (%d) reached. Exiting...",
|
||||
opts.packet_ctr_limit);
|
||||
break;
|
||||
}
|
||||
else /* got_signal was not set (should be if we are here) */
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"Capture ended without signal. Exiting...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log_msg(LOG_INFO, "Shutting Down fwknopd.");
|
||||
|
||||
/* Kill the TCP server (if we have one running).
|
||||
*/
|
||||
if(opts.tcp_server_pid > 0)
|
||||
{
|
||||
log_msg(LOG_INFO, "Killing the TCP server (pid=%i)",
|
||||
opts.tcp_server_pid);
|
||||
|
||||
kill(opts.tcp_server_pid, SIGTERM);
|
||||
|
||||
/* --DSS XXX: This seems to be necessary if the tcp server
|
||||
* was restarted byt this program. We need to
|
||||
* investigate an fix this. For now, this works
|
||||
* (it is kludgy, but does no harm afaik).
|
||||
*/
|
||||
kill(opts.tcp_server_pid, SIGKILL);
|
||||
}
|
||||
|
||||
/* Other cleanup.
|
||||
*/
|
||||
fw_cleanup();
|
||||
free_logging();
|
||||
free_configs(&opts);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Ensure the specified directory exists. If not, create it or die.
|
||||
*/
|
||||
static void
|
||||
check_dir_path(const char *filepath, const char *fp_desc, unsigned char use_basename)
|
||||
{
|
||||
struct stat st;
|
||||
int res;
|
||||
char tmp_path[MAX_PATH_LEN];
|
||||
char *ndx;
|
||||
|
||||
/*
|
||||
* FIXME: We shouldn't use a hard-coded dir-separator here.
|
||||
*/
|
||||
/* But first make sure we are using an absolute path.
|
||||
*/
|
||||
if(*filepath != PATH_SEP)
|
||||
{
|
||||
log_msg(LOG_ERR,
|
||||
"Configured %s directory (%s) is not an absolute path.", fp_desc, filepath
|
||||
);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* If this is a file path that we want to use only the basename, strip
|
||||
* the trailing filename here.
|
||||
*/
|
||||
if(use_basename && ((ndx = strrchr(filepath, PATH_SEP)) != NULL))
|
||||
strlcpy(tmp_path, filepath, (ndx-filepath)+1);
|
||||
else
|
||||
strcpy(tmp_path, filepath);
|
||||
|
||||
/* At this point, we should make the path is more than just the
|
||||
* PATH_SEP. If it is not, silently return.
|
||||
*/
|
||||
if(strlen(tmp_path) < 2)
|
||||
return;
|
||||
|
||||
/* Make sure we have a valid directory.
|
||||
*/
|
||||
res = stat(tmp_path, &st);
|
||||
if(res != 0)
|
||||
{
|
||||
if(errno == ENOENT)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"%s directory: %s does not exist. Attempting to create it.",
|
||||
fp_desc, tmp_path
|
||||
);
|
||||
|
||||
/* Directory does not exist, so attempt to create it.
|
||||
*/
|
||||
res = make_dir_path(tmp_path);
|
||||
if(res != 0)
|
||||
{
|
||||
log_msg(LOG_ERR,
|
||||
"Unable to create %s directory: %s (error: %i)",
|
||||
fp_desc, tmp_path, errno
|
||||
);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
log_msg(LOG_ERR,
|
||||
"Successfully created %s directory: %s", fp_desc, tmp_path
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_msg(LOG_ERR,
|
||||
"Stat of %s returned error %i", tmp_path, errno
|
||||
);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It is a file, but is it a directory?
|
||||
*/
|
||||
if(! S_ISDIR(st.st_mode))
|
||||
{
|
||||
log_msg(LOG_ERR,
|
||||
"Specified %s directory: %s is NOT a directory\n\n", fp_desc, tmp_path
|
||||
);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
make_dir_path(const char *run_dir)
|
||||
{
|
||||
struct stat st;
|
||||
int res, len;
|
||||
char tmp_path[MAX_PATH_LEN];
|
||||
char *ndx;
|
||||
|
||||
strlcpy(tmp_path, run_dir, MAX_PATH_LEN);
|
||||
|
||||
len = strlen(tmp_path);
|
||||
|
||||
/* Strip any trailing dir sep char.
|
||||
*/
|
||||
if(tmp_path[len-1] == PATH_SEP)
|
||||
tmp_path[len-1] = '\0';
|
||||
|
||||
for(ndx = tmp_path+1; *ndx; ndx++)
|
||||
{
|
||||
if(*ndx == '/')
|
||||
{
|
||||
*ndx = '\0';
|
||||
|
||||
/* Stat this part of the path to see if it is a valid directory.
|
||||
* If it does not exist, attempt to create it. If it does, and
|
||||
* it is a directory, go on. Otherwise, any other error cause it
|
||||
* to bail.
|
||||
*/
|
||||
if(stat(tmp_path, &st) != 0)
|
||||
{
|
||||
if(errno == ENOENT)
|
||||
res = mkdir(tmp_path, S_IRWXU);
|
||||
|
||||
if(res != 0)
|
||||
return res;
|
||||
}
|
||||
|
||||
if(! S_ISDIR(st.st_mode))
|
||||
{
|
||||
log_msg(LOG_ERR,
|
||||
"Component: %s of %s is NOT a directory\n\n", tmp_path, run_dir
|
||||
);
|
||||
return(ENOTDIR);
|
||||
}
|
||||
|
||||
*ndx = '/';
|
||||
}
|
||||
}
|
||||
|
||||
res = mkdir(tmp_path, S_IRWXU);
|
||||
|
||||
return(res);
|
||||
}
|
||||
|
||||
/* Become a daemon: fork(), start a new session, chdir "/",
|
||||
* and close unneeded standard filehandles.
|
||||
*/
|
||||
static void
|
||||
daemonize_process(fko_srv_options_t *opts)
|
||||
{
|
||||
pid_t pid, old_pid;
|
||||
|
||||
/* Reset the our umask
|
||||
*/
|
||||
umask(0);
|
||||
|
||||
if ((pid = fork()) < 0)
|
||||
{
|
||||
perror("Unable to fork: ");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else if (pid != 0) /* parent */
|
||||
{
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* Child process from here on out */
|
||||
|
||||
/* Start a new session
|
||||
*/
|
||||
setsid();
|
||||
|
||||
/* Create the PID file (or be blocked by an existing one).
|
||||
*/
|
||||
old_pid = write_pid_file(opts);
|
||||
if(old_pid > 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"* An instance of fwknopd is already running: (PID=%i).\n", old_pid
|
||||
);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else if(old_pid < 0)
|
||||
{
|
||||
fprintf(stderr, "* PID file error. The lock may not be effective.\n");
|
||||
}
|
||||
|
||||
/* Chdir to the root of the filesystem
|
||||
*/
|
||||
if ((chdir("/")) < 0) {
|
||||
perror("Could not chdir() to /: ");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Close un-needed file handles
|
||||
*/
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
write_pid_file(fko_srv_options_t *opts)
|
||||
{
|
||||
pid_t old_pid, my_pid;
|
||||
int op_fd, lck_res, num_bytes;
|
||||
char buf[6] = {0};
|
||||
|
||||
/* Reset errno (just in case)
|
||||
*/
|
||||
errno = 0;
|
||||
|
||||
/* Open the PID file
|
||||
*/
|
||||
op_fd = open(
|
||||
opts->config[CONF_FWKNOP_PID_FILE], O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR
|
||||
);
|
||||
|
||||
if(op_fd == -1)
|
||||
{
|
||||
perror("Error trying to open PID file: ");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fcntl(op_fd, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
/* Attempt to lock the PID file. If we get an EWOULDBLOCK
|
||||
* error, another instance already has the lock. So we grab
|
||||
* the pid from the existing lock file, complain and bail.
|
||||
*/
|
||||
lck_res = lockf(op_fd, F_TLOCK, 0);
|
||||
if(lck_res == -1)
|
||||
{
|
||||
if(errno != EAGAIN)
|
||||
{
|
||||
perror("Unexpected error from lockf: ");
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(op_fd);
|
||||
|
||||
/* Look for an existing lock holder. If we get a pid return it.
|
||||
*/
|
||||
old_pid = get_running_pid(opts);
|
||||
if(old_pid)
|
||||
return old_pid;
|
||||
|
||||
/* Otherwise, consider it an error.
|
||||
*/
|
||||
perror("Unable read existing PID file: ");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write our PID to the file
|
||||
*/
|
||||
my_pid = getpid();
|
||||
snprintf(buf, 6, "%i\n", my_pid);
|
||||
|
||||
if(opts->verbose > 1)
|
||||
log_msg(LOG_INFO, "[+] Writing my PID (%i) to the lock file: %s\n",
|
||||
my_pid, opts->config[CONF_FWKNOP_PID_FILE]);
|
||||
|
||||
num_bytes = write(op_fd, buf, strlen(buf));
|
||||
|
||||
if(errno || num_bytes != strlen(buf))
|
||||
perror("Lock may not be valid. PID file write error: ");
|
||||
|
||||
/* Sync/flush regardless...
|
||||
*/
|
||||
fsync(op_fd);
|
||||
|
||||
/* Put the lock file discriptor in out options struct so any
|
||||
* child processes we my spawn can close and release it.
|
||||
*/
|
||||
opts->lock_fd = op_fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pid_t
|
||||
get_running_pid(fko_srv_options_t *opts)
|
||||
{
|
||||
int op_fd;
|
||||
char buf[6] = {0};
|
||||
pid_t rpid = 0;
|
||||
|
||||
op_fd = open(opts->config[CONF_FWKNOP_PID_FILE], O_RDONLY);
|
||||
|
||||
if(op_fd > 0)
|
||||
{
|
||||
if (read(op_fd, buf, 6) > 0)
|
||||
rpid = (pid_t)atoi(buf);
|
||||
|
||||
close(op_fd);
|
||||
}
|
||||
|
||||
return(rpid);
|
||||
}
|
||||
|
||||
/***EOF***/
|
||||
39
server/fwknopd.c.rej
Normal file
39
server/fwknopd.c.rej
Normal file
@ -0,0 +1,39 @@
|
||||
--- server/fwknopd.c
|
||||
+++ server/fwknopd.c
|
||||
@@ -51,7 +51,7 @@
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
- int res, last_sig, rp_cache_count;
|
||||
+ int res, last_sig, rpdb_count;
|
||||
char *locale;
|
||||
pid_t old_pid;
|
||||
|
||||
@@ -239,15 +239,14 @@
|
||||
dump_access_list(&opts);
|
||||
}
|
||||
|
||||
- /* Initialize the digest cache for replay attack detection (either
|
||||
- * with dbm support or with the default simple cache file strategy
|
||||
+ /* Initialize the digest cache (replay attack detection dbm)
|
||||
* if so configured.
|
||||
*/
|
||||
if(strncasecmp(opts.config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
|
||||
{
|
||||
- rp_cache_count = replay_cache_init(&opts);
|
||||
+ rpdb_count = replay_db_init(&opts);
|
||||
|
||||
- if(rp_cache_count < 0)
|
||||
+ if(rpdb_count < 0)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"Error opening digest cache file. Incoming digests will not be remembered."
|
||||
@@ -258,7 +257,7 @@
|
||||
if(opts.verbose)
|
||||
log_msg(LOG_ERR,
|
||||
"Using Digest Cache: '%s' (entry count = %i)",
|
||||
- opts.config[CONF_DIGEST_FILE], rp_cache_count
|
||||
+ opts.config[CONF_DIGEST_FILE], rpdb_count
|
||||
);
|
||||
}
|
||||
|
||||
541
server/incoming_spa.c.orig
Normal file
541
server/incoming_spa.c.orig
Normal file
@ -0,0 +1,541 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: incoming_spa.c
|
||||
*
|
||||
* Author: Damien S. Stuart
|
||||
*
|
||||
* Purpose: Process an incoming SPA data packet for fwknopd.
|
||||
*
|
||||
* Copyright 2010 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU 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 "netinet_common.h"
|
||||
|
||||
#if HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include "incoming_spa.h"
|
||||
#include "access.h"
|
||||
#include "extcmd.h"
|
||||
#include "log_msg.h"
|
||||
#include "utils.h"
|
||||
#include "fw_util.h"
|
||||
#include "fwknopd_errors.h"
|
||||
#include "replay_cache.h"
|
||||
|
||||
/* Validate and in some cases preprocess/reformat the SPA data. Return an
|
||||
* error code value if there is any indication the data is not valid spa data.
|
||||
*/
|
||||
static int
|
||||
preprocess_spa_data(fko_srv_options_t *opts, char *src_ip)
|
||||
{
|
||||
spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
|
||||
|
||||
char *ndx = (char *)&(spa_pkt->packet_data);
|
||||
int pkt_data_len = spa_pkt->packet_data_len;
|
||||
int i;
|
||||
|
||||
/* At this point, we can 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 (after this one that is).
|
||||
*/
|
||||
spa_pkt->packet_data_len = 0;
|
||||
|
||||
/* Expect the data to be at least the minimum required size.
|
||||
*/
|
||||
if(pkt_data_len < MIN_SPA_DATA_SIZE)
|
||||
return(SPA_MSG_LEN_TOO_SMALL);
|
||||
|
||||
/* Detect and parse out SPA data from an HTTP reqest. If the SPA data
|
||||
* starts with "GET /" and the user agent starts with "Fwknop", then
|
||||
* assume it is a SPA over HTTP request.
|
||||
*/
|
||||
if(strncasecmp(ndx, "GET /", 5) == 0
|
||||
&& strstr(ndx, "User-Agent: Fwknop") != NULL)
|
||||
{
|
||||
/* This looks like an HTTP request, so let's see if we are
|
||||
* configured to accept such request and if so, find the SPA
|
||||
* data.
|
||||
*/
|
||||
if(strncasecmp(opts->config[CONF_ENABLE_SPA_OVER_HTTP], "N", 1) == 0)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"HTTP request from %s detected, but not enabled.", src_ip
|
||||
);
|
||||
return(SPA_MSG_HTTP_NOT_ENABLED);
|
||||
}
|
||||
|
||||
/* Now extract, adjust (convert characters translated by the fwknop
|
||||
* client), and reset the SPA message itself.
|
||||
*/
|
||||
strlcpy((char *)spa_pkt->packet_data, ndx+5, pkt_data_len);
|
||||
|
||||
for(i=0; i<pkt_data_len; i++)
|
||||
{
|
||||
if(isspace(*ndx)) /* The first space marks the end of the req */
|
||||
{
|
||||
*ndx = '\0';
|
||||
break;
|
||||
}
|
||||
else if(*ndx == '-') /* Convert '-' to '+' */
|
||||
*ndx = '+';
|
||||
else if(*ndx == '_') /* Convert '_' to '/' */
|
||||
*ndx = '/';
|
||||
/* Make sure it is a valid base64 char. */
|
||||
else if(!(isalnum(*ndx) || *ndx == '/' || *ndx == '+' || *ndx == '='))
|
||||
return(SPA_MSG_NOT_SPA_DATA);
|
||||
|
||||
ndx++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Make sure the data is valid Base64-encoded characters
|
||||
* (at least the first MIN_SPA_DATA_SIZE bytes).
|
||||
*/
|
||||
ndx = (char *)spa_pkt->packet_data;
|
||||
for(i=0; i<MIN_SPA_DATA_SIZE; i++)
|
||||
{
|
||||
if(!(isalnum(*ndx) || *ndx == '/' || *ndx == '+' || *ndx == '='))
|
||||
return(SPA_MSG_NOT_SPA_DATA);
|
||||
ndx++;
|
||||
}
|
||||
}
|
||||
|
||||
/* --DSS: Are there other checks we can do here ??? */
|
||||
|
||||
/* If we made it here, we have no reason to assume this is not SPA data
|
||||
* (at least until we come up with more checks).
|
||||
*/
|
||||
return(FKO_SUCCESS);
|
||||
}
|
||||
|
||||
/* Popluate a spa_data struct from an initialized (and populated) FKO context.
|
||||
*/
|
||||
static int
|
||||
get_spa_data_fields(fko_ctx_t ctx, spa_data_t *spdat)
|
||||
{
|
||||
int res = FKO_SUCCESS;
|
||||
|
||||
res = fko_get_username(ctx, &(spdat->username));
|
||||
if(res != FKO_SUCCESS)
|
||||
return(res);
|
||||
|
||||
res = fko_get_timestamp(ctx, &(spdat->timestamp));
|
||||
if(res != FKO_SUCCESS)
|
||||
return(res);
|
||||
|
||||
res = fko_get_version(ctx, &(spdat->version));
|
||||
if(res != FKO_SUCCESS)
|
||||
return(res);
|
||||
|
||||
res = fko_get_spa_message_type(ctx, &(spdat->message_type));
|
||||
if(res != FKO_SUCCESS)
|
||||
return(res);
|
||||
|
||||
res = fko_get_spa_message(ctx, &(spdat->spa_message));
|
||||
if(res != FKO_SUCCESS)
|
||||
return(res);
|
||||
|
||||
res = fko_get_spa_nat_access(ctx, &(spdat->nat_access));
|
||||
if(res != FKO_SUCCESS)
|
||||
return(res);
|
||||
|
||||
res = fko_get_spa_server_auth(ctx, &(spdat->server_auth));
|
||||
if(res != FKO_SUCCESS)
|
||||
return(res);
|
||||
|
||||
res = fko_get_spa_client_timeout(ctx, (int *)&(spdat->client_timeout));
|
||||
if(res != FKO_SUCCESS)
|
||||
return(res);
|
||||
|
||||
return(res);
|
||||
}
|
||||
|
||||
/* Process the SPA packet data
|
||||
*/
|
||||
int
|
||||
incoming_spa(fko_srv_options_t *opts)
|
||||
{
|
||||
/* Always a good idea to initialize ctx to null if it will be used
|
||||
* repeatedly (especially when using fko_new_with_data().
|
||||
*/
|
||||
fko_ctx_t ctx = NULL;
|
||||
|
||||
char *spa_ip_demark, *gpg_id;
|
||||
time_t now_ts;
|
||||
int res, status, ts_diff, enc_type;
|
||||
|
||||
spa_pkt_info_t *spa_pkt = &(opts->spa_pkt);
|
||||
|
||||
/* This will hold our pertinent SPA data.
|
||||
*/
|
||||
spa_data_t spadat;
|
||||
|
||||
/* 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);
|
||||
|
||||
inet_ntop(AF_INET, &(spa_pkt->packet_src_ip),
|
||||
spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip));
|
||||
|
||||
/* At this point, we want to validate and (if needed) preprocess the
|
||||
* SPA data and/or to be reasonably sure we have a SPA packet (i.e
|
||||
* try to eliminate obvious non-spa packets).
|
||||
*/
|
||||
res = preprocess_spa_data(opts, spadat.pkt_source_ip);
|
||||
if(res != FKO_SUCCESS)
|
||||
return(SPA_MSG_NOT_SPA_DATA);
|
||||
|
||||
log_msg(LOG_INFO, "SPA Packet from IP: %s received.", spadat.pkt_source_ip);
|
||||
|
||||
if(acc == NULL)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"No access data found for source IP: %s", spadat.pkt_source_ip
|
||||
);
|
||||
|
||||
return(SPA_MSG_ACCESS_DENIED);
|
||||
}
|
||||
|
||||
if(opts->verbose > 1)
|
||||
log_msg(LOG_INFO, "SPA Packet: '%s'\n", spa_pkt->packet_data);
|
||||
|
||||
/* Get encryption type and try its decoding routine first (if the key
|
||||
* for that type is set)
|
||||
*/
|
||||
enc_type = fko_encryption_type((char *)spa_pkt->packet_data);
|
||||
|
||||
if(enc_type == FKO_ENCRYPTION_RIJNDAEL)
|
||||
{
|
||||
if(acc->key != NULL)
|
||||
res = fko_new_with_data(&ctx, (char *)spa_pkt->packet_data, acc->key);
|
||||
else
|
||||
{
|
||||
log_msg(LOG_ERR,
|
||||
"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, (char *)spa_pkt->packet_data, NULL);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"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)
|
||||
res = fko_set_gpg_home_dir(ctx, acc->gpg_home_dir);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"Error setting GPG keyring path to %s: %s",
|
||||
acc->gpg_home_dir,
|
||||
fko_errstr(res)
|
||||
);
|
||||
return(SPA_MSG_FKO_CTX_ERROR);
|
||||
}
|
||||
|
||||
if(acc->gpg_decrypt_id != NULL)
|
||||
fko_set_gpg_recipient(ctx, acc->gpg_decrypt_id);
|
||||
|
||||
/* If GPG_REQUIRE_SIG is set for this acc stanza, then set
|
||||
* the FKO context accordingly and check the other GPG Sig-
|
||||
* related parameters. This also applies when REMOTE_ID is
|
||||
* set.
|
||||
*/
|
||||
if(acc->gpg_require_sig)
|
||||
{
|
||||
fko_set_gpg_signature_verify(ctx, 1);
|
||||
|
||||
/* Set whether or not to ignore signature verification errors.
|
||||
*/
|
||||
fko_set_gpg_ignore_verify_error(ctx, acc->gpg_ignore_sig_error);
|
||||
}
|
||||
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,
|
||||
"No GPG_DECRYPT_PW for GPG encrypted messages");
|
||||
return(SPA_MSG_FKO_CTX_ERROR);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log_msg(LOG_ERR, "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, "Error creating fko context: %s",
|
||||
fko_errstr(res));
|
||||
|
||||
if(IS_GPG_ERROR(res))
|
||||
log_msg(LOG_WARNING, " - GPG ERROR: %s",
|
||||
fko_gpg_errstr(ctx));
|
||||
|
||||
goto clean_and_bail;
|
||||
}
|
||||
|
||||
/* At this point, we assume the SPA data is valid. Now we need to see
|
||||
* if it meets our access criteria.
|
||||
*/
|
||||
if(opts->verbose > 2)
|
||||
log_msg(LOG_INFO, "SPA Decode (res=%i):\n%s", res, dump_ctx(ctx));
|
||||
|
||||
/* First, if this is a GPG message, and GPG_REMOTE_ID list is not empty,
|
||||
* then we need to make sure this incoming message is signer ID matches
|
||||
* an entry in the list.
|
||||
*/
|
||||
if(enc_type == FKO_ENCRYPTION_GPG && acc->gpg_require_sig)
|
||||
{
|
||||
res = fko_get_gpg_signature_id(ctx, &gpg_id);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
log_msg(LOG_WARNING, "Error pulling the GPG signature ID from the context: %s",
|
||||
fko_gpg_errstr(ctx));
|
||||
goto clean_and_bail;
|
||||
}
|
||||
|
||||
if(opts->verbose)
|
||||
log_msg(LOG_INFO, "Incoming SPA data signed by '%s'.", gpg_id);
|
||||
|
||||
if(acc->gpg_remote_id != NULL && !acc_check_gpg_remote_id(acc, gpg_id))
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"Incoming SPA packet signed by ID: %s, but that ID is not the GPG_REMOTE_ID list.",
|
||||
gpg_id);
|
||||
goto clean_and_bail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for replays if so configured.
|
||||
*/
|
||||
if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0)
|
||||
{
|
||||
res = replay_check(opts, ctx);
|
||||
if(res != 0) /* non-zero means we have seen this packet before. */
|
||||
goto clean_and_bail;
|
||||
}
|
||||
|
||||
/* Populate our spa data struct for future reference.
|
||||
*/
|
||||
res = get_spa_data_fields(ctx, &spadat);
|
||||
|
||||
/* Figure out what our timeout will be. If it is specified in the SPA
|
||||
* data, then use that. If not, try the FW_ACCESS_TIMEOUT from the
|
||||
* access.conf file (if there is one). Otherwise use the default.
|
||||
*/
|
||||
if(spadat.client_timeout > 0)
|
||||
spadat.fw_access_timeout = spadat.client_timeout;
|
||||
else if(acc->fw_access_timeout > 0)
|
||||
spadat.fw_access_timeout = acc->fw_access_timeout;
|
||||
else
|
||||
spadat.fw_access_timeout = DEF_FW_ACCESS_TIMEOUT;
|
||||
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
log_msg(LOG_ERR, "Unexpected error pulling SPA data from the context: %s",
|
||||
fko_errstr(res));
|
||||
res = SPA_MSG_ERROR;
|
||||
goto clean_and_bail;
|
||||
}
|
||||
|
||||
/* Check packet age if so configured.
|
||||
*/
|
||||
if(strncasecmp(opts->config[CONF_ENABLE_SPA_PACKET_AGING], "Y", 1) == 0)
|
||||
{
|
||||
time(&now_ts);
|
||||
|
||||
ts_diff = now_ts - spadat.timestamp;
|
||||
|
||||
if(ts_diff > atoi(opts->config[CONF_MAX_SPA_PACKET_AGE]))
|
||||
{
|
||||
log_msg(LOG_WARNING, "SPA data is too old (%i seconds).",
|
||||
ts_diff);
|
||||
res = SPA_MSG_TOO_OLD;
|
||||
goto clean_and_bail;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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(spadat.spa_message, ',');
|
||||
if(spa_ip_demark == NULL)
|
||||
{
|
||||
log_msg(LOG_WARNING, "Error parsing SPA message string: %s",
|
||||
fko_errstr(res));
|
||||
res = SPA_MSG_ERROR;
|
||||
goto clean_and_bail;
|
||||
}
|
||||
|
||||
strlcpy(spadat.spa_message_src_ip, spadat.spa_message, (spa_ip_demark-spadat.spa_message)+1);
|
||||
strlcpy(spadat.spa_message_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(spadat.spa_message_src_ip, "0.0.0.0") == 0)
|
||||
{
|
||||
if(acc->require_source_address)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"Got 0.0.0.0 when valid source IP was required."
|
||||
);
|
||||
res = SPA_MSG_ACCESS_DENIED;
|
||||
goto clean_and_bail;
|
||||
}
|
||||
|
||||
spadat.use_src_ip = spadat.pkt_source_ip;
|
||||
}
|
||||
else
|
||||
spadat.use_src_ip = spadat.spa_message_src_ip;
|
||||
|
||||
/* If REQUIRE_USERNAME is set, make sure the username in this SPA data
|
||||
* matches.
|
||||
*/
|
||||
if(acc->require_username != NULL)
|
||||
{
|
||||
if(strcmp(spadat.username, acc->require_username) != 0)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"Username in SPA data (%s) does not match required username: %s",
|
||||
spadat.username, acc->require_username
|
||||
);
|
||||
res = SPA_MSG_ACCESS_DENIED;
|
||||
goto clean_and_bail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Take action based on SPA message type. */
|
||||
|
||||
/* Command messages.
|
||||
*/
|
||||
if(spadat.message_type == FKO_COMMAND_MSG)
|
||||
{
|
||||
if(!acc->enable_cmd_exec)
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"SPA Command message are not allowed in the current configuration."
|
||||
);
|
||||
res = SPA_MSG_ACCESS_DENIED;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_msg(LOG_INFO,
|
||||
"Processing SPA Command message: command='%s'.",
|
||||
spadat.spa_message_remain
|
||||
);
|
||||
|
||||
/* 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, 0, 0);
|
||||
}
|
||||
else /* Just run it as we are (root that is). */
|
||||
res = run_extcmd(spadat.spa_message_remain, NULL, 0, 5);
|
||||
|
||||
/* --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(res);
|
||||
|
||||
if(opts->verbose > 2)
|
||||
log_msg(LOG_WARNING,
|
||||
"CMD_EXEC: command returned %i", status);
|
||||
|
||||
if(status != 0)
|
||||
res = SPA_MSG_COMMAND_ERROR;
|
||||
}
|
||||
|
||||
goto clean_and_bail;
|
||||
}
|
||||
|
||||
/* From this point forward, we have some kind of access message. So
|
||||
* we first see if access is allowed by checking access against
|
||||
* restrict_ports and open_ports.
|
||||
*
|
||||
* --DSS TODO: We should add BLACKLIST support here as well.
|
||||
*/
|
||||
if(! acc_check_port_access(acc, spadat.spa_message_remain))
|
||||
{
|
||||
log_msg(LOG_WARNING,
|
||||
"One or more requested protocol/ports was denied per access.conf."
|
||||
);
|
||||
|
||||
res = SPA_MSG_ACCESS_DENIED;
|
||||
|
||||
goto clean_and_bail;
|
||||
}
|
||||
|
||||
/* At this point, we can process the SPA request.
|
||||
*/
|
||||
res = process_spa_request(opts, &spadat);
|
||||
|
||||
clean_and_bail:
|
||||
if(ctx != NULL)
|
||||
fko_destroy(ctx);
|
||||
|
||||
return(res);
|
||||
}
|
||||
|
||||
/***EOF***/
|
||||
326
server/replay_cache.c.orig
Normal file
326
server/replay_cache.c.orig
Normal file
@ -0,0 +1,326 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
*
|
||||
* File: replay_cache.c
|
||||
*
|
||||
* Author: Damien S. Stuart
|
||||
*
|
||||
* Purpose: Provides the functions to check for possible replay attacks
|
||||
* by using a cache of previously seen digests. This cache is a
|
||||
* simple file by default, but can be made to use a dbm solution
|
||||
* (ndbm or gdbm in ndbm compatibility mode) file to store the digest
|
||||
* of a previously received SPA packets.
|
||||
*
|
||||
* Copyright 2010 Damien Stuart (dstuart@dstuart.org)
|
||||
*
|
||||
* License (GNU 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 "replay_cache.h"
|
||||
#include "log_msg.h"
|
||||
#include "fwknopd_errors.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#if HAVE_LIBGDBM
|
||||
#include <gdbm.h>
|
||||
|
||||
#define MY_DBM_FETCH(d, k) gdbm_fetch(d, k)
|
||||
#define MY_DBM_STORE(d, k, v, m) gdbm_store(d, k, v, m)
|
||||
#define MY_DBM_STRERROR(x) gdbm_strerror(x)
|
||||
#define MY_DBM_CLOSE(d) gdbm_close(d)
|
||||
|
||||
#define MY_DBM_REPLACE GDBM_REPLACE
|
||||
#define MY_DBM_INSERT GDBM_INSERT
|
||||
|
||||
#elif HAVE_LIBNDBM
|
||||
#include <ndbm.h>
|
||||
|
||||
#define MY_DBM_FETCH(d, k) dbm_fetch(d, k)
|
||||
#define MY_DBM_STORE(d, k, v, m) dbm_store(d, k, v, m)
|
||||
#define MY_DBM_STRERROR(x) strerror(x)
|
||||
#define MY_DBM_CLOSE(d) dbm_close(d)
|
||||
|
||||
#define MY_DBM_REPLACE DBM_REPLACE
|
||||
#define MY_DBM_INSERT DBM_INSERT
|
||||
|
||||
#else
|
||||
#error "No GDBM or NDBM header file found. WTF?"
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#define MAX_DIGEST_SIZE 64
|
||||
|
||||
/* Rotate the digest file by simply renaming it.
|
||||
*/
|
||||
static void
|
||||
rotate_digest_cache_file(fko_srv_options_t *opts)
|
||||
{
|
||||
#ifdef NO_DIGEST_CACHE
|
||||
log_msg(LOG_WARNING, "Digest cache not supported. Nothing to rotate.");
|
||||
#else
|
||||
int res;
|
||||
char *new_file = NULL;
|
||||
|
||||
log_msg(LOG_INFO, "Rotating digest cache file.");
|
||||
|
||||
new_file = malloc(strlen(opts->config[CONF_DIGEST_FILE])+5);
|
||||
|
||||
if(new_file == NULL)
|
||||
{
|
||||
log_msg(LOG_ERR, "rotate_digest_cache_file: Memory allocation error.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* The new filename is just the original with a trailing '-old'.
|
||||
*/
|
||||
strcpy(new_file, opts->config[CONF_DIGEST_FILE]);
|
||||
strcat(new_file, "-old");
|
||||
|
||||
res = rename(opts->config[CONF_DIGEST_FILE], new_file);
|
||||
|
||||
if(res < 0)
|
||||
log_msg(LOG_ERR, "Unable to rename digest file: %s to %s: %s",
|
||||
opts->config[CONF_DIGEST_FILE], new_file, strerror(errno)
|
||||
);
|
||||
#endif /* NO_DIGEST_CACHE */
|
||||
}
|
||||
|
||||
/* Check for the existence of the replay dbm file, and create it if it does
|
||||
* not exist. Returns the number of db entries or -1 on error.
|
||||
*/
|
||||
int
|
||||
replay_db_init(fko_srv_options_t *opts)
|
||||
{
|
||||
#ifdef NO_DIGEST_CACHE
|
||||
return 0;
|
||||
#else
|
||||
|
||||
#ifdef HAVE_LIBGDBM
|
||||
GDBM_FILE rpdb;
|
||||
#elif HAVE_LIBNDBM
|
||||
DBM *rpdb;
|
||||
#endif
|
||||
|
||||
datum db_key, db_ent, db_next_key;
|
||||
int db_count = 0;
|
||||
|
||||
/* If rotation was specified, do it.
|
||||
*/
|
||||
if(opts->rotate_digest_cache)
|
||||
rotate_digest_cache_file(opts);
|
||||
|
||||
#ifdef HAVE_LIBGDBM
|
||||
rpdb = gdbm_open(
|
||||
opts->config[CONF_DIGEST_FILE], 512, GDBM_WRCREAT, S_IRUSR|S_IWUSR, 0
|
||||
);
|
||||
#elif HAVE_LIBNDBM
|
||||
rpdb = dbm_open(
|
||||
opts->config[CONF_DIGEST_FILE], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR
|
||||
);
|
||||
#endif
|
||||
|
||||
if(!rpdb)
|
||||
{
|
||||
log_msg(LOG_ERR,
|
||||
"Unable to open digest cache file: '%s': %s",
|
||||
opts->config[CONF_DIGEST_FILE],
|
||||
MY_DBM_STRERROR(errno)
|
||||
);
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBGDBM
|
||||
db_key = gdbm_firstkey(rpdb);
|
||||
|
||||
while (db_key.dptr != NULL)
|
||||
{
|
||||
db_count++;
|
||||
db_next_key = gdbm_nextkey(rpdb, db_key);
|
||||
free(db_key.dptr);
|
||||
db_key = db_next_key;
|
||||
}
|
||||
#elif HAVE_LIBNDBM
|
||||
for (db_key = dbm_firstkey(rpdb); db_ent.dptr != NULL; db_key = dbm_nextkey(rpdb))
|
||||
db_count++;
|
||||
#endif
|
||||
|
||||
MY_DBM_CLOSE(rpdb);
|
||||
|
||||
return(db_count);
|
||||
#endif /* NO_DIGEST_CACHE */
|
||||
}
|
||||
|
||||
/* Take an fko context, pull the digest and use it as the key to check the
|
||||
* replay db (digest cache). Returns 1 if there was a match (a replay),
|
||||
* 0 for no match, and -1 on error.
|
||||
*/
|
||||
int
|
||||
replay_check(fko_srv_options_t *opts, fko_ctx_t ctx)
|
||||
{
|
||||
#ifdef NO_DIGEST_CACHE
|
||||
return 0;
|
||||
#else
|
||||
|
||||
#ifdef HAVE_LIBGDBM
|
||||
GDBM_FILE rpdb;
|
||||
#elif HAVE_LIBNDBM
|
||||
DBM *rpdb;
|
||||
#endif
|
||||
datum db_key, db_ent;
|
||||
|
||||
char created[18], first[18], last[18];
|
||||
|
||||
char curr_ip[INET_ADDRSTRLEN+1] = {0};
|
||||
char last_ip[INET_ADDRSTRLEN+1] = {0};
|
||||
|
||||
char *digest;
|
||||
int digest_len, res;
|
||||
|
||||
digest_cache_info_t dc_info, *dci_p;
|
||||
|
||||
res = fko_get_spa_digest(ctx, &digest);
|
||||
if(res != FKO_SUCCESS)
|
||||
{
|
||||
log_msg(LOG_WARNING, "Error getting digest from SPA data: %s",
|
||||
fko_errstr(res));
|
||||
|
||||
return(SPA_MSG_DIGEST_ERROR);
|
||||
}
|
||||
|
||||
digest_len = strlen(digest);
|
||||
|
||||
db_key.dptr = digest;
|
||||
db_key.dsize = digest_len;
|
||||
|
||||
/* Check the db for the key
|
||||
*/
|
||||
#ifdef HAVE_LIBGDBM
|
||||
rpdb = gdbm_open(
|
||||
opts->config[CONF_DIGEST_FILE], 512, GDBM_WRCREAT, S_IRUSR|S_IWUSR, 0
|
||||
);
|
||||
#elif HAVE_LIBNDBM
|
||||
rpdb = dbm_open(opts->config[CONF_DIGEST_FILE], O_RDWR, 0);
|
||||
#endif
|
||||
|
||||
if(!rpdb)
|
||||
{
|
||||
log_msg(LOG_WARNING, "Error opening digest_cache: '%s': %s",
|
||||
opts->config[CONF_DIGEST_FILE],
|
||||
MY_DBM_STRERROR(errno)
|
||||
);
|
||||
|
||||
return(SPA_MSG_DIGEST_CACHE_ERROR);
|
||||
}
|
||||
|
||||
db_ent = MY_DBM_FETCH(rpdb, db_key);
|
||||
|
||||
/* If the datum is not null, we have a match. Otherwise, we add
|
||||
* this entry to the cache.
|
||||
*/
|
||||
if(db_ent.dptr != NULL)
|
||||
{
|
||||
dci_p = (digest_cache_info_t *)db_ent.dptr;
|
||||
|
||||
/* Convert the IPs to a human readable form
|
||||
*/
|
||||
inet_ntop(AF_INET, &(opts->spa_pkt.packet_src_ip),
|
||||
curr_ip, INET_ADDRSTRLEN);
|
||||
inet_ntop(AF_INET, &(dci_p->src_ip), last_ip, INET_ADDRSTRLEN);
|
||||
|
||||
/* Mark the last_replay time.
|
||||
*/
|
||||
dci_p->last_replay = time(NULL);
|
||||
|
||||
/* Increment the replay count and check to see if it is the first one.
|
||||
*/
|
||||
if(++(dci_p->replay_count) == 1)
|
||||
{
|
||||
/* This is the first replay so make it the same as last_replay
|
||||
*/
|
||||
dci_p->first_replay = dci_p->last_replay;
|
||||
}
|
||||
|
||||
strftime(created, 18, "%D %H:%M:%S", localtime(&(dci_p->created)));
|
||||
strftime(first, 18, "%D %H:%M:%S", localtime(&(dci_p->first_replay)));
|
||||
strftime(last, 18, "%D %H:%M:%S", localtime(&(dci_p->last_replay)));
|
||||
|
||||
log_msg(LOG_WARNING,
|
||||
"Replay detected from source IP: %s\n"
|
||||
" Original source IP: %s\n"
|
||||
" Entry created: %s\n"
|
||||
" First replay: %s\n"
|
||||
" Last replay: %s\n"
|
||||
" Replay count: %i\n",
|
||||
curr_ip, last_ip,
|
||||
created,
|
||||
first,
|
||||
last,
|
||||
dci_p->replay_count
|
||||
);
|
||||
|
||||
/* Save it back to the digest cache
|
||||
*/
|
||||
if(MY_DBM_STORE(rpdb, db_key, db_ent, MY_DBM_REPLACE) != 0)
|
||||
log_msg(LOG_WARNING, "Error updating entry in digest_cache: '%s': %s",
|
||||
opts->config[CONF_DIGEST_FILE],
|
||||
MY_DBM_STRERROR(errno)
|
||||
);
|
||||
|
||||
#ifdef HAVE_LIBGDBM
|
||||
free(db_ent.dptr);
|
||||
#endif
|
||||
|
||||
res = SPA_MSG_REPLAY;
|
||||
} else {
|
||||
/* This is a new SPA packet that needs to be added to the cache.
|
||||
*/
|
||||
dc_info.src_ip = opts->spa_pkt.packet_src_ip;
|
||||
dc_info.created = time(NULL);
|
||||
dc_info.first_replay = dc_info.last_replay = dc_info.replay_count = 0;
|
||||
|
||||
db_ent.dsize = sizeof(digest_cache_info_t);
|
||||
db_ent.dptr = (char*)&(dc_info);
|
||||
|
||||
if(MY_DBM_STORE(rpdb, db_key, db_ent, MY_DBM_INSERT) != 0)
|
||||
{
|
||||
log_msg(LOG_WARNING, "Error adding entry digest_cache: %s",
|
||||
MY_DBM_STRERROR(errno)
|
||||
);
|
||||
|
||||
res = SPA_MSG_DIGEST_CACHE_ERROR;
|
||||
}
|
||||
|
||||
res = SPA_MSG_SUCCESS;
|
||||
}
|
||||
|
||||
MY_DBM_CLOSE(rpdb);
|
||||
|
||||
return(res);
|
||||
#endif /* NO_DIGEST_CACHE */
|
||||
}
|
||||
|
||||
/***EOF***/
|
||||
Loading…
x
Reference in New Issue
Block a user