]> git.saurik.com Git - apple/ipsec.git/blobdiff - ipsec-tools/racoon/session.c
ipsec-146.3.tar.gz
[apple/ipsec.git] / ipsec-tools / racoon / session.c
index 102829069cb2583b81b899cc55f2611db6b91eff..5bfbaba3b5c73349079b570682bc3b6419580a97 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 <resolv.h>
+#include <TargetConditionals.h>
+#include <vproc_priv.h>
+
 #include "libpfkey.h"
 
 #include "var.h"
@@ -76,6 +86,8 @@
 #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"
 #endif
 #include "vpn_control_var.h"
 #include "policy.h"
+#include "algorithm.h" /* XXX ??? */
+
+#include "sainfo.h"
+#include "power_mgmt.h"
+
+
 
 extern pid_t racoon_pid;
+extern char    logFileStr[];
+extern int launchedbylaunchd(void);
 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 int set_signal __P((int sig, RETSIGTYPE (*func) __P((int, siginfo_t *, void *))));
 static void check_sigreq __P((void));
 static void check_flushsa_stub __P((void *));
 static void check_flushsa __P((void));
@@ -109,6 +129,61 @@ static int nfds = 0;
 static volatile sig_atomic_t sigreq[NSIG + 1];
 static int dying = 0;
 static struct sched *check_rtsock_sched = NULL;
+int terminated = 0;
+
+#define HANDLE_TENTATIVE_INTF_FAILURES() do {                                                                                                                                                    \
+                                                                                       if (tentative_failures) {                                                                                                         \
+                                                                                               plog(LLV_ERROR, LOCATION, NULL,                                                                                   \
+                                                                                                        "detected tentative interface/address issues: will retry later.\n"); \
+                                                                                               if (check_rtsock_sched == NULL) {                                                                                 \
+                                                                                                       /* only schedule if not already done */                                                           \
+                                                                                                       check_rtsock_sched = sched_new(5, check_rtsock, NULL);                            \
+                                                                                               }                                                                                                                                                 \
+                                                                                       }                                                                                                                                                         \
+                                                                               } while(0)
+
+static void
+reinit_socks (void)
+{
+       int tentative_failures;
+
+       isakmp_close(); 
+       close(lcconf->rtsock);
+       initmyaddr();
+       if (isakmp_open(&tentative_failures) < 0) {
+               plog(LLV_ERROR2, LOCATION, NULL,
+                        "failed to reopen isakmp sockets\n");
+       }
+       initfds();      
+       HANDLE_TENTATIVE_INTF_FAILURES();
+}
+
+static int64_t racoon_keepalive = -1;
+
+/*
+ * 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 (launchedbylaunchd()) {
+               vproc_t vp = vprocmgr_lookup_vproc("com.apple.racoon");
+               if (vp) {
+                       int64_t     val = (__typeof__(val))enabled;
+                       if (vproc_swap_integer(vp,
+                                                                  VPROC_GSK_BASIC_KEEPALIVE,
+                                                                  &val,
+                                                                  &racoon_keepalive)) {
+                               plog(LLV_ERROR2, LOCATION, NULL,
+                                        "failed to swap launchd keepalive integer %d\n", enabled);
+                       }
+                       vproc_release(vp);
+               }
+       }
+       return racoon_keepalive;
+}
 
 int
 session(void)
@@ -120,18 +195,25 @@ session(void)
        char pid_file[MAXPATHLEN];
        FILE *fp;
        int i, update_fds;
+       int tentative_failures;
 
        /* initialize schedular */
        sched_init();
 
+       /* needs to be called after schedular */
+       if (init_power_mgmt() < 0) {
+               errx(1, "failed to initialize power-mgmt.");
+       }
+
        initmyaddr();
 
-       if (isakmp_init() < 0) {
+       if (isakmp_init(false, &tentative_failures) < 0) {
                plog(LLV_ERROR2, LOCATION, NULL,
                                "failed to initialize isakmp");
                exit(1);
        }
-
+       HANDLE_TENTATIVE_INTF_FAILURES();
+               
 #ifdef ENABLE_ADMINPORT
        if (admin_init() < 0) {
                plog(LLV_ERROR2, LOCATION, NULL,
@@ -145,23 +227,20 @@ session(void)
                        "failed to initialize vpn control port");
                exit(1);
        }
+       
 #endif
 
        init_signal();
        initfds();
 
-#ifndef __APPLE__
-#ifdef ENABLE_NATT
-       natt_keepalive_init ();
-#endif
-#endif
-
+#ifdef HAVE_OPENSSL
        if (privsep_init() != 0) {
                plog(LLV_ERROR2, LOCATION, NULL,
                        "failed to initialize privsep");
                exit(1);
        }
-
+#endif
+       
        for (i = 0; i <= NSIG; i++)
                sigreq[i] = 0;
 
@@ -169,12 +248,12 @@ 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) {
@@ -192,7 +271,14 @@ session(void)
                }
        }
 
+#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
+               
        while (1) {
+               if (!TAILQ_EMPTY(&lcconf->saved_msg_queue))
+                       pfkey_post_handler();
                update_fds = 0;
                /*
                 * asynchronous requests via signal.
@@ -200,8 +286,23 @@ session(void)
                 */
                check_sigreq();
 
+               check_power_mgmt();
+
                /* scheduling */
                timeout = schedular();
+               // <rdar://problem/7650111> Workaround: make sure timeout is playing nice
+               if (timeout) {
+                       if (timeout->tv_usec < 0 || timeout->tv_usec > SELECT_USEC_MAX ) {
+                               timeout->tv_sec += ((__typeof__(timeout->tv_sec))timeout->tv_usec)/SELECT_USEC_MAX;
+                               timeout->tv_usec %= SELECT_USEC_MAX;
+                       }
+                       if (timeout->tv_sec > SELECT_SEC_MAX /* tv_sec is unsigned */) {
+                               timeout->tv_sec = SELECT_SEC_MAX;
+                       }
+                       if (!timeout->tv_sec && !timeout->tv_usec) {
+                               timeout->tv_sec = 1;
+                       }
+               }
 
                if (dying)
                        rfds = maskdying;
@@ -214,9 +315,11 @@ session(void)
                                continue;
                        default:
                                plog(LLV_ERROR2, LOCATION, NULL,
-                                       "failed select (%s)\n",
-                                       strerror(errno));
-                               exit(1);
+                                       "failed select (%s) nfds %d\n",
+                                       strerror(errno), nfds);
+                               reinit_socks();
+                               update_fds = 0;
+                               continue;
                        }
                        /*NOTREACHED*/
                }
@@ -247,28 +350,22 @@ session(void)
                                                update_fds = 1;         // socket closed by peer - update mask
                        }
                }
-#endif
+#endif                 
 
                for (p = lcconf->myaddrs; p; p = p->next) {
                        if (!p->addr)
                                continue;
-                       if (FD_ISSET(p->sock, &rfds))
+                       if ((p->sock != -1) &&
+                               (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;
-                       }
+                       plog(LLV_ERROR2, LOCATION, NULL,
+                                "failed to process isakmp port\n");
+                       reinit_socks();
+                       update_fds = 0;
+                       continue;
                }
 
                if (FD_ISSET(lcconf->sock_pfkey, &rfds))
@@ -277,7 +374,18 @@ session(void)
                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);
+                                       check_rtsock_sched = sched_new(1, check_rtsock, NULL);
+                               else {
+                                       // force reinit if schedule is too far off (3 seconds or more)
+                                       time_t too_far = current_time() + 3;
+                                       if (check_rtsock_sched->dead ||
+                                               check_rtsock_sched->xtime >= too_far) {
+                                               plog(LLV_DEBUG, LOCATION, NULL,
+                                                        "forced reinit of addrs\n");
+                                               update_fds = 0;
+                                               check_rtsock(NULL);
+                                       }
+                               }
                        // initfds();   //%%% BUG FIX - not needed here
                }
                if (update_fds) {
@@ -292,10 +400,17 @@ session(void)
 static void
 close_session()
 {
-       flushph1();
+       if ( terminated )
+               flushph2(false);
+       flushph1(false);
        close_sockets();
        backupsa_clean();
 
+#if !TARGET_OS_EMBEDDED
+       // a clean exit, so disable launchd keepalive
+       (void)launchd_update_racoon_keepalive(false);
+#endif // !TARGET_OS_EMBEDDED
+
        plog(LLV_INFO, LOCATION, NULL, "racoon shutdown\n");
        exit(0);
 }
@@ -304,16 +419,18 @@ static void
 check_rtsock(p)
        void *p;
 {      
+       int tentative_failures;
 
        check_rtsock_sched = NULL;
        grab_myaddrs();
        isakmp_close_unused();
 
        autoconf_myaddrsport();
-       isakmp_open();
+       isakmp_open(&tentative_failures);
 
        /* initialize socket list again */
        initfds();
+       HANDLE_TENTATIVE_INTF_FAILURES();
 }
 
 static void
@@ -365,7 +482,6 @@ initfds()
                        }
                }
        }
-
 #endif
 
        if (lcconf->sock_pfkey >= FD_SETSIZE) {
@@ -383,7 +499,7 @@ initfds()
                FD_SET(lcconf->rtsock, &mask0);
                nfds = (nfds > lcconf->rtsock ? nfds : lcconf->rtsock);
        }
-
+       
        for (p = lcconf->myaddrs; p; p = p->next) {
                if (!p->addr)
                        continue;
@@ -399,6 +515,7 @@ initfds()
        nfds++;
 }
 
+
 static int signals[] = {
        SIGHUP,
        SIGINT,
@@ -415,19 +532,31 @@ static int signals[] = {
  * main loop in session().
  */
 RETSIGTYPE
-signal_handler(sig)
+signal_handler(sig, sigi, ctx)
        int sig;
+       siginfo_t *sigi;
+       void *ctx;
 {
+#if 0
+       plog(LLV_DEBUG, LOCATION, NULL, 
+                "%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;
+       }
 }
 
 static void
 check_sigreq()
 {
        int sig;
+       int tentative_failures;
 
        /* 
         * XXX We are not able to tell if we got 
@@ -467,22 +596,55 @@ check_sigreq()
                        break;
 #endif
 
+               case SIGUSR1:
                case SIGHUP:
+#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
+                       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) {
+                               flushph2(true);
+                               flushph1(true);
+                       }               
                        /* Save old configuration, load new one...  */
                        isakmp_close();
                        close(lcconf->rtsock);
-                       if (cfreparse()) {
+                       if (cfreparse(sig)) {
                                plog(LLV_ERROR2, LOCATION, NULL,
                                         "configuration read failed\n");
                                exit(1);
                        }
-                       if (lcconf->logfile_param == NULL)
+                       if (lcconf->logfile_param == NULL && logFileStr[0] == 0)
                                plogreset(lcconf->pathinfo[LC_PATHTYPE_LOGFILE]);
                                
                        initmyaddr();
                        isakmp_cleanup();
-                       isakmp_init();
+                       isakmp_init(true, &tentative_failures);
+                       HANDLE_TENTATIVE_INTF_FAILURES();
                        initfds();
+#if TARGET_OS_EMBEDDED
+                       if (no_remote_configs(TRUE)) {
+                               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;
+                       }
+#endif
                        break;
 
                case SIGINT:
@@ -492,8 +654,14 @@ check_sigreq()
                        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;
+                       if ( sig == SIGTERM ){
+                               terminated = 1;                 /* in case if it hasn't been set yet */
+                               close_session();
+                       }
+                       else
+                               sched_new(1, check_flushsa_stub, NULL);
+
+                               dying = 1;
                        break;
 
                default:
@@ -577,12 +745,18 @@ check_flushsa()
        }
 
        close_session();
+#if !TARGET_OS_EMBEDDED
+       if (lcconf->vt)
+               vproc_transaction_end(NULL, lcconf->vt);
+#endif
 }
 
 void
 auto_exit_do(void *p)
 {
        EVT_PUSH(NULL, NULL, EVTT_RACOON_QUIT, NULL);
+       plog(LLV_DEBUG, LOCATION, NULL,
+                               "performing auto exit\n");
        pfkey_send_flush(lcconf->sock_pfkey, SADB_SATYPE_UNSPEC);
        sched_new(1, check_flushsa_stub, NULL);
        dying = 1;
@@ -591,16 +765,17 @@ auto_exit_do(void *p)
 void
 check_auto_exit(void)
 {
-
        if (lcconf->auto_exit_sched != NULL) {  /* exit scheduled? */
                if (lcconf->auto_exit_state != LC_AUTOEXITSTATE_ENABLED
                        || vpn_control_connected()                              /* vpn control connected */
-                       || policies_installed())                                /* policies installed in kernel */
+                       || 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())
+                       && !vpn_control_connected()     
+                       && !policies_installed()
+                       && no_remote_configs(FALSE))
                                if (lcconf->auto_exit_delay == 0)
                                        auto_exit_do(NULL);             /* immediate exit */
                                else
@@ -626,13 +801,13 @@ init_signal()
 static int
 set_signal(sig, func)
        int sig;
-       RETSIGTYPE (*func) __P((int));
+       RETSIGTYPE (*func) __P((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_flags = SA_RESTART | SA_SIGINFO;
 
        if (sigemptyset(&sa.sa_mask) < 0)
                return -1;
@@ -657,3 +832,4 @@ close_sockets()
        return 0;
 }
 
+