| /* $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; |
| } |
| |