Blob Blame History Raw
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;