xref: /freebsd/sys/netipsec/xform_tcp.c (revision 0fb0711dba76a32a2202d2f41d64aa1247b5e51d)
1c398230bSWarner Losh /*-
2fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
3fe267a55SPedro 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>
58*0fb0711dSMichael Tuexen #include <netinet/udp.h>
5955d2c71bSBruce M Simpson 
60eddfbb76SRobert Watson #include <net/vnet.h>
61eddfbb76SRobert Watson 
6255d2c71bSBruce M Simpson #include <netipsec/ipsec.h>
63fcf59617SAndrey V. Elsukov #include <netipsec/ipsec_support.h>
6455d2c71bSBruce M Simpson #include <netipsec/xform.h>
6555d2c71bSBruce M Simpson 
6655d2c71bSBruce M Simpson #ifdef INET6
6755d2c71bSBruce M Simpson #include <netinet/ip6.h>
6855d2c71bSBruce M Simpson #include <netipsec/ipsec6.h>
6955d2c71bSBruce M Simpson #endif
7055d2c71bSBruce M Simpson 
7155d2c71bSBruce M Simpson #include <netipsec/key.h>
7255d2c71bSBruce M Simpson #include <netipsec/key_debug.h>
7355d2c71bSBruce M Simpson 
74fcf59617SAndrey V. Elsukov #define	TCP_SIGLEN	16	/* length of computed digest in bytes */
75fcf59617SAndrey V. Elsukov #define	TCP_KEYLEN_MIN	1	/* minimum length of TCP-MD5 key */
76fcf59617SAndrey V. Elsukov #define	TCP_KEYLEN_MAX	80	/* maximum length of TCP-MD5 key */
77fcf59617SAndrey V. Elsukov 
78fcf59617SAndrey V. Elsukov static int
79fcf59617SAndrey V. Elsukov tcp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt)
80fcf59617SAndrey V. Elsukov {
81fcf59617SAndrey V. Elsukov 	struct tcpcb *tp;
82fcf59617SAndrey V. Elsukov 	int error, optval;
83fcf59617SAndrey V. Elsukov 
84fcf59617SAndrey V. Elsukov 	if (sopt->sopt_name != TCP_MD5SIG) {
85fcf59617SAndrey V. Elsukov 		return (ENOPROTOOPT);
86fcf59617SAndrey V. Elsukov 	}
87fcf59617SAndrey V. Elsukov 
88fcf59617SAndrey V. Elsukov 	if (sopt->sopt_dir == SOPT_GET) {
89968ac175SSean Bruno 		INP_RLOCK(inp);
9053af6903SGleb Smirnoff 		if (inp->inp_flags & INP_DROPPED) {
91968ac175SSean Bruno 			INP_RUNLOCK(inp);
92968ac175SSean Bruno 			return (ECONNRESET);
93968ac175SSean Bruno 		}
94968ac175SSean Bruno 		tp = intotcpcb(inp);
95fcf59617SAndrey V. Elsukov 		optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0;
96968ac175SSean Bruno 		INP_RUNLOCK(inp);
97fcf59617SAndrey V. Elsukov 
98fcf59617SAndrey V. Elsukov 		/* On success return with released INP_WLOCK */
99fcf59617SAndrey V. Elsukov 		return (sooptcopyout(sopt, &optval, sizeof(optval)));
100fcf59617SAndrey V. Elsukov 	}
101fcf59617SAndrey V. Elsukov 
102fcf59617SAndrey V. Elsukov 	error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval));
103fcf59617SAndrey V. Elsukov 	if (error != 0)
104fcf59617SAndrey V. Elsukov 		return (error);
105fcf59617SAndrey V. Elsukov 
106fcf59617SAndrey V. Elsukov 	/* INP_WLOCK_RECHECK */
107fcf59617SAndrey V. Elsukov 	INP_WLOCK(inp);
10853af6903SGleb Smirnoff 	if (inp->inp_flags & INP_DROPPED) {
109fcf59617SAndrey V. Elsukov 		INP_WUNLOCK(inp);
110fcf59617SAndrey V. Elsukov 		return (ECONNRESET);
111fcf59617SAndrey V. Elsukov 	}
112968ac175SSean Bruno 	tp = intotcpcb(inp);
113fcf59617SAndrey V. Elsukov 	if (optval > 0)
114fcf59617SAndrey V. Elsukov 		tp->t_flags |= TF_SIGNATURE;
115fcf59617SAndrey V. Elsukov 	else
116fcf59617SAndrey V. Elsukov 		tp->t_flags &= ~TF_SIGNATURE;
117fcf59617SAndrey V. Elsukov 
118968ac175SSean Bruno 	INP_WUNLOCK(inp);
119fcf59617SAndrey V. Elsukov 	return (error);
120fcf59617SAndrey V. Elsukov }
121fcf59617SAndrey V. Elsukov 
122fcf59617SAndrey V. Elsukov /*
123fcf59617SAndrey V. Elsukov  * Callback function invoked by m_apply() to digest TCP segment data
124fcf59617SAndrey V. Elsukov  * contained within an mbuf chain.
125fcf59617SAndrey V. Elsukov  */
126fcf59617SAndrey V. Elsukov static int
127fcf59617SAndrey V. Elsukov tcp_signature_apply(void *fstate, void *data, u_int len)
128fcf59617SAndrey V. Elsukov {
129fcf59617SAndrey V. Elsukov 
130fcf59617SAndrey V. Elsukov 	MD5Update(fstate, (u_char *)data, len);
131fcf59617SAndrey V. Elsukov 	return (0);
132fcf59617SAndrey V. Elsukov }
133fcf59617SAndrey V. Elsukov 
134fcf59617SAndrey V. Elsukov #ifdef INET
135fcf59617SAndrey V. Elsukov static int
136fcf59617SAndrey V. Elsukov ip_pseudo_compute(struct mbuf *m, MD5_CTX *ctx)
137fcf59617SAndrey V. Elsukov {
138fcf59617SAndrey V. Elsukov 	struct ippseudo ipp;
139fcf59617SAndrey V. Elsukov 	struct ip *ip;
140*0fb0711dSMichael Tuexen 	int hdr_len;
141fcf59617SAndrey V. Elsukov 
142fcf59617SAndrey V. Elsukov 	ip = mtod(m, struct ip *);
143fcf59617SAndrey V. Elsukov 	ipp.ippseudo_src.s_addr = ip->ip_src.s_addr;
144fcf59617SAndrey V. Elsukov 	ipp.ippseudo_dst.s_addr = ip->ip_dst.s_addr;
145fcf59617SAndrey V. Elsukov 	ipp.ippseudo_p = IPPROTO_TCP;
146fcf59617SAndrey V. Elsukov 	ipp.ippseudo_pad = 0;
147*0fb0711dSMichael Tuexen 	hdr_len = ip->ip_hl << 2;
148*0fb0711dSMichael Tuexen 	if (ip->ip_p == IPPROTO_UDP)
149*0fb0711dSMichael Tuexen 		/* TCP over UDP */
150*0fb0711dSMichael Tuexen 		hdr_len += sizeof(struct udphdr);
151*0fb0711dSMichael Tuexen 	ipp.ippseudo_len = htons(m->m_pkthdr.len - hdr_len);
152fcf59617SAndrey V. Elsukov 	MD5Update(ctx, (char *)&ipp, sizeof(ipp));
153*0fb0711dSMichael Tuexen 	return (hdr_len);
154fcf59617SAndrey V. Elsukov }
155fcf59617SAndrey V. Elsukov #endif
156fcf59617SAndrey V. Elsukov 
157fcf59617SAndrey V. Elsukov #ifdef INET6
158fcf59617SAndrey V. Elsukov static int
159fcf59617SAndrey V. Elsukov ip6_pseudo_compute(struct mbuf *m, MD5_CTX *ctx)
160fcf59617SAndrey V. Elsukov {
161fcf59617SAndrey V. Elsukov 	struct ip6_pseudo {
162fcf59617SAndrey V. Elsukov 		struct in6_addr src, dst;
163fcf59617SAndrey V. Elsukov 		uint32_t len;
164fcf59617SAndrey V. Elsukov 		uint32_t nxt;
165fcf59617SAndrey V. Elsukov 	} ip6p __aligned(4);
166fcf59617SAndrey V. Elsukov 	struct ip6_hdr *ip6;
167*0fb0711dSMichael Tuexen 	int hdr_len;
168fcf59617SAndrey V. Elsukov 
169fcf59617SAndrey V. Elsukov 	ip6 = mtod(m, struct ip6_hdr *);
170fcf59617SAndrey V. Elsukov 	ip6p.src = ip6->ip6_src;
171fcf59617SAndrey V. Elsukov 	ip6p.dst = ip6->ip6_dst;
172*0fb0711dSMichael Tuexen 	hdr_len = sizeof(struct ip6_hdr);
173*0fb0711dSMichael Tuexen 	if (ip6->ip6_nxt == IPPROTO_UDP)
174*0fb0711dSMichael Tuexen 		/* TCP over UDP */
175*0fb0711dSMichael Tuexen 		hdr_len += sizeof(struct udphdr);
176*0fb0711dSMichael Tuexen 	/* XXX: ext headers */
177*0fb0711dSMichael Tuexen 	ip6p.len = htonl(m->m_pkthdr.len - hdr_len);
178fcf59617SAndrey V. Elsukov 	ip6p.nxt = htonl(IPPROTO_TCP);
179fcf59617SAndrey V. Elsukov 	MD5Update(ctx, (char *)&ip6p, sizeof(ip6p));
180*0fb0711dSMichael Tuexen 	return (hdr_len);
181fcf59617SAndrey V. Elsukov }
182fcf59617SAndrey V. Elsukov #endif
183fcf59617SAndrey V. Elsukov 
184fcf59617SAndrey V. Elsukov static int
185fcf59617SAndrey V. Elsukov tcp_signature_compute(struct mbuf *m, struct tcphdr *th,
186fcf59617SAndrey V. Elsukov     struct secasvar *sav, u_char *buf)
187fcf59617SAndrey V. Elsukov {
188fcf59617SAndrey V. Elsukov 	MD5_CTX ctx;
189fcf59617SAndrey V. Elsukov 	int len;
190fcf59617SAndrey V. Elsukov 	u_short csum;
191fcf59617SAndrey V. Elsukov 
192fcf59617SAndrey V. Elsukov 	MD5Init(&ctx);
193fcf59617SAndrey V. Elsukov 	 /* Step 1: Update MD5 hash with IP(v6) pseudo-header. */
194fcf59617SAndrey V. Elsukov 	switch (sav->sah->saidx.dst.sa.sa_family) {
195fcf59617SAndrey V. Elsukov #ifdef INET
196fcf59617SAndrey V. Elsukov 	case AF_INET:
197fcf59617SAndrey V. Elsukov 		len = ip_pseudo_compute(m, &ctx);
198fcf59617SAndrey V. Elsukov 		break;
199fcf59617SAndrey V. Elsukov #endif
200fcf59617SAndrey V. Elsukov #ifdef INET6
201fcf59617SAndrey V. Elsukov 	case AF_INET6:
202fcf59617SAndrey V. Elsukov 		len = ip6_pseudo_compute(m, &ctx);
203fcf59617SAndrey V. Elsukov 		break;
204fcf59617SAndrey V. Elsukov #endif
205fcf59617SAndrey V. Elsukov 	default:
206fcf59617SAndrey V. Elsukov 		return (EAFNOSUPPORT);
207fcf59617SAndrey V. Elsukov 	}
208fcf59617SAndrey V. Elsukov 	/*
209fcf59617SAndrey V. Elsukov 	 * Step 2: Update MD5 hash with TCP header, excluding options.
210fcf59617SAndrey V. Elsukov 	 * The TCP checksum must be set to zero.
211fcf59617SAndrey V. Elsukov 	 */
212fcf59617SAndrey V. Elsukov 	csum = th->th_sum;
213fcf59617SAndrey V. Elsukov 	th->th_sum = 0;
214fcf59617SAndrey V. Elsukov 	MD5Update(&ctx, (char *)th, sizeof(struct tcphdr));
215fcf59617SAndrey V. Elsukov 	th->th_sum = csum;
216fcf59617SAndrey V. Elsukov 	/*
217fcf59617SAndrey V. Elsukov 	 * Step 3: Update MD5 hash with TCP segment data.
218fcf59617SAndrey V. Elsukov 	 * Use m_apply() to avoid an early m_pullup().
219fcf59617SAndrey V. Elsukov 	 */
220fcf59617SAndrey V. Elsukov 	len += (th->th_off << 2);
221fcf59617SAndrey V. Elsukov 	if (m->m_pkthdr.len - len > 0)
222fcf59617SAndrey V. Elsukov 		m_apply(m, len, m->m_pkthdr.len - len,
223fcf59617SAndrey V. Elsukov 		    tcp_signature_apply, &ctx);
224fcf59617SAndrey V. Elsukov 	/*
225fcf59617SAndrey V. Elsukov 	 * Step 4: Update MD5 hash with shared secret.
226fcf59617SAndrey V. Elsukov 	 */
227fcf59617SAndrey V. Elsukov 	MD5Update(&ctx, sav->key_auth->key_data, _KEYLEN(sav->key_auth));
228fcf59617SAndrey V. Elsukov 	MD5Final(buf, &ctx);
229fcf59617SAndrey V. Elsukov 	key_sa_recordxfer(sav, m);
230fcf59617SAndrey V. Elsukov 	return (0);
231fcf59617SAndrey V. Elsukov }
232fcf59617SAndrey V. Elsukov 
233fcf59617SAndrey V. Elsukov static void
234fcf59617SAndrey V. Elsukov setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
235fcf59617SAndrey V. Elsukov     union sockaddr_union *dst)
236fcf59617SAndrey V. Elsukov {
237fcf59617SAndrey V. Elsukov 	struct ip *ip;
238fcf59617SAndrey V. Elsukov 
239fcf59617SAndrey V. Elsukov 	IPSEC_ASSERT(m->m_len >= sizeof(*ip), ("unexpected mbuf len"));
240fcf59617SAndrey V. Elsukov 
241fcf59617SAndrey V. Elsukov 	ip = mtod(m, struct ip *);
242fcf59617SAndrey V. Elsukov 	switch (ip->ip_v) {
243fcf59617SAndrey V. Elsukov #ifdef INET
244fcf59617SAndrey V. Elsukov 	case IPVERSION:
245fcf59617SAndrey V. Elsukov 		ipsec4_setsockaddrs(m, src, dst);
246fcf59617SAndrey V. Elsukov 		break;
247fcf59617SAndrey V. Elsukov #endif
248fcf59617SAndrey V. Elsukov #ifdef INET6
249fcf59617SAndrey V. Elsukov 	case (IPV6_VERSION >> 4):
250fcf59617SAndrey V. Elsukov 		ipsec6_setsockaddrs(m, src, dst);
251fcf59617SAndrey V. Elsukov 		break;
252fcf59617SAndrey V. Elsukov #endif
253fcf59617SAndrey V. Elsukov 	default:
254fcf59617SAndrey V. Elsukov 		bzero(src, sizeof(*src));
255fcf59617SAndrey V. Elsukov 		bzero(dst, sizeof(*dst));
256fcf59617SAndrey V. Elsukov 	}
257fcf59617SAndrey V. Elsukov }
258fcf59617SAndrey V. Elsukov 
259fcf59617SAndrey V. Elsukov /*
260fcf59617SAndrey V. Elsukov  * Compute TCP-MD5 hash of an *INBOUND* TCP segment.
261fcf59617SAndrey V. Elsukov  * Parameters:
262fcf59617SAndrey V. Elsukov  * m		pointer to head of mbuf chain
263fcf59617SAndrey V. Elsukov  * th		pointer to TCP header
264fcf59617SAndrey V. Elsukov  * buf		pointer to storage for computed MD5 digest
265fcf59617SAndrey V. Elsukov  *
26691d38811SRobert Wing  * Return 0 if successful, otherwise return error code.
267fcf59617SAndrey V. Elsukov  */
268fcf59617SAndrey V. Elsukov static int
269fcf59617SAndrey V. Elsukov tcp_ipsec_input(struct mbuf *m, struct tcphdr *th, u_char *buf)
270fcf59617SAndrey V. Elsukov {
271fcf59617SAndrey V. Elsukov 	char tmpdigest[TCP_SIGLEN];
272fcf59617SAndrey V. Elsukov 	struct secasindex saidx;
273fcf59617SAndrey V. Elsukov 	struct secasvar *sav;
274fcf59617SAndrey V. Elsukov 
275fcf59617SAndrey V. Elsukov 	setsockaddrs(m, &saidx.src, &saidx.dst);
276fcf59617SAndrey V. Elsukov 	saidx.proto = IPPROTO_TCP;
277fcf59617SAndrey V. Elsukov 	saidx.mode = IPSEC_MODE_TCPMD5;
278fcf59617SAndrey V. Elsukov 	saidx.reqid = 0;
279fcf59617SAndrey V. Elsukov 	sav = key_allocsa_tcpmd5(&saidx);
280fcf59617SAndrey V. Elsukov 	if (sav == NULL) {
281fcf59617SAndrey V. Elsukov 		KMOD_TCPSTAT_INC(tcps_sig_err_buildsig);
28291d38811SRobert Wing 		return (ENOENT);
283fcf59617SAndrey V. Elsukov 	}
284eb18708eSRobert Wing 	if (buf == NULL) {
285eb18708eSRobert Wing 		key_freesav(&sav);
286eb18708eSRobert Wing 		KMOD_TCPSTAT_INC(tcps_sig_err_nosigopt);
287eb18708eSRobert Wing 		return (EACCES);
288eb18708eSRobert Wing 	}
289fcf59617SAndrey V. Elsukov 	/*
290fcf59617SAndrey V. Elsukov 	 * tcp_input() operates with TCP header fields in host
291fcf59617SAndrey V. Elsukov 	 * byte order. We expect them in network byte order.
292fcf59617SAndrey V. Elsukov 	 */
293fcf59617SAndrey V. Elsukov 	tcp_fields_to_net(th);
294fcf59617SAndrey V. Elsukov 	tcp_signature_compute(m, th, sav, tmpdigest);
295fcf59617SAndrey V. Elsukov 	tcp_fields_to_host(th);
296fcf59617SAndrey V. Elsukov 	key_freesav(&sav);
297fcf59617SAndrey V. Elsukov 	if (bcmp(buf, tmpdigest, TCP_SIGLEN) != 0) {
298fcf59617SAndrey V. Elsukov 		KMOD_TCPSTAT_INC(tcps_sig_rcvbadsig);
299fcf59617SAndrey V. Elsukov 		return (EACCES);
300fcf59617SAndrey V. Elsukov 	}
301fcf59617SAndrey V. Elsukov 	KMOD_TCPSTAT_INC(tcps_sig_rcvgoodsig);
302fcf59617SAndrey V. Elsukov 	return (0);
303fcf59617SAndrey V. Elsukov }
304fcf59617SAndrey V. Elsukov 
305fcf59617SAndrey V. Elsukov /*
306fcf59617SAndrey V. Elsukov  * Compute TCP-MD5 hash of an *OUTBOUND* TCP segment.
307fcf59617SAndrey V. Elsukov  * Parameters:
308fcf59617SAndrey V. Elsukov  * m		pointer to head of mbuf chain
309fcf59617SAndrey V. Elsukov  * th		pointer to TCP header
310fcf59617SAndrey V. Elsukov  * buf		pointer to storage for computed MD5 digest
311fcf59617SAndrey V. Elsukov  *
312fcf59617SAndrey V. Elsukov  * Return 0 if successful, otherwise return error code.
313fcf59617SAndrey V. Elsukov  */
314fcf59617SAndrey V. Elsukov static int
315fcf59617SAndrey V. Elsukov tcp_ipsec_output(struct mbuf *m, struct tcphdr *th, u_char *buf)
316fcf59617SAndrey V. Elsukov {
317fcf59617SAndrey V. Elsukov 	struct secasindex saidx;
318fcf59617SAndrey V. Elsukov 	struct secasvar *sav;
319fcf59617SAndrey V. Elsukov 
320fcf59617SAndrey V. Elsukov 	setsockaddrs(m, &saidx.src, &saidx.dst);
321fcf59617SAndrey V. Elsukov 	saidx.proto = IPPROTO_TCP;
322fcf59617SAndrey V. Elsukov 	saidx.mode = IPSEC_MODE_TCPMD5;
323fcf59617SAndrey V. Elsukov 	saidx.reqid = 0;
324fcf59617SAndrey V. Elsukov 	sav = key_allocsa_tcpmd5(&saidx);
325fcf59617SAndrey V. Elsukov 	if (sav == NULL) {
326fcf59617SAndrey V. Elsukov 		KMOD_TCPSTAT_INC(tcps_sig_err_buildsig);
32791d38811SRobert Wing 		return (ENOENT);
328fcf59617SAndrey V. Elsukov 	}
329fcf59617SAndrey V. Elsukov 	tcp_signature_compute(m, th, sav, buf);
330fcf59617SAndrey V. Elsukov 	key_freesav(&sav);
331fcf59617SAndrey V. Elsukov 	return (0);
332fcf59617SAndrey V. Elsukov }
333fcf59617SAndrey V. Elsukov 
33455d2c71bSBruce M Simpson /*
33555d2c71bSBruce M Simpson  * Initialize a TCP-MD5 SA. Called when the SA is being set up.
33655d2c71bSBruce M Simpson  *
33755d2c71bSBruce M Simpson  * We don't need to set up the tdb prefixed fields, as we don't use the
33855d2c71bSBruce M Simpson  * opencrypto code; we just perform a key length check.
33955d2c71bSBruce M Simpson  *
340fcf59617SAndrey V. Elsukov  * XXX: Currently we have used single 'magic' SPI and need to still
341fcf59617SAndrey V. Elsukov  * support this.
34255d2c71bSBruce M Simpson  *
34355d2c71bSBruce M Simpson  * This allows per-host granularity without affecting the userland
34455d2c71bSBruce M Simpson  * interface, which is a simple socket option toggle switch,
34555d2c71bSBruce M Simpson  * TCP_SIGNATURE_ENABLE.
34655d2c71bSBruce M Simpson  *
34755d2c71bSBruce M Simpson  * To allow per-service granularity requires that we have a means
34855d2c71bSBruce M Simpson  * of mapping port to SPI. The mandated way of doing this is to
34955d2c71bSBruce M Simpson  * use SPD entries to specify packet flows which get the TCP-MD5
35055d2c71bSBruce M Simpson  * treatment, however the code to do this is currently unstable
35155d2c71bSBruce M Simpson  * and unsuitable for production use.
35255d2c71bSBruce M Simpson  *
35355d2c71bSBruce M Simpson  * Therefore we use this compromise in the meantime.
35455d2c71bSBruce M Simpson  */
35555d2c71bSBruce M Simpson static int
35655d2c71bSBruce M Simpson tcpsignature_init(struct secasvar *sav, struct xformsw *xsp)
35755d2c71bSBruce M Simpson {
35855d2c71bSBruce M Simpson 	int keylen;
35955d2c71bSBruce M Simpson 
36055d2c71bSBruce M Simpson 	if (sav->alg_auth != SADB_X_AALG_TCP_MD5) {
36155d2c71bSBruce M Simpson 		DPRINTF(("%s: unsupported authentication algorithm %u\n",
36255d2c71bSBruce M Simpson 		    __func__, sav->alg_auth));
36355d2c71bSBruce M Simpson 		return (EINVAL);
36455d2c71bSBruce M Simpson 	}
36555d2c71bSBruce M Simpson 	if (sav->key_auth == NULL) {
36655d2c71bSBruce M Simpson 		DPRINTF(("%s: no authentication key present\n", __func__));
36755d2c71bSBruce M Simpson 		return (EINVAL);
36855d2c71bSBruce M Simpson 	}
36955d2c71bSBruce M Simpson 	keylen = _KEYLEN(sav->key_auth);
37055d2c71bSBruce M Simpson 	if ((keylen < TCP_KEYLEN_MIN) || (keylen > TCP_KEYLEN_MAX)) {
37155d2c71bSBruce M Simpson 		DPRINTF(("%s: invalid key length %u\n", __func__, keylen));
37255d2c71bSBruce M Simpson 		return (EINVAL);
37355d2c71bSBruce M Simpson 	}
374fcf59617SAndrey V. Elsukov 	sav->tdb_xform = xsp;
37555d2c71bSBruce M Simpson 	return (0);
37655d2c71bSBruce M Simpson }
37755d2c71bSBruce M Simpson 
37855d2c71bSBruce M Simpson /*
37955d2c71bSBruce M Simpson  * Called when the SA is deleted.
38055d2c71bSBruce M Simpson  */
381dae61c9dSJohn Baldwin static void
382dae61c9dSJohn Baldwin tcpsignature_cleanup(struct secasvar *sav)
38355d2c71bSBruce M Simpson {
38455d2c71bSBruce M Simpson }
38555d2c71bSBruce M Simpson 
38655d2c71bSBruce M Simpson static struct xformsw tcpsignature_xformsw = {
387fcf59617SAndrey V. Elsukov 	.xf_type =	XF_TCPSIGNATURE,
388fcf59617SAndrey V. Elsukov 	.xf_name =	"TCP-MD5",
389fcf59617SAndrey V. Elsukov 	.xf_init =	tcpsignature_init,
390dae61c9dSJohn Baldwin 	.xf_cleanup =	tcpsignature_cleanup,
39155d2c71bSBruce M Simpson };
39255d2c71bSBruce M Simpson 
393fcf59617SAndrey V. Elsukov static const struct tcpmd5_methods tcpmd5_methods = {
394fcf59617SAndrey V. Elsukov 	.input = tcp_ipsec_input,
395fcf59617SAndrey V. Elsukov 	.output = tcp_ipsec_output,
396fcf59617SAndrey V. Elsukov 	.pcbctl = tcp_ipsec_pcbctl,
397fcf59617SAndrey V. Elsukov };
398fcf59617SAndrey V. Elsukov 
399fcf59617SAndrey V. Elsukov #ifndef KLD_MODULE
400fcf59617SAndrey V. Elsukov /* TCP-MD5 support is build in the kernel */
401fcf59617SAndrey V. Elsukov static const struct tcpmd5_support tcpmd5_ipsec = {
402fcf59617SAndrey V. Elsukov 	.enabled = IPSEC_MODULE_ENABLED,
403fcf59617SAndrey V. Elsukov 	.methods = &tcpmd5_methods
404fcf59617SAndrey V. Elsukov };
405fcf59617SAndrey V. Elsukov const struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec;
406fcf59617SAndrey V. Elsukov #endif /* !KLD_MODULE */
407fcf59617SAndrey V. Elsukov 
408fcf59617SAndrey V. Elsukov static int
409fcf59617SAndrey V. Elsukov tcpmd5_modevent(module_t mod, int type, void *data)
41055d2c71bSBruce M Simpson {
41155d2c71bSBruce M Simpson 
412fcf59617SAndrey V. Elsukov 	switch (type) {
413fcf59617SAndrey V. Elsukov 	case MOD_LOAD:
414fcf59617SAndrey V. Elsukov 		xform_attach(&tcpsignature_xformsw);
415fcf59617SAndrey V. Elsukov #ifdef KLD_MODULE
416fcf59617SAndrey V. Elsukov 		tcpmd5_support_enable(&tcpmd5_methods);
417fcf59617SAndrey V. Elsukov #endif
418fcf59617SAndrey V. Elsukov 		break;
419fcf59617SAndrey V. Elsukov 	case MOD_UNLOAD:
420fcf59617SAndrey V. Elsukov #ifdef KLD_MODULE
421fcf59617SAndrey V. Elsukov 		tcpmd5_support_disable();
422fcf59617SAndrey V. Elsukov #endif
423fcf59617SAndrey V. Elsukov 		xform_detach(&tcpsignature_xformsw);
424fcf59617SAndrey V. Elsukov 		break;
425fcf59617SAndrey V. Elsukov 	default:
426fcf59617SAndrey V. Elsukov 		return (EOPNOTSUPP);
427fcf59617SAndrey V. Elsukov 	}
428fcf59617SAndrey V. Elsukov 	return (0);
42955d2c71bSBruce M Simpson }
43055d2c71bSBruce M Simpson 
431fcf59617SAndrey V. Elsukov static moduledata_t tcpmd5_mod = {
432fcf59617SAndrey V. Elsukov 	"tcpmd5",
433fcf59617SAndrey V. Elsukov 	tcpmd5_modevent,
434fcf59617SAndrey V. Elsukov 	0
435fcf59617SAndrey V. Elsukov };
436fcf59617SAndrey V. Elsukov 
437fcf59617SAndrey V. Elsukov DECLARE_MODULE(tcpmd5, tcpmd5_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);
438fcf59617SAndrey V. Elsukov MODULE_VERSION(tcpmd5, 1);
439fcf59617SAndrey V. Elsukov #ifdef KLD_MODULE
440fcf59617SAndrey V. Elsukov MODULE_DEPEND(tcpmd5, ipsec_support, 1, 1, 1);
441fcf59617SAndrey V. Elsukov #endif
442