XAUTH interoperability with Cisco
[strongswan.git] / src / pluto / demux.c
index 10544b2..7e59b18 100644 (file)
@@ -436,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_MODE_CFG_R2, STATE_UNDEFINED
+    { 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_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
@@ -1228,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;
     }
 
@@ -1417,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))
@@ -1451,7 +1497,7 @@ process_packet(struct msg_digest **mdp)
        }
        else
        {
-           set_cur_state(st);
+           set_cur_state(st);  
            from_state = st->st_state;
        }
 
@@ -1460,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;
@@ -1468,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;
@@ -1476,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;
@@ -1487,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?
             */
@@ -1496,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;
@@ -1504,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 */
@@ -1535,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;
@@ -1543,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;
            }
@@ -1558,7 +1624,6 @@ process_packet(struct msg_digest **mdp)
            set_cur_state(st);
            from_state = st->st_state;
        }
-
        break;
 
 #ifdef NOTYET
@@ -1600,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);
@@ -2013,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;
@@ -2228,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
@@ -2241,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
@@ -2282,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);
@@ -2296,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;
            }
@@ -2346,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;