#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <paths.h>
#include "app.h"
#include "conf.h"
#include "connection.h"
#include "init.h"
#include "libcrypto.h"
#include "log.h"
#include "message.h"
#include "monitor.h"
#include "nat_traversal.h"
#include "sa.h"
#include "timer.h"
#include "transport.h"
#include "udp.h"
#include "udp_encap.h"
#include "ui.h"
#include "util.h"
#include "cert.h"
#include "policy.h"
static void usage(void);
int debug = 0;
int acquire_only = 0;
int delete_sas = 1;
volatile sig_atomic_t sighupped = 0;
volatile sig_atomic_t sigusr1ed = 0;
static char *report_file = "/var/run/isakmpd.report";
volatile sig_atomic_t sigtermed = 0;
void daemon_shutdown_now(int);
void set_slave_signals(void);
void sanitise_stdfd(void);
char *pid_file = "/var/run/isakmpd.pid";
static char *pcap_file = 0;
static void
usage(void)
{
extern char *__progname;
fprintf(stderr,
"usage: %s [-46adKLnSTv] [-c config-file] [-D class=level] [-f fifo]\n"
" [-i pid-file] [-l packetlog-file] [-N udpencap-port]\n"
" [-p listen-port] [-R report-file]\n",
__progname);
exit(1);
}
static void
parse_args(int argc, char *argv[])
{
int ch;
int cls, level;
int do_packetlog = 0;
while ((ch = getopt(argc, argv, "46ac:dD:f:i:KnN:p:Ll:R:STv")) != -1) {
switch (ch) {
case '4':
bind_family |= BIND_FAMILY_INET4;
break;
case '6':
bind_family |= BIND_FAMILY_INET6;
break;
case 'a':
acquire_only = 1;
break;
case 'c':
conf_path = optarg;
break;
case 'd':
debug++;
break;
case 'D':
if (sscanf(optarg, "%d=%d", &cls, &level) != 2) {
if (sscanf(optarg, "A=%d", &level) == 1) {
for (cls = 0; cls < LOG_ENDCLASS;
cls++)
log_debug_cmd(cls, level);
} else
log_print("parse_args: -D argument "
"unparseable: %s", optarg);
} else
log_debug_cmd(cls, level);
break;
case 'f':
ui_fifo = optarg;
break;
case 'i':
pid_file = optarg;
break;
case 'K':
ignore_policy++;
break;
case 'n':
app_none++;
break;
case 'N':
udp_encap_default_port = optarg;
break;
case 'p':
udp_default_port = optarg;
break;
case 'l':
pcap_file = optarg;
case 'L':
do_packetlog++;
break;
case 'R':
report_file = optarg;
break;
case 'S':
delete_sas = 0;
ui_daemon_passive = 1;
break;
case 'T':
disable_nat_t = 1;
break;
case 'v':
verbose_logging = 1;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc > 0)
usage();
if (do_packetlog && !pcap_file)
pcap_file = PCAP_FILE_DEFAULT;
}
static void
sighup(int sig)
{
sighupped = 1;
}
static void
report(void)
{
FILE *rfp, *old;
mode_t old_umask;
old_umask = umask(S_IRWXG | S_IRWXO);
rfp = monitor_fopen(report_file, "w");
umask(old_umask);
if (!rfp) {
log_error("report: fopen (\"%s\", \"w\") failed", report_file);
return;
}
old = log_current();
log_to(rfp);
ui_report("r");
log_to(old);
fclose(rfp);
}
static void
sigusr1(int sig)
{
sigusr1ed = 1;
}
static int
phase2_sa_check(struct sa *sa, void *arg)
{
return sa->phase == 2;
}
static int
phase1_sa_check(struct sa *sa, void *arg)
{
return sa->phase == 1;
}
void
set_slave_signals(void)
{
int n;
for (n = 1; n < _NSIG; n++)
signal(n, SIG_DFL);
signal(SIGTERM, daemon_shutdown_now);
if (debug == 1)
signal(SIGINT, daemon_shutdown_now);
signal(SIGHUP, sighup);
signal(SIGUSR1, sigusr1);
}
static void
daemon_shutdown(void)
{
struct sa *sa;
if (sigtermed == 1) {
log_print("isakmpd: shutting down...");
if (delete_sas &&
strncmp("no", conf_get_str("General", "Delete-SAs"), 2)) {
while ((sa = sa_find(phase2_sa_check, NULL)))
sa_delete(sa, 1);
while ((sa = sa_find(phase1_sa_check, NULL)))
sa_delete(sa, 1);
}
sigtermed++;
}
if (transport_prio_sendqs_empty()) {
log_packet_stop();
log_print("isakmpd: exit");
exit(0);
}
}
void
daemon_shutdown_now(int sig)
{
sigtermed = 1;
}
static void
write_pid_file(void)
{
FILE *fp;
unlink(pid_file);
fp = fopen(pid_file, "w");
if (fp != NULL) {
if (fprintf(fp, "%ld\n", (long) getpid()) < 0)
log_error("write_pid_file: failed to write PID to "
"\"%.100s\"", pid_file);
fclose(fp);
} else
log_fatal("write_pid_file: fopen (\"%.100s\", \"w\") failed",
pid_file);
}
void
sanitise_stdfd(void)
{
int nullfd, dupfd;
if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
fprintf(stderr, "Couldn't open /dev/null: %s\n",
strerror(errno));
exit(1);
}
while (++dupfd <= STDERR_FILENO) {
if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) {
if (dup2(nullfd, dupfd) == -1) {
fprintf(stderr, "dup2: %s\n", strerror(errno));
exit(1);
}
}
}
if (nullfd > STDERR_FILENO)
close(nullfd);
}
int
main(int argc, char *argv[])
{
fd_set *rfds, *wfds;
int n, m;
size_t mask_size;
struct timespec ts, *timeout;
closefrom(STDERR_FILENO + 1);
sanitise_stdfd();
log_to(stderr);
parse_args(argc, argv);
log_init(debug);
log_print("isakmpd: starting");
setprotoent(1);
setservent(1);
ui_init();
set_slave_signals();
if (!debug)
if (daemon(0, 0))
log_fatal("main: daemon (0, 0) failed");
tzset();
write_pid_file();
if (monitor_init(debug)) {
monitor_loop(debug);
exit(0);
}
init();
if (pcap_file != 0)
log_packet_init(pcap_file);
n = getdtablesize();
mask_size = howmany(n, NFDBITS) * sizeof(fd_mask);
rfds = malloc(mask_size);
if (!rfds)
log_fatal("main: malloc (%lu) failed",
(unsigned long)mask_size);
wfds = malloc(mask_size);
if (!wfds)
log_fatal("main: malloc (%lu) failed",
(unsigned long)mask_size);
monitor_init_done();
while (1) {
if (sighupped) {
sighupped = 0;
log_print("SIGHUP received");
reinit();
}
if (sigusr1ed) {
sigusr1ed = 0;
log_print("SIGUSR1 received");
report();
}
if (sigtermed)
daemon_shutdown();
bzero(rfds, mask_size);
n = transport_fd_set(rfds);
FD_SET(ui_socket, rfds);
if (ui_socket + 1 > n)
n = ui_socket + 1;
if (!app_none && app_socket >= 0) {
FD_SET(app_socket, rfds);
if (app_socket + 1 > n)
n = app_socket + 1;
}
bzero(wfds, mask_size);
m = transport_pending_wfd_set(wfds);
if (m > n)
n = m;
timeout = &ts;
timer_next_event(&timeout);
n = pselect(n, rfds, wfds, NULL, timeout, NULL);
if (n == -1) {
if (errno != EINTR) {
log_error("main: select");
sleep(1);
}
} else if (n) {
transport_handle_messages(rfds);
transport_send_messages(wfds);
if (FD_ISSET(ui_socket, rfds))
ui_handler();
if (!app_none && app_socket >= 0 &&
FD_ISSET(app_socket, rfds))
app_handler();
}
timer_handle_expirations();
}
}