/* $NetBSD: session.c,v 1.7.6.2 2007/08/01 11:52:22 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 <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_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" #ifdef ENABLE_NATT #include "nattraversal.h" #endif #include "algorithm.h" /* XXX ??? */ #include "sainfo.h" static void close_session __P((void)); static void check_rtsock __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_stub __P((void *)); static void check_flushsa __P((void)); static int close_sockets __P((void)); static fd_set mask0; static fd_set maskdying; static int nfds = 0; static volatile sig_atomic_t sigreq[NSIG + 1]; static int dying = 0; int session(void) { fd_set rfds; struct timeval *timeout; int error; struct myaddrs *p; char pid_file[MAXPATHLEN]; FILE *fp; pid_t racoon_pid = 0; int i; /* initialize schedular */ sched_init(); init_signal(); #ifdef ENABLE_ADMINPORT if (admin_init() < 0) exit(1); #endif initmyaddr(); if (isakmp_init() < 0) exit(1); initfds(); #ifdef ENABLE_NATT natt_keepalive_init (); #endif if (privsep_init() != 0) exit(1); for (i = 0; i <= NSIG; i++) sigreq[i] = 0; /* write .pid file */ racoon_pid = getpid(); 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); } fprintf(fp, "%ld\n", (long)racoon_pid); fclose(fp); } else { plog(LLV_ERROR, LOCATION, NULL, "cannot open %s", pid_file); } while (1) { if (dying) rfds = maskdying; else rfds = mask0; /* * asynchronous requests via signal. * make sure to reset sigreq to 0. */ check_sigreq(); /* scheduling */ timeout = schedular(); error = select(nfds, &rfds, (fd_set *)0, (fd_set *)0, 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*/ } #ifdef ENABLE_ADMINPORT if ((lcconf->sock_admin != -1) && (FD_ISSET(lcconf->sock_admin, &rfds))) admin_handler(); #endif for (p = lcconf->myaddrs; p; p = p->next) { if (!p->addr) continue; if (FD_ISSET(p->sock, &rfds)) isakmp_handler(p->sock); } if (FD_ISSET(lcconf->sock_pfkey, &rfds)) pfkey_handler(); if (lcconf->rtsock >= 0 && FD_ISSET(lcconf->rtsock, &rfds)) { if (update_myaddrs() && lcconf->autograbaddr) check_rtsock(NULL); else initfds(); } } } /* clear all status and exit program. */ static void close_session() { #ifdef ENABLE_FASTQUIT flushph2(); #endif flushph1(); close_sockets(); backupsa_clean(); plog(LLV_INFO, LOCATION, NULL, "racoon shutdown\n"); exit(0); } static void check_rtsock(unused) void *unused; { isakmp_close(); grab_myaddrs(); autoconf_myaddrsport(); isakmp_open(); /* initialize socket list again */ initfds(); } static void initfds() { struct myaddrs *p; nfds = 0; FD_ZERO(&mask0); FD_ZERO(&maskdying); #ifdef ENABLE_ADMINPORT if (lcconf->sock_admin != -1) { if (lcconf->sock_admin >= FD_SETSIZE) { plog(LLV_ERROR, LOCATION, NULL, "fd_set overrun\n"); exit(1); } FD_SET(lcconf->sock_admin, &mask0); /* XXX should we listen on admin socket when dying ? */ #if 0 FD_SET(lcconf->sock_admin, &maskdying); #endif nfds = (nfds > lcconf->sock_admin ? nfds : lcconf->sock_admin); } #endif if (lcconf->sock_pfkey >= FD_SETSIZE) { plog(LLV_ERROR, LOCATION, NULL, "fd_set overrun\n"); exit(1); } FD_SET(lcconf->sock_pfkey, &mask0); FD_SET(lcconf->sock_pfkey, &maskdying); nfds = (nfds > lcconf->sock_pfkey ? nfds : lcconf->sock_pfkey); if (lcconf->rtsock >= 0) { if (lcconf->rtsock >= FD_SETSIZE) { plog(LLV_ERROR, LOCATION, NULL, "fd_set overrun\n"); exit(1); } FD_SET(lcconf->rtsock, &mask0); nfds = (nfds > lcconf->rtsock ? nfds : lcconf->rtsock); } for (p = lcconf->myaddrs; p; p = p->next) { if (!p->addr) continue; if (p->sock >= FD_SETSIZE) { plog(LLV_ERROR, LOCATION, NULL, "fd_set overrun\n"); exit(1); } FD_SET(p->sock, &mask0); nfds = (nfds > p->sock ? nfds : p->sock); } nfds++; } 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; { /* Do not just set it to 1, because we may miss some signals by just setting * values to 0/1 */ sigreq[sig]++; } /* 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 save_sainfotree(); /* TODO: save / restore / flush old lcconf (?) / rmtree */ /* initlcconf();*/ /* racoon_conf ? ! */ save_rmconf(); initrmconf(); /* Do a part of pfkey_init() ? * SPD reload ? */ save_params(); 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 /* * init_myaddr() ? * If running in privilege separation, do not reinitialize * the IKE listener, as we will not have the right to * setsockopt(IP_IPSEC_POLICY). */ if (geteuid() == 0) check_rtsock(NULL); /* Revalidate ph1 / ph2tree !!! * update ctdtree if removing some ph1 ! */ revalidate_ph12(); /* Update ctdtree ? */ save_sainfotree_flush(); save_rmconf_flush(); } static void check_sigreq() { int sig; /* * XXX We are not able to tell if we got * several time the same signal. This is * not a problem for the current code, * but we shall remember this limitation. */ for (sig = 0; sig <= NSIG; sig++) { if (sigreq[sig] == 0) continue; sigreq[sig]--; switch(sig) { case 0: return; /* Catch up childs, mainly scripts. */ case SIGCHLD: { pid_t pid; int s; pid = wait(&s); } 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); EVT_PUSH(NULL, NULL, EVTT_RACOON_QUIT, NULL); pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC); #ifdef ENABLE_FASTQUIT close_session(); #else sched_new(1, check_flushsa_stub, NULL); #endif dying = 1; break; default: plog(LLV_INFO, LOCATION, NULL, "caught signal %d\n", sig); break; } } } /* * waiting the termination of processing until sending DELETE message * for all inbound SA will complete. */ static void check_flushsa_stub(p) void *p; { check_flushsa(); } static void check_flushsa() { vchar_t *buf; struct sadb_msg *msg, *end, *next; struct sadb_sa *sa; caddr_t mhp[SADB_EXT_MAX + 1]; int n; buf = pfkey_dump_sadb(SADB_SATYPE_UNSPEC); if (buf == NULL) { plog(LLV_DEBUG, LOCATION, NULL, "pfkey_dump_sadb: returned nothing.\n"); return; } msg = (struct sadb_msg *)buf->v; end = (struct sadb_msg *)(buf->v + buf->l); /* counting SA except of dead one. */ n = 0; while (msg < end) { if (PFKEY_UNUNIT64(msg->sadb_msg_len) < sizeof(*msg)) break; next = (struct sadb_msg *)((caddr_t)msg + PFKEY_UNUNIT64(msg->sadb_msg_len)); if (msg->sadb_msg_type != SADB_DUMP) { msg = next; continue; } if (pfkey_align(msg, mhp) || pfkey_check(mhp)) { plog(LLV_ERROR, LOCATION, NULL, "pfkey_check (%s)\n", ipsec_strerror()); msg = next; continue; } sa = (struct sadb_sa *)(mhp[SADB_EXT_SA]); if (!sa) { msg = next; continue; } if (sa->sadb_sa_state != SADB_SASTATE_DEAD) { n++; msg = next; continue; } msg = next; } if (buf != NULL) vfree(buf); if (n) { sched_new(1, check_flushsa_stub, NULL); return; } close_session(); } static void init_signal() { int i; 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() { isakmp_close(); pfkey_close(lcconf->sock_pfkey); #ifdef ENABLE_ADMINPORT (void)admin_close(); #endif return 0; }