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