]> git.saurik.com Git - apple/ipsec.git/blobdiff - ipsec-tools/racoon/session.c
ipsec-317.220.1.tar.gz
[apple/ipsec.git] / ipsec-tools / racoon / session.c
index 2f9f55786549e1249f315dd7da5088f83edc26f2..208ff5dcb110e78d81555a8ed83cd8b0a062dd4e 100644 (file)
@@ -1,3 +1,5 @@
+/*     $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 $        */
 
 /*
 #include <sys/stat.h>
 #include <paths.h>
 
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include <TargetConditionals.h>
+#include <vproc_priv.h>
+#include <dispatch/dispatch.h>
+#include <xpc/xpc.h>
+
 #include "libpfkey.h"
 
 #include "var.h"
 #include "vmbuf.h"
 #include "plog.h"
 #include "debug.h"
+#include "plog.h"
 
 #include "schedule.h"
 #include "session.h"
 #include "grabmyaddr.h"
-#include "evt.h"
 #include "cfparse_proto.h"
 #include "isakmp_var.h"
-#include "admin_var.h"
-#include "admin.h"
-#include "privsep.h"
+#include "isakmp_xauth.h"
+#include "isakmp_cfg.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 "vpn_control_var.h"
 #include "policy.h"
+#include "algorithm.h" /* XXX ??? */
+
+#include "sainfo.h"
+#include "power_mgmt.h"
+
+#include <NetworkExtension/NEPolicy.h>
+#include <sys/proc_info.h>
+#include <libproc.h>
+
 
 extern pid_t racoon_pid;
-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 void auto_exit_do __P((void *));
-static int close_sockets __P((void));
-
-static fd_set mask0;
-static fd_set maskdying;
-static int nfds = 0;
+extern int launchdlaunched;
+static void close_session (int);
+static int init_signal (void);
+static int set_signal (int sig, RETSIGTYPE (*func) (int, siginfo_t *, void *));
+static void check_flushsa_stub (void *);
+static void check_flushsa (void);
+static void auto_exit_do (void *);
+static int close_sockets (void);
+
 static volatile sig_atomic_t sigreq[NSIG + 1];
-static int dying = 0;
-static struct sched *check_rtsock_sched = NULL;
+int terminated = 0;
+int pending_signal_handle = 0;
+
+static int64_t racoon_keepalive = -1;
+
+dispatch_queue_t main_queue;
+
+static NEPolicySessionRef policySession = NULL;
+
+/*
+ * This is used to (manually) update racoon's launchd keepalive, which is needed because racoon is (mostly) 
+ * launched on demand and for <rdar://problem/8768510> requires a keepalive on dirty/failure exits.
+ * The launchd plist can't be used for this because RunOnLoad is required to have keepalive on a failure exit.
+ */
+int64_t
+launchd_update_racoon_keepalive (Boolean enabled)
+{
+       if (launchdlaunched) {          
+               int64_t     val = (__typeof__(val))enabled;
+               /* Set our own KEEPALIVE value */
+               if (vproc_swap_integer(NULL,
+                                                          VPROC_GSK_BASIC_KEEPALIVE,
+                                                          &val,
+                                                          &racoon_keepalive)) {
+                       plog(ASL_LEVEL_ERR, 
+                                "failed to swap launchd keepalive integer %d\n", enabled);
+               }
+       }
+       return racoon_keepalive;
+}
+
+static CFUUIDRef
+copy_racoon_proc_uuid(void)
+{
+       struct proc_uniqidentifierinfo procu;
+       CFUUIDBytes uuidBytes;
+       int size = 0;
+
+       memset(&procu, 0, sizeof(procu));
+       size = proc_pidinfo(getpid(), PROC_PIDUNIQIDENTIFIERINFO, 1, &procu, PROC_PIDUNIQIDENTIFIERINFO_SIZE);
+       if (size != PROC_PIDUNIQIDENTIFIERINFO_SIZE) {
+               return (NULL);
+       }
+
+       memcpy(&uuidBytes, procu.p_uuid, sizeof(CFUUIDBytes));
+       return CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, uuidBytes);
+}
+
+static bool
+policy_session_init(void)
+{
+       bool success = true;
+       policySession = NEPolicyCreateSession(kCFAllocatorDefault, CFSTR("racoon"), NULL, NULL);
+       if (policySession == NULL) {
+               return false;
+       }
+       
+       CFUUIDRef proc_uuid = copy_racoon_proc_uuid();
+       if (proc_uuid == NULL) {
+               return false;
+       }
+       
+       CFMutableArrayRef conditions = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+       if (conditions) {
+               CFMutableDictionaryRef uuidCondition = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+               if (uuidCondition) {
+                       CFDictionarySetValue(uuidCondition, kNEPolicyConditionType, kNEPolicyValPolicyConditionTypeApplication);
+                       CFDictionarySetValue(uuidCondition, kNEPolicyApplicationUUID, proc_uuid);
+                       CFArrayAppendValue(conditions, uuidCondition);
+                       CFRelease(uuidCondition);
+               } else {
+                       success = false;
+               }
+               
+               CFMutableDictionaryRef interfacesCondition = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+               if (interfacesCondition) {
+                       CFDictionarySetValue(interfacesCondition, kNEPolicyConditionType, kNEPolicyValPolicyConditionTypeAllInterfaces);
+                       CFArrayAppendValue(conditions, interfacesCondition);
+                       CFRelease(interfacesCondition);
+               } else {
+                       success = false;
+               }
+       } else {
+               success = false;
+       }
+       
+       CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+       if (result) {
+               CFDictionaryAddValue(result, kNEPolicyResult, kNEPolicyValPolicyResultPass);
+       } else {
+               success = false;
+       }
+       
+       if (success) {
+               success = (NEPolicyAdd(policySession, 0, conditions, result, NULL) != kNEPolicyIDInvalid);
+       }
+       
+       if (result) {
+               CFRelease(result);
+       }
+       if (conditions) {
+               CFRelease(conditions);
+       }
+       if (proc_uuid) {
+               CFRelease(proc_uuid);
+       }
+       
+       return (success && NEPolicyApply(policySession));
+}
 
-int
+//
+// Session
+// 
+// Initialize listening sockets, timers, vpn control etc.,
+// write the PID file and call dispatch_main.
+//
+void
 session(void)
 {
-       fd_set rfds;
-       struct timeval *timeout;
-       int error;
-       struct myaddrs *p;
        char pid_file[MAXPATHLEN];
        FILE *fp;
-       int i, update_fds;
+       int i;
+    
+    main_queue = dispatch_get_main_queue();
 
        /* initialize schedular */
        sched_init();
 
-       initmyaddr();
-
-       if (isakmp_init() < 0) {
-               plog(LLV_ERROR2, LOCATION, NULL,
-                               "failed to initialize isakmp");
+       /* needs to be called after schedular */
+       if (init_power_mgmt() < 0) {
+        plog(ASL_LEVEL_ERR, 
+             "failed to initialize power-mgmt.");
                exit(1);
        }
 
-#ifdef ENABLE_ADMINPORT
-       if (admin_init() < 0) {
-               plog(LLV_ERROR2, LOCATION, NULL,
-                               "failed to initialize admin port");
-               exit(1);
+    if (lcconf->autograbaddr == 1)
+        if (pfroute_init()) {
+            plog(ASL_LEVEL_ERR, "failed to initialize route socket.\n");
+            exit(1);
+        }
+       
+       if (!policy_session_init()) {
+               plog(ASL_LEVEL_ERR, "failed to initialize NEPolicy session.\n");
        }
-#endif
-#ifdef ENABLE_VPNCONTROL_PORT
-       if (vpncontrol_init() < 0) {
-               plog(LLV_ERROR2, LOCATION, NULL,
-                       "failed to initialize vpn control port");
+       
+    if (initmyaddr()) {
+        plog(ASL_LEVEL_ERR, "failed to initialize listening addresses.\n");
+        exit(1);
+    }
+       if (isakmp_init()) {
+               plog(ASL_LEVEL_ERR, "failed to initialize isakmp");
                exit(1);
+       }    
+#ifdef ENABLE_VPNCONTROL_PORT
+       if (vpncontrol_init()) {
+               plog(ASL_LEVEL_ERR, "failed to initialize vpn control port");
+               //exit(1);
        }
 #endif
 
-       init_signal();
-       initfds();
-
-#ifndef __APPLE__
-#ifdef ENABLE_NATT
-       natt_keepalive_init ();
-#endif
-#endif
-
-       if (privsep_init() != 0) {
-               plog(LLV_ERROR2, LOCATION, NULL,
-                       "failed to initialize privsep");
-               exit(1);
-       }
-
+       if (init_signal()) {
+        plog(ASL_LEVEL_ERR, "failed to initialize signals.\n");
+               exit(1); 
+    }
+    
        for (i = 0; i <= NSIG; i++)
                sigreq[i] = 0;
 
@@ -169,342 +292,67 @@ session(void)
        if (!f_foreground) {
                racoon_pid = getpid();
                if (lcconf->pathinfo[LC_PATHTYPE_PIDFILE] == NULL) 
-                       strlcpy(pid_file, _PATH_VARRUN "racoon.pid", MAXPATHLEN);
+                       strlcpy(pid_file, _PATH_VARRUN "racoon.pid", sizeof(pid_file));
                else if (lcconf->pathinfo[LC_PATHTYPE_PIDFILE][0] == '/') 
-                       strlcpy(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], MAXPATHLEN);
+                       strlcpy(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], sizeof(pid_file));
                else {
-                       strlcat(pid_file, _PATH_VARRUN, MAXPATHLEN);
-                       strlcat(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], MAXPATHLEN);
+                       strlcat(pid_file, _PATH_VARRUN, sizeof(pid_file));
+                       strlcat(pid_file, lcconf->pathinfo[LC_PATHTYPE_PIDFILE], sizeof(pid_file));
                } 
                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));
+                               plog(ASL_LEVEL_ERR, "%s", strerror(errno));
                                fclose(fp);
                                exit(1);
                        }
                        fprintf(fp, "%ld\n", (long)racoon_pid);
                        fclose(fp);
                } else {
-                       plog(LLV_ERROR, LOCATION, NULL,
+                       plog(ASL_LEVEL_ERR, 
                                "cannot open %s", pid_file);
                }
        }
-
-       while (1) {
-               if (!TAILQ_EMPTY(&lcconf->saved_msg_queue))
-                       pfkey_post_handler();
-               update_fds = 0;
-               /*
-                * asynchronous requests via signal.
-                * make sure to reset sigreq to 0.
-                */
-               check_sigreq();
-
-               /* scheduling */
-               timeout = schedular();
-
-               if (dying)
-                       rfds = maskdying;
-               else
-                       rfds = mask0;
-               error = select(nfds, &rfds, (fd_set *)0, (fd_set *)0, timeout);
-               if (error < 0) {
-                       switch (errno) {
-                       case EINTR:
-                               continue;
-                       default:
-                               plog(LLV_ERROR2, LOCATION, NULL,
-                                       "failed select (%s)\n",
-                                       strerror(errno));
-                               exit(1);
-                       }
-                       /*NOTREACHED*/
-               }
-
-#ifdef ENABLE_ADMINPORT
-               if ((lcconf->sock_admin != -1) &&
-                   (FD_ISSET(lcconf->sock_admin, &rfds)))
-                       admin_handler();
-#endif
-#ifdef ENABLE_VPNCONTROL_PORT
-               {
-                       struct vpnctl_socket_elem *elem;
-                       struct vpnctl_socket_elem *t_elem;
-                       
-                       if ((lcconf->sock_vpncontrol != -1) &&
-                               (FD_ISSET(lcconf->sock_vpncontrol, &rfds))) {
-                               vpncontrol_handler();
-                               update_fds = 1;                 //  in case new socket created - update mask
-                       }
-                       /* The handler may close and remove the list element
-                        * so we can't rely on it being valid after calling
-                        * the handler.
-                        */
-                       LIST_FOREACH_SAFE(elem, &lcconf->vpnctl_comm_socks, chain, t_elem) {
-                               if ((elem->sock != -1) &&
-                                       (FD_ISSET(elem->sock, &rfds)))
-                                       if (vpncontrol_comm_handler(elem))
-                                               update_fds = 1;         // socket closed by peer - update mask
-                       }
-               }
-#endif
-
-               for (p = lcconf->myaddrs; p; p = p->next) {
-                       if (!p->addr)
-                               continue;
-                       if (FD_ISSET(p->sock, &rfds))
-                               if ((error = isakmp_handler(p->sock)) == -2)
-                                       break;
-               }
-               if (error == -2) {
-                       if (lcconf->autograbaddr) {
-                               /* serious socket problem - close all listening sockets and re-open */
-                               isakmp_close(); 
-                               initfds();
-                               sched_new(5, check_rtsock, NULL);
-                               continue;
-                       } else {
-                               isakmp_close_sockets();
-                               isakmp_open();
-                               initfds();
-                               continue;
-                       }
-               }
-
-               if (FD_ISSET(lcconf->sock_pfkey, &rfds))
-                       pfkey_handler();
-
-               if (lcconf->rtsock >= 0 && FD_ISSET(lcconf->rtsock, &rfds)) {
-                       if (update_myaddrs() && lcconf->autograbaddr)
-                               if (check_rtsock_sched == NULL) /* only schedule if not already done */
-                                       check_rtsock_sched = sched_new(5, check_rtsock, NULL);
-                       // initfds();   //%%% BUG FIX - not needed here
-               }
-               if (update_fds) {
-                       initfds();
-                       update_fds = 0;
-               }
-       }
+       
+       xpc_transaction_begin();
+       
+#if !TARGET_OS_EMBEDDED
+       // enable keepalive for recovery (from crashes and bad exits... after init)
+       (void)launchd_update_racoon_keepalive(true);
+#endif // !TARGET_OS_EMBEDDED
+               
+    // Off to the races!
+    if (!terminated) {
+        dispatch_main();
+    }
+            
+    exit(1);    // should not be reached!!!
 }
 
 
 /* clear all status and exit program. */
 static void
-close_session()
+close_session(int error)
 {
-       flushph1();
+    sched_killall();    
+    cleanup_power_mgmt();
+       if ( terminated )
+               ike_session_flush_all_phase2(false);
+       ike_session_flush_all_phase1(false);
        close_sockets();
-       backupsa_clean();
-
-       plog(LLV_INFO, LOCATION, NULL, "racoon shutdown\n");
-       exit(0);
-}
-
-static void
-check_rtsock(p)
-       void *p;
-{      
-
-       check_rtsock_sched = NULL;
-       grab_myaddrs();
-       isakmp_close_unused();
-
-       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_ERROR2, LOCATION, NULL, "fd_set overrun - admin socket\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
-#ifdef ENABLE_VPNCONTROL_PORT
-       {
-               struct vpnctl_socket_elem *elem;
-               
-               if (lcconf->sock_vpncontrol != -1) {
-                       if (lcconf->sock_vpncontrol >= FD_SETSIZE) {
-                               plog(LLV_ERROR2, LOCATION, NULL, "fd_set overrun - vpncontrol socket\n");
-                               exit(1);
-                       }
-                       FD_SET(lcconf->sock_vpncontrol, &mask0);
-                       nfds = (nfds > lcconf->sock_vpncontrol ? nfds : lcconf->sock_vpncontrol);
-               }
-               
-               LIST_FOREACH(elem, &lcconf->vpnctl_comm_socks, chain) {
-                       if (elem->sock != -1) {
-                               if (elem->sock >= FD_SETSIZE) {
-                                       plog(LLV_ERROR2, LOCATION, NULL, "fd_set overrun vpnctl_comm socket\n");
-                                       exit(1);
-                               }
-                               FD_SET(elem->sock, &mask0);
-                               nfds = (nfds > elem->sock ? nfds : elem->sock);
-                       }
-               }
-       }
-
-#endif
-
-       if (lcconf->sock_pfkey >= FD_SETSIZE) {
-               plog(LLV_ERROR2, LOCATION, NULL, "fd_set overrun - pfkey socket\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_ERROR2, LOCATION, NULL, "fd_set overrun - rt socket\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 < 0)
-                       continue;
-               if (p->sock >= FD_SETSIZE) {
-                       plog(LLV_ERROR2, LOCATION, NULL, "fd_set overrun - isakmp socket\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,
-       SIGPIPE,
-       0
-};
+       xpc_transaction_end();
+       
+#if !TARGET_OS_EMBEDDED
+       // a clean exit, so disable launchd keepalive
+       (void)launchd_update_racoon_keepalive(false);
+#endif // !TARGET_OS_EMBEDDED
 
-/*
- * 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]++;
+       plog(ASL_LEVEL_NOTICE, "racoon shutdown\n");
+       exit(0);
 }
 
-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...  */
-                       isakmp_close();
-                       close(lcconf->rtsock);
-                       if (cfreparse()) {
-                               plog(LLV_ERROR2, LOCATION, NULL,
-                                        "configuration read failed\n");
-                               exit(1);
-                       }
-                       if (lcconf->logfile_param == NULL)
-                               plogreset(lcconf->pathinfo[LC_PATHTYPE_LOGFILE]);
-                               
-                       initmyaddr();
-                       isakmp_cleanup();
-                       isakmp_init();
-                       initfds();
-                       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);
-                       sched_new(1, check_flushsa_stub, NULL);
-                       dying = 1;
-                       break;
-
-               default:
-                       plog(LLV_INFO, LOCATION, NULL, 
-                           "caught signal %d\n", sig);
-                       break;
-               }
-       }
-}
 
 /*
  * waiting the termination of processing until sending DELETE message
@@ -514,7 +362,6 @@ static void
 check_flushsa_stub(p)
        void *p;
 {
-
        check_flushsa();
 }
 
@@ -529,33 +376,33 @@ check_flushsa()
 
        buf = pfkey_dump_sadb(SADB_SATYPE_UNSPEC);
        if (buf == NULL) {
-               plog(LLV_DEBUG, LOCATION, NULL,
+               plog(ASL_LEVEL_DEBUG, 
                    "pfkey_dump_sadb: returned nothing.\n");
                return;
        }
 
-       msg = (struct sadb_msg *)buf->v;
-       end = (struct sadb_msg *)(buf->v + buf->l);
+       msg = ALIGNED_CAST(struct sadb_msg *)buf->v; 
+       end = ALIGNED_CAST(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));
+               next = ALIGNED_CAST(struct sadb_msg *)((caddr_t)msg + PFKEY_UNUNIT64(msg->sadb_msg_len));    // Wcast-align fix (void*) - aligned buffer + multiple of 64
                if (msg->sadb_msg_type != SADB_DUMP) {
                        msg = next;
                        continue;
                }
 
                if (pfkey_align(msg, mhp) || pfkey_check(mhp)) {
-                       plog(LLV_ERROR, LOCATION, NULL,
+                       plog(ASL_LEVEL_ERR, 
                                "pfkey_check (%s)\n", ipsec_strerror());
                        msg = next;
                        continue;
                }
 
-               sa = (struct sadb_sa *)(mhp[SADB_EXT_SA]);
+               sa = ALIGNED_CAST(struct sadb_sa *)(mhp[SADB_EXT_SA]);       // Wcast-align fix (void*) - mhp contains pointers to aligned structs
                if (!sa) {
                        msg = next;
                        continue;
@@ -578,63 +425,219 @@ check_flushsa()
                return;
        }
 
-       close_session();
+#if !TARGET_OS_EMBEDDED
+       if (lcconf->vt)
+               vproc_transaction_end(NULL, lcconf->vt);
+#endif
+    close_session(0);
 }
 
 void
 auto_exit_do(void *p)
 {
-       EVT_PUSH(NULL, NULL, EVTT_RACOON_QUIT, NULL);
+       plog(ASL_LEVEL_NOTICE,
+                               "performing auto exit\n");
+#if ENABLE_NO_SA_FLUSH
+       close_session(0);
+#else
        pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC);
        sched_new(1, check_flushsa_stub, NULL);
-       dying = 1;
+       dying();
+#endif /* ENABLE_NO_SA_FLUSH */
 }
 
 void
 check_auto_exit(void)
 {
-
-       if (lcconf->auto_exit_sched != NULL) {  /* exit scheduled? */
+       if (lcconf->auto_exit_sched) {  /* exit scheduled? */
                if (lcconf->auto_exit_state != LC_AUTOEXITSTATE_ENABLED
-                       || vpn_control_connected()                              /* vpn control connected */
-                       || policies_installed())                                /* policies installed in kernel */
+                       || vpn_control_connected()          /* vpn control connected */
+                       || policies_installed()             /* policies installed in kernel */
+                       || !no_remote_configs(FALSE))   {       /* remote or anonymous configs */
                        SCHED_KILL(lcconf->auto_exit_sched);
+        }
        } else {                                                                /* exit not scheduled */
                if (lcconf->auto_exit_state == LC_AUTOEXITSTATE_ENABLED
-                       && !vpn_control_connected()             
-                       && !policies_installed())
-                               if (lcconf->auto_exit_delay == 0)
-                                       auto_exit_do(NULL);             /* immediate exit */
-                               else
-                                       lcconf->auto_exit_sched = sched_new(lcconf->auto_exit_delay, auto_exit_do, NULL);
+                       && !vpn_control_connected()
+                       && !policies_installed()
+                       && no_remote_configs(FALSE)) {
+            if (lcconf->auto_exit_delay == 0) {
+                auto_exit_do(NULL);            /* immediate exit */
+            } else {
+                lcconf->auto_exit_sched = sched_new(lcconf->auto_exit_delay, auto_exit_do, NULL);
+            }
+        }
        }
 }
 
+static int signals[] = {
+       SIGHUP,
+       SIGINT,
+       SIGTERM,
+       SIGUSR1,
+       SIGUSR2,
+       SIGPIPE,
+       0
+};
 
-static void
+
+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 SIGUSR1:
+            case SIGHUP:
+#ifdef ENABLE_HYBRID
+                if ((isakmp_cfg_init(ISAKMP_CFG_INIT_WARM)) != 0) {
+                    plog(ASL_LEVEL_ERR, 
+                         "ISAKMP mode config structure reset failed, "
+                         "not reloading\n");
+                    return;
+                }
+#endif
+                if ( terminated )
+                    break;
+                               
+                /*
+                 * if we got a HUP... try graceful teardown of sessions before we close and reopen sockets...
+                 * so that info-deletes notifications can make it to the peer.
+                 */
+                if (sig == SIGHUP) {
+                    ike_session_flush_all_phase2(true);
+                    ike_session_flush_all_phase1(true);
+                }              
+
+                /* Save old configuration, load new one...  */
+                if (cfreparse(sig)) {
+                    plog(ASL_LEVEL_ERR, 
+                         "configuration read failed\n");
+                    exit(1);
+                }
+                if (lcconf->logfile_param == NULL && logFileStr[0] == 0)
+                    plogresetfile(lcconf->pathinfo[LC_PATHTYPE_LOGFILE]);
+                                           
+#if TARGET_OS_EMBEDDED
+                if (no_remote_configs(TRUE)) {
+#if ENABLE_NO_SA_FLUSH
+                    close_session(0);
+#else
+                    pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC);
+#ifdef ENABLE_FASTQUIT
+                    close_session(0);
+#else
+                    sched_new(1, check_flushsa_stub, NULL);
+#endif /* ENABLE_FASTQUIT */
+                    dying();
+#endif /* ENABLE_NO_SA_FLUSH */
+                }
+#endif
+
+                break;
+                
+            case SIGINT:
+            case SIGTERM:                      
+                plog(ASL_LEVEL_NOTICE, 
+                     "caught signal %d\n", sig);
+#if ENABLE_NO_SA_FLUSH
+                close_session(0);
+#else
+                pfkey_send_flush(lcconf->sock_pfkey,
+                                 SADB_SATYPE_UNSPEC);
+                if ( sig == SIGTERM ){
+                    terminated = 1;                    /* in case if it hasn't been set yet */
+                    close_session(0);
+                }
+                else
+                    sched_new(1, check_flushsa_stub, NULL);
+                
+                               dying();
+#endif /* ENABLE_NO_SA_FLUSH */
+                break;
+                
+            default:
+                plog(ASL_LEVEL_NOTICE, 
+                     "caught signal %d\n", sig);
+                break;
+               }
+       }
+}
+
+    
+/*
+ * asynchronous requests will actually dispatched in the
+ * main loop in session().
+ */
+RETSIGTYPE
+signal_handler(int sig, siginfo_t *sigi, void *ctx)
+{
+#if 0
+    plog(ASL_LEVEL_NOTICE,
+         "%s received signal %d from pid %d uid %d\n\n",
+         __FUNCTION__, sig, sigi->si_pid, sigi->si_uid);
+#endif
+    
+    /* Do not just set it to 1, because we may miss some signals by just setting
+     * values to 0/1
+     */
+    sigreq[sig]++;
+    if ( sig == SIGTERM ){
+        terminated = 1;
+    }
+
+       pending_signal_handle = 1;
+    dispatch_async(main_queue, 
+                   ^{
+                                               if (pending_signal_handle) {
+                                                       check_sigreq();
+                                                       pending_signal_handle = 0;
+                                               }
+                   });
+}
+
+
+static int
 init_signal()
 {
        int i;
 
-       for (i = 0; signals[i] != 0; i++)
+       for (i = 0; signals[i] != 0; i++) {
                if (set_signal(signals[i], signal_handler) < 0) {
-                       plog(LLV_ERROR2, LOCATION, NULL,
+                       plog(ASL_LEVEL_ERR, 
                                "failed to set_signal (%s)\n",
                                strerror(errno));
-                       exit(1);
+                       return (1);
                }
+    }
+    return 0;
 }
 
 static int
-set_signal(sig, func)
-       int sig;
-       RETSIGTYPE (*func) __P((int));
+set_signal(int sig, RETSIGTYPE (*func) (int, siginfo_t *, void *))
 {
        struct sigaction sa;
 
        memset((caddr_t)&sa, 0, sizeof(sa));
-       sa.sa_handler = func;
-       sa.sa_flags = SA_RESTART;
+    sa.sa_sigaction = func;
+       sa.sa_flags = SA_RESTART | SA_SIGINFO;
 
        if (sigemptyset(&sa.sa_mask) < 0)
                return -1;
@@ -645,17 +648,34 @@ set_signal(sig, func)
        return 0;
 }
 
+void
+fatal_error(int error)
+{
+    close_session(error == 0 ? -1 : error);
+}
+
+/* suspend all socket sources except pf_key */
+void
+dying(void)
+{
+    if (lcconf->rt_source)
+        dispatch_suspend(lcconf->rt_source);
+    if (lcconf->vpncontrol_source)
+        dispatch_suspend(lcconf->vpncontrol_source);
+    isakmp_suspend_sockets();
+}
+    
 static int
 close_sockets()
 {
+    pfroute_close();
        isakmp_close();
-       pfkey_close(lcconf->sock_pfkey);
-#ifdef ENABLE_ADMINPORT
-       (void)admin_close();
-#endif
+       pfkey_close();
 #ifdef ENABLE_VPNCONTROL_PORT
        vpncontrol_close();
 #endif
+    
        return 0;
 }
 
+