XAUTH interoperability with Cisco
[strongswan.git] / src / pluto / demux.c
index eaacd3e..7e59b18 100644 (file)
 #include "timer.h"
 #include "whack.h"     /* requires connections.h */
 #include "server.h"
-#ifdef NAT_TRAVERSAL
 #include "nat_traversal.h"
-#endif
 #include "vendor.h"
 #include "modecfg.h"
 
@@ -273,11 +271,7 @@ static const struct state_microcode state_microcode_table[] = {
      */
     { STATE_MAIN_R1, STATE_MAIN_R2
     , SMF_PSK_AUTH | SMF_DS_AUTH | SMF_REPLY
-#ifdef NAT_TRAVERSAL
     , P(KE) | P(NONCE), P(VID) | P(CR) | P(NATD_RFC), PT(KE)
-#else
-    , P(KE) | P(NONCE), P(VID) | P(CR), PT(KE)
-#endif    
     , EVENT_RETRANSMIT, main_inI2_outR2 },
 
     { STATE_MAIN_R1, STATE_UNDEFINED
@@ -302,11 +296,7 @@ static const struct state_microcode state_microcode_table[] = {
      */
     { STATE_MAIN_I2, STATE_MAIN_I3
     , SMF_PSK_AUTH | SMF_DS_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED | SMF_REPLY
-#ifdef NAT_TRAVERSAL
     , P(KE) | P(NONCE), P(VID) | P(CR) | P(NATD_RFC), PT(ID)
-#else
-    , P(KE) | P(NONCE), P(VID) | P(CR), PT(ID)
-#endif    
     , EVENT_RETRANSMIT, main_inR2_outI3 },
 
     { STATE_MAIN_I2, STATE_UNDEFINED
@@ -397,11 +387,7 @@ static const struct state_microcode state_microcode_table[] = {
      */
     { STATE_QUICK_R0, STATE_QUICK_R1
     , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY
-#ifdef NAT_TRAVERSAL
     , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID) | P(NATOA_RFC), PT(NONE)
-#else
-    , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID), PT(NONE)
-#endif
     , EVENT_RETRANSMIT, quick_inI1_outR1 },
 
     /* STATE_QUICK_I1:
@@ -412,11 +398,7 @@ static const struct state_microcode state_microcode_table[] = {
      */
     { STATE_QUICK_I1, STATE_QUICK_I2
     , SMF_ALL_AUTH | SMF_INITIATOR | SMF_ENCRYPTED | SMF_REPLY
-#ifdef NAT_TRAVERSAL
     , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID) | P(NATOA_RFC), PT(HASH)
-#else
-    , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID), PT(HASH)
-#endif
     , EVENT_SA_REPLACE, quick_inR1_outI2 },
 
     /* STATE_QUICK_R1: HDR*, HASH(3) --> done
@@ -454,34 +436,80 @@ static const struct state_microcode state_microcode_table[] = {
     , P(HASH), LEMPTY, PT(NONE)
     , EVENT_NULL, informational },
 
-    /* MODE_CFG_x:
-     * Case R0:  Responder  -> Initiator
-     *                    <-   Req(addr=0)
-     *     Reply(ad=x)     ->
-     *
-     * Case R1: Set(addr=x) ->
-     *                    <-   Ack(ok)
-     */
-
-    { STATE_MODE_CFG_R0, STATE_MODE_CFG_R1
+    /* XAUTH state transitions */
+    { STATE_XAUTH_I0, STATE_XAUTH_I1
     , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY
     , P(ATTR) | P(HASH), P(VID), PT(HASH)
-    , EVENT_SA_REPLACE, modecfg_inR0 },
+    , EVENT_RETRANSMIT, xauth_inI0 },
 
-    { STATE_MODE_CFG_R1, STATE_MODE_CFG_R2
+    { STATE_XAUTH_R1, STATE_XAUTH_R2
     , SMF_ALL_AUTH | SMF_ENCRYPTED
     , P(ATTR) | P(HASH), P(VID), PT(HASH)
-    , EVENT_SA_REPLACE, modecfg_inR1 },
+    , EVENT_RETRANSMIT, xauth_inR1 },
+
+    { STATE_XAUTH_I1, STATE_XAUTH_I2
+    , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2
+    , P(ATTR) | P(HASH), P(VID), PT(HASH)
+    , EVENT_SA_REPLACE, xauth_inI1 },
+
+    { STATE_XAUTH_R2, STATE_XAUTH_R3
+    , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
+    , P(ATTR) | P(HASH), P(VID), PT(NONE)
+    , EVENT_SA_REPLACE, xauth_inR2 },
+
+    { STATE_XAUTH_I2, STATE_UNDEFINED
+    , SMF_ALL_AUTH | SMF_ENCRYPTED
+    , LEMPTY, LEMPTY, PT(NONE)
+    , EVENT_NULL, unexpected },
 
-    { STATE_MODE_CFG_R2, STATE_UNDEFINED
+    { STATE_XAUTH_R3, STATE_UNDEFINED
     , SMF_ALL_AUTH | SMF_ENCRYPTED
     , LEMPTY, LEMPTY, PT(NONE)
     , EVENT_NULL, unexpected },
 
+    /* ModeCfg pull mode state transitions */
+
+    { STATE_MODE_CFG_R0, STATE_MODE_CFG_R1
+    , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2
+    , P(ATTR) | P(HASH), P(VID), PT(HASH)
+    , EVENT_SA_REPLACE, modecfg_inR0 },
+
     { STATE_MODE_CFG_I1, STATE_MODE_CFG_I2
     , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
     , P(ATTR) | P(HASH), P(VID), PT(HASH)
-    , EVENT_SA_REPLACE, modecfg_inR1 },
+    , EVENT_SA_REPLACE, modecfg_inI1 },
+
+    { STATE_MODE_CFG_R1, STATE_UNDEFINED
+    , SMF_ALL_AUTH | SMF_ENCRYPTED
+    , LEMPTY, LEMPTY, PT(NONE)
+    , EVENT_NULL, unexpected },
+
+    { STATE_MODE_CFG_I2, STATE_UNDEFINED
+    , SMF_ALL_AUTH | SMF_ENCRYPTED
+    , LEMPTY, LEMPTY, PT(NONE)
+    , EVENT_NULL, unexpected },
+
+   /* ModeCfg push mode state transitions */
+
+    { STATE_MODE_CFG_I0, STATE_MODE_CFG_I3
+    , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2
+    , P(ATTR) | P(HASH), P(VID), PT(HASH)
+    , EVENT_SA_REPLACE, modecfg_inI0 },
+
+    { STATE_MODE_CFG_R3, STATE_MODE_CFG_R4
+    , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
+    , P(ATTR) | P(HASH), P(VID), PT(HASH)
+    , EVENT_SA_REPLACE, modecfg_inR3 },
+
+    { STATE_MODE_CFG_I3, STATE_UNDEFINED
+    , SMF_ALL_AUTH | SMF_ENCRYPTED
+    , LEMPTY, LEMPTY, PT(NONE)
+    , EVENT_NULL, unexpected },
+
+    { STATE_MODE_CFG_R4, STATE_UNDEFINED
+    , SMF_ALL_AUTH | SMF_ENCRYPTED
+    , LEMPTY, LEMPTY, PT(NONE)
+    , EVENT_NULL, unexpected },
 
 #undef P
 #undef PT
@@ -744,7 +772,6 @@ check_msg_errqueue(const struct iface *ifp, short interest)
                    /* note dirty trick to suppress ~ at start of format
                     * if we know what state to blame.
                     */
-#ifdef NAT_TRAVERSAL
                    if ((packet_len == 1) && (buffer[0] = 0xff)
 #ifdef DEBUG
                        && ((cur_debugging & DBG_NATT) == 0)
@@ -755,7 +782,6 @@ check_msg_errqueue(const struct iface *ifp, short interest)
                             */
                    }
                    else
-#endif              
                    plog((sender != NULL) + "~"
                        "ERROR: asynchronous network error report on %s"
                        "%s"
@@ -793,24 +819,19 @@ check_msg_errqueue(const struct iface *ifp, short interest)
 #endif /* defined(IP_RECVERR) && defined(MSG_ERRQUEUE) */
 
 bool
-#ifdef NAT_TRAVERSAL
-_send_packet(struct state *st, const char *where, bool verbose)
-#else
 send_packet(struct state *st, const char *where)
-#endif
 {
     struct connection *c = st->st_connection;
     int port_buf;
     bool err;
-
-#ifdef NAT_TRAVERSAL
     u_int8_t ike_pkt[MAX_OUTPUT_UDP_SIZE];
     u_int8_t *ptr;
     unsigned long len;
 
-    if ((c->interface->ike_float == TRUE) && (st->st_tpacket.len != 1)) {
-       if ((unsigned long) st->st_tpacket.len >
-           (MAX_OUTPUT_UDP_SIZE-sizeof(u_int32_t))) {
+    if (c->interface->ike_float && st->st_tpacket.len != 1)
+    {
+       if ((unsigned long) st->st_tpacket.len > (MAX_OUTPUT_UDP_SIZE-sizeof(u_int32_t)))
+       {
            DBG_log("send_packet(): really too big");
            return FALSE;
        }
@@ -821,11 +842,11 @@ send_packet(struct state *st, const char *where)
            (unsigned long)st->st_tpacket.len);
        len = (unsigned long) st->st_tpacket.len + sizeof(u_int32_t);
     }
-    else {
+    else
+    {
        ptr = st->st_tpacket.ptr;
        len = (unsigned long) st->st_tpacket.len;
     }
-#endif
 
     DBG(DBG_RAW,
        {
@@ -850,28 +871,19 @@ send_packet(struct state *st, const char *where)
     (void) check_msg_errqueue(c->interface, POLLOUT);
 #endif /* defined(IP_RECVERR) && defined(MSG_ERRQUEUE) */
 
-#ifdef NAT_TRAVERSAL
     err = sendto(c->interface->fd
        , ptr, len, 0
        , sockaddrof(&c->spd.that.host_addr)
        , sockaddrlenof(&c->spd.that.host_addr)) != (ssize_t)len;
-#else
-    err = sendto(c->interface->fd
-       , st->st_tpacket.ptr, st->st_tpacket.len, 0
-       , sockaddrof(&c->spd.that.host_addr)
-       , sockaddrlenof(&c->spd.that.host_addr)) != (ssize_t)st->st_tpacket.len;
-#endif
 
     /* restore port */
     setportof(port_buf, &c->spd.that.host_addr);
 
     if (err)
     {
-#ifdef NAT_TRAVERSAL
-        /* do not log NAT-T Keep Alive packets */
-        if (!verbose)
-           return FALSE;
-#endif
+       /* do not log NAT-T Keep Alive packets */
+        if (streq(where, "NAT-T Keep Alive"))
+           return FALSE;
        log_errno((e, "sendto on %s to %s:%u failed in %s"
            , c->interface->rname
            , ip_str(&c->spd.that.host_addr)
@@ -1141,16 +1153,19 @@ read_packet(struct msg_digest *md)
     cur_from = &md->sender;
     cur_from_port = md->sender_port;
 
-#ifdef NAT_TRAVERSAL
-    if (ifp->ike_float == TRUE) {
+    if (ifp->ike_float == TRUE)
+    {
        u_int32_t non_esp;
-       if (packet_len < (int)sizeof(u_int32_t)) {
+
+       if (packet_len < (int)sizeof(u_int32_t))
+       {
            plog("recvfrom %s:%u too small packet (%d)"
                , ip_str(cur_from), (unsigned) cur_from_port, packet_len);
            return FALSE;
        }
        memcpy(&non_esp, buffer, sizeof(u_int32_t));
-       if (non_esp != 0) {
+       if (non_esp != 0)
+       {
            plog("recvfrom %s:%u has no Non-ESP marker"
                , ip_str(cur_from), (unsigned) cur_from_port);
            return FALSE;
@@ -1161,7 +1176,6 @@ read_packet(struct msg_digest *md)
        pfree(buffer);
        buffer = buffer_nat;
     }
-#endif
 
     /* Clone actual message contents
      * and set up md->packet_pbs to describe it.
@@ -1180,21 +1194,20 @@ read_packet(struct msg_digest *md)
     DBG(DBG_RAW,
        DBG_dump("", md->packet_pbs.start, pbs_room(&md->packet_pbs)));
 
-#ifdef NAT_TRAVERSAL
-       if ((pbs_room(&md->packet_pbs)==1) && (md->packet_pbs.start[0]==0xff)) {
-               /**
-                * NAT-T Keep-alive packets should be discared by kernel ESPinUDP
-                * layer. But boggus keep-alive packets (sent with a non-esp marker)
-                * can reach this point. Complain and discard them.
-                */
-               DBG(DBG_NATT,
-                       DBG_log("NAT-T keep-alive (boggus ?) should not reach this point. "
-                               "Ignored. Sender: %s:%u", ip_str(cur_from),
-                               (unsigned) cur_from_port);
-                       );
-               return FALSE;
+       if ((pbs_room(&md->packet_pbs)==1) && (md->packet_pbs.start[0]==0xff))
+       {
+           /**
+            * NAT-T Keep-alive packets should be discared by kernel ESPinUDP
+            * layer. But boggus keep-alive packets (sent with a non-esp marker)
+            * can reach this point. Complain and discard them.
+            */
+           DBG(DBG_NATT,
+               DBG_log("NAT-T keep-alive (boggus ?) should not reach this point. "
+                       "Ignored. Sender: %s:%u", ip_str(cur_from),
+                       (unsigned) cur_from_port);
+           )
+           return FALSE;
        }
-#endif
 
 #define IKEV2_VERSION_OFFSET   17
 #define IKEV2_VERSION          0x20
@@ -1261,6 +1274,11 @@ process_packet(struct msg_digest **mdp)
     {
        plog("size (%u) differs from size specified in ISAKMP HDR (%u)"
            , (unsigned) pbs_room(&md->packet_pbs), md->hdr.isa_length);
+#ifdef CISCO_QUIRKS
+       if (pbs_room(&md->packet_pbs) - md->hdr.isa_length == 16)
+           plog("Cisco VPN client appends 16 surplus NULL bytes");
+       else
+#endif
        return;
     }
 
@@ -1450,11 +1468,6 @@ process_packet(struct msg_digest **mdp)
                return;
            }
 
-           if (st->st_state == STATE_MODE_CFG_R2)   /* Have we just give an IP address to peer? */
-           {
-               st->st_state = STATE_MAIN_R3;       /* ISAKMP is up... */
-           }
-           
            set_cur_state(st);
 
            if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state))
@@ -1484,7 +1497,7 @@ process_packet(struct msg_digest **mdp)
        }
        else
        {
-           set_cur_state(st);
+           set_cur_state(st);  
            from_state = st->st_state;
        }
 
@@ -1493,7 +1506,7 @@ process_packet(struct msg_digest **mdp)
     case ISAKMP_XCHG_MODE_CFG:
        if (is_zero_cookie(md->hdr.isa_icookie))
        {
-           plog("Mode Config message is invalid because"
+           plog("ModeCfg message is invalid because"
                " it has an Initiator Cookie of 0");
            /* XXX Could send notification back */
            return;
@@ -1501,7 +1514,7 @@ process_packet(struct msg_digest **mdp)
 
        if (is_zero_cookie(md->hdr.isa_rcookie))
        {
-           plog("Mode Config message is invalid because"
+           plog("ModeCfg message is invalid because"
                " it has a Responder Cookie of 0");
            /* XXX Could send notification back */
            return;
@@ -1509,7 +1522,7 @@ process_packet(struct msg_digest **mdp)
 
        if (md->hdr.isa_msgid == 0)
        {
-           plog("Mode Config message is invalid because"
+           plog("ModeCfg message is invalid because"
                " it has a Message ID of 0");
            /* XXX Could send notification back */
            return;
@@ -1520,7 +1533,9 @@ process_packet(struct msg_digest **mdp)
 
        if (st == NULL)
        {
-           /* No appropriate Mode Config state.
+           bool has_xauth_policy;
+
+           /* No appropriate ModeCfg state.
             * See if we have a Main Mode state.
             * ??? what if this is a duplicate of another message?
             */
@@ -1529,7 +1544,7 @@ process_packet(struct msg_digest **mdp)
 
            if (st == NULL)
            {
-               plog("Mode Config message is for a non-existent (expired?)"
+               plog("ModeCfg message is for a non-existent (expired?)"
                    " ISAKMP SA");
                /* XXX Could send notification back */
                return;
@@ -1537,9 +1552,18 @@ process_packet(struct msg_digest **mdp)
 
            set_cur_state(st);
 
+           /* the XAUTH_STATUS message might have a new msgid */
+           if (st->st_state == STATE_XAUTH_I1)
+           {
+               init_phase2_iv(st, &md->hdr.isa_msgid);
+               new_iv_set = TRUE;
+               from_state = st->st_state;
+               break;
+           }
+
            if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state))
            {
-               loglog(RC_LOG_SERIOUS, "Mode Config message is unacceptable because"
+               loglog(RC_LOG_SERIOUS, "ModeCfg message is unacceptable because"
                        " it is for an incomplete ISAKMP SA (state=%s)"
                        , enum_name(&state_names, st->st_state));
                /* XXX Could send notification back */
@@ -1568,7 +1592,16 @@ process_packet(struct msg_digest **mdp)
             *
             */
 
-           if (st->st_connection->spd.that.modecfg
+           has_xauth_policy = (st->st_connection->policy
+                              & (POLICY_XAUTH_RSASIG | POLICY_XAUTH_PSK))
+                              != LEMPTY;
+
+           if (has_xauth_policy && !st->st_xauth.started
+           && IS_PHASE1(st->st_state))
+           {
+               from_state = STATE_XAUTH_I0;
+           }
+           else if (st->st_connection->spd.that.modecfg
            && IS_PHASE1(st->st_state))
            {
                from_state = STATE_MODE_CFG_R0;
@@ -1576,12 +1609,12 @@ process_packet(struct msg_digest **mdp)
            else if (st->st_connection->spd.this.modecfg
            && IS_PHASE1(st->st_state))
            {
-               from_state = STATE_MODE_CFG_R1;
+               from_state = STATE_MODE_CFG_I0;
            }
            else
            {
                /* XXX check if we are being a mode config server here */
-               plog("received MODECFG message when in state %s, and we aren't mode config client"
+               plog("received ModeCfg message when in state %s, and we aren't mode config client"
                     , enum_name(&state_names, st->st_state));
                return;
            }
@@ -1591,7 +1624,6 @@ process_packet(struct msg_digest **mdp)
            set_cur_state(st);
            from_state = st->st_state;
        }
-
        break;
 
 #ifdef NOTYET
@@ -1633,7 +1665,23 @@ process_packet(struct msg_digest **mdp)
 
     if (st != NULL)
     {
-       while (!LHAS(smc->flags, st->st_oakley.auth))
+       u_int16_t auth;
+
+       switch (st->st_oakley.auth)
+       {
+       case XAUTHInitPreShared:
+       case XAUTHRespPreShared:
+           auth = OAKLEY_PRESHARED_KEY;
+           break;
+       case XAUTHInitRSA:
+       case XAUTHRespRSA:
+           auth = OAKLEY_RSA_SIG;
+           break;
+       default:
+           auth = st->st_oakley.auth;
+       }
+       
+       while (!LHAS(smc->flags, auth))
        {
            smc++;
            passert(smc->state == from_state);
@@ -1809,12 +1857,12 @@ process_packet(struct msg_digest **mdp)
                return;
            }
 
-#ifdef NAT_TRAVERSAL
            switch (np)
            {
                case ISAKMP_NEXT_NATD_RFC:
                case ISAKMP_NEXT_NATOA_RFC:
-                   if ((!st) || (!(st->nat_traversal & NAT_T_WITH_RFC_VALUES))) {
+                   if (!st || !(st->nat_traversal & NAT_T_WITH_RFC_VALUES))
+                   {
                        /*
                         * don't accept NAT-D/NAT-OA reloc directly in message, unless
                         * we're using NAT-T RFC
@@ -1823,7 +1871,6 @@ process_packet(struct msg_digest **mdp)
                    }
                    break;
            }
-#endif
 
            if (sd == NULL)
            {
@@ -1834,7 +1881,6 @@ process_packet(struct msg_digest **mdp)
                    sd = IS_PHASE1(from_state)
                        ? &isakmp_identification_desc : &isakmp_ipsec_identification_desc;
                    break;
-#ifdef NAT_TRAVERSAL
                case ISAKMP_NEXT_NATD_DRAFTS:
                    np = ISAKMP_NEXT_NATD_RFC;  /* NAT-D relocated */
                    sd = payload_descs[np];
@@ -1843,7 +1889,6 @@ process_packet(struct msg_digest **mdp)
                    np = ISAKMP_NEXT_NATOA_RFC;  /* NAT-OA relocated */
                    sd = payload_descs[np];
                    break;
-#endif             
                default:
                    loglog(RC_LOG_SERIOUS, "%smessage ignored because it contains an unknown or"
                        " unexpected payload type (%s) at the outermost level"
@@ -2049,6 +2094,8 @@ process_packet(struct msg_digest **mdp)
 void
 complete_state_transition(struct msg_digest **mdp, stf_status result)
 {
+    bool has_xauth_policy;
+    bool is_xauth_server;
     struct msg_digest *md = *mdp;
     const struct state_microcode *smc = md->smc;
     enum state_kind from_state = md->from_state;
@@ -2107,10 +2154,8 @@ complete_state_transition(struct msg_digest **mdp, stf_status result)
                clonetochunk(st->st_tpacket, md->reply.start
                    , pbs_offset(&md->reply), "reply packet");
 
-#ifdef NAT_TRAVERSAL
                if (nat_traversal_enabled)
                    nat_traversal_change_port_lookup(md, md->st);
-#endif
 
                /* actually send the packet
                 * Note: this is a great place to implement "impairments"
@@ -2266,7 +2311,7 @@ complete_state_transition(struct msg_digest **mdp, stf_status result)
                    }
                    /* advance b to end of string */
                    b = b + strlen(b);
-                   
+
                    if (st->st_ah.present)
                    {
                        snprintf(b, sizeof(sadetails)-(b-sadetails)-1
@@ -2279,7 +2324,7 @@ complete_state_transition(struct msg_digest **mdp, stf_status result)
                    }
                    /* advance b to end of string */
                    b = b + strlen(b);
-                   
+
                    if (st->st_ipcomp.present)
                    {
                        snprintf(b, sizeof(sadetails)-(b-sadetails)-1
@@ -2293,7 +2338,6 @@ complete_state_transition(struct msg_digest **mdp, stf_status result)
                    /* advance b to end of string */
                    b = b + strlen(b);
 
-#ifdef NAT_TRAVERSAL
                    if (st->nat_traversal)
                    {
                        char oa[ADDRTOT_BUF];
@@ -2304,7 +2348,6 @@ complete_state_transition(struct msg_digest **mdp, stf_status result)
                        ini = " ";
                        fin = "}";
                    }
-#endif
 
                    /* advance b to end of string */
                    b = b + strlen(b);
@@ -2322,7 +2365,7 @@ complete_state_transition(struct msg_digest **mdp, stf_status result)
                }
 
                if (IS_ISAKMP_SA_ESTABLISHED(st->st_state)
-               || IS_IPSEC_SA_ESTABLISHED(st->st_state))
+               ||  IS_IPSEC_SA_ESTABLISHED(st->st_state))
                {
                    /* log our success */
                    plog("%s%s", story, sadetails);
@@ -2336,38 +2379,69 @@ complete_state_transition(struct msg_digest **mdp, stf_status result)
                    , story, sadetails);
            }
 
-           /* Should we start Mode Config as a client */
+           has_xauth_policy = (st->st_connection->policy
+                              & (POLICY_XAUTH_RSASIG | POLICY_XAUTH_PSK))
+                              != LEMPTY;
+           is_xauth_server =  (st->st_connection->policy
+                              & POLICY_XAUTH_SERVER)
+                              != LEMPTY;
+
+           /* Should we start XAUTH as a server */
+           if (has_xauth_policy && is_xauth_server
+           && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
+           && !st->st_xauth.started)
+           {
+               DBG(DBG_CONTROL,
+                   DBG_log("starting XAUTH server")
+               )
+               xauth_send_request(st);
+               break;
+           }
+
+           /* Wait for XAUTH request from server */
+           if (has_xauth_policy && !is_xauth_server
+           && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
+           && !st->st_xauth.started)
+           {
+               DBG(DBG_CONTROL,
+                   DBG_log("waiting for XAUTH request from server")
+               )
+               break;
+           }
+
+           /* Should we start ModeConfig as a client? */
            if (st->st_connection->spd.this.modecfg
            && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
+           && !(st->st_connection->policy & POLICY_MODECFG_PUSH)
            && !st->st_modecfg.started)
            {
                DBG(DBG_CONTROL,
-                   DBG_log("modecfg client is starting")
+                   DBG_log("starting ModeCfg client in pull mode")
                )
                modecfg_send_request(st);
                break;
            }
 
-           /* Should we set the peer's IP address regardless? */
-/*         if (st->st_connection->spd.that.modecfg
+           /* Should we start ModeConfig as a server? */
+           if (st->st_connection->spd.that.modecfg
            && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
-           && !st->st_modecfg.vars_set
-           && !(st->st_connection->policy & POLICY_MODECFG_PULL))
+           && !st->st_modecfg.started
+           && (st->st_connection->policy & POLICY_MODECFG_PUSH))
            {
-               st->st_state = STATE_MODE_CFG_R1;
-               set_cur_state(st);
-               plog("Sending MODE CONFIG set");
-               modecfg_start_set(st);
+               DBG(DBG_CONTROL,
+                   DBG_log("starting ModeCfg server in push mode")
+               )
+               modecfg_send_set(st);
                break;
            }
-*/
-           /* wait for modecfg_set */
+
+           /* Wait for ModeConfig set from server */
            if (st->st_connection->spd.this.modecfg
            && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
            && !st->st_modecfg.vars_set)
            {
                DBG(DBG_CONTROL,
-                   DBG_log("waiting for modecfg set from server")
+                   DBG_log("waiting for ModeCfg set from server")
                )
                break;
            }
@@ -2386,7 +2460,7 @@ complete_state_transition(struct msg_digest **mdp, stf_status result)
            }
 
            if (IS_ISAKMP_SA_ESTABLISHED(st->st_state)
-           || IS_IPSEC_SA_ESTABLISHED(st->st_state))
+           ||  IS_IPSEC_SA_ESTABLISHED(st->st_state))
                release_whack(st);
            break;