Re: Revised notify_delete-2.00.diff, was Re: delete-sa-01 test
"D. Hugh Redelmeier" <[email protected]>
| Newsgroups | gmane.network.freeswan.devel |
|---|---|
| Message-ID | <[email protected]> |
| From: D. Hugh Redelmeier <[email protected]> | I've not finished reviewing the code, but I've attached the my working | copy. It compiles cleanly but has never been run. I haven't got more time to work on this just now. I've advanced the code a bit since the last posting. Math? Ken? Care to take this code back for now? Thanks, Hugh Redelmeier [email protected] voice: +1 416 482-8253
notify_delete-2.00.diff.DHR02
(text/plain, 38.3 KB)
Index: demux.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/demux.c,v
retrieving revision 1.137
diff -u -r1.137 demux.c
--- demux.c 7 Feb 2003 04:25:35 -0000 1.137
+++ demux.c 13 Feb 2003 07:11:23 -0000
@@ -783,7 +783,6 @@
static stf_status
informational(struct msg_digest *md UNUSED)
{
- loglog(RC_LOG_SERIOUS, "received and ignored informational message");
return STF_IGNORE;
}
@@ -1002,9 +1001,9 @@
/* Clone actual message contents
* and set up md->packet_pbs to describe it.
*/
- init_pbs(&md->packet_pbs,
- clone_bytes(bigbuffer, packet_len, "message buffer in comm_handle()"),
- packet_len, "packet");
+ init_pbs(&md->packet_pbs
+ , clone_bytes(bigbuffer, packet_len, "message buffer in comm_handle()")
+ , packet_len, "packet");
DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL,
{
@@ -1035,12 +1034,33 @@
struct state *st = NULL;
enum state_kind from_state = STATE_UNDEFINED; /* state we started in */
+# define SEND_NOTIFICATION(t) { \
+ if (st == NULL) \
+ send_notification_from_md(md, t); \
+ else \
+ send_notification_from_state(st, from_state, 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 +1068,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;
}
@@ -1059,18 +1080,18 @@
#endif
case ISAKMP_XCHG_IDPROT: /* part of a Main Mode exchange */
- if (md->hdr.isa_msgid != 0)
+ if (md->hdr.isa_msgid != MAINMODE_MSGID)
{
- log("Message ID was 0x%08lx but should be zero in Main Mode",
- (unsigned long) md->hdr.isa_msgid);
- /* XXX Could send notification back */
+ log("Message ID was 0x%08lx but should be zero in Main Mode"
+ , (unsigned long) md->hdr.isa_msgid);
+ 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 +1104,7 @@
{
log("initial Main Mode message is invalid:"
" its Encrypted Flag is on");
+ SEND_NOTIFICATION(INVALID_FLAGS);
return;
}
@@ -1123,7 +1145,7 @@
case ISAKMP_XCHG_INFO: /* an informational exchange */
st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
- , &md->sender, 0);
+ , &md->sender, MAINMODE_MSGID);
if (st != NULL)
set_cur_state(st);
@@ -1145,7 +1167,7 @@
return;
}
- if (md->hdr.isa_msgid == 0)
+ if (md->hdr.isa_msgid == MAINMODE_MSGID)
{
loglog(RC_LOG_SERIOUS, "Informational Exchange message is invalid because"
" it has a Message ID of 0");
@@ -1185,7 +1207,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,15 +1215,15 @@
{
log("Quick Mode message is invalid because"
" it has a Responder Cookie of 0");
- /* XXX Could send notification back */
+ SEND_NOTIFICATION(INVALID_COOKIE);
return;
}
- if (md->hdr.isa_msgid == 0)
+ if (md->hdr.isa_msgid == MAINMODE_MSGID)
{
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;
}
@@ -1215,7 +1237,7 @@
* ??? what if this is a duplicate of another message?
*/
st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
- , &md->sender, 0);
+ , &md->sender, MAINMODE_MSGID);
if (st == NULL)
{
@@ -1231,7 +1253,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 +1264,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 +1290,7 @@
default:
log("unsupported exchange type %s in message"
, enum_show(&exchange_names, md->hdr.isa_xchg));
+ SEND_NOTIFICATION(UNSUPPORTED_EXCHANGE_TYPE);
return;
}
@@ -1364,23 +1387,24 @@
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;
}
/* Mark as encrypted */
md->encrypted = TRUE;
- DBG(DBG_CRYPT, DBG_log("decrypting %u bytes using algorithm %s",
- (unsigned) pbs_left(&md->message_pbs),
- enum_show(&oakley_enc_names, st->st_oakley.encrypt)));
+ DBG(DBG_CRYPT,
+ DBG_log("decrypting %u bytes using algorithm %s"
+ , (unsigned) pbs_left(&md->message_pbs)
+ , enum_show(&oakley_enc_names, st->st_oakley.encrypt)));
/* do the specified decryption
*
@@ -1403,7 +1427,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;
}
@@ -1425,8 +1449,8 @@
, st);
}
- DBG_cond_dump(DBG_CRYPT, "decrypted:\n", md->message_pbs.cur,
- md->message_pbs.roof - md->message_pbs.cur);
+ DBG_cond_dump(DBG_CRYPT, "decrypted:\n", md->message_pbs.cur
+ , md->message_pbs.roof - md->message_pbs.cur);
DBG_cond_dump(DBG_CRYPT, "next IV:"
, st->st_new_iv, st->st_new_iv_len);
@@ -1438,7 +1462,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 +1489,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 +1506,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 +1520,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 +1529,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 +1569,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 +1585,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 +1609,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 +1623,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 +1644,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 +1676,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));
}
@@ -1685,6 +1719,7 @@
, smc->first_out_payload);
complete_state_transition(mdp, smc->processor(md));
+# undef SEND_NOTIFICATION
}
/* complete job started by the state-specific state transition function */
@@ -1910,8 +1945,8 @@
, enum_name(&state_names, st->st_state));
DBG(DBG_CONTROL,
- DBG_log("state transition function for %s had internal error",
- enum_name(&state_names, from_state)));
+ DBG_log("state transition function for %s had internal error"
+ , enum_name(&state_names, from_state)));
break;
#ifdef DODGE_DH_MISSING_ZERO_BUG
@@ -1934,13 +1969,17 @@
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));
+
+ if (st == NULL)
+ send_notification_from_md(md, md->note);
+ else
+ send_notification_from_state(st, from_state, md->note);
DBG(DBG_CONTROL,
DBG_log("state transition function for %s failed: %s"
Index: ipsec_doi.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/ipsec_doi.c,v
retrieving revision 1.181
diff -u -r1.181 ipsec_doi.c
--- ipsec_doi.c 9 Feb 2003 20:02:09 -0000 1.181
+++ ipsec_doi.c 13 Feb 2003 07:11:25 -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
@@ -286,80 +285,210 @@
return out_generic_chunk(np, &isakmp_nonce_desc, outs, *n, name);
}
-/*
- * Send a notification to the peer. We could make a decision on
+/* 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(¬ification_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 */
+ * This only handles notifications arising during negotiation.
+ * It doesn't handle DELETE notifications (see send_ipsec_delete)
+ */
+static void
+send_notification(struct state *p1st, notification_t type)
+{
+ u_char buffer[1024];
+ pb_stream pbs, r_hdr_pbs;
+ u_char *r_hashval, *r_hash_start;
+ bool secure = IS_ISAKMP_SA_ESTABLISHED(p1st->st_state);
+ msgid_t msgid = secure? generate_msgid(p1st) : MAINMODE_MSGID;
+
+ passert(IS_PHASE1(p1st->st_state));
+
+ log("sending %snotification %s to %s:%u"
+ , secure? "encrypted " : ""
+ , enum_name(&ipsec_notification_names, type)
+ , ip_str(&p1st->st_connection->that.host_addr)
+ , (unsigned)p1st->st_connection->that.host_port);
+
+ zero(&buffer); /* paranoia */
+ 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 = secure ? ISAKMP_NEXT_HASH : ISAKMP_NEXT_N;
+ hdr.isa_xchg = ISAKMP_XCHG_INFO;
+ hdr.isa_msgid = msgid;
+ hdr.isa_flags = secure ? ISAKMP_FLAG_ENCRYPTION : 0;
+ memcpy(hdr.isa_icookie, p1st->st_icookie, COOKIE_SIZE);
+ memcpy(hdr.isa_rcookie, p1st->st_rcookie, COOKIE_SIZE);
+ if (!out_struct(&hdr, &isakmp_hdr_desc, &pbs, &r_hdr_pbs))
+ impossible();
+ }
+
+ /* HASH -- value to be filled later */
+ if (secure)
+ {
+ 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
+ p1st->st_oakley.hasher->hash_digest_size
+#else
+ p1st->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 = 0;
+ isan.isan_protoid = PROTO_ISAKMP;
+
+ if (!out_struct(&isan, &isakmp_notification_desc, &r_hdr_pbs, ¬_pbs))
+ impossible();
+ close_output_pbs(¬_pbs);
+ }
+
+ /* calculate hash value and patch into Hash Payload */
+ if (secure)
+ {
+ struct hmac_ctx ctx;
+
+ hmac_init_chunk(&ctx, p1st->st_oakley.hasher, p1st->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 (secure)
+ {
+ u_char old_iv[MAX_DIGEST_LEN];
+
+ if (p1st->st_iv_len > MAX_DIGEST_LEN)
+ impossible();
+ memcpy(old_iv, p1st->st_iv, p1st->st_iv_len);
+ init_phase2_iv(p1st, &msgid);
+ if (!encrypt_message(&r_hdr_pbs, p1st))
+ impossible();
+ memcpy(p1st->st_iv, old_iv, p1st->st_iv_len);
+ }
+ else
+ {
+ close_output_pbs(&r_hdr_pbs);
+ }
+
+ /* Send packet (preserve st_tpacket) */
+ {
+ chunk_t saved_tpacket = p1st->st_tpacket;
+
+ setchunk(p1st->st_tpacket, pbs.start, pbs_offset(&pbs));
+ send_packet(p1st, "notification packet");
+ p1st->st_tpacket = saved_tpacket;
+ }
+}
+
+void
+send_notification_from_state(struct state *st
+, enum state_kind state
+, notification_t type)
+{
+ struct state *p1st = st;
+
+ if (state == STATE_UNDEFINED)
+ state = st->st_state;
+
+ if (IS_QUICK(state))
+ {
+ /* find a Main Mode state with our cookies */
+ p1st = find_state(st->st_icookie, st->st_rcookie
+ , &st->st_connection->that.host_addr, MAINMODE_MSGID);
+
+ if (p1st == NULL)
+ {
+ /* Since we are in a negotiation, this ought to be rare.
+ * Should happen only if the negotiation drags on a long time.
+ */
+ loglog(RC_LOG_SERIOUS
+ , "no Phase 1 state for Quick Mode notification");
+ return;
+ }
+ }
+
+ send_notification(p1st, type);
+}
+
+void
+send_notification_from_md(struct msg_digest *md, notification_t type)
+{
+ if (md->st == NULL)
+ {
+ /**
+ * Create a dummy state to be able to use send_packet in
+ * send_notification
+ *
+ * we need to set:
+ * st_state
+ * st_connection->that.host_addr
+ * st_connection->that.host_port
+ * st_connection->interface
+ * st_icookie
+ * st_rcookie
+ */
+ struct state st;
+ struct connection cnx;
-/* Send a Delete Notification to announce deletion of inbound IPSEC SAs.
+ zero(&st);
+ zero(&cnx);
+ st.st_state = STATE_MAIN_R0; /* lie! */
+ st.st_connection = &cnx;
+ cnx.that.host_addr = md->sender;
+ cnx.that.host_port = md->sender_port;
+ cnx.interface = md->iface;
+ memcpy(st.st_icookie, md->hdr.isa_icookie, COOKIE_SIZE);
+ memcpy(st.st_rcookie, md->hdr.isa_rcookie, COOKIE_SIZE);
+
+ send_notification(&st, type);
+ }
+ else
+ {
+ send_notification_from_state(md->st, STATE_UNDEFINED, type);
+ }
+}
+
+/* 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 +500,45 @@
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)
+ if (IS_IPSEC_SA_ESTABLISHED(st->st_state))
{
- DBG(DBG_CONTROL, DBG_log("no Phase 1 state for Delete"));
- return;
- }
+ 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;
+ }
- msgid = generate_msgid(p1st);
+ 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++;
+ }
- if (p2st->st_ah.present)
+ passert(ns != said); /* there must be some SAs to delete */
+ }
+ else if (IS_ISAKMP_SA_ESTABLISHED(st->st_state))
{
- 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++;
+ p1st = st;
+ isakmp_sa = TRUE;
+ }
+ 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,21 +572,46 @@
}
/* Delete Payloads */
- while (ns != said) {
- pb_stream del_pbs;
- struct isakmp_delete isad;
-
- ns--;
- isad.isad_doi = ISAKMP_DOI_IPSEC;
- isad.isad_np = ns == said? ISAKMP_NEXT_NONE : ISAKMP_NEXT_D;
- isad.isad_spisize = sizeof(ipsec_spi_t);
- isad.isad_protoid = ns->proto;
-
- isad.isad_nospi = 1;
- if (!out_struct(&isad, &isakmp_delete_desc, &r_hdr_pbs, &del_pbs)
- || !out_raw(&ns->spi, sizeof(ipsec_spi_t), &del_pbs, "delete payload"))
- impossible();
- close_output_pbs(&del_pbs);
+ 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;
+
+ ns--;
+ isad.isad_doi = ISAKMP_DOI_IPSEC;
+ isad.isad_np = ns == said? ISAKMP_NEXT_NONE : ISAKMP_NEXT_D;
+ isad.isad_spisize = sizeof(ipsec_spi_t);
+ isad.isad_protoid = ns->proto;
+
+ isad.isad_nospi = 1;
+ if (!out_struct(&isad, &isakmp_delete_desc, &r_hdr_pbs, &del_pbs)
+ || !out_raw(&ns->spi, sizeof(ipsec_spi_t), &del_pbs, "delete payload"))
+ impossible();
+ close_output_pbs(&del_pbs);
+ }
}
/* calculate hash value and patch into Hash Payload */
@@ -504,6 +654,157 @@
}
}
+void
+accept_delete(struct state *st, struct msg_digest *md, struct payload_digest *p)
+{
+ struct isakmp_delete *d = &(p->payload.delete);
+ size_t sizespi;
+ int i;
+
+ if (st == NULL && (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION) == 0)
+ {
+ 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;
+ }
+
+ 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;
+ case PROTO_IPCOMP:
+ /* nothing interesting to delete */
+ return;
+ default:
+ loglog(RC_LOG_SERIOUS
+ , "ignoring Delete SA payload: unknown Protocol ID (%s)"
+ , enum_show(&protocol_names, d->isad_protoid));
+ return;
+ }
+
+ if (d->isad_spisize != sizespi)
+ {
+ loglog(RC_LOG_SERIOUS
+ , "ignoring Delete SA payload: bad SPI size (%d) for %s"
+ , d->isad_spisize, enum_show(&protocol_names, d->isad_protoid));
+ return;
+ }
+
+ if (pbs_left(&p->pbs) != d->isad_nospi * sizespi)
+ {
+ loglog(RC_LOG_SERIOUS
+ , "ignoring Delete SA payload: invalid payload size");
+ return;
+ }
+
+ for (i = 0; i < d->isad_nospi; i++)
+ {
+ u_char *spi = p->pbs.cur + (i * sizespi);
+
+ if (d->isad_protoid == PROTO_ISAKMP)
+ {
+ /**
+ * ISAKMP
+ */
+ struct state *dst = find_state(spi /*iCookie*/
+ , spi+COOKIE_SIZE /*rCookie*/
+ , &st->st_connection->that.host_addr
+ , MAINMODE_MSGID);
+
+ if (dst == NULL)
+ {
+ loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload: "
+ "ISAKMP SA not found (maybe expired)");
+ }
+ else
+ {
+ loglog(RC_LOG_SERIOUS, "received Delete SA payload: "
+ "deleting ISAKMP State #%lu", dst->st_serialno);
+ delete_state(dst);
+ }
+ }
+ else
+ {
+ /**
+ * IPSEC (ESP/AH)
+ */
+ bool bogus;
+ struct state *dst = find_phase2_state_to_delete(st
+ , d->isad_protoid
+ , *(ipsec_spi_t *)spi /* network order */
+ , &bogus);
+
+ if (dst == NULL)
+ {
+ loglog(RC_LOG_SERIOUS
+ , "ignoring Delete SA payload: %s SA not found (%s)"
+ , enum_show(&protocol_names, d->isad_protoid)
+ , bogus ? "our SPI - bogus implementation" : "maybe expired");
+ }
+ else
+ {
+ struct connection *rc = dst->st_connection;
+
+ if (rc != NULL
+ && rc->newest_ipsec_sa == dst->st_serialno
+ && (rc->policy & POLICY_UP))
+ {
+ /* Last IPSec SA for a permanent connection that we
+ * have initiated. Replace it in a few seconds.
+ *
+ * Useful if the other peer is rebooting.
+ */
+#define DELETE_SA_DELAY EVENT_RETRANSMIT_DELAY_0
+ if (dst->st_event != NULL
+ && 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);
+ }
+ }
+ }
+ }
+}
+
/* 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.
@@ -826,8 +1127,8 @@
/* XXX */
default:
- exit_log("generate_skeyids_iv(): unsupported authentication method %s",
- enum_show(&oakley_auth_names, st->st_oakley.auth));
+ exit_log("generate_skeyids_iv(): unsupported authentication method %s"
+ , enum_show(&oakley_auth_names, st->st_oakley.auth));
}
/* generate SKEYID_* from SKEYID */
@@ -1355,7 +1656,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 +1702,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 +2104,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 +2130,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;
@@ -1843,9 +2140,8 @@
char buf[IDTOA_BUF];
idtoa(&peer, buf, sizeof(buf));
- DBG_log("Peer's ID is %s: '%s'",
- enum_show(&ident_names, id->isaid_idtype),
- buf);
+ DBG_log("Peer's ID is %s: '%s'"
+ , enum_show(&ident_names, id->isaid_idtype), buf);
});
/* Now that we've decoded the ID payload, let's see if we
@@ -1939,7 +2235,6 @@
/* XXX support more */
loglog(RC_LOG_SERIOUS, "unsupported ID type %s"
, idtypename);
- /* XXX Could send notification back */
return FALSE;
}
@@ -1975,7 +2270,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;
}
if (isanyaddr(&temp_address))
@@ -2001,7 +2295,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
@@ -2018,7 +2311,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,
@@ -2041,7 +2333,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);
@@ -2052,7 +2343,6 @@
{
loglog(RC_LOG_SERIOUS, "%s ID payload %s malformed (%s) in Quick I1"
, which, idtypename, ugh);
- /* XXX Could send notification back */
return FALSE;
}
@@ -2139,8 +2429,8 @@
needed_len = DES_CBC_BLOCK_SIZE * 3;
break;
default:
- exit_log("transform %s not implemented yet",
- enum_show(&esp_transformid_names, pi->attrs.transid));
+ exit_log("transform %s not implemented yet"
+ , enum_show(&esp_transformid_names, pi->attrs.transid));
}
switch (pi->attrs.auth)
@@ -2155,8 +2445,8 @@
break;
case AUTH_ALGORITHM_DES_MAC:
default:
- exit_log("AUTH algorithm %s not implemented yet",
- enum_show(&auth_alg_names, pi->attrs.auth));
+ exit_log("AUTH algorithm %s not implemented yet"
+ , enum_show(&auth_alg_names, pi->attrs.auth));
}
break;
@@ -2170,14 +2460,14 @@
needed_len = HMAC_SHA1_KEY_LEN;
break;
default:
- exit_log("transform %s not implemented yet",
- enum_show(&ah_transformid_names, pi->attrs.transid));
+ exit_log("transform %s not implemented yet"
+ , enum_show(&ah_transformid_names, pi->attrs.transid));
}
break;
default:
- exit_log("protocol %s not implemented yet",
- enum_show(&protocol_names, protoid));
+ exit_log("protocol %s not implemented yet"
+ , enum_show(&protocol_names, protoid));
break;
}
@@ -2232,10 +2522,10 @@
hmac_reinit(&ctx_me);
hmac_reinit(&ctx_peer);
- hmac_update(&ctx_me, pi->our_keymat + i - ctx_me.hmac_digest_len,
- ctx_me.hmac_digest_len);
- hmac_update(&ctx_peer, pi->peer_keymat + i - ctx_peer.hmac_digest_len,
- ctx_peer.hmac_digest_len);
+ hmac_update(&ctx_me, pi->our_keymat + i - ctx_me.hmac_digest_len
+ , ctx_me.hmac_digest_len);
+ hmac_update(&ctx_peer, pi->peer_keymat + i - ctx_peer.hmac_digest_len
+ , ctx_peer.hmac_digest_len);
}
}
@@ -2719,7 +3009,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: 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
--- ipsec_doi.h 28 Jul 2002 03:56:25 -0000 1.29
+++ ipsec_doi.h 13 Feb 2003 07:11:25 -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, notification_t type);
+extern void send_notification_from_md(struct msg_digest *md, notification_t type);
+
extern const char *init_pluto_vendorid(void);
Index: spdb.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/spdb.c,v
retrieving revision 1.81
diff -u -r1.81 spdb.c
--- spdb.c 7 Feb 2003 04:25:35 -0000 1.81
+++ spdb.c 13 Feb 2003 07:11:26 -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: state.c
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/state.c,v
retrieving revision 1.102
diff -u -r1.102 state.c
--- state.c 9 Nov 2002 23:19:02 -0000 1.102
+++ state.c 13 Feb 2003 07:11:26 -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 */
@@ -489,8 +490,10 @@
* Find a state object.
*/
struct state *
-find_state(const u_char *icookie, const u_char *rcookie,
- const ip_address *peer, msgid_t /*network order*/ msgid)
+find_state(const u_char *icookie
+, const u_char *rcookie
+, const ip_address *peer
+, msgid_t /*network order*/ msgid)
{
struct state *st = *state_hash(icookie, rcookie, peer);
@@ -531,6 +534,40 @@
&& 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;
+
+ *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))
+ {
+ struct ipsec_proto_info *pr = protoid == PROTO_IPSEC_AH
+ ? &st->st_ah : &st->st_esp;
+
+ if (pr->present)
+ {
+ if (pr->attrs.spi == spi)
+ return st;
+ if (pr->our_spi)
+ *bogus = TRUE;
+ }
+ }
+ }
+ }
return NULL;
}
Index: state.h
===================================================================
RCS file: /freeswan/MASTER/freeswan/programs/pluto/state.h,v
retrieving revision 1.61
diff -u -r1.61 state.h
--- state.h 9 Nov 2002 23:19:02 -0000 1.61
+++ state.h 13 Feb 2003 07:11:27 -0000
@@ -39,6 +39,7 @@
*/
typedef u_int32_t msgid_t; /* Network order! */
+#define MAINMODE_MSGID ((msgid_t) 0)
struct state; /* forward declaration of tag */
extern bool reserve_msgid(struct state *isakmp_sa, msgid_t msgid);
@@ -224,6 +225,8 @@
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(const struct connection *c, lset_t ok_states),
*find_sender(size_t packet_len, u_char *packet);