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