summaryrefslogtreecommitdiff
path: root/src/pluto/demux.c
diff options
context:
space:
mode:
authorRene Mayrhofer <rene@mayrhofer.eu.org>2009-06-23 11:25:24 +0000
committerRene Mayrhofer <rene@mayrhofer.eu.org>2009-06-23 11:25:24 +0000
commit41787e147279ff0695e9d759487266a60b80867b (patch)
tree8f28566c8fd7106c80d2536d2df540dbb4499cc5 /src/pluto/demux.c
parentc3e7f611ea8273c6b3909cb006ade4903a74aad0 (diff)
downloadvyos-strongswan-41787e147279ff0695e9d759487266a60b80867b.tar.gz
vyos-strongswan-41787e147279ff0695e9d759487266a60b80867b.zip
[svn-upgrade] Integrating new upstream version, strongswan (4.3.2)
Diffstat (limited to 'src/pluto/demux.c')
-rw-r--r--src/pluto/demux.c3950
1 files changed, 1986 insertions, 1964 deletions
diff --git a/src/pluto/demux.c b/src/pluto/demux.c
index 04728a4a8..3cfc909af 100644
--- a/src/pluto/demux.c
+++ b/src/pluto/demux.c
@@ -11,8 +11,6 @@
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
- *
- * RCSID $Id: demux.c 3686 2008-03-28 11:48:14Z martin $
*/
/* Ordering Constraints on Payloads
@@ -110,8 +108,8 @@
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
-#include <sys/time.h> /* only used for belt-and-suspenders select call */
-#include <sys/poll.h> /* only used for forensic poll call */
+#include <sys/time.h> /* only used for belt-and-suspenders select call */
+#include <sys/poll.h> /* only used for forensic poll call */
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
@@ -119,9 +117,9 @@
#include <sys/queue.h>
#if defined(IP_RECVERR) && defined(MSG_ERRQUEUE)
-# include <asm/types.h> /* for __u8, __u32 */
+# include <asm/types.h> /* for __u8, __u32 */
# include <linux/errqueue.h>
-# include <sys/uio.h> /* struct iovec */
+# include <sys/uio.h> /* struct iovec */
#endif
#include <freeswan.h>
@@ -132,15 +130,13 @@
#include "connections.h"
#include "state.h"
#include "packet.h"
-#include "md5.h"
-#include "sha1.h"
-#include "crypto.h" /* requires sha1.h and md5.h */
+#include "crypto.h"
#include "ike_alg.h"
#include "log.h"
-#include "demux.h" /* needs packet.h */
-#include "ipsec_doi.h" /* needs demux.h and state.h */
+#include "demux.h" /* needs packet.h */
+#include "ipsec_doi.h" /* needs demux.h and state.h */
#include "timer.h"
-#include "whack.h" /* requires connections.h */
+#include "whack.h" /* requires connections.h */
#include "server.h"
#include "nat_traversal.h"
#include "vendor.h"
@@ -173,14 +169,14 @@ u_int8_t reply_buffer[MAX_OUTPUT_UDP_SIZE];
*/
struct state_microcode {
- enum state_kind state, next_state;
- lset_t flags;
- lset_t req_payloads; /* required payloads (allows just one) */
- lset_t opt_payloads; /* optional payloads (any mumber) */
- /* if not ISAKMP_NEXT_NONE, process_packet will emit HDR with this as np */
- u_int8_t first_out_payload;
- enum event_type timeout_event;
- state_transition_fn *processor;
+ enum state_kind state, next_state;
+ lset_t flags;
+ lset_t req_payloads; /* required payloads (allows just one) */
+ lset_t opt_payloads; /* optional payloads (any mumber) */
+ /* if not ISAKMP_NEXT_NONE, process_packet will emit HDR with this as np */
+ u_int8_t first_out_payload;
+ enum event_type timeout_event;
+ state_transition_fn *processor;
};
/* State Microcode Flags, in several groups */
@@ -190,19 +186,21 @@ struct state_microcode {
* Note: SMF_ALL_AUTH matches 0 for those circumstances when no auth
* has been set.
*/
-#define SMF_ALL_AUTH LRANGE(0, OAKLEY_AUTH_ROOF-1)
-#define SMF_PSK_AUTH LELEM(OAKLEY_PRESHARED_KEY)
-#define SMF_DS_AUTH (LELEM(OAKLEY_DSS_SIG) | LELEM(OAKLEY_RSA_SIG))
-#define SMF_PKE_AUTH (LELEM(OAKLEY_RSA_ENC) | LELEM(OAKLEY_ELGAMAL_ENC))
-#define SMF_RPKE_AUTH (LELEM(OAKLEY_RSA_ENC_REV) | LELEM(OAKLEY_ELGAMAL_ENC_REV))
+#define SMF_ALL_AUTH LRANGE(0, OAKLEY_AUTH_ROOF-1)
+#define SMF_PSK_AUTH LELEM(OAKLEY_PRESHARED_KEY)
+#define SMF_DS_AUTH (LELEM(OAKLEY_DSS_SIG) | LELEM(OAKLEY_RSA_SIG) | \
+ LELEM(OAKLEY_ECDSA_SIG) | LELEM(OAKLEY_ECDSA_256) | \
+ LELEM(OAKLEY_ECDSA_384) | LELEM(OAKLEY_ECDSA_521))
+#define SMF_PKE_AUTH (LELEM(OAKLEY_RSA_ENC) | LELEM(OAKLEY_ELGAMAL_ENC))
+#define SMF_RPKE_AUTH (LELEM(OAKLEY_RSA_ENC_REV) | LELEM(OAKLEY_ELGAMAL_ENC_REV))
/* misc flags */
-#define SMF_INITIATOR LELEM(OAKLEY_AUTH_ROOF + 0)
-#define SMF_FIRST_ENCRYPTED_INPUT LELEM(OAKLEY_AUTH_ROOF + 1)
-#define SMF_INPUT_ENCRYPTED LELEM(OAKLEY_AUTH_ROOF + 2)
-#define SMF_OUTPUT_ENCRYPTED LELEM(OAKLEY_AUTH_ROOF + 3)
-#define SMF_RETRANSMIT_ON_DUPLICATE LELEM(OAKLEY_AUTH_ROOF + 4)
+#define SMF_INITIATOR LELEM(OAKLEY_AUTH_ROOF + 0)
+#define SMF_FIRST_ENCRYPTED_INPUT LELEM(OAKLEY_AUTH_ROOF + 1)
+#define SMF_INPUT_ENCRYPTED LELEM(OAKLEY_AUTH_ROOF + 2)
+#define SMF_OUTPUT_ENCRYPTED LELEM(OAKLEY_AUTH_ROOF + 3)
+#define SMF_RETRANSMIT_ON_DUPLICATE LELEM(OAKLEY_AUTH_ROOF + 4)
#define SMF_ENCRYPTED (SMF_INPUT_ENCRYPTED | SMF_OUTPUT_ENCRYPTED)
@@ -210,14 +208,14 @@ struct state_microcode {
#define SMF_REPLY LELEM(OAKLEY_AUTH_ROOF + 5)
/* this state completes P1, so any pending P2 negotiations should start */
-#define SMF_RELEASE_PENDING_P2 LELEM(OAKLEY_AUTH_ROOF + 6)
+#define SMF_RELEASE_PENDING_P2 LELEM(OAKLEY_AUTH_ROOF + 6)
/* end of flags */
-static state_transition_fn /* forward declaration */
- unexpected,
- informational;
+static state_transition_fn /* forward declaration */
+ unexpected,
+ informational;
/* state_microcode_table is a table of all state_microcode tuples.
* It must be in order of state (the first element).
@@ -228,288 +226,288 @@ static state_transition_fn /* forward declaration */
*/
static const struct state_microcode
- *ike_microcode_index[STATE_IKE_ROOF - STATE_IKE_FLOOR];
+ *ike_microcode_index[STATE_IKE_ROOF - STATE_IKE_FLOOR];
static const struct state_microcode state_microcode_table[] = {
#define PT(n) ISAKMP_NEXT_##n
#define P(n) LELEM(PT(n))
- /***** Phase 1 Main Mode *****/
-
- /* No state for main_outI1: --> HDR, SA */
-
- /* STATE_MAIN_R0: I1 --> R1
- * HDR, SA --> HDR, SA
- */
- { STATE_MAIN_R0, STATE_MAIN_R1
- , SMF_ALL_AUTH | SMF_REPLY
- , P(SA), P(VID) | P(CR), PT(NONE)
- , EVENT_RETRANSMIT, main_inI1_outR1},
-
- /* STATE_MAIN_I1: R1 --> I2
- * HDR, SA --> auth dependent
- * SMF_PSK_AUTH, SMF_DS_AUTH: --> HDR, KE, Ni
- * SMF_PKE_AUTH:
- * --> HDR, KE, [ HASH(1), ] <IDi1_b>PubKey_r, <Ni_b>PubKey_r
- * SMF_RPKE_AUTH:
- * --> HDR, [ HASH(1), ] <Ni_b>Pubkey_r, <KE_b>Ke_i, <IDi1_b>Ke_i [,<<Cert-I_b>Ke_i]
- * Note: since we don't know auth at start, we cannot differentiate
- * microcode entries based on it.
- */
- { STATE_MAIN_I1, STATE_MAIN_I2
- , SMF_ALL_AUTH | SMF_INITIATOR | SMF_REPLY
- , P(SA), P(VID) | P(CR), PT(NONE) /* don't know yet */
- , EVENT_RETRANSMIT, main_inR1_outI2 },
-
- /* STATE_MAIN_R1: I2 --> R2
- * SMF_PSK_AUTH, SMF_DS_AUTH: HDR, KE, Ni --> HDR, KE, Nr
- * SMF_PKE_AUTH: HDR, KE, [ HASH(1), ] <IDi1_b>PubKey_r, <Ni_b>PubKey_r
- * --> HDR, KE, <IDr1_b>PubKey_i, <Nr_b>PubKey_i
- * SMF_RPKE_AUTH:
- * HDR, [ HASH(1), ] <Ni_b>Pubkey_r, <KE_b>Ke_i, <IDi1_b>Ke_i [,<<Cert-I_b>Ke_i]
- * --> HDR, <Nr_b>PubKey_i, <KE_b>Ke_r, <IDr1_b>Ke_r
- */
- { STATE_MAIN_R1, STATE_MAIN_R2
- , SMF_PSK_AUTH | SMF_DS_AUTH | SMF_REPLY
- , P(KE) | P(NONCE), P(VID) | P(CR) | P(NATD_RFC), PT(KE)
- , EVENT_RETRANSMIT, main_inI2_outR2 },
-
- { STATE_MAIN_R1, STATE_UNDEFINED
- , SMF_PKE_AUTH | SMF_REPLY
- , P(KE) | P(ID) | P(NONCE), P(VID) | P(CR) | P(HASH), PT(KE)
- , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ },
-
- { STATE_MAIN_R1, STATE_UNDEFINED
- , SMF_RPKE_AUTH | SMF_REPLY
- , P(NONCE) | P(KE) | P(ID), P(VID) | P(CR) | P(HASH) | P(CERT), PT(NONCE)
- , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ },
-
- /* for states from here on, output message must be encrypted */
-
- /* STATE_MAIN_I2: R2 --> I3
- * SMF_PSK_AUTH: HDR, KE, Nr --> HDR*, IDi1, HASH_I
- * SMF_DS_AUTH: HDR, KE, Nr --> HDR*, IDi1, [ CERT, ] SIG_I
- * SMF_PKE_AUTH: HDR, KE, <IDr1_b>PubKey_i, <Nr_b>PubKey_i
- * --> HDR*, HASH_I
- * SMF_RPKE_AUTH: HDR, <Nr_b>PubKey_i, <KE_b>Ke_r, <IDr1_b>Ke_r
- * --> HDR*, HASH_I
- */
- { STATE_MAIN_I2, STATE_MAIN_I3
- , SMF_PSK_AUTH | SMF_DS_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED | SMF_REPLY
- , P(KE) | P(NONCE), P(VID) | P(CR) | P(NATD_RFC), PT(ID)
- , EVENT_RETRANSMIT, main_inR2_outI3 },
-
- { STATE_MAIN_I2, STATE_UNDEFINED
- , SMF_PKE_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED | SMF_REPLY
- , P(KE) | P(ID) | P(NONCE), P(VID) | P(CR), PT(HASH)
- , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ },
-
- { STATE_MAIN_I2, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED | SMF_REPLY
- , P(NONCE) | P(KE) | P(ID), P(VID) | P(CR), PT(HASH)
- , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ },
-
- /* for states from here on, input message must be encrypted */
-
- /* STATE_MAIN_R2: I3 --> R3
- * SMF_PSK_AUTH: HDR*, IDi1, HASH_I --> HDR*, IDr1, HASH_R
- * SMF_DS_AUTH: HDR*, IDi1, [ CERT, ] SIG_I --> HDR*, IDr1, [ CERT, ] SIG_R
- * SMF_PKE_AUTH, SMF_RPKE_AUTH: HDR*, HASH_I --> HDR*, HASH_R
- */
- { STATE_MAIN_R2, STATE_MAIN_R3
- , SMF_PSK_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED
- | SMF_REPLY | SMF_RELEASE_PENDING_P2
- , P(ID) | P(HASH), P(VID) | P(CR), PT(NONE)
- , EVENT_SA_REPLACE, main_inI3_outR3 },
-
- { STATE_MAIN_R2, STATE_MAIN_R3
- , SMF_DS_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED
- | SMF_REPLY | SMF_RELEASE_PENDING_P2
- , P(ID) | P(SIG), P(VID) | P(CR) | P(CERT), PT(NONE)
- , EVENT_SA_REPLACE, main_inI3_outR3 },
-
- { STATE_MAIN_R2, STATE_UNDEFINED
- , SMF_PKE_AUTH | SMF_RPKE_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED
- | SMF_REPLY | SMF_RELEASE_PENDING_P2
- , P(HASH), P(VID) | P(CR), PT(NONE)
- , EVENT_SA_REPLACE, unexpected /* ??? not yet implemented */ },
-
- /* STATE_MAIN_I3: R3 --> done
- * SMF_PSK_AUTH: HDR*, IDr1, HASH_R --> done
- * SMF_DS_AUTH: HDR*, IDr1, [ CERT, ] SIG_R --> done
- * SMF_PKE_AUTH, SMF_RPKE_AUTH: HDR*, HASH_R --> done
- * May initiate quick mode by calling quick_outI1
- */
- { STATE_MAIN_I3, STATE_MAIN_I4
- , SMF_PSK_AUTH | SMF_INITIATOR
- | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
- , P(ID) | P(HASH), P(VID) | P(CR), PT(NONE)
- , EVENT_SA_REPLACE, main_inR3 },
-
- { STATE_MAIN_I3, STATE_MAIN_I4
- , SMF_DS_AUTH | SMF_INITIATOR
- | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
- , P(ID) | P(SIG), P(VID) | P(CR) | P(CERT), PT(NONE)
- , EVENT_SA_REPLACE, main_inR3 },
-
- { STATE_MAIN_I3, STATE_UNDEFINED
- , SMF_PKE_AUTH | SMF_RPKE_AUTH | SMF_INITIATOR
- | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
- , P(HASH), P(VID) | P(CR), PT(NONE)
- , EVENT_SA_REPLACE, unexpected /* ??? not yet implemented */ },
-
- /* STATE_MAIN_R3: can only get here due to packet loss */
- { STATE_MAIN_R3, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RETRANSMIT_ON_DUPLICATE
- , LEMPTY, LEMPTY
- , PT(NONE), EVENT_NULL, unexpected },
-
- /* STATE_MAIN_I4: can only get here due to packet loss */
- { STATE_MAIN_I4, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_INITIATOR | SMF_ENCRYPTED
- , LEMPTY, LEMPTY
- , PT(NONE), EVENT_NULL, unexpected },
-
-
- /***** Phase 2 Quick Mode *****/
-
- /* No state for quick_outI1:
- * --> HDR*, HASH(1), SA, Nr [, KE ] [, IDci, IDcr ]
- */
-
- /* STATE_QUICK_R0:
- * HDR*, HASH(1), SA, Ni [, KE ] [, IDci, IDcr ] -->
- * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ]
- * Installs inbound IPsec SAs.
- * Because it may suspend for asynchronous DNS, first_out_payload
- * is set to NONE to suppress early emission of HDR*.
- * ??? it is legal to have multiple SAs, but we don't support it yet.
- */
- { STATE_QUICK_R0, STATE_QUICK_R1
- , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY
- , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID) | P(NATOA_RFC), PT(NONE)
- , EVENT_RETRANSMIT, quick_inI1_outR1 },
-
- /* STATE_QUICK_I1:
- * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ] -->
- * HDR*, HASH(3)
- * Installs inbound and outbound IPsec SAs, routing, etc.
- * ??? it is legal to have multiple SAs, but we don't support it yet.
- */
- { STATE_QUICK_I1, STATE_QUICK_I2
- , SMF_ALL_AUTH | SMF_INITIATOR | SMF_ENCRYPTED | SMF_REPLY
- , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID) | P(NATOA_RFC), PT(HASH)
- , EVENT_SA_REPLACE, quick_inR1_outI2 },
-
- /* STATE_QUICK_R1: HDR*, HASH(3) --> done
- * Installs outbound IPsec SAs, routing, etc.
- */
- { STATE_QUICK_R1, STATE_QUICK_R2
- , SMF_ALL_AUTH | SMF_ENCRYPTED
- , P(HASH), LEMPTY, PT(NONE)
- , EVENT_SA_REPLACE, quick_inI2 },
-
- /* STATE_QUICK_I2: can only happen due to lost packet */
- { STATE_QUICK_I2, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_INITIATOR | SMF_ENCRYPTED | SMF_RETRANSMIT_ON_DUPLICATE
- , LEMPTY, LEMPTY, PT(NONE)
- , EVENT_NULL, unexpected },
-
- /* STATE_QUICK_R2: can only happen due to lost packet */
- { STATE_QUICK_R2, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_ENCRYPTED
- , LEMPTY, LEMPTY, PT(NONE)
- , EVENT_NULL, unexpected },
-
-
- /***** informational messages *****/
-
- /* STATE_INFO: */
- { STATE_INFO, STATE_UNDEFINED
- , SMF_ALL_AUTH
- , LEMPTY, LEMPTY, PT(NONE)
- , EVENT_NULL, informational },
-
- /* STATE_INFO_PROTECTED: */
- { STATE_INFO_PROTECTED, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_ENCRYPTED
- , P(HASH), LEMPTY, PT(NONE)
- , EVENT_NULL, informational },
-
- /* XAUTH state transitions */
- { STATE_XAUTH_I0, STATE_XAUTH_I1
- , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY
- , P(ATTR) | P(HASH), P(VID), PT(HASH)
- , EVENT_RETRANSMIT, xauth_inI0 },
-
- { STATE_XAUTH_R1, STATE_XAUTH_R2
- , SMF_ALL_AUTH | SMF_ENCRYPTED
- , P(ATTR) | P(HASH), P(VID), PT(HASH)
- , EVENT_RETRANSMIT, xauth_inR1 },
-
- { STATE_XAUTH_I1, STATE_XAUTH_I2
- , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2
- , P(ATTR) | P(HASH), P(VID), PT(HASH)
- , EVENT_SA_REPLACE, xauth_inI1 },
-
- { STATE_XAUTH_R2, STATE_XAUTH_R3
- , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
- , P(ATTR) | P(HASH), P(VID), PT(NONE)
- , EVENT_SA_REPLACE, xauth_inR2 },
-
- { STATE_XAUTH_I2, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_ENCRYPTED
- , LEMPTY, LEMPTY, PT(NONE)
- , EVENT_NULL, unexpected },
-
- { STATE_XAUTH_R3, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_ENCRYPTED
- , LEMPTY, LEMPTY, PT(NONE)
- , EVENT_NULL, unexpected },
-
- /* ModeCfg pull mode state transitions */
-
- { STATE_MODE_CFG_R0, STATE_MODE_CFG_R1
- , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2
- , P(ATTR) | P(HASH), P(VID), PT(HASH)
- , EVENT_SA_REPLACE, modecfg_inR0 },
-
- { STATE_MODE_CFG_I1, STATE_MODE_CFG_I2
- , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
- , P(ATTR) | P(HASH), P(VID), PT(HASH)
- , EVENT_SA_REPLACE, modecfg_inI1 },
-
- { STATE_MODE_CFG_R1, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_ENCRYPTED
- , LEMPTY, LEMPTY, PT(NONE)
- , EVENT_NULL, unexpected },
-
- { STATE_MODE_CFG_I2, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_ENCRYPTED
- , LEMPTY, LEMPTY, PT(NONE)
- , EVENT_NULL, unexpected },
+ /***** Phase 1 Main Mode *****/
+
+ /* No state for main_outI1: --> HDR, SA */
+
+ /* STATE_MAIN_R0: I1 --> R1
+ * HDR, SA --> HDR, SA
+ */
+ { STATE_MAIN_R0, STATE_MAIN_R1
+ , SMF_ALL_AUTH | SMF_REPLY
+ , P(SA), P(VID) | P(CR), PT(NONE)
+ , EVENT_RETRANSMIT, main_inI1_outR1},
+
+ /* STATE_MAIN_I1: R1 --> I2
+ * HDR, SA --> auth dependent
+ * SMF_PSK_AUTH, SMF_DS_AUTH: --> HDR, KE, Ni
+ * SMF_PKE_AUTH:
+ * --> HDR, KE, [ HASH(1), ] <IDi1_b>PubKey_r, <Ni_b>PubKey_r
+ * SMF_RPKE_AUTH:
+ * --> HDR, [ HASH(1), ] <Ni_b>Pubkey_r, <KE_b>Ke_i, <IDi1_b>Ke_i [,<<Cert-I_b>Ke_i]
+ * Note: since we don't know auth at start, we cannot differentiate
+ * microcode entries based on it.
+ */
+ { STATE_MAIN_I1, STATE_MAIN_I2
+ , SMF_ALL_AUTH | SMF_INITIATOR | SMF_REPLY
+ , P(SA), P(VID) | P(CR), PT(NONE) /* don't know yet */
+ , EVENT_RETRANSMIT, main_inR1_outI2 },
+
+ /* STATE_MAIN_R1: I2 --> R2
+ * SMF_PSK_AUTH, SMF_DS_AUTH: HDR, KE, Ni --> HDR, KE, Nr
+ * SMF_PKE_AUTH: HDR, KE, [ HASH(1), ] <IDi1_b>PubKey_r, <Ni_b>PubKey_r
+ * --> HDR, KE, <IDr1_b>PubKey_i, <Nr_b>PubKey_i
+ * SMF_RPKE_AUTH:
+ * HDR, [ HASH(1), ] <Ni_b>Pubkey_r, <KE_b>Ke_i, <IDi1_b>Ke_i [,<<Cert-I_b>Ke_i]
+ * --> HDR, <Nr_b>PubKey_i, <KE_b>Ke_r, <IDr1_b>Ke_r
+ */
+ { STATE_MAIN_R1, STATE_MAIN_R2
+ , SMF_PSK_AUTH | SMF_DS_AUTH | SMF_REPLY
+ , P(KE) | P(NONCE), P(VID) | P(CR) | P(NATD_RFC), PT(KE)
+ , EVENT_RETRANSMIT, main_inI2_outR2 },
+
+ { STATE_MAIN_R1, STATE_UNDEFINED
+ , SMF_PKE_AUTH | SMF_REPLY
+ , P(KE) | P(ID) | P(NONCE), P(VID) | P(CR) | P(HASH), PT(KE)
+ , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ },
+
+ { STATE_MAIN_R1, STATE_UNDEFINED
+ , SMF_RPKE_AUTH | SMF_REPLY
+ , P(NONCE) | P(KE) | P(ID), P(VID) | P(CR) | P(HASH) | P(CERT), PT(NONCE)
+ , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ },
+
+ /* for states from here on, output message must be encrypted */
+
+ /* STATE_MAIN_I2: R2 --> I3
+ * SMF_PSK_AUTH: HDR, KE, Nr --> HDR*, IDi1, HASH_I
+ * SMF_DS_AUTH: HDR, KE, Nr --> HDR*, IDi1, [ CERT, ] SIG_I
+ * SMF_PKE_AUTH: HDR, KE, <IDr1_b>PubKey_i, <Nr_b>PubKey_i
+ * --> HDR*, HASH_I
+ * SMF_RPKE_AUTH: HDR, <Nr_b>PubKey_i, <KE_b>Ke_r, <IDr1_b>Ke_r
+ * --> HDR*, HASH_I
+ */
+ { STATE_MAIN_I2, STATE_MAIN_I3
+ , SMF_PSK_AUTH | SMF_DS_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED | SMF_REPLY
+ , P(KE) | P(NONCE), P(VID) | P(CR) | P(NATD_RFC), PT(ID)
+ , EVENT_RETRANSMIT, main_inR2_outI3 },
+
+ { STATE_MAIN_I2, STATE_UNDEFINED
+ , SMF_PKE_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED | SMF_REPLY
+ , P(KE) | P(ID) | P(NONCE), P(VID) | P(CR), PT(HASH)
+ , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ },
+
+ { STATE_MAIN_I2, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED | SMF_REPLY
+ , P(NONCE) | P(KE) | P(ID), P(VID) | P(CR), PT(HASH)
+ , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ },
+
+ /* for states from here on, input message must be encrypted */
+
+ /* STATE_MAIN_R2: I3 --> R3
+ * SMF_PSK_AUTH: HDR*, IDi1, HASH_I --> HDR*, IDr1, HASH_R
+ * SMF_DS_AUTH: HDR*, IDi1, [ CERT, ] SIG_I --> HDR*, IDr1, [ CERT, ] SIG_R
+ * SMF_PKE_AUTH, SMF_RPKE_AUTH: HDR*, HASH_I --> HDR*, HASH_R
+ */
+ { STATE_MAIN_R2, STATE_MAIN_R3
+ , SMF_PSK_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED
+ | SMF_REPLY | SMF_RELEASE_PENDING_P2
+ , P(ID) | P(HASH), P(VID) | P(CR), PT(NONE)
+ , EVENT_SA_REPLACE, main_inI3_outR3 },
+
+ { STATE_MAIN_R2, STATE_MAIN_R3
+ , SMF_DS_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED
+ | SMF_REPLY | SMF_RELEASE_PENDING_P2
+ , P(ID) | P(SIG), P(VID) | P(CR) | P(CERT), PT(NONE)
+ , EVENT_SA_REPLACE, main_inI3_outR3 },
+
+ { STATE_MAIN_R2, STATE_UNDEFINED
+ , SMF_PKE_AUTH | SMF_RPKE_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED
+ | SMF_REPLY | SMF_RELEASE_PENDING_P2
+ , P(HASH), P(VID) | P(CR), PT(NONE)
+ , EVENT_SA_REPLACE, unexpected /* ??? not yet implemented */ },
+
+ /* STATE_MAIN_I3: R3 --> done
+ * SMF_PSK_AUTH: HDR*, IDr1, HASH_R --> done
+ * SMF_DS_AUTH: HDR*, IDr1, [ CERT, ] SIG_R --> done
+ * SMF_PKE_AUTH, SMF_RPKE_AUTH: HDR*, HASH_R --> done
+ * May initiate quick mode by calling quick_outI1
+ */
+ { STATE_MAIN_I3, STATE_MAIN_I4
+ , SMF_PSK_AUTH | SMF_INITIATOR
+ | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
+ , P(ID) | P(HASH), P(VID) | P(CR), PT(NONE)
+ , EVENT_SA_REPLACE, main_inR3 },
+
+ { STATE_MAIN_I3, STATE_MAIN_I4
+ , SMF_DS_AUTH | SMF_INITIATOR
+ | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
+ , P(ID) | P(SIG), P(VID) | P(CR) | P(CERT), PT(NONE)
+ , EVENT_SA_REPLACE, main_inR3 },
+
+ { STATE_MAIN_I3, STATE_UNDEFINED
+ , SMF_PKE_AUTH | SMF_RPKE_AUTH | SMF_INITIATOR
+ | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
+ , P(HASH), P(VID) | P(CR), PT(NONE)
+ , EVENT_SA_REPLACE, unexpected /* ??? not yet implemented */ },
+
+ /* STATE_MAIN_R3: can only get here due to packet loss */
+ { STATE_MAIN_R3, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RETRANSMIT_ON_DUPLICATE
+ , LEMPTY, LEMPTY
+ , PT(NONE), EVENT_NULL, unexpected },
+
+ /* STATE_MAIN_I4: can only get here due to packet loss */
+ { STATE_MAIN_I4, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_INITIATOR | SMF_ENCRYPTED
+ , LEMPTY, LEMPTY
+ , PT(NONE), EVENT_NULL, unexpected },
+
+
+ /***** Phase 2 Quick Mode *****/
+
+ /* No state for quick_outI1:
+ * --> HDR*, HASH(1), SA, Nr [, KE ] [, IDci, IDcr ]
+ */
+
+ /* STATE_QUICK_R0:
+ * HDR*, HASH(1), SA, Ni [, KE ] [, IDci, IDcr ] -->
+ * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ]
+ * Installs inbound IPsec SAs.
+ * Because it may suspend for asynchronous DNS, first_out_payload
+ * is set to NONE to suppress early emission of HDR*.
+ * ??? it is legal to have multiple SAs, but we don't support it yet.
+ */
+ { STATE_QUICK_R0, STATE_QUICK_R1
+ , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY
+ , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID) | P(NATOA_RFC), PT(NONE)
+ , EVENT_RETRANSMIT, quick_inI1_outR1 },
+
+ /* STATE_QUICK_I1:
+ * HDR*, HASH(2), SA, Nr [, KE ] [, IDci, IDcr ] -->
+ * HDR*, HASH(3)
+ * Installs inbound and outbound IPsec SAs, routing, etc.
+ * ??? it is legal to have multiple SAs, but we don't support it yet.
+ */
+ { STATE_QUICK_I1, STATE_QUICK_I2
+ , SMF_ALL_AUTH | SMF_INITIATOR | SMF_ENCRYPTED | SMF_REPLY
+ , P(HASH) | P(SA) | P(NONCE), /* P(SA) | */ P(KE) | P(ID) | P(NATOA_RFC), PT(HASH)
+ , EVENT_SA_REPLACE, quick_inR1_outI2 },
+
+ /* STATE_QUICK_R1: HDR*, HASH(3) --> done
+ * Installs outbound IPsec SAs, routing, etc.
+ */
+ { STATE_QUICK_R1, STATE_QUICK_R2
+ , SMF_ALL_AUTH | SMF_ENCRYPTED
+ , P(HASH), LEMPTY, PT(NONE)
+ , EVENT_SA_REPLACE, quick_inI2 },
+
+ /* STATE_QUICK_I2: can only happen due to lost packet */
+ { STATE_QUICK_I2, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_INITIATOR | SMF_ENCRYPTED | SMF_RETRANSMIT_ON_DUPLICATE
+ , LEMPTY, LEMPTY, PT(NONE)
+ , EVENT_NULL, unexpected },
+
+ /* STATE_QUICK_R2: can only happen due to lost packet */
+ { STATE_QUICK_R2, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_ENCRYPTED
+ , LEMPTY, LEMPTY, PT(NONE)
+ , EVENT_NULL, unexpected },
+
+
+ /***** informational messages *****/
+
+ /* STATE_INFO: */
+ { STATE_INFO, STATE_UNDEFINED
+ , SMF_ALL_AUTH
+ , LEMPTY, LEMPTY, PT(NONE)
+ , EVENT_NULL, informational },
+
+ /* STATE_INFO_PROTECTED: */
+ { STATE_INFO_PROTECTED, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_ENCRYPTED
+ , P(HASH), LEMPTY, PT(NONE)
+ , EVENT_NULL, informational },
+
+ /* XAUTH state transitions */
+ { STATE_XAUTH_I0, STATE_XAUTH_I1
+ , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY
+ , P(ATTR) | P(HASH), P(VID), PT(HASH)
+ , EVENT_RETRANSMIT, xauth_inI0 },
+
+ { STATE_XAUTH_R1, STATE_XAUTH_R2
+ , SMF_ALL_AUTH | SMF_ENCRYPTED
+ , P(ATTR) | P(HASH), P(VID), PT(HASH)
+ , EVENT_RETRANSMIT, xauth_inR1 },
+
+ { STATE_XAUTH_I1, STATE_XAUTH_I2
+ , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2
+ , P(ATTR) | P(HASH), P(VID), PT(HASH)
+ , EVENT_SA_REPLACE, xauth_inI1 },
+
+ { STATE_XAUTH_R2, STATE_XAUTH_R3
+ , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
+ , P(ATTR) | P(HASH), P(VID), PT(NONE)
+ , EVENT_SA_REPLACE, xauth_inR2 },
+
+ { STATE_XAUTH_I2, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_ENCRYPTED
+ , LEMPTY, LEMPTY, PT(NONE)
+ , EVENT_NULL, unexpected },
+
+ { STATE_XAUTH_R3, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_ENCRYPTED
+ , LEMPTY, LEMPTY, PT(NONE)
+ , EVENT_NULL, unexpected },
+
+ /* ModeCfg pull mode state transitions */
+
+ { STATE_MODE_CFG_R0, STATE_MODE_CFG_R1
+ , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2
+ , P(ATTR) | P(HASH), P(VID), PT(HASH)
+ , EVENT_SA_REPLACE, modecfg_inR0 },
+
+ { STATE_MODE_CFG_I1, STATE_MODE_CFG_I2
+ , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
+ , P(ATTR) | P(HASH), P(VID), PT(HASH)
+ , EVENT_SA_REPLACE, modecfg_inI1 },
+
+ { STATE_MODE_CFG_R1, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_ENCRYPTED
+ , LEMPTY, LEMPTY, PT(NONE)
+ , EVENT_NULL, unexpected },
+
+ { STATE_MODE_CFG_I2, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_ENCRYPTED
+ , LEMPTY, LEMPTY, PT(NONE)
+ , EVENT_NULL, unexpected },
/* ModeCfg push mode state transitions */
- { STATE_MODE_CFG_I0, STATE_MODE_CFG_I3
- , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2
- , P(ATTR) | P(HASH), P(VID), PT(HASH)
- , EVENT_SA_REPLACE, modecfg_inI0 },
+ { STATE_MODE_CFG_I0, STATE_MODE_CFG_I3
+ , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2
+ , P(ATTR) | P(HASH), P(VID), PT(HASH)
+ , EVENT_SA_REPLACE, modecfg_inI0 },
- { STATE_MODE_CFG_R3, STATE_MODE_CFG_R4
- , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
- , P(ATTR) | P(HASH), P(VID), PT(HASH)
- , EVENT_SA_REPLACE, modecfg_inR3 },
+ { STATE_MODE_CFG_R3, STATE_MODE_CFG_R4
+ , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2
+ , P(ATTR) | P(HASH), P(VID), PT(HASH)
+ , EVENT_SA_REPLACE, modecfg_inR3 },
- { STATE_MODE_CFG_I3, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_ENCRYPTED
- , LEMPTY, LEMPTY, PT(NONE)
- , EVENT_NULL, unexpected },
+ { STATE_MODE_CFG_I3, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_ENCRYPTED
+ , LEMPTY, LEMPTY, PT(NONE)
+ , EVENT_NULL, unexpected },
- { STATE_MODE_CFG_R4, STATE_UNDEFINED
- , SMF_ALL_AUTH | SMF_ENCRYPTED
- , LEMPTY, LEMPTY, PT(NONE)
- , EVENT_NULL, unexpected },
+ { STATE_MODE_CFG_R4, STATE_UNDEFINED
+ , SMF_ALL_AUTH | SMF_ENCRYPTED
+ , LEMPTY, LEMPTY, PT(NONE)
+ , EVENT_NULL, unexpected },
#undef P
#undef PT
@@ -518,23 +516,23 @@ static const struct state_microcode state_microcode_table[] = {
void
init_demux(void)
{
- /* fill ike_microcode_index:
- * make ike_microcode_index[s] point to first entry in
- * state_microcode_table for state s (backward scan makes this easier).
- * Check that table is in order -- catch coding errors.
- * For what it's worth, this routine is idempotent.
- */
- const struct state_microcode *t;
-
- for (t = &state_microcode_table[elemsof(state_microcode_table) - 1];;)
- {
- passert(STATE_IKE_FLOOR <= t->state && t->state < STATE_IKE_ROOF);
- ike_microcode_index[t->state - STATE_IKE_FLOOR] = t;
- if (t == state_microcode_table)
- break;
- t--;
- passert(t[0].state <= t[1].state);
- }
+ /* fill ike_microcode_index:
+ * make ike_microcode_index[s] point to first entry in
+ * state_microcode_table for state s (backward scan makes this easier).
+ * Check that table is in order -- catch coding errors.
+ * For what it's worth, this routine is idempotent.
+ */
+ const struct state_microcode *t;
+
+ for (t = &state_microcode_table[countof(state_microcode_table) - 1];;)
+ {
+ passert(STATE_IKE_FLOOR <= t->state && t->state < STATE_IKE_ROOF);
+ ike_microcode_index[t->state - STATE_IKE_FLOOR] = t;
+ if (t == state_microcode_table)
+ break;
+ t--;
+ passert(t[0].state <= t[1].state);
+ }
}
/* Process any message on the MSG_ERRQUEUE
@@ -586,357 +584,357 @@ init_demux(void)
static bool
check_msg_errqueue(const struct iface *ifp, short interest)
{
- struct pollfd pfd;
+ struct pollfd pfd;
- pfd.fd = ifp->fd;
- pfd.events = interest | POLLPRI | POLLOUT;
+ pfd.fd = ifp->fd;
+ pfd.events = interest | POLLPRI | POLLOUT;
- while (pfd.revents = 0
- , poll(&pfd, 1, -1) > 0 && (pfd.revents & POLLERR))
- {
- u_int8_t buffer[3000]; /* hope that this is big enough */
- union
+ while (pfd.revents = 0
+ , poll(&pfd, 1, -1) > 0 && (pfd.revents & POLLERR))
{
- struct sockaddr sa;
- struct sockaddr_in sa_in4;
- struct sockaddr_in6 sa_in6;
- } from;
+ u_int8_t buffer[3000]; /* hope that this is big enough */
+ union
+ {
+ struct sockaddr sa;
+ struct sockaddr_in sa_in4;
+ struct sockaddr_in6 sa_in6;
+ } from;
- int from_len = sizeof(from);
+ int from_len = sizeof(from);
- int packet_len;
+ int packet_len;
- struct msghdr emh;
- struct iovec eiov;
- union {
- /* force alignment (not documented as necessary) */
- struct cmsghdr ecms;
+ struct msghdr emh;
+ struct iovec eiov;
+ union {
+ /* force alignment (not documented as necessary) */
+ struct cmsghdr ecms;
- /* how much space is enough? */
- unsigned char space[256];
- } ecms_buf;
+ /* how much space is enough? */
+ unsigned char space[256];
+ } ecms_buf;
- struct cmsghdr *cm;
- char fromstr[sizeof(" for message to port 65536") + INET6_ADDRSTRLEN];
- struct state *sender = NULL;
+ struct cmsghdr *cm;
+ char fromstr[sizeof(" for message to port 65536") + INET6_ADDRSTRLEN];
+ struct state *sender = NULL;
- zero(&from.sa);
- from_len = sizeof(from);
+ zero(&from.sa);
+ from_len = sizeof(from);
- emh.msg_name = &from.sa; /* ??? filled in? */
- emh.msg_namelen = sizeof(from);
- emh.msg_iov = &eiov;
- emh.msg_iovlen = 1;
- emh.msg_control = &ecms_buf;
- emh.msg_controllen = sizeof(ecms_buf);
- emh.msg_flags = 0;
+ emh.msg_name = &from.sa; /* ??? filled in? */
+ emh.msg_namelen = sizeof(from);
+ emh.msg_iov = &eiov;
+ emh.msg_iovlen = 1;
+ emh.msg_control = &ecms_buf;
+ emh.msg_controllen = sizeof(ecms_buf);
+ emh.msg_flags = 0;
- eiov.iov_base = buffer; /* see readv(2) */
- eiov.iov_len = sizeof(buffer);
+ eiov.iov_base = buffer; /* see readv(2) */
+ eiov.iov_len = sizeof(buffer);
- packet_len = recvmsg(ifp->fd, &emh, MSG_ERRQUEUE);
+ packet_len = recvmsg(ifp->fd, &emh, MSG_ERRQUEUE);
- if (packet_len == -1)
- {
- log_errno((e, "recvmsg(,, MSG_ERRQUEUE) on %s failed in comm_handle"
- , ifp->rname));
- break;
- }
- else if (packet_len == sizeof(buffer))
- {
- plog("MSG_ERRQUEUE message longer than %lu bytes; truncated"
- , (unsigned long) sizeof(buffer));
- }
- else
- {
- sender = find_sender((size_t) packet_len, buffer);
- }
-
- DBG_cond_dump(DBG_ALL, "rejected packet:\n", buffer, packet_len);
- DBG_cond_dump(DBG_ALL, "control:\n", emh.msg_control, emh.msg_controllen);
- /* ??? Andi Kleen <ak@suse.de> and misc documentation
- * suggests that name will have the original destination
- * of the packet. We seem to see msg_namelen == 0.
- * Andi says that this is a kernel bug and has fixed it.
- * Perhaps in 2.2.18/2.4.0.
- */
- passert(emh.msg_name == &from.sa);
- DBG_cond_dump(DBG_ALL, "name:\n", emh.msg_name
- , emh.msg_namelen);
-
- fromstr[0] = '\0'; /* usual case :-( */
- switch (from.sa.sa_family)
- {
- char as[INET6_ADDRSTRLEN];
-
- case AF_INET:
- if (emh.msg_namelen == sizeof(struct sockaddr_in))
- snprintf(fromstr, sizeof(fromstr)
- , " for message to %s port %u"
- , inet_ntop(from.sa.sa_family
- , &from.sa_in4.sin_addr, as, sizeof(as))
- , ntohs(from.sa_in4.sin_port));
- break;
- case AF_INET6:
- if (emh.msg_namelen == sizeof(struct sockaddr_in6))
- snprintf(fromstr, sizeof(fromstr)
- , " for message to %s port %u"
- , inet_ntop(from.sa.sa_family
- , &from.sa_in6.sin6_addr, as, sizeof(as))
- , ntohs(from.sa_in6.sin6_port));
- break;
- }
-
- for (cm = CMSG_FIRSTHDR(&emh)
- ; cm != NULL
- ; cm = CMSG_NXTHDR(&emh,cm))
- {
- if (cm->cmsg_level == SOL_IP
- && cm->cmsg_type == IP_RECVERR)
- {
- /* ip(7) and recvmsg(2) specify:
- * ee_origin is SO_EE_ORIGIN_ICMP for ICMP
- * or SO_EE_ORIGIN_LOCAL for locally generated errors.
- * ee_type and ee_code are from the ICMP header.
- * ee_info is the discovered MTU for EMSGSIZE errors
- * ee_data is not used.
- *
- * ??? recvmsg(2) says "SOCK_EE_OFFENDER" but
- * means "SO_EE_OFFENDER". The OFFENDER is really
- * the router that complained. As such, the port
- * is meaningless.
- */
+ if (packet_len == -1)
+ {
+ log_errno((e, "recvmsg(,, MSG_ERRQUEUE) on %s failed in comm_handle"
+ , ifp->rname));
+ break;
+ }
+ else if (packet_len == sizeof(buffer))
+ {
+ plog("MSG_ERRQUEUE message longer than %lu bytes; truncated"
+ , (unsigned long) sizeof(buffer));
+ }
+ else
+ {
+ sender = find_sender((size_t) packet_len, buffer);
+ }
- /* ??? cmsg(3) claims that CMSG_DATA returns
- * void *, but RFC 2292 and /usr/include/bits/socket.h
- * say unsigned char *. The manual is being fixed.
+ DBG_cond_dump(DBG_ALL, "rejected packet:\n", buffer, packet_len);
+ DBG_cond_dump(DBG_ALL, "control:\n", emh.msg_control, emh.msg_controllen);
+ /* ??? Andi Kleen <ak@suse.de> and misc documentation
+ * suggests that name will have the original destination
+ * of the packet. We seem to see msg_namelen == 0.
+ * Andi says that this is a kernel bug and has fixed it.
+ * Perhaps in 2.2.18/2.4.0.
*/
- struct sock_extended_err *ee = (void *)CMSG_DATA(cm);
- const char *offstr = "unspecified";
- char offstrspace[INET6_ADDRSTRLEN];
- char orname[50];
+ passert(emh.msg_name == &from.sa);
+ DBG_cond_dump(DBG_ALL, "name:\n", emh.msg_name
+ , emh.msg_namelen);
- if (cm->cmsg_len > CMSG_LEN(sizeof(struct sock_extended_err)))
+ fromstr[0] = '\0'; /* usual case :-( */
+ switch (from.sa.sa_family)
{
- const struct sockaddr *offender = SO_EE_OFFENDER(ee);
-
- switch (offender->sa_family)
- {
- case AF_INET:
- offstr = inet_ntop(offender->sa_family
- , &((const struct sockaddr_in *)offender)->sin_addr
- , offstrspace, sizeof(offstrspace));
- break;
- case AF_INET6:
- offstr = inet_ntop(offender->sa_family
- , &((const struct sockaddr_in6 *)offender)->sin6_addr
- , offstrspace, sizeof(offstrspace));
+ char as[INET6_ADDRSTRLEN];
+
+ case AF_INET:
+ if (emh.msg_namelen == sizeof(struct sockaddr_in))
+ snprintf(fromstr, sizeof(fromstr)
+ , " for message to %s port %u"
+ , inet_ntop(from.sa.sa_family
+ , &from.sa_in4.sin_addr, as, sizeof(as))
+ , ntohs(from.sa_in4.sin_port));
break;
- default:
- offstr = "unknown";
+ case AF_INET6:
+ if (emh.msg_namelen == sizeof(struct sockaddr_in6))
+ snprintf(fromstr, sizeof(fromstr)
+ , " for message to %s port %u"
+ , inet_ntop(from.sa.sa_family
+ , &from.sa_in6.sin6_addr, as, sizeof(as))
+ , ntohs(from.sa_in6.sin6_port));
break;
- }
}
- switch (ee->ee_origin)
+ for (cm = CMSG_FIRSTHDR(&emh)
+ ; cm != NULL
+ ; cm = CMSG_NXTHDR(&emh,cm))
{
- case SO_EE_ORIGIN_NONE:
- snprintf(orname, sizeof(orname), "none");
- break;
- case SO_EE_ORIGIN_LOCAL:
- snprintf(orname, sizeof(orname), "local");
- break;
- case SO_EE_ORIGIN_ICMP:
- snprintf(orname, sizeof(orname)
- , "ICMP type %d code %d (not authenticated)"
- , ee->ee_type, ee->ee_code
- );
- break;
- case SO_EE_ORIGIN_ICMP6:
- snprintf(orname, sizeof(orname)
- , "ICMP6 type %d code %d (not authenticated)"
- , ee->ee_type, ee->ee_code
- );
- break;
- default:
- snprintf(orname, sizeof(orname), "invalid origin %lu"
- , (unsigned long) ee->ee_origin);
- break;
- }
-
- {
- struct state *old_state = cur_state;
-
- cur_state = sender;
-
- /* note dirty trick to suppress ~ at start of format
- * if we know what state to blame.
- */
- if ((packet_len == 1) && (buffer[0] = 0xff)
+ if (cm->cmsg_level == SOL_IP
+ && cm->cmsg_type == IP_RECVERR)
+ {
+ /* ip(7) and recvmsg(2) specify:
+ * ee_origin is SO_EE_ORIGIN_ICMP for ICMP
+ * or SO_EE_ORIGIN_LOCAL for locally generated errors.
+ * ee_type and ee_code are from the ICMP header.
+ * ee_info is the discovered MTU for EMSGSIZE errors
+ * ee_data is not used.
+ *
+ * ??? recvmsg(2) says "SOCK_EE_OFFENDER" but
+ * means "SO_EE_OFFENDER". The OFFENDER is really
+ * the router that complained. As such, the port
+ * is meaningless.
+ */
+
+ /* ??? cmsg(3) claims that CMSG_DATA returns
+ * void *, but RFC 2292 and /usr/include/bits/socket.h
+ * say unsigned char *. The manual is being fixed.
+ */
+ struct sock_extended_err *ee = (void *)CMSG_DATA(cm);
+ const char *offstr = "unspecified";
+ char offstrspace[INET6_ADDRSTRLEN];
+ char orname[50];
+
+ if (cm->cmsg_len > CMSG_LEN(sizeof(struct sock_extended_err)))
+ {
+ const struct sockaddr *offender = SO_EE_OFFENDER(ee);
+
+ switch (offender->sa_family)
+ {
+ case AF_INET:
+ offstr = inet_ntop(offender->sa_family
+ , &((const struct sockaddr_in *)offender)->sin_addr
+ , offstrspace, sizeof(offstrspace));
+ break;
+ case AF_INET6:
+ offstr = inet_ntop(offender->sa_family
+ , &((const struct sockaddr_in6 *)offender)->sin6_addr
+ , offstrspace, sizeof(offstrspace));
+ break;
+ default:
+ offstr = "unknown";
+ break;
+ }
+ }
+
+ switch (ee->ee_origin)
+ {
+ case SO_EE_ORIGIN_NONE:
+ snprintf(orname, sizeof(orname), "none");
+ break;
+ case SO_EE_ORIGIN_LOCAL:
+ snprintf(orname, sizeof(orname), "local");
+ break;
+ case SO_EE_ORIGIN_ICMP:
+ snprintf(orname, sizeof(orname)
+ , "ICMP type %d code %d (not authenticated)"
+ , ee->ee_type, ee->ee_code
+ );
+ break;
+ case SO_EE_ORIGIN_ICMP6:
+ snprintf(orname, sizeof(orname)
+ , "ICMP6 type %d code %d (not authenticated)"
+ , ee->ee_type, ee->ee_code
+ );
+ break;
+ default:
+ snprintf(orname, sizeof(orname), "invalid origin %lu"
+ , (unsigned long) ee->ee_origin);
+ break;
+ }
+
+ {
+ struct state *old_state = cur_state;
+
+ cur_state = sender;
+
+ /* note dirty trick to suppress ~ at start of format
+ * if we know what state to blame.
+ */
+ if ((packet_len == 1) && (buffer[0] = 0xff)
#ifdef DEBUG
- && ((cur_debugging & DBG_NATT) == 0)
+ && ((cur_debugging & DBG_NATT) == 0)
#endif
- ) {
- /* don't log NAT-T keepalive related errors unless NATT debug is
- * enabled
- */
- }
- else
- plog((sender != NULL) + "~"
- "ERROR: asynchronous network error report on %s"
- "%s"
- ", complainant %s"
- ": %s"
- " [errno %lu, origin %s"
- /* ", pad %d, info %ld" */
- /* ", data %ld" */
- "]"
- , ifp->rname
- , fromstr
- , offstr
- , strerror(ee->ee_errno)
- , (unsigned long) ee->ee_errno
- , orname
- /* , ee->ee_pad, (unsigned long)ee->ee_info */
- /* , (unsigned long)ee->ee_data */
- );
- cur_state = old_state;
+ ) {
+ /* don't log NAT-T keepalive related errors unless NATT debug is
+ * enabled
+ */
+ }
+ else
+ plog((sender != NULL) + "~"
+ "ERROR: asynchronous network error report on %s"
+ "%s"
+ ", complainant %s"
+ ": %s"
+ " [errno %lu, origin %s"
+ /* ", pad %d, info %ld" */
+ /* ", data %ld" */
+ "]"
+ , ifp->rname
+ , fromstr
+ , offstr
+ , strerror(ee->ee_errno)
+ , (unsigned long) ee->ee_errno
+ , orname
+ /* , ee->ee_pad, (unsigned long)ee->ee_info */
+ /* , (unsigned long)ee->ee_data */
+ );
+ cur_state = old_state;
+ }
+ }
+ else
+ {
+ /* .cmsg_len is a kernel_size_t(!), but the value
+ * certainly ought to fit in an unsigned long.
+ */
+ plog("unknown cmsg: level %d, type %d, len %lu"
+ , cm->cmsg_level, cm->cmsg_type
+ , (unsigned long) cm->cmsg_len);
+ }
}
- }
- else
- {
- /* .cmsg_len is a kernel_size_t(!), but the value
- * certainly ought to fit in an unsigned long.
- */
- plog("unknown cmsg: level %d, type %d, len %lu"
- , cm->cmsg_level, cm->cmsg_type
- , (unsigned long) cm->cmsg_len);
- }
}
- }
- return (pfd.revents & interest) != 0;
+ return (pfd.revents & interest) != 0;
}
#endif /* defined(IP_RECVERR) && defined(MSG_ERRQUEUE) */
bool
send_packet(struct state *st, const char *where)
{
- struct connection *c = st->st_connection;
- int port_buf;
- bool err;
- u_int8_t ike_pkt[MAX_OUTPUT_UDP_SIZE];
- u_int8_t *ptr;
- unsigned long len;
-
- if (c->interface->ike_float && st->st_tpacket.len != 1)
- {
- if ((unsigned long) st->st_tpacket.len > (MAX_OUTPUT_UDP_SIZE-sizeof(u_int32_t)))
+ struct connection *c = st->st_connection;
+ int port_buf;
+ bool err;
+ u_int8_t ike_pkt[MAX_OUTPUT_UDP_SIZE];
+ u_int8_t *ptr;
+ unsigned long len;
+
+ if (c->interface->ike_float && st->st_tpacket.len != 1)
{
- DBG_log("send_packet(): really too big");
- return FALSE;
+ if ((unsigned long) st->st_tpacket.len > (MAX_OUTPUT_UDP_SIZE-sizeof(u_int32_t)))
+ {
+ DBG_log("send_packet(): really too big");
+ return FALSE;
+ }
+ ptr = ike_pkt;
+ /** Add Non-ESP marker **/
+ memset(ike_pkt, 0, sizeof(u_int32_t));
+ memcpy(ike_pkt + sizeof(u_int32_t), st->st_tpacket.ptr,
+ (unsigned long)st->st_tpacket.len);
+ len = (unsigned long) st->st_tpacket.len + sizeof(u_int32_t);
}
- ptr = ike_pkt;
- /** Add Non-ESP marker **/
- memset(ike_pkt, 0, sizeof(u_int32_t));
- memcpy(ike_pkt + sizeof(u_int32_t), st->st_tpacket.ptr,
- (unsigned long)st->st_tpacket.len);
- len = (unsigned long) st->st_tpacket.len + sizeof(u_int32_t);
- }
- else
- {
- ptr = st->st_tpacket.ptr;
- len = (unsigned long) st->st_tpacket.len;
- }
-
- DBG(DBG_RAW,
+ else
{
- DBG_log("sending %lu bytes for %s through %s to %s:%u:"
- , (unsigned long) st->st_tpacket.len
- , where
- , c->interface->rname
- , ip_str(&c->spd.that.host_addr)
- , (unsigned)c->spd.that.host_port);
- DBG_dump_chunk(NULL, st->st_tpacket);
- });
-
- /* XXX: Not very clean. We manipulate the port of the ip_address to
- * have a port in the sockaddr*, but we retain the original port
- * and restore it afterwards.
- */
-
- port_buf = portof(&c->spd.that.host_addr);
- setportof(htons(c->spd.that.host_port), &c->spd.that.host_addr);
+ ptr = st->st_tpacket.ptr;
+ len = (unsigned long) st->st_tpacket.len;
+ }
+
+ DBG(DBG_RAW,
+ {
+ DBG_log("sending %lu bytes for %s through %s to %s:%u:"
+ , (unsigned long) st->st_tpacket.len
+ , where
+ , c->interface->rname
+ , ip_str(&c->spd.that.host_addr)
+ , (unsigned)c->spd.that.host_port);
+ DBG_dump_chunk(NULL, st->st_tpacket);
+ });
+
+ /* XXX: Not very clean. We manipulate the port of the ip_address to
+ * have a port in the sockaddr*, but we retain the original port
+ * and restore it afterwards.
+ */
+
+ port_buf = portof(&c->spd.that.host_addr);
+ setportof(htons(c->spd.that.host_port), &c->spd.that.host_addr);
#if defined(IP_RECVERR) && defined(MSG_ERRQUEUE)
- (void) check_msg_errqueue(c->interface, POLLOUT);
+ (void) check_msg_errqueue(c->interface, POLLOUT);
#endif /* defined(IP_RECVERR) && defined(MSG_ERRQUEUE) */
- err = sendto(c->interface->fd
- , ptr, len, 0
- , sockaddrof(&c->spd.that.host_addr)
- , sockaddrlenof(&c->spd.that.host_addr)) != (ssize_t)len;
-
- /* restore port */
- setportof(port_buf, &c->spd.that.host_addr);
-
- if (err)
- {
- /* do not log NAT-T Keep Alive packets */
- if (streq(where, "NAT-T Keep Alive"))
- return FALSE;
- log_errno((e, "sendto on %s to %s:%u failed in %s"
- , c->interface->rname
- , ip_str(&c->spd.that.host_addr)
- , (unsigned)c->spd.that.host_port
- , where));
- return FALSE;
- }
- else
- {
- return TRUE;
- }
+ err = sendto(c->interface->fd
+ , ptr, len, 0
+ , sockaddrof(&c->spd.that.host_addr)
+ , sockaddrlenof(&c->spd.that.host_addr)) != (ssize_t)len;
+
+ /* restore port */
+ setportof(port_buf, &c->spd.that.host_addr);
+
+ if (err)
+ {
+ /* do not log NAT-T Keep Alive packets */
+ if (streq(where, "NAT-T Keep Alive"))
+ return FALSE;
+ log_errno((e, "sendto on %s to %s:%u failed in %s"
+ , c->interface->rname
+ , ip_str(&c->spd.that.host_addr)
+ , (unsigned)c->spd.that.host_port
+ , where));
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
}
static stf_status
unexpected(struct msg_digest *md)
{
- loglog(RC_LOG_SERIOUS, "unexpected message received in state %s"
- , enum_name(&state_names, md->st->st_state));
- return STF_IGNORE;
+ loglog(RC_LOG_SERIOUS, "unexpected message received in state %s"
+ , enum_name(&state_names, md->st->st_state));
+ return STF_IGNORE;
}
static stf_status
informational(struct msg_digest *md UNUSED)
{
- struct payload_digest *const n_pld = md->chain[ISAKMP_NEXT_N];
-
- /* If the Notification Payload is not null... */
- if (n_pld != NULL)
- {
- pb_stream *const n_pbs = &n_pld->pbs;
- struct isakmp_notification *const n = &n_pld->payload.notification;
- int disp_len;
- char disp_buf[200];
-
- /* Switch on Notification Type (enum) */
- switch (n->isan_type)
- {
- case R_U_THERE:
- return dpd_inI_outR(md->st, n, n_pbs);
-
- case R_U_THERE_ACK:
- return dpd_inR(md->st, n, n_pbs);
- default:
- if (pbs_left(n_pbs) >= sizeof(disp_buf)-1)
- disp_len = sizeof(disp_buf)-1;
- else
- disp_len = pbs_left(n_pbs);
- memcpy(disp_buf, n_pbs->cur, disp_len);
- disp_buf[disp_len] = '\0';
- break;
- }
- }
- return STF_IGNORE;
+ struct payload_digest *const n_pld = md->chain[ISAKMP_NEXT_N];
+
+ /* If the Notification Payload is not null... */
+ if (n_pld != NULL)
+ {
+ pb_stream *const n_pbs = &n_pld->pbs;
+ struct isakmp_notification *const n = &n_pld->payload.notification;
+ int disp_len;
+ char disp_buf[200];
+
+ /* Switch on Notification Type (enum) */
+ switch (n->isan_type)
+ {
+ case R_U_THERE:
+ return dpd_inI_outR(md->st, n, n_pbs);
+
+ case R_U_THERE_ACK:
+ return dpd_inR(md->st, n, n_pbs);
+ default:
+ if (pbs_left(n_pbs) >= sizeof(disp_buf)-1)
+ disp_len = sizeof(disp_buf)-1;
+ else
+ disp_len = pbs_left(n_pbs);
+ memcpy(disp_buf, n_pbs->cur, disp_len);
+ disp_buf[disp_len] = '\0';
+ break;
+ }
+ }
+ return STF_IGNORE;
}
/* message digest allocation and deallocation */
@@ -947,54 +945,57 @@ static struct msg_digest *md_pool = NULL;
void
free_md_pool(void)
{
- for (;;)
- {
- struct msg_digest *md = md_pool;
+ for (;;)
+ {
+ struct msg_digest *md = md_pool;
- if (md == NULL)
- break;
- md_pool = md->next;
- pfree(md);
- }
+ if (md == NULL)
+ break;
+ md_pool = md->next;
+ free(md);
+ }
}
static struct msg_digest *
-alloc_md(void)
+malloc_md(void)
{
- struct msg_digest *md = md_pool;
-
- /* convenient initializer:
- * - all pointers NULL
- * - .note = NOTHING_WRONG
- * - .encrypted = FALSE
- */
- static const struct msg_digest blank_md;
-
- if (md == NULL)
- md = alloc_thing(struct msg_digest, "msg_digest");
- else
- md_pool = md->next;
-
- *md = blank_md;
- md->digest_roof = md->digest;
-
- /* note: although there may be multiple msg_digests at once
- * (due to suspended state transitions), there is a single
- * global reply_buffer. It will need to be saved and restored.
- */
- init_pbs(&md->reply, reply_buffer, sizeof(reply_buffer), "reply packet");
-
- return md;
+ struct msg_digest *md = md_pool;
+
+ /* convenient initializer:
+ * - all pointers NULL
+ * - .note = NOTHING_WRONG
+ * - .encrypted = FALSE
+ */
+ static const struct msg_digest blank_md;
+
+ if (md == NULL)
+ {
+ md = malloc_thing(struct msg_digest);
+ zero(md);
+ }
+ else
+ md_pool = md->next;
+
+ *md = blank_md;
+ md->digest_roof = md->digest;
+
+ /* note: although there may be multiple msg_digests at once
+ * (due to suspended state transitions), there is a single
+ * global reply_buffer. It will need to be saved and restored.
+ */
+ init_pbs(&md->reply, reply_buffer, sizeof(reply_buffer), "reply packet");
+
+ return md;
}
void
release_md(struct msg_digest *md)
{
- freeanychunk(md->raw_packet);
- pfreeany(md->packet_pbs.start);
- md->packet_pbs.start = NULL;
- md->next = md_pool;
- md_pool = md;
+ chunk_free(&md->raw_packet);
+ free(md->packet_pbs.start);
+ md->packet_pbs.start = NULL;
+ md->next = md_pool;
+ md_pool = md;
}
/* wrapper for read_packet and process_packet
@@ -1013,35 +1014,35 @@ release_md(struct msg_digest *md)
void
comm_handle(const struct iface *ifp)
{
- static struct msg_digest *md;
+ static struct msg_digest *md;
#if defined(IP_RECVERR) && defined(MSG_ERRQUEUE)
- /* Even though select(2) says that there is a message,
- * it might only be a MSG_ERRQUEUE message. At least
- * sometimes that leads to a hanging recvfrom. To avoid
- * what appears to be a kernel bug, check_msg_errqueue
- * uses poll(2) and tells us if there is anything for us
- * to read.
- *
- * This is early enough that teardown isn't required:
- * just return on failure.
- */
- if (!check_msg_errqueue(ifp, POLLIN))
- return; /* no normal message to read */
+ /* Even though select(2) says that there is a message,
+ * it might only be a MSG_ERRQUEUE message. At least
+ * sometimes that leads to a hanging recvfrom. To avoid
+ * what appears to be a kernel bug, check_msg_errqueue
+ * uses poll(2) and tells us if there is anything for us
+ * to read.
+ *
+ * This is early enough that teardown isn't required:
+ * just return on failure.
+ */
+ if (!check_msg_errqueue(ifp, POLLIN))
+ return; /* no normal message to read */
#endif /* defined(IP_RECVERR) && defined(MSG_ERRQUEUE) */
- md = alloc_md();
- md->iface = ifp;
+ md = malloc_md();
+ md->iface = ifp;
- if (read_packet(md))
- process_packet(&md);
+ if (read_packet(md))
+ process_packet(&md);
- if (md != NULL)
- release_md(md);
+ if (md != NULL)
+ release_md(md);
- cur_state = NULL;
- reset_cur_connection();
- cur_from = NULL;
+ cur_state = NULL;
+ reset_cur_connection();
+ cur_from = NULL;
}
/* read the message.
@@ -1052,177 +1053,177 @@ comm_handle(const struct iface *ifp)
static bool
read_packet(struct msg_digest *md)
{
- const struct iface *ifp = md->iface;
- int packet_len;
- u_int8_t *buffer;
- u_int8_t *buffer_nat;
- union
- {
- struct sockaddr sa;
- struct sockaddr_in sa_in4;
- struct sockaddr_in6 sa_in6;
- } from;
- int from_len = sizeof(from);
- err_t from_ugh = NULL;
- static const char undisclosed[] = "unknown source";
-
- happy(anyaddr(addrtypeof(&ifp->addr), &md->sender));
- zero(&from.sa);
- ioctl(ifp->fd, FIONREAD, &packet_len);
- buffer = alloc_bytes(packet_len, "buffer read packet");
- packet_len = recvfrom(ifp->fd, buffer, packet_len, 0
- , &from.sa, &from_len);
-
- /* First: digest the from address.
- * We presume that nothing here disturbs errno.
- */
- if (packet_len == -1
- && from_len == sizeof(from)
- && all_zero((const void *)&from.sa, sizeof(from)))
- {
- /* "from" is untouched -- not set by recvfrom */
- from_ugh = undisclosed;
- }
- else if (from_len
- < (int) (offsetof(struct sockaddr, sa_family) + sizeof(from.sa.sa_family)))
- {
- from_ugh = "truncated";
- }
- else
- {
- const struct af_info *afi = aftoinfo(from.sa.sa_family);
-
- if (afi == NULL)
+ const struct iface *ifp = md->iface;
+ int packet_len;
+ u_int8_t *buffer;
+ u_int8_t *buffer_nat;
+ union
{
- from_ugh = "unexpected Address Family";
+ struct sockaddr sa;
+ struct sockaddr_in sa_in4;
+ struct sockaddr_in6 sa_in6;
+ } from;
+ int from_len = sizeof(from);
+ err_t from_ugh = NULL;
+ static const char undisclosed[] = "unknown source";
+
+ happy(anyaddr(addrtypeof(&ifp->addr), &md->sender));
+ zero(&from.sa);
+ ioctl(ifp->fd, FIONREAD, &packet_len);
+ buffer = malloc(packet_len);
+ packet_len = recvfrom(ifp->fd, buffer, packet_len, 0
+ , &from.sa, &from_len);
+
+ /* First: digest the from address.
+ * We presume that nothing here disturbs errno.
+ */
+ if (packet_len == -1
+ && from_len == sizeof(from)
+ && all_zero((const void *)&from.sa, sizeof(from)))
+ {
+ /* "from" is untouched -- not set by recvfrom */
+ from_ugh = undisclosed;
}
- else if (from_len != (int)afi->sa_sz)
+ else if (from_len
+ < (int) (offsetof(struct sockaddr, sa_family) + sizeof(from.sa.sa_family)))
{
- from_ugh = "wrong length";
+ from_ugh = "truncated";
}
else
{
- switch (from.sa.sa_family)
- {
- case AF_INET:
- from_ugh = initaddr((void *) &from.sa_in4.sin_addr
- , sizeof(from.sa_in4.sin_addr), AF_INET, &md->sender);
- md->sender_port = ntohs(from.sa_in4.sin_port);
- break;
- case AF_INET6:
- from_ugh = initaddr((void *) &from.sa_in6.sin6_addr
- , sizeof(from.sa_in6.sin6_addr), AF_INET6, &md->sender);
- md->sender_port = ntohs(from.sa_in6.sin6_port);
- break;
- }
+ const struct af_info *afi = aftoinfo(from.sa.sa_family);
+
+ if (afi == NULL)
+ {
+ from_ugh = "unexpected Address Family";
+ }
+ else if (from_len != (int)afi->sa_sz)
+ {
+ from_ugh = "wrong length";
+ }
+ else
+ {
+ switch (from.sa.sa_family)
+ {
+ case AF_INET:
+ from_ugh = initaddr((void *) &from.sa_in4.sin_addr
+ , sizeof(from.sa_in4.sin_addr), AF_INET, &md->sender);
+ md->sender_port = ntohs(from.sa_in4.sin_port);
+ break;
+ case AF_INET6:
+ from_ugh = initaddr((void *) &from.sa_in6.sin6_addr
+ , sizeof(from.sa_in6.sin6_addr), AF_INET6, &md->sender);
+ md->sender_port = ntohs(from.sa_in6.sin6_port);
+ break;
+ }
+ }
}
- }
- /* now we report any actual I/O error */
- if (packet_len == -1)
- {
- if (from_ugh == undisclosed
- && errno == ECONNREFUSED)
+ /* now we report any actual I/O error */
+ if (packet_len == -1)
{
- /* Tone down scary message for vague event:
- * We get "connection refused" in response to some
- * datagram we sent, but we cannot tell which one.
- */
- plog("some IKE message we sent has been rejected with ECONNREFUSED (kernel supplied no details)");
+ if (from_ugh == undisclosed
+ && errno == ECONNREFUSED)
+ {
+ /* Tone down scary message for vague event:
+ * We get "connection refused" in response to some
+ * datagram we sent, but we cannot tell which one.
+ */
+ plog("some IKE message we sent has been rejected with ECONNREFUSED (kernel supplied no details)");
+ }
+ else if (from_ugh != NULL)
+ {
+ log_errno((e, "recvfrom on %s failed; Pluto cannot decode source sockaddr in rejection: %s"
+ , ifp->rname, from_ugh));
+ }
+ else
+ {
+ log_errno((e, "recvfrom on %s from %s:%u failed"
+ , ifp->rname
+ , ip_str(&md->sender), (unsigned)md->sender_port));
+ }
+
+ return FALSE;
}
else if (from_ugh != NULL)
{
- log_errno((e, "recvfrom on %s failed; Pluto cannot decode source sockaddr in rejection: %s"
- , ifp->rname, from_ugh));
- }
- else
- {
- log_errno((e, "recvfrom on %s from %s:%u failed"
- , ifp->rname
- , ip_str(&md->sender), (unsigned)md->sender_port));
+ plog("recvfrom on %s returned misformed source sockaddr: %s"
+ , ifp->rname, from_ugh);
+ return FALSE;
}
+ cur_from = &md->sender;
+ cur_from_port = md->sender_port;
- return FALSE;
- }
- else if (from_ugh != NULL)
- {
- plog("recvfrom on %s returned misformed source sockaddr: %s"
- , ifp->rname, from_ugh);
- return FALSE;
- }
- cur_from = &md->sender;
- cur_from_port = md->sender_port;
-
- if (ifp->ike_float == TRUE)
- {
- u_int32_t non_esp;
-
- if (packet_len < (int)sizeof(u_int32_t))
- {
- plog("recvfrom %s:%u too small packet (%d)"
- , ip_str(cur_from), (unsigned) cur_from_port, packet_len);
- return FALSE;
- }
- memcpy(&non_esp, buffer, sizeof(u_int32_t));
- if (non_esp != 0)
+ if (ifp->ike_float == TRUE)
{
- plog("recvfrom %s:%u has no Non-ESP marker"
- , ip_str(cur_from), (unsigned) cur_from_port);
- return FALSE;
+ u_int32_t non_esp;
+
+ if (packet_len < (int)sizeof(u_int32_t))
+ {
+ plog("recvfrom %s:%u too small packet (%d)"
+ , ip_str(cur_from), (unsigned) cur_from_port, packet_len);
+ return FALSE;
+ }
+ memcpy(&non_esp, buffer, sizeof(u_int32_t));
+ if (non_esp != 0)
+ {
+ plog("recvfrom %s:%u has no Non-ESP marker"
+ , ip_str(cur_from), (unsigned) cur_from_port);
+ return FALSE;
+ }
+ packet_len -= sizeof(u_int32_t);
+ buffer_nat = malloc(packet_len);
+ memcpy(buffer_nat, buffer + sizeof(u_int32_t), packet_len);
+ free(buffer);
+ buffer = buffer_nat;
}
- packet_len -= sizeof(u_int32_t);
- buffer_nat = alloc_bytes(packet_len, "buffer read packet");
- memcpy(buffer_nat, buffer + sizeof(u_int32_t), packet_len);
- pfree(buffer);
- buffer = buffer_nat;
- }
-
- /* Clone actual message contents
- * and set up md->packet_pbs to describe it.
- */
- init_pbs(&md->packet_pbs, buffer, packet_len, "packet");
-
- DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL,
- {
- DBG_log(BLANK_FORMAT);
- DBG_log("*received %d bytes from %s:%u on %s"
- , (int) pbs_room(&md->packet_pbs)
- , ip_str(cur_from), (unsigned) cur_from_port
- , ifp->rname);
- });
- DBG(DBG_RAW,
- DBG_dump("", md->packet_pbs.start, pbs_room(&md->packet_pbs)));
+ /* Clone actual message contents
+ * and set up md->packet_pbs to describe it.
+ */
+ init_pbs(&md->packet_pbs, buffer, packet_len, "packet");
- if ((pbs_room(&md->packet_pbs)==1) && (md->packet_pbs.start[0]==0xff))
- {
- /**
- * NAT-T Keep-alive packets should be discarded by kernel ESPinUDP
- * layer. But bogus keep-alive packets (sent with a non-esp marker)
- * can reach this point. Complain and discard them.
- */
- DBG(DBG_NATT,
- DBG_log("NAT-T keep-alive (bogus ?) should not reach this point. "
- "Ignored. Sender: %s:%u", ip_str(cur_from),
- (unsigned) cur_from_port);
- )
- return FALSE;
- }
+ DBG(DBG_RAW | DBG_CRYPT | DBG_PARSING | DBG_CONTROL,
+ {
+ DBG_log(BLANK_FORMAT);
+ DBG_log("*received %d bytes from %s:%u on %s"
+ , (int) pbs_room(&md->packet_pbs)
+ , ip_str(cur_from), (unsigned) cur_from_port
+ , ifp->rname);
+ });
+
+ DBG(DBG_RAW,
+ DBG_dump("", md->packet_pbs.start, pbs_room(&md->packet_pbs)));
-#define IKEV2_VERSION_OFFSET 17
-#define IKEV2_VERSION 0x20
+ if ((pbs_room(&md->packet_pbs)==1) && (md->packet_pbs.start[0]==0xff))
+ {
+ /**
+ * NAT-T Keep-alive packets should be discarded by kernel ESPinUDP
+ * layer. But bogus keep-alive packets (sent with a non-esp marker)
+ * can reach this point. Complain and discard them.
+ */
+ DBG(DBG_NATT,
+ DBG_log("NAT-T keep-alive (bogus ?) should not reach this point. "
+ "Ignored. Sender: %s:%u", ip_str(cur_from),
+ (unsigned) cur_from_port);
+ )
+ return FALSE;
+ }
- /* ignore IKEv2 packets - they will be handled by charon */
- if (pbs_room(&md->packet_pbs) > IKEV2_VERSION_OFFSET
- && md->packet_pbs.start[IKEV2_VERSION_OFFSET] == IKEV2_VERSION)
- {
- DBG(DBG_CONTROLMORE,
- DBG_log(" ignoring IKEv2 packet")
- )
- return FALSE;
- }
+#define IKEV2_VERSION_OFFSET 17
+#define IKEV2_VERSION 0x20
+
+ /* ignore IKEv2 packets - they will be handled by charon */
+ if (pbs_room(&md->packet_pbs) > IKEV2_VERSION_OFFSET
+ && md->packet_pbs.start[IKEV2_VERSION_OFFSET] == IKEV2_VERSION)
+ {
+ DBG(DBG_CONTROLMORE,
+ DBG_log(" ignoring IKEv2 packet")
+ )
+ return FALSE;
+ }
- return TRUE;
+ return TRUE;
}
/* process an input packet, possibly generating a reply.
@@ -1233,860 +1234,881 @@ read_packet(struct msg_digest *md)
static void
process_packet(struct msg_digest **mdp)
{
- struct msg_digest *md = *mdp;
- const struct state_microcode *smc;
- bool new_iv_set = FALSE;
- bool restore_iv = FALSE;
- u_char new_iv[MAX_DIGEST_LEN];
- u_int new_iv_len = 0;
+ struct msg_digest *md = *mdp;
+ const struct state_microcode *smc;
+ bool new_iv_set = FALSE;
+ bool restore_iv = FALSE;
+ u_char new_iv[MAX_DIGEST_LEN];
+ u_int new_iv_len = 0;
- struct state *st = NULL;
- enum state_kind from_state = STATE_UNDEFINED; /* state we started in */
+ 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 (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))
- {
- /* Identify specific failures:
- * - bad ISAKMP major/minor version numbers
- */
- if (md->packet_pbs.roof - md->packet_pbs.cur >= (ptrdiff_t)isakmp_hdr_desc.size)
+ if (!in_struct(&md->hdr, &isakmp_hdr_desc, &md->packet_pbs, &md->message_pbs))
{
- 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);
+ /* Identify specific failures:
+ * - bad ISAKMP major/minor version numbers
+ */
+ 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;
- }
}
- SEND_NOTIFICATION(PAYLOAD_MALFORMED);
- return;
- }
-
- if (md->packet_pbs.roof != md->message_pbs.roof)
- {
- plog("size (%u) differs from size specified in ISAKMP HDR (%u)"
- , (unsigned) pbs_room(&md->packet_pbs), md->hdr.isa_length);
+
+ if (md->packet_pbs.roof != md->message_pbs.roof)
+ {
+ plog("size (%u) differs from size specified in ISAKMP HDR (%u)"
+ , (unsigned) pbs_room(&md->packet_pbs), md->hdr.isa_length);
#ifdef CISCO_QUIRKS
- if (pbs_room(&md->packet_pbs) - md->hdr.isa_length == 16)
- plog("Cisco VPN client appends 16 surplus NULL bytes");
- else
+ if (pbs_room(&md->packet_pbs) - md->hdr.isa_length == 16)
+ plog("Cisco VPN client appends 16 surplus NULL bytes");
+ else
#endif
- return;
- }
+ return;
+ }
- switch (md->hdr.isa_xchg)
- {
+ switch (md->hdr.isa_xchg)
+ {
#ifdef NOTYET
- case ISAKMP_XCHG_NONE:
- case ISAKMP_XCHG_BASE:
+ case ISAKMP_XCHG_NONE:
+ case ISAKMP_XCHG_BASE:
#endif
- case ISAKMP_XCHG_IDPROT: /* part of a Main Mode exchange */
- if (md->hdr.isa_msgid != MAINMODE_MSGID)
- {
- plog("Message ID was 0x%08lx but should be zero in Main Mode",
- (unsigned long) md->hdr.isa_msgid);
- SEND_NOTIFICATION(INVALID_MESSAGE_ID);
- return;
- }
-
- if (is_zero_cookie(md->hdr.isa_icookie))
- {
- plog("Initiator Cookie must not be zero in Main Mode message");
- SEND_NOTIFICATION(INVALID_COOKIE);
- return;
- }
+ case ISAKMP_XCHG_IDPROT: /* part of a Main Mode exchange */
+ if (md->hdr.isa_msgid != MAINMODE_MSGID)
+ {
+ plog("Message ID was 0x%08lx but should be zero in Main Mode",
+ (unsigned long) md->hdr.isa_msgid);
+ SEND_NOTIFICATION(INVALID_MESSAGE_ID);
+ return;
+ }
- if (is_zero_cookie(md->hdr.isa_rcookie))
- {
- /* initial message from initiator
- * ??? what if this is a duplicate of another message?
- */
- if (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION)
- {
- plog("initial Main Mode message is invalid:"
- " its Encrypted Flag is on");
- SEND_NOTIFICATION(INVALID_FLAGS);
- return;
- }
+ if (is_zero_cookie(md->hdr.isa_icookie))
+ {
+ plog("Initiator Cookie must not be zero in Main Mode message");
+ SEND_NOTIFICATION(INVALID_COOKIE);
+ return;
+ }
- /* don't build a state until the message looks tasty */
- from_state = STATE_MAIN_R0;
- }
- else
- {
- /* not an initial message */
+ if (is_zero_cookie(md->hdr.isa_rcookie))
+ {
+ /* initial message from initiator
+ * ??? what if this is a duplicate of another message?
+ */
+ if (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION)
+ {
+ plog("initial Main Mode message is invalid:"
+ " its Encrypted Flag is on");
+ SEND_NOTIFICATION(INVALID_FLAGS);
+ return;
+ }
- st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
- , &md->sender, md->hdr.isa_msgid);
+ /* don't build a state until the message looks tasty */
+ from_state = STATE_MAIN_R0;
+ }
+ else
+ {
+ /* not an initial message */
- if (st == NULL)
- {
- /* perhaps this is a first message from the responder
- * and contains a responder cookie that we've not yet seen.
- */
- st = find_state(md->hdr.isa_icookie, zero_cookie
- , &md->sender, md->hdr.isa_msgid);
+ st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
+ , &md->sender, md->hdr.isa_msgid);
- if (st == NULL)
- {
- plog("Main Mode message is part of an unknown exchange");
- /* XXX Could send notification back */
- return;
+ if (st == NULL)
+ {
+ /* perhaps this is a first message from the responder
+ * and contains a responder cookie that we've not yet seen.
+ */
+ st = find_state(md->hdr.isa_icookie, zero_cookie
+ , &md->sender, md->hdr.isa_msgid);
+
+ if (st == NULL)
+ {
+ plog("Main Mode message is part of an unknown exchange");
+ /* XXX Could send notification back */
+ return;
+ }
+ }
+ set_cur_state(st);
+ from_state = st->st_state;
}
- }
- set_cur_state(st);
- from_state = st->st_state;
- }
- break;
+ break;
#ifdef NOTYET
- case ISAKMP_XCHG_AO:
- case ISAKMP_XCHG_AGGR:
+ case ISAKMP_XCHG_AO:
+ case ISAKMP_XCHG_AGGR:
#endif
- case ISAKMP_XCHG_INFO: /* an informational exchange */
- st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
- , &md->sender, MAINMODE_MSGID);
+ case ISAKMP_XCHG_INFO: /* an informational exchange */
+ st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
+ , &md->sender, MAINMODE_MSGID);
- if (st != NULL)
- set_cur_state(st);
+ if (st != NULL)
+ set_cur_state(st);
- if (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION)
- {
- if (st == NULL)
- {
- plog("Informational Exchange is for an unknown (expired?) SA");
- /* XXX Could send notification back */
- return;
- }
+ if (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION)
+ {
+ if (st == NULL)
+ {
+ plog("Informational Exchange is for an unknown (expired?) SA");
+ /* XXX Could send notification back */
+ return;
+ }
- if (!IS_ISAKMP_ENCRYPTED(st->st_state))
- {
- loglog(RC_LOG_SERIOUS, "encrypted Informational Exchange message is invalid"
- " because no key is known");
- /* XXX Could send notification back */
- return;
- }
+ if (!IS_ISAKMP_ENCRYPTED(st->st_state))
+ {
+ loglog(RC_LOG_SERIOUS, "encrypted Informational Exchange message is invalid"
+ " because no key is known");
+ /* XXX Could send notification back */
+ return;
+ }
- if (md->hdr.isa_msgid == MAINMODE_MSGID)
- {
- loglog(RC_LOG_SERIOUS, "Informational Exchange message is invalid because"
- " it has a Message ID of 0");
- /* XXX Could send notification back */
- return;
- }
-
- if (!reserve_msgid(st, md->hdr.isa_msgid))
- {
- 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 */
- return;
- }
+ if (md->hdr.isa_msgid == MAINMODE_MSGID)
+ {
+ loglog(RC_LOG_SERIOUS, "Informational Exchange message is invalid because"
+ " it has a Message ID of 0");
+ /* XXX Could send notification back */
+ return;
+ }
- if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state))
- {
- memcpy(st->st_ph1_iv, st->st_new_iv, st->st_new_iv_len);
- st->st_ph1_iv_len = st->st_new_iv_len;
-
- /* backup new_iv */
- new_iv_len = st->st_new_iv_len;
- passert(new_iv_len <= MAX_DIGEST_LEN)
- memcpy(new_iv, st->st_new_iv, new_iv_len);
- restore_iv = TRUE;
- }
- init_phase2_iv(st, &md->hdr.isa_msgid);
- new_iv_set = TRUE;
-
- from_state = STATE_INFO_PROTECTED;
- }
- else
- {
- if (st != NULL && IS_ISAKMP_ENCRYPTED(st->st_state))
- {
- loglog(RC_LOG_SERIOUS, "Informational Exchange message"
- " must be encrypted");
- /* XXX Could send notification back */
- return;
- }
- from_state = STATE_INFO;
- }
- break;
+ if (!reserve_msgid(st, md->hdr.isa_msgid))
+ {
+ 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 */
+ return;
+ }
- case ISAKMP_XCHG_QUICK: /* part of a Quick Mode exchange */
- if (is_zero_cookie(md->hdr.isa_icookie))
- {
- plog("Quick Mode message is invalid because"
- " it has an Initiator Cookie of 0");
- SEND_NOTIFICATION(INVALID_COOKIE);
- return;
- }
+ if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state))
+ {
+ memcpy(st->st_ph1_iv, st->st_new_iv, st->st_new_iv_len);
+ st->st_ph1_iv_len = st->st_new_iv_len;
+
+ /* backup new_iv */
+ new_iv_len = st->st_new_iv_len;
+ passert(new_iv_len <= MAX_DIGEST_LEN)
+ memcpy(new_iv, st->st_new_iv, new_iv_len);
+ restore_iv = TRUE;
+ }
+ init_phase2_iv(st, &md->hdr.isa_msgid);
+ new_iv_set = TRUE;
- if (is_zero_cookie(md->hdr.isa_rcookie))
- {
- plog("Quick Mode message is invalid because"
- " it has a Responder Cookie of 0");
- SEND_NOTIFICATION(INVALID_COOKIE);
- return;
- }
+ from_state = STATE_INFO_PROTECTED;
+ }
+ else
+ {
+ if (st != NULL && IS_ISAKMP_ENCRYPTED(st->st_state))
+ {
+ loglog(RC_LOG_SERIOUS, "Informational Exchange message"
+ " must be encrypted");
+ /* XXX Could send notification back */
+ return;
+ }
+ from_state = STATE_INFO;
+ }
+ break;
- if (md->hdr.isa_msgid == MAINMODE_MSGID)
- {
- plog("Quick Mode message is invalid because"
- " it has a Message ID of 0");
- SEND_NOTIFICATION(INVALID_MESSAGE_ID);
- return;
- }
+ case ISAKMP_XCHG_QUICK: /* part of a Quick Mode exchange */
+ if (is_zero_cookie(md->hdr.isa_icookie))
+ {
+ plog("Quick Mode message is invalid because"
+ " it has an Initiator Cookie of 0");
+ SEND_NOTIFICATION(INVALID_COOKIE);
+ return;
+ }
- st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
- , &md->sender, md->hdr.isa_msgid);
+ if (is_zero_cookie(md->hdr.isa_rcookie))
+ {
+ plog("Quick Mode message is invalid because"
+ " it has a Responder Cookie of 0");
+ SEND_NOTIFICATION(INVALID_COOKIE);
+ return;
+ }
- if (st == NULL)
- {
- /* No appropriate Quick Mode state.
- * See if we have a Main Mode state.
- * ??? what if this is a duplicate of another message?
- */
- st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
- , &md->sender, MAINMODE_MSGID);
-
- if (st == NULL)
- {
- plog("Quick Mode message is for a non-existent (expired?)"
- " ISAKMP SA");
- /* XXX Could send notification back */
- return;
- }
+ if (md->hdr.isa_msgid == MAINMODE_MSGID)
+ {
+ plog("Quick Mode message is invalid because"
+ " it has a Message ID of 0");
+ SEND_NOTIFICATION(INVALID_MESSAGE_ID);
+ return;
+ }
- set_cur_state(st);
+ st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
+ , &md->sender, md->hdr.isa_msgid);
- if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state))
- {
- loglog(RC_LOG_SERIOUS, "Quick Mode message is unacceptable because"
- " it is for an incomplete ISAKMP SA");
- SEND_NOTIFICATION(PAYLOAD_MALFORMED /* XXX ? */);
- return;
- }
-
- /* only accept this new Quick Mode exchange if it has a unique message ID */
- if (!reserve_msgid(st, md->hdr.isa_msgid))
- {
- loglog(RC_LOG_SERIOUS, "Quick Mode I1 message is unacceptable because"
- " it uses a previously used Message ID 0x%08lx"
- " (perhaps this is a duplicated packet)"
- , (unsigned long) md->hdr.isa_msgid);
- SEND_NOTIFICATION(INVALID_MESSAGE_ID);
- return;
- }
+ if (st == NULL)
+ {
+ /* No appropriate Quick Mode state.
+ * See if we have a Main Mode state.
+ * ??? what if this is a duplicate of another message?
+ */
+ st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
+ , &md->sender, MAINMODE_MSGID);
- /* Quick Mode Initial IV */
- init_phase2_iv(st, &md->hdr.isa_msgid);
- new_iv_set = TRUE;
+ if (st == NULL)
+ {
+ plog("Quick Mode message is for a non-existent (expired?)"
+ " ISAKMP SA");
+ /* XXX Could send notification back */
+ return;
+ }
- from_state = STATE_QUICK_R0;
- }
- else
- {
- set_cur_state(st);
- from_state = st->st_state;
- }
+ set_cur_state(st);
- break;
+ if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state))
+ {
+ loglog(RC_LOG_SERIOUS, "Quick Mode message is unacceptable because"
+ " it is for an incomplete ISAKMP SA");
+ SEND_NOTIFICATION(PAYLOAD_MALFORMED /* XXX ? */);
+ return;
+ }
- case ISAKMP_XCHG_MODE_CFG:
- if (is_zero_cookie(md->hdr.isa_icookie))
- {
- plog("ModeCfg message is invalid because"
- " it has an Initiator Cookie of 0");
- /* XXX Could send notification back */
- return;
- }
+ /* only accept this new Quick Mode exchange if it has a unique message ID */
+ if (!reserve_msgid(st, md->hdr.isa_msgid))
+ {
+ loglog(RC_LOG_SERIOUS, "Quick Mode I1 message is unacceptable because"
+ " it uses a previously used Message ID 0x%08lx"
+ " (perhaps this is a duplicated packet)"
+ , (unsigned long) md->hdr.isa_msgid);
+ SEND_NOTIFICATION(INVALID_MESSAGE_ID);
+ return;
+ }
- if (is_zero_cookie(md->hdr.isa_rcookie))
- {
- plog("ModeCfg message is invalid because"
- " it has a Responder Cookie of 0");
- /* XXX Could send notification back */
- return;
- }
+ /* Quick Mode Initial IV */
+ init_phase2_iv(st, &md->hdr.isa_msgid);
+ new_iv_set = TRUE;
- if (md->hdr.isa_msgid == 0)
- {
- plog("ModeCfg message is invalid because"
- " it has a Message ID of 0");
- /* XXX Could send notification back */
- return;
- }
+ from_state = STATE_QUICK_R0;
+ }
+ else
+ {
+ set_cur_state(st);
+ from_state = st->st_state;
+ }
- st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
- , &md->sender, md->hdr.isa_msgid);
+ break;
- if (st == NULL)
- {
- bool has_xauth_policy;
-
- /* No appropriate ModeCfg state.
- * See if we have a Main Mode state.
- * ??? what if this is a duplicate of another message?
- */
- st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
- , &md->sender, 0);
-
- if (st == NULL)
- {
- plog("ModeCfg message is for a non-existent (expired?)"
- " ISAKMP SA");
- /* XXX Could send notification back */
- return;
- }
+ case ISAKMP_XCHG_MODE_CFG:
+ if (is_zero_cookie(md->hdr.isa_icookie))
+ {
+ plog("ModeCfg message is invalid because"
+ " it has an Initiator Cookie of 0");
+ /* XXX Could send notification back */
+ return;
+ }
+
+ if (is_zero_cookie(md->hdr.isa_rcookie))
+ {
+ plog("ModeCfg message is invalid because"
+ " it has a Responder Cookie of 0");
+ /* XXX Could send notification back */
+ return;
+ }
+
+ if (md->hdr.isa_msgid == 0)
+ {
+ plog("ModeCfg message is invalid because"
+ " it has a Message ID of 0");
+ /* XXX Could send notification back */
+ return;
+ }
- set_cur_state(st);
+ st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
+ , &md->sender, md->hdr.isa_msgid);
- /* the XAUTH_STATUS message might have a new msgid */
- if (st->st_state == STATE_XAUTH_I1)
- {
- init_phase2_iv(st, &md->hdr.isa_msgid);
- new_iv_set = TRUE;
- from_state = st->st_state;
+ if (st == NULL)
+ {
+ bool has_xauth_policy;
+
+ /* No appropriate ModeCfg state.
+ * See if we have a Main Mode state.
+ * ??? what if this is a duplicate of another message?
+ */
+ st = find_state(md->hdr.isa_icookie, md->hdr.isa_rcookie
+ , &md->sender, 0);
+
+ if (st == NULL)
+ {
+ plog("ModeCfg message is for a non-existent (expired?)"
+ " ISAKMP SA");
+ /* XXX Could send notification back */
+ return;
+ }
+
+ set_cur_state(st);
+
+ /* the XAUTH_STATUS message might have a new msgid */
+ if (st->st_state == STATE_XAUTH_I1)
+ {
+ init_phase2_iv(st, &md->hdr.isa_msgid);
+ new_iv_set = TRUE;
+ from_state = st->st_state;
+ break;
+ }
+
+ if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state))
+ {
+ loglog(RC_LOG_SERIOUS, "ModeCfg message is unacceptable because"
+ " it is for an incomplete ISAKMP SA (state=%s)"
+ , enum_name(&state_names, st->st_state));
+ /* XXX Could send notification back */
+ return;
+ }
+ init_phase2_iv(st, &md->hdr.isa_msgid);
+ new_iv_set = TRUE;
+
+ /*
+ * okay, now we have to figure out if we are receiving a bogus
+ * new message in an oustanding XAUTH server conversation
+ * (i.e. a reply to our challenge)
+ * (this occurs with some broken other implementations).
+ *
+ * or if receiving for the first time, an XAUTH challenge.
+ *
+ * or if we are getting a MODECFG request.
+ *
+ * we distinguish these states because we can not both be an
+ * XAUTH server and client, and our policy tells us which
+ * one we are.
+ *
+ * to complicate further, it is normal to start a new msgid
+ * when going from one state to another, or when restarting
+ * the challenge.
+ *
+ */
+
+ has_xauth_policy = (st->st_connection->policy
+ & (POLICY_XAUTH_RSASIG | POLICY_XAUTH_PSK))
+ != LEMPTY;
+
+ if (has_xauth_policy && !st->st_xauth.started
+ && IS_PHASE1(st->st_state))
+ {
+ from_state = STATE_XAUTH_I0;
+ }
+ else if (st->st_connection->spd.that.modecfg
+ && IS_PHASE1(st->st_state))
+ {
+ from_state = STATE_MODE_CFG_R0;
+ }
+ else if (st->st_connection->spd.this.modecfg
+ && IS_PHASE1(st->st_state))
+ {
+ from_state = STATE_MODE_CFG_I0;
+ }
+ else
+ {
+ /* XXX check if we are being a mode config server here */
+ plog("received ModeCfg message when in state %s, and we aren't mode config client"
+ , enum_name(&state_names, st->st_state));
+ return;
+ }
+ }
+ else
+ {
+ set_cur_state(st);
+ from_state = st->st_state;
+ }
break;
- }
-
- if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state))
- {
- loglog(RC_LOG_SERIOUS, "ModeCfg message is unacceptable because"
- " it is for an incomplete ISAKMP SA (state=%s)"
- , enum_name(&state_names, st->st_state));
- /* XXX Could send notification back */
- return;
- }
- init_phase2_iv(st, &md->hdr.isa_msgid);
- new_iv_set = TRUE;
-
- /*
- * okay, now we have to figure out if we are receiving a bogus
- * new message in an oustanding XAUTH server conversation
- * (i.e. a reply to our challenge)
- * (this occurs with some broken other implementations).
- *
- * or if receiving for the first time, an XAUTH challenge.
- *
- * or if we are getting a MODECFG request.
- *
- * we distinguish these states because we can not both be an
- * XAUTH server and client, and our policy tells us which
- * one we are.
- *
- * to complicate further, it is normal to start a new msgid
- * when going from one state to another, or when restarting
- * the challenge.
- *
- */
-
- has_xauth_policy = (st->st_connection->policy
- & (POLICY_XAUTH_RSASIG | POLICY_XAUTH_PSK))
- != LEMPTY;
-
- if (has_xauth_policy && !st->st_xauth.started
- && IS_PHASE1(st->st_state))
- {
- from_state = STATE_XAUTH_I0;
- }
- else if (st->st_connection->spd.that.modecfg
- && IS_PHASE1(st->st_state))
- {
- from_state = STATE_MODE_CFG_R0;
- }
- else if (st->st_connection->spd.this.modecfg
- && IS_PHASE1(st->st_state))
- {
- from_state = STATE_MODE_CFG_I0;
- }
- else
- {
- /* XXX check if we are being a mode config server here */
- plog("received ModeCfg message when in state %s, and we aren't mode config client"
- , enum_name(&state_names, st->st_state));
- return;
- }
- }
- else
- {
- set_cur_state(st);
- from_state = st->st_state;
- }
- break;
#ifdef NOTYET
- case ISAKMP_XCHG_NGRP:
- case ISAKMP_XCHG_ACK_INFO:
+ case ISAKMP_XCHG_NGRP:
+ case ISAKMP_XCHG_ACK_INFO:
#endif
- default:
- plog("unsupported exchange type %s in message"
- , enum_show(&exchange_names, md->hdr.isa_xchg));
- SEND_NOTIFICATION(UNSUPPORTED_EXCHANGE_TYPE);
- return;
- }
-
- /* We have found a from_state, and perhaps a state object.
- * If we need to build a new state object,
- * we wait until the packet has been sanity checked.
- */
-
- /* We don't support the Commit Flag. It is such a bad feature.
- * It isn't protected -- neither encrypted nor authenticated.
- * A man in the middle turns it on, leading to DoS.
- * We just ignore it, with a warning.
- * By placing the check here, we could easily add a policy bit
- * to a connection to suppress the warning. This might be useful
- * because the Commit Flag is expected from some peers.
- */
- if (md->hdr.isa_flags & ISAKMP_FLAG_COMMIT)
- {
- plog("IKE message has the Commit Flag set but Pluto doesn't implement this feature; ignoring flag");
- }
-
- /* Set smc to describe this state's properties.
- * Look up the appropriate microcode based on state and
- * possibly Oakley Auth type.
- */
- passert(STATE_IKE_FLOOR <= from_state && from_state <= STATE_IKE_ROOF);
- smc = ike_microcode_index[from_state - STATE_IKE_FLOOR];
-
- if (st != NULL)
- {
- u_int16_t auth;
-
- switch (st->st_oakley.auth)
- {
- case XAUTHInitPreShared:
- case XAUTHRespPreShared:
- auth = OAKLEY_PRESHARED_KEY;
- break;
- case XAUTHInitRSA:
- case XAUTHRespRSA:
- auth = OAKLEY_RSA_SIG;
- break;
default:
- auth = st->st_oakley.auth;
+ plog("unsupported exchange type %s in message"
+ , enum_show(&exchange_names, md->hdr.isa_xchg));
+ SEND_NOTIFICATION(UNSUPPORTED_EXCHANGE_TYPE);
+ return;
}
-
- while (!LHAS(smc->flags, auth))
+
+ /* We have found a from_state, and perhaps a state object.
+ * If we need to build a new state object,
+ * we wait until the packet has been sanity checked.
+ */
+
+ /* We don't support the Commit Flag. It is such a bad feature.
+ * It isn't protected -- neither encrypted nor authenticated.
+ * A man in the middle turns it on, leading to DoS.
+ * We just ignore it, with a warning.
+ * By placing the check here, we could easily add a policy bit
+ * to a connection to suppress the warning. This might be useful
+ * because the Commit Flag is expected from some peers.
+ */
+ if (md->hdr.isa_flags & ISAKMP_FLAG_COMMIT)
{
- smc++;
- passert(smc->state == from_state);
+ plog("IKE message has the Commit Flag set but Pluto doesn't implement this feature; ignoring flag");
}
- }
-
- /* Ignore a packet if the state has a suspended state transition
- * Probably a duplicated packet but the original packet is not yet
- * recorded in st->st_rpacket, so duplicate checking won't catch.
- * ??? Should the packet be recorded earlier to improve diagnosis?
- */
- if (st != NULL && st->st_suspended_md != NULL)
- {
- loglog(RC_LOG, "discarding packet received during DNS lookup in %s"
- , enum_name(&state_names, st->st_state));
- return;
- }
-
- /* Detect and handle duplicated packets.
- * This won't work for the initial packet of an exchange
- * because we won't have a state object to remember it.
- * If we are in a non-receiving state (terminal), and the preceding
- * state did transmit, then the duplicate may indicate that that
- * transmission wasn't received -- retransmit it.
- * Otherwise, just discard it.
- * ??? Notification packets are like exchanges -- I hope that
- * they are idempotent!
- */
- if (st != NULL
- && st->st_rpacket.ptr != NULL
- && st->st_rpacket.len == pbs_room(&md->packet_pbs)
- && memcmp(st->st_rpacket.ptr, md->packet_pbs.start, st->st_rpacket.len) == 0)
- {
- if (smc->flags & SMF_RETRANSMIT_ON_DUPLICATE)
+
+ /* Set smc to describe this state's properties.
+ * Look up the appropriate microcode based on state and
+ * possibly Oakley Auth type.
+ */
+ passert(STATE_IKE_FLOOR <= from_state && from_state <= STATE_IKE_ROOF);
+ smc = ike_microcode_index[from_state - STATE_IKE_FLOOR];
+
+ if (st != NULL)
{
- if (st->st_retransmit < MAXIMUM_RETRANSMISSIONS)
- {
- st->st_retransmit++;
- loglog(RC_RETRANSMISSION
- , "retransmitting in response to duplicate packet; already %s"
- , enum_name(&state_names, st->st_state));
- send_packet(st, "retransmit in response to duplicate");
- }
- else
- {
- loglog(RC_LOG_SERIOUS, "discarding duplicate packet -- exhausted retransmission; already %s"
- , enum_name(&state_names, st->st_state));
- }
+ u_int16_t auth;
+
+ switch (st->st_oakley.auth)
+ {
+ case XAUTHInitPreShared:
+ case XAUTHRespPreShared:
+ auth = OAKLEY_PRESHARED_KEY;
+ break;
+ case XAUTHInitRSA:
+ case XAUTHRespRSA:
+ auth = OAKLEY_RSA_SIG;
+ break;
+ default:
+ auth = st->st_oakley.auth;
+ }
+
+ while (!LHAS(smc->flags, auth))
+ {
+ smc++;
+ passert(smc->state == from_state);
+ }
}
- else
+
+ /* Ignore a packet if the state has a suspended state transition
+ * Probably a duplicated packet but the original packet is not yet
+ * recorded in st->st_rpacket, so duplicate checking won't catch.
+ * ??? Should the packet be recorded earlier to improve diagnosis?
+ */
+ if (st != NULL && st->st_suspended_md != NULL)
{
- loglog(RC_LOG_SERIOUS, "discarding duplicate packet; already %s"
- , enum_name(&state_names, st->st_state));
+ loglog(RC_LOG, "discarding packet received during DNS lookup in %s"
+ , enum_name(&state_names, st->st_state));
+ return;
}
- return;
- }
-
- if (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION)
- {
- DBG(DBG_CRYPT, DBG_log("received encrypted packet from %s:%u"
- , ip_str(&md->sender), (unsigned)md->sender_port));
- if (st == NULL)
+ /* Detect and handle duplicated packets.
+ * This won't work for the initial packet of an exchange
+ * because we won't have a state object to remember it.
+ * If we are in a non-receiving state (terminal), and the preceding
+ * state did transmit, then the duplicate may indicate that that
+ * transmission wasn't received -- retransmit it.
+ * Otherwise, just discard it.
+ * ??? Notification packets are like exchanges -- I hope that
+ * they are idempotent!
+ */
+ if (st != NULL
+ && st->st_rpacket.ptr != NULL
+ && st->st_rpacket.len == pbs_room(&md->packet_pbs)
+ && memeq(st->st_rpacket.ptr, md->packet_pbs.start, st->st_rpacket.len))
{
- plog("discarding encrypted message for an unknown ISAKMP SA");
- SEND_NOTIFICATION(PAYLOAD_MALFORMED /* XXX ? */);
- return;
+ if (smc->flags & SMF_RETRANSMIT_ON_DUPLICATE)
+ {
+ if (st->st_retransmit < MAXIMUM_RETRANSMISSIONS)
+ {
+ st->st_retransmit++;
+ loglog(RC_RETRANSMISSION
+ , "retransmitting in response to duplicate packet; already %s"
+ , enum_name(&state_names, st->st_state));
+ send_packet(st, "retransmit in response to duplicate");
+ }
+ else
+ {
+ loglog(RC_LOG_SERIOUS, "discarding duplicate packet -- exhausted retransmission; already %s"
+ , enum_name(&state_names, st->st_state));
+ }
+ }
+ else
+ {
+ loglog(RC_LOG_SERIOUS, "discarding duplicate packet; already %s"
+ , enum_name(&state_names, st->st_state));
+ }
+ return;
}
- if (st->st_skeyid_e.ptr == (u_char *) NULL)
+
+ if (md->hdr.isa_flags & ISAKMP_FLAG_ENCRYPTION)
{
- loglog(RC_LOG_SERIOUS, "discarding encrypted message"
- " because we haven't yet negotiated keying materiel");
- SEND_NOTIFICATION(INVALID_FLAGS);
- return;
- }
+ DBG(DBG_CRYPT, DBG_log("received encrypted packet from %s:%u"
+ , ip_str(&md->sender), (unsigned)md->sender_port));
- /* Mark as encrypted */
- md->encrypted = TRUE;
+ if (st == NULL)
+ {
+ plog("discarding encrypted message for an unknown ISAKMP SA");
+ 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");
+ SEND_NOTIFICATION(INVALID_FLAGS);
+ return;
+ }
- DBG(DBG_CRYPT, DBG_log("decrypting %u bytes using algorithm %s"
- , (unsigned) pbs_left(&md->message_pbs)
- , enum_show(&oakley_enc_names, st->st_oakley.encrypt)));
+ /* Mark as encrypted */
+ md->encrypted = TRUE;
- /* do the specified decryption
- *
- * IV is from st->st_iv or (if new_iv_set) st->st_new_iv.
- * The new IV is placed in st->st_new_iv
- *
- * See RFC 2409 "IKE" Appendix B
- *
- * XXX The IV should only be updated really if the packet
- * is successfully processed.
- * We should keep this value, check for a success return
- * value from the parsing routines and then replace.
- *
- * Each post phase 1 exchange generates IVs from
- * the last phase 1 block, not the last block sent.
- */
- {
- const struct encrypt_desc *e = st->st_oakley.encrypter;
+ DBG(DBG_CRYPT, DBG_log("decrypting %u bytes using algorithm %s"
+ , (unsigned) pbs_left(&md->message_pbs)
+ , enum_show(&oakley_enc_names, st->st_oakley.encrypt)));
- if (pbs_left(&md->message_pbs) % e->enc_blocksize != 0)
- {
- loglog(RC_LOG_SERIOUS, "malformed message: not a multiple of encryption blocksize");
- SEND_NOTIFICATION(PAYLOAD_MALFORMED);
- return;
- }
-
- /* XXX Detect weak keys */
-
- /* grab a copy of raw packet (for duplicate packet detection) */
- clonetochunk(md->raw_packet, md->packet_pbs.start
- , pbs_room(&md->packet_pbs), "raw packet");
-
- /* Decrypt everything after header */
- if (!new_iv_set)
- {
- /* use old IV */
- passert(st->st_iv_len <= sizeof(st->st_new_iv));
- st->st_new_iv_len = st->st_iv_len;
- memcpy(st->st_new_iv, st->st_iv, st->st_new_iv_len);
- }
- crypto_cbc_encrypt(e, FALSE, md->message_pbs.cur,
- pbs_left(&md->message_pbs) , st);
- if (restore_iv)
- {
- memcpy(st->st_new_iv, new_iv, new_iv_len);
- st->st_new_iv_len = new_iv_len;
- }
- }
+ /* do the specified decryption
+ *
+ * IV is from st->st_iv or (if new_iv_set) st->st_new_iv.
+ * The new IV is placed in st->st_new_iv
+ *
+ * See RFC 2409 "IKE" Appendix B
+ *
+ * XXX The IV should only be updated really if the packet
+ * is successfully processed.
+ * We should keep this value, check for a success return
+ * value from the parsing routines and then replace.
+ *
+ * Each post phase 1 exchange generates IVs from
+ * the last phase 1 block, not the last block sent.
+ */
+ {
+ size_t crypter_block_size;
+ encryption_algorithm_t enc_alg;
+ crypter_t *crypter;
+ chunk_t data, iv;
+ char *new_iv;
+
+ enc_alg = oakley_to_encryption_algorithm(st->st_oakley.encrypt);
+ crypter = lib->crypto->create_crypter(lib->crypto, enc_alg, st->st_enc_key.len);
+ crypter_block_size = crypter->get_block_size(crypter);
+
+ if (pbs_left(&md->message_pbs) % crypter_block_size != 0)
+ {
+ loglog(RC_LOG_SERIOUS, "malformed message: not a multiple of encryption blocksize");
+ SEND_NOTIFICATION(PAYLOAD_MALFORMED);
+ return;
+ }
- DBG_cond_dump(DBG_CRYPT, "decrypted:\n", md->message_pbs.cur
- , md->message_pbs.roof - md->message_pbs.cur);
+ /* XXX Detect weak keys */
- DBG_cond_dump(DBG_CRYPT, "next IV:"
- , st->st_new_iv, st->st_new_iv_len);
- }
- else
- {
- /* packet was not encryped -- should it have been? */
+ /* grab a copy of raw packet (for duplicate packet detection) */
+ md->raw_packet = chunk_create(md->packet_pbs.start, pbs_room(&md->packet_pbs));
+ md->raw_packet = chunk_clone(md->raw_packet);
- if (smc->flags & SMF_INPUT_ENCRYPTED)
- {
- loglog(RC_LOG_SERIOUS, "packet rejected: should have been encrypted");
- SEND_NOTIFICATION(INVALID_FLAGS);
- return;
+ data = chunk_create(md->message_pbs.cur, pbs_left(&md->message_pbs));
+
+ /* Decrypt everything after header */
+ if (!new_iv_set)
+ {
+ /* use old IV */
+ passert(st->st_iv_len <= sizeof(st->st_new_iv));
+ st->st_new_iv_len = st->st_iv_len;
+ memcpy(st->st_new_iv, st->st_iv, st->st_new_iv_len);
+ }
+
+ /* form iv by truncation */
+ st->st_new_iv_len = crypter_block_size;
+ iv = chunk_create(st->st_new_iv, st->st_new_iv_len);
+ new_iv = alloca(crypter_block_size);
+ memcpy(new_iv, data.ptr + data.len - crypter_block_size,
+ crypter_block_size);
+
+ crypter->set_key(crypter, st->st_enc_key);
+ crypter->decrypt(crypter, data, iv, NULL);
+ crypter->destroy(crypter);
+
+ memcpy(st->st_new_iv, new_iv, crypter_block_size);
+ if (restore_iv)
+ {
+ memcpy(st->st_new_iv, new_iv, new_iv_len);
+ st->st_new_iv_len = new_iv_len;
+ }
+ }
+
+ DBG_cond_dump(DBG_CRYPT, "decrypted:\n", md->message_pbs.cur
+ , md->message_pbs.roof - md->message_pbs.cur);
+
+ DBG_cond_dump(DBG_CRYPT, "next IV:"
+ , st->st_new_iv, st->st_new_iv_len);
}
- }
-
- /* Digest the message.
- * Padding must be removed to make hashing work.
- * Padding comes from encryption (so this code must be after decryption).
- * Padding rules are described before the definition of
- * struct isakmp_hdr in packet.h.
- */
- {
- struct payload_digest *pd = md->digest;
- int np = md->hdr.isa_np;
- lset_t needed = smc->req_payloads;
- const char *excuse
- = LIN(SMF_PSK_AUTH | SMF_FIRST_ENCRYPTED_INPUT, smc->flags)
- ? "probable authentication failure (mismatch of preshared secrets?): "
- : "";
-
- while (np != ISAKMP_NEXT_NONE)
+ else
{
- struct_desc *sd = np < ISAKMP_NEXT_ROOF? payload_descs[np] : NULL;
+ /* packet was not encryped -- should it have been? */
- if (pd == &md->digest[PAYLIMIT])
- {
- loglog(RC_LOG_SERIOUS, "more than %d payloads in message; ignored", PAYLIMIT);
- SEND_NOTIFICATION(PAYLOAD_MALFORMED);
- return;
- }
-
- switch (np)
- {
- case ISAKMP_NEXT_NATD_RFC:
- case ISAKMP_NEXT_NATOA_RFC:
- if (!st || !(st->nat_traversal & NAT_T_WITH_RFC_VALUES))
- {
- /*
- * don't accept NAT-D/NAT-OA reloc directly in message, unless
- * we're using NAT-T RFC
- */
- sd = NULL;
- }
- break;
- }
-
- if (sd == NULL)
- {
- /* payload type is out of range or requires special handling */
- switch (np)
+ if (smc->flags & SMF_INPUT_ENCRYPTED)
{
- case ISAKMP_NEXT_ID:
- sd = IS_PHASE1(from_state)
- ? &isakmp_identification_desc : &isakmp_ipsec_identification_desc;
- break;
- case ISAKMP_NEXT_NATD_DRAFTS:
- np = ISAKMP_NEXT_NATD_RFC; /* NAT-D relocated */
- sd = payload_descs[np];
- break;
- case ISAKMP_NEXT_NATOA_DRAFTS:
- np = ISAKMP_NEXT_NATOA_RFC; /* NAT-OA relocated */
- sd = payload_descs[np];
- break;
- default:
- 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;
+ loglog(RC_LOG_SERIOUS, "packet rejected: should have been encrypted");
+ SEND_NOTIFICATION(INVALID_FLAGS);
+ return;
}
- }
-
- {
- lset_t s = LELEM(np);
+ }
- if (LDISJOINT(s
- , needed | smc->opt_payloads| LELEM(ISAKMP_NEXT_N) | LELEM(ISAKMP_NEXT_D)))
+ /* Digest the message.
+ * Padding must be removed to make hashing work.
+ * Padding comes from encryption (so this code must be after decryption).
+ * Padding rules are described before the definition of
+ * struct isakmp_hdr in packet.h.
+ */
+ {
+ struct payload_digest *pd = md->digest;
+ int np = md->hdr.isa_np;
+ lset_t needed = smc->req_payloads;
+ const char *excuse
+ = LIN(SMF_PSK_AUTH | SMF_FIRST_ENCRYPTED_INPUT, smc->flags)
+ ? "probable authentication failure (mismatch of preshared secrets?): "
+ : "";
+
+ while (np != ISAKMP_NEXT_NONE)
{
- loglog(RC_LOG_SERIOUS, "%smessage ignored because it "
- "contains an unexpected payload type (%s)"
- , excuse, enum_show(&payload_names, np));
- SEND_NOTIFICATION(INVALID_PAYLOAD_TYPE);
- return;
+ struct_desc *sd = np < ISAKMP_NEXT_ROOF? payload_descs[np] : NULL;
+
+ if (pd == &md->digest[PAYLIMIT])
+ {
+ loglog(RC_LOG_SERIOUS, "more than %d payloads in message; ignored", PAYLIMIT);
+ SEND_NOTIFICATION(PAYLOAD_MALFORMED);
+ return;
+ }
+
+ switch (np)
+ {
+ case ISAKMP_NEXT_NATD_RFC:
+ case ISAKMP_NEXT_NATOA_RFC:
+ if (!st || !(st->nat_traversal & NAT_T_WITH_RFC_VALUES))
+ {
+ /*
+ * don't accept NAT-D/NAT-OA reloc directly in message, unless
+ * we're using NAT-T RFC
+ */
+ sd = NULL;
+ }
+ break;
+ }
+
+ if (sd == NULL)
+ {
+ /* payload type is out of range or requires special handling */
+ switch (np)
+ {
+ case ISAKMP_NEXT_ID:
+ sd = IS_PHASE1(from_state)
+ ? &isakmp_identification_desc : &isakmp_ipsec_identification_desc;
+ break;
+ case ISAKMP_NEXT_NATD_DRAFTS:
+ np = ISAKMP_NEXT_NATD_RFC; /* NAT-D relocated */
+ sd = payload_descs[np];
+ break;
+ case ISAKMP_NEXT_NATOA_DRAFTS:
+ np = ISAKMP_NEXT_NATOA_RFC; /* NAT-OA relocated */
+ sd = payload_descs[np];
+ break;
+ default:
+ 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;
+ }
+ }
+
+ {
+ lset_t s = LELEM(np);
+
+ if (LDISJOINT(s
+ , needed | smc->opt_payloads| LELEM(ISAKMP_NEXT_N) | LELEM(ISAKMP_NEXT_D)))
+ {
+ loglog(RC_LOG_SERIOUS, "%smessage ignored because it "
+ "contains an unexpected payload type (%s)"
+ , excuse, enum_show(&payload_names, np));
+ SEND_NOTIFICATION(INVALID_PAYLOAD_TYPE);
+ return;
+ }
+ needed &= ~s;
+ }
+
+ if (!in_struct(&pd->payload, sd, &md->message_pbs, &pd->pbs))
+ {
+ loglog(RC_LOG_SERIOUS, "%smalformed payload in packet", excuse);
+ if (md->hdr.isa_xchg != ISAKMP_XCHG_INFO)
+ SEND_NOTIFICATION(PAYLOAD_MALFORMED);
+ return;
+ }
+
+ /* place this payload at the end of the chain for this type */
+ {
+ struct payload_digest **p;
+
+ for (p = &md->chain[np]; *p != NULL; p = &(*p)->next)
+ ;
+ *p = pd;
+ pd->next = NULL;
+ }
+
+ np = pd->payload.generic.isag_np;
+ pd++;
+
+ /* since we've digested one payload happily, it is probably
+ * the case that any decryption worked. So we will not suggest
+ * encryption failure as an excuse for subsequent payload
+ * problems.
+ */
+ excuse = "";
}
- needed &= ~s;
- }
-
- if (!in_struct(&pd->payload, sd, &md->message_pbs, &pd->pbs))
- {
- loglog(RC_LOG_SERIOUS, "%smalformed payload in packet", excuse);
- if (md->hdr.isa_xchg != ISAKMP_XCHG_INFO)
- SEND_NOTIFICATION(PAYLOAD_MALFORMED);
- return;
- }
-
- /* place this payload at the end of the chain for this type */
- {
- struct payload_digest **p;
-
- for (p = &md->chain[np]; *p != NULL; p = &(*p)->next)
- ;
- *p = pd;
- pd->next = NULL;
- }
-
- np = pd->payload.generic.isag_np;
- pd++;
-
- /* since we've digested one payload happily, it is probably
- * the case that any decryption worked. So we will not suggest
- * encryption failure as an excuse for subsequent payload
- * problems.
- */
- excuse = "";
- }
- md->digest_roof = pd;
+ md->digest_roof = pd;
- DBG(DBG_PARSING,
- if (pbs_left(&md->message_pbs) != 0)
- DBG_log("removing %d bytes of padding", (int) pbs_left(&md->message_pbs)));
+ DBG(DBG_PARSING,
+ if (pbs_left(&md->message_pbs) != 0)
+ DBG_log("removing %d bytes of padding", (int) pbs_left(&md->message_pbs)));
- md->message_pbs.roof = md->message_pbs.cur;
+ md->message_pbs.roof = md->message_pbs.cur;
- /* check that all mandatory payloads appeared */
+ /* check that all mandatory payloads appeared */
- if (needed != 0)
- {
- 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;
+ if (needed != 0)
+ {
+ 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;
+ }
}
- }
- /* more sanity checking: enforce most ordering constraints */
+ /* more sanity checking: enforce most ordering constraints */
- if (IS_PHASE1(from_state))
- {
- /* rfc2409: The Internet Key Exchange (IKE), 5 Exchanges:
- * "The SA payload MUST precede all other payloads in a phase 1 exchange."
- */
- if (md->chain[ISAKMP_NEXT_SA] != NULL
- && md->hdr.isa_np != ISAKMP_NEXT_SA)
+ if (IS_PHASE1(from_state))
{
- loglog(RC_LOG_SERIOUS, "malformed Phase 1 message: does not start with an SA payload");
- SEND_NOTIFICATION(PAYLOAD_MALFORMED);
- return;
+ /* rfc2409: The Internet Key Exchange (IKE), 5 Exchanges:
+ * "The SA payload MUST precede all other payloads in a phase 1 exchange."
+ */
+ if (md->chain[ISAKMP_NEXT_SA] != NULL
+ && 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;
+ }
}
- }
- else if (IS_QUICK(from_state))
- {
- /* rfc2409: The Internet Key Exchange (IKE), 5.5 Phase 2 - Quick Mode
- *
- * "In Quick Mode, a HASH payload MUST immediately follow the ISAKMP
- * header and a SA payload MUST immediately follow the HASH."
- * [NOTE: there may be more than one SA payload, so this is not
- * totally reasonable. Probably all SAs should be so constrained.]
- *
- * "If ISAKMP is acting as a client negotiator on behalf of another
- * party, the identities of the parties MUST be passed as IDci and
- * then IDcr."
- *
- * "With the exception of the HASH, SA, and the optional ID payloads,
- * there are no payload ordering restrictions on Quick Mode."
- */
-
- if (md->hdr.isa_np != ISAKMP_NEXT_HASH)
+ else if (IS_QUICK(from_state))
{
- loglog(RC_LOG_SERIOUS, "malformed Quick Mode message: does not start with a HASH payload");
- SEND_NOTIFICATION(PAYLOAD_MALFORMED);
- return;
- }
+ /* rfc2409: The Internet Key Exchange (IKE), 5.5 Phase 2 - Quick Mode
+ *
+ * "In Quick Mode, a HASH payload MUST immediately follow the ISAKMP
+ * header and a SA payload MUST immediately follow the HASH."
+ * [NOTE: there may be more than one SA payload, so this is not
+ * totally reasonable. Probably all SAs should be so constrained.]
+ *
+ * "If ISAKMP is acting as a client negotiator on behalf of another
+ * party, the identities of the parties MUST be passed as IDci and
+ * then IDcr."
+ *
+ * "With the exception of the HASH, SA, and the optional ID payloads,
+ * there are no payload ordering restrictions on Quick Mode."
+ */
- {
- struct payload_digest *p;
- int i;
+ 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;
+ }
- for (p = md->chain[ISAKMP_NEXT_SA], i = 1; p != NULL
- ; p = p->next, i++)
- {
- if (p != &md->digest[i])
{
- loglog(RC_LOG_SERIOUS, "malformed Quick Mode message: SA payload is in wrong position");
- SEND_NOTIFICATION(PAYLOAD_MALFORMED);
- return;
+ struct payload_digest *p;
+ int i;
+
+ for (p = md->chain[ISAKMP_NEXT_SA], i = 1; p != NULL
+ ; p = p->next, i++)
+ {
+ if (p != &md->digest[i])
+ {
+ loglog(RC_LOG_SERIOUS, "malformed Quick Mode message: SA payload is in wrong position");
+ SEND_NOTIFICATION(PAYLOAD_MALFORMED);
+ return;
+ }
+ }
+ }
+
+ /* rfc2409: The Internet Key Exchange (IKE), 5.5 Phase 2 - Quick Mode:
+ * "If ISAKMP is acting as a client negotiator on behalf of another
+ * party, the identities of the parties MUST be passed as IDci and
+ * then IDcr."
+ */
+ {
+ struct payload_digest *id = md->chain[ISAKMP_NEXT_ID];
+
+ if (id != NULL)
+ {
+ if (id->next == NULL || id->next->next != NULL)
+ {
+ 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;
+ }
+ }
}
- }
}
- /* rfc2409: The Internet Key Exchange (IKE), 5.5 Phase 2 - Quick Mode:
- * "If ISAKMP is acting as a client negotiator on behalf of another
- * party, the identities of the parties MUST be passed as IDci and
- * then IDcr."
+ /* Ignore payloads that we don't handle:
+ * Delete, Notification, VendorID
*/
+ /* XXX Handle deletions */
+ /* XXX Handle Notifications */
+ /* XXX Handle VID payloads */
{
- struct payload_digest *id = md->chain[ISAKMP_NEXT_ID];
+ struct payload_digest *p;
- if (id != NULL)
- {
- if (id->next == NULL || id->next->next != NULL)
+ for (p = md->chain[ISAKMP_NEXT_N]; p != NULL; p = p->next)
{
- 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 (p->payload.notification.isan_type != R_U_THERE
+ && p->payload.notification.isan_type != R_U_THERE_ACK)
+ {
+ loglog(RC_LOG_SERIOUS, "ignoring informational payload, type %s"
+ , enum_show(&notification_names, p->payload.notification.isan_type));
+ }
+ DBG_cond_dump(DBG_PARSING, "info:", p->pbs.cur, pbs_left(&p->pbs));
}
- if (id+1 != id->next)
+
+ for (p = md->chain[ISAKMP_NEXT_D]; p != NULL; p = p->next)
{
- loglog(RC_LOG_SERIOUS, "malformed Quick Mode message:"
- " the ID payloads are not adjacent");
- SEND_NOTIFICATION(PAYLOAD_MALFORMED);
- return;
+ accept_delete(st, md, p);
+ DBG_cond_dump(DBG_PARSING, "del:", p->pbs.cur, pbs_left(&p->pbs));
}
- }
- }
- }
-
- /* Ignore payloads that we don't handle:
- * Delete, Notification, VendorID
- */
- /* XXX Handle deletions */
- /* XXX Handle Notifications */
- /* XXX Handle VID payloads */
- {
- struct payload_digest *p;
-
- for (p = md->chain[ISAKMP_NEXT_N]; p != NULL; p = p->next)
- {
- if (p->payload.notification.isan_type != R_U_THERE
- && p->payload.notification.isan_type != R_U_THERE_ACK)
- {
- loglog(RC_LOG_SERIOUS, "ignoring informational payload, type %s"
- , enum_show(&notification_names, p->payload.notification.isan_type));
- }
- DBG_cond_dump(DBG_PARSING, "info:", p->pbs.cur, pbs_left(&p->pbs));
- }
-
- for (p = md->chain[ISAKMP_NEXT_D]; p != NULL; p = p->next)
- {
- accept_delete(st, md, p);
- DBG_cond_dump(DBG_PARSING, "del:", p->pbs.cur, pbs_left(&p->pbs));
- }
- for (p = md->chain[ISAKMP_NEXT_VID]; p != NULL; p = p->next)
- {
- handle_vendorid(md, p->pbs.cur, pbs_left(&p->pbs));
+ for (p = md->chain[ISAKMP_NEXT_VID]; p != NULL; p = p->next)
+ {
+ handle_vendorid(md, p->pbs.cur, pbs_left(&p->pbs));
+ }
}
- }
- md->from_state = from_state;
- md->smc = smc;
- md->st = st;
+ md->from_state = from_state;
+ md->smc = smc;
+ md->st = st;
- /* possibly fill in hdr */
- if (smc->first_out_payload != ISAKMP_NEXT_NONE)
- echo_hdr(md, (smc->flags & SMF_OUTPUT_ENCRYPTED) != 0
- , smc->first_out_payload);
+ /* possibly fill in hdr */
+ if (smc->first_out_payload != ISAKMP_NEXT_NONE)
+ echo_hdr(md, (smc->flags & SMF_OUTPUT_ENCRYPTED) != 0
+ , smc->first_out_payload);
- complete_state_transition(mdp, smc->processor(md));
+ complete_state_transition(mdp, smc->processor(md));
}
/* complete job started by the state-specific state transition function */
@@ -2094,406 +2116,406 @@ process_packet(struct msg_digest **mdp)
void
complete_state_transition(struct msg_digest **mdp, stf_status result)
{
- bool has_xauth_policy;
- bool is_xauth_server;
- struct msg_digest *md = *mdp;
- const struct state_microcode *smc = md->smc;
- enum state_kind from_state = md->from_state;
- struct state *st;
-
- cur_state = st = md->st; /* might have changed */
-
- /* If state has DPD support, import it */
- if (st && md->dpd)
- st->st_dpd = TRUE;
-
- switch (result)
- {
- case STF_IGNORE:
- break;
-
- case STF_SUSPEND:
- /* the stf didn't complete its job: don't relase md */
- *mdp = NULL;
- break;
-
- case STF_OK:
- /* advance the state */
- st->st_state = smc->next_state;
-
- /* Delete previous retransmission event.
- * New event will be scheduled below.
- */
- delete_event(st);
-
- /* replace previous receive packet with latest */
-
- pfreeany(st->st_rpacket.ptr);
-
- if (md->encrypted)
- {
- /* if encrypted, duplication already done */
- st->st_rpacket = md->raw_packet;
- md->raw_packet.ptr = NULL;
- }
- else
- {
- clonetochunk(st->st_rpacket
- , md->packet_pbs.start
- , pbs_room(&md->packet_pbs), "raw packet");
- }
-
- /* free previous transmit packet */
- freeanychunk(st->st_tpacket);
-
- /* if requested, send the new reply packet */
- if (smc->flags & SMF_REPLY)
- {
- close_output_pbs(&md->reply); /* good form, but actually a no-op */
-
- clonetochunk(st->st_tpacket, md->reply.start
- , pbs_offset(&md->reply), "reply packet");
-
- if (nat_traversal_enabled)
- nat_traversal_change_port_lookup(md, md->st);
-
- /* actually send the packet
- * Note: this is a great place to implement "impairments"
- * for testing purposes. Suppress or duplicate the
- * send_packet call depending on st->st_state.
- */
- send_packet(st, enum_name(&state_names, from_state));
- }
+ bool has_xauth_policy;
+ bool is_xauth_server;
+ struct msg_digest *md = *mdp;
+ const struct state_microcode *smc = md->smc;
+ enum state_kind from_state = md->from_state;
+ struct state *st;
- /* Schedule for whatever timeout is specified */
- {
- time_t delay = UNDEFINED_TIME;
- enum event_type kind = smc->timeout_event;
- bool agreed_time = FALSE;
- struct connection *c = st->st_connection;
+ cur_state = st = md->st; /* might have changed */
- switch (kind)
- {
- case EVENT_RETRANSMIT: /* Retransmit packet */
- delay = EVENT_RETRANSMIT_DELAY_0;
- break;
-
- case EVENT_SA_REPLACE: /* SA replacement event */
- if (IS_PHASE1(st->st_state))
- {
- /* Note: we will defer to the "negotiated" (dictated)
- * lifetime if we are POLICY_DONT_REKEY.
- * This allows the other side to dictate
- * a time we would not otherwise accept
- * but it prevents us from having to initiate
- * rekeying. The negative consequences seem
- * minor.
+ /* If state has DPD support, import it */
+ if (st && md->dpd)
+ st->st_dpd = TRUE;
+
+ switch (result)
+ {
+ case STF_IGNORE:
+ break;
+
+ case STF_SUSPEND:
+ /* the stf didn't complete its job: don't relase md */
+ *mdp = NULL;
+ break;
+
+ case STF_OK:
+ /* advance the state */
+ st->st_state = smc->next_state;
+
+ /* Delete previous retransmission event.
+ * New event will be scheduled below.
*/
- delay = c->sa_ike_life_seconds;
- if ((c->policy & POLICY_DONT_REKEY)
- || delay >= st->st_oakley.life_seconds)
+ delete_event(st);
+
+ /* replace previous receive packet with latest */
+
+ free(st->st_rpacket.ptr);
+
+ if (md->encrypted)
{
- agreed_time = TRUE;
- delay = st->st_oakley.life_seconds;
+ /* if encrypted, duplication already done */
+ st->st_rpacket = md->raw_packet;
+ md->raw_packet.ptr = NULL;
}
- }
- else
- {
- /* Delay is min of up to four things:
- * each can limit the lifetime.
- */
- delay = c->sa_ipsec_life_seconds;
- if (st->st_ah.present
- && delay >= st->st_ah.attrs.life_seconds)
+ else
{
- agreed_time = TRUE;
- delay = st->st_ah.attrs.life_seconds;
+ st->st_rpacket = chunk_create(md->packet_pbs.start,
+ pbs_room(&md->packet_pbs));
+ st->st_rpacket = chunk_clone(st->st_rpacket);
}
- if (st->st_esp.present
- && delay >= st->st_esp.attrs.life_seconds)
+
+ /* free previous transmit packet */
+ chunk_free(&st->st_tpacket);
+
+ /* if requested, send the new reply packet */
+ if (smc->flags & SMF_REPLY)
{
- agreed_time = TRUE;
- delay = st->st_esp.attrs.life_seconds;
+ close_output_pbs(&md->reply); /* good form, but actually a no-op */
+
+ st->st_tpacket = chunk_create(md->reply.start, pbs_offset(&md->reply));
+ st->st_tpacket = chunk_clone(st->st_tpacket);
+
+ if (nat_traversal_enabled)
+ nat_traversal_change_port_lookup(md, md->st);
+
+ /* actually send the packet
+ * Note: this is a great place to implement "impairments"
+ * for testing purposes. Suppress or duplicate the
+ * send_packet call depending on st->st_state.
+ */
+ send_packet(st, enum_name(&state_names, from_state));
}
- if (st->st_ipcomp.present
- && delay >= st->st_ipcomp.attrs.life_seconds)
+
+ /* Schedule for whatever timeout is specified */
{
- agreed_time = TRUE;
- delay = st->st_ipcomp.attrs.life_seconds;
+ time_t delay = UNDEFINED_TIME;
+ enum event_type kind = smc->timeout_event;
+ bool agreed_time = FALSE;
+ struct connection *c = st->st_connection;
+
+ switch (kind)
+ {
+ case EVENT_RETRANSMIT: /* Retransmit packet */
+ delay = EVENT_RETRANSMIT_DELAY_0;
+ break;
+
+ case EVENT_SA_REPLACE: /* SA replacement event */
+ if (IS_PHASE1(st->st_state))
+ {
+ /* Note: we will defer to the "negotiated" (dictated)
+ * lifetime if we are POLICY_DONT_REKEY.
+ * This allows the other side to dictate
+ * a time we would not otherwise accept
+ * but it prevents us from having to initiate
+ * rekeying. The negative consequences seem
+ * minor.
+ */
+ delay = c->sa_ike_life_seconds;
+ if ((c->policy & POLICY_DONT_REKEY)
+ || delay >= st->st_oakley.life_seconds)
+ {
+ agreed_time = TRUE;
+ delay = st->st_oakley.life_seconds;
+ }
+ }
+ else
+ {
+ /* Delay is min of up to four things:
+ * each can limit the lifetime.
+ */
+ delay = c->sa_ipsec_life_seconds;
+ if (st->st_ah.present
+ && delay >= st->st_ah.attrs.life_seconds)
+ {
+ agreed_time = TRUE;
+ delay = st->st_ah.attrs.life_seconds;
+ }
+ if (st->st_esp.present
+ && delay >= st->st_esp.attrs.life_seconds)
+ {
+ agreed_time = TRUE;
+ delay = st->st_esp.attrs.life_seconds;
+ }
+ if (st->st_ipcomp.present
+ && delay >= st->st_ipcomp.attrs.life_seconds)
+ {
+ agreed_time = TRUE;
+ delay = st->st_ipcomp.attrs.life_seconds;
+ }
+ }
+
+ /* By default, we plan to rekey.
+ *
+ * If there isn't enough time to rekey, plan to
+ * expire.
+ *
+ * If we are --dontrekey, a lot more rules apply.
+ * If we are the Initiator, use REPLACE_IF_USED.
+ * If we are the Responder, and the dictated time
+ * was unacceptable (too large), plan to REPLACE
+ * (the only way to ratchet down the time).
+ * If we are the Responder, and the dictated time
+ * is acceptable, plan to EXPIRE.
+ *
+ * Important policy lies buried here.
+ * For example, we favour the initiator over the
+ * responder by making the initiator start rekeying
+ * sooner. Also, fuzz is only added to the
+ * initiator's margin.
+ *
+ * Note: for ISAKMP SA, we let the negotiated
+ * time stand (implemented by earlier logic).
+ */
+ if (agreed_time
+ && (c->policy & POLICY_DONT_REKEY))
+ {
+ kind = (smc->flags & SMF_INITIATOR)
+ ? EVENT_SA_REPLACE_IF_USED
+ : EVENT_SA_EXPIRE;
+ }
+ if (kind != EVENT_SA_EXPIRE)
+ {
+ unsigned long marg = c->sa_rekey_margin;
+
+ if (smc->flags & SMF_INITIATOR)
+ marg += marg
+ * c->sa_rekey_fuzz / 100.E0
+ * (rand() / (RAND_MAX + 1.E0));
+ else
+ marg /= 2;
+
+ if ((unsigned long)delay > marg)
+ {
+ delay -= marg;
+ st->st_margin = marg;
+ }
+ else
+ {
+ kind = EVENT_SA_EXPIRE;
+ }
+ }
+ break;
+
+ case EVENT_NULL: /* non-event */
+ case EVENT_REINIT_SECRET: /* Refresh cookie secret */
+ default:
+ bad_case(kind);
+ }
+ event_schedule(kind, delay, st);
}
- }
-
- /* By default, we plan to rekey.
- *
- * If there isn't enough time to rekey, plan to
- * expire.
- *
- * If we are --dontrekey, a lot more rules apply.
- * If we are the Initiator, use REPLACE_IF_USED.
- * If we are the Responder, and the dictated time
- * was unacceptable (too large), plan to REPLACE
- * (the only way to ratchet down the time).
- * If we are the Responder, and the dictated time
- * is acceptable, plan to EXPIRE.
- *
- * Important policy lies buried here.
- * For example, we favour the initiator over the
- * responder by making the initiator start rekeying
- * sooner. Also, fuzz is only added to the
- * initiator's margin.
- *
- * Note: for ISAKMP SA, we let the negotiated
- * time stand (implemented by earlier logic).
- */
- if (agreed_time
- && (c->policy & POLICY_DONT_REKEY))
- {
- kind = (smc->flags & SMF_INITIATOR)
- ? EVENT_SA_REPLACE_IF_USED
- : EVENT_SA_EXPIRE;
- }
- if (kind != EVENT_SA_EXPIRE)
- {
- unsigned long marg = c->sa_rekey_margin;
-
- if (smc->flags & SMF_INITIATOR)
- marg += marg
- * c->sa_rekey_fuzz / 100.E0
- * (rand() / (RAND_MAX + 1.E0));
- else
- marg /= 2;
- if ((unsigned long)delay > marg)
+ /* tell whack and log of progress */
{
- delay -= marg;
- st->st_margin = marg;
+ const char *story = state_story[st->st_state - STATE_MAIN_R0];
+ enum rc_type w = RC_NEW_STATE + st->st_state;
+ char sadetails[128];
+
+ sadetails[0]='\0';
+
+ if (IS_IPSEC_SA_ESTABLISHED(st->st_state))
+ {
+ char *b = sadetails;
+ const char *ini = " {";
+ const char *fin = "";
+
+ /* -1 is to leave space for "fin" */
+
+ if (st->st_esp.present)
+ {
+ snprintf(b, sizeof(sadetails)-(b-sadetails)-1
+ , "%sESP=>0x%08x <0x%08x"
+ , ini
+ , ntohl(st->st_esp.attrs.spi)
+ , ntohl(st->st_esp.our_spi));
+ ini = " ";
+ fin = "}";
+ }
+ /* advance b to end of string */
+ b = b + strlen(b);
+
+ if (st->st_ah.present)
+ {
+ snprintf(b, sizeof(sadetails)-(b-sadetails)-1
+ , "%sAH=>0x%08x <0x%08x"
+ , ini
+ , ntohl(st->st_ah.attrs.spi)
+ , ntohl(st->st_ah.our_spi));
+ ini = " ";
+ fin = "}";
+ }
+ /* advance b to end of string */
+ b = b + strlen(b);
+
+ if (st->st_ipcomp.present)
+ {
+ snprintf(b, sizeof(sadetails)-(b-sadetails)-1
+ , "%sIPCOMP=>0x%08x <0x%08x"
+ , ini
+ , ntohl(st->st_ipcomp.attrs.spi)
+ , ntohl(st->st_ipcomp.our_spi));
+ ini = " ";
+ fin = "}";
+ }
+ /* advance b to end of string */
+ b = b + strlen(b);
+
+ if (st->nat_traversal)
+ {
+ char oa[ADDRTOT_BUF];
+ addrtot(&st->nat_oa, 0, oa, sizeof(oa));
+ snprintf(b, sizeof(sadetails)-(b-sadetails)-1
+ , "%sNATOA=%s"
+ , ini, oa);
+ ini = " ";
+ fin = "}";
+ }
+
+ /* advance b to end of string */
+ b = b + strlen(b);
+
+ if (st->st_dpd)
+ {
+ snprintf(b, sizeof(sadetails)-(b-sadetails)-1
+ , "%sDPD"
+ , ini);
+ ini = " ";
+ fin = "}";
+ }
+
+ strcat(b, fin);
+ }
+
+ if (IS_ISAKMP_SA_ESTABLISHED(st->st_state)
+ || IS_IPSEC_SA_ESTABLISHED(st->st_state))
+ {
+ /* log our success */
+ plog("%s%s", story, sadetails);
+ w = RC_SUCCESS;
+ }
+
+ /* tell whack our progress */
+ whack_log(w
+ , "%s: %s%s"
+ , enum_name(&state_names, st->st_state)
+ , story, sadetails);
}
- else
+
+ has_xauth_policy = (st->st_connection->policy
+ & (POLICY_XAUTH_RSASIG | POLICY_XAUTH_PSK))
+ != LEMPTY;
+ is_xauth_server = (st->st_connection->policy
+ & POLICY_XAUTH_SERVER)
+ != LEMPTY;
+
+ /* Should we start XAUTH as a server */
+ if (has_xauth_policy && is_xauth_server
+ && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
+ && !st->st_xauth.started)
{
- kind = EVENT_SA_EXPIRE;
+ DBG(DBG_CONTROL,
+ DBG_log("starting XAUTH server")
+ )
+ xauth_send_request(st);
+ break;
}
- }
- break;
- case EVENT_NULL: /* non-event */
- case EVENT_REINIT_SECRET: /* Refresh cookie secret */
- default:
- bad_case(kind);
- }
- event_schedule(kind, delay, st);
- }
-
- /* tell whack and log of progress */
- {
- const char *story = state_story[st->st_state - STATE_MAIN_R0];
- enum rc_type w = RC_NEW_STATE + st->st_state;
- char sadetails[128];
-
- sadetails[0]='\0';
+ /* Wait for XAUTH request from server */
+ if (has_xauth_policy && !is_xauth_server
+ && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
+ && !st->st_xauth.started)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("waiting for XAUTH request from server")
+ )
+ break;
+ }
- if (IS_IPSEC_SA_ESTABLISHED(st->st_state))
- {
- char *b = sadetails;
- const char *ini = " {";
- const char *fin = "";
-
- /* -1 is to leave space for "fin" */
-
- if (st->st_esp.present)
- {
- snprintf(b, sizeof(sadetails)-(b-sadetails)-1
- , "%sESP=>0x%08x <0x%08x"
- , ini
- , ntohl(st->st_esp.attrs.spi)
- , ntohl(st->st_esp.our_spi));
- ini = " ";
- fin = "}";
- }
- /* advance b to end of string */
- b = b + strlen(b);
-
- if (st->st_ah.present)
- {
- snprintf(b, sizeof(sadetails)-(b-sadetails)-1
- , "%sAH=>0x%08x <0x%08x"
- , ini
- , ntohl(st->st_ah.attrs.spi)
- , ntohl(st->st_ah.our_spi));
- ini = " ";
- fin = "}";
- }
- /* advance b to end of string */
- b = b + strlen(b);
-
- if (st->st_ipcomp.present)
- {
- snprintf(b, sizeof(sadetails)-(b-sadetails)-1
- , "%sIPCOMP=>0x%08x <0x%08x"
- , ini
- , ntohl(st->st_ipcomp.attrs.spi)
- , ntohl(st->st_ipcomp.our_spi));
- ini = " ";
- fin = "}";
- }
- /* advance b to end of string */
- b = b + strlen(b);
-
- if (st->nat_traversal)
- {
- char oa[ADDRTOT_BUF];
- addrtot(&st->nat_oa, 0, oa, sizeof(oa));
- snprintf(b, sizeof(sadetails)-(b-sadetails)-1
- , "%sNATOA=%s"
- , ini, oa);
- ini = " ";
- fin = "}";
- }
-
- /* advance b to end of string */
- b = b + strlen(b);
-
- if (st->st_dpd)
- {
- snprintf(b, sizeof(sadetails)-(b-sadetails)-1
- , "%sDPD"
- , ini);
- ini = " ";
- fin = "}";
- }
-
- strcat(b, fin);
- }
+ /* Should we start ModeConfig as a client? */
+ if (st->st_connection->spd.this.modecfg
+ && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
+ && !(st->st_connection->policy & POLICY_MODECFG_PUSH)
+ && !st->st_modecfg.started)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("starting ModeCfg client in pull mode")
+ )
+ modecfg_send_request(st);
+ break;
+ }
- if (IS_ISAKMP_SA_ESTABLISHED(st->st_state)
- || IS_IPSEC_SA_ESTABLISHED(st->st_state))
- {
- /* log our success */
- plog("%s%s", story, sadetails);
- w = RC_SUCCESS;
- }
+ /* Should we start ModeConfig as a server? */
+ if (st->st_connection->spd.that.modecfg
+ && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
+ && !st->st_modecfg.started
+ && (st->st_connection->policy & POLICY_MODECFG_PUSH))
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("starting ModeCfg server in push mode")
+ )
+ modecfg_send_set(st);
+ break;
+ }
- /* tell whack our progress */
- whack_log(w
- , "%s: %s%s"
- , enum_name(&state_names, st->st_state)
- , story, sadetails);
- }
-
- has_xauth_policy = (st->st_connection->policy
- & (POLICY_XAUTH_RSASIG | POLICY_XAUTH_PSK))
- != LEMPTY;
- is_xauth_server = (st->st_connection->policy
- & POLICY_XAUTH_SERVER)
- != LEMPTY;
-
- /* Should we start XAUTH as a server */
- if (has_xauth_policy && is_xauth_server
- && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
- && !st->st_xauth.started)
- {
- DBG(DBG_CONTROL,
- DBG_log("starting XAUTH server")
- )
- xauth_send_request(st);
- break;
- }
-
- /* Wait for XAUTH request from server */
- if (has_xauth_policy && !is_xauth_server
- && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
- && !st->st_xauth.started)
- {
- DBG(DBG_CONTROL,
- DBG_log("waiting for XAUTH request from server")
- )
- break;
- }
-
- /* Should we start ModeConfig as a client? */
- if (st->st_connection->spd.this.modecfg
- && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
- && !(st->st_connection->policy & POLICY_MODECFG_PUSH)
- && !st->st_modecfg.started)
- {
- DBG(DBG_CONTROL,
- DBG_log("starting ModeCfg client in pull mode")
- )
- modecfg_send_request(st);
- break;
- }
-
- /* Should we start ModeConfig as a server? */
- if (st->st_connection->spd.that.modecfg
- && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
- && !st->st_modecfg.started
- && (st->st_connection->policy & POLICY_MODECFG_PUSH))
- {
- DBG(DBG_CONTROL,
- DBG_log("starting ModeCfg server in push mode")
- )
- modecfg_send_set(st);
- break;
- }
-
- /* Wait for ModeConfig set from server */
- if (st->st_connection->spd.this.modecfg
- && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
- && !st->st_modecfg.vars_set)
- {
- DBG(DBG_CONTROL,
- DBG_log("waiting for ModeCfg set from server")
- )
- break;
- }
+ /* Wait for ModeConfig set from server */
+ if (st->st_connection->spd.this.modecfg
+ && IS_ISAKMP_SA_ESTABLISHED(st->st_state)
+ && !st->st_modecfg.vars_set)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("waiting for ModeCfg set from server")
+ )
+ break;
+ }
- if (smc->flags & SMF_RELEASE_PENDING_P2)
- {
- /* Initiate any Quick Mode negotiations that
- * were waiting to piggyback on this Keying Channel.
- *
- * ??? there is a potential race condition
- * if we are the responder: the initial Phase 2
- * message might outrun the final Phase 1 message.
- * I think that retransmission will recover.
- */
- unpend(st);
- }
-
- if (IS_ISAKMP_SA_ESTABLISHED(st->st_state)
- || IS_IPSEC_SA_ESTABLISHED(st->st_state))
- release_whack(st);
- break;
-
- case STF_INTERNAL_ERROR:
- whack_log(RC_INTERNALERR + md->note
- , "%s: internal error"
- , enum_name(&state_names, st->st_state));
-
- DBG(DBG_CONTROL,
- DBG_log("state transition function for %s had internal error"
- , enum_name(&state_names, from_state)));
- break;
-
- default: /* a shortcut to STF_FAIL, setting md->note */
- passert(result > STF_FAIL);
- md->note = result - STF_FAIL;
- result = STF_FAIL;
- /* FALL THROUGH ... */
- case STF_FAIL:
- /* 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 == NULL)? STATE_MAIN_R0:st->st_state)
- , enum_name(&notification_names, md->note));
-
- SEND_NOTIFICATION(md->note);
-
- DBG(DBG_CONTROL,
- DBG_log("state transition function for %s failed: %s"
- , enum_name(&state_names, from_state)
- , enum_name(&notification_names, md->note)));
- break;
- }
+ if (smc->flags & SMF_RELEASE_PENDING_P2)
+ {
+ /* Initiate any Quick Mode negotiations that
+ * were waiting to piggyback on this Keying Channel.
+ *
+ * ??? there is a potential race condition
+ * if we are the responder: the initial Phase 2
+ * message might outrun the final Phase 1 message.
+ * I think that retransmission will recover.
+ */
+ unpend(st);
+ }
+
+ if (IS_ISAKMP_SA_ESTABLISHED(st->st_state)
+ || IS_IPSEC_SA_ESTABLISHED(st->st_state))
+ release_whack(st);
+ break;
+
+ case STF_INTERNAL_ERROR:
+ whack_log(RC_INTERNALERR + md->note
+ , "%s: internal error"
+ , enum_name(&state_names, st->st_state));
+
+ DBG(DBG_CONTROL,
+ DBG_log("state transition function for %s had internal error"
+ , enum_name(&state_names, from_state)));
+ break;
+
+ default: /* a shortcut to STF_FAIL, setting md->note */
+ passert(result > STF_FAIL);
+ md->note = result - STF_FAIL;
+ result = STF_FAIL;
+ /* FALL THROUGH ... */
+ case STF_FAIL:
+ /* 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 == NULL)? STATE_MAIN_R0:st->st_state)
+ , enum_name(&notification_names, md->note));
+
+ SEND_NOTIFICATION(md->note);
+
+ DBG(DBG_CONTROL,
+ DBG_log("state transition function for %s failed: %s"
+ , enum_name(&state_names, from_state)
+ , enum_name(&notification_names, md->note)));
+ break;
+ }
}