Revised notify_delete-2.00.diff, was Re: delete-sa-01 test

Sam Sgro <[email protected]>
Newsgroups gmane.network.freeswan.devel
Message-ID <[email protected]>
-----BEGIN PGP SIGNED MESSAGE-----


On Sun, 9 Feb 2003, Ken Bantoft wrote:

> My fault :(  I should have inspected the code more when I was porting
> up the patch.  Thanks for the updated patch, I've updated freeswan.ca with
> it as well.

Well, if it makes you feel any better, I uploaded the wrong diff. :) Here is
the corrected version.

- -- 
Sam Sgro
[email protected]

-----BEGIN PGP SIGNATURE-----
Version: 2.6.3ia
Charset: noconv
Comment: For the matching public key, finger the Reply-To: address.

iQCVAwUBPkdpukOSC4btEQUtAQHebAP+K0DEP6eoTG1HRjYJKaeJpdk+mzRO5DVt
H3kHrAZZSb3jvqt/Ux14FpDXxWf9El7MYwo3D0+pU9+dAbM9YvZCIHDHGxLi379F
kmKCXGwxm5qGwYoYl9Ve0BJx++WZk8KrZ+TjVwMT8WwwSMHhtbG1y+CBZ7fcg0NB
oB0EDBKn9bE=
=5O0P
-----END PGP SIGNATURE-----
notify_delete-2.00.diff (text/plain, 34.1 KB)
Index: programs/pluto/connections.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/connections.c,v
retrieving revision 1.152
diff -u -r1.152 connections.c
--- programs/pluto/connections.c	30 Jan 2003 21:54:25 -0000	1.152
+++ programs/pluto/connections.c	1 Feb 2003 01:29:27 -0000
@@ -713,6 +713,7 @@
 	}
 
 	/* set internal fields */
+	c->initiated = FALSE;
 	c->instance_serial = 0;
 	c->ac_next = connections;
 	connections = c;
@@ -1201,6 +1202,7 @@
 	     * This is a fudge, but not yet important.
 	     * If we are to proceed asynchronously, whackfd will be NULL_FD.
 	     */
+            c->initiated = TRUE;
 	    c->policy |= POLICY_UP;
 	    ipsecdoi_initiate(whackfd, c, c->policy, 1, SOS_NOBODY);
 	    whackfd = NULL_FD;	/* protect from close */
@@ -1531,6 +1533,7 @@
 	{
 	    set_cur_connection(c);
 	    log("terminating SAs using this connection");
+            c->initiated = FALSE;
 	    c->policy &= ~POLICY_UP;
 	    flush_pending_by_connection(c);
 	    delete_states_by_connection(c);
Index: programs/pluto/connections.h
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/connections.h,v
retrieving revision 1.67
diff -u -r1.67 connections.h
--- programs/pluto/connections.h	26 Jan 2003 18:41:41 -0000	1.67
+++ programs/pluto/connections.h	1 Feb 2003 01:29:27 -0000
@@ -118,6 +118,7 @@
     enum connection_kind kind;
     const struct iface *interface;	/* filled in iff oriented */
     enum routing_t routing;	/* level of routing in place */
+    bool initiated;
 
     so_serial_t	/* state object serial number */
 	newest_isakmp_sa,
Index: programs/pluto/demux.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/demux.c,v
retrieving revision 1.136
diff -u -r1.136 demux.c
--- programs/pluto/demux.c	9 Nov 2002 22:48:10 -0000	1.136
+++ programs/pluto/demux.c	1 Feb 2003 01:29:29 -0000
@@ -783,7 +783,7 @@
 static stf_status
 informational(struct msg_digest *md UNUSED)
 {
-    loglog(RC_LOG_SERIOUS, "received and ignored informational message");
+    /* loglog(RC_LOG_SERIOUS, "received and ignored informational message"); */
     return STF_IGNORE;
 }
 
@@ -1035,12 +1035,27 @@
     struct state *st = NULL;
     enum state_kind from_state = STATE_UNDEFINED;	/* state we started in */
 
+#define SEND_NOTIFICATION(t) { \
+    if (st) send_notification_from_state(st, from_state, t); \
+    else send_notification_from_md(md, t); }
+
     if (!in_struct(&md->hdr, &isakmp_hdr_desc, &md->packet_pbs, &md->message_pbs))
     {
-	/* XXX specific failures (special notification?):
+	/* Identify specific failures:
 	 * - bad ISAKMP major/minor version numbers
-	 * - size of packet vs size of message
 	 */
+	if (md->packet_pbs.roof - md->packet_pbs.cur >= (ptrdiff_t)isakmp_hdr_desc.size) {
+	    struct isakmp_hdr *hdr = (struct isakmp_hdr *)md->packet_pbs.cur;
+	    if ((hdr->isa_version >> ISA_MAJ_SHIFT) != ISAKMP_MAJOR_VERSION) {
+		SEND_NOTIFICATION(INVALID_MAJOR_VERSION);
+		return;
+	    }
+	    else if ((hdr->isa_version & ISA_MIN_MASK) != ISAKMP_MINOR_VERSION) {
+		SEND_NOTIFICATION(INVALID_MINOR_VERSION);
+		return;
+	    }
+	}
+	SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 	return;
     }
 
@@ -1048,6 +1063,7 @@
     {
 	log("size (%u) differs from size specified in ISAKMP HDR (%u)"
 	    , (unsigned) pbs_room(&md->packet_pbs), md->hdr.isa_length);
+	SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 	return;
     }
 
@@ -1063,14 +1079,14 @@
 	{
 	    log("Message ID was 0x%08lx but should be zero in Main Mode",
 		(unsigned long) md->hdr.isa_msgid);
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_MESSAGE_ID);
 	    return;
 	}
 
 	if (is_zero_cookie(md->hdr.isa_icookie))
 	{
 	    log("Initiator Cookie must not be zero in Main Mode message");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_COOKIE);
 	    return;
 	}
 
@@ -1083,6 +1099,7 @@
 	    {
 		log("initial Main Mode message is invalid:"
 		    " its Encrypted Flag is on");
+		SEND_NOTIFICATION(INVALID_FLAGS);
 		return;
 	    }
 
@@ -1185,7 +1202,7 @@
 	{
 	    log("Quick Mode message is invalid because"
 		" it has an Initiator Cookie of 0");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_COOKIE);
 	    return;
 	}
 
@@ -1193,7 +1210,7 @@
 	{
 	    log("Quick Mode message is invalid because"
 		" it has a Responder Cookie of 0");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_COOKIE);
 	    return;
 	}
 
@@ -1201,7 +1218,7 @@
 	{
 	    log("Quick Mode message is invalid because"
 		" it has a Message ID of 0");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_MESSAGE_ID);
 	    return;
 	}
 
@@ -1231,7 +1248,7 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "Quick Mode message is unacceptable because"
 		    " it is for an incomplete ISAKMP SA");
-		/* XXX Could send notification back */
+		SEND_NOTIFICATION(PAYLOAD_MALFORMED /* XXX ? */);
 		return;
 	    }
 
@@ -1242,7 +1259,7 @@
 		    " it uses a previously used Message ID 0x%08lx"
 		    " (perhaps this is a duplicated packet)"
 		    , (unsigned long) md->hdr.isa_msgid);
-		/* XXX Could send notification INVALID_MESSAGE_ID back */
+		SEND_NOTIFICATION(INVALID_MESSAGE_ID);
 		return;
 	    }
 
@@ -1268,6 +1285,7 @@
     default:
 	log("unsupported exchange type %s in message"
 	    , enum_show(&exchange_names, md->hdr.isa_xchg));
+	SEND_NOTIFICATION(UNSUPPORTED_EXCHANGE_TYPE);
 	return;
     }
 
@@ -1364,14 +1382,14 @@
 	if (st == NULL)
 	{
 	    log("discarding encrypted message for an unknown ISAKMP SA");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(PAYLOAD_MALFORMED /* XXX ? */);
 	    return;
 	}
 	if (st->st_skeyid_e.ptr == (u_char *) NULL)
 	{
 	    loglog(RC_LOG_SERIOUS, "discarding encrypted message"
 		" because we haven't yet negotiated keying materiel");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_FLAGS);
 	    return;
 	}
 
@@ -1403,7 +1421,7 @@
 	    if (pbs_left(&md->message_pbs) % e->blocksize != 0)
 	    {
 		loglog(RC_LOG_SERIOUS, "malformed message: not a multiple of encryption blocksize");
-		/* XXX Could send notification back */
+		SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		return;
 	    }
 
@@ -1438,7 +1456,7 @@
 	if (smc->flags & SMF_INPUT_ENCRYPTED)
 	{
 	    loglog(RC_LOG_SERIOUS, "packet rejected: should have been encrypted");
-	    /* XXX Could send notification back */
+	    SEND_NOTIFICATION(INVALID_FLAGS);
 	    return;
 	}
     }
@@ -1465,6 +1483,7 @@
 	    if (pd == &md->digest[PAYLIMIT])
 	    {
 		loglog(RC_LOG_SERIOUS, "more than %d payloads in message; ignored", PAYLIMIT);
+		SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		return;
 	    }
 
@@ -1481,6 +1500,7 @@
 		    loglog(RC_LOG_SERIOUS, "%smessage ignored because it contains an unknown or"
 			" unexpected payload type (%s) at the outermost level"
 			, excuse, enum_show(&payload_names, np));
+		    SEND_NOTIFICATION(INVALID_PAYLOAD_TYPE);
 		    return;
 		}
 	    }
@@ -1494,6 +1514,7 @@
 		    loglog(RC_LOG_SERIOUS, "%smessage ignored because it contains a"
 			" payload type (%s) unexpected in this message"
 			, excuse, enum_show(&payload_names, np));
+		    SEND_NOTIFICATION(INVALID_PAYLOAD_TYPE);
 		    return;
 		}
 		needed &= ~s;
@@ -1502,6 +1523,7 @@
 	    if (!in_struct(&pd->payload, sd, &md->message_pbs, &pd->pbs))
 	    {
 		loglog(RC_LOG_SERIOUS, "%smalformed payload in packet", excuse);
+		SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		return;
 	    }
 
@@ -1541,6 +1563,7 @@
 	    loglog(RC_LOG_SERIOUS, "message for %s is missing payloads %s"
 		, enum_show(&state_names, from_state)
 		, bitnamesof(payload_name, needed));
+	    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 	    return;
 	}
     }
@@ -1556,6 +1579,7 @@
 	&& md->hdr.isa_np != ISAKMP_NEXT_SA)
 	{
 	    loglog(RC_LOG_SERIOUS, "malformed Phase 1 message: does not start with an SA payload");
+	    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 	    return;
 	}
     }
@@ -1579,6 +1603,7 @@
 	if (md->hdr.isa_np != ISAKMP_NEXT_HASH)
 	{
 	    loglog(RC_LOG_SERIOUS, "malformed Quick Mode message: does not start with a HASH payload");
+	    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 	    return;
 	}
 
@@ -1592,6 +1617,7 @@
 		if (p != &md->digest[i])
 		{
 		    loglog(RC_LOG_SERIOUS, "malformed Quick Mode message: SA payload is in wrong position");
+		    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		    return;
 		}
 	    }
@@ -1612,12 +1638,14 @@
 		    loglog(RC_LOG_SERIOUS, "malformed Quick Mode message:"
 			" if any ID payload is present,"
 			" there must be exactly two");
+		    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		    return;
 		}
 		if (id+1 != id->next)
 		{
 		    loglog(RC_LOG_SERIOUS, "malformed Quick Mode message:"
 			" the ID payloads are not adjacent");
+		    SEND_NOTIFICATION(PAYLOAD_MALFORMED);
 		    return;
 		}
 	    }
@@ -1642,7 +1670,7 @@
 
 	for (p = md->chain[ISAKMP_NEXT_D]; p != NULL; p = p->next)
 	{
-	    loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload");
+	    accept_delete(st, md, p);
 	    DBG_cond_dump(DBG_PARSING, "del:", p->pbs.cur, pbs_left(&p->pbs));
 	}
 
@@ -1934,13 +1962,14 @@
 	    result = STF_FAIL;
 	    /* FALL THROUGH ... */
 	case STF_FAIL:
-	    /* XXX Could send notification back
-	     * As it is, we act as if this message never happened:
+	    /* As it is, we act as if this message never happened:
 	     * whatever retrying was in place, remains in place.
 	     */
 	    whack_log(RC_NOTIFICATION + md->note
 		, "%s: %s", enum_name(&state_names, st->st_state)
 		, enum_name(&ipsec_notification_names, md->note));
+
+	    SEND_NOTIFICATION(md->note);
 
 	    DBG(DBG_CONTROL,
 		DBG_log("state transition function for %s failed: %s"
Index: programs/pluto/ipsec_doi.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/ipsec_doi.c,v
retrieving revision 1.179
diff -u -r1.179 ipsec_doi.c
--- programs/pluto/ipsec_doi.c	17 Dec 2002 14:26:54 -0000	1.179
+++ programs/pluto/ipsec_doi.c	1 Feb 2003 01:29:31 -0000
@@ -228,7 +228,6 @@
     {
 	loglog(RC_LOG_SERIOUS, "KE has %u byte DH public value; %u required"
 	    , (unsigned) pbs_left(pbs), (unsigned) gr->bytes);
-	/* XXX Could send notification back */
 #ifdef DODGE_DH_MISSING_ZERO_BUG
 	if (pbs_left(pbs) > gr->bytes)
 #endif
@@ -290,76 +289,198 @@
  * Send a notification to the peer. We could make a decision on
  * whether to send the notification, based on the type and the
  * destination, if we care to.
- * XXX It doesn't handle DELETE notifications (which are also
- * XXX informational exchanges).
- * XXX Not modified to support ip_address and related (IPv4+IPv6) functions.
- */
-#if 0 /* not currently used */
-//static void
-//send_notification(int sock,
-//    u_int16_t type,
-//    u_char *spi,
-//    u_char spilen,
-//    u_char protoid,
-//    u_char *icookie,
-//    u_char *rcookie,
-//    msgid_t /*network order*/ msgid,
-//    struct sockaddr sa)
-//{
-//    u_char buffer[sizeof(struct isakmp_hdr) +
-//		 sizeof(struct isakmp_notification)];
-//    struct isakmp_hdr *isa = (struct isakmp_hdr *) buffer;
-//    struct isakmp_notification *isan = (struct isakmp_notification *)
-//				       (buffer + sizeof(struct isakmp_hdr));
-//
-//    memset(buffer, '\0', sizeof(struct isakmp_hdr) +
-//	  sizeof(struct isakmp_notification));
-//
-//    if (icookie != (u_char *) NULL)
-//	memcpy(isa->isa_icookie, icookie, COOKIE_SIZE);
-//
-//    if (rcookie != (u_char *) NULL)
-//	memcpy(isa->isa_rcookie, rcookie, COOKIE_SIZE);
-//
-//    /* Standard header */
-//    isa->isa_np = ISAKMP_NEXT_N;
-//    isa->isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;
-//    isa->isa_xchg = ISAKMP_XCHG_INFO;
-//    isa->isa_msgid = msgid;
-//    isa->isa_length = htonl(sizeof(struct isakmp_hdr) +
-//			    sizeof(struct isakmp_notification) +
-//			    spilen);
-//
-//    /* Notification header */
-//    isan->isan_type = htons(type);
-//    isan->isan_doi = htonl(ISAKMP_DOI_IPSEC);
-//    isan->isan_length = htons(sizeof(struct isakmp_notification) + spilen);
-//    isan->isan_spisize = spilen;
-//    memcpy((u_char *)isan + sizeof(struct isakmp_notification), spi, spilen);
-//    isan->isan_protoid = protoid;
-//
-//    DBG(DBG_CONTROL, DBG_log("sending INFO type %s to %s",
-//	enum_show(&notification_names, type),
-//	show_sa(&sa)));
-//
-//    if (sendto(sock, buffer, ntohl(isa->isa_length), 0, &sa,
-//	       sizeof(sa)) != ntohl(isa->isa_length))
-//	log_errno((e, "sendto() failed in send_notification() to %s",
-//	    show_sa(&sa)));
-//    else
-//    {
-//	DBG(DBG_CONTROL, DBG_log("transmitted %d bytes", ntohl(isa->isa_length)));
-//    }
-//}
-#endif /* not currently used */
+ * It doesn't handle DELETE notifications (see send_ipsec_delete)
+ */
+static void
+send_notification(struct state *sndst, u_int16_t type, struct state *encst,
+    msgid_t msgid, u_char *icookie, u_char *rcookie,
+    u_char *spi, size_t spisize, u_char protoid)
+{
+    u_char buffer[1024];
+    pb_stream pbs, r_hdr_pbs;
+    u_char *r_hashval, *r_hash_start;
+
+    passert((sndst) && (sndst->st_connection));
+
+    log("sending %snotification %s to %s:%u",
+	encst ? "encrypted " : "",
+	enum_name(&ipsec_notification_names, type),
+	ip_str(&sndst->st_connection->that.host_addr),
+	(unsigned)sndst->st_connection->that.host_port);
+	
+    memset(buffer, 0, sizeof(buffer));
+    init_pbs(&pbs, buffer, sizeof(buffer), "notification msg");
+
+    /* HDR* */
+    {
+	struct isakmp_hdr hdr;
+
+	hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;
+	hdr.isa_np = encst ? ISAKMP_NEXT_HASH : ISAKMP_NEXT_N;
+	hdr.isa_xchg = ISAKMP_XCHG_INFO;
+	hdr.isa_msgid = msgid;
+	hdr.isa_flags = encst ? ISAKMP_FLAG_ENCRYPTION : 0;
+	if (icookie)
+	    memcpy(hdr.isa_icookie, icookie, COOKIE_SIZE);
+	if (rcookie)
+	    memcpy(hdr.isa_rcookie, rcookie, COOKIE_SIZE);
+	if (!out_struct(&hdr, &isakmp_hdr_desc, &pbs, &r_hdr_pbs))
+	    impossible();
+    }
+
+    /* HASH -- value to be filled later */
+    if (encst)
+    {
+	pb_stream hash_pbs;
+	if (!out_generic(ISAKMP_NEXT_N, &isakmp_hash_desc, &r_hdr_pbs,
+	    &hash_pbs))
+	    impossible();
+	r_hashval = hash_pbs.cur;  /* remember where to plant value */
+	if (!out_zero(
+#ifdef _IKE_ALG_H
+	    encst->st_oakley.hasher->hash_digest_size,
+#else
+	    encst->st_oakley.hasher->hash_digest_len,
+#endif
+	    &hash_pbs, "HASH(1)"))
+	    impossible();
+	close_output_pbs(&hash_pbs);
+	r_hash_start = r_hdr_pbs.cur; /* hash from after HASH(1) */
+    }
+
+    /* Notification Payload */
+    {
+	pb_stream not_pbs;
+	struct isakmp_notification isan;
+
+	isan.isan_doi = ISAKMP_DOI_IPSEC;
+	isan.isan_np = ISAKMP_NEXT_NONE;
+	isan.isan_type = type;
+	isan.isan_spisize = spisize;
+	isan.isan_protoid = protoid;
+
+	if (!out_struct(&isan, &isakmp_notification_desc, &r_hdr_pbs, &not_pbs)
+	    || !out_raw(spi, spisize, &not_pbs, "spi"))
+	    impossible();
+	close_output_pbs(&not_pbs);
+    }
+
+    /* calculate hash value and patch into Hash Payload */
+    if (encst)
+    {
+	struct hmac_ctx ctx;
+	hmac_init_chunk(&ctx, encst->st_oakley.hasher, encst->st_skeyid_a);
+	hmac_update(&ctx, (u_char *) &msgid, sizeof(msgid_t));
+	hmac_update(&ctx, r_hash_start, r_hdr_pbs.cur-r_hash_start);
+	hmac_final(r_hashval, &ctx);
+
+#ifdef _IKE_ALG_H
+	DBG(DBG_CRYPT,
+	    DBG_log("HASH(1) computed:");
+	    DBG_dump("", r_hashval, ctx.hmac_digest_size);
+	)
+    }
+#else
+	DBG(DBG_CRYPT,
+	    DBG_log("HASH(1) computed:");
+	    DBG_dump("", r_hashval, ctx.hmac_digest_len);
+	)
+    }
+#endif
+
+    /* Encrypt message (preserve st_iv) */
+    if (encst)
+    {
+	u_char old_iv[MAX_DIGEST_LEN];
+	if (encst->st_iv_len > MAX_DIGEST_LEN)
+	    impossible();
+	memcpy(old_iv, encst->st_iv, encst->st_iv_len);
+	init_phase2_iv(encst, &msgid);
+	if (!encrypt_message(&r_hdr_pbs, encst))
+	    impossible();
+	memcpy(encst->st_iv, old_iv, encst->st_iv_len);
+    }
+    else
+    {
+	close_output_pbs(&r_hdr_pbs);
+    }
+
+    /* Send packet (preserve st_tpacket) */
+    {
+	chunk_t saved_tpacket = sndst->st_tpacket;
+
+	setchunk(sndst->st_tpacket, pbs.start, pbs_offset(&pbs));
+	send_packet(sndst, "notification packet");
+	sndst->st_tpacket = saved_tpacket;
+    }
+}
+
+void
+send_notification_from_state(struct state *st, enum state_kind state,
+    u_int16_t type)
+{
+    struct state *p1st;
+
+    passert(st);
+
+    if (state == STATE_UNDEFINED)
+	state = st->st_state;
+
+    if (IS_QUICK(state)) {
+	p1st = find_phase1_state(st->st_connection, TRUE);
+	if ((p1st == NULL) || (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state))) {
+	    loglog(RC_LOG_SERIOUS,
+		"no Phase1 state for Quick mode notification");
+	    return;
+	}
+	send_notification(st, type, p1st, generate_msgid(p1st),
+	    st->st_icookie, st->st_rcookie, NULL, 0, PROTO_ISAKMP);
+    }
+    else if (IS_ISAKMP_SA_ESTABLISHED(state)) {
+	send_notification(st, type, st, generate_msgid(st),
+	    st->st_icookie, st->st_rcookie, NULL, 0, PROTO_ISAKMP);
+    }
+    else {
+	/* no ISAKMP SA established - don't encrypt notification */
+	send_notification(st, type, NULL, 0,
+	    st->st_icookie, st->st_rcookie, NULL, 0, PROTO_ISAKMP);
+    }
+}
+
+void
+send_notification_from_md(struct msg_digest *md, u_int16_t type)
+{
+    /**
+     * Create a dummy state to be able to use send_packet in
+     * send_notification
+     *
+     * we need to set:
+     *   st_connection->that.host_addr
+     *   st_connection->that.host_port
+     *   st_connection->interface
+     */
+    struct state st;
+    struct connection cnx;
+
+    passert(md);
+
+    memset(&st, 0, sizeof(st));
+    memset(&cnx, 0, sizeof(cnx));
+    st.st_connection = &cnx;
+    cnx.that.host_addr = md->sender;
+    cnx.that.host_port = md->sender_port;
+    cnx.interface = md->iface;
+
+    send_notification(&st, type, NULL, 0,
+	md->hdr.isa_icookie, md->hdr.isa_rcookie, NULL, 0, PROTO_ISAKMP);
+}
 
-/* Send a Delete Notification to announce deletion of inbound IPSEC SAs.
+/* Send a Delete Notification to announce deletion of inbound IPSEC/ISAKMP SAs.
  * Ignores states that don't have any.
- * Delete Notifications cannot announce deletion of outbound IPSEC SAs.
- * We don't bother announcing deletion of ISAKMP SAs at this point.
+ * Delete Notifications cannot announce deletion of outbound IPSEC/ISAKMP SAs.
  */
 void
-send_ipsec_delete(struct state *p2st)
+send_delete(struct state *st)
 {
     pb_stream reply_pbs;
     pb_stream r_hdr_pbs;
@@ -371,49 +492,42 @@
     u_char
 	*r_hashval,	/* where in reply to jam hash value */
 	*r_hash_start;	/* start of what is to be hashed */
+    bool isakmp_sa = FALSE;
 
-    if (!IS_IPSEC_SA_ESTABLISHED(p2st->st_state))
-	return;	/* nothing to do */
-
-    /* find an ISAKMP SA that we can use -- must be established */
-    p1st = find_phase1_state(p2st->st_connection, ISAKMP_SA_ESTABLISHED_STATES);
-    if (p1st == NULL)
-    {
-	DBG(DBG_CONTROL, DBG_log("no Phase 1 state for Delete"));
-	return;
+    if (IS_IPSEC_SA_ESTABLISHED(st->st_state)) {
+       p1st = find_phase1_state(st->st_connection, ISAKMP_SA_ESTABLISHED_STATES);
+       if (p1st == NULL)
+       {
+           DBG(DBG_CONTROL, DBG_log("no Phase 1 state for Delete"));
+           return;
+       }
+
+       if (st->st_ah.present)
+       {
+           ns->spi = st->st_ah.our_spi;
+           ns->dst = st->st_connection->this.host_addr;
+           ns->proto = PROTO_IPSEC_AH;
+           ns++;
+       }
+       if (st->st_esp.present)
+       {
+           ns->spi = st->st_esp.our_spi;
+           ns->dst = st->st_connection->this.host_addr;
+           ns->proto = PROTO_IPSEC_ESP;
+           ns++;
+       }
+        
+       passert(ns != said);    /* there must be some SAs to delete */
+    }
+    else if (IS_ISAKMP_SA_ESTABLISHED(st->st_state)) {
+       p1st = st;
+       isakmp_sa = TRUE;
     }
-
-    msgid = generate_msgid(p1st);
-
-    if (p2st->st_ah.present)
-    {
-	ns->spi = p2st->st_ah.attrs.spi;
-	ns->dst = p2st->st_connection->this.host_addr;
-	ns->proto = PROTO_IPSEC_AH;
-	ns++;
-    }
-    if (p2st->st_esp.present)
-    {
-	ns->spi = p2st->st_esp.attrs.spi;
-	ns->dst = p2st->st_connection->this.host_addr;
-	ns->proto = PROTO_IPSEC_ESP;
-	ns++;
-    }
-    /* I doubt that it makes sense to delete an IPCOMP with a well-known CPI.
-     * Maybe it never makes sense to delete a CPI.
-     */
-#if 0
-    if (p2st->st_ipcomp.present)
-    {
-	ns->spi = p2st->st_ipcomp.attrs.spi;
-	ns->dst = p2st->st_connection->this.host_addr;
-	ns->proto = PROTO_IPCOMP;
-	ns++;
+    else {
+       return; /* nothing to do */
     }
-#endif
-    /* IPIP isn't a real SA, so we don't mention it */
 
-    passert(ns != said);	/* there must be some SAs to delete */
+    msgid = generate_msgid(p1st);
 
     zero(buffer);
     init_pbs(&reply_pbs, buffer, sizeof(buffer), "delete msg");
@@ -447,7 +561,27 @@
     }
 
     /* Delete Payloads */
-    while (ns != said) {
+    if (isakmp_sa) {
+       pb_stream del_pbs;
+       struct isakmp_delete isad;
+       u_char isakmp_spi[2*COOKIE_SIZE];
+
+       isad.isad_doi = ISAKMP_DOI_IPSEC;
+       isad.isad_np = ISAKMP_NEXT_NONE;
+       isad.isad_spisize = (2 * COOKIE_SIZE);
+       isad.isad_protoid = PROTO_ISAKMP;
+       isad.isad_nospi = 1;
+
+       memcpy(isakmp_spi, st->st_icookie, COOKIE_SIZE);
+       memcpy(isakmp_spi+COOKIE_SIZE, st->st_rcookie, COOKIE_SIZE);
+
+       if (!out_struct(&isad, &isakmp_delete_desc, &r_hdr_pbs, &del_pbs)
+       || !out_raw(&isakmp_spi, (2*COOKIE_SIZE), &del_pbs, "delete payload"))
+           impossible();
+       close_output_pbs(&del_pbs);
+    }
+    else while (ns != said) {
+
 	pb_stream del_pbs;
 	struct isakmp_delete isad;
 
@@ -504,6 +638,132 @@
     }
 }
 
+void
+accept_delete(struct state *st, struct msg_digest *md, struct payload_digest *p)
+{
+    struct isakmp_delete *d = &(p->payload.delete);
+    size_t sizespi = 0;
+    u_char *spi;
+    struct state *dst = NULL;
+    int i;
+
+    if ((!st) && (!(md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION))) {
+	loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: not encrypted");
+	return;
+    }
+
+    if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state)) {
+	/* can't happen (if msg is encrypt), but just to be sure */
+	loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: "
+	"ISAKMP SA not established");
+	return;
+    }
+
+    if (d->isad_nospi == 0) {
+	loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: no SPI");
+	return;
+    }
+
+    if (pbs_left(&p->pbs) != ((unsigned)d->isad_spisize * d->isad_nospi)) {
+	loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: invalid size");
+	return;
+    }
+
+    switch (d->isad_protoid) {
+	case PROTO_ISAKMP:
+	    sizespi = (2*COOKIE_SIZE);
+	    break;
+	case PROTO_IPSEC_AH:
+	case PROTO_IPSEC_ESP:
+	    sizespi = sizeof(ipsec_spi_t);
+	    break;
+	default:
+	    loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: "
+		"unknown Protocol ID (%s)",
+		enum_show(&protocol_names, d->isad_protoid));
+	    return;
+	    break;
+    }
+
+    if (d->isad_spisize != sizespi) {
+	loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: "
+	    "bad size (%d) for Protocol (%s)",
+	    d->isad_spisize,
+	    enum_show(&protocol_names, d->isad_protoid));
+	return;
+    }
+
+    for (i=0; i<d->isad_nospi; i++) {
+	spi = p->pbs.cur + (i * sizespi);
+	if (d->isad_protoid == PROTO_ISAKMP) {
+	    /**
+	     * ISAKMP
+	     */
+	    dst = find_phase1_state_to_delete(st, spi /*iCookie*/,
+		spi+COOKIE_SIZE /*rCookie*/);
+	    if (dst) {
+		loglog(RC_LOG_SERIOUS, "received Delete SA payload: "
+		    "deleting ISAKMP State #%lu", dst->st_serialno);
+		delete_state(dst);
+	    }
+	    else {
+		loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: "
+		    "ISAKMP SA not found (maybe expired)");
+	    }
+	}
+	else {
+	    /**
+	     * IPSEC (ESP/AH)
+	     */
+	    bool bogus;
+	    ipsec_spi_t ipsec_spi = *((ipsec_spi_t *)spi);
+
+	    dst = find_phase2_state_to_delete(st, d->isad_protoid, ipsec_spi, &bogus);
+	    if (dst) {
+		struct connection *rc = dst->st_connection;
+		if ((rc) && (rc->newest_ipsec_sa == dst->st_serialno) &&
+		    (rc->initiated)) {
+		    /*
+		     * Last IPSec SA for a permanent connection that we
+		     * have initiated. Replace it in a few seconds.
+		     *
+		     * Usefull if the other peer is rebooting
+		     */
+#define DELETE_SA_DELAY  EVENT_RETRANSMIT_DELAY_0
+		    if ((dst->st_event) &&
+			(dst->st_event->ev_type == EVENT_SA_REPLACE) &&
+			(dst->st_event->ev_time <= DELETE_SA_DELAY + now())) {
+			/*
+			 * Patch from Angus Lees to ignore retransmited Delete SA.
+			 */
+			loglog(RC_LOG_SERIOUS, "received Delete SA payload: "
+			    "already replacing IPSEC State #%lu in %d seconds",
+			    dst->st_serialno, (int)(dst->st_event->ev_time - now()));
+		    }
+		    else {
+			loglog(RC_LOG_SERIOUS, "received Delete SA payload: "
+			    "replace IPSEC State #%lu in %d seconds",
+			    dst->st_serialno, DELETE_SA_DELAY);
+			dst->st_margin = DELETE_SA_DELAY;
+			delete_event(dst);
+			event_schedule(EVENT_SA_REPLACE, DELETE_SA_DELAY, dst);
+		    }
+		}
+		else {
+		    loglog(RC_LOG_SERIOUS, "received Delete SA payload: "
+			"deleting IPSEC State #%lu", dst->st_serialno);
+		    delete_state(dst);
+		}
+	    }
+	    else {
+		loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: "
+		    "IPSEC SA not found (%s)",
+		    bogus ? "our spi - bogus implementation" : "maybe expired");
+	    }
+	}
+    }
+}
+
 /* The whole message must be a multiple of 4 octets.
  * I'm not sure where this is spelled out, but look at
  * rfc2408 3.6 Transform Payload.
@@ -1355,7 +1615,6 @@
 	if (s.best_ugh[0] == '9')
 	{
 	    loglog(RC_LOG_SERIOUS, "%s", s.best_ugh + 1);
-	    /* XXX Could send notification back */
 	    return STF_FAIL + INVALID_HASH_INFORMATION;
 	}
 	else
@@ -1402,7 +1661,6 @@
 	{ \
 	    DBG_cond_dump(DBG_CRYPT, "received " hash_name ":", hash_pbs->cur, pbs_left(hash_pbs)); \
 	    loglog(RC_LOG_SERIOUS, "received " hash_name " does not match computed value in " msg_name); \
-	    /* XXX Could send notification back */ \
 	    return STF_FAIL + INVALID_HASH_INFORMATION; \
 	} \
     }
@@ -1805,7 +2063,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "improper %s identification payload: %s"
 		    , enum_show(&ident_names, peer.kind), ugh);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 	}
@@ -1832,7 +2089,6 @@
 	break;
 
     default:
-	/* XXX Could send notification back */
 	loglog(RC_LOG_SERIOUS, "Unacceptable identity type (%s) in Phase 1 ID Payload"
 	    , enum_show(&ident_names, peer.kind));
 	return FALSE;
@@ -1937,7 +2193,6 @@
 	    /* XXX support more */
 	    loglog(RC_LOG_SERIOUS, "unsupported ID type %s"
 		, idtypename);
-	    /* XXX Could send notification back */
 	    return FALSE;
     }
 
@@ -1973,7 +2228,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "%s ID payload %s has wrong length in Quick I1 (%s)"
 		    , which, idtypename, ugh);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 	    happy(initsubnet(&temp_address, afi->mask_cnt, '0', net));
@@ -1997,7 +2251,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "%s ID payload %s wrong length in Quick I1"
 		    , which, idtypename);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 	    ugh = initaddr(id_pbs->cur
@@ -2012,7 +2265,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "%s ID payload %s bad subnet in Quick I1 (%s)"
 		    , which, idtypename, ugh);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 	    DBG(DBG_PARSING | DBG_CONTROL,
@@ -2035,7 +2287,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "%s ID payload %s wrong length in Quick I1"
 		    , which, idtypename);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 	    ugh = initaddr(id_pbs->cur, afi->ia_sz, afi->af, &temp_address_from);
@@ -2046,7 +2297,6 @@
 	    {
 		loglog(RC_LOG_SERIOUS, "%s ID payload %s malformed (%s) in Quick I1"
 		    , which, idtypename, ugh);
-		/* XXX Could send notification back */
 		return FALSE;
 	    }
 
@@ -2711,7 +2961,6 @@
 		DBG_cond_dump(DBG_CRYPT, "received HASH:"
 		    , hash_pbs->cur, pbs_left(hash_pbs));
 		loglog(RC_LOG_SERIOUS, "received Hash Payload does not match computed value");
-		/* XXX Could send notification back */
 		r = STF_FAIL + INVALID_HASH_INFORMATION;
 	    }
 	}
Index: programs/pluto/ipsec_doi.h
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/ipsec_doi.h,v
retrieving revision 1.29
diff -u -r1.29 ipsec_doi.h
--- programs/pluto/ipsec_doi.h	28 Jul 2002 03:56:25 -0000	1.29
+++ programs/pluto/ipsec_doi.h	1 Feb 2003 01:29:32 -0000
@@ -41,6 +41,13 @@
     quick_inR1_outI2,
     quick_inI2;
 
-extern void send_ipsec_delete(struct state *p2st);
+extern void send_delete(struct state *st);
+extern void accept_delete(struct state *st, struct msg_digest *md,
+     struct payload_digest *p);
+
+extern void send_notification_from_state(struct state *st,
+     enum state_kind state, u_int16_t type);
+extern void send_notification_from_md(struct msg_digest *md, u_int16_t type);
+
 
 extern const char *init_pluto_vendorid(void);
Index: programs/pluto/spdb.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/spdb.c,v
retrieving revision 1.80
diff -u -r1.80 spdb.c
--- programs/pluto/spdb.c	9 Nov 2002 22:48:10 -0000	1.80
+++ programs/pluto/spdb.c	1 Feb 2003 01:29:33 -0000
@@ -679,7 +679,6 @@
     if (sa->isasa_doi != ISAKMP_DOI_IPSEC)
     {
 	loglog(RC_LOG_SERIOUS, "Unknown/unsupported DOI %s", enum_show(&doi_names, sa->isasa_doi));
-	/* XXX Could send notification back */
 	return DOI_NOT_SUPPORTED;
     }
 
@@ -693,7 +692,6 @@
     {
 	loglog(RC_LOG_SERIOUS, "unsupported IPsec DOI situation (%s)"
 	    , bitnamesof(sit_bit_names, ipsecdoisit));
-	/* XXX Could send notification back */
 	return SITUATION_NOT_SUPPORTED;
     }
 
@@ -1485,7 +1483,6 @@
     if (sa->isasa_doi != ISAKMP_DOI_IPSEC)
     {
 	loglog(RC_LOG_SERIOUS, "Unknown or unsupported DOI %s", enum_show(&doi_names, sa->isasa_doi));
-	/* XXX Could send notification back */
 	return DOI_NOT_SUPPORTED;
     }
 
@@ -1497,7 +1494,6 @@
     {
 	loglog(RC_LOG_SERIOUS, "unsupported IPsec DOI situation (%s)"
 	    , bitnamesof(sit_bit_names, ipsecdoisit));
-	/* XXX Could send notification back */
 	return SITUATION_NOT_SUPPORTED;
     }
 
Index: programs/pluto/state.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/state.c,v
retrieving revision 1.102
diff -u -r1.102 state.c
--- programs/pluto/state.c	9 Nov 2002 23:19:02 -0000	1.102
+++ programs/pluto/state.c	1 Feb 2003 01:29:33 -0000
@@ -293,8 +293,9 @@
     }
 
     /* tell the other side of any IPSEC SAs that are going down */
-    if (IS_IPSEC_SA_ESTABLISHED(st->st_state))
-	send_ipsec_delete(st);
+    if (IS_IPSEC_SA_ESTABLISHED(st->st_state) ||
+	IS_ISAKMP_SA_ESTABLISHED(st->st_state))
+	send_delete(st);
 
     delete_event(st);	/* delete any pending timer event */
 
@@ -531,6 +532,55 @@
 		&& memcmp(st->st_tpacket.ptr, packet, packet_len) == 0)
 		    return st;
 
+    return NULL;
+}
+
+struct state *
+find_phase2_state_to_delete(const struct state *p1st, u_int8_t protoid,
+    ipsec_spi_t spi, bool *bogus)
+{
+    struct state *st;
+    int i;
+
+    if (bogus) *bogus = FALSE;
+    for (i = 0; i < STATE_TABLE_SIZE; i++)
+	for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+	    if ( (IS_IPSEC_SA_ESTABLISHED(st->st_state))
+		&& (p1st->st_connection->host_pair == st->st_connection->host_pair)
+		&& (same_peer_ids(p1st->st_connection, st->st_connection, NULL)))
+		{
+		    if ((protoid == PROTO_IPSEC_ESP) && (st->st_esp.present)
+			&&(st->st_esp.attrs.spi == spi))
+			return st;
+		    else if ((protoid == PROTO_IPSEC_AH) && (st->st_ah.present)
+			&& (st->st_ah.attrs.spi == spi))
+			return st;
+		    else if ((protoid == PROTO_IPSEC_ESP) && (st->st_esp.present)
+			&& (st->st_esp.our_spi == spi) && (bogus))
+			*bogus = TRUE;
+		    else if ((protoid == PROTO_IPSEC_AH) && (st->st_ah.present)
+			&& (st->st_ah.our_spi == spi) && (bogus))
+			*bogus = TRUE;
+		}
+    return NULL;
+}
+
+struct state *
+find_phase1_state_to_delete(const struct state *p1st,
+    const u_char *icookie, const u_char *rcookie)
+{
+    struct state *st;
+    int i;
+
+    for (i = 0; i < STATE_TABLE_SIZE; i++)
+	for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+	    if ( (IS_ISAKMP_SA_ESTABLISHED(st->st_state))
+		&& (memcmp(icookie, st->st_icookie, COOKIE_SIZE) == 0)
+		&& (memcmp(rcookie, st->st_rcookie, COOKIE_SIZE) == 0)
+		&& (p1st->st_connection->host_pair == st->st_connection->host_pair)
+		&& (same_peer_ids(p1st->st_connection, st->st_connection, NULL)) ) {
+		    return st;
+		}
     return NULL;
 }
 
Index: programs/pluto/state.h
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/state.h,v
retrieving revision 1.61
diff -u -r1.61 state.h
--- programs/pluto/state.h	9 Nov 2002 23:19:02 -0000	1.61
+++ programs/pluto/state.h	1 Feb 2003 01:29:33 -0000
@@ -224,6 +224,11 @@
 	const ip_address *peer,
 	msgid_t msgid),
     *state_with_serialno(so_serial_t sn),
+    *find_phase2_state_to_delete(const struct state *p1st, u_int8_t protoid,
+       ipsec_spi_t spi, bool *bogus),
+    *find_phase1_state_to_delete(const struct state *p1st,
+       const u_char *icookie, const u_char *rcookie),
+
     *find_phase1_state(const struct connection *c, lset_t ok_states),
     *find_sender(size_t packet_len, u_char *packet);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.