mirror of
https://git.rtems.org/rtems-libbsd/
synced 2025-05-13 14:39:36 +08:00

Git mirror commit 59f44d20be3f99d181ca742e636d45fc39ec982b. This commit updates OpenSSL to version 1.1.1. This required an update of racoon which uses some internal stuff from OpenSSL and seems to be mostly unmaintained, e.g. there is update in the FreeBSD ports to cope with OpenSSL 1.1.1. Update #3472.
427 lines
11 KiB
C
427 lines
11 KiB
C
#include <machine/rtems-bsd-kernel-space.h>
|
|
|
|
/*-
|
|
* Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <rtems/bsd/local/opt_inet.h>
|
|
#include <rtems/bsd/local/opt_inet6.h>
|
|
#include <rtems/bsd/local/opt_ipsec.h>
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/module.h>
|
|
#include <sys/priv.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sockopt.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/proc.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_pcb.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/ip6.h>
|
|
|
|
#include <netipsec/ipsec_support.h>
|
|
#include <netipsec/ipsec.h>
|
|
#include <netipsec/ipsec6.h>
|
|
#include <netipsec/key.h>
|
|
#include <netipsec/key_debug.h>
|
|
#include <netipsec/xform.h>
|
|
|
|
#include <machine/atomic.h>
|
|
/*
|
|
* This file is build in the kernel only when 'options IPSEC' or
|
|
* 'options IPSEC_SUPPORT' is enabled.
|
|
*/
|
|
|
|
#ifdef INET
|
|
void
|
|
ipsec4_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
|
|
union sockaddr_union *dst)
|
|
{
|
|
static const struct sockaddr_in template = {
|
|
sizeof (struct sockaddr_in),
|
|
AF_INET,
|
|
0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }
|
|
};
|
|
|
|
src->sin = template;
|
|
dst->sin = template;
|
|
|
|
if (m->m_len < sizeof (struct ip)) {
|
|
m_copydata(m, offsetof(struct ip, ip_src),
|
|
sizeof (struct in_addr),
|
|
(caddr_t) &src->sin.sin_addr);
|
|
m_copydata(m, offsetof(struct ip, ip_dst),
|
|
sizeof (struct in_addr),
|
|
(caddr_t) &dst->sin.sin_addr);
|
|
} else {
|
|
const struct ip *ip = mtod(m, const struct ip *);
|
|
src->sin.sin_addr = ip->ip_src;
|
|
dst->sin.sin_addr = ip->ip_dst;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef INET6
|
|
void
|
|
ipsec6_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
|
|
union sockaddr_union *dst)
|
|
{
|
|
struct ip6_hdr ip6buf;
|
|
const struct ip6_hdr *ip6;
|
|
|
|
if (m->m_len >= sizeof(*ip6))
|
|
ip6 = mtod(m, const struct ip6_hdr *);
|
|
else {
|
|
m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf);
|
|
ip6 = &ip6buf;
|
|
}
|
|
|
|
bzero(&src->sin6, sizeof(struct sockaddr_in6));
|
|
src->sin6.sin6_family = AF_INET6;
|
|
src->sin6.sin6_len = sizeof(struct sockaddr_in6);
|
|
bcopy(&ip6->ip6_src, &src->sin6.sin6_addr, sizeof(ip6->ip6_src));
|
|
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
|
|
src->sin6.sin6_addr.s6_addr16[1] = 0;
|
|
src->sin6.sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
|
|
}
|
|
|
|
bzero(&dst->sin6, sizeof(struct sockaddr_in6));
|
|
dst->sin6.sin6_family = AF_INET6;
|
|
dst->sin6.sin6_len = sizeof(struct sockaddr_in6);
|
|
bcopy(&ip6->ip6_dst, &dst->sin6.sin6_addr, sizeof(ip6->ip6_dst));
|
|
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
|
|
dst->sin6.sin6_addr.s6_addr16[1] = 0;
|
|
dst->sin6.sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#define IPSEC_MODULE_INCR 2
|
|
static int
|
|
ipsec_kmod_enter(volatile u_int *cntr)
|
|
{
|
|
u_int old, new;
|
|
|
|
do {
|
|
old = *cntr;
|
|
if ((old & IPSEC_MODULE_ENABLED) == 0)
|
|
return (ENXIO);
|
|
new = old + IPSEC_MODULE_INCR;
|
|
} while(atomic_cmpset_acq_int(cntr, old, new) == 0);
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
ipsec_kmod_exit(volatile u_int *cntr)
|
|
{
|
|
u_int old, new;
|
|
|
|
do {
|
|
old = *cntr;
|
|
new = old - IPSEC_MODULE_INCR;
|
|
} while (atomic_cmpset_rel_int(cntr, old, new) == 0);
|
|
}
|
|
|
|
static void
|
|
ipsec_kmod_drain(volatile u_int *cntr)
|
|
{
|
|
u_int old, new;
|
|
|
|
do {
|
|
old = *cntr;
|
|
new = old & ~IPSEC_MODULE_ENABLED;
|
|
} while (atomic_cmpset_acq_int(cntr, old, new) == 0);
|
|
while (atomic_cmpset_int(cntr, 0, 0) == 0)
|
|
pause("ipsecd", hz/2);
|
|
}
|
|
|
|
static LIST_HEAD(xforms_list, xformsw) xforms = LIST_HEAD_INITIALIZER();
|
|
static struct mtx xforms_lock;
|
|
MTX_SYSINIT(xfroms_list, &xforms_lock, "IPsec transforms list", MTX_DEF);
|
|
#define XFORMS_LOCK() mtx_lock(&xforms_lock)
|
|
#define XFORMS_UNLOCK() mtx_unlock(&xforms_lock)
|
|
|
|
void
|
|
xform_attach(void *data)
|
|
{
|
|
struct xformsw *xsp, *entry;
|
|
|
|
xsp = (struct xformsw *)data;
|
|
XFORMS_LOCK();
|
|
LIST_FOREACH(entry, &xforms, chain) {
|
|
if (entry->xf_type == xsp->xf_type) {
|
|
XFORMS_UNLOCK();
|
|
printf("%s: failed to register %s xform\n",
|
|
__func__, xsp->xf_name);
|
|
return;
|
|
}
|
|
}
|
|
LIST_INSERT_HEAD(&xforms, xsp, chain);
|
|
xsp->xf_cntr = IPSEC_MODULE_ENABLED;
|
|
XFORMS_UNLOCK();
|
|
}
|
|
|
|
void
|
|
xform_detach(void *data)
|
|
{
|
|
struct xformsw *xsp = (struct xformsw *)data;
|
|
|
|
XFORMS_LOCK();
|
|
LIST_REMOVE(xsp, chain);
|
|
XFORMS_UNLOCK();
|
|
|
|
/* Delete all SAs related to this xform. */
|
|
key_delete_xform(xsp);
|
|
if (xsp->xf_cntr & IPSEC_MODULE_ENABLED)
|
|
ipsec_kmod_drain(&xsp->xf_cntr);
|
|
}
|
|
|
|
/*
|
|
* Initialize transform support in an sav.
|
|
*/
|
|
int
|
|
xform_init(struct secasvar *sav, u_short xftype)
|
|
{
|
|
struct xformsw *entry;
|
|
int ret;
|
|
|
|
IPSEC_ASSERT(sav->tdb_xform == NULL,
|
|
("tdb_xform is already initialized"));
|
|
|
|
XFORMS_LOCK();
|
|
LIST_FOREACH(entry, &xforms, chain) {
|
|
if (entry->xf_type == xftype) {
|
|
ret = ipsec_kmod_enter(&entry->xf_cntr);
|
|
XFORMS_UNLOCK();
|
|
if (ret != 0)
|
|
return (ret);
|
|
ret = (*entry->xf_init)(sav, entry);
|
|
ipsec_kmod_exit(&entry->xf_cntr);
|
|
return (ret);
|
|
}
|
|
}
|
|
XFORMS_UNLOCK();
|
|
return (EINVAL);
|
|
}
|
|
|
|
#ifdef IPSEC_SUPPORT
|
|
/*
|
|
* IPSEC_SUPPORT - loading of ipsec.ko and tcpmd5.ko is supported.
|
|
* IPSEC + IPSEC_SUPPORT - loading tcpmd5.ko is supported.
|
|
* IPSEC + TCP_SIGNATURE - all is build in the kernel, do not build
|
|
* IPSEC_SUPPORT.
|
|
*/
|
|
#if !defined(IPSEC) || !defined(TCP_SIGNATURE)
|
|
#define METHOD_DECL(...) __VA_ARGS__
|
|
#define METHOD_ARGS(...) __VA_ARGS__
|
|
#define IPSEC_KMOD_METHOD(type, name, sc, method, decl, args) \
|
|
type name (decl) \
|
|
{ \
|
|
type ret = (type)ipsec_kmod_enter(&sc->enabled); \
|
|
if (ret == 0) { \
|
|
ret = (*sc->methods->method)(args); \
|
|
ipsec_kmod_exit(&sc->enabled); \
|
|
} \
|
|
return (ret); \
|
|
}
|
|
|
|
static int
|
|
ipsec_support_modevent(module_t mod, int type, void *data)
|
|
{
|
|
|
|
switch (type) {
|
|
case MOD_LOAD:
|
|
return (0);
|
|
case MOD_UNLOAD:
|
|
return (EBUSY);
|
|
default:
|
|
return (EOPNOTSUPP);
|
|
}
|
|
}
|
|
|
|
static moduledata_t ipsec_support_mod = {
|
|
"ipsec_support",
|
|
ipsec_support_modevent,
|
|
0
|
|
};
|
|
DECLARE_MODULE(ipsec_support, ipsec_support_mod, SI_SUB_PROTO_DOMAIN,
|
|
SI_ORDER_ANY);
|
|
MODULE_VERSION(ipsec_support, 1);
|
|
#endif /* !IPSEC || !TCP_SIGNATURE */
|
|
|
|
#ifndef TCP_SIGNATURE
|
|
/* Declare TCP-MD5 support as kernel module. */
|
|
static struct tcpmd5_support tcpmd5_ipsec = {
|
|
.enabled = 0,
|
|
.methods = NULL
|
|
};
|
|
struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
|
|
|
|
IPSEC_KMOD_METHOD(int, tcpmd5_kmod_input, sc,
|
|
input, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
|
|
struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
|
|
)
|
|
|
|
IPSEC_KMOD_METHOD(int, tcpmd5_kmod_output, sc,
|
|
output, METHOD_DECL(struct tcpmd5_support * const sc, struct mbuf *m,
|
|
struct tcphdr *th, u_char *buf), METHOD_ARGS(m, th, buf)
|
|
)
|
|
|
|
IPSEC_KMOD_METHOD(int, tcpmd5_kmod_pcbctl, sc,
|
|
pcbctl, METHOD_DECL(struct tcpmd5_support * const sc, struct inpcb *inp,
|
|
struct sockopt *sopt), METHOD_ARGS(inp, sopt)
|
|
)
|
|
|
|
void
|
|
tcpmd5_support_enable(const struct tcpmd5_methods * const methods)
|
|
{
|
|
|
|
KASSERT(tcp_ipsec_support->enabled == 0, ("TCP-MD5 already enabled"));
|
|
tcp_ipsec_support->methods = methods;
|
|
tcp_ipsec_support->enabled |= IPSEC_MODULE_ENABLED;
|
|
}
|
|
|
|
void
|
|
tcpmd5_support_disable(void)
|
|
{
|
|
|
|
if (tcp_ipsec_support->enabled & IPSEC_MODULE_ENABLED) {
|
|
ipsec_kmod_drain(&tcp_ipsec_support->enabled);
|
|
tcp_ipsec_support->methods = NULL;
|
|
}
|
|
}
|
|
#endif /* !TCP_SIGNATURE */
|
|
|
|
#ifndef IPSEC
|
|
/*
|
|
* IPsec support is build as kernel module.
|
|
*/
|
|
#ifdef INET
|
|
static struct ipsec_support ipv4_ipsec = {
|
|
.enabled = 0,
|
|
.methods = NULL
|
|
};
|
|
struct ipsec_support * const ipv4_ipsec_support = &ipv4_ipsec;
|
|
|
|
IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_input, sc,
|
|
udp_input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
|
|
int off, int af), METHOD_ARGS(m, off, af)
|
|
)
|
|
|
|
IPSEC_KMOD_METHOD(int, ipsec_kmod_udp_pcbctl, sc,
|
|
udp_pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
|
|
struct sockopt *sopt), METHOD_ARGS(inp, sopt)
|
|
)
|
|
#endif
|
|
|
|
#ifdef INET6
|
|
static struct ipsec_support ipv6_ipsec = {
|
|
.enabled = 0,
|
|
.methods = NULL
|
|
};
|
|
struct ipsec_support * const ipv6_ipsec_support = &ipv6_ipsec;
|
|
#endif
|
|
|
|
IPSEC_KMOD_METHOD(int, ipsec_kmod_input, sc,
|
|
input, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
|
|
int offset, int proto), METHOD_ARGS(m, offset, proto)
|
|
)
|
|
|
|
IPSEC_KMOD_METHOD(int, ipsec_kmod_check_policy, sc,
|
|
check_policy, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
|
|
struct inpcb *inp), METHOD_ARGS(m, inp)
|
|
)
|
|
|
|
IPSEC_KMOD_METHOD(int, ipsec_kmod_forward, sc,
|
|
forward, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m),
|
|
(m)
|
|
)
|
|
|
|
IPSEC_KMOD_METHOD(int, ipsec_kmod_output, sc,
|
|
output, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
|
|
struct inpcb *inp), METHOD_ARGS(m, inp)
|
|
)
|
|
|
|
IPSEC_KMOD_METHOD(int, ipsec_kmod_pcbctl, sc,
|
|
pcbctl, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp,
|
|
struct sockopt *sopt), METHOD_ARGS(inp, sopt)
|
|
)
|
|
|
|
IPSEC_KMOD_METHOD(size_t, ipsec_kmod_hdrsize, sc,
|
|
hdrsize, METHOD_DECL(struct ipsec_support * const sc, struct inpcb *inp),
|
|
(inp)
|
|
)
|
|
|
|
static IPSEC_KMOD_METHOD(int, ipsec_kmod_caps, sc,
|
|
capability, METHOD_DECL(struct ipsec_support * const sc, struct mbuf *m,
|
|
u_int cap), METHOD_ARGS(m, cap)
|
|
)
|
|
|
|
int
|
|
ipsec_kmod_capability(struct ipsec_support * const sc, struct mbuf *m,
|
|
u_int cap)
|
|
{
|
|
|
|
/*
|
|
* Since PF_KEY is build in the kernel, we can directly
|
|
* call key_havesp() without additional synchronizations.
|
|
*/
|
|
if (cap == IPSEC_CAP_OPERABLE)
|
|
return (key_havesp(IPSEC_DIR_INBOUND) != 0 ||
|
|
key_havesp(IPSEC_DIR_OUTBOUND) != 0);
|
|
return (ipsec_kmod_caps(sc, m, cap));
|
|
}
|
|
|
|
void
|
|
ipsec_support_enable(struct ipsec_support * const sc,
|
|
const struct ipsec_methods * const methods)
|
|
{
|
|
|
|
KASSERT(sc->enabled == 0, ("IPsec already enabled"));
|
|
sc->methods = methods;
|
|
sc->enabled |= IPSEC_MODULE_ENABLED;
|
|
}
|
|
|
|
void
|
|
ipsec_support_disable(struct ipsec_support * const sc)
|
|
{
|
|
|
|
if (sc->enabled & IPSEC_MODULE_ENABLED) {
|
|
ipsec_kmod_drain(&sc->enabled);
|
|
sc->methods = NULL;
|
|
}
|
|
}
|
|
#endif /* !IPSEC */
|
|
#endif /* IPSEC_SUPPORT */
|