Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 20 Nov 2016 13:04:02 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r308885 - projects/ipsec/sys/netipsec
Message-ID:  <201611201304.uAKD42lb072170@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Sun Nov 20 13:04:02 2016
New Revision: 308885
URL: https://svnweb.freebsd.org/changeset/base/308885

Log:
  Modify ipsec_in_reject() and add ipsec_check_history() function.
  
  Also add net.inet.ipsec.check_policy_history sysctl to enable
  strict policy checking using history from mbuf tags.
  In ipsec_in_reject() do cache security policy in PCB if possible.
  Reflect changes in struct ipsecrequest. Use ipsec_check_history()
  when this check is enabled.
  Use security policy and transform index to determine required transform
  level in ipsec_get_reqlevel().

Modified:
  projects/ipsec/sys/netipsec/ipsec.c
  projects/ipsec/sys/netipsec/ipsec.h

Modified: projects/ipsec/sys/netipsec/ipsec.c
==============================================================================
--- projects/ipsec/sys/netipsec/ipsec.c	Sun Nov 20 12:25:14 2016	(r308884)
+++ projects/ipsec/sys/netipsec/ipsec.c	Sun Nov 20 13:04:02 2016	(r308885)
@@ -176,6 +176,9 @@ SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEB
 SYSCTL_INT(_net_inet_ipsec, OID_AUTO, crypto_support,
 	CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(crypto_support), 0,
 	"Crypto driver selection.");
+SYSCTL_INT(_net_inet_ipsec, OID_AUTO, check_policy_history,
+	CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(check_policy_history), 0,
+	"Use strict check of inbound packets to security policy compliance");
 SYSCTL_VNET_PCPUSTAT(_net_inet_ipsec, OID_AUTO, ipsecstats, struct ipsecstat,
     ipsec4stat, "IPsec IPv4 statistics.");
 
@@ -240,7 +243,8 @@ SYSCTL_VNET_PCPUSTAT(_net_inet6_ipsec6, 
     struct ipsecstat, ipsec6stat, "IPsec IPv6 statistics.");
 #endif /* INET6 */
 
-static int ipsec_in_reject(struct secpolicy *, const struct mbuf *);
+static int ipsec_in_reject(struct secpolicy *, struct inpcb *,
+    const struct mbuf *);
 static int ipsec_setspidx_inpcb(const struct mbuf *, struct inpcb *);
 static int ipsec_setspidx(const struct mbuf *, struct secpolicyindex *, int);
 static void ipsec4_get_ulp(const struct mbuf *m, struct secpolicyindex *, int);
@@ -1230,32 +1234,36 @@ ipsec_delete_pcbpolicy(struct inpcb *inp
  * Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned.
  */
 u_int
-ipsec_get_reqlevel(struct ipsecrequest *isr)
+ipsec_get_reqlevel(struct secpolicy *sp, u_int idx)
 {
-	u_int level = 0;
+	struct ipsecrequest *isr;
 	u_int esp_trans_deflev, esp_net_deflev;
 	u_int ah_trans_deflev, ah_net_deflev;
+	u_int level = 0;
 
-	IPSEC_ASSERT(isr != NULL && isr->sp != NULL, ("null argument"));
-	IPSEC_ASSERT(isr->sp->spidx.src.sa.sa_family == isr->sp->spidx.dst.sa.sa_family,
-		("af family mismatch, src %u, dst %u",
-		 isr->sp->spidx.src.sa.sa_family,
-		 isr->sp->spidx.dst.sa.sa_family));
-
+	IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx));
 /* XXX Note that we have ipseclog() expanded here - code sync issue. */
 #define IPSEC_CHECK_DEFAULT(lev) \
-	(((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE	      \
-			&& (lev) != IPSEC_LEVEL_UNIQUE)			      \
-		? (V_ipsec_debug						      \
-			? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\
-				(lev), IPSEC_LEVEL_REQUIRE)		      \
-			: 0),						      \
-			(lev) = IPSEC_LEVEL_REQUIRE,			      \
-			(lev)						      \
-		: (lev))
+	(((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE &&	\
+	  (lev) != IPSEC_LEVEL_UNIQUE)					\
+		? (V_ipsec_debug  ?					\
+		log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\
+		(lev), IPSEC_LEVEL_REQUIRE) : 0),			\
+		(lev) = IPSEC_LEVEL_REQUIRE, (lev) : (lev))
+
+	/*
+	 * IPsec VTI uses unique security policy with fake spidx filled
+	 * with zeroes. Just return IPSEC_LEVEL_REQUIRE instead of doing
+	 * full level lookup for such policies.
+	 */
+	if (sp->state == IPSEC_SPSTATE_IFNET) {
+		IPSEC_ASSERT(sp->req[idx]->level == IPSEC_LEVEL_UNIQUE,
+		    ("Wrong IPsec request level %d", sp->req[idx]->level));
+		return (IPSEC_LEVEL_REQUIRE);
+	}
 
 	/* Set default level. */
-	switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) {
+	switch (sp->spidx.src.sa.sa_family) {
 #ifdef INET
 	case AF_INET:
 		esp_trans_deflev = IPSEC_CHECK_DEFAULT(V_ip4_esp_trans_deflev);
@@ -1274,11 +1282,12 @@ ipsec_get_reqlevel(struct ipsecrequest *
 #endif /* INET6 */
 	default:
 		panic("%s: unknown af %u",
-			__func__, isr->sp->spidx.src.sa.sa_family);
+			__func__, sp->spidx.src.sa.sa_family);
 	}
 
 #undef IPSEC_CHECK_DEFAULT
 
+	isr = sp->req[idx];
 	/* Set level. */
 	switch (isr->level) {
 	case IPSEC_LEVEL_DEFAULT:
@@ -1323,6 +1332,45 @@ ipsec_get_reqlevel(struct ipsecrequest *
 	return (level);
 }
 
+static int
+ipsec_check_history(const struct mbuf *m, struct secpolicy *sp, u_int idx)
+{
+	struct xform_history *xh;
+	struct m_tag *mtag;
+
+	mtag = NULL;
+	while ((mtag = m_tag_find(__DECONST(struct mbuf *, m),
+	    PACKET_TAG_IPSEC_IN_DONE, mtag)) != NULL) {
+		xh = (struct xform_history *)(mtag + 1);
+		KEYDBG(IPSEC_DATA,
+		    char buf[IPSEC_ADDRSTRLEN];
+		    printf("%s: mode %s proto %u dst %s\n", __func__,
+			kdebug_secasindex_mode(xh->mode), xh->proto,
+			ipsec_address(&xh->dst, buf, sizeof(buf))));
+		if (xh->proto != sp->req[idx]->saidx.proto)
+			continue;
+		/* If SA had IPSEC_MODE_ANY, consider this as match. */
+		if (xh->mode != sp->req[idx]->saidx.mode &&
+		    xh->mode != IPSEC_MODE_ANY)
+			continue;
+		/*
+		 * For transport mode IPsec request doesn't contain
+		 * addresses. We need to use address from spidx.
+		 */
+		if (sp->req[idx]->saidx.mode == IPSEC_MODE_TRANSPORT) {
+			if (key_sockaddrcmp_withmask(&xh->dst.sa,
+			    &sp->spidx.dst.sa, sp->spidx.prefd) != 0)
+				continue;
+		} else {
+			if (key_sockaddrcmp(&xh->dst.sa,
+			    &sp->req[idx]->saidx.dst.sa, 0) != 0)
+				continue;
+		}
+		return (0); /* matched */
+	}
+	return (1);
+}
+
 /*
  * Check security policy requirements against the actual
  * packet contents.  Return one if the packet should be
@@ -1334,14 +1382,33 @@ ipsec_get_reqlevel(struct ipsecrequest *
  *	1: invalid
  */
 static int
-ipsec_in_reject(struct secpolicy *sp, const struct mbuf *m)
+ipsec_in_reject(struct secpolicy *sp, struct inpcb *inp, const struct mbuf *m)
 {
-	struct ipsecrequest *isr;
-	int need_auth;
+	uint32_t genid;
+	int i;
 
-	KEYDEBUG(KEYDEBUG_IPSEC_DATA,
-		printf("%s: using SP\n", __func__); kdebug_secpolicy(sp));
+	KEYDBG(IPSEC_STAMP,
+	    printf("%s: PCB(%p): using SP(%p)\n", __func__, inp, sp));
+	KEYDBG(IPSEC_DATA, kdebug_secpolicy(sp));
 
+	if (inp != NULL &&
+	    (inp->inp_sp->flags & INP_INBOUND_POLICY) == 0 &&
+	    inp->inp_sp->sp_in == NULL) {
+		/*
+		 * Save found INBOUND policy into PCB SP cache.
+		 */
+		genid = key_getspgen();
+		inp->inp_sp->sp_in = sp;
+		if (genid != inp->inp_sp->genid) {
+			/* Reset OUTBOUND cached policy if genid is changed */
+			if ((inp->inp_sp->flags & INP_OUTBOUND_POLICY) == 0)
+				inp->inp_sp->sp_out = NULL;
+			inp->inp_sp->genid = genid;
+		}
+		KEYDBG(IPSEC_STAMP,
+		    printf("%s: PCB(%p): cached SP(%p)\n",
+		    __func__, inp, sp));
+	}
 	/* Check policy. */
 	switch (sp->policy) {
 	case IPSEC_POLICY_DISCARD:
@@ -1354,48 +1421,42 @@ ipsec_in_reject(struct secpolicy *sp, co
 	IPSEC_ASSERT(sp->policy == IPSEC_POLICY_IPSEC,
 		("invalid policy %u", sp->policy));
 
-	/* XXX Should compare policy against IPsec header history. */
-
-	need_auth = 0;
-	for (isr = sp->req; isr != NULL; isr = isr->next) {
-		if (ipsec_get_reqlevel(isr) != IPSEC_LEVEL_REQUIRE)
+	/*
+	 * ipsec[46]_common_input_cb after each transform adds
+	 * PACKET_TAG_IPSEC_IN_DONE mbuf tag. It contains SPI, proto, mode
+	 * and destination address from saidx. We can compare info from
+	 * these tags with requirements in SP.
+	 */
+	for (i = 0; i < sp->tcount; i++) {
+		/*
+		 * Do not check IPcomp, since IPcomp document
+		 * says that we shouldn't compress small packets.
+		 * IPComp policy should always be treated as being
+		 * in "use" level.
+		 */
+		if (sp->req[i]->saidx.proto == IPPROTO_IPCOMP ||
+		    ipsec_get_reqlevel(sp, i) != IPSEC_LEVEL_REQUIRE)
 			continue;
-		switch (isr->saidx.proto) {
+		if (V_check_policy_history != 0 &&
+		    ipsec_check_history(m, sp, i) != 0)
+			return (1);
+		else switch (sp->req[i]->saidx.proto) {
 		case IPPROTO_ESP:
 			if ((m->m_flags & M_DECRYPTED) == 0) {
-				KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+				KEYDBG(IPSEC_DUMP,
 				    printf("%s: ESP m_flags:%x\n", __func__,
 					    m->m_flags));
 				return (1);
 			}
-
-			if (!need_auth &&
-			    isr->sav != NULL &&
-			    isr->sav->tdb_authalgxform != NULL &&
-			    (m->m_flags & M_AUTHIPDGM) == 0) {
-				KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
-				    printf("%s: ESP/AH m_flags:%x\n", __func__,
-					    m->m_flags));
-				return (1);
-			}
 			break;
 		case IPPROTO_AH:
-			need_auth = 1;
 			if ((m->m_flags & M_AUTHIPHDR) == 0) {
-				KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+				KEYDBG(IPSEC_DUMP,
 				    printf("%s: AH m_flags:%x\n", __func__,
 					    m->m_flags));
 				return (1);
 			}
 			break;
-		case IPPROTO_IPCOMP:
-			/*
-			 * We don't really care, as IPcomp document
-			 * says that we shouldn't compress small
-			 * packets.  IPComp policy should always be
-			 * treated as being in "use" level.
-			 */
-			break;
 		}
 	}
 	return (0);		/* Valid. */

Modified: projects/ipsec/sys/netipsec/ipsec.h
==============================================================================
--- projects/ipsec/sys/netipsec/ipsec.h	Sun Nov 20 12:25:14 2016	(r308884)
+++ projects/ipsec/sys/netipsec/ipsec.h	Sun Nov 20 13:04:02 2016	(r308885)
@@ -301,18 +301,16 @@ VNET_DECLARE(int, crypto_support);
 extern	struct ipsecrequest *ipsec_newisr(void);
 extern	void ipsec_delisr(struct ipsecrequest *);
 
-struct tdb_ident;
-extern struct secpolicy *ipsec_getpolicy(struct tdb_ident*, u_int);
 struct inpcb;
-extern struct secpolicy *ipsec4_checkpolicy(const struct mbuf *, u_int,
-    int *, struct inpcb *);
+struct secpolicy *ipsec4_checkpolicy(const struct mbuf *, struct inpcb *,
+    int *);
 extern struct secpolicy * ipsec_getpolicybyaddr(const struct mbuf *, u_int,
     int *);
 
-struct inpcb;
 extern int ipsec_init_policy(struct socket *so, struct inpcbpolicy **);
 extern int ipsec_copy_policy(struct inpcbpolicy *, struct inpcbpolicy *);
-extern u_int ipsec_get_reqlevel(struct ipsecrequest *);
+
+u_int ipsec_get_reqlevel(struct secpolicy *, u_int);
 
 extern int ipsec_set_policy(struct inpcb *inp, int optname,
 	caddr_t request, size_t len, struct ucred *cred);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201611201304.uAKD42lb072170>