diff -ur freeswan-1.98b/pluto/connections.c freeswan-1.98b-notify-delete/pluto/connections.c --- freeswan-1.98b/pluto/connections.c Fri Jun 21 21:22:47 2002 +++ freeswan-1.98b-notify-delete/pluto/connections.c Wed Jul 24 18:57:21 2002 @@ -674,6 +674,7 @@ } /* set internal fields */ + c->initiated = FALSE; c->instance_serial = 0; c->ac_next = connections; connections = c; @@ -1070,6 +1071,7 @@ * This is a fudge, but not yet important. * If we are to proceed asynchronously, whackfd will be NULL_FD. */ + c->initiated = TRUE; ipsecdoi_initiate(whackfd, c, c->policy, 1, SOS_NOBODY); whackfd = NULL_FD; /* protect from close */ } @@ -1369,6 +1371,7 @@ { set_cur_connection(c); log("terminating SAs using this connection"); + c->initiated = FALSE; delete_states_by_connection(c); reset_cur_connection(); } diff -ur freeswan-1.98b/pluto/connections.h freeswan-1.98b-notify-delete/pluto/connections.h --- freeswan-1.98b/pluto/connections.h Fri Jun 21 21:22:47 2002 +++ freeswan-1.98b-notify-delete/pluto/connections.h Wed Jul 24 18:57:21 2002 @@ -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, diff -ur freeswan-1.98b/pluto/demux.c freeswan-1.98b-notify-delete/pluto/demux.c --- freeswan-1.98b/pluto/demux.c Sat Mar 9 21:45:38 2002 +++ freeswan-1.98b-notify-delete/pluto/demux.c Wed Jul 24 18:38:42 2002 @@ -779,7 +779,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; } @@ -1031,12 +1031,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; } @@ -1044,6 +1059,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,14 +1075,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; } @@ -1079,6 +1095,7 @@ { log("initial Main Mode message is invalid:" " its Encrypted Flag is on"); + SEND_NOTIFICATION(INVALID_FLAGS); return; } @@ -1137,7 +1154,7 @@ { loglog(RC_LOG_SERIOUS, "encrypted Informational Exchange message is invalid" " because it is for incomplete ISAKMP SA"); - /* XXX Could send notification back */ + SEND_NOTIFICATION(INVALID_FLAGS); return; } @@ -1145,7 +1162,7 @@ { loglog(RC_LOG_SERIOUS, "Informational Exchange message is invalid because" " it has a Message ID of 0"); - /* XXX Could send notification back */ + SEND_NOTIFICATION(INVALID_MESSAGE_ID); return; } @@ -1154,7 +1171,7 @@ loglog(RC_LOG_SERIOUS, "Informational Exchange message is invalid because" " it has a previously used Message ID (0x%08lx)" , (unsigned long)md->hdr.isa_msgid); - /* XXX Could send notification back */ + SEND_NOTIFICATION(INVALID_MESSAGE_ID); return; } @@ -1169,7 +1186,7 @@ { loglog(RC_LOG_SERIOUS, "Informational Exchange message for" " an established ISAKMP SA must be encrypted"); - /* XXX Could send notification back */ + SEND_NOTIFICATION(INVALID_FLAGS); return; } from_state = STATE_INFO; @@ -1181,7 +1198,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; } @@ -1189,7 +1206,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; } @@ -1197,7 +1214,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; } @@ -1227,7 +1244,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; } @@ -1238,7 +1255,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; } @@ -1264,6 +1281,7 @@ default: log("unsupported exchange type %s in message" , enum_show(&exchange_names, md->hdr.isa_xchg)); + SEND_NOTIFICATION(UNSUPPORTED_EXCHANGE_TYPE); return; } @@ -1359,14 +1377,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; } @@ -1398,7 +1416,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; } @@ -1433,7 +1451,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; } } @@ -1460,6 +1478,7 @@ if (pd == &md->digest[PAYLIMIT]) { loglog(RC_LOG_SERIOUS, "more than %d payloads in message; ignored", PAYLIMIT); + SEND_NOTIFICATION(PAYLOAD_MALFORMED); return; } @@ -1476,6 +1495,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; } } @@ -1489,6 +1509,7 @@ loglog(RC_LOG_SERIOUS, "%smessage ignored because it contains an" " payload type (%s) unexpected in this message" , excuse, enum_show(&payload_names, np)); + SEND_NOTIFICATION(INVALID_PAYLOAD_TYPE); return; } needed &= ~s; @@ -1497,6 +1518,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; } @@ -1536,6 +1558,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; } } @@ -1551,6 +1574,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; } } @@ -1574,6 +1598,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; } @@ -1587,6 +1612,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; } } @@ -1607,12 +1633,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; } } @@ -1635,7 +1663,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)); } @@ -1882,13 +1910,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" diff -ur freeswan-1.98b/pluto/ipsec_doi.c freeswan-1.98b-notify-delete/pluto/ipsec_doi.c --- freeswan-1.98b/pluto/ipsec_doi.c Fri Jun 21 21:22:47 2002 +++ freeswan-1.98b-notify-delete/pluto/ipsec_doi.c Wed Jul 24 19:22:09 2002 @@ -182,7 +182,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 @@ -244,76 +243,185 @@ * 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 */ + * 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(encst->st_oakley.hasher->hash_digest_size, &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, ¬_pbs) + || !out_raw(spi, spisize, ¬_pbs, "spi")) + impossible(); + close_output_pbs(¬_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); + + DBG(DBG_CRYPT, + DBG_log("HASH(1) computed:"); + DBG_dump("", r_hashval, ctx.hmac_digest_size); + ) + } + + /* 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 a Delete Notification to announce deletion of inbound IPSEC SAs. + 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/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; @@ -325,48 +433,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 */ + if (IS_IPSEC_SA_ESTABLISHED(st->st_state)) { + p1st = find_phase1_state(st->st_connection, TRUE); + if (p1st == NULL) + { + DBG(DBG_CONTROL, DBG_log("no Phase 1 state for Delete")); + return; + } - p1st = find_phase1_state(p2st->st_connection, TRUE); - 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.attrs.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.attrs.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); memset(buffer, '\0', sizeof(buffer)); init_pbs(&reply_pbs, buffer, sizeof(buffer), "delete msg"); @@ -400,7 +502,26 @@ } /* 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; @@ -409,8 +530,8 @@ 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(); @@ -457,6 +578,118 @@ } } +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; iisad_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"); + } + } + else { + /** + * IPSEC (ESP/AH) + */ + ipsec_spi_t ipsec_spi = *((ipsec_spi_t *)spi); + + dst = find_phase2_state_to_delete(st, d->isad_protoid, ipsec_spi); + 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 + */ + loglog(RC_LOG_SERIOUS, "received Delete SA payload: " + "replace IPSEC State #%lu in %d seconds", + dst->st_serialno, EVENT_RETRANSMIT_DELAY_0); + dst->st_margin = EVENT_RETRANSMIT_DELAY_0; + delete_event(dst); + event_schedule(EVENT_SA_REPLACE, + EVENT_RETRANSMIT_DELAY_0, 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"); + } + } + } +} + /* 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. @@ -1282,7 +1515,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 @@ -1329,7 +1561,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; \ } \ } @@ -1733,7 +1964,6 @@ { loglog(RC_LOG_SERIOUS, "improper %s identification payload: %s" , enum_show(&ident_names, peer.kind), ugh); - /* XXX Could send notification back */ return FALSE; } } @@ -1760,7 +1990,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; @@ -1865,7 +2094,6 @@ /* XXX support more */ loglog(RC_LOG_SERIOUS, "unsupported ID type %s" , idtypename); - /* XXX Could send notification back */ return FALSE; } @@ -1901,7 +2129,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)); @@ -1925,7 +2152,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 @@ -1940,7 +2166,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, @@ -1963,7 +2188,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); @@ -1974,7 +2198,6 @@ { loglog(RC_LOG_SERIOUS, "%s ID payload %s malformed (%s) in Quick I1" , which, idtypename, ugh); - /* XXX Could send notification back */ return FALSE; } @@ -2587,7 +2810,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; } } diff -ur freeswan-1.98b/pluto/ipsec_doi.h freeswan-1.98b-notify-delete/pluto/ipsec_doi.h --- freeswan-1.98b/pluto/ipsec_doi.h Sat Mar 23 21:15:32 2002 +++ freeswan-1.98b-notify-delete/pluto/ipsec_doi.h Wed Jul 24 18:38:42 2002 @@ -41,4 +41,11 @@ 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); + diff -ur freeswan-1.98b/pluto/spdb.c freeswan-1.98b-notify-delete/pluto/spdb.c --- freeswan-1.98b/pluto/spdb.c Tue Jun 4 03:28:33 2002 +++ freeswan-1.98b-notify-delete/pluto/spdb.c Wed Jul 24 18:38:42 2002 @@ -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; } diff -ur freeswan-1.98b/pluto/state.c freeswan-1.98b-notify-delete/pluto/state.c --- freeswan-1.98b/pluto/state.c Tue Jun 4 03:28:33 2002 +++ freeswan-1.98b-notify-delete/pluto/state.c Wed Jul 24 18:38:42 2002 @@ -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,48 @@ && 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) +{ + 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_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; + } + 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; } diff -ur freeswan-1.98b/pluto/state.h freeswan-1.98b-notify-delete/pluto/state.h --- freeswan-1.98b/pluto/state.h Tue Jun 4 03:28:33 2002 +++ freeswan-1.98b-notify-delete/pluto/state.h Wed Jul 24 18:38:42 2002 @@ -224,6 +224,10 @@ 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), + *find_phase1_state_to_delete(const struct state *p1st, + const u_char *icookie, const u_char *rcookie), *find_phase1_state(const struct connection *c, bool established), *find_sender(size_t packet_len, u_char *packet);