From owner-svn-src-projects@freebsd.org Sun Nov 20 13:04:04 2016 Return-Path: Delivered-To: svn-src-projects@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 2F504C49F5B for ; Sun, 20 Nov 2016 13:04:04 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id E66B96CB; Sun, 20 Nov 2016 13:04:03 +0000 (UTC) (envelope-from ae@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id uAKD43HK072172; Sun, 20 Nov 2016 13:04:03 GMT (envelope-from ae@FreeBSD.org) Received: (from ae@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id uAKD42lb072170; Sun, 20 Nov 2016 13:04:02 GMT (envelope-from ae@FreeBSD.org) Message-Id: <201611201304.uAKD42lb072170@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ae set sender to ae@FreeBSD.org using -f From: "Andrey V. Elsukov" Date: Sun, 20 Nov 2016 13:04:02 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r308885 - projects/ipsec/sys/netipsec X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 20 Nov 2016 13:04:04 -0000 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);