diff options
author | Andreas Steffen <andreas.steffen@strongswan.org> | 2006-12-19 22:51:48 +0000 |
---|---|---|
committer | Andreas Steffen <andreas.steffen@strongswan.org> | 2006-12-19 22:51:48 +0000 |
commit | 86ae0969b4e2e884764f811bc28118a99e14a694 (patch) | |
tree | 8c3caa062668eed032da9c30014ed413ef60bc2a /src | |
parent | 0c409f097c4db02cdf4c614398aadb154b54d332 (diff) | |
download | strongswan-86ae0969b4e2e884764f811bc28118a99e14a694.tar.bz2 strongswan-86ae0969b4e2e884764f811bc28118a99e14a694.tar.xz |
added XAUTH server and client support
Diffstat (limited to 'src')
-rw-r--r-- | src/pluto/connections.c | 44 | ||||
-rw-r--r-- | src/pluto/constants.c | 22 | ||||
-rw-r--r-- | src/pluto/constants.h | 22 | ||||
-rw-r--r-- | src/pluto/demux.c | 118 | ||||
-rw-r--r-- | src/pluto/ike_alg.c | 31 | ||||
-rw-r--r-- | src/pluto/ipsec_doi.c | 41 | ||||
-rw-r--r-- | src/pluto/keys.c | 109 | ||||
-rw-r--r-- | src/pluto/keys.h | 13 | ||||
-rw-r--r-- | src/pluto/modecfg.c | 308 | ||||
-rw-r--r-- | src/pluto/modecfg.h | 15 | ||||
-rw-r--r-- | src/pluto/spdb.c | 297 | ||||
-rw-r--r-- | src/pluto/spdb.h | 11 |
12 files changed, 765 insertions, 266 deletions
diff --git a/src/pluto/connections.c b/src/pluto/connections.c index 960aaa1f5..eb1570163 100644 --- a/src/pluto/connections.c +++ b/src/pluto/connections.c @@ -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 .*/ diff --git a/src/pluto/constants.c b/src/pluto/constants.c index 53de6f669..3ba5bc09d 100644 --- a/src/pluto/constants.c +++ b/src/pluto/constants.c @@ -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 */ }; diff --git a/src/pluto/constants.h b/src/pluto/constants.h index 9ff74a469..1295b29cf 100644 --- a/src/pluto/constants.h +++ b/src/pluto/constants.h @@ -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 \ diff --git a/src/pluto/demux.c b/src/pluto/demux.c index adccf1b6e..564988cb2 100644 --- a/src/pluto/demux.c +++ b/src/pluto/demux.c @@ -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) diff --git a/src/pluto/ike_alg.c b/src/pluto/ike_alg.c index 43cb24ea2..a62285809 100644 --- a/src/pluto/ike_alg.c +++ b/src/pluto/ike_alg.c @@ -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; diff --git a/src/pluto/ipsec_doi.c b/src/pluto/ipsec_doi.c index a17eae0dc..9d53a1a99 100644 --- a/src/pluto/ipsec_doi.c +++ b/src/pluto/ipsec_doi.c @@ -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" diff --git a/src/pluto/keys.c b/src/pluto/keys.c index 106573f33..37cb6fbe6 100644 --- a/src/pluto/keys.c +++ b/src/pluto/keys.c @@ -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"); } diff --git a/src/pluto/keys.h b/src/pluto/keys.h index acee84432..3e583e495 100644 --- a/src/pluto/keys.h +++ b/src/pluto/keys.h @@ -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 */ diff --git a/src/pluto/modecfg.c b/src/pluto/modecfg.c index 439293ae7..af2ee30c1 100644 --- a/src/pluto/modecfg.c +++ b/src/pluto/modecfg.c @@ -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; +} diff --git a/src/pluto/modecfg.h b/src/pluto/modecfg.h index 969a014a0..fe00067b7 100644 --- a/src/pluto/modecfg.h +++ b/src/pluto/modecfg.h @@ -15,7 +15,11 @@ * 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 */ diff --git a/src/pluto/spdb.c b/src/pluto/spdb.c index 6bdc82dae..996585135 100644 --- a/src/pluto/spdb.c +++ b/src/pluto/spdb.c @@ -50,158 +50,26 @@ /**************** 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: diff --git a/src/pluto/spdb.h b/src/pluto/spdb.h index 5eebf86cf..0df488841 100644 --- a/src/pluto/spdb.h +++ b/src/pluto/spdb.h @@ -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 */ |