diff -Naur libreswan-3.8-orig/include/ietf_constants.h libreswan-3.8/include/ietf_constants.h
--- libreswan-3.8-orig/include/ietf_constants.h 2014-01-16 02:46:24.000000000 -0500
+++ libreswan-3.8/include/ietf_constants.h 2014-04-10 15:55:22.303340560 -0400
@@ -607,7 +607,7 @@
/* IKEv2 things */
ISAKMP_v2_SA_INIT = 34,
ISAKMP_v2_AUTH = 35,
- ISAKMP_v2_CHILD_SA = 36,
+ ISAKMP_v2_CREATE_CHILD_SA = 36,
ISAKMP_v2_INFORMATIONAL = 37,
ISAKMP_v2_IKE_SESSION_RESUME = 38, /* RFC 5723 */
diff -Naur libreswan-3.8-orig/include/pluto_constants.h libreswan-3.8/include/pluto_constants.h
--- libreswan-3.8-orig/include/pluto_constants.h 2014-01-16 02:46:24.000000000 -0500
+++ libreswan-3.8/include/pluto_constants.h 2014-04-10 15:55:22.303340560 -0400
@@ -424,6 +424,11 @@
#define IS_PARENT_SA_ESTABLISHED(s) ((s) == STATE_PARENT_I2 || (s) == \
STATE_PARENT_R1 || (s) == STATE_IKESA_DEL)
+
+#define IS_V2_INITIATOR(s) ((s) == STATE_PARENT_I1 || \
+ (s) == STATE_PARENT_I2 || \
+ (s) == STATE_PARENT_I3)
+
/*
* Issue here is that our child sa appears as a STATE_PARENT_I3/STATE_PARENT_R2 state which it should not
* So we fall back to checking if it is cloned, and therefor really a child
diff -Naur libreswan-3.8-orig/lib/libswan/constants.c libreswan-3.8/lib/libswan/constants.c
--- libreswan-3.8-orig/lib/libswan/constants.c 2014-01-16 02:46:24.000000000 -0500
+++ libreswan-3.8/lib/libswan/constants.c 2014-04-10 15:55:22.303340560 -0400
@@ -269,7 +269,7 @@
static const char *const exchange_name_ikev2[] = {
"ISAKMP_v2_SA_INIT",
"ISAKMP_v2_AUTH",
- "ISAKMP_v2_CHILD_SA",
+ "ISAKMP_v2_CREATE_CHILD_SA",
"ISAKMP_v2_INFORMATIONAL",
"ISAKMP_v2_IKE_SESSION_RESUME",
};
diff -Naur libreswan-3.8-orig/programs/pluto/ikev2.c libreswan-3.8/programs/pluto/ikev2.c
--- libreswan-3.8-orig/programs/pluto/ikev2.c 2014-01-16 02:46:24.000000000 -0500
+++ libreswan-3.8/programs/pluto/ikev2.c 2014-04-10 15:55:37.668690909 -0400
@@ -150,6 +150,31 @@
* TSi, TSr}
* [Child SA established]
*
+ *
+ * CREATE_CHILD_SA Exchanges:
+ *
+ * New Child SA
+ *
+ * HDR, SK {SA, Ni, [KEi],
+ * TSi, TSr} -->
+ *
+ * <-- HDR, SK {SA, Nr, [KEr],
+ * TSi, TSr}
+ *
+ * Rekey Child SA
+ *
+ * HDR, SK {N(REKEY_SA), SA, Ni, [KEi],
+ * TSi, TSr} -->
+ *
+ * <-- HDR, SK {SA, Nr, [KEr],
+ * TSi, TSr}
+ *
+ * Rekey IKE SA (yes, IKE SA can be rekeyed using CREATE_CHILD_SA)
+ *
+ * HDR, SK {SA, Ni, KEi} -->
+ *
+ * <-- HDR, SK {SA, Nr, KEr}
+ *
*/
/* Short forms for building payload type sets */
@@ -287,6 +312,36 @@
.processor = process_informational_ikev2,
.recv_type = ISAKMP_v2_INFORMATIONAL, },
+ /*
+ * There are three different CREATE_CHILD_SA's invocations,
+ * this is the combined write up (not in RFC). See above for
+ * individual cases from RFC
+ *
+ * HDR, SK {SA, Ni, [KEi], [N(REKEY_SA)], [TSi, TSr]} -->
+ * <-- HDR, SK {N}
+ * <-- HDR, SK {SA, Nr, [KEr], [TSi, TSr]}
+ */
+
+ /* Create Child SA Exchange*/
+ { .state = STATE_PARENT_I3,
+ .next_state = STATE_PARENT_I3,
+ .flags = SMF2_STATENEEDED | SMF2_REPLY,
+ .req_clear_payloads = P(E),
+ .req_enc_payloads = P(SA) | P(Ni),
+ .opt_enc_payloads = P(KE) | P(N) | P(TSi) | P(TSr),
+ .processor = ikev2_in_create_child_sa,
+ .recv_type = ISAKMP_v2_CREATE_CHILD_SA, },
+
+ /* Create Child SA Exchange*/
+ { .state = STATE_PARENT_R2,
+ .next_state = STATE_PARENT_R2,
+ .flags = SMF2_STATENEEDED | SMF2_REPLY,
+ .req_clear_payloads = P(E),
+ .req_enc_payloads = P(SA) | P(Ni),
+ .opt_enc_payloads = P(KE) | P(N) | P(TSi) | P(TSr),
+ .processor = ikev2_in_create_child_sa,
+ .recv_type = ISAKMP_v2_CREATE_CHILD_SA, },
+
/* Informational Exchange*/
{ .state = STATE_PARENT_R2,
.next_state = STATE_PARENT_R2,
@@ -607,7 +662,11 @@
continue;
/* ??? not sure that this is necessary, but it ought to be correct */
- if ( ((svm->flags&SMF2_INITIATOR) != 0) != ((md->hdr.isa_flags & ISAKMP_FLAGS_R) != 0) )
+ /* This check cannot apply for an informational exchange since one
+ * can be initiated by the initial responder.
+ */
+ if (ix != ISAKMP_v2_INFORMATIONAL &&
+ (((svm->flags&SMF2_INITIATOR) != 0) != ((md->hdr.isa_flags & ISAKMP_FLAGS_R) != 0)))
continue;
/* must be the right state */
@@ -832,6 +891,10 @@
case RESPONDER:
pst->st_msgid_lastrecv = md->msgid_received;
+ /* the responder requires msgid_nextuse if it ever needs to
+ * initiate an informational exchange
+ */
+ pst->st_msgid_nextuse = md->msgid_received + 1;
break;
}
}
diff -Naur libreswan-3.8-orig/programs/pluto/ikev2.h libreswan-3.8/programs/pluto/ikev2.h
--- libreswan-3.8-orig/programs/pluto/ikev2.h 2014-01-16 02:46:24.000000000 -0500
+++ libreswan-3.8/programs/pluto/ikev2.h 2014-04-10 15:55:22.304340582 -0400
@@ -35,6 +35,8 @@
extern stf_status ikev2_send_informational(struct state *st);
extern stf_status process_informational_ikev2(struct msg_digest *md);
+extern stf_status ikev2_in_create_child_sa(struct msg_digest *md);
+
extern stf_status ikev2parent_inI1outR1(struct msg_digest *md);
extern stf_status ikev2parent_inR1(struct msg_digest *md);
extern stf_status ikev2parent_inR1outI2(struct msg_digest *md);
diff -Naur libreswan-3.8-orig/programs/pluto/ikev2_parent.c libreswan-3.8/programs/pluto/ikev2_parent.c
--- libreswan-3.8-orig/programs/pluto/ikev2_parent.c 2014-01-16 02:46:24.000000000 -0500
+++ libreswan-3.8/programs/pluto/ikev2_parent.c 2014-04-10 15:55:37.668690909 -0400
@@ -2722,8 +2722,125 @@
delete_state(pst);
}
+static stf_status ikev2_in_create_child_sa_refuse(struct msg_digest *md)
+{
+ struct state *st = md->st;
+ struct state *pst = st;
+ {
+ unsigned char *authstart;
+ unsigned char *encstart;
+ unsigned char *iv;
+ int ivsize;
+ struct ikev2_generic e;
+ pb_stream e_pbs, e_pbs_cipher;
+ pb_stream request;
+
+ zero(&reply_buffer);
+ init_pbs(&request, reply_buffer, sizeof(reply_buffer),
+ "create child SA exchange request response");
+ authstart = request.cur;
+
+ /* HDR out */
+ {
+ struct isakmp_hdr r_hdr;
+ zero(&r_hdr);
+ r_hdr.isa_version = build_ike_version();
+ memcpy(r_hdr.isa_rcookie, pst->st_rcookie,
+ COOKIE_SIZE);
+ memcpy(r_hdr.isa_icookie, pst->st_icookie,
+ COOKIE_SIZE);
+ r_hdr.isa_xchg = ISAKMP_v2_CREATE_CHILD_SA;
+ r_hdr.isa_np = ISAKMP_NEXT_v2E;
+ r_hdr.isa_flags |= ISAKMP_FLAGS_R;
+ r_hdr.isa_msgid = htonl(pst->st_msgid_nextuse);
+
+ /* encryption role based on original state not md state */
+ if (IS_V2_INITIATOR(pst->st_state))
+ md->role = INITIATOR;
+ else
+ md->role = RESPONDER;
+
+ if (!out_struct(&r_hdr, &isakmp_hdr_desc,
+ &request, &md->rbody)) {
+ libreswan_log("error initializing hdr for "
+ "CREATE_CHILD_SA message");
+ return STF_FATAL;
+ }
+ } /* HDR done*/
+
+ /* insert an Encryption payload header */
+ e.isag_np = ISAKMP_NEXT_v2N;
+ e.isag_critical = ISAKMP_PAYLOAD_NONCRITICAL;
+ if (!out_struct(&e, &ikev2_e_desc, &md->rbody, &e_pbs))
+ return STF_FATAL;
+
+ /* IV */
+ iv = e_pbs.cur;
+ ivsize = pst->st_oakley.encrypter->iv_size;
+ if (!out_zero(ivsize, &e_pbs, "iv"))
+ return STF_FATAL;
+
+ get_rnd_bytes(iv, ivsize);
+
+ /* note where cleartext starts */
+ init_pbs(&e_pbs_cipher, e_pbs.cur, e_pbs.roof - e_pbs.cur,
+ "cleartext");
+ e_pbs_cipher.container = &e_pbs;
+ e_pbs_cipher.desc = NULL;
+ e_pbs_cipher.cur = e_pbs.cur;
+ encstart = e_pbs_cipher.cur;
+
+ chunk_t child_spi;
+ memset(&child_spi, 0, sizeof(child_spi));
+
+ ship_v2N(ISAKMP_NEXT_v2NONE,
+ ISAKMP_PAYLOAD_NONCRITICAL,
+ PROTO_ISAKMP,
+ &child_spi,
+ v2N_NO_ADDITIONAL_SAS, NULL,
+ &e_pbs_cipher);
+
+ ikev2_padup_pre_encrypt(md, &e_pbs_cipher);
+ close_output_pbs(&e_pbs_cipher);
+
+ {
+ stf_status ret;
+ unsigned char *authloc = ikev2_authloc(md, &e_pbs);
+
+ if (!authloc)
+ return STF_FATAL;
+
+ close_output_pbs(&e_pbs);
+ close_output_pbs(&md->rbody);
+ close_output_pbs(&request);
+
+ ret = ikev2_encrypt_msg(md, md->role,
+ authstart,
+ iv, encstart, authloc,
+ &e_pbs, &e_pbs_cipher);
+ if (ret != STF_OK)
+ return ret;
+ }
+
+ /* keep it for a retransmit if necessary */
+ freeanychunk(pst->st_tpacket);
+ clonetochunk(pst->st_tpacket, request.start,
+ pbs_offset(&request),
+ "reply packet for CREATE_CHILD_SA exchange");
+ send_ike_msg(pst, __FUNCTION__);
+ }
+
+ return STF_OK;
+}
+
+stf_status ikev2_in_create_child_sa(struct msg_digest *md)
+{
+ return ikev2_in_create_child_sa_refuse(md);
+}
+
stf_status process_informational_ikev2(struct msg_digest *md)
{
+ enum phase1_role prole;
/* verify that there is in fact an encrypted payload */
if (md->chain[ISAKMP_NEXT_v2E] == NULL) {
libreswan_log(
@@ -2734,15 +2851,23 @@
/* decrypt things. */
{
stf_status ret;
+ struct state *ost = md->st;
- if (md->hdr.isa_flags & ISAKMP_FLAGS_I) {
+ /*
+ * Since an informational exchange can be started by the original responder,
+ * things such as encryption, decryption should be done based on the original
+ * role and not the md->role
+ */
+ if (IS_V2_INITIATOR(ost->st_state)) {
+ prole = INITIATOR;
DBG(DBG_CONTROLMORE,
- DBG_log("received informational exchange request from INITIATOR"));
- ret = ikev2_decrypt_msg(md, RESPONDER);
+ DBG_log("received informational exchange request from the original responder"));
+ ret = ikev2_decrypt_msg(md, INITIATOR);
} else {
+ prole = RESPONDER;
DBG(DBG_CONTROLMORE,
- DBG_log("received informational exchange request from RESPONDER"));
- ret = ikev2_decrypt_msg(md, INITIATOR);
+ DBG_log("received informational exchange request from the original initiator"));
+ ret = ikev2_decrypt_msg(md, RESPONDER);
}
if (ret != STF_OK)
@@ -2791,10 +2916,6 @@
r_hdr.isa_np = ISAKMP_NEXT_v2E;
r_hdr.isa_msgid = htonl(md->msgid_received);
- /*set initiator bit if we are initiator*/
- if (md->role == INITIATOR)
- r_hdr.isa_flags |= ISAKMP_FLAGS_I;
-
r_hdr.isa_flags |= ISAKMP_FLAGS_R;
if (!out_struct(&r_hdr, &isakmp_hdr_desc,
@@ -3016,7 +3137,7 @@
close_output_pbs(&md->rbody);
close_output_pbs(&reply_stream);
- ret = ikev2_encrypt_msg(md, md->role,
+ ret = ikev2_encrypt_msg(md, prole,
authstart,
iv, encstart, authloc,
&e_pbs, &e_pbs_cipher);
@@ -3158,7 +3279,7 @@
stf_status ikev2_send_informational(struct state *st)
{
- struct state *pst = NULL;
+ struct state *pst = st;
if (IS_CHILD_SA(st)) {
pst = state_with_serialno(st->st_clonedfrom);
@@ -3169,8 +3290,6 @@
DBG_log("INFORMATIONAL exchange can not be sent"));
return STF_IGNORE;
}
- } else {
- pst = st;
}
{
@@ -3180,7 +3299,6 @@
int ivsize;
struct msg_digest md;
struct ikev2_generic e;
- enum phase1_role role;
pb_stream e_pbs, e_pbs_cipher;
pb_stream rbody;
pb_stream request;
@@ -3204,18 +3322,14 @@
COOKIE_SIZE);
r_hdr.isa_xchg = ISAKMP_v2_INFORMATIONAL;
r_hdr.isa_np = ISAKMP_NEXT_v2E;
+ r_hdr.isa_flags |= ISAKMP_FLAGS_I;
+ r_hdr.isa_msgid = htonl(pst->st_msgid_nextuse);
- if (pst->st_state == STATE_PARENT_I2 ||
- pst->st_state == STATE_PARENT_I3) {
- r_hdr.isa_flags |= ISAKMP_FLAGS_I;
- role = INITIATOR;
- r_hdr.isa_msgid = htonl(pst->st_msgid_nextuse);
- } else {
- role = RESPONDER;
- r_hdr.isa_msgid = htonl(
- pst->st_msgid_lastrecv + 1);
- }
-
+ /* encryption role based on original state not md state */
+ if (IS_V2_INITIATOR(pst->st_state))
+ md.role = INITIATOR;
+ else
+ md.role = RESPONDER;
if (!out_struct(&r_hdr, &isakmp_hdr_desc,
&request, &rbody)) {
libreswan_log(
@@ -3261,7 +3375,7 @@
close_output_pbs(&rbody);
close_output_pbs(&request);
- ret = ikev2_encrypt_msg(&md, role,
+ ret = ikev2_encrypt_msg(&md, md.role,
authstart,
iv, encstart, authloc,
&e_pbs, &e_pbs_cipher);
@@ -3276,7 +3390,6 @@
"reply packet for informational exchange");
pst->st_pend_liveness = TRUE; /* we should only do this when dpd/liveness is active? */
send_ike_msg(pst, __FUNCTION__);
- ikev2_update_counters(&md);
}
return STF_OK;