added XAUTH server and client support
authorAndreas Steffen <andreas.steffen@strongswan.org>
Tue, 19 Dec 2006 22:51:48 +0000 (22:51 -0000)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Tue, 19 Dec 2006 22:51:48 +0000 (22:51 -0000)
12 files changed:
src/pluto/connections.c
src/pluto/constants.c
src/pluto/constants.h
src/pluto/demux.c
src/pluto/ike_alg.c
src/pluto/ipsec_doi.c
src/pluto/keys.c
src/pluto/keys.h
src/pluto/modecfg.c
src/pluto/modecfg.h
src/pluto/spdb.c
src/pluto/spdb.h

index 960aaa1..eb15701 100644 (file)
@@ -1043,10 +1043,10 @@ add_connection(const whack_message_t *wm)
        c->sa_keying_tries = wm->sa_keying_tries;
 
        /* RFC 3706 DPD */
-        c->dpd_delay = wm->dpd_delay;
-        c->dpd_timeout = wm->dpd_timeout;
-        c->dpd_action = wm->dpd_action;
-       
+       c->dpd_delay = wm->dpd_delay;
+       c->dpd_timeout = wm->dpd_timeout;
+       c->dpd_action = wm->dpd_action;
+
        c->addr_family = wm->addr_family;
        c->tunnel_addr_family = wm->tunnel_addr_family;
 
@@ -3213,13 +3213,17 @@ find_host_connection(const ip_address *me, u_int16_t my_port
 
     if (policy != LEMPTY)
     {
+       lset_t auth_requested  = policy & POLICY_ID_AUTH_MASK;
+
        /* if we have requirements for the policy,
         * choose the first matching connection.
         */
        while (c != NULL)
        {
-           if ((c->policy & policy) == policy)
-           break;
+           if (c->policy & auth_requested)
+           {
+               break;
+           }
            c = c->hp_next;
        }
     }
@@ -3326,11 +3330,17 @@ refine_host_connection(const struct state *st, const struct id *peer_id
        if (psk == NULL)
            return NULL;        /* cannot determine PSK! */
        break;
-
+    case XAUTHInitPreShared:
+    case XAUTHRespPreShared:
+       auth_policy = POLICY_XAUTH_PSK;
+       break;
     case OAKLEY_RSA_SIG:
        auth_policy = POLICY_RSASIG;
        break;
-
+    case XAUTHInitRSA:
+    case XAUTHRespRSA:
+       auth_policy = POLICY_XAUTH_RSASIG;
+       break;
     default:
        bad_case(auth);
     }
@@ -3353,17 +3363,21 @@ refine_host_connection(const struct state *st, const struct id *peer_id
 
            bool matching_id = match_id(peer_id
                                        , &d->spd.that.id, &wildcards);
+           bool matching_auth = (d->policy & auth_policy) != LEMPTY;
+
            bool matching_trust = trusted_ca(peer_ca
                                        , d->spd.that.ca, &peer_pathlen);
            bool matching_request = match_requested_ca(c->requested_ca
                                        , d->spd.this.ca, &our_pathlen);
-           bool match = matching_id && matching_trust && matching_request;
-           
+           bool match = matching_id && matching_auth &&
+                        matching_trust && matching_request;
+
            DBG(DBG_CONTROLMORE,
-               DBG_log("%s: %s match (id: %s, trust: %s, request: %s)"
+               DBG_log("%s: %s match (id: %s, auth: %s, trust: %s, request: %s)"
                    , d->name
                    , match ? "full":" no"
                    , match_name[matching_id]
+                   , match_name[matching_auth]
                    , match_name[matching_trust]
                    , match_name[matching_request])
            )
@@ -3382,13 +3396,11 @@ refine_host_connection(const struct state *st, const struct id *peer_id
                continue;
            }
 
-           /* authentication used must fit policy of this connection */
-           if ((d->policy & auth_policy) == LEMPTY)
-               continue;       /* our auth isn't OK for this connection */
-
            switch (auth)
            {
            case OAKLEY_PRESHARED_KEY:
+           case XAUTHInitPreShared:
+           case XAUTHRespPreShared:
                /* secret must match the one we already used */
                {
                    const chunk_t *dpsk = get_preshared_secret(d);
@@ -3404,6 +3416,8 @@ refine_host_connection(const struct state *st, const struct id *peer_id
                break;
 
            case OAKLEY_RSA_SIG:
+           case XAUTHInitRSA:
+           case XAUTHRespRSA:
                /*
                 * We must at least be able to find our private key
                .*/
index 53de6f6..3ba5bc0 100644 (file)
@@ -183,8 +183,10 @@ static const char *const state_name[] = {
        "STATE_INFO",
        "STATE_INFO_PROTECTED",
 
-       "STATE_XAUTH_R0",
        "STATE_XAUTH_R1",
+       "STATE_XAUTH_R2",
+       "STATE_XAUTH_R3",
+       "STATE_XAUTH_I0",
        "STATE_XAUTH_I1",
        "STATE_XAUTH_I2",
 
@@ -222,16 +224,18 @@ const char *const state_story[] = {
        "got Informational Message in clear",    /* STATE_INFO */
        "got encrypted Informational Message",   /* STATE_INFO_PROTECTED */
 
-       "sent XAUTH request, expecting reply",   /* STATE_XAUTH_R0 */
-       "sent XAUTH status, expecting ack",      /* STATE_XAUTH_R1 */
-       "received XAUTH request, sent reply",    /* STATE_XAUTH_I1 */
-       "received XAUTH status, sent ack",       /* STATE_XAUTH_I2 */
+       "sent XAUTH request, expecting reply",   /* STATE_XAUTH_R1 */
+       "sent XAUTH status, expecting ack",      /* STATE_XAUTH_R2 */
+       "received XAUTH ack, established",       /* STATE_XAUTH_R3 */
+       "expecting XAUTH request",               /* STATE_XAUTH_I0 */
+       "sent XAUTH reply, expecting status",    /* STATE_XAUTH_I1 */
+       "sent XAUTH ack, established",           /* STATE_XAUTH_I2 */
 
-       "sent ModeCfg reply",                    /* STATE_MODE_CFG_R0 */
-       "sent ModeCfg reply",                    /* STATE_MODE_CFG_R1 */
-       "received ModeCfg ack",                  /* STATE_MODE_CFG_R2 */
+       "expecting ModeCfg request",             /* STATE_MODE_CFG_R0 */
+       "sent ModeCfg reply, expecting ack",     /* STATE_MODE_CFG_R1 */
+       "received ModeCfg ack, established"      /* STATE_MODE_CFG_R2 */
        "sent ModeCfg request, expecting reply", /* STATE_MODE_CFG_I1 */
-       "received ModeCfg reply",                /* STATE_MODE_CFG_I2 */
+       "sent ModeCfg ack, established",         /* STATE_MODE_CFG_I2 */
        "received ModeCfg set, sent ack",        /* STATE_MODE_CFG_I3 */
     };
 
index 9ff74a4..1295b29 100644 (file)
@@ -508,10 +508,12 @@ enum state_kind {
 
     /* XAUTH states */
 
-    STATE_XAUTH_R0,              /* responder states (server) */
-    STATE_XAUTH_R1,
+    STATE_XAUTH_R1,              /* responder states (server) */
+    STATE_XAUTH_R2,
+    STATE_XAUTH_R3,
 
-    STATE_XAUTH_I1,              /* initiator states (client) */
+    STATE_XAUTH_I0,              /* initiator states (client) */
+    STATE_XAUTH_I1,
     STATE_XAUTH_I2,
 
     /* Mode Config states */
@@ -531,18 +533,26 @@ enum state_kind {
 
 #define PHASE1_INITIATOR_STATES         (LELEM(STATE_MAIN_I1) | LELEM(STATE_MAIN_I2) \
     | LELEM(STATE_MAIN_I3) | LELEM(STATE_MAIN_I4))
-#define ISAKMP_SA_ESTABLISHED_STATES  (LELEM(STATE_MAIN_R3) | LELEM(STATE_MAIN_I4) \
+#define ISAKMP_SA_ESTABLISHED_STATES ( \
+      LELEM(STATE_MAIN_R3)     | LELEM(STATE_MAIN_I4)     \
+    | LELEM(STATE_XAUTH_R1)    | LELEM(STATE_XAUTH_R2) | LELEM(STATE_XAUTH_R3) \
+    | LELEM(STATE_XAUTH_I1)    | LELEM(STATE_XAUTH_I2)    \
     | LELEM(STATE_MODE_CFG_R1) | LELEM(STATE_MODE_CFG_R2) \
     | LELEM(STATE_MODE_CFG_I2) | LELEM(STATE_MODE_CFG_I3))
 
 #define IS_PHASE1(s) ((STATE_MAIN_R0 <= (s) && (s) <= STATE_MAIN_I4) \
+                  || (STATE_XAUTH_R1 <= (s) && (s) <= STATE_XAUTH_I2) \
                   || (STATE_MODE_CFG_R0 <= (s) && (s) <= STATE_MODE_CFG_I3))
 #define IS_QUICK(s) (STATE_QUICK_R0 <= (s) && (s) <= STATE_QUICK_R2)
 #define IS_ISAKMP_ENCRYPTED(s) (STATE_MAIN_I2 <= (s))
 #define IS_ISAKMP_SA_ESTABLISHED(s) (         \
-                                        (s) == STATE_MAIN_R3     \
+                                    (s) == STATE_MAIN_R3     \
                                  || (s) == STATE_MAIN_I4     \
-                                 || (s) == STATE_MODE_CFG_R0 \
+                                 || (s) == STATE_XAUTH_R1    \
+                                 || (s) == STATE_XAUTH_R2    \
+                                 || (s) == STATE_XAUTH_R3    \
+                                 || (s) == STATE_XAUTH_I1    \
+                                 || (s) == STATE_XAUTH_I2    \
                                  || (s) == STATE_MODE_CFG_R1 \
                                  || (s) == STATE_MODE_CFG_R2 \
                                  || (s) == STATE_MODE_CFG_I2 \
index adccf1b..564988c 100644 (file)
@@ -436,6 +436,38 @@ static const struct state_microcode state_microcode_table[] = {
     , P(HASH), LEMPTY, PT(NONE)
     , EVENT_NULL, informational },
 
+    /* XAUTH server */
+    { STATE_XAUTH_R1, STATE_XAUTH_R2
+    , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY
+    , P(ATTR) | P(HASH), P(VID), PT(HASH)
+    , EVENT_RETRANSMIT, xauth_inR1 },
+
+    { 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_R3, STATE_UNDEFINED
+    , SMF_ALL_AUTH | SMF_ENCRYPTED
+    , LEMPTY, LEMPTY, PT(NONE)
+    , EVENT_NULL, unexpected },
+
+    /* XAUTH client */
+    { STATE_XAUTH_I0, STATE_XAUTH_I1
+    , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY
+    , P(ATTR) | P(HASH), P(VID), PT(HASH)
+    , EVENT_SA_REPLACE, xauth_inI0 },
+
+    { 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_I2, STATE_UNDEFINED
+    , SMF_ALL_AUTH | SMF_ENCRYPTED
+    , LEMPTY, LEMPTY, PT(NONE)
+    , EVENT_NULL, unexpected },
+
     /* MODE_CFG_x:
      * Case R0:  Responder  -> Initiator
      *                    <-   Req(addr=0)
@@ -1238,6 +1270,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;
     }
 
@@ -1470,7 +1507,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;
@@ -1478,7 +1515,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;
@@ -1486,7 +1523,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;
@@ -1497,7 +1534,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?
             */
@@ -1506,7 +1545,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;
@@ -1516,7 +1555,7 @@ process_packet(struct msg_digest **mdp)
 
            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 */
@@ -1545,7 +1584,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
+           && 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;
@@ -1558,7 +1606,7 @@ process_packet(struct msg_digest **mdp)
            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;
            }
@@ -1610,7 +1658,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);
@@ -2023,6 +2087,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;
@@ -2238,7 +2304,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
@@ -2251,7 +2317,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
@@ -2306,6 +2372,25 @@ complete_state_transition(struct msg_digest **mdp, stf_status result)
                    , story, sadetails);
            }
 
+           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;
+           }
+
            /* Should we start ModeConfig as a client? */
            if (st->st_connection->spd.this.modecfg
            && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
@@ -2332,6 +2417,17 @@ complete_state_transition(struct msg_digest **mdp, stf_status result)
                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;
+           }
+
            /* Wait for ModeConfig set from server */
            if (st->st_connection->spd.this.modecfg
            && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
index 43cb24e..a622858 100644 (file)
@@ -233,6 +233,7 @@ ike_alg_db_new(struct alg_info_ike *ai , lset_t policy)
     struct ike_info *ike_info;
     u_int ealg, halg, modp, eklen = 0;
     struct encrypt_desc *enc_desc;
+    bool is_xauth_server;
     int i;
 
     if (!ai)
@@ -298,11 +299,37 @@ ike_alg_db_new(struct alg_info_ike *ai , lset_t policy)
            db_trans_add(db_ctx, KEY_IKE);
            db_attr_add_values(db_ctx, OAKLEY_ENCRYPTION_ALGORITHM, ealg);
            db_attr_add_values(db_ctx, OAKLEY_HASH_ALGORITHM, halg);
-           if (ike_info->ike_eklen)
-               db_attr_add_values(db_ctx, OAKLEY_KEY_LENGTH, ike_info->ike_eklen);
+           if (eklen)
+               db_attr_add_values(db_ctx, OAKLEY_KEY_LENGTH, eklen);
            db_attr_add_values(db_ctx, OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY);
            db_attr_add_values(db_ctx, OAKLEY_GROUP_DESCRIPTION, modp);
        }
+
+       is_xauth_server = (policy & POLICY_XAUTH_SERVER) != LEMPTY;
+
+       if (policy & POLICY_XAUTH_RSASIG)
+       {
+           db_trans_add(db_ctx, KEY_IKE);
+           db_attr_add_values(db_ctx, OAKLEY_ENCRYPTION_ALGORITHM, ealg);
+           db_attr_add_values(db_ctx, OAKLEY_HASH_ALGORITHM, halg);
+           if (eklen)
+               db_attr_add_values(db_ctx, OAKLEY_KEY_LENGTH, eklen);
+           db_attr_add_values(db_ctx, OAKLEY_AUTHENTICATION_METHOD
+               , is_xauth_server ? XAUTHRespRSA : XAUTHInitRSA);
+           db_attr_add_values(db_ctx, OAKLEY_GROUP_DESCRIPTION, modp);
+       }
+
+       if (policy & POLICY_XAUTH_PSK)
+       {
+           db_trans_add(db_ctx, KEY_IKE);
+           db_attr_add_values(db_ctx, OAKLEY_ENCRYPTION_ALGORITHM, ealg);
+           db_attr_add_values(db_ctx, OAKLEY_HASH_ALGORITHM, halg);
+           if (eklen)
+               db_attr_add_values(db_ctx, OAKLEY_KEY_LENGTH, eklen);
+           db_attr_add_values(db_ctx, OAKLEY_AUTHENTICATION_METHOD
+               , is_xauth_server ? XAUTHRespPreShared : XAUTHInitPreShared);
+           db_attr_add_values(db_ctx, OAKLEY_GROUP_DESCRIPTION, modp);
+       }
     }
 fail:
     return db_ctx;
index a17eae0..9d53a1a 100644 (file)
@@ -950,8 +950,8 @@ main_outI1(int whack_sock, struct connection *c, struct state *predecessor
        u_char *sa_start = rbody.cur;
        lset_t auth_policy = policy & POLICY_ID_AUTH_MASK;
 
-       if (!out_sa(&rbody, &oakley_sadb[auth_policy >> POLICY_ISAKMP_SHIFT]
-       , st, TRUE, vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE))
+       if (!out_sa(&rbody, &oakley_sadb, st, TRUE
+       , vids_to_send-- ? ISAKMP_NEXT_VID : ISAKMP_NEXT_NONE))
        {
            reset_cur_state();
            return STF_INTERNAL_ERROR;
@@ -1211,11 +1211,15 @@ generate_skeyids_iv(struct state *st)
     switch (st->st_oakley.auth)
     {
        case OAKLEY_PRESHARED_KEY:
+       case XAUTHInitPreShared:
+       case XAUTHRespPreShared:
            if (!skeyid_preshared(st))
                return FALSE;
            break;
 
        case OAKLEY_RSA_SIG:
+       case XAUTHInitRSA:
+       case XAUTHRespRSA:
            if (!skeyid_digisig(st))
                return FALSE;
            break;
@@ -3151,7 +3155,7 @@ main_inI1_outR1(struct msg_digest *md)
 
     /* SA body in and out */
     RETURN_STF_FAILURE(parse_isakmp_sa_body(ipsecdoisit, &proposal_pbs
-       ,&proposal, &r_sa_pbs, st));
+       ,&proposal, &r_sa_pbs, st, FALSE));
 
     /* if enabled send Pluto Vendor ID */
     if (SEND_PLUTO_VID)
@@ -3258,7 +3262,7 @@ main_inR1_outI2(struct msg_digest *md)
            RETURN_STF_FAILURE(BAD_PROPOSAL_SYNTAX);
         }
        RETURN_STF_FAILURE(parse_isakmp_sa_body(ipsecdoisit
-           , &proposal_pbs, &proposal, NULL, st));
+           , &proposal_pbs, &proposal, NULL, st, TRUE));
     }
 
     if (nat_traversal_enabled && md->nat_traversal_vid)
@@ -3343,9 +3347,11 @@ main_inI2_outR2(struct msg_digest *md)
     pb_stream *keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs;
  
     /* send CR if auth is RSA and no preloaded RSA public key exists*/
-    bool send_cr = !no_cr_send && (st->st_oakley.auth == OAKLEY_RSA_SIG) &&
-                  !has_preloaded_public_key(st);
-   
+    bool RSA_auth = st->st_oakley.auth == OAKLEY_RSA_SIG
+                || st->st_oakley.auth == XAUTHInitRSA
+                || st->st_oakley.auth == XAUTHRespRSA;
+    bool send_cr = !no_cr_send && RSA_auth && !has_preloaded_public_key(st);
+
     u_int8_t np = ISAKMP_NEXT_NONE;
 
     /* KE in */
@@ -3488,6 +3494,10 @@ main_inR2_outI3(struct msg_digest *md)
     cert_t mycert = st->st_connection->spd.this.cert;
     bool requested, send_cert, send_cr;
 
+    bool RSA_auth = st->st_oakley.auth == OAKLEY_RSA_SIG
+                || st->st_oakley.auth == XAUTHInitRSA
+                || st->st_oakley.auth == XAUTHRespRSA;
+
     /* KE in */
     RETURN_STF_FAILURE(accept_KE(&st->st_gr, "Gr", st->st_oakley.group, keyex_pbs));
 
@@ -3509,8 +3519,7 @@ main_inR2_outI3(struct msg_digest *md)
      */
     requested = cert_policy == CERT_SEND_IF_ASKED
                && st->st_connection->got_certrequest;
-    send_cert = st->st_oakley.auth == OAKLEY_RSA_SIG
-               && mycert.type != CERT_NONE
+    send_cert = RSA_auth && mycert.type != CERT_NONE
                && (cert_policy == CERT_ALWAYS_SEND || requested);
 
     /* send certificate request if we don't have a preloaded RSA public key */
@@ -3554,7 +3563,7 @@ main_inR2_outI3(struct msg_digest *md)
     }
 
     /* CERT out */
-    if ( st->st_oakley.auth == OAKLEY_RSA_SIG)
+    if (RSA_auth)
     {
        DBG(DBG_CONTROL,
            DBG_log("our certificate policy is %s"
@@ -3710,6 +3719,8 @@ main_id_and_auth(struct msg_digest *md
     switch (st->st_oakley.auth)
     {
     case OAKLEY_PRESHARED_KEY:
+    case XAUTHInitPreShared:
+    case XAUTHRespPreShared:
        {
            pb_stream *const hash_pbs = &md->chain[ISAKMP_NEXT_HASH]->pbs;
 
@@ -3726,6 +3737,8 @@ main_id_and_auth(struct msg_digest *md
        break;
 
     case OAKLEY_RSA_SIG:
+    case XAUTHInitRSA:
+    case XAUTHRespRSA:
        r = RSA_check_signature(&peer, st, hash_val, hash_len
            , &md->chain[ISAKMP_NEXT_SIG]->pbs
 #ifdef USE_KEYRR
@@ -3903,6 +3916,7 @@ main_inI3_outR3_tail(struct msg_digest *md
     pb_stream r_id_pbs;        /* ID Payload; also used for hash calculation */
     certpolicy_t cert_policy;
     cert_t mycert;
+    bool RSA_auth;
     bool send_cert;
     bool requested;
 
@@ -3925,7 +3939,10 @@ main_inI3_outR3_tail(struct msg_digest *md
     mycert = st->st_connection->spd.this.cert;
     requested = cert_policy == CERT_SEND_IF_ASKED
                && st->st_connection->got_certrequest;
-    send_cert = st->st_oakley.auth == OAKLEY_RSA_SIG
+    RSA_auth = st->st_oakley.auth == OAKLEY_RSA_SIG
+           || st->st_oakley.auth == XAUTHInitRSA
+            || st->st_oakley.auth == XAUTHRespRSA;
+    send_cert = RSA_auth
                && mycert.type != CERT_NONE
                && (cert_policy == CERT_ALWAYS_SEND || requested);
 
@@ -3963,7 +3980,7 @@ main_inI3_outR3_tail(struct msg_digest *md
     }
 
     /* CERT out */
-    if (st->st_oakley.auth == OAKLEY_RSA_SIG)
+    if (RSA_auth)
     {
        DBG(DBG_CONTROL,
            DBG_log("our certificate policy is %s"
index 106573f..37cb6fb 100644 (file)
@@ -72,6 +72,7 @@ struct secret {
     union {
        chunk_t preshared_secret;
        RSA_private_key_t RSA_private_key;
+       xauth_t xauth_secret;
        smartcard_t *smartcard;
     } u;
     secret_t *next;
@@ -293,14 +294,12 @@ get_preshared_secret(const struct connection *c)
 {
     const secret_t *s = get_secret(c, PPK_PSK, FALSE);
 
-#ifdef DEBUG
     DBG(DBG_PRIVATE,
        if (s == NULL)
            DBG_log("no Preshared Key Found");
        else
            DBG_dump_chunk("Preshared Key", s->u.preshared_secret);
-       );
-#endif
+    )
     return s == NULL? NULL : &s->u.preshared_secret;
 }
 
@@ -584,6 +583,98 @@ process_rsa_keyfile(RSA_private_key_t *rsak, int whackfd)
 }
 
 /*
+ * process xauth secret read from ipsec.secrets
+ */
+static err_t
+process_xauth(secret_t *s)
+{
+    chunk_t user_name;
+    chunk_t user_password;
+
+    s->kind = PPK_XAUTH;
+
+    if (!shift())
+       return "missing xauth user name";
+    if (*tok == '"' || *tok == '\'')  /* quoted user name */
+    {
+       user_name.ptr = tok + 1;
+       user_name.len = flp->cur - tok - 2;
+    }
+    else
+    {
+       user_name.ptr = tok;
+       user_name.len = flp->cur - tok;
+    }
+    if (!shift())
+       return "missing xauth user password";
+    if (*tok == '"' || *tok == '\'')  /* quoted user password */
+    {
+       user_password.ptr = tok + 1;
+       user_password.len = flp->cur - tok - 2;
+    }
+    else
+    {
+       user_password.ptr = tok;
+       user_password.len = flp->cur - tok;
+    }
+    if (shift())
+       return "unexpected token after xauth user passpword";
+    clonetochunk(s->u.xauth_secret.user_name
+       , user_name.ptr, user_name.len, "user_name");
+    clonetochunk(s->u.xauth_secret.user_password
+       , user_password.ptr, user_password.len, "user_password");
+    return NULL;
+}
+
+/* get XAUTH secret from chained secrets lists
+ * only one entry is currently supported
+ */
+bool
+xauth_get_secret(xauth_t *xauth_secret)
+{
+    secret_t *s;
+    bool found = FALSE;
+
+    for (s = secrets; s != NULL; s = s->next)
+    {
+       if (s->kind == PPK_XAUTH)
+       {
+           if (found)
+           {
+               plog("found multiple xauth secrets - first selected");
+           }
+           else
+           {
+               found = TRUE;
+               *xauth_secret = s->u.xauth_secret;
+           }
+       }
+    }
+    return found;
+}
+
+/*
+ * find a matching secret
+ */
+bool
+xauth_verify_secret(const xauth_t *xauth_secret)
+{
+    secret_t *s;
+
+    for (s = secrets; s != NULL; s = s->next)
+    {
+       if (s->kind == PPK_XAUTH)
+       {
+           if (!same_chunk(xauth_secret->user_name, s->u.xauth_secret.user_name))
+               continue;
+           if (same_chunk(xauth_secret->user_password, s->u.xauth_secret.user_password))
+               return TRUE;
+       }
+    }
+    return FALSE;
+}
+
+/*
  * process pin read from ipsec.secrets or prompted for it using whack
  */
 static err_t
@@ -694,6 +785,10 @@ process_secret(secret_t *s, int whackfd)
           ugh = process_rsa_keyfile(&s->u.RSA_private_key, whackfd);
        }
     }
+    else if (tokeqword("xauth"))
+    {
+       ugh = process_xauth(s);
+    }
     else if (tokeqword("pin"))
     {
        ugh = process_pin(s, whackfd);
@@ -923,7 +1018,7 @@ void
 free_preshared_secrets(void)
 {
     lock_certs_and_keys("free_preshared_secrets");
-    
+
     if (secrets != NULL)
     {
        secret_t *s, *ns;
@@ -949,6 +1044,10 @@ free_preshared_secrets(void)
            case PPK_RSA:
                free_RSA_private_content(&s->u.RSA_private_key);
                break;
+           case PPK_XAUTH:
+               pfree(s->u.xauth_secret.user_name.ptr);
+               pfree(s->u.xauth_secret.user_password.ptr);
+               break;
            case PPK_PIN:
                scx_release(s->u.smartcard);
                break;
@@ -959,7 +1058,7 @@ free_preshared_secrets(void)
        }
        secrets = NULL;
     }
-    
+
     unlock_certs_and_keys("free_preshard_secrets");
 }
 
index acee844..3e583e4 100644 (file)
@@ -37,6 +37,7 @@ enum PrivateKeyKind {
     PPK_PSK,
  /* PPK_DSS, */        /* not implemented */
     PPK_RSA,
+    PPK_XAUTH,
     PPK_PIN
 };
 
@@ -100,11 +101,21 @@ extern void add_pgp_public_key(pgpcert_t *cert, time_t until
 extern void remove_x509_public_key(const x509cert_t *cert);
 extern void list_public_keys(bool utc);
 
+/* XAUTH credentials */
+
+typedef struct {
+    chunk_t user_name;
+    chunk_t user_password;
+} xauth_t;
+
+extern bool xauth_get_secrect(const xauth_t *xauth_secret);
+extern bool xauth_verify_secret(const xauth_t *xauth_secret);
+
 struct gw_info;        /* forward declaration of tag (defined in dnskey.h) */
 extern void transfer_to_public_keys(struct gw_info *gateways_from_dns
 #ifdef USE_KEYRR
     , pubkey_list_t **keys
 #endif /* USE_KEYRR */
     );
-    
+
 #endif /* _KEYS_H */
index 439293a..af2ee30 100644 (file)
@@ -39,6 +39,7 @@
 #include "crypto.h"
 #include "modecfg.h"
 #include "whack.h"
+#include "keys.h"
 
 #define SUPPORTED_ATTR_SET   ( LELEM(INTERNAL_IP4_ADDRESS) \
                              | LELEM(INTERNAL_IP4_NETMASK) \
@@ -53,10 +54,17 @@ typedef struct internal_addr internal_addr_t;
 
 struct internal_addr
 {
-    lset_t        attr_set;
-    ip_address    ipaddr;
-    ip_address    dns[2];
-    ip_address    wins[2];
+    lset_t attr_set;
+
+    /* ModeCfg variables */
+    ip_address ipaddr;
+    ip_address dns[2];
+    ip_address wins[2];
+
+    /* XAUTH variables */
+    u_int16_t  xauth_type;
+    xauth_t    xauth_secret;
+    bool       xauth_status;
 };
 
 /*
@@ -66,6 +74,10 @@ static void
 init_internal_addr(internal_addr_t *ia)
 {
     ia->attr_set = LEMPTY;
+    ia->xauth_secret.user_name = empty_chunk;
+    ia->xauth_secret.user_password = empty_chunk;
+    ia->xauth_status = FALSE;
+
     anyaddr(AF_INET, &ia->ipaddr);
     anyaddr(AF_INET, &ia->dns[0]);
     anyaddr(AF_INET, &ia->dns[1]);
@@ -218,7 +230,20 @@ modecfg_build_msg(struct state *st, pb_stream *rbody
                u_int len;
 
                /* ISAKMP attr out */
-               attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TLV;
+               if (attr_type == XAUTH_TYPE)
+               {
+                   attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TV;
+                   attr.isaat_lv = ia->xauth_type;
+               }
+               else if (attr_type == XAUTH_STATUS)
+               {
+                   attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TV;
+                   attr.isaat_lv = ia->xauth_status;
+               }
+               else
+               {
+                   attr.isaat_af_type = attr_type | ISAKMP_ATTR_AF_TLV;
+               }
                out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, &attrval);
 
                switch (attr_type)
@@ -288,14 +313,34 @@ modecfg_build_msg(struct state *st, pb_stream *rbody
                case INTERNAL_IP4_NBNS:
                    if (!isanyaddr(&ia->wins[wins_idx]))
                    {
-                       len = addrbytesptr(&ia->wins[wins_idx++], &byte_ptr);
-                       out_raw(byte_ptr, len, &attrval, "IP4_wins");
+                       len = addrbytesptr(&ia->wins[wins_idx++], &byte_ptr);
+                       out_raw(byte_ptr, len, &attrval, "IP4_wins");
                    }
                    if (wins_idx < 2 && !isanyaddr(&ia->wins[wins_idx]))
                    {
                        dont_advance = TRUE;
                    }
-                   break;
+                   break;
+               case XAUTH_TYPE:
+                   break;
+               case XAUTH_USER_NAME:
+                   if (ia->xauth_secret.user_name.ptr != NULL)
+                   {
+                       out_raw(ia->xauth_secret.user_name.ptr
+                             , ia->xauth_secret.user_name.len
+                             , &attrval, "xauth_user_name");
+                   }
+                   break;
+               case XAUTH_USER_PASSWORD:
+                   if (ia->xauth_secret.user_password.ptr != NULL)
+                   {
+                       out_raw(ia->xauth_secret.user_password.ptr
+                             , ia->xauth_secret.user_password.len
+                             , &attrval, "xauth_user_password");
+                   }
+                   break;
+               case XAUTH_STATUS:
+                   break;
                default:
                    plog("attempt to send unsupported mode cfg attribute %s."
                         , enum_show(&modecfg_attr_names, attr_type));
@@ -306,13 +351,17 @@ modecfg_build_msg(struct state *st, pb_stream *rbody
            if (!dont_advance)
            {
                attr_type++;
+               if (attr_type == MODECFG_ROOF)
+               {
+                   attr_type = XAUTH_BASE;
+               }
                attr_set >>= 1;
            }
        }
        close_message(&strattr);
     }
 
-    modecfg_hash(r_hashval, r_hash_start, rbody->cur,st);
+    modecfg_hash(r_hashval, r_hash_start, rbody->cur, st);
     close_message(rbody);
     encrypt_message(rbody, st);
     return STF_OK;
@@ -371,7 +420,6 @@ modecfg_send_msg(struct state *st, int isama_type, internal_addr_t *ia)
        delete_event(st);
        event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st);
     }
-    st->st_modecfg.started = TRUE;
     return STF_OK;
 }
 
@@ -381,6 +429,7 @@ modecfg_send_msg(struct state *st, int isama_type, internal_addr_t *ia)
 stf_status
 modecfg_send_request(struct state *st)
 {
+    stf_status stat;
     internal_addr_t ia;
 
     init_internal_addr(&ia);
@@ -389,7 +438,10 @@ modecfg_send_request(struct state *st)
 
     plog("sending ModeCfg request");
     st->st_state = STATE_MODE_CFG_I1;
-    return modecfg_send_msg(st, ISAKMP_CFG_REQUEST, &ia);
+    stat = modecfg_send_msg(st, ISAKMP_CFG_REQUEST, &ia);
+    if (stat == STF_OK)
+       st->st_modecfg.started = TRUE;
+    return stat;
 }
 
 /*
@@ -398,13 +450,38 @@ modecfg_send_request(struct state *st)
 stf_status
 modecfg_send_set(struct state *st)
 {
+    stf_status stat;
     internal_addr_t ia;
 
     get_internal_addr(st->st_connection, &ia);
 
     plog("sending ModeCfg set");
     st->st_state = STATE_MODE_CFG_R1;
-    return modecfg_send_msg(st, ISAKMP_CFG_SET, &ia);
+    stat = modecfg_send_msg(st, ISAKMP_CFG_SET, &ia);
+    if (stat == STF_OK)
+       st->st_modecfg.started = TRUE;
+    return stat;
+}
+
+/*
+ * Send XAUTH credentials request (username + password)
+ */
+stf_status
+xauth_send_request(struct state *st)
+{
+    stf_status stat;
+    internal_addr_t ia;
+
+    init_internal_addr(&ia);
+    ia.attr_set = LELEM(XAUTH_USER_NAME     - XAUTH_BASE + MODECFG_ROOF)
+               | LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE + MODECFG_ROOF);
+
+    plog("sending XAUTH request");
+    st->st_state = STATE_XAUTH_R1;
+    stat = modecfg_send_msg(st, ISAKMP_CFG_REQUEST, &ia);
+    if (stat == STF_OK)
+       st->st_xauth.started = TRUE;
+    return stat;
 }
 
 /*
@@ -442,6 +519,22 @@ modecfg_parse_attributes(pb_stream *attrs, internal_addr_t *ia)
        case INTERNAL_IP4_NBNS:
            ia->attr_set |= LELEM(attr_type);
            break;
+       case XAUTH_TYPE:
+           ia->xauth_type = attr.isaat_lv;
+           ia->attr_set |= LELEM(attr_type - XAUTH_BASE + MODECFG_ROOF);
+           break;
+       case XAUTH_USER_NAME:
+           setchunk(ia->xauth_secret.user_name, strattr.cur, attr_len);
+           ia->attr_set |= LELEM(attr_type - XAUTH_BASE + MODECFG_ROOF);
+           break;
+       case XAUTH_USER_PASSWORD:
+           setchunk(ia->xauth_secret.user_password, strattr.cur, attr_len);
+           ia->attr_set |= LELEM(attr_type - XAUTH_BASE + MODECFG_ROOF);
+           break;
+       case XAUTH_STATUS:
+           ia->xauth_status = attr.isaat_lv;
+           ia->attr_set |= LELEM(attr_type - XAUTH_BASE + MODECFG_ROOF);
+           break;
        default:
            plog("unsupported ModeCfg attribute %s received."
                , enum_show(&modecfg_attr_names, attr_type));
@@ -483,7 +576,7 @@ modecfg_parse_msg(struct msg_digest *md, int isama_type, u_int16_t *isama_id
            stat = modecfg_parse_attributes(&p->pbs, &ia_candidate);
            if (stat == STF_OK)
            {
-               /* retrun with a valid set of attributes */
+               /* return with a valid set of attributes */
                *ia = ia_candidate;
                return STF_OK;
            }
@@ -521,7 +614,8 @@ modecfg_inR0(struct msg_digest *md)
 
     get_internal_addr(st->st_connection, &ia);
 
-    /* build ISAKMP_CFG_REPLY */ 
+    plog("sending ModeCfg reply");
+
     stat = modecfg_build_msg(st, &md->rbody
                               , ISAKMP_CFG_REPLY
                               , &ia
@@ -611,6 +705,8 @@ modecfg_inI2(struct msg_digest *md)
     init_internal_addr(&ia);
     ia.attr_set = attr_set & SUPPORTED_ATTR_SET;
 
+    plog("sending ModeCfg ack");
+
     stat = modecfg_build_msg(st, &md->rbody
                               , ISAKMP_CFG_ACK
                               , &ia
@@ -625,3 +721,187 @@ modecfg_inI2(struct msg_digest *md)
     st->st_msgid = 0;
     return STF_OK;
 }
+
+/* STATE_XAUTH_R1:
+ *  HDR*, HASH, ATTR(REPLY=USERNAME/PASSWORD) --> HDR*, HASH, ATTR(STATUS)
+ *
+ *  used on the XAUTH server (responder)
+ */
+stf_status
+xauth_inR1(struct msg_digest *md)
+{
+    struct state *const st = md->st;
+    u_int16_t isama_id;
+    internal_addr_t ia;
+    stf_status stat;
+    bool status;
+
+    plog("parsing XAUTH reply");
+
+    stat = modecfg_parse_msg(md, ISAKMP_CFG_REPLY, &isama_id, &ia);
+    if (stat != STF_OK)
+       return stat;
+
+    /* check XAUTH reply */
+    if ((ia.attr_set & LELEM(XAUTH_STATUS - XAUTH_BASE + MODECFG_ROOF)) != LEMPTY)
+    {
+       plog("received FAIL status in XAUTH reply");
+       return STF_INTERNAL_ERROR;
+    }
+    if ((ia.attr_set & LELEM(XAUTH_USER_NAME - XAUTH_BASE + MODECFG_ROOF)) == LEMPTY)
+    {
+       plog("user name attribute is missing in XAUTH reply");
+       return STF_SUSPEND;
+    }
+    if ((ia.attr_set & LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE + MODECFG_ROOF)) == LEMPTY)
+    {
+       plog("user password attribute is missing in XAUTH reply");
+       return STF_SUSPEND;
+    }
+
+    status = xauth_verify_secret(&ia.xauth_secret);
+
+    /* prepare XAUTH set which sends the authentication status */
+    init_internal_addr(&ia);
+    ia.attr_set = LELEM(XAUTH_STATUS - XAUTH_BASE + MODECFG_ROOF);
+    ia.xauth_status = status;
+
+    plog("sending XAUTH status: %s", status? "OK":"FAIL");
+
+    stat = modecfg_build_msg(st, &md->rbody
+                              , ISAKMP_CFG_SET
+                              , &ia
+                              , isama_id);
+    return STF_OK;
+}
+
+/* STATE_XAUTH_R2:
+ * HDR*, ATTR(STATUS), HASH --> Done
+ *
+ * used on the XAUTH server (responder)
+ */
+stf_status
+xauth_inR2(struct msg_digest *md)
+{
+    struct state *const st = md->st;
+    u_int16_t isama_id;
+    internal_addr_t ia;
+    stf_status stat;
+
+    plog("parsing XAUTH ack");
+
+    stat = modecfg_parse_msg(md, ISAKMP_CFG_ACK, &isama_id, &ia);
+    if (stat != STF_OK)
+       return stat;
+
+    st->st_msgid = 0;
+    return STF_OK;
+}
+
+/* STATE_XAUTH_I0:
+ * HDR*, HASH, ATTR(REQ) --> HDR*, HASH, ATTR(REPLY=USERNAME/PASSWORD)
+ *
+ * used on the XAUTH client (initiator)
+ */
+stf_status
+xauth_inI0(struct msg_digest *md)
+{
+    struct state *const st = md->st;
+    u_int16_t isama_id;
+    internal_addr_t ia;
+    stf_status stat;
+
+    plog("parsing XAUTH request");
+
+    stat = modecfg_parse_msg(md, ISAKMP_CFG_REQUEST, &isama_id, &ia);
+    if (stat != STF_OK)
+       return stat;
+    /* check XAUTH request */
+    if ((ia.attr_set & LELEM(XAUTH_TYPE - XAUTH_BASE + MODECFG_ROOF)) != LEMPTY
+    && ia.xauth_type != XAUTH_TYPE_GENERIC)
+    {
+       plog("xauth type %s is not supported", enum_name(&xauth_type_names, ia.xauth_type));
+       stat = STF_FAIL;
+    }
+    if ((ia.attr_set & LELEM(XAUTH_USER_NAME - XAUTH_BASE + MODECFG_ROOF)) == LEMPTY)
+    {
+       plog("user name attribute is missing in XAUTH request");
+       stat = STF_FAIL;
+    }
+    if ((ia.attr_set & LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE + MODECFG_ROOF)) == LEMPTY)
+    {
+       plog("user password attribute is missing in XAUTH request");
+       stat = STF_FAIL;
+    }
+
+    /* prepare XAUTH reply */
+    init_internal_addr(&ia);
+
+    if (stat == STF_OK)
+    {
+       /* get user credentials */
+       if (!xauth_get_secret(&ia.xauth_secret))
+       {
+           plog("xauth user credentials not found");
+           stat = STF_FAIL;
+       }
+    }
+    if (stat == STF_OK)
+    {
+       ia.attr_set = LELEM(XAUTH_USER_NAME - XAUTH_BASE + MODECFG_ROOF)
+                   | LELEM(XAUTH_USER_PASSWORD - XAUTH_BASE + MODECFG_ROOF);
+    }
+    else
+    {
+       ia.attr_set = LELEM(XAUTH_STATUS - XAUTH_BASE + MODECFG_ROOF);
+       ia.xauth_status = FALSE;
+    }
+
+    plog("sending XAUTH reply");
+
+    stat = modecfg_build_msg(st, &md->rbody
+                              , ISAKMP_CFG_REPLY
+                              , &ia
+                              , isama_id);
+    if (stat != STF_OK)
+    {
+       /* notification payload - not exactly the right choice, but okay */
+       md->note = ATTRIBUTES_NOT_SUPPORTED;
+       return stat;
+    }
+    st->st_xauth.started = TRUE;
+    return STF_OK;
+}
+
+/* STATE_XAUTH_I1:
+ * HDR*, HASH, ATTR(STATUS) --> HDR*, HASH, ATTR(ACK)
+ *
+ * used on the XAUTH client (initiator)
+ */
+stf_status
+xauth_inI1(struct msg_digest *md)
+{
+     struct state *const st = md->st;
+    u_int16_t isama_id;
+    internal_addr_t ia;
+    stf_status stat;
+
+    plog("parsing XAUTH status");
+
+    stat = modecfg_parse_msg(md, ISAKMP_CFG_SET, &isama_id, &ia);
+    if (stat != STF_OK)
+       return stat;
+
+    /* prepare XAUTH set which sends the authentication status */
+    init_internal_addr(&ia);
+
+    plog("sending XAUTH ack");
+
+    stat = modecfg_build_msg(st, &md->rbody
+                              , ISAKMP_CFG_ACK
+                              , &ia
+                              , isama_id);
+    st->st_msgid = 0;
+    return stat;
+}
index 969a014..fe00067 100644 (file)
  * RCSID $Id: modecfg.h,v 1.1 2005/01/06 22:10:15 as Exp $
  */
 
+#ifndef _MODECFG_H
+#define _MODECFG_H
+
 struct state;
+struct msg_digest;
 
 /* ModeConfig starting functions */
 extern stf_status modecfg_send_request(struct state *st);
@@ -26,3 +30,14 @@ extern stf_status modecfg_inR0(struct msg_digest *md);
 extern stf_status modecfg_inR1(struct msg_digest *md);
 extern stf_status modecfg_inI1(struct msg_digest *md);
 extern stf_status modecfg_inI2(struct msg_digest *md);
+
+/* XAUTH start function */
+extern stf_status xauth_send_request(struct state *st);
+
+/* XAUTH state transition funcgtions */
+extern stf_status xauth_inR1(struct msg_digest *md);
+extern stf_status xauth_inR2(struct msg_digest *md);
+extern stf_status xauth_inI0(struct msg_digest *md);
+extern stf_status xauth_inI1(struct msg_digest *md);
+
+#endif /* _MODECFG_H */
index 6bdc82d..9965851 100644 (file)
 
 /**************** Oakely (main mode) SA database ****************/
 
-/* arrays of attributes for transforms, preshared key */
-
-static struct db_attr otpsk1024des3md5[] = {
-       { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
-       { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 },
-       { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY },
-       { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 },
-       };
-
-static struct db_attr otpsk1536des3md5[] = {
-       { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
-       { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 },
-       { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY },
-       { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1536 },
-       };
-
-static struct db_attr otpsk1024des3sha[] = {
-       { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
-       { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA },
-       { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY },
-       { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 },
-       };
-
-static struct db_attr otpsk1536des3sha[] = {
-       { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
-       { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA },
-       { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY },
-       { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1536 },
-       };
-
-/* arrays of attributes for transforms, RSA signatures */
-
-static struct db_attr otrsasig1024des3md5[] = {
-       { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
-       { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 },
-       { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_SIG },
-       { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 },
-       };
-
-static struct db_attr otrsasig1536des3md5[] = {
-       { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
-       { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 },
-       { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_SIG },
-       { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1536 },
-       };
-
-static struct db_attr otrsasig1024des3sha[] = {
-       { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
-       { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA },
-       { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_SIG },
-       { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 },
-       };
-
-static struct db_attr otrsasig1536des3sha[] = {
-       { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
-       { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA },
-       { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_SIG },
-       { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1536 },
-       };
-
-/* We won't accept this, but by proposing it, we get to test
- * our rejection.  We better not propose it to an IKE daemon
- * that will accept it!
- */
-#ifdef TEST_INDECENT_PROPOSAL
-static struct db_attr otpsk1024des3tiger[] = {
-       { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
-       { OAKLEY_HASH_ALGORITHM, OAKLEY_TIGER },
-       { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY },
-       { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 },
-       };
-#endif /* TEST_INDECENT_PROPOSAL */
-
-/* tables of transforms, in preference order (select based on AUTH) */
-
-static struct db_trans oakley_trans_psk[] = {
-#ifdef TEST_INDECENT_PROPOSAL
-       { KEY_IKE, AD(otpsk1024des3tiger) },
-#endif
-       { KEY_IKE, AD(otpsk1536des3md5) },
-       { KEY_IKE, AD(otpsk1536des3sha) },
-       { KEY_IKE, AD(otpsk1024des3sha) },
-       { KEY_IKE, AD(otpsk1024des3md5) },
-    };
-
-static struct db_trans oakley_trans_rsasig[] = {
-       { KEY_IKE, AD(otrsasig1536des3md5) },
-       { KEY_IKE, AD(otrsasig1536des3sha) },
-       { KEY_IKE, AD(otrsasig1024des3sha) },
-       { KEY_IKE, AD(otrsasig1024des3md5) },
-    };
-
-/* In this table, either PSK or RSA sig is accepted.
- * The order matters, but I don't know what would be best.
- */
-static struct db_trans oakley_trans_pskrsasig[] = {
-#ifdef TEST_INDECENT_PROPOSAL
-       { KEY_IKE, AD(otpsk1024des3tiger) },
-#endif
-       { KEY_IKE, AD(otrsasig1536des3md5) },
-       { KEY_IKE, AD(otpsk1536des3md5) },
-       { KEY_IKE, AD(otrsasig1536des3sha) },
-       { KEY_IKE, AD(otpsk1536des3sha) },
-       { KEY_IKE, AD(otrsasig1024des3sha) },
-       { KEY_IKE, AD(otpsk1024des3sha) },
-       { KEY_IKE, AD(otrsasig1024des3md5) },
-       { KEY_IKE, AD(otpsk1024des3md5) },
-    };
-
 /* array of proposals to be conjoined (can only be one for Oakley) */
 
-static struct db_prop oakley_pc_psk[] =
-    { { PROTO_ISAKMP, AD(oakley_trans_psk) } };
-
-static struct db_prop oakley_pc_rsasig[] =
-    { { PROTO_ISAKMP, AD(oakley_trans_rsasig) } };
-
-static struct db_prop oakley_pc_pskrsasig[] =
-    { { PROTO_ISAKMP, AD(oakley_trans_pskrsasig) } };
+static struct db_prop oakley_pc[] =
+    { { PROTO_ISAKMP, AD_NULL } };
 
 /* array of proposal conjuncts (can only be one) */
 
-static struct db_prop_conj oakley_props_psk[] = { { AD(oakley_pc_psk) } };
+static struct db_prop_conj oakley_props[] = { { AD(oakley_pc) } };
 
-static struct db_prop_conj oakley_props_rsasig[] = { { AD(oakley_pc_rsasig) } };
-
-static struct db_prop_conj oakley_props_pskrsasig[] = { { AD(oakley_pc_pskrsasig) } };
-
-/* the sadb entry, subscripted by POLICY_PSK and POLICY_RSASIG bits */
-struct db_sa oakley_sadb[] = {
-    { AD_NULL },       /* none */
-    { AD(oakley_props_psk) },  /* POLICY_PSK */
-    { AD(oakley_props_rsasig) },       /* POLICY_RSASIG */
-    { AD(oakley_props_pskrsasig) },    /* POLICY_PSK + POLICY_RSASIG */
-    };
+/* the sadb entry */
+struct db_sa oakley_sadb = { AD(oakley_props) };
 
 /**************** IPsec (quick mode) SA database ****************/
 
 /* arrays of attributes for transforms */
 
-static struct db_attr espmd5_attr[] = {
-    { AUTH_ALGORITHM, AUTH_ALGORITHM_HMAC_MD5 },
-    };
-
 static struct db_attr espsha1_attr[] = {
     { AUTH_ALGORITHM, AUTH_ALGORITHM_HMAC_SHA1 },
     };
 
-static struct db_attr ah_HMAC_MD5_attr[] = {
-    { AUTH_ALGORITHM, AUTH_ALGORITHM_HMAC_MD5 },
-    };
-
 static struct db_attr ah_HMAC_SHA1_attr[] = {
     { AUTH_ALGORITHM, AUTH_ALGORITHM_HMAC_SHA1 },
     };
@@ -209,7 +77,6 @@ static struct db_attr ah_HMAC_SHA1_attr[] = {
 /* arrays of transforms, each in in preference order */
 
 static struct db_trans espa_trans[] = {
-    { ESP_3DES, AD(espmd5_attr) },
     { ESP_3DES, AD(espsha1_attr) },
     };
 
@@ -219,13 +86,11 @@ static struct db_trans esp_trans[] = {
 
 #ifdef SUPPORT_ESP_NULL
 static struct db_trans espnull_trans[] = {
-    { ESP_NULL, AD(espmd5_attr) },
     { ESP_NULL, AD(espsha1_attr) },
     };
 #endif /* SUPPORT_ESP_NULL */
 
 static struct db_trans ah_trans[] = {
-    { AH_MD5, AD(ah_HMAC_MD5_attr) },
     { AH_SHA, AD(ah_HMAC_SHA1_attr) },
     };
 
@@ -448,7 +313,7 @@ out_sa(pb_stream *outs
            proposal.isap_spisize = oakley_mode ? 0
                : p->protoid == PROTO_IPCOMP ? IPCOMP_CPI_SIZE
                : IPSEC_DOI_SPI_SIZE;
-           
+
            /* In quick mode ONLY, create proposal for runtime kernel algos.
             *  Replace ESP proposals with runtime created one
             */
@@ -499,7 +364,7 @@ out_sa(pb_stream *outs
                    return_on(ret, FALSE);
                }
            }
-           
+
            proposal.isap_notrans = p->trans_cnt;
            if (!out_struct(&proposal, &isakmp_proposal_desc, &sa_pbs, &proposal_pbs))
                return_on(ret, FALSE);
@@ -877,7 +742,9 @@ restore_pbs(pb_stream *pbs)
  * Parse an ISAKMP Proposal Payload for RSA and PSK authentication policies
  */
 notification_t
-parse_isakmp_policy(pb_stream *proposal_pbs, u_int notrans, lset_t *policy)
+parse_isakmp_policy(pb_stream *proposal_pbs
+                 , u_int notrans
+                 , lset_t *policy)
 {
     int last_transnum = -1;
 
@@ -934,6 +801,18 @@ parse_isakmp_policy(pb_stream *proposal_pbs, u_int notrans, lset_t *policy)
                case OAKLEY_RSA_SIG:
                    *policy |= POLICY_RSASIG;
                    break;
+               case XAUTHInitPreShared:
+                   *policy |= POLICY_XAUTH_SERVER;
+                   /* fall through */
+               case XAUTHRespPreShared:
+                   *policy |= POLICY_XAUTH_PSK;
+                   break;
+               case XAUTHInitRSA:
+                   *policy |= POLICY_XAUTH_SERVER;
+                   /* fall through */
+               case XAUTHRespRSA:
+                   *policy |= POLICY_XAUTH_RSASIG;
+                   break;
                default:
                    break;
                }
@@ -943,23 +822,35 @@ parse_isakmp_policy(pb_stream *proposal_pbs, u_int notrans, lset_t *policy)
            }
        }
     }
+    DBG(DBG_CONTROL|DBG_PARSING,
+       DBG_log("preparse_isakmp_policy: peer requests %s authentication"
+               , prettypolicy(*policy))
+    )
+    return NOTHING_WRONG;
+}
 
-    if ((*policy & POLICY_PSK) && (*policy & POLICY_RSASIG))
-    {
-       DBG(DBG_CONTROL|DBG_PARSING,
-          DBG_log("preparse_isakmp_policy: "
-                  "peer supports both PSK and RSASIG authentication")
-       )
-       *policy = LEMPTY;
-    }
-    else
+/* 
+ * check that we can find a preshared secret
+ */
+static err_t
+find_preshared_key(struct state* st)
+{
+    err_t ugh = NULL;
+    struct connection *c = st->st_connection;
+
+    if (get_preshared_secret(c) == NULL)
     {
-       DBG(DBG_CONTROL|DBG_PARSING,
-          DBG_log("preparse_isakmp_policy: peer requests %s authentication"
-               , (*policy & POLICY_PSK) ? "PSK":"RSASIG")
-       )
+       char my_id[BUF_LEN], his_id[BUF_LEN];
+
+       idtoa(&c->spd.this.id, my_id, sizeof(my_id));
+       if (his_id_was_instantiated(c))
+           strcpy(his_id, "%any");
+       else
+           idtoa(&c->spd.that.id, his_id, sizeof(his_id));
+       ugh = builddiag("Can't authenticate: no preshared key found for `%s' and `%s'"
+                       , my_id, his_id);
     }
-    return NOTHING_WRONG;
+    return ugh;
 }
 
 /* Parse the body of an ISAKMP SA Payload (i.e. Phase 1 / Main Mode).
@@ -976,7 +867,8 @@ parse_isakmp_sa_body(u_int32_t ipsecdoisit
                   , pb_stream *proposal_pbs
                   , struct isakmp_proposal *proposal
                   , pb_stream *r_sa_pbs
-                  , struct state *st)
+                  , struct state *st
+                  , bool initiator)
 {
     struct connection *c = st->st_connection;
     unsigned no_trans_left;
@@ -1082,6 +974,10 @@ parse_isakmp_sa_body(u_int32_t ipsecdoisit
                    /* check that authentication method is acceptable */
                    lset_t iap = st->st_policy & POLICY_ID_AUTH_MASK;
 
+                   /* is the initiator the XAUTH client? */
+                   bool xauth_init = initiator && (st->st_policy & POLICY_XAUTH_SERVER) == LEMPTY
+                                 || !initiator && (st->st_policy & POLICY_XAUTH_SERVER) != LEMPTY;
+
                    switch (val)
                    {
                    case OAKLEY_PRESHARED_KEY:
@@ -1091,23 +987,30 @@ parse_isakmp_sa_body(u_int32_t ipsecdoisit
                        }
                        else
                        {
-                           /* check that we can find a preshared secret */
-                           struct connection *c = st->st_connection;
-
-                           if (get_preshared_secret(c) == NULL)
-                           {
-                               char mid[BUF_LEN]
-                                   , hid[BUF_LEN];
-
-                               idtoa(&c->spd.this.id, mid, sizeof(mid));
-                               if (his_id_was_instantiated(c))
-                                   strcpy(hid, "%any");
-                               else
-                                   idtoa(&c->spd.that.id, hid, sizeof(hid));
-                               ugh = builddiag("Can't authenticate: no preshared key found for `%s' and `%s'"
-                                   , mid, hid);
-                           }
-                           ta.auth = val;
+                           ugh = find_preshared_key(st);
+                           ta.auth = OAKLEY_PRESHARED_KEY;
+                       }
+                       break;
+                   case XAUTHInitPreShared:
+                       if ((iap & POLICY_XAUTH_PSK) == LEMPTY || !xauth_init)
+                       {
+                           ugh = "policy does not allow XAUTHInitPreShared authentication";
+                       }
+                       else
+                       {
+                           ugh = find_preshared_key(st);
+                           ta.auth = XAUTHInitPreShared;
+                       }
+                       break;
+                   case XAUTHRespPreShared:
+                       if ((iap & POLICY_XAUTH_PSK) == LEMPTY || xauth_init)
+                       {
+                           ugh = "policy does not allow XAUTHRespPreShared authentication";
+                       }
+                       else
+                       {
+                           ugh = find_preshared_key(st);
+                           ta.auth = XAUTHRespPreShared;
                        }
                        break;
                    case OAKLEY_RSA_SIG:
@@ -1118,18 +1021,29 @@ parse_isakmp_sa_body(u_int32_t ipsecdoisit
                        }
                        else
                        {
-                           /* We'd like to check that we can find a public
-                            * key for him and a private key for us that is
-                            * suitable, but we don't yet have his
-                            * Id Payload, so it seems futile to try.
-                            * We can assume that if he proposes it, he
-                            * thinks we've got it.  If we proposed it,
-                            * perhaps we know what we're doing.
-                            */
-                           ta.auth = val;
+                           ta.auth = OAKLEY_RSA_SIG;
+                       }
+                       break;
+                   case XAUTHInitRSA:
+                       if ((iap & POLICY_XAUTH_RSASIG) == LEMPTY || !xauth_init)
+                       {
+                           ugh = "policy does not allow XAUTHInitRSA authentication";
+                       }
+                       else
+                       {
+                           ta.auth = XAUTHInitRSA;
+                       }
+                       break;
+                   case XAUTHRespRSA:
+                       if ((iap & POLICY_XAUTH_RSASIG) == LEMPTY || xauth_init)
+                       {
+                           ugh = "policy does not allow XAUTHRespRSA authentication";
+                       }
+                       else
+                       {
+                           ta.auth = XAUTHRespRSA;
                        }
                        break;
-
                    default:
                        ugh = builddiag("Pluto does not support %s authentication"
                            , enum_show(&oakley_auth_names, val));
@@ -1183,10 +1097,23 @@ parse_isakmp_sa_body(u_int32_t ipsecdoisit
                {
                case OAKLEY_LIFE_SECONDS:
                    if (val > OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM)
+                   {
+#ifdef CISCO_QUIRKS
+                       plog("peer requested %lu seconds"
+                                       " which exceeds our limit %d seconds"
+                                       , (long) val
+                                       , OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM);
+                       plog("lifetime reduced to %d seconds "
+                                       "(todo: IPSEC_RESPONDER_LIFETIME notification)"
+                                       , OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM);
+                       val = OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM;
+#else
                        ugh = builddiag("peer requested %lu seconds"
                                        " which exceeds our limit %d seconds"
                                        , (long) val
                                        , OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM);
+#endif
+                   }   
                    ta.life_seconds = val;
                    break;
                case OAKLEY_LIFE_KILOBYTES:
index 5eebf86..0df4888 100644 (file)
@@ -60,10 +60,8 @@ struct db_sa {
      */
 };
 
-/* The oakley sadb is subscripted by a bitset with members
- * from POLICY_PSK and POLICY_RSASIG.
- */
-extern struct db_sa oakley_sadb[1 << 2];
+/* The oakley sadb */
+extern struct db_sa oakley_sadb;
 
 /* The ipsec sadb is subscripted by a bitset with members
  * from POLICY_ENCRYPT, POLICY_AUTHENTICATE, POLICY_COMPRESS
@@ -90,14 +88,15 @@ extern notification_t preparse_isakmp_sa_body(
 extern notification_t parse_isakmp_policy(
     pb_stream *proposal_pbs,   /* body of proposal Payload */
     u_int notrans,             /* number of transforms */
-    lset_t *policy);           /* RSA or PSK policy */
+    lset_t *policy);           /* RSA, PSK or XAUTH policy */
 
 extern notification_t parse_isakmp_sa_body(
     u_int32_t ipsecdoisit,     /* IPsec DOI SIT bitset */
     pb_stream *proposal_pbs,   /* body of proposal Payload */
     struct isakmp_proposal *proposal,
     pb_stream *r_sa_pbs,       /* if non-NULL, where to emit winning SA */
-    struct state *st);         /* current state object */
+    struct state *st,          /* current state object */
+    bool initiator);           /* is caller initiator? */
 
 extern notification_t parse_ipsec_sa_body(
     pb_stream *sa_pbs,         /* body of input SA Payload */