xref: /freebsd/sys/netipsec/xform_tcp.c (revision fe267a559009cbf34f9341666fe4d88a92c02d5e)
1c398230bSWarner Losh /*-
2*fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
3*fe267a55SPedro F. Giffuni  *
455d2c71bSBruce M Simpson  * Copyright (c) 2003 Bruce M. Simpson <bms@spc.org>
5fcf59617SAndrey V. Elsukov  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
655d2c71bSBruce M Simpson  *
755d2c71bSBruce M Simpson  * Redistribution and use in source and binary forms, with or without
855d2c71bSBruce M Simpson  * modification, are permitted provided that the following conditions
955d2c71bSBruce M Simpson  * are met:
1055d2c71bSBruce M Simpson  *
1155d2c71bSBruce M Simpson  * 1. Redistributions of source code must retain the above copyright
1255d2c71bSBruce M Simpson  *   notice, this list of conditions and the following disclaimer.
1355d2c71bSBruce M Simpson  * 2. Redistributions in binary form must reproduce the above copyright
1455d2c71bSBruce M Simpson  *   notice, this list of conditions and the following disclaimer in the
1555d2c71bSBruce M Simpson  *   documentation and/or other materials provided with the distribution.
1655d2c71bSBruce M Simpson  * 3. The name of the author may not be used to endorse or promote products
1755d2c71bSBruce M Simpson  *   derived from this software without specific prior written permission.
1855d2c71bSBruce M Simpson  *
1955d2c71bSBruce M Simpson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2055d2c71bSBruce M Simpson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2155d2c71bSBruce M Simpson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2255d2c71bSBruce M Simpson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2355d2c71bSBruce M Simpson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2455d2c71bSBruce M Simpson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2555d2c71bSBruce M Simpson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2655d2c71bSBruce M Simpson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2755d2c71bSBruce M Simpson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2855d2c71bSBruce M Simpson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2955d2c71bSBruce M Simpson  */
3055d2c71bSBruce M Simpson 
3155d2c71bSBruce M Simpson /* TCP MD5 Signature Option (RFC2385) */
32fcf59617SAndrey V. Elsukov #include <sys/cdefs.h>
33fcf59617SAndrey V. Elsukov __FBSDID("$FreeBSD$");
34fcf59617SAndrey V. Elsukov 
3555d2c71bSBruce M Simpson #include "opt_inet.h"
3655d2c71bSBruce M Simpson #include "opt_inet6.h"
37fcf59617SAndrey V. Elsukov #include "opt_ipsec.h"
3855d2c71bSBruce M Simpson 
3955d2c71bSBruce M Simpson #include <sys/param.h>
4055d2c71bSBruce M Simpson #include <sys/systm.h>
4155d2c71bSBruce M Simpson #include <sys/mbuf.h>
4255d2c71bSBruce M Simpson #include <sys/lock.h>
43fcf59617SAndrey V. Elsukov #include <sys/md5.h>
44fcf59617SAndrey V. Elsukov #include <sys/rmlock.h>
4555d2c71bSBruce M Simpson #include <sys/socket.h>
46fcf59617SAndrey V. Elsukov #include <sys/sockopt.h>
4755d2c71bSBruce M Simpson #include <sys/kernel.h>
48fcf59617SAndrey V. Elsukov #include <sys/module.h>
4955d2c71bSBruce M Simpson #include <sys/protosw.h>
5055d2c71bSBruce M Simpson 
5155d2c71bSBruce M Simpson #include <netinet/in.h>
52fcf59617SAndrey V. Elsukov #include <netinet/in_pcb.h>
5355d2c71bSBruce M Simpson #include <netinet/in_systm.h>
5455d2c71bSBruce M Simpson #include <netinet/ip.h>
5555d2c71bSBruce M Simpson #include <netinet/ip_var.h>
5655d2c71bSBruce M Simpson #include <netinet/tcp.h>
5755d2c71bSBruce M Simpson #include <netinet/tcp_var.h>
5855d2c71bSBruce M Simpson 
59eddfbb76SRobert Watson #include <net/vnet.h>
60eddfbb76SRobert Watson 
6155d2c71bSBruce M Simpson #include <netipsec/ipsec.h>
62fcf59617SAndrey V. Elsukov #include <netipsec/ipsec_support.h>
6355d2c71bSBruce M Simpson #include <netipsec/xform.h>
6455d2c71bSBruce M Simpson 
6555d2c71bSBruce M Simpson #ifdef INET6
6655d2c71bSBruce M Simpson #include <netinet/ip6.h>
6755d2c71bSBruce M Simpson #include <netipsec/ipsec6.h>
6855d2c71bSBruce M Simpson #endif
6955d2c71bSBruce M Simpson 
7055d2c71bSBruce M Simpson #include <netipsec/key.h>
7155d2c71bSBruce M Simpson #include <netipsec/key_debug.h>
7255d2c71bSBruce M Simpson 
73fcf59617SAndrey V. Elsukov #define	TCP_SIGLEN	16	/* length of computed digest in bytes */
74fcf59617SAndrey V. Elsukov #define	TCP_KEYLEN_MIN	1	/* minimum length of TCP-MD5 key */
75fcf59617SAndrey V. Elsukov #define	TCP_KEYLEN_MAX	80	/* maximum length of TCP-MD5 key */
76fcf59617SAndrey V. Elsukov 
77fcf59617SAndrey V. Elsukov static int
78fcf59617SAndrey V. Elsukov tcp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt)
79fcf59617SAndrey V. Elsukov {
80fcf59617SAndrey V. Elsukov 	struct tcpcb *tp;
81fcf59617SAndrey V. Elsukov 	int error, optval;
82fcf59617SAndrey V. Elsukov 
83fcf59617SAndrey V. Elsukov 	INP_WLOCK_ASSERT(inp);
84fcf59617SAndrey V. Elsukov 	if (sopt->sopt_name != TCP_MD5SIG) {
85fcf59617SAndrey V. Elsukov 		INP_WUNLOCK(inp);
86fcf59617SAndrey V. Elsukov 		return (ENOPROTOOPT);
87fcf59617SAndrey V. Elsukov 	}
88fcf59617SAndrey V. Elsukov 
89fcf59617SAndrey V. Elsukov 	tp = intotcpcb(inp);
90fcf59617SAndrey V. Elsukov 	if (sopt->sopt_dir == SOPT_GET) {
91fcf59617SAndrey V. Elsukov 		optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0;
92fcf59617SAndrey V. Elsukov 		INP_WUNLOCK(inp);
93fcf59617SAndrey V. Elsukov 
94fcf59617SAndrey V. Elsukov 		/* On success return with released INP_WLOCK */
95fcf59617SAndrey V. Elsukov 		return (sooptcopyout(sopt, &optval, sizeof(optval)));
96fcf59617SAndrey V. Elsukov 	}
97fcf59617SAndrey V. Elsukov 
98fcf59617SAndrey V. Elsukov 	INP_WUNLOCK(inp);
99fcf59617SAndrey V. Elsukov 
100fcf59617SAndrey V. Elsukov 	error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
101fcf59617SAndrey V. Elsukov 	if (error != 0)
102fcf59617SAndrey V. Elsukov 		return (error);
103fcf59617SAndrey V. Elsukov 
104fcf59617SAndrey V. Elsukov 	/* INP_WLOCK_RECHECK */
105fcf59617SAndrey V. Elsukov 	INP_WLOCK(inp);
106fcf59617SAndrey V. Elsukov 	if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
107fcf59617SAndrey V. Elsukov 		INP_WUNLOCK(inp);
108fcf59617SAndrey V. Elsukov 		return (ECONNRESET);
109fcf59617SAndrey V. Elsukov 	}
110fcf59617SAndrey V. Elsukov 	if (optval > 0)
111fcf59617SAndrey V. Elsukov 		tp->t_flags |= TF_SIGNATURE;
112fcf59617SAndrey V. Elsukov 	else
113fcf59617SAndrey V. Elsukov 		tp->t_flags &= ~TF_SIGNATURE;
114fcf59617SAndrey V. Elsukov 
115fcf59617SAndrey V. Elsukov 	/* On success return with acquired INP_WLOCK */
116fcf59617SAndrey V. Elsukov 	return (error);
117fcf59617SAndrey V. Elsukov }
118fcf59617SAndrey V. Elsukov 
119fcf59617SAndrey V. Elsukov /*
120fcf59617SAndrey V. Elsukov  * Callback function invoked by m_apply() to digest TCP segment data
121fcf59617SAndrey V. Elsukov  * contained within an mbuf chain.
122fcf59617SAndrey V. Elsukov  */
123fcf59617SAndrey V. Elsukov static int
124fcf59617SAndrey V. Elsukov tcp_signature_apply(void *fstate, void *data, u_int len)
125fcf59617SAndrey V. Elsukov {
126fcf59617SAndrey V. Elsukov 
127fcf59617SAndrey V. Elsukov 	MD5Update(fstate, (u_char *)data, len);
128fcf59617SAndrey V. Elsukov 	return (0);
129fcf59617SAndrey V. Elsukov }
130fcf59617SAndrey V. Elsukov 
131fcf59617SAndrey V. Elsukov #ifdef INET
132fcf59617SAndrey V. Elsukov static int
133fcf59617SAndrey V. Elsukov ip_pseudo_compute(struct mbuf *m, MD5_CTX *ctx)
134fcf59617SAndrey V. Elsukov {
135fcf59617SAndrey V. Elsukov 	struct ippseudo ipp;
136fcf59617SAndrey V. Elsukov 	struct ip *ip;
137fcf59617SAndrey V. Elsukov 
138fcf59617SAndrey V. Elsukov 	ip = mtod(m, struct ip *);
139fcf59617SAndrey V. Elsukov 	ipp.ippseudo_src.s_addr = ip->ip_src.s_addr;
140fcf59617SAndrey V. Elsukov 	ipp.ippseudo_dst.s_addr = ip->ip_dst.s_addr;
141fcf59617SAndrey V. Elsukov 	ipp.ippseudo_p = IPPROTO_TCP;
142fcf59617SAndrey V. Elsukov 	ipp.ippseudo_pad = 0;
143fcf59617SAndrey V. Elsukov 	ipp.ippseudo_len = htons(m->m_pkthdr.len - (ip->ip_hl << 2));
144fcf59617SAndrey V. Elsukov 	MD5Update(ctx, (char *)&ipp, sizeof(ipp));
145fcf59617SAndrey V. Elsukov 	return (ip->ip_hl << 2);
146fcf59617SAndrey V. Elsukov }
147fcf59617SAndrey V. Elsukov #endif
148fcf59617SAndrey V. Elsukov 
149fcf59617SAndrey V. Elsukov #ifdef INET6
150fcf59617SAndrey V. Elsukov static int
151fcf59617SAndrey V. Elsukov ip6_pseudo_compute(struct mbuf *m, MD5_CTX *ctx)
152fcf59617SAndrey V. Elsukov {
153fcf59617SAndrey V. Elsukov 	struct ip6_pseudo {
154fcf59617SAndrey V. Elsukov 		struct in6_addr src, dst;
155fcf59617SAndrey V. Elsukov 		uint32_t len;
156fcf59617SAndrey V. Elsukov 		uint32_t nxt;
157fcf59617SAndrey V. Elsukov 	} ip6p __aligned(4);
158fcf59617SAndrey V. Elsukov 	struct ip6_hdr *ip6;
159fcf59617SAndrey V. Elsukov 
160fcf59617SAndrey V. Elsukov 	ip6 = mtod(m, struct ip6_hdr *);
161fcf59617SAndrey V. Elsukov 	ip6p.src = ip6->ip6_src;
162fcf59617SAndrey V. Elsukov 	ip6p.dst = ip6->ip6_dst;
163fcf59617SAndrey V. Elsukov 	ip6p.len = htonl(m->m_pkthdr.len - sizeof(*ip6)); /* XXX: ext headers */
164fcf59617SAndrey V. Elsukov 	ip6p.nxt = htonl(IPPROTO_TCP);
165fcf59617SAndrey V. Elsukov 	MD5Update(ctx, (char *)&ip6p, sizeof(ip6p));
166fcf59617SAndrey V. Elsukov 	return (sizeof(*ip6));
167fcf59617SAndrey V. Elsukov }
168fcf59617SAndrey V. Elsukov #endif
169fcf59617SAndrey V. Elsukov 
170fcf59617SAndrey V. Elsukov static int
171fcf59617SAndrey V. Elsukov tcp_signature_compute(struct mbuf *m, struct tcphdr *th,
172fcf59617SAndrey V. Elsukov     struct secasvar *sav, u_char *buf)
173fcf59617SAndrey V. Elsukov {
174fcf59617SAndrey V. Elsukov 	MD5_CTX ctx;
175fcf59617SAndrey V. Elsukov 	int len;
176fcf59617SAndrey V. Elsukov 	u_short csum;
177fcf59617SAndrey V. Elsukov 
178fcf59617SAndrey V. Elsukov 	MD5Init(&ctx);
179fcf59617SAndrey V. Elsukov 	 /* Step 1: Update MD5 hash with IP(v6) pseudo-header. */
180fcf59617SAndrey V. Elsukov 	switch (sav->sah->saidx.dst.sa.sa_family) {
181fcf59617SAndrey V. Elsukov #ifdef INET
182fcf59617SAndrey V. Elsukov 	case AF_INET:
183fcf59617SAndrey V. Elsukov 		len = ip_pseudo_compute(m, &ctx);
184fcf59617SAndrey V. Elsukov 		break;
185fcf59617SAndrey V. Elsukov #endif
186fcf59617SAndrey V. Elsukov #ifdef INET6
187fcf59617SAndrey V. Elsukov 	case AF_INET6:
188fcf59617SAndrey V. Elsukov 		len = ip6_pseudo_compute(m, &ctx);
189fcf59617SAndrey V. Elsukov 		break;
190fcf59617SAndrey V. Elsukov #endif
191fcf59617SAndrey V. Elsukov 	default:
192fcf59617SAndrey V. Elsukov 		return (EAFNOSUPPORT);
193fcf59617SAndrey V. Elsukov 	}
194fcf59617SAndrey V. Elsukov 	/*
195fcf59617SAndrey V. Elsukov 	 * Step 2: Update MD5 hash with TCP header, excluding options.
196fcf59617SAndrey V. Elsukov 	 * The TCP checksum must be set to zero.
197fcf59617SAndrey V. Elsukov 	 */
198fcf59617SAndrey V. Elsukov 	csum = th->th_sum;
199fcf59617SAndrey V. Elsukov 	th->th_sum = 0;
200fcf59617SAndrey V. Elsukov 	MD5Update(&ctx, (char *)th, sizeof(struct tcphdr));
201fcf59617SAndrey V. Elsukov 	th->th_sum = csum;
202fcf59617SAndrey V. Elsukov 	/*
203fcf59617SAndrey V. Elsukov 	 * Step 3: Update MD5 hash with TCP segment data.
204fcf59617SAndrey V. Elsukov 	 * Use m_apply() to avoid an early m_pullup().
205fcf59617SAndrey V. Elsukov 	 */
206fcf59617SAndrey V. Elsukov 	len += (th->th_off << 2);
207fcf59617SAndrey V. Elsukov 	if (m->m_pkthdr.len - len > 0)
208fcf59617SAndrey V. Elsukov 		m_apply(m, len, m->m_pkthdr.len - len,
209fcf59617SAndrey V. Elsukov 		    tcp_signature_apply, &ctx);
210fcf59617SAndrey V. Elsukov 	/*
211fcf59617SAndrey V. Elsukov 	 * Step 4: Update MD5 hash with shared secret.
212fcf59617SAndrey V. Elsukov 	 */
213fcf59617SAndrey V. Elsukov 	MD5Update(&ctx, sav->key_auth->key_data, _KEYLEN(sav->key_auth));
214fcf59617SAndrey V. Elsukov 	MD5Final(buf, &ctx);
215fcf59617SAndrey V. Elsukov 	key_sa_recordxfer(sav, m);
216fcf59617SAndrey V. Elsukov 	return (0);
217fcf59617SAndrey V. Elsukov }
218fcf59617SAndrey V. Elsukov 
219fcf59617SAndrey V. Elsukov static void
220fcf59617SAndrey V. Elsukov setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
221fcf59617SAndrey V. Elsukov     union sockaddr_union *dst)
222fcf59617SAndrey V. Elsukov {
223fcf59617SAndrey V. Elsukov 	struct ip *ip;
224fcf59617SAndrey V. Elsukov 
225fcf59617SAndrey V. Elsukov 	IPSEC_ASSERT(m->m_len >= sizeof(*ip), ("unexpected mbuf len"));
226fcf59617SAndrey V. Elsukov 
227fcf59617SAndrey V. Elsukov 	ip = mtod(m, struct ip *);
228fcf59617SAndrey V. Elsukov 	switch (ip->ip_v) {
229fcf59617SAndrey V. Elsukov #ifdef INET
230fcf59617SAndrey V. Elsukov 	case IPVERSION:
231fcf59617SAndrey V. Elsukov 		ipsec4_setsockaddrs(m, src, dst);
232fcf59617SAndrey V. Elsukov 		break;
233fcf59617SAndrey V. Elsukov #endif
234fcf59617SAndrey V. Elsukov #ifdef INET6
235fcf59617SAndrey V. Elsukov 	case (IPV6_VERSION >> 4):
236fcf59617SAndrey V. Elsukov 		ipsec6_setsockaddrs(m, src, dst);
237fcf59617SAndrey V. Elsukov 		break;
238fcf59617SAndrey V. Elsukov #endif
239fcf59617SAndrey V. Elsukov 	default:
240fcf59617SAndrey V. Elsukov 		bzero(src, sizeof(*src));
241fcf59617SAndrey V. Elsukov 		bzero(dst, sizeof(*dst));
242fcf59617SAndrey V. Elsukov 	}
243fcf59617SAndrey V. Elsukov }
244fcf59617SAndrey V. Elsukov 
245fcf59617SAndrey V. Elsukov /*
246fcf59617SAndrey V. Elsukov  * Compute TCP-MD5 hash of an *INBOUND* TCP segment.
247fcf59617SAndrey V. Elsukov  * Parameters:
248fcf59617SAndrey V. Elsukov  * m		pointer to head of mbuf chain
249fcf59617SAndrey V. Elsukov  * th		pointer to TCP header
250fcf59617SAndrey V. Elsukov  * buf		pointer to storage for computed MD5 digest
251fcf59617SAndrey V. Elsukov  *
252fcf59617SAndrey V. Elsukov  * Return 0 if successful, otherwise return -1.
253fcf59617SAndrey V. Elsukov  */
254fcf59617SAndrey V. Elsukov static int
255fcf59617SAndrey V. Elsukov tcp_ipsec_input(struct mbuf *m, struct tcphdr *th, u_char *buf)
256fcf59617SAndrey V. Elsukov {
257fcf59617SAndrey V. Elsukov 	char tmpdigest[TCP_SIGLEN];
258fcf59617SAndrey V. Elsukov 	struct secasindex saidx;
259fcf59617SAndrey V. Elsukov 	struct secasvar *sav;
260fcf59617SAndrey V. Elsukov 
261fcf59617SAndrey V. Elsukov 	setsockaddrs(m, &saidx.src, &saidx.dst);
262fcf59617SAndrey V. Elsukov 	saidx.proto = IPPROTO_TCP;
263fcf59617SAndrey V. Elsukov 	saidx.mode = IPSEC_MODE_TCPMD5;
264fcf59617SAndrey V. Elsukov 	saidx.reqid = 0;
265fcf59617SAndrey V. Elsukov 	sav = key_allocsa_tcpmd5(&saidx);
266fcf59617SAndrey V. Elsukov 	if (sav == NULL) {
267fcf59617SAndrey V. Elsukov 		KMOD_TCPSTAT_INC(tcps_sig_err_buildsig);
268fcf59617SAndrey V. Elsukov 		return (EACCES);
269fcf59617SAndrey V. Elsukov 	}
270fcf59617SAndrey V. Elsukov 	/*
271fcf59617SAndrey V. Elsukov 	 * tcp_input() operates with TCP header fields in host
272fcf59617SAndrey V. Elsukov 	 * byte order. We expect them in network byte order.
273fcf59617SAndrey V. Elsukov 	 */
274fcf59617SAndrey V. Elsukov 	tcp_fields_to_net(th);
275fcf59617SAndrey V. Elsukov 	tcp_signature_compute(m, th, sav, tmpdigest);
276fcf59617SAndrey V. Elsukov 	tcp_fields_to_host(th);
277fcf59617SAndrey V. Elsukov 	key_freesav(&sav);
278fcf59617SAndrey V. Elsukov 	if (bcmp(buf, tmpdigest, TCP_SIGLEN) != 0) {
279fcf59617SAndrey V. Elsukov 		KMOD_TCPSTAT_INC(tcps_sig_rcvbadsig);
280fcf59617SAndrey V. Elsukov 		return (EACCES);
281fcf59617SAndrey V. Elsukov 	}
282fcf59617SAndrey V. Elsukov 	KMOD_TCPSTAT_INC(tcps_sig_rcvgoodsig);
283fcf59617SAndrey V. Elsukov 	return (0);
284fcf59617SAndrey V. Elsukov }
285fcf59617SAndrey V. Elsukov 
286fcf59617SAndrey V. Elsukov /*
287fcf59617SAndrey V. Elsukov  * Compute TCP-MD5 hash of an *OUTBOUND* TCP segment.
288fcf59617SAndrey V. Elsukov  * Parameters:
289fcf59617SAndrey V. Elsukov  * m		pointer to head of mbuf chain
290fcf59617SAndrey V. Elsukov  * th		pointer to TCP header
291fcf59617SAndrey V. Elsukov  * buf		pointer to storage for computed MD5 digest
292fcf59617SAndrey V. Elsukov  *
293fcf59617SAndrey V. Elsukov  * Return 0 if successful, otherwise return error code.
294fcf59617SAndrey V. Elsukov  */
295fcf59617SAndrey V. Elsukov static int
296fcf59617SAndrey V. Elsukov tcp_ipsec_output(struct mbuf *m, struct tcphdr *th, u_char *buf)
297fcf59617SAndrey V. Elsukov {
298fcf59617SAndrey V. Elsukov 	struct secasindex saidx;
299fcf59617SAndrey V. Elsukov 	struct secasvar *sav;
300fcf59617SAndrey V. Elsukov 
301fcf59617SAndrey V. Elsukov 	setsockaddrs(m, &saidx.src, &saidx.dst);
302fcf59617SAndrey V. Elsukov 	saidx.proto = IPPROTO_TCP;
303fcf59617SAndrey V. Elsukov 	saidx.mode = IPSEC_MODE_TCPMD5;
304fcf59617SAndrey V. Elsukov 	saidx.reqid = 0;
305fcf59617SAndrey V. Elsukov 	sav = key_allocsa_tcpmd5(&saidx);
306fcf59617SAndrey V. Elsukov 	if (sav == NULL) {
307fcf59617SAndrey V. Elsukov 		KMOD_TCPSTAT_INC(tcps_sig_err_buildsig);
308fcf59617SAndrey V. Elsukov 		return (EACCES);
309fcf59617SAndrey V. Elsukov 	}
310fcf59617SAndrey V. Elsukov 	tcp_signature_compute(m, th, sav, buf);
311fcf59617SAndrey V. Elsukov 	key_freesav(&sav);
312fcf59617SAndrey V. Elsukov 	return (0);
313fcf59617SAndrey V. Elsukov }
314fcf59617SAndrey V. Elsukov 
31555d2c71bSBruce M Simpson /*
31655d2c71bSBruce M Simpson  * Initialize a TCP-MD5 SA. Called when the SA is being set up.
31755d2c71bSBruce M Simpson  *
31855d2c71bSBruce M Simpson  * We don't need to set up the tdb prefixed fields, as we don't use the
31955d2c71bSBruce M Simpson  * opencrypto code; we just perform a key length check.
32055d2c71bSBruce M Simpson  *
321fcf59617SAndrey V. Elsukov  * XXX: Currently we have used single 'magic' SPI and need to still
322fcf59617SAndrey V. Elsukov  * support this.
32355d2c71bSBruce M Simpson  *
32455d2c71bSBruce M Simpson  * This allows per-host granularity without affecting the userland
32555d2c71bSBruce M Simpson  * interface, which is a simple socket option toggle switch,
32655d2c71bSBruce M Simpson  * TCP_SIGNATURE_ENABLE.
32755d2c71bSBruce M Simpson  *
32855d2c71bSBruce M Simpson  * To allow per-service granularity requires that we have a means
32955d2c71bSBruce M Simpson  * of mapping port to SPI. The mandated way of doing this is to
33055d2c71bSBruce M Simpson  * use SPD entries to specify packet flows which get the TCP-MD5
33155d2c71bSBruce M Simpson  * treatment, however the code to do this is currently unstable
33255d2c71bSBruce M Simpson  * and unsuitable for production use.
33355d2c71bSBruce M Simpson  *
33455d2c71bSBruce M Simpson  * Therefore we use this compromise in the meantime.
33555d2c71bSBruce M Simpson  */
33655d2c71bSBruce M Simpson static int
33755d2c71bSBruce M Simpson tcpsignature_init(struct secasvar *sav, struct xformsw *xsp)
33855d2c71bSBruce M Simpson {
33955d2c71bSBruce M Simpson 	int keylen;
34055d2c71bSBruce M Simpson 
34155d2c71bSBruce M Simpson 	if (sav->alg_auth != SADB_X_AALG_TCP_MD5) {
34255d2c71bSBruce M Simpson 		DPRINTF(("%s: unsupported authentication algorithm %u\n",
34355d2c71bSBruce M Simpson 		    __func__, sav->alg_auth));
34455d2c71bSBruce M Simpson 		return (EINVAL);
34555d2c71bSBruce M Simpson 	}
34655d2c71bSBruce M Simpson 	if (sav->key_auth == NULL) {
34755d2c71bSBruce M Simpson 		DPRINTF(("%s: no authentication key present\n", __func__));
34855d2c71bSBruce M Simpson 		return (EINVAL);
34955d2c71bSBruce M Simpson 	}
35055d2c71bSBruce M Simpson 	keylen = _KEYLEN(sav->key_auth);
35155d2c71bSBruce M Simpson 	if ((keylen < TCP_KEYLEN_MIN) || (keylen > TCP_KEYLEN_MAX)) {
35255d2c71bSBruce M Simpson 		DPRINTF(("%s: invalid key length %u\n", __func__, keylen));
35355d2c71bSBruce M Simpson 		return (EINVAL);
35455d2c71bSBruce M Simpson 	}
355fcf59617SAndrey V. Elsukov 	sav->tdb_xform = xsp;
35655d2c71bSBruce M Simpson 	return (0);
35755d2c71bSBruce M Simpson }
35855d2c71bSBruce M Simpson 
35955d2c71bSBruce M Simpson /*
36055d2c71bSBruce M Simpson  * Called when the SA is deleted.
36155d2c71bSBruce M Simpson  */
36255d2c71bSBruce M Simpson static int
36355d2c71bSBruce M Simpson tcpsignature_zeroize(struct secasvar *sav)
36455d2c71bSBruce M Simpson {
36555d2c71bSBruce M Simpson 
366fcf59617SAndrey V. Elsukov 	if (sav->key_auth != NULL)
367a0196c3cSGeorge V. Neville-Neil 		bzero(sav->key_auth->key_data, _KEYLEN(sav->key_auth));
36855d2c71bSBruce M Simpson 	sav->tdb_xform = NULL;
36955d2c71bSBruce M Simpson 	return (0);
37055d2c71bSBruce M Simpson }
37155d2c71bSBruce M Simpson 
37255d2c71bSBruce M Simpson static struct xformsw tcpsignature_xformsw = {
373fcf59617SAndrey V. Elsukov 	.xf_type =	XF_TCPSIGNATURE,
374fcf59617SAndrey V. Elsukov 	.xf_name =	"TCP-MD5",
375fcf59617SAndrey V. Elsukov 	.xf_init =	tcpsignature_init,
376fcf59617SAndrey V. Elsukov 	.xf_zeroize =	tcpsignature_zeroize,
37755d2c71bSBruce M Simpson };
37855d2c71bSBruce M Simpson 
379fcf59617SAndrey V. Elsukov static const struct tcpmd5_methods tcpmd5_methods = {
380fcf59617SAndrey V. Elsukov 	.input = tcp_ipsec_input,
381fcf59617SAndrey V. Elsukov 	.output = tcp_ipsec_output,
382fcf59617SAndrey V. Elsukov 	.pcbctl = tcp_ipsec_pcbctl,
383fcf59617SAndrey V. Elsukov };
384fcf59617SAndrey V. Elsukov 
385fcf59617SAndrey V. Elsukov #ifndef KLD_MODULE
386fcf59617SAndrey V. Elsukov /* TCP-MD5 support is build in the kernel */
387fcf59617SAndrey V. Elsukov static const struct tcpmd5_support tcpmd5_ipsec = {
388fcf59617SAndrey V. Elsukov 	.enabled = IPSEC_MODULE_ENABLED,
389fcf59617SAndrey V. Elsukov 	.methods = &tcpmd5_methods
390fcf59617SAndrey V. Elsukov };
391fcf59617SAndrey V. Elsukov const struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
392fcf59617SAndrey V. Elsukov #endif /* !KLD_MODULE */
393fcf59617SAndrey V. Elsukov 
394fcf59617SAndrey V. Elsukov static int
395fcf59617SAndrey V. Elsukov tcpmd5_modevent(module_t mod, int type, void *data)
39655d2c71bSBruce M Simpson {
39755d2c71bSBruce M Simpson 
398fcf59617SAndrey V. Elsukov 	switch (type) {
399fcf59617SAndrey V. Elsukov 	case MOD_LOAD:
400fcf59617SAndrey V. Elsukov 		xform_attach(&tcpsignature_xformsw);
401fcf59617SAndrey V. Elsukov #ifdef KLD_MODULE
402fcf59617SAndrey V. Elsukov 		tcpmd5_support_enable(&tcpmd5_methods);
403fcf59617SAndrey V. Elsukov #endif
404fcf59617SAndrey V. Elsukov 		break;
405fcf59617SAndrey V. Elsukov 	case MOD_UNLOAD:
406fcf59617SAndrey V. Elsukov #ifdef KLD_MODULE
407fcf59617SAndrey V. Elsukov 		tcpmd5_support_disable();
408fcf59617SAndrey V. Elsukov #endif
409fcf59617SAndrey V. Elsukov 		xform_detach(&tcpsignature_xformsw);
410fcf59617SAndrey V. Elsukov 		break;
411fcf59617SAndrey V. Elsukov 	default:
412fcf59617SAndrey V. Elsukov 		return (EOPNOTSUPP);
413fcf59617SAndrey V. Elsukov 	}
414fcf59617SAndrey V. Elsukov 	return (0);
41555d2c71bSBruce M Simpson }
41655d2c71bSBruce M Simpson 
417fcf59617SAndrey V. Elsukov static moduledata_t tcpmd5_mod = {
418fcf59617SAndrey V. Elsukov 	"tcpmd5",
419fcf59617SAndrey V. Elsukov 	tcpmd5_modevent,
420fcf59617SAndrey V. Elsukov 	0
421fcf59617SAndrey V. Elsukov };
422fcf59617SAndrey V. Elsukov 
423fcf59617SAndrey V. Elsukov DECLARE_MODULE(tcpmd5, tcpmd5_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);
424fcf59617SAndrey V. Elsukov MODULE_VERSION(tcpmd5, 1);
425fcf59617SAndrey V. Elsukov #ifdef KLD_MODULE
426fcf59617SAndrey V. Elsukov MODULE_DEPEND(tcpmd5, ipsec_support, 1, 1, 1);
427fcf59617SAndrey V. Elsukov #endif
428