X-Git-Url: https://git.saurik.com/apple/ipsec.git/blobdiff_plain/52b7d2ce06d68d0a9160d16f6e7c08c21c149d0d..d06a7ccbc5c7dbb1b65b08cfdbb7c4ec0824c666:/ipsec-tools/racoon/isakmp_inf.c diff --git a/ipsec-tools/racoon/isakmp_inf.c b/ipsec-tools/racoon/isakmp_inf.c index a681061..ea0e34b 100644 --- a/ipsec-tools/racoon/isakmp_inf.c +++ b/ipsec-tools/racoon/isakmp_inf.c @@ -1,4 +1,6 @@ -/* $Id: isakmp_inf.c,v 1.14.4.9 2005/08/02 15:09:26 vanhu Exp $ */ +/* $NetBSD: isakmp_inf.c,v 1.14.4.8 2007/08/01 11:52:20 vanhu Exp $ */ + +/* Id: isakmp_inf.c,v 1.44 2006/05/06 20:45:52 manubsd Exp */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -30,16 +32,13 @@ */ #include "config.h" +#include "racoon_types.h" #include #include #include -#ifdef __APPLE__ -#include -#else #include -#endif #include #include #ifndef HAVE_NETINET6_IPSEC @@ -62,6 +61,9 @@ # include # endif #endif +#ifdef ENABLE_HYBRID +#include +#endif #include "libpfkey.h" @@ -72,53 +74,107 @@ #include "misc.h" #include "plog.h" #include "debug.h" +#include "fsm.h" +#include "session.h" +#include "ike_session.h" #include "localconf.h" #include "remoteconf.h" #include "sockmisc.h" +#include "handler.h" +#include "policy.h" +#include "proposal.h" #include "isakmp_var.h" -#include "evt.h" #include "isakmp.h" #ifdef ENABLE_HYBRID #include "isakmp_xauth.h" +#include "isakmp_unity.h" #include "isakmp_cfg.h" #endif #include "isakmp_inf.h" #include "oakley.h" -#include "handler.h" #include "ipsec_doi.h" #include "crypto_openssl.h" #include "pfkey.h" #include "policy.h" #include "algorithm.h" #include "proposal.h" -#include "admin.h" #include "strnames.h" #ifdef ENABLE_NATT #include "nattraversal.h" #endif #include "vpn_control_var.h" #include "vpn_control.h" +#include "ike_session.h" +#include "ipsecSessionTracer.h" +#include "ipsecMessageTracer.h" /* information exchange */ -static int isakmp_info_recv_n __P((struct ph1handle *, vchar_t *, int)); -static int isakmp_info_recv_d __P((struct ph1handle *, vchar_t *)); +static int isakmp_info_recv_n (phase1_handle_t *, struct isakmp_pl_n *, u_int32_t, int); +static int isakmp_info_recv_d (phase1_handle_t *, struct isakmp_pl_d *, u_int32_t, int); #ifdef ENABLE_DPD -static int isakmp_info_recv_r_u __P((struct ph1handle *, - struct isakmp_pl_ru *, u_int32_t)); -static int isakmp_info_recv_r_u_ack __P((struct ph1handle *, - struct isakmp_pl_ru *, u_int32_t)); -static void isakmp_info_send_r_u __P((void *)); +static int isakmp_info_recv_r_u (phase1_handle_t *, struct isakmp_pl_ru *, u_int32_t); +static int isakmp_info_recv_r_u_ack (phase1_handle_t *, struct isakmp_pl_ru *, u_int32_t); #endif #ifdef ENABLE_VPNCONTROL_PORT -static int isakmp_info_recv_lb __P((struct ph1handle *, struct isakmp_pl_lb *lb, int)); +static int isakmp_info_recv_lb (phase1_handle_t *, struct isakmp_pl_lb *lb, int); #endif -static void purge_isakmp_spi __P((int, isakmp_index *, size_t)); -static void purge_ipsec_spi __P((struct sockaddr *, int, u_int32_t *, size_t)); -static void info_recv_initialcontact __P((struct ph1handle *)); +static int +isakmp_ph1_responder_lifetime (phase1_handle_t *iph1, struct isakmp_pl_resp_lifetime *notify) +{ + char *spi; + + if (ntohs(notify->h.len) < sizeof(*notify) + notify->spi_size) { + plog(ASL_LEVEL_ERR, + "invalid spi_size in notification payload.\n"); + return -1; + } + spi = val2str((char *)(notify + 1), notify->spi_size); + + plog(ASL_LEVEL_DEBUG, + "notification message ISAKMP-SA RESPONDER-LIFETIME, " + "doi=%d proto_id=%d spi=%s(size=%d).\n", + ntohl(notify->doi), notify->proto_id, spi, notify->spi_size); + + /* TODO */ + #if 0 + struct isakmp_pl_attr *attrpl; + int len = ntohs(notify->h.len) - (sizeof(*notify) + notify->spi_size); + + attrpl = (struct isakmp_pl_attr *)((char *)(notify + 1) + notify->spi_size); + while (len > 0) { + } + #endif + + racoon_free(spi); + return 0; +} + +static int +isakmp_ph2_responder_lifetime (phase2_handle_t *iph2, struct isakmp_pl_resp_lifetime *notify) +{ + char *spi; + + if (ntohs(notify->h.len) < sizeof(*notify) + notify->spi_size) { + plog(ASL_LEVEL_ERR, + "invalid spi_size in notification payload.\n"); + return -1; + } + spi = val2str((char *)(notify + 1), notify->spi_size); + + plog(ASL_LEVEL_DEBUG, + "notification message IPSEC-SA RESPONDER-LIFETIME, " + "doi=%d proto_id=%d spi=%s(size=%d).\n", + ntohl(notify->doi), notify->proto_id, spi, notify->spi_size); + + /* TODO */ + + racoon_free(spi); + return 0; +} /* %%% * Information Exchange @@ -127,45 +183,71 @@ static void info_recv_initialcontact __P((struct ph1handle *)); * receive Information */ int -isakmp_info_recv(iph1, msg0) - struct ph1handle *iph1; - vchar_t *msg0; +isakmp_info_recv(phase1_handle_t *iph1, vchar_t *msg0) { vchar_t *msg = NULL; + vchar_t *pbuf = NULL; + u_int32_t msgid = 0; int error = -1; struct isakmp *isakmp; struct isakmp_gen *gen; + struct isakmp_parse_t *pa; void *p; vchar_t *hash, *payload; struct isakmp_gen *nd; u_int8_t np; int encrypted; + int flag = 0; + int disconnect = 0; - plog(LLV_DEBUG, LOCATION, NULL, "receive Information.\n"); + plog(ASL_LEVEL_DEBUG, "receive Information.\n"); encrypted = ISSET(((struct isakmp *)msg0->v)->flags, ISAKMP_FLAG_E); + msgid = ((struct isakmp *)msg0->v)->msgid; /* Use new IV to decrypt Informational message. */ if (encrypted) { - struct isakmp_ivm *ivm; + if (iph1->ivm == NULL) { + plog(ASL_LEVEL_ERR, "iph1->ivm == NULL\n"); + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKE_PACKET_RX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to process Information Message (no IV)")); + return -1; + } + /* compute IV */ ivm = oakley_newiv2(iph1, ((struct isakmp *)msg0->v)->msgid); - if (ivm == NULL) + if (ivm == NULL) { + plog(ASL_LEVEL_ERR, + "failed to compute IV\n"); + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKE_PACKET_RX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to process Information Message (can't compute IV)")); return -1; + } msg = oakley_do_decrypt(iph1, msg0, ivm->iv, ivm->ive); oakley_delivm(ivm); - if (msg == NULL) + if (msg == NULL) { + plog(ASL_LEVEL_ERR, + "failed to decrypt packet\n"); + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKE_PACKET_RX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to decrypt Information message")); return -1; + } } else msg = vdup(msg0); /* Safety check */ if (msg->l < sizeof(*isakmp) + sizeof(*gen)) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "ignore information because the " "message is way too short\n"); goto end; @@ -177,14 +259,15 @@ isakmp_info_recv(iph1, msg0) if (encrypted) { if (isakmp->np != ISAKMP_NPTYPE_HASH) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "ignore information because the " "message has no hash payload.\n"); goto end; } - if (iph1->status != PHASE1ST_ESTABLISHED) { - plog(LLV_ERROR, LOCATION, NULL, + if (!FSM_STATE_IS_ESTABLISHED(iph1->status) && + (!iph1->approval || !iph1->skeyid_a)) { + plog(ASL_LEVEL_ERR, "ignore information because ISAKMP-SA " "has not been established yet.\n"); goto end; @@ -192,7 +275,7 @@ isakmp_info_recv(iph1, msg0) /* Safety check */ if (msg->l < sizeof(*isakmp) + ntohs(gen->len) + sizeof(*nd)) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "ignore information because the " "message is too short\n"); goto end; @@ -204,20 +287,20 @@ isakmp_info_recv(iph1, msg0) /* nd length check */ if (ntohs(nd->len) > msg->l - (sizeof(struct isakmp) + ntohs(gen->len))) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "too long payload length (broken message?)\n"); goto end; } if (ntohs(nd->len) < sizeof(*nd)) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "too short payload length (broken message?)\n"); goto end; } payload = vmalloc(ntohs(nd->len)); if (payload == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "cannot allocate memory\n"); goto end; } @@ -227,7 +310,7 @@ isakmp_info_recv(iph1, msg0) /* compute HASH */ hash = oakley_compute_hash1(iph1, isakmp->msgid, payload); if (hash == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "cannot compute hash\n"); vfree(payload); @@ -235,7 +318,7 @@ isakmp_info_recv(iph1, msg0) } if (ntohs(gen->len) - sizeof(struct isakmp_gen) != hash->l) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "ignore information due to hash length mismatch\n"); vfree(hash); @@ -244,7 +327,7 @@ isakmp_info_recv(iph1, msg0) } if (memcmp(p, hash->v, hash->l) != 0) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "ignore information due to hash mismatch\n"); vfree(hash); @@ -252,59 +335,457 @@ isakmp_info_recv(iph1, msg0) goto end; } - plog(LLV_DEBUG, LOCATION, NULL, "hash validated.\n"); + plog(ASL_LEVEL_DEBUG, "hash validated.\n"); vfree(hash); vfree(payload); } else { - /* make sure the packet were encrypted after the beginning of phase 1. */ + /* make sure phase 1 was not yet at encrypted state */ switch (iph1->etype) { case ISAKMP_ETYPE_AGG: - case ISAKMP_ETYPE_BASE: + // %%%%% should also check for unity/mode cfg - last pkt is encrypted in such cases + if (!FSM_STATE_IS_ESTABLISHED(iph1->status) && + ((iph1->side == INITIATOR && iph1->status == IKEV1_STATE_AGG_I_MSG3SENT) || + (iph1->side == RESPONDER && iph1->status == IKEV1_STATE_AGG_R_MSG3RCVD))) { + break; + } + /*FALLTHRU*/ case ISAKMP_ETYPE_IDENT: - if ((iph1->side == INITIATOR && iph1->status < PHASE1ST_MSG3SENT) - || (iph1->side == RESPONDER && iph1->status < PHASE1ST_MSG2SENT)) { + if (!FSM_STATE_IS_ESTABLISHED(iph1->status) && + ((iph1->side == INITIATOR && (iph1->status == IKEV1_STATE_IDENT_I_MSG5SENT + || iph1->status == IKEV1_STATE_IDENT_I_MSG6RCVD)) || + (iph1->side == RESPONDER && (iph1->status == IKEV1_STATE_IDENT_R_MSG5RCVD)))) { break; } /*FALLTHRU*/ default: - plog(LLV_ERROR, LOCATION, iph1->remote, - "%s message must be encrypted\n", - s_isakmp_nptype(np)); + if ((np == ISAKMP_NPTYPE_NONE) && + !FSM_STATE_IS_ESTABLISHED(iph1->status) && + (iph1->side == INITIATOR && (iph1->status == IKEV1_STATE_AGG_I_MSG1SENT))) { + // proposal rejected by peer, terminate now. + disconnect = 1; + } + + plog(ASL_LEVEL_ERR, + "%s message must be encrypted, status 0x%x, side %d\n", + s_isakmp_nptype(np), iph1->status, iph1->side); + error = 0; goto end; } } - switch (np) { - case ISAKMP_NPTYPE_N: - isakmp_info_recv_n(iph1, msg, encrypted); + if (!(pbuf = isakmp_parse(msg))) { + plog(ASL_LEVEL_ERR, + "failed to parse msg"); + error = -1; + goto end; + } + + error = 0; + for (pa = ALIGNED_CAST(struct isakmp_parse_t *)pbuf->v; pa->type; pa++) { // Wcast-align fix (void*) - aligned buffer of aligned (unpacked) structs + switch (pa->type) { + case ISAKMP_NPTYPE_HASH: + /* Handled above */ + break; + case ISAKMP_NPTYPE_N: + if ((ntohs(((struct isakmp_pl_n *)pa->ptr)->type) == ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN) && + !FSM_STATE_IS_ESTABLISHED(iph1->status) && + (iph1->side == INITIATOR && (iph1->status == IKEV1_STATE_AGG_I_MSG1SENT))) { + // proposal rejected by peer, terminate now. + disconnect = 1; + plog(ASL_LEVEL_ERR, + "%s message with %s notification receveid, status 0x%x, side %d\n", + s_isakmp_nptype(np), s_isakmp_notify_msg(ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN), iph1->status, iph1->side); + break; + } + error = isakmp_info_recv_n(iph1, + (struct isakmp_pl_n *)pa->ptr, + msgid, encrypted); + break; + case ISAKMP_NPTYPE_D: + error = isakmp_info_recv_d(iph1, + (struct isakmp_pl_d *)pa->ptr, + msgid, encrypted); + break; + case ISAKMP_NPTYPE_NONCE: + /* XXX to be 6.4.2 ike-01.txt */ + /* XXX IV is to be synchronized. */ + plog(ASL_LEVEL_ERR, + "ignore Acknowledged Informational\n"); + break; + default: + /* don't send information, see isakmp_ident_r1() */ + error = 0; + plog(ASL_LEVEL_ERR, + "reject the packet, " + "received unexpected payload type %s.\n", + s_isakmp_nptype(gen->np)); + } + if(error < 0) { + break; + } else { + flag |= error; + } + } + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKE_PACKET_RX_SUCC, + CONSTSTR("Information message"), + CONSTSTR(NULL)); + +end: + if (error) { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKE_PACKET_RX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to process Information Message")); + } + if (msg != NULL) + vfree(msg); + if (pbuf != NULL) + vfree(pbuf); + if (disconnect) { + ike_session_t *session = NULL; + + if (session = iph1->parent_session) { + gettimeofday(&session->stop_timestamp, NULL); + if (!session->term_reason) { + session->term_reason = ike_session_stopped_by_peer; + } + ike_session_purge_ph1s_by_session(session); + } + } + return error; +} + +/* + * handling of Notification payload + */ +static int +isakmp_info_recv_n(phase1_handle_t *iph1, struct isakmp_pl_n *notify, u_int32_t msgid, int encrypted) +{ + u_int type; + vchar_t *ndata; + char *nraw; + size_t l; + char *spi; + + type = ntohs(notify->type); + + switch (type) { + case ISAKMP_NTYPE_CONNECTED: + case ISAKMP_NTYPE_REPLAY_STATUS: +#ifdef ENABLE_HYBRID + case ISAKMP_NTYPE_UNITY_HEARTBEAT: +#endif + /* do something */ break; - case ISAKMP_NPTYPE_D: + case ISAKMP_NTYPE_RESPONDER_LIFETIME: + if (encrypted) { + return(isakmp_ph1_responder_lifetime(iph1, + (struct isakmp_pl_resp_lifetime *)notify)); + } + break; + case ISAKMP_NTYPE_INITIAL_CONTACT: + if (encrypted) { + info_recv_initialcontact(iph1); + return 0; + } + break; +#ifdef ENABLE_DPD + case ISAKMP_NTYPE_R_U_THERE: if (encrypted) - isakmp_info_recv_d(iph1, msg); - else - plog(LLV_ERROR, LOCATION, iph1->remote, - "unencrypted information message with delete payload received\n"); + return isakmp_info_recv_r_u(iph1, + (struct isakmp_pl_ru *)notify, msgid); + break; + case ISAKMP_NTYPE_R_U_THERE_ACK: + if (encrypted) + return isakmp_info_recv_r_u_ack(iph1, + (struct isakmp_pl_ru *)notify, msgid); break; - case ISAKMP_NPTYPE_NONCE: - /* XXX to be 6.4.2 ike-01.txt */ - /* XXX IV is to be synchronized. */ - plog(LLV_ERROR, LOCATION, iph1->remote, - "ignore Acknowledged Informational\n"); +#endif +#ifdef ENABLE_VPNCONTROL_PORT + case ISAKMP_NTYPE_LOAD_BALANCE: + isakmp_info_recv_lb(iph1, (struct isakmp_pl_lb *)notify, encrypted); break; +#endif + default: - /* don't send information, see isakmp_ident_r1() */ - error = 0; - plog(LLV_ERROR, LOCATION, iph1->remote, - "reject the packet, " - "received unexpecting payload type %d.\n", - gen->np); + { + /* XXX there is a potential of dos attack. */ + if(type >= ISAKMP_NTYPE_MINERROR && + type <= ISAKMP_NTYPE_MAXERROR) { + if (msgid == 0) { + /* don't think this realy deletes ph1 ? */ + plog(ASL_LEVEL_ERR, + "Delete Phase 1 handle.\n"); + return -1; + } else { + if (ike_session_getph2bymsgid(iph1, msgid) == NULL) { + plog(ASL_LEVEL_ERR, + "Fatal %s notify messsage, " + "Phase 1 should be deleted.\n", + s_isakmp_notify_msg(type)); + } else { + plog(ASL_LEVEL_ERR, + "Fatal %s notify messsage, " + "Phase 2 should be deleted.\n", + s_isakmp_notify_msg(type)); + } + } + } else { + plog(ASL_LEVEL_ERR, + "Unhandled notify message %s, " + "no Phase 2 handle found.\n", + s_isakmp_notify_msg(type)); + } + } + break; + } + + /* get spi if specified and allocate */ + if(notify->spi_size > 0) { + if (ntohs(notify->h.len) < sizeof(*notify) + notify->spi_size) { + plog(ASL_LEVEL_ERR, + "Invalid spi_size in notification payload.\n"); + return -1; + } + spi = val2str((char *)(notify + 1), notify->spi_size); + + plog(ASL_LEVEL_DEBUG, + "Notification message %d:%s, " + "doi=%d proto_id=%d spi=%s(size=%d).\n", + type, s_isakmp_notify_msg(type), + ntohl(notify->doi), notify->proto_id, spi, notify->spi_size); + + racoon_free(spi); + } + + /* Send the message data to the logs */ + if(type >= ISAKMP_NTYPE_MINERROR && + type <= ISAKMP_NTYPE_MAXERROR) { + l = ntohs(notify->h.len) - sizeof(*notify) - notify->spi_size; + if (l > 0) { + nraw = (char*)notify; + nraw += sizeof(*notify) + notify->spi_size; + if ((ndata = vmalloc(l)) != NULL) { + memcpy(ndata->v, nraw, ndata->l); + plog(ASL_LEVEL_ERR, + "Message: '%s'.\n", + binsanitize(ndata->v, ndata->l)); + vfree(ndata); + } else { + plog(ASL_LEVEL_ERR, + "Cannot allocate memory\n"); + } + } + } + return 0; +} + +#ifdef ENABLE_VPNCONTROL_PORT +static void +isakmp_info_vpncontrol_notify_ike_failed (phase1_handle_t *iph1, int isakmp_info_initiator, int type, vchar_t *data) +{ + u_int32_t address = iph1_get_remote_v4_address(iph1); + u_int32_t fail_reason; + + /* notify the API that we have received the delete */ + + if (isakmp_info_initiator == FROM_REMOTE) { + int premature = oakley_find_status_in_certchain(iph1->cert, CERT_STATUS_PREMATURE); + int expired = oakley_find_status_in_certchain(iph1->cert, CERT_STATUS_EXPIRED); + + if (premature) { + fail_reason = VPNCTL_NTYPE_LOCAL_CERT_PREMATURE; + plog(ASL_LEVEL_NOTICE, ">>> Server reports client's certificate is pre-mature\n"); + } else if (expired) { + fail_reason = VPNCTL_NTYPE_LOCAL_CERT_EXPIRED; + plog(ASL_LEVEL_NOTICE, ">>> Server reports client's certificate is expired\n"); + } else { + fail_reason = type; + } + vpncontrol_notify_ike_failed(fail_reason, isakmp_info_initiator, address, 0, NULL); + return; + } else { + /* FROM_LOCAL */ + if (type == ISAKMP_INTERNAL_ERROR || + type <= ISAKMP_NTYPE_UNEQUAL_PAYLOAD_LENGTHS) { + int premature = oakley_find_status_in_certchain(iph1->cert_p, CERT_STATUS_PREMATURE); + int expired = oakley_find_status_in_certchain(iph1->cert_p, CERT_STATUS_EXPIRED); + int subjname = oakley_find_status_in_certchain(iph1->cert_p, CERT_STATUS_INVALID_SUBJNAME); + int subjaltname = oakley_find_status_in_certchain(iph1->cert_p, CERT_STATUS_INVALID_SUBJALTNAME); + + if (premature) { + fail_reason = VPNCTL_NTYPE_PEER_CERT_PREMATURE; + plog(ASL_LEVEL_NOTICE, ">>> Server's certificate is pre-mature\n"); + } else if (expired) { + fail_reason = VPNCTL_NTYPE_PEER_CERT_EXPIRED; + plog(ASL_LEVEL_NOTICE, ">>> Server's certificate is expired\n"); + } else if (subjname) { + fail_reason = VPNCTL_NTYPE_PEER_CERT_INVALID_SUBJNAME; + plog(ASL_LEVEL_NOTICE, ">>> Server's certificate subject name not valid\n"); + } else if (subjaltname) { + fail_reason = VPNCTL_NTYPE_PEER_CERT_INVALID_SUBJALTNAME; + plog(ASL_LEVEL_NOTICE, ">>> Server's certificate subject alternate name not valid\n"); + } else { + fail_reason = type; + } + (void)vpncontrol_notify_ike_failed(fail_reason, isakmp_info_initiator, address, + (data ? data->l : 0), (u_int8_t *)(data ? data->v : NULL)); + return; + } + } +} +#endif /* ENABLE_VPNCONTROL_PORT */ + +/* + * handling of Deletion payload + */ +static int +isakmp_info_recv_d(phase1_handle_t *iph1, struct isakmp_pl_d *delete, u_int32_t msgid, int encrypted) +{ + int tlen, num_spi; + phase1_handle_t *del_ph1; + union { + u_int32_t spi32; + u_int16_t spi16[2]; + } spi; + + if (ntohl(delete->doi) != IPSEC_DOI) { + plog(ASL_LEVEL_ERR, + "delete payload with invalid doi:%d.\n", + ntohl(delete->doi)); +#ifdef ENABLE_HYBRID + /* + * At deconnexion time, Cisco VPN client does this + * with a zero DOI. Don't give up in that situation. + */ + if (((iph1->mode_cfg->flags & + ISAKMP_CFG_VENDORID_UNITY) == 0) || (delete->doi != 0)) + return 0; +#else + return 0; +#endif + } + + num_spi = ntohs(delete->num_spi); + tlen = ntohs(delete->h.len) - sizeof(struct isakmp_pl_d); + + if (tlen != num_spi * delete->spi_size) { + plog(ASL_LEVEL_ERR, + "deletion payload with invalid length.\n"); + return 0; + } + + plog(ASL_LEVEL_DEBUG, + "delete payload for protocol %s\n", + s_ipsecdoi_proto(delete->proto_id)); + + if(!iph1->rmconf->weak_phase1_check && !encrypted) { + plog(ASL_LEVEL_WARNING, + "Ignoring unencrypted delete payload " + "(check the weak_phase1_check option)\n"); + return 0; + } + + switch (delete->proto_id) { + case IPSECDOI_PROTO_ISAKMP: + if (delete->spi_size != sizeof(isakmp_index)) { + plog(ASL_LEVEL_ERR, + "delete payload with strange spi " + "size %d(proto_id:%d)\n", + delete->spi_size, delete->proto_id); + return 0; + } + + del_ph1 = ike_session_getph1byindex(iph1->parent_session, (isakmp_index *)(delete + 1)); + if(del_ph1 != NULL){ + + // hack: start a rekey now, if one was pending (only for client). + if (del_ph1->sce_rekey && + del_ph1->parent_session && + del_ph1->parent_session->is_client && + del_ph1->parent_session->established && + !(del_ph1->rmconf->natt_multiple_user && + del_ph1->parent_session->is_l2tpvpn_ipsec)) { + isakmp_ph1rekeyexpire(del_ph1, FALSE); + } + + if (del_ph1->scr) + SCHED_KILL(del_ph1->scr); + + /* + * Do not delete IPsec SAs when receiving an IKE delete notification. + * Just delete the IKE SA. + */ +#ifdef ENABLE_VPNCONTROL_PORT + if (del_ph1->started_by_api || (del_ph1->is_rekey && del_ph1->parent_session && del_ph1->parent_session->is_client)) { + if (ike_session_islast_ph1(del_ph1)) { + isakmp_info_vpncontrol_notify_ike_failed(del_ph1, FROM_REMOTE, VPNCTL_NTYPE_PH1_DELETE, NULL); + } + } +#endif + if (del_ph1->rmconf->natt_multiple_user && + del_ph1->parent_session->is_l2tpvpn_ipsec) { + plog(ASL_LEVEL_DEBUG, "Ignoring IKE delete from peer for L2TP server\n"); + break; + } + isakmp_ph1expire(del_ph1); + } + break; + + case IPSECDOI_PROTO_IPSEC_AH: + case IPSECDOI_PROTO_IPSEC_ESP: + if (delete->spi_size != sizeof(u_int32_t)) { + plog(ASL_LEVEL_ERR, + "delete payload with strange spi " + "size %d(proto_id:%d)\n", + delete->spi_size, delete->proto_id); + return 0; + } + if (iph1->rmconf->natt_multiple_user && + iph1->parent_session->is_l2tpvpn_ipsec) { + uint32_t *ph2_spi = ALIGNED_CAST(u_int32_t *)(delete + 1); + phase2_handle_t *iph2 = ike_session_getph2bysaidx(iph1->local, iph1->remote, delete->proto_id, ph2_spi[0]); + + if (iph2 != NULL) { + iph2->is_defunct = 1; + plog(ASL_LEVEL_DEBUG, "Ignoring SA delete from peer for L2TP server\n"); + break; + } + } + purge_ipsec_spi(iph1->remote, delete->proto_id, + ALIGNED_CAST(u_int32_t *)(delete + 1), num_spi, NULL, NULL); // Wcast-align fix (void*) - delete payload is aligned + break; + + case IPSECDOI_PROTO_IPCOMP: + /* need to handle both 16bit/32bit SPI */ + memset(&spi, 0, sizeof(spi)); + if (delete->spi_size == sizeof(spi.spi16[1])) { + memcpy(&spi.spi16[1], delete + 1, + sizeof(spi.spi16[1])); + } else if (delete->spi_size == sizeof(spi.spi32)) + memcpy(&spi.spi32, delete + 1, sizeof(spi.spi32)); + else { + plog(ASL_LEVEL_ERR, + "delete payload with strange spi " + "size %d(proto_id:%d)\n", + delete->spi_size, delete->proto_id); + return 0; + } + purge_ipsec_spi(iph1->remote, delete->proto_id, + &spi.spi32, num_spi, NULL, NULL); break; + + default: + plog(ASL_LEVEL_ERR, + "deletion message received, " + "invalid proto_id: %d\n", + delete->proto_id); + return 0; } - end: - if (msg != NULL) - vfree(msg); + plog(ASL_LEVEL_DEBUG, "purged SAs.\n"); return 0; } @@ -313,15 +794,14 @@ isakmp_info_recv(iph1, msg0) * send Delete payload (for ISAKMP SA) in Informational exchange. */ int -isakmp_info_send_d1(iph1) - struct ph1handle *iph1; +isakmp_info_send_d1(phase1_handle_t *iph1) { struct isakmp_pl_d *d; vchar_t *payload = NULL; int tlen; int error = 0; - if (iph1->status != PHASE2ST_ESTABLISHED) + if (!FSM_STATE_IS_ESTABLISHED(iph1->status)) return 0; /* create delete payload */ @@ -331,7 +811,7 @@ isakmp_info_send_d1(iph1) tlen = sizeof(*d) + sizeof(isakmp_index); payload = vmalloc(tlen); if (payload == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "failed to get buffer for payload.\n"); return errno; } @@ -348,6 +828,17 @@ isakmp_info_send_d1(iph1) error = isakmp_info_send_common(iph1, payload, ISAKMP_NPTYPE_D, 0); vfree(payload); + if (error) { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("Delete ISAKMP-SA"), + CONSTSTR("Failed to transmit Delete-ISAKMP-SA message")); + } else { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_SUCC, + CONSTSTR("Delete ISAKMP-SA"), + CONSTSTR(NULL)); + } return error; } @@ -357,10 +848,9 @@ isakmp_info_send_d1(iph1) * pfkey msg. It sends always single SPI. */ int -isakmp_info_send_d2(iph2) - struct ph2handle *iph2; +isakmp_info_send_d2(phase2_handle_t *iph2) { - struct ph1handle *iph1; + phase1_handle_t *iph1; struct saproto *pr; struct isakmp_pl_d *d; vchar_t *payload = NULL; @@ -368,16 +858,30 @@ isakmp_info_send_d2(iph2) int error = 0; u_int8_t *spi; - if (iph2->status != PHASE2ST_ESTABLISHED) + if (!FSM_STATE_IS_ESTABLISHED(iph2->status)) return 0; - + /* * don't send delete information if there is no phase 1 handler. * It's nonsensical to negotiate phase 1 to send the information. */ - iph1 = getph1byaddr(iph2->src, iph2->dst); - if (iph1 == NULL) + iph1 = ike_session_get_established_ph1(iph2->parent_session); + if (!iph1) { + iph1 = ike_session_getph1byaddr(iph2->parent_session, iph2->src, iph2->dst); + } + if (iph1 == NULL){ + IPSECSESSIONTRACEREVENT(iph2->parent_session, + IPSECSESSIONEVENTCODE_IKE_PACKET_TX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to transmit Information message")); + IPSECSESSIONTRACEREVENT(iph2->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("Delete IPSEC-SA"), + CONSTSTR("Failed to transmit Delete-IPSEC-SA message")); + plog(ASL_LEVEL_DEBUG, + "No ph1 handler found, could not send DELETE_SA\n"); return 0; + } /* create delete payload */ for (pr = iph2->approval->head; pr != NULL; pr = pr->next) { @@ -392,7 +896,15 @@ isakmp_info_send_d2(iph2) tlen = sizeof(*d) + pr->spisize; payload = vmalloc(tlen); if (payload == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + IPSECSESSIONTRACEREVENT(iph2->parent_session, + IPSECSESSIONEVENTCODE_IKE_PACKET_TX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to transmit Information message")); + IPSECSESSIONTRACEREVENT(iph2->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("Delete IPSEC-SA"), + CONSTSTR("Failed to transmit Delete-IPSEC-SA message")); + plog(ASL_LEVEL_ERR, "failed to get buffer for payload.\n"); return errno; } @@ -416,53 +928,76 @@ isakmp_info_send_d2(iph2) error = isakmp_info_send_common(iph1, payload, ISAKMP_NPTYPE_D, 0); vfree(payload); + if (error) { + IPSECSESSIONTRACEREVENT(iph2->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("Delete IPSEC-SA"), + CONSTSTR("Failed to transmit Delete-IPSEC-SA")); + } else { + IPSECSESSIONTRACEREVENT(iph2->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_SUCC, + CONSTSTR("Delete IPSEC-SA"), + CONSTSTR(NULL)); + } } return error; } /* - * send Notification payload (for without ISAKMP SA) in Informational exchange + * send Notification payload (without ISAKMP SA) in an Informational exchange */ int -isakmp_info_send_nx(isakmp, remote, local, type, data) - struct isakmp *isakmp; - struct sockaddr *remote, *local; - int type; - vchar_t *data; +isakmp_info_send_nx(struct isakmp *isakmp, struct sockaddr_storage *remote, struct sockaddr_storage *local, + int type, vchar_t *data) { - struct ph1handle *iph1 = NULL; + phase1_handle_t *iph1 = NULL; struct remoteconf *rmconf; vchar_t *payload = NULL; int tlen; int error = -1; struct isakmp_pl_n *n; int spisiz = 0; /* see below */ + ike_session_t *sess = ike_session_get_session(local, remote, FALSE, NULL); /* search appropreate configuration */ rmconf = getrmconf(remote); if (rmconf == NULL) { - plog(LLV_ERROR, LOCATION, remote, + IPSECSESSIONTRACEREVENT(sess, + IPSECSESSIONEVENTCODE_IKE_PACKET_TX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to transmit Information message (no remote configuration)")); + plog(ASL_LEVEL_ERR, "no configuration found for peer address.\n"); goto end; } /* add new entry to isakmp status table. */ - iph1 = newph1(); - if (iph1 == NULL) + iph1 = ike_session_newph1(ISAKMP_VERSION_NUMBER_IKEV1); + if (iph1 == NULL) { + IPSECSESSIONTRACEREVENT(sess, + IPSECSESSIONEVENTCODE_IKE_PACKET_TX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to transmit Information message (no new Phase 1)")); + plog(ASL_LEVEL_ERR, + "failed to allocate ph1"); return -1; + } memcpy(&iph1->index.i_ck, &isakmp->i_ck, sizeof(cookie_t)); isakmp_newcookie((char *)&iph1->index.r_ck, remote, local); - iph1->status = PHASE1ST_START; + fsm_set_state(&iph1->status, IKEV1_STATE_INFO); iph1->rmconf = rmconf; + retain_rmconf(iph1->rmconf); iph1->side = INITIATOR; iph1->version = isakmp->v; iph1->flags = 0; iph1->msgid = 0; /* XXX */ #ifdef ENABLE_HYBRID - if ((iph1->mode_cfg = isakmp_cfg_mkstate()) == NULL) - return -1; + if ((iph1->mode_cfg = isakmp_cfg_mkstate()) == NULL) { + error = -1; + goto end; + } #endif #ifdef ENABLE_FRAG iph1->frag = 0; @@ -470,16 +1005,30 @@ isakmp_info_send_nx(isakmp, remote, local, type, data) #endif /* copy remote address */ - if (copy_ph1addresses(iph1, rmconf, remote, local) < 0) - return -1; + if (copy_ph1addresses(iph1, rmconf, remote, local) < 0) { + IPSECSESSIONTRACEREVENT(sess, + IPSECSESSIONEVENTCODE_IKE_PACKET_TX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to transmit Information Message (can't copy Phase 1 addresses)")); + plog(ASL_LEVEL_ERR, + "failed to copy ph1 addresses"); + error = -1; + iph1 = NULL; /* deleted in copy_ph1addresses */ + goto end; + } tlen = sizeof(*n) + spisiz; if (data) tlen += data->l; payload = vmalloc(tlen); if (payload == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + IPSECSESSIONTRACEREVENT(sess, + IPSECSESSIONEVENTCODE_IKE_PACKET_TX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to transmit Information Message (can't allocate payload)")); + plog(ASL_LEVEL_ERR, "failed to get buffer to send.\n"); + error = -1; goto end; } @@ -491,43 +1040,42 @@ isakmp_info_send_nx(isakmp, remote, local, type, data) n->spi_size = spisiz; n->type = htons(type); if (spisiz) - memset(n + 1, 0, spisiz); /*XXX*/ + memset(n + 1, 0, spisiz); /* XXX spisiz is always 0 */ if (data) memcpy((caddr_t)(n + 1) + spisiz, data->v, data->l); #ifdef ENABLE_VPNCONTROL_PORT - { - u_int32_t address; - if (type == ISAKMP_INTERNAL_ERROR || - type <= ISAKMP_NTYPE_UNEQUAL_PAYLOAD_LENGTHS) { - if (remote->sa_family == AF_INET) - address = ((struct sockaddr_in *)remote)->sin_addr.s_addr; - else - address = 0; - (void)vpncontrol_notify_ike_failed(type, FROM_LOCAL, address, - (data ? data->l : 0), (data ? data->v : NULL)); - } - } + isakmp_info_vpncontrol_notify_ike_failed(iph1, FROM_LOCAL, type, data); #endif - + if (ike_session_link_phase1(sess, iph1)) + fatal_error(-1); + error = isakmp_info_send_common(iph1, payload, ISAKMP_NPTYPE_N, 0); vfree(payload); - + if (error) { + IPSECSESSIONTRACEREVENT(sess, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("Without ISAKMP-SA"), + CONSTSTR("Failed to transmit Without-ISAKMP-SA message")); + } else { + IPSECSESSIONTRACEREVENT(sess, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_SUCC, + CONSTSTR("Without ISAKMP-SA"), + CONSTSTR(NULL)); + } + end: if (iph1 != NULL) - delph1(iph1); + ike_session_unlink_phase1(iph1); return error; } /* - * send Notification payload (for ISAKMP SA) in Informational exchange + * send Notification payload (with ISAKMP SA) in an Informational exchange */ int -isakmp_info_send_n1(iph1, type, data) - struct ph1handle *iph1; - int type; - vchar_t *data; +isakmp_info_send_n1(phase1_handle_t *iph1, int type, vchar_t *data) { vchar_t *payload = NULL; int tlen; @@ -556,7 +1104,11 @@ isakmp_info_send_n1(iph1, type, data) tlen += data->l; payload = vmalloc(tlen); if (payload == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("ISAKMP-SA"), + CONSTSTR("Failed to transmit ISAKMP-SA message (can't allocate payload)")); + plog(ASL_LEVEL_ERR, "failed to get buffer to send.\n"); return errno; } @@ -574,37 +1126,33 @@ isakmp_info_send_n1(iph1, type, data) memcpy((caddr_t)(n + 1) + spisiz, data->v, data->l); #ifdef ENABLE_VPNCONTROL_PORT - { - u_int32_t address; - - if (type == ISAKMP_INTERNAL_ERROR || - type <= ISAKMP_NTYPE_UNEQUAL_PAYLOAD_LENGTHS) { - if (iph1->remote->sa_family == AF_INET) - address = ((struct sockaddr_in *)iph1->remote)->sin_addr.s_addr; - else - address = 0; - (void)vpncontrol_notify_ike_failed(type, FROM_LOCAL, address, - (data ? data->l : 0), (data ? data->v : NULL)); - } - } + isakmp_info_vpncontrol_notify_ike_failed(iph1, FROM_LOCAL, type, data); #endif error = isakmp_info_send_common(iph1, payload, ISAKMP_NPTYPE_N, iph1->flags); vfree(payload); + if (error) { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("ISAKMP-SA"), + CONSTSTR("Can't transmit ISAKMP-SA message")); + } else { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_SUCC, + CONSTSTR("ISAKMP-SA"), + CONSTSTR(NULL)); + } return error; } /* - * send Notification payload (for IPsec SA) in Informational exchange + * send Notification payload (with IPsec SA) in an Informational exchange */ int -isakmp_info_send_n2(iph2, type, data) - struct ph2handle *iph2; - int type; - vchar_t *data; +isakmp_info_send_n2(phase2_handle_t *iph2, int type, vchar_t *data) { - struct ph1handle *iph1 = iph2->ph1; + phase1_handle_t *iph1 = iph2->ph1; vchar_t *payload = NULL; int tlen; int error = 0; @@ -622,7 +1170,11 @@ isakmp_info_send_n2(iph2, type, data) tlen += data->l; payload = vmalloc(tlen); if (payload == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + IPSECSESSIONTRACEREVENT(iph2->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("IPSEC-SA"), + CONSTSTR("Failed to transmit IPSEC-SA message (can't allocate payload)")); + plog(ASL_LEVEL_ERR, "failed to get buffer to send.\n"); return errno; } @@ -634,14 +1186,25 @@ isakmp_info_send_n2(iph2, type, data) n->proto_id = pr->proto_id; /* IPSEC AH/ESP/whatever*/ n->spi_size = pr->spisize; n->type = htons(type); - *(u_int32_t *)(n + 1) = pr->spi; + memcpy(n + 1, &pr->spi, sizeof(u_int32_t)); // Wcast-align fix - copy instead of assign if (data) memcpy((caddr_t)(n + 1) + pr->spisize, data->v, data->l); iph2->flags |= ISAKMP_FLAG_E; /* XXX Should we do FLAG_A ? */ error = isakmp_info_send_common(iph1, payload, ISAKMP_NPTYPE_N, iph2->flags); vfree(payload); - + if (error) { + IPSECSESSIONTRACEREVENT(iph2->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("IPSEC-SA"), + CONSTSTR("Failed to transmit IPSEC-SA message")); + } else { + IPSECSESSIONTRACEREVENT(iph2->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_SUCC, + CONSTSTR("IPSEC-SA"), + CONSTSTR(NULL)); + } + return error; } @@ -650,13 +1213,9 @@ isakmp_info_send_n2(iph2, type, data) * When ph1->skeyid_a == NULL, send message without encoding. */ int -isakmp_info_send_common(iph1, payload, np, flags) - struct ph1handle *iph1; - vchar_t *payload; - u_int32_t np; - int flags; +isakmp_info_send_common(phase1_handle_t *iph1, vchar_t *payload, u_int32_t np, int flags) { - struct ph2handle *iph2 = NULL; + phase2_handle_t *iph2 = NULL; vchar_t *hash = NULL; struct isakmp *isakmp; struct isakmp_gen *gen; @@ -665,48 +1224,68 @@ isakmp_info_send_common(iph1, payload, np, flags) int error = -1; /* add new entry to isakmp status table */ - iph2 = newph2(); - if (iph2 == NULL) + iph2 = ike_session_newph2(ISAKMP_VERSION_NUMBER_IKEV1, PHASE2_TYPE_INFO); + if (iph2 == NULL) { + plog(ASL_LEVEL_ERR, + "failed to allocate ph2"); goto end; + } iph2->dst = dupsaddr(iph1->remote); + if (iph2->dst == NULL) { + plog(ASL_LEVEL_ERR, + "failed to duplicate remote address"); + ike_session_delph2(iph2); + goto end; + } iph2->src = dupsaddr(iph1->local); - switch (iph1->remote->sa_family) { + if (iph2->src == NULL) { + plog(ASL_LEVEL_ERR, + "failed to duplicate local address"); + ike_session_delph2(iph2); + goto end; + } + switch (iph1->remote->ss_family) { case AF_INET: -#ifndef ENABLE_NATT +#if (!defined(ENABLE_NATT)) || (defined(BROKEN_NATT)) ((struct sockaddr_in *)iph2->dst)->sin_port = 0; ((struct sockaddr_in *)iph2->src)->sin_port = 0; #endif break; #ifdef INET6 case AF_INET6: +#if (!defined(ENABLE_NATT)) || (defined(BROKEN_NATT)) ((struct sockaddr_in6 *)iph2->dst)->sin6_port = 0; ((struct sockaddr_in6 *)iph2->src)->sin6_port = 0; +#endif break; #endif default: - plog(LLV_ERROR, LOCATION, NULL, - "invalid family: %d\n", iph1->remote->sa_family); - delph2(iph2); + plog(ASL_LEVEL_ERR, + "invalid family: %d\n", iph1->remote->ss_family); + ike_session_delph2(iph2); goto end; } - iph2->ph1 = iph1; iph2->side = INITIATOR; - iph2->status = PHASE2ST_START; + fsm_set_state(&iph2->status, IKEV1_STATE_INFO); iph2->msgid = isakmp_newmsgid2(iph1); /* get IV and HASH(1) if skeyid_a was generated. */ if (iph1->skeyid_a != NULL) { iph2->ivm = oakley_newiv2(iph1, iph2->msgid); if (iph2->ivm == NULL) { - delph2(iph2); + plog(ASL_LEVEL_ERR, + "failed to generate IV"); + ike_session_delph2(iph2); goto end; } /* generate HASH(1) */ - hash = oakley_compute_hash1(iph2->ph1, iph2->msgid, payload); + hash = oakley_compute_hash1(iph1, iph2->msgid, payload); if (hash == NULL) { - delph2(iph2); + plog(ASL_LEVEL_ERR, + "failed to generate HASH"); + ike_session_delph2(iph2); goto end; } @@ -725,15 +1304,14 @@ isakmp_info_send_common(iph1, payload, np, flags) else iph2->flags = (hash == NULL ? 0 : ISAKMP_FLAG_A); - insph2(iph2); - bindph12(iph1, iph2); + ike_session_link_ph2_to_ph1(iph1, iph2); tlen += sizeof(*isakmp) + payload->l; /* create buffer for isakmp payload */ iph2->sendbuf = vmalloc(tlen); if (iph2->sendbuf == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "failed to get buffer to send.\n"); goto err; } @@ -775,18 +1353,23 @@ isakmp_info_send_common(iph1, payload, np, flags) tmp = oakley_do_encrypt(iph2->ph1, iph2->sendbuf, iph2->ivm->ive, iph2->ivm->iv); VPTRINIT(iph2->sendbuf); - if (tmp == NULL) + if (tmp == NULL) { + plog(ASL_LEVEL_ERR, + "failed to encrypt packet"); goto err; + } iph2->sendbuf = tmp; } /* HDR*, HASH(1), N */ if (isakmp_send(iph2->ph1, iph2->sendbuf) < 0) { + plog(ASL_LEVEL_ERR, + "failed to send packet"); VPTRINIT(iph2->sendbuf); goto err; } - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_DEBUG, "sendto Information %s.\n", s_isakmp_nptype(np)); /* @@ -797,17 +1380,26 @@ isakmp_info_send_common(iph1, payload, np, flags) /* XXX If Acknowledged Informational required, don't delete ph2handle */ error = 0; VPTRINIT(iph2->sendbuf); + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKE_PACKET_TX_SUCC, + CONSTSTR("Information message"), + CONSTSTR(NULL)); + goto err; /* XXX */ end: + if (error) { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKE_PACKET_TX_FAIL, + CONSTSTR("Information message"), + CONSTSTR("Failed to transmit Information message")); + } if (hash) vfree(hash); return error; err: - unbindph12(iph2); - remph2(iph2); - delph2(iph2); + ike_session_unlink_phase2(iph2); goto end; } @@ -818,12 +1410,7 @@ err: * XXX Which is SPI to be included, inbound or outbound ? */ vchar_t * -isakmp_add_pl_n(buf0, np_p, type, pr, data) - vchar_t *buf0; - u_int8_t **np_p; - int type; - struct saproto *pr; - vchar_t *data; +isakmp_add_pl_n(vchar_t *buf0, u_int8_t **np_p, int type, struct saproto *pr, vchar_t *data) { vchar_t *buf = NULL; struct isakmp_pl_n *n; @@ -843,7 +1430,7 @@ isakmp_add_pl_n(buf0, np_p, type, pr, data) } else buf = vmalloc(tlen); if (!buf) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "failed to get a payload buffer.\n"); return NULL; } @@ -855,7 +1442,7 @@ isakmp_add_pl_n(buf0, np_p, type, pr, data) n->proto_id = pr->proto_id; /* IPSEC AH/ESP/whatever*/ n->spi_size = pr->spisize; n->type = htons(type); - *(u_int32_t *)(n + 1) = pr->spi; /* XXX */ + memcpy(n + 1, &pr->spi, sizeof(u_int32_t)); // Wcast-align fix - copy instead of assign with cast if (data) memcpy((caddr_t)(n + 1) + pr->spisize, data->v, data->l); @@ -865,232 +1452,60 @@ isakmp_add_pl_n(buf0, np_p, type, pr, data) return buf; } -/* - * handling to receive Notification payload - */ -static int -isakmp_info_recv_n(iph1, msg, encrypted) - struct ph1handle *iph1; - vchar_t *msg; - int encrypted; -{ - struct isakmp_pl_n *n = NULL; - u_int type; - vchar_t *pbuf; - struct isakmp_parse_t *pa, *pap; - char *spi; - - if (!(pbuf = isakmp_parse(msg))) - return -1; - pa = (struct isakmp_parse_t *)pbuf->v; - for (pap = pa; pap->type; pap++) { - switch (pap->type) { - case ISAKMP_NPTYPE_HASH: - /* do something here */ - break; - case ISAKMP_NPTYPE_NONCE: - /* send to ack */ - break; - case ISAKMP_NPTYPE_N: - n = (struct isakmp_pl_n *)pap->ptr; - break; - default: - vfree(pbuf); - return -1; - } - } - vfree(pbuf); - if (!n) - return -1; - - type = ntohs(n->type); - - switch (type) { - case ISAKMP_NTYPE_CONNECTED: - case ISAKMP_NTYPE_RESPONDER_LIFETIME: - case ISAKMP_NTYPE_REPLAY_STATUS: - /* do something */ - break; - case ISAKMP_NTYPE_INITIAL_CONTACT: - if (encrypted) - info_recv_initialcontact(iph1); - else - plog(LLV_ERROR, LOCATION, iph1->remote, - "unencrypted INITIAL_CONTACT message received"); - break; -#ifdef ENABLE_DPD - case ISAKMP_NTYPE_R_U_THERE: - if (encrypted) - isakmp_info_recv_r_u(iph1, (struct isakmp_pl_ru *)n, - ((struct isakmp *)msg->v)->msgid); - else - plog(LLV_ERROR, LOCATION, iph1->remote, - "unencrypted R_U_THERE message received"); - break; - case ISAKMP_NTYPE_R_U_THERE_ACK: - if (encrypted) - isakmp_info_recv_r_u_ack(iph1, (struct isakmp_pl_ru *)n, - ((struct isakmp *)msg->v)->msgid); - else - plog(LLV_ERROR, LOCATION, iph1->remote, - "unencrypted R_U_THERE_ACK received"); - break; -#endif - -#ifdef ENABLE_VPNCONTROL_PORT - case ISAKMP_NTYPE_LOAD_BALANCE: - isakmp_info_recv_lb(iph1, (struct isakmp_pl_lb *)n, encrypted); - break; -#endif - - default: - if (encrypted) { - u_int32_t msgid = ((struct isakmp *)msg->v)->msgid; - struct ph2handle *iph2; - - /* XXX there is a potential of dos attack. */ - if (msgid == 0) { - /* delete ph1 */ - plog(LLV_ERROR, LOCATION, iph1->remote, - "delete phase1 handle.\n"); - return -1; - } else { - iph2 = getph2bymsgid(iph1, msgid); - if (iph2 == NULL) { - plog(LLV_ERROR, LOCATION, iph1->remote, - "unknown notify message, " - "no phase2 handle found.\n"); - } else { - /* sanity check */ - if (n->h.len < (sizeof(struct isakmp_pl_n) + n->spi_size)) - plog(LLV_ERROR, LOCATION, iph1->remote, - "notification payload length invalid.\n"); - else { - -#ifdef ENABLE_VPNCONTROL_PORT - - u_int32_t address; - u_int16_t data_len = n->h.len - (sizeof(struct isakmp_pl_n) + n->spi_size); - u_int8_t *data_ptr = ((u_int8_t *)n) + (n->h.len - data_len); - - if (type == ISAKMP_INTERNAL_ERROR || - type <= ISAKMP_NTYPE_UNEQUAL_PAYLOAD_LENGTHS) { - if (iph1->remote->sa_family == AF_INET) - address = ((struct sockaddr_in *)iph1->remote)->sin_addr.s_addr; - else - address = 0; - - vpncontrol_notify_ike_failed(type, FROM_REMOTE, address, data_len, data_ptr); - } -#endif - /* delete ph2 */ - unbindph12(iph2); - remph2(iph2); - delph2(iph2); - } - } - } - } else - plog(LLV_ERROR, LOCATION, iph1->remote, - "unencrypted notification message received"); - break; - } - - /* get spi and allocate */ - if (ntohs(n->h.len) < sizeof(*n) + n->spi_size) { - plog(LLV_ERROR, LOCATION, iph1->remote, - "invalid spi_size in notification payload.\n"); - return -1; - } - spi = val2str((char *)(n + 1), n->spi_size); - - plog(LLV_DEBUG, LOCATION, iph1->remote, - "notification message %d:%s, " - "doi=%d proto_id=%d spi=%s(size=%d).\n", - type, s_isakmp_notify_msg(type), - ntohl(n->doi), n->proto_id, spi, n->spi_size); - - racoon_free(spi); - - return(0); -} void -purge_isakmp_spi(proto, spi, n) - int proto; - isakmp_index *spi; /*network byteorder*/ - size_t n; -{ - struct ph1handle *iph1; - size_t i; - - for (i = 0; i < n; i++) { - iph1 = getph1byindex(&spi[i]); - if (!iph1) - continue; - - plog(LLV_INFO, LOCATION, NULL, - "purged ISAKMP-SA proto_id=%s spi=%s.\n", - s_ipsecdoi_proto(proto), - isakmp_pindex(&spi[i], 0)); - - if (iph1->sce) - SCHED_KILL(iph1->sce); - iph1->status = PHASE1ST_EXPIRED; - iph1->sce = sched_new(1, isakmp_ph1delete_stub, iph1); - } -} - -static void -purge_ipsec_spi(dst0, proto, spi, n) - struct sockaddr *dst0; - int proto; - u_int32_t *spi; /*network byteorder*/ - size_t n; +purge_ipsec_spi(struct sockaddr_storage *dst0, int proto, u_int32_t *spi /*network byteorder*/, size_t n, u_int32_t *inbound_spi, size_t *max_inbound_spi) { vchar_t *buf = NULL; struct sadb_msg *msg, *next, *end; struct sadb_sa *sa; - struct sockaddr *src, *dst; - struct ph2handle *iph2; - size_t i; + struct sadb_lifetime *lt; + struct sockaddr_storage *src, *dst; + phase2_handle_t *iph2; + u_int64_t created; + size_t i, j = 0; caddr_t mhp[SADB_EXT_MAX + 1]; buf = pfkey_dump_sadb(ipsecdoi2pfkey_proto(proto)); if (buf == NULL) { - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_DEBUG, "pfkey_dump_sadb returned nothing.\n"); return; } - msg = (struct sadb_msg *)buf->v; - end = (struct sadb_msg *)(buf->v + buf->l); + msg = ALIGNED_CAST(struct sadb_msg *)buf->v; + end = ALIGNED_CAST(struct sadb_msg *)(buf->v + buf->l); while (msg < end) { if ((msg->sadb_msg_len << 3) < sizeof(*msg)) break; - next = (struct sadb_msg *)((caddr_t)msg + (msg->sadb_msg_len << 3)); + next = ALIGNED_CAST(struct sadb_msg *)((caddr_t)msg + (msg->sadb_msg_len << 3)); if (msg->sadb_msg_type != SADB_DUMP) { msg = next; continue; } if (pfkey_align(msg, mhp) || pfkey_check(mhp)) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "pfkey_check (%s)\n", ipsec_strerror()); msg = next; continue; } - sa = (struct sadb_sa *)(mhp[SADB_EXT_SA]); + sa = ALIGNED_CAST(struct sadb_sa *)(mhp[SADB_EXT_SA]); // Wcast-align fix (void*) - buffer of pointers to aligned structs if (!sa || !mhp[SADB_EXT_ADDRESS_SRC] || !mhp[SADB_EXT_ADDRESS_DST]) { msg = next; continue; } - src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); - dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + src = ALIGNED_CAST(struct sockaddr_storage*)PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); // Wcast-align fix (void*) - buffer of pointers to aligned structs + dst = ALIGNED_CAST(struct sockaddr_storage*)PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + lt = ALIGNED_CAST(struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; + if(lt != NULL) + created = lt->sadb_lifetime_addtime; + else + created = 0; if (sa->sadb_sa_state != SADB_SASTATE_MATURE && sa->sadb_sa_state != SADB_SASTATE_DYING) { @@ -1100,47 +1515,56 @@ purge_ipsec_spi(dst0, proto, spi, n) /* XXX n^2 algorithm, inefficient */ - /* don't delete inbound SAs at the moment */ + /* don't delete inbound SAs at the moment (just save them in inbound_spi) */ /* XXX should we remove SAs with opposite direction as well? */ - if (CMPSADDR(dst0, dst)) { + if (CMPSADDR2(dst0, dst)) { msg = next; continue; } for (i = 0; i < n; i++) { - plog(LLV_DEBUG, LOCATION, NULL, - "check spi(packet)=%u spi(db)=%u.\n", - ntohl(spi[i]), ntohl(sa->sadb_sa_spi)); + u_int32_t *i_spi; + if (spi[i] != sa->sadb_sa_spi) continue; - pfkey_send_delete(lcconf->sock_pfkey, - msg->sadb_msg_satype, - IPSEC_MODE_ANY, - src, dst, sa->sadb_sa_spi); - /* * delete a relative phase 2 handler. * continue to process if no relative phase 2 handler * exists. */ - iph2 = getph2bysaidx(src, dst, proto, spi[i]); - if (iph2) { + if (inbound_spi && max_inbound_spi && j < *max_inbound_spi) { + i_spi = &inbound_spi[j]; + } else { + i_spi = NULL; + } + iph2 = ike_session_getph2bysaidx2(src, dst, proto, spi[i], i_spi); + + pfkey_send_delete(lcconf->sock_pfkey, + msg->sadb_msg_satype, + IPSEC_MODE_ANY, + src, dst, sa->sadb_sa_spi); + + if(iph2 != NULL){ delete_spd(iph2); - unbindph12(iph2); - remph2(iph2); - delph2(iph2); + ike_session_unlink_phase2(iph2); + if (i_spi) { + j++; + } } - plog(LLV_INFO, LOCATION, NULL, - "purged IPsec-SA proto_id=%s spi=%u.\n", - s_ipsecdoi_proto(proto), - ntohl(spi[i])); + plog(ASL_LEVEL_INFO, "Purged IPsec-SA proto_id=%s spi=%u.\n", + s_ipsecdoi_proto(proto), + ntohl(spi[i])); } msg = next; } + if (max_inbound_spi) { + *max_inbound_spi = j; + } + if (buf) vfree(buf); } @@ -1152,17 +1576,16 @@ purge_ipsec_spi(dst0, proto, spi, n) * Sun IKE behavior, and makes rekeying work much better when the peer * restarts. */ -static void -info_recv_initialcontact(iph1) - struct ph1handle *iph1; +void +info_recv_initialcontact(phase1_handle_t *iph1) { vchar_t *buf = NULL; struct sadb_msg *msg, *next, *end; struct sadb_sa *sa; - struct sockaddr *src, *dst; + struct sockaddr_storage *src, *dst; caddr_t mhp[SADB_EXT_MAX + 1]; int proto_id, i; - struct ph2handle *iph2; + phase2_handle_t *iph2; #if 0 char *loc, *rem; #endif @@ -1170,9 +1593,12 @@ info_recv_initialcontact(iph1) if (f_local) return; + // TODO: make sure that is_rekey is cleared for this. and session indicates the same #if 0 - loc = strdup(saddrwop2str(iph1->local)); - rem = strdup(saddrwop2str(iph1->remote)); + loc = racoon_strdup(saddrwop2str(iph1->local)); + rem = racoon_strdup(saddrwop2str(iph1->remote)); + STRDUP_FATAL(loc); + STRDUP_FATAL(rem); /* * Purge all IPSEC-SAs for the peer. We can do this @@ -1182,35 +1608,35 @@ info_recv_initialcontact(iph1) for (i = 0; i < pfkey_nsatypes; i++) { proto_id = pfkey2ipsecdoi_proto(pfkey_satypes[i].ps_satype); - plog(LLV_INFO, LOCATION, NULL, + plog(ASL_LEVEL_INFO, "purging %s SAs for %s -> %s\n", pfkey_satypes[i].ps_name, loc, rem); if (pfkey_send_delete_all(lcconf->sock_pfkey, pfkey_satypes[i].ps_satype, IPSEC_MODE_ANY, iph1->local, iph1->remote) == -1) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "delete_all %s -> %s failed for %s (%s)\n", loc, rem, pfkey_satypes[i].ps_name, ipsec_strerror()); goto the_hard_way; } - deleteallph2(iph1->local, iph1->remote, proto_id); + ike_session_deleteallph2(iph1->local, iph1->remote, proto_id); - plog(LLV_INFO, LOCATION, NULL, + plog(ASL_LEVEL_INFO, "purging %s SAs for %s -> %s\n", pfkey_satypes[i].ps_name, rem, loc); if (pfkey_send_delete_all(lcconf->sock_pfkey, pfkey_satypes[i].ps_satype, IPSEC_MODE_ANY, iph1->remote, iph1->local) == -1) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "delete_all %s -> %s failed for %s (%s)\n", rem, loc, pfkey_satypes[i].ps_name, ipsec_strerror()); goto the_hard_way; } - deleteallph2(iph1->remote, iph1->local, proto_id); + ike_session_deleteallph2(iph1->remote, iph1->local, proto_id); } racoon_free(loc); @@ -1224,25 +1650,25 @@ info_recv_initialcontact(iph1) buf = pfkey_dump_sadb(SADB_SATYPE_UNSPEC); if (buf == NULL) { - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_DEBUG, "pfkey_dump_sadb returned nothing.\n"); return; } - msg = (struct sadb_msg *)buf->v; - end = (struct sadb_msg *)(buf->v + buf->l); + msg = ALIGNED_CAST(struct sadb_msg *)buf->v; + end = ALIGNED_CAST(struct sadb_msg *)(buf->v + buf->l); while (msg < end) { if ((msg->sadb_msg_len << 3) < sizeof(*msg)) break; - next = (struct sadb_msg *)((caddr_t)msg + (msg->sadb_msg_len << 3)); + next = ALIGNED_CAST(struct sadb_msg *)((caddr_t)msg + (msg->sadb_msg_len << 3)); if (msg->sadb_msg_type != SADB_DUMP) { msg = next; continue; } if (pfkey_align(msg, mhp) || pfkey_check(mhp)) { - plog(LLV_ERROR, LOCATION, NULL, + plog(ASL_LEVEL_ERR, "pfkey_check (%s)\n", ipsec_strerror()); msg = next; continue; @@ -1254,9 +1680,9 @@ info_recv_initialcontact(iph1) msg = next; continue; } - sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; - src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); - dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + sa = ALIGNED_CAST(struct sadb_sa *)mhp[SADB_EXT_SA]; // Wcast-align fix (void*) - buffer of pointers to aligned structs + src = ALIGNED_CAST(struct sockaddr_storage *)PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = ALIGNED_CAST(struct sockaddr_storage *)PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); if (sa->sadb_sa_state != SADB_SASTATE_MATURE && sa->sadb_sa_state != SADB_SASTATE_DYING) { @@ -1321,7 +1747,7 @@ info_recv_initialcontact(iph1) continue; } - plog(LLV_INFO, LOCATION, NULL, + plog(ASL_LEVEL_INFO, "purging spi=%u.\n", ntohl(sa->sadb_sa_spi)); pfkey_send_delete(lcconf->sock_pfkey, msg->sadb_msg_satype, @@ -1333,12 +1759,10 @@ info_recv_initialcontact(iph1) * exists. */ proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype); - iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi); + iph2 = ike_session_getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi); if (iph2) { delete_spd(iph2); - unbindph12(iph2); - remph2(iph2); - delph2(iph2); + ike_session_unlink_phase2(iph2); } msg = next; @@ -1347,244 +1771,127 @@ info_recv_initialcontact(iph1) vfree(buf); } -/* - * handling to receive Deletion payload - */ -static int -isakmp_info_recv_d(iph1, msg) - struct ph1handle *iph1; - vchar_t *msg; -{ - struct isakmp_pl_d *d; - int tlen, num_spi; - vchar_t *pbuf; - struct isakmp_parse_t *pa, *pap; - int protected = 0; - union { - u_int32_t spi32; - u_int16_t spi16[2]; - } spi; - - /* validate the type of next payload */ - if (!(pbuf = isakmp_parse(msg))) - return -1; - pa = (struct isakmp_parse_t *)pbuf->v; - for (pap = pa; pap->type; pap++) { - switch (pap->type) { - case ISAKMP_NPTYPE_D: - break; - case ISAKMP_NPTYPE_HASH: - if (pap == pa) { - protected++; - break; - } - plog(LLV_ERROR, LOCATION, iph1->remote, - "received next payload type %d " - "in wrong place (must be the first payload).\n", - pap->type); - vfree(pbuf); - return -1; - default: - /* don't send information, see isakmp_ident_r1() */ - plog(LLV_ERROR, LOCATION, iph1->remote, - "reject the packet, " - "received unexpecting payload type %d.\n", - pap->type); - vfree(pbuf); - return 0; - } - } - - if (!protected) { - plog(LLV_ERROR, LOCATION, NULL, - "delete payload is not proteted, " - "ignored.\n"); - vfree(pbuf); - return -1; - } - - /* process a delete payload */ - for (pap = pa; pap->type; pap++) { - if (pap->type != ISAKMP_NPTYPE_D) - continue; - - d = (struct isakmp_pl_d *)pap->ptr; - - if (ntohl(d->doi) != IPSEC_DOI) { - plog(LLV_ERROR, LOCATION, iph1->remote, - "delete payload with invalid doi:%d.\n", - ntohl(d->doi)); -#ifdef ENABLE_HYBRID - /* - * At deconnexion time, Cisco VPN client does this - * with a zero DOI. Don't give up in that situation. - */ - if (((iph1->mode_cfg->flags & - ISAKMP_CFG_VENDORID_UNITY) == 0) || (d->doi != 0)) - continue; -#else - continue; -#endif - } - - num_spi = ntohs(d->num_spi); - tlen = ntohs(d->h.len) - sizeof(struct isakmp_pl_d); - - if (tlen != num_spi * d->spi_size) { - plog(LLV_ERROR, LOCATION, iph1->remote, - "deletion payload with invalid length.\n"); - vfree(pbuf); - return -1; - } - - switch (d->proto_id) { - case IPSECDOI_PROTO_ISAKMP: - if (d->spi_size != sizeof(isakmp_index)) { - plog(LLV_ERROR, LOCATION, iph1->remote, - "delete payload with strange spi " - "size %d(proto_id:%d)\n", - d->spi_size, d->proto_id); - continue; - } - - if (iph1->scr) - SCHED_KILL(iph1->scr); - - purge_remote(iph1); - break; - - case IPSECDOI_PROTO_IPSEC_AH: - case IPSECDOI_PROTO_IPSEC_ESP: - if (d->spi_size != sizeof(u_int32_t)) { - plog(LLV_ERROR, LOCATION, iph1->remote, - "delete payload with strange spi " - "size %d(proto_id:%d)\n", - d->spi_size, d->proto_id); - continue; - } - EVT_PUSH(iph1->local, iph1->remote, - EVTT_PEER_DELETE, NULL); - purge_ipsec_spi(iph1->remote, d->proto_id, - (u_int32_t *)(d + 1), num_spi); - break; - - case IPSECDOI_PROTO_IPCOMP: - /* need to handle both 16bit/32bit SPI */ - memset(&spi, 0, sizeof(spi)); - if (d->spi_size == sizeof(spi.spi16[1])) { - memcpy(&spi.spi16[1], d + 1, - sizeof(spi.spi16[1])); - } else if (d->spi_size == sizeof(spi.spi32)) - memcpy(&spi.spi32, d + 1, sizeof(spi.spi32)); - else { - plog(LLV_ERROR, LOCATION, iph1->remote, - "delete payload with strange spi " - "size %d(proto_id:%d)\n", - d->spi_size, d->proto_id); - continue; - } - purge_ipsec_spi(iph1->remote, d->proto_id, - &spi.spi32, num_spi); - break; - - default: - plog(LLV_ERROR, LOCATION, iph1->remote, - "deletion message received, " - "invalid proto_id: %d\n", - d->proto_id); - continue; - } - - plog(LLV_DEBUG, LOCATION, NULL, "purged SAs.\n"); - } - - vfree(pbuf); - - return 0; -} - void -isakmp_check_notify(gen, iph1) - struct isakmp_gen *gen; /* points to Notify payload */ - struct ph1handle *iph1; +isakmp_check_notify(struct isakmp_gen *gen /* points to Notify payload */, phase1_handle_t *iph1) { struct isakmp_pl_n *notify = (struct isakmp_pl_n *)gen; - plog(LLV_DEBUG, LOCATION, iph1->remote, + plog(ASL_LEVEL_DEBUG, "Notify Message received\n"); switch (ntohs(notify->type)) { case ISAKMP_NTYPE_CONNECTED: - plog(LLV_WARNING, LOCATION, iph1->remote, - "ignore CONNECTED notification.\n"); - break; case ISAKMP_NTYPE_RESPONDER_LIFETIME: - plog(LLV_WARNING, LOCATION, iph1->remote, - "ignore RESPONDER-LIFETIME notification.\n"); - break; case ISAKMP_NTYPE_REPLAY_STATUS: - plog(LLV_WARNING, LOCATION, iph1->remote, - "ignore REPLAY-STATUS notification.\n"); + case ISAKMP_NTYPE_HEARTBEAT: +#ifdef ENABLE_HYBRID + case ISAKMP_NTYPE_UNITY_HEARTBEAT: +#endif + plog(ASL_LEVEL_WARNING, + "Ignore %s notification.\n", + s_isakmp_notify_msg(ntohs(notify->type))); break; case ISAKMP_NTYPE_INITIAL_CONTACT: - plog(LLV_WARNING, LOCATION, iph1->remote, - "ignore INITIAL-CONTACT notification, " - "because it is only accepted after phase1.\n"); + plog(ASL_LEVEL_WARNING, + "Ignore INITIAL-CONTACT notification, " + "because it is only accepted after Phase 1.\n"); break; case ISAKMP_NTYPE_LOAD_BALANCE: - plog(LLV_WARNING, LOCATION, iph1->remote, - "ignore LOAD-BALANCE notification, " - "because it is only accepted after phase1.\n"); - break; - case ISAKMP_NTYPE_HEARTBEAT: - plog(LLV_WARNING, LOCATION, iph1->remote, - "ignore HEARTBEAT notification\n"); + plog(ASL_LEVEL_WARNING, + "Ignore LOAD-BALANCE notification, " + "because it is only accepted after Phase 1.\n"); break; default: isakmp_info_send_n1(iph1, ISAKMP_NTYPE_INVALID_PAYLOAD_TYPE, NULL); - plog(LLV_ERROR, LOCATION, iph1->remote, - "received unknown notification type %u.\n", - ntohs(notify->type)); + plog(ASL_LEVEL_ERR, + "Received unknown notification type %s.\n", + s_isakmp_notify_msg(ntohs(notify->type))); } return; } +void +isakmp_check_ph2_notify(struct isakmp_gen *gen /* points to Notify payload */, phase2_handle_t *iph2) +{ + struct isakmp_pl_n *notify = (struct isakmp_pl_n *)gen; + + plog(ASL_LEVEL_DEBUG, + "Phase 2 Notify Message received\n"); + + switch (ntohs(notify->type)) { + case ISAKMP_NTYPE_RESPONDER_LIFETIME: + return((void)isakmp_ph2_responder_lifetime(iph2, + (struct isakmp_pl_resp_lifetime *)notify)); + break; + case ISAKMP_NTYPE_CONNECTED: + case ISAKMP_NTYPE_REPLAY_STATUS: + case ISAKMP_NTYPE_HEARTBEAT: +#ifdef ENABLE_HYBRID + case ISAKMP_NTYPE_UNITY_HEARTBEAT: +#endif + plog(ASL_LEVEL_WARNING, + "Ignore %s notification.\n", + s_isakmp_notify_msg(ntohs(notify->type))); + break; + case ISAKMP_NTYPE_INITIAL_CONTACT: + plog(ASL_LEVEL_WARNING, + "Ignore INITIAL-CONTACT notification, " + "because it is only accepted after Phase 1.\n"); + break; + case ISAKMP_NTYPE_LOAD_BALANCE: + plog(ASL_LEVEL_WARNING, + "Ignore LOAD-BALANCE notification, " + "because it is only accepted after Phase 1.\n"); + break; + default: + isakmp_info_send_n1(iph2->ph1, ISAKMP_NTYPE_INVALID_PAYLOAD_TYPE, NULL); + plog(ASL_LEVEL_ERR, + "Received unknown notification type %s.\n", + s_isakmp_notify_msg(ntohs(notify->type))); + } + + return; +} + #ifdef ENABLE_VPNCONTROL_PORT static int -isakmp_info_recv_lb(iph1, n, encrypted) - struct ph1handle *iph1; - struct isakmp_pl_lb *n; - int encrypted; +isakmp_info_recv_lb(phase1_handle_t *iph1, struct isakmp_pl_lb *n, int encrypted) { if (iph1->side != INITIATOR) { - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_DEBUG, "LOAD-BALANCE notification ignored - we are not the initiator.\n"); return 0; } - if (iph1->remote->sa_family != AF_INET) { - plog(LLV_DEBUG, LOCATION, NULL, - "LOAD-BALANCE notification ignored - only supported for IPv4.\n"); - return 0; - } if (!encrypted) { - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_DEBUG, "LOAD-BALANCE notification ignored - not protected.\n"); return 0; } if (ntohs(n->h.len) != sizeof(struct isakmp_pl_lb)) { - plog(LLV_DEBUG, LOCATION, NULL, + plog(ASL_LEVEL_DEBUG, "Invalid length of payload\n"); return -1; - } + } + vpncontrol_notify_ike_failed(ISAKMP_NTYPE_LOAD_BALANCE, FROM_REMOTE, - ((struct sockaddr_in*)iph1->remote)->sin_addr.s_addr, 4, (u_int8_t*)(&(n->address))); + iph1_get_remote_v4_address(iph1), 4, (u_int8_t*)(&(n->address))); - plog(LLV_DEBUG, LOCATION, iph1->remote, - "received LOAD_BALANCE notification - redirect address=%x.\n", - ntohl(n->address)); + plog(ASL_LEVEL_NOTICE, + "Received LOAD_BALANCE notification.\n"); + + if (((struct sockaddr_in*)iph1->remote)->sin_addr.s_addr != ntohl(n->address)) { + plog(ASL_LEVEL_DEBUG, + "Deleting old Phase 1 because of LOAD_BALANCE notification - redirect address=%x.\n", + ntohl(n->address)); + + if (FSM_STATE_IS_ESTABLISHED(iph1->status)) { + isakmp_info_send_d1(iph1); + } + isakmp_ph1expire(iph1); + } return 0; } @@ -1592,17 +1899,14 @@ isakmp_info_recv_lb(iph1, n, encrypted) #ifdef ENABLE_DPD static int -isakmp_info_recv_r_u (iph1, ru, msgid) - struct ph1handle *iph1; - struct isakmp_pl_ru *ru; - u_int32_t msgid; +isakmp_info_recv_r_u (phase1_handle_t *iph1, struct isakmp_pl_ru *ru, u_int32_t msgid) { struct isakmp_pl_ru *ru_ack; vchar_t *payload = NULL; int tlen; int error = 0; - plog(LLV_DEBUG, LOCATION, iph1->remote, + plog(ASL_LEVEL_DEBUG, "DPD R-U-There received\n"); /* XXX should compare cookies with iph1->index? @@ -1610,7 +1914,11 @@ isakmp_info_recv_r_u (iph1, ru, msgid) tlen = sizeof(*ru_ack); payload = vmalloc(tlen); if (payload == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("R-U-THERE? ACK"), + CONSTSTR("Failed to transmit DPD response")); + plog(ASL_LEVEL_ERR, "failed to get buffer to send.\n"); return errno; } @@ -1630,51 +1938,75 @@ isakmp_info_recv_r_u (iph1, ru, msgid) error = isakmp_info_send_common(iph1, payload, ISAKMP_NPTYPE_N, ISAKMP_FLAG_E); vfree(payload); + if (error) { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("R-U-THERE? ACK"), + CONSTSTR("Failed to transmit DPD ack")); + } else { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_SUCC, + CONSTSTR("R-U-THERE? ACK"), + CONSTSTR(NULL)); + } - plog(LLV_DEBUG, LOCATION, NULL, "received a valid R-U-THERE, ACK sent\n"); + plog(ASL_LEVEL_DEBUG, "received a valid R-U-THERE, ACK sent\n"); /* Should we mark tunnel as active ? */ return error; } static int -isakmp_info_recv_r_u_ack (iph1, ru, msgid) - struct ph1handle *iph1; - struct isakmp_pl_ru *ru; - u_int32_t msgid; +isakmp_info_recv_r_u_ack (phase1_handle_t *iph1, struct isakmp_pl_ru *ru, u_int32_t msgid) { - plog(LLV_DEBUG, LOCATION, iph1->remote, + plog(ASL_LEVEL_DEBUG, "DPD R-U-There-Ack received\n"); /* XXX Maintain window of acceptable sequence numbers ? * => ru->data <= iph2->dpd_seq && * ru->data >= iph2->dpd_seq - iph2->dpd_fails ? */ - if (ntohl(ru->data) != iph1->dpd_seq-1) { - plog(LLV_ERROR, LOCATION, iph1->remote, + if (ntohl(ru->data) != iph1->dpd_seq) { + plog(ASL_LEVEL_ERR, "Wrong DPD sequence number (%d, %d expected).\n", - ntohl(ru->data), iph1->dpd_seq-1); + ntohl(ru->data), iph1->dpd_seq); return 0; } if (memcmp(ru->i_ck, iph1->index.i_ck, sizeof(cookie_t)) || memcmp(ru->r_ck, iph1->index.r_ck, sizeof(cookie_t))) { - plog(LLV_ERROR, LOCATION, iph1->remote, + plog(ASL_LEVEL_ERR, "Cookie mismatch in DPD ACK!.\n"); return 0; } iph1->dpd_fails = 0; + iph1->dpd_seq++; + /* Useless ??? */ iph1->dpd_lastack = time(NULL); - if (iph1->dpd_r_u != NULL) - SCHED_KILL(iph1->dpd_r_u); + SCHED_KILL(iph1->dpd_r_u); isakmp_sched_r_u(iph1, 0); - plog(LLV_DEBUG, LOCATION, NULL, "received an R-U-THERE-ACK\n"); + if (iph1->side == INITIATOR) { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_DPD_INIT_RESP, + CONSTSTR("Initiator DPD Response"), + CONSTSTR(NULL)); + } else { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_DPD_RESP_RESP, + CONSTSTR("Responder DPD Response"), + CONSTSTR(NULL)); + } + plog(ASL_LEVEL_DEBUG, "received an R-U-THERE-ACK\n"); + +#ifdef ENABLE_VPNCONTROL_PORT + vpncontrol_notify_peer_resp_ph1(1, iph1); +#endif /* ENABLE_VPNCONTROL_PORT */ return 0; } @@ -1683,11 +2015,10 @@ isakmp_info_recv_r_u_ack (iph1, ru, msgid) /* * send Delete payload (for ISAKMP SA) in Informational exchange. */ -static void -isakmp_info_send_r_u(arg) - void *arg; +void +isakmp_info_send_r_u(void *arg) { - struct ph1handle *iph1 = arg; + phase1_handle_t *iph1 = arg; /* create R-U-THERE payload */ struct isakmp_pl_ru *ru; @@ -1695,12 +2026,22 @@ isakmp_info_send_r_u(arg) int tlen; int error = 0; - plog(LLV_DEBUG, LOCATION, iph1->remote, "DPD monitoring....\n"); + if (!FSM_STATE_IS_ESTABLISHED(iph1->status)) { + plog(ASL_LEVEL_DEBUG, "DPD r-u send aborted, invalid Phase 1 status %d....\n", + iph1->status); + return; + } if (iph1->dpd_fails >= iph1->rmconf->dpd_maxfails) { - EVT_PUSH(iph1->local, iph1->remote, EVTT_DPD_TIMEOUT, NULL); + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_DPD_MAX_RETRANSMIT, + CONSTSTR("DPD maximum retransmits"), + CONSTSTR("maxed-out of DPD requests without receiving an ack")); + + (void)vpncontrol_notify_ike_failed(VPNCTL_NTYPE_PEER_DEAD, FROM_LOCAL, iph1_get_remote_v4_address(iph1), 0, NULL); + purge_remote(iph1); - plog(LLV_DEBUG, LOCATION, iph1->remote, + plog(ASL_LEVEL_DEBUG, "DPD: remote seems to be dead\n"); /* Do not reschedule here: phase1 is deleted, @@ -1709,27 +2050,14 @@ isakmp_info_send_r_u(arg) return; } - /* TODO: check recent activity to avoid useless sends... */ - - /* XXX: why do we have a NULL LIST_FIRST even when a Phase2 exists ??? */ -#if 0 - if (LIST_FIRST(&iph1->ph2tree) == NULL){ - /* XXX: No Ph2 => no need to test ph1 ? - */ - /* Reschedule the r_u_there.... - XXX: reschedule when a new ph2 ? - */ - isakmp_sched_r_u(iph1, 0); - plog(LLV_DEBUG, LOCATION, iph1->remote, - "no phase2 handler, rescheduling send_r_u (%d).\n", iph1->rmconf->dpd_interval); - return 0; - } -#endif - tlen = sizeof(*ru); payload = vmalloc(tlen); if (payload == NULL) { - plog(LLV_ERROR, LOCATION, NULL, + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("R-U-THERE?"), + CONSTSTR("Failed to transmit DPD request")); + plog(ASL_LEVEL_ERR, "failed to get buffer for payload.\n"); return; } @@ -1754,29 +2082,136 @@ isakmp_info_send_r_u(arg) error = isakmp_info_send_common(iph1, payload, ISAKMP_NPTYPE_N, 0); vfree(payload); + if (error) { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_FAIL, + CONSTSTR("R-U-THERE?"), + CONSTSTR("Failed to transmit DPD request")); + } else { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + IPSECSESSIONEVENTCODE_IKEV1_INFO_NOTICE_TX_SUCC, + CONSTSTR("R-U-THERE?"), + CONSTSTR(NULL)); + } - plog(LLV_DEBUG, LOCATION, iph1->remote, + if (iph1->side == INITIATOR) { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + iph1->dpd_fails? IPSECSESSIONEVENTCODE_IKEV1_DPD_INIT_RETRANSMIT : IPSECSESSIONEVENTCODE_IKEV1_DPD_INIT_REQ, + CONSTSTR("Initiator DPD Request"), + CONSTSTR(NULL)); + } else { + IPSECSESSIONTRACEREVENT(iph1->parent_session, + iph1->dpd_fails? IPSECSESSIONEVENTCODE_IKEV1_DPD_RESP_RETRANSMIT : IPSECSESSIONEVENTCODE_IKEV1_DPD_RESP_REQ, + CONSTSTR("Responder DPD Request"), + CONSTSTR(NULL)); + } + plog(ASL_LEVEL_DEBUG, "DPD R-U-There sent (%d)\n", error); /* will be decreased if ACK received... */ iph1->dpd_fails++; - /* XXX should be increased only when ACKed ? */ - iph1->dpd_seq++; - /* Reschedule the r_u_there with a short delay, * will be deleted/rescheduled if ACK received before */ isakmp_sched_r_u(iph1, 1); - plog(LLV_DEBUG, LOCATION, iph1->remote, + plog(ASL_LEVEL_DEBUG, "rescheduling send_r_u (%d).\n", iph1->rmconf->dpd_retry); } +/* + * monitor DPD (ALGORITHM_INBOUND_DETECT) Informational exchange. + */ +static void +isakmp_info_monitor_r_u_algo_inbound_detect (phase1_handle_t *iph1) +{ + if (!FSM_STATE_IS_ESTABLISHED(iph1->status)) { + plog(ASL_LEVEL_DEBUG, "DPD monitoring (for ALGORITHM_INBOUND_DETECT) aborted, invalid Phase 1 status %d....\n", + iph1->status); + return; + } + + plog(ASL_LEVEL_DEBUG, "DPD monitoring (for ALGORITHM_INBOUND_DETECT) ....\n"); + + // check phase1 for ike packets received from peer + if (iph1->peer_sent_ike) { + // yes, reshedule check + iph1->peer_sent_ike = 0; + + /* ike packets received from peer... reschedule dpd */ + isakmp_sched_r_u(iph1, 0); + + plog(ASL_LEVEL_DEBUG, + "ike packets received from peer... reschedule monitor.\n"); + + return; + } + + // after ike packets, next we check if any data was received + if (!iph1->parent_session->peer_sent_data_sc_dpd) { + isakmp_info_send_r_u(iph1); + } else { + isakmp_sched_r_u(iph1, 0); + + plog(ASL_LEVEL_DEBUG, + "rescheduling DPD monitoring (for ALGORITHM_INBOUND_DETECT).\n"); + } + iph1->parent_session->peer_sent_data_sc_dpd = 0; +} + +/* + * monitor DPD (ALGORITHM_BLACKHOLE_DETECT) Informational exchange. + */ +static void +isakmp_info_monitor_r_u_algo_blackhole_detect (phase1_handle_t *iph1) +{ + if (!FSM_STATE_IS_ESTABLISHED(iph1->status)) { + plog(ASL_LEVEL_DEBUG, "DPD monitoring (for ALGORITHM_BLACKHOLE_DETECT) aborted, invalid Phase 1 status %d....\n", + iph1->status); + return; + } + + plog(ASL_LEVEL_DEBUG, "DPD monitoring (for ALGORITHM_BLACKHOLE_DETECT) ....\n"); + + // check if data was sent but none was received + if (iph1->parent_session->i_sent_data_sc_dpd && + !iph1->parent_session->peer_sent_data_sc_dpd) { + isakmp_info_send_r_u(iph1); + } else { + isakmp_sched_r_u(iph1, 0); + + plog(ASL_LEVEL_DEBUG, + "rescheduling DPD monitoring (for ALGORITHM_BLACKHOLE_DETECT) i = %d, peer %d.\n", + iph1->parent_session->i_sent_data_sc_dpd, + iph1->parent_session->peer_sent_data_sc_dpd); + } + iph1->parent_session->i_sent_data_sc_dpd = 0; + iph1->parent_session->peer_sent_data_sc_dpd = 0; +} + +/* + * monitor DPD Informational exchange. + */ +static void +isakmp_info_monitor_r_u(void *arg) +{ + phase1_handle_t *iph1 = arg; + + if (iph1 && iph1->rmconf) { + if (iph1->rmconf->dpd_algo == DPD_ALGO_INBOUND_DETECT) { + isakmp_info_monitor_r_u_algo_inbound_detect(iph1); + } else if (iph1->rmconf->dpd_algo == DPD_ALGO_BLACKHOLE_DETECT) { + isakmp_info_monitor_r_u_algo_blackhole_detect(iph1); + } else { + plog(ASL_LEVEL_DEBUG, "DPD monitoring aborted, invalid algorithm %d....\n", + iph1->rmconf->dpd_algo); + } + } +} + /* Schedule a new R-U-THERE */ int -isakmp_sched_r_u(iph1, retry) - struct ph1handle *iph1; - int retry; +isakmp_sched_r_u(phase1_handle_t *iph1, int retry) { if(iph1 == NULL || iph1->rmconf == NULL) @@ -1787,13 +2222,48 @@ isakmp_sched_r_u(iph1, retry) iph1->rmconf->dpd_interval == 0) return 0; - if(retry) + if(retry) { iph1->dpd_r_u = sched_new(iph1->rmconf->dpd_retry, - isakmp_info_send_r_u, iph1); - else - sched_new(iph1->rmconf->dpd_interval, - isakmp_info_send_r_u, iph1); + isakmp_info_send_r_u, iph1); + } else { + if (iph1->rmconf->dpd_algo == DPD_ALGO_INBOUND_DETECT || + iph1->rmconf->dpd_algo == DPD_ALGO_BLACKHOLE_DETECT) { + iph1->dpd_r_u = sched_new(iph1->rmconf->dpd_interval, + isakmp_info_monitor_r_u, iph1); + } else { + iph1->dpd_r_u = sched_new(iph1->rmconf->dpd_interval, + isakmp_info_send_r_u, iph1); + } + } return 0; } + +/* + * punts dpd for later because of some activity that: + * 1) implicitly does dpd (e.g. phase2 exchanges), or + * 2) indicates liveness (e.g. received ike packets). + */ +void +isakmp_reschedule_info_monitor_if_pending (phase1_handle_t *iph1, char *reason) +{ + if (!iph1 || + !FSM_STATE_IS_ESTABLISHED(iph1->status) || + !iph1->dpd_support || + !iph1->rmconf->dpd_interval || + iph1->rmconf->dpd_algo == DPD_ALGO_DEFAULT) { + return; + } + + if (!iph1->peer_sent_ike) { + SCHED_KILL(iph1->dpd_r_u); + + isakmp_sched_r_u(iph1, 0); + + plog(ASL_LEVEL_DEBUG, + "%s... rescheduling send_r_u.\n", + reason); + } + iph1->peer_sent_ike++; +} #endif