diff options
Diffstat (limited to 'ipsec-tools/src/racoon/session.c')
-rw-r--r-- | ipsec-tools/src/racoon/session.c | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/ipsec-tools/src/racoon/session.c b/ipsec-tools/src/racoon/session.c new file mode 100644 index 00000000..85e29a39 --- /dev/null +++ b/ipsec-tools/src/racoon/session.c @@ -0,0 +1,540 @@ +/* $NetBSD: session.c,v 1.32 2011/03/02 15:09:16 vanhu Exp $ */ + +/* $KAME: session.c,v 1.32 2003/09/24 02:01:17 jinmei Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#if HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif +#ifndef WEXITSTATUS +# define WEXITSTATUS(s) ((unsigned)(s) >> 8) +#endif +#ifndef WIFEXITED +# define WIFEXITED(s) (((s) & 255) == 0) +#endif + +#include PATH_IPSEC_H + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <signal.h> +#include <sys/stat.h> +#include <paths.h> +#include <err.h> + +#include <netinet/in.h> +#include <resolv.h> + +#include "libpfkey.h" + +#include "var.h" +#include "misc.h" +#include "vmbuf.h" +#include "plog.h" +#include "debug.h" + +#include "schedule.h" +#include "session.h" +#include "grabmyaddr.h" +#include "evt.h" +#include "cfparse_proto.h" +#include "isakmp_var.h" +#include "isakmp.h" +#include "isakmp_var.h" +#include "isakmp_xauth.h" +#include "isakmp_cfg.h" +#include "admin_var.h" +#include "admin.h" +#include "privsep.h" +#include "oakley.h" +#include "pfkey.h" +#include "handler.h" +#include "localconf.h" +#include "remoteconf.h" +#include "backupsa.h" +#include "remoteconf.h" +#ifdef ENABLE_NATT +#include "nattraversal.h" +#endif + +#include "algorithm.h" /* XXX ??? */ + +#include "sainfo.h" + +struct fd_monitor { + int (*callback)(void *ctx, int fd); + void *ctx; + int prio; + int fd; + TAILQ_ENTRY(fd_monitor) chain; +}; + +#define NUM_PRIORITIES 2 + +static void close_session __P((void)); +static void initfds __P((void)); +static void init_signal __P((void)); +static int set_signal __P((int sig, RETSIGTYPE (*func) __P((int)))); +static void check_sigreq __P((void)); +static void check_flushsa __P((void)); +static int close_sockets __P((void)); + +static fd_set preset_mask, active_mask; +static struct fd_monitor fd_monitors[FD_SETSIZE]; +static TAILQ_HEAD(fd_monitor_list, fd_monitor) fd_monitor_tree[NUM_PRIORITIES]; +static int nfds = 0; + +static volatile sig_atomic_t sigreq[NSIG + 1]; +static struct sched scflushsa = SCHED_INITIALIZER(); + +void +monitor_fd(int fd, int (*callback)(void *, int), void *ctx, int priority) +{ + if (fd < 0 || fd >= FD_SETSIZE) { + plog(LLV_ERROR, LOCATION, NULL, "fd_set overrun"); + exit(1); + } + + FD_SET(fd, &preset_mask); + if (fd > nfds) + nfds = fd; + if (priority <= 0) + priority = 0; + if (priority >= NUM_PRIORITIES) + priority = NUM_PRIORITIES - 1; + + fd_monitors[fd].callback = callback; + fd_monitors[fd].ctx = ctx; + fd_monitors[fd].prio = priority; + fd_monitors[fd].fd = fd; + TAILQ_INSERT_TAIL(&fd_monitor_tree[priority], + &fd_monitors[fd], chain); +} + +void +unmonitor_fd(int fd) +{ + if (fd < 0 || fd >= FD_SETSIZE) { + plog(LLV_ERROR, LOCATION, NULL, "fd_set overrun"); + exit(1); + } + + if (fd_monitors[fd].callback == NULL) + return; + + FD_CLR(fd, &preset_mask); + FD_CLR(fd, &active_mask); + fd_monitors[fd].callback = NULL; + fd_monitors[fd].ctx = NULL; + TAILQ_REMOVE(&fd_monitor_tree[fd_monitors[fd].prio], + &fd_monitors[fd], chain); +} + +int +session(void) +{ + struct timeval *timeout; + int error; + char pid_file[MAXPATHLEN]; + FILE *fp; + pid_t racoon_pid = 0; + int i, count; + struct fd_monitor *fdm; + + nfds = 0; + FD_ZERO(&preset_mask); + + for (i = 0; i < NUM_PRIORITIES; i++) + TAILQ_INIT(&fd_monitor_tree[i]); + + /* initialize schedular */ + sched_init(); + init_signal(); + + if (pfkey_init() < 0) + errx(1, "failed to initialize pfkey socket"); + + if (isakmp_init() < 0) + errx(1, "failed to initialize ISAKMP structures"); + +#ifdef ENABLE_HYBRID + if (isakmp_cfg_init(ISAKMP_CFG_INIT_COLD)) + errx(1, "could not initialize ISAKMP mode config structures"); +#endif + +#ifdef HAVE_LIBLDAP + if (xauth_ldap_init_conf() != 0) + errx(1, "could not initialize ldap config"); +#endif + +#ifdef HAVE_LIBRADIUS + if (xauth_radius_init_conf(0) != 0) + errx(1, "could not initialize radius config"); +#endif + + myaddr_init_lists(); + + /* + * in order to prefer the parameters by command line, + * saving some parameters before parsing configuration file. + */ + save_params(); + if (cfparse() != 0) + errx(1, "failed to parse configuration file."); + restore_params(); + +#ifdef ENABLE_ADMINPORT + if (admin_init() < 0) + errx(1, "failed to initialize admin port socket"); +#endif + + +#ifdef ENABLE_HYBRID + if(isakmp_cfg_config.network4 && isakmp_cfg_config.pool_size == 0) + if ((error = isakmp_cfg_resize_pool(ISAKMP_CFG_MAX_CNX)) != 0) + return error; +#endif + + if (dump_config) + dumprmconf(); + +#ifdef HAVE_LIBRADIUS + if (xauth_radius_init() != 0) + errx(1, "could not initialize libradius"); +#endif + + if (myaddr_init() != 0) + errx(1, "failed to listen to configured addresses"); + myaddr_sync(); + +#ifdef ENABLE_NATT + natt_keepalive_init (); +#endif + + /* write .pid file */ + if (lcconf->pathinfo[LC_PATHTYPE_PIDFILE] == NULL) + strlcpy(pid_file, _PATH_VARRUN "racoon.pid", MAXPATHLEN); + else if (lcconf->pathinfo[LC_PATHTYPE_PIDFILE][0] == '/') + strlcpy(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], MAXPATHLEN); + else { + strlcat(pid_file, _PATH_VARRUN, MAXPATHLEN); + strlcat(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], MAXPATHLEN); + } + fp = fopen(pid_file, "w"); + if (fp) { + if (fchmod(fileno(fp), + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) { + syslog(LOG_ERR, "%s", strerror(errno)); + fclose(fp); + exit(1); + } + } else { + plog(LLV_ERROR, LOCATION, NULL, + "cannot open %s", pid_file); + } + + if (privsep_init() != 0) + exit(1); + + /* + * The fork()'ed privileged side will close its copy of fp. We wait + * until here to get the correct child pid. + */ + racoon_pid = getpid(); + fprintf(fp, "%ld\n", (long)racoon_pid); + fclose(fp); + + for (i = 0; i <= NSIG; i++) + sigreq[i] = 0; + + while (1) { + /* + * asynchronous requests via signal. + * make sure to reset sigreq to 0. + */ + check_sigreq(); + + /* scheduling */ + timeout = schedular(); + + /* schedular can change select() mask, so we reset + * the working copy here */ + active_mask = preset_mask; + + error = select(nfds + 1, &active_mask, NULL, NULL, timeout); + if (error < 0) { + switch (errno) { + case EINTR: + continue; + default: + plog(LLV_ERROR, LOCATION, NULL, + "failed to select (%s)\n", + strerror(errno)); + return -1; + } + /*NOTREACHED*/ + } + + count = 0; + for (i = 0; i < NUM_PRIORITIES; i++) { + TAILQ_FOREACH(fdm, &fd_monitor_tree[i], chain) { + if (!FD_ISSET(fdm->fd, &active_mask)) + continue; + + FD_CLR(fdm->fd, &active_mask); + if (fdm->callback != NULL) { + fdm->callback(fdm->ctx, fdm->fd); + count++; + } else + plog(LLV_ERROR, LOCATION, NULL, + "fd %d set, but no active callback\n", i); + } + if (count != 0) + break; + } + + } +} + +/* clear all status and exit program. */ +static void +close_session() +{ + evt_generic(EVT_RACOON_QUIT, NULL); + pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC); + flushph2(); + flushph1(); + flushrmconf(); + flushsainfo(); + close_sockets(); + backupsa_clean(); + + plog(LLV_INFO, LOCATION, NULL, "racoon process %d shutdown\n", getpid()); + + exit(0); +} + +static int signals[] = { + SIGHUP, + SIGINT, + SIGTERM, + SIGUSR1, + SIGUSR2, + SIGCHLD, + 0 +}; + +/* + * asynchronous requests will actually dispatched in the + * main loop in session(). + */ +RETSIGTYPE +signal_handler(sig) + int sig; +{ + sigreq[sig] = 1; +} + + +/* XXX possible mem leaks and no way to go back for now !!! + */ +static void reload_conf(){ + int error; + +#ifdef ENABLE_HYBRID + if ((isakmp_cfg_init(ISAKMP_CFG_INIT_WARM)) != 0) { + plog(LLV_ERROR, LOCATION, NULL, + "ISAKMP mode config structure reset failed, " + "not reloading\n"); + return; + } +#endif + + sainfo_start_reload(); + + /* TODO: save / restore / flush old lcconf (?) / rmtree + */ + rmconf_start_reload(); + +#ifdef HAVE_LIBRADIUS + /* free and init radius configuration */ + xauth_radius_init_conf(1); +#endif + + pfkey_reload(); + + save_params(); + flushlcconf(); + error = cfparse(); + if (error != 0){ + plog(LLV_ERROR, LOCATION, NULL, "config reload failed\n"); + /* We are probably in an inconsistant state... */ + return; + } + restore_params(); + +#if 0 + if (dump_config) + dumprmconf (); +#endif + + myaddr_sync(); + +#ifdef HAVE_LIBRADIUS + /* re-initialize radius state */ + xauth_radius_init(); +#endif + + /* Revalidate ph1 / ph2tree !!! + * update ctdtree if removing some ph1 ! + */ + revalidate_ph12(); + /* Update ctdtree ? + */ + + sainfo_finish_reload(); + rmconf_finish_reload(); +} + +static void +check_sigreq() +{ + int sig, s; + + for (sig = 0; sig <= NSIG; sig++) { + if (sigreq[sig] == 0) + continue; + sigreq[sig] = 0; + + switch(sig) { + case 0: + return; + + case SIGCHLD: + /* Reap all pending children */ + while (waitpid(-1, &s, WNOHANG) > 0) + ; + break; + +#ifdef DEBUG_RECORD_MALLOCATION + /* + * XXX This operation is signal handler unsafe and may lead to + * crashes and security breaches: See Henning Brauer talk at + * EuroBSDCon 2005. Do not run in production with this option + * enabled. + */ + case SIGUSR2: + DRM_dump(); + break; +#endif + + case SIGHUP: + /* Save old configuration, load new one... */ + reload_conf(); + break; + + case SIGINT: + case SIGTERM: + plog(LLV_INFO, LOCATION, NULL, + "caught signal %d\n", sig); + close_session(); + break; + + default: + plog(LLV_INFO, LOCATION, NULL, + "caught signal %d\n", sig); + break; + } + } +} + +static void +init_signal() +{ + int i; + + /* + * Ignore SIGPIPE as we check the return value of system calls + * that write to pipe-like fds. + */ + signal(SIGPIPE, SIG_IGN); + + for (i = 0; signals[i] != 0; i++) + if (set_signal(signals[i], signal_handler) < 0) { + plog(LLV_ERROR, LOCATION, NULL, + "failed to set_signal (%s)\n", + strerror(errno)); + exit(1); + } +} + +static int +set_signal(sig, func) + int sig; + RETSIGTYPE (*func) __P((int)); +{ + struct sigaction sa; + + memset((caddr_t)&sa, 0, sizeof(sa)); + sa.sa_handler = func; + sa.sa_flags = SA_RESTART; + + if (sigemptyset(&sa.sa_mask) < 0) + return -1; + + if (sigaction(sig, &sa, (struct sigaction *)0) < 0) + return(-1); + + return 0; +} + +static int +close_sockets() +{ + myaddr_close(); + pfkey_close(lcconf->sock_pfkey); +#ifdef ENABLE_ADMINPORT + (void)admin_close(); +#endif + return 0; +} + |