1/*- 2 * Copyright (c) 2003 Bruce M. Simpson <bms@spc.org> 3 * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29/* TCP MD5 Signature Option (RFC2385) */ 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/11/sys/netipsec/xform_tcp.c 315514 2017-03-18 22:04:20Z ae $"); 32 33#include "opt_inet.h" 34#include "opt_inet6.h" 35#include "opt_ipsec.h" 36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/mbuf.h> 40#include <sys/lock.h> 41#include <sys/md5.h> 42#include <sys/rmlock.h> 43#include <sys/socket.h> 44#include <sys/sockopt.h> 45#include <sys/kernel.h> 46#include <sys/module.h> 47#include <sys/protosw.h> 48 49#include <netinet/in.h> 50#include <netinet/in_pcb.h> 51#include <netinet/in_systm.h> 52#include <netinet/ip.h> 53#include <netinet/ip_var.h> 54#include <netinet/tcp.h> 55#include <netinet/tcp_var.h> 56 57#include <net/vnet.h> 58 59#include <netipsec/ipsec.h> 60#include <netipsec/ipsec_support.h> 61#include <netipsec/xform.h> 62 63#ifdef INET6 64#include <netinet/ip6.h> 65#include <netipsec/ipsec6.h> 66#endif 67 68#include <netipsec/key.h> 69#include <netipsec/key_debug.h> 70 71#define TCP_SIGLEN 16 /* length of computed digest in bytes */ 72#define TCP_KEYLEN_MIN 1 /* minimum length of TCP-MD5 key */ 73#define TCP_KEYLEN_MAX 80 /* maximum length of TCP-MD5 key */ 74 75static int 76tcp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt) 77{ 78 struct tcpcb *tp; 79 int error, optval; 80 81 INP_WLOCK_ASSERT(inp); 82 if (sopt->sopt_name != TCP_MD5SIG) { 83 INP_WUNLOCK(inp); 84 return (ENOPROTOOPT); 85 } 86 87 tp = intotcpcb(inp); 88 if (sopt->sopt_dir == SOPT_GET) { 89 optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0; 90 INP_WUNLOCK(inp); 91 92 /* On success return with released INP_WLOCK */ 93 return (sooptcopyout(sopt, &optval, sizeof(optval))); 94 } 95 96 INP_WUNLOCK(inp); 97 98 error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval)); 99 if (error != 0) 100 return (error); 101 102 /* INP_WLOCK_RECHECK */ 103 INP_WLOCK(inp); 104 if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 105 INP_WUNLOCK(inp); 106 return (ECONNRESET); 107 } 108 if (optval > 0) 109 tp->t_flags |= TF_SIGNATURE; 110 else 111 tp->t_flags &= ~TF_SIGNATURE; 112 113 /* On success return with acquired INP_WLOCK */ 114 return (error); 115} 116 117/* 118 * Callback function invoked by m_apply() to digest TCP segment data 119 * contained within an mbuf chain. 120 */ 121static int 122tcp_signature_apply(void *fstate, void *data, u_int len) 123{ 124 125 MD5Update(fstate, (u_char *)data, len); 126 return (0); 127} 128 129#ifdef INET 130static int 131ip_pseudo_compute(struct mbuf *m, MD5_CTX *ctx) 132{ 133 struct ippseudo ipp; 134 struct ip *ip; 135 136 ip = mtod(m, struct ip *); 137 ipp.ippseudo_src.s_addr = ip->ip_src.s_addr; 138 ipp.ippseudo_dst.s_addr = ip->ip_dst.s_addr; 139 ipp.ippseudo_p = IPPROTO_TCP; 140 ipp.ippseudo_pad = 0; 141 ipp.ippseudo_len = htons(m->m_pkthdr.len - (ip->ip_hl << 2)); 142 MD5Update(ctx, (char *)&ipp, sizeof(ipp)); 143 return (ip->ip_hl << 2); 144} 145#endif 146 147#ifdef INET6 148static int 149ip6_pseudo_compute(struct mbuf *m, MD5_CTX *ctx) 150{ 151 struct ip6_pseudo { 152 struct in6_addr src, dst; 153 uint32_t len; 154 uint32_t nxt; 155 } ip6p __aligned(4); 156 struct ip6_hdr *ip6; 157 158 ip6 = mtod(m, struct ip6_hdr *); 159 ip6p.src = ip6->ip6_src; 160 ip6p.dst = ip6->ip6_dst; 161 ip6p.len = htonl(m->m_pkthdr.len - sizeof(*ip6)); /* XXX: ext headers */ 162 ip6p.nxt = htonl(IPPROTO_TCP); 163 MD5Update(ctx, (char *)&ip6p, sizeof(ip6p)); 164 return (sizeof(*ip6)); 165} 166#endif 167 168static int 169tcp_signature_compute(struct mbuf *m, struct tcphdr *th, 170 struct secasvar *sav, u_char *buf) 171{ 172 MD5_CTX ctx; 173 int len; 174 u_short csum; 175 176 MD5Init(&ctx); 177 /* Step 1: Update MD5 hash with IP(v6) pseudo-header. */ 178 switch (sav->sah->saidx.dst.sa.sa_family) { 179#ifdef INET 180 case AF_INET: 181 len = ip_pseudo_compute(m, &ctx); 182 break; 183#endif 184#ifdef INET6 185 case AF_INET6: 186 len = ip6_pseudo_compute(m, &ctx); 187 break; 188#endif 189 default: 190 return (EAFNOSUPPORT); 191 } 192 /* 193 * Step 2: Update MD5 hash with TCP header, excluding options. 194 * The TCP checksum must be set to zero. 195 */ 196 csum = th->th_sum; 197 th->th_sum = 0; 198 MD5Update(&ctx, (char *)th, sizeof(struct tcphdr)); 199 th->th_sum = csum; 200 /* 201 * Step 3: Update MD5 hash with TCP segment data. 202 * Use m_apply() to avoid an early m_pullup(). 203 */ 204 len += (th->th_off << 2); 205 if (m->m_pkthdr.len - len > 0) 206 m_apply(m, len, m->m_pkthdr.len - len, 207 tcp_signature_apply, &ctx); 208 /* 209 * Step 4: Update MD5 hash with shared secret. 210 */ 211 MD5Update(&ctx, sav->key_auth->key_data, _KEYLEN(sav->key_auth)); 212 MD5Final(buf, &ctx); 213 key_sa_recordxfer(sav, m); 214 return (0); 215} 216 217static void 218setsockaddrs(const struct mbuf *m, union sockaddr_union *src, 219 union sockaddr_union *dst) 220{ 221 struct ip *ip; 222 223 IPSEC_ASSERT(m->m_len >= sizeof(*ip), ("unexpected mbuf len")); 224 225 ip = mtod(m, struct ip *); 226 switch (ip->ip_v) { 227#ifdef INET 228 case IPVERSION: 229 ipsec4_setsockaddrs(m, src, dst); 230 break; 231#endif 232#ifdef INET6 233 case (IPV6_VERSION >> 4): 234 ipsec6_setsockaddrs(m, src, dst); 235 break; 236#endif 237 default: 238 bzero(src, sizeof(*src)); 239 bzero(dst, sizeof(*dst)); 240 } 241} 242 243/* 244 * Compute TCP-MD5 hash of an *INBOUND* TCP segment. 245 * Parameters: 246 * m pointer to head of mbuf chain 247 * th pointer to TCP header 248 * buf pointer to storage for computed MD5 digest 249 * 250 * Return 0 if successful, otherwise return -1. 251 */ 252static int 253tcp_ipsec_input(struct mbuf *m, struct tcphdr *th, u_char *buf) 254{ 255 char tmpdigest[TCP_SIGLEN]; 256 struct secasindex saidx; 257 struct secasvar *sav; 258 259 setsockaddrs(m, &saidx.src, &saidx.dst); 260 saidx.proto = IPPROTO_TCP; 261 saidx.mode = IPSEC_MODE_TCPMD5; 262 saidx.reqid = 0; 263 sav = key_allocsa_tcpmd5(&saidx); 264 if (sav == NULL) { 265 KMOD_TCPSTAT_INC(tcps_sig_err_buildsig); 266 return (EACCES); 267 } 268 /* 269 * tcp_input() operates with TCP header fields in host 270 * byte order. We expect them in network byte order. 271 */ 272 tcp_fields_to_net(th); 273 tcp_signature_compute(m, th, sav, tmpdigest); 274 tcp_fields_to_host(th); 275 key_freesav(&sav); 276 if (bcmp(buf, tmpdigest, TCP_SIGLEN) != 0) { 277 KMOD_TCPSTAT_INC(tcps_sig_rcvbadsig); 278 return (EACCES); 279 } 280 KMOD_TCPSTAT_INC(tcps_sig_rcvgoodsig); 281 return (0); 282} 283 284/* 285 * Compute TCP-MD5 hash of an *OUTBOUND* TCP segment. 286 * Parameters: 287 * m pointer to head of mbuf chain 288 * th pointer to TCP header 289 * buf pointer to storage for computed MD5 digest 290 * 291 * Return 0 if successful, otherwise return error code. 292 */ 293static int 294tcp_ipsec_output(struct mbuf *m, struct tcphdr *th, u_char *buf) 295{ 296 struct secasindex saidx; 297 struct secasvar *sav; 298 299 setsockaddrs(m, &saidx.src, &saidx.dst); 300 saidx.proto = IPPROTO_TCP; 301 saidx.mode = IPSEC_MODE_TCPMD5; 302 saidx.reqid = 0; 303 sav = key_allocsa_tcpmd5(&saidx); 304 if (sav == NULL) { 305 KMOD_TCPSTAT_INC(tcps_sig_err_buildsig); 306 return (EACCES); 307 } 308 tcp_signature_compute(m, th, sav, buf); 309 key_freesav(&sav); 310 return (0); 311} 312 313/* 314 * Initialize a TCP-MD5 SA. Called when the SA is being set up. 315 * 316 * We don't need to set up the tdb prefixed fields, as we don't use the 317 * opencrypto code; we just perform a key length check. 318 * 319 * XXX: Currently we have used single 'magic' SPI and need to still 320 * support this. 321 * 322 * This allows per-host granularity without affecting the userland 323 * interface, which is a simple socket option toggle switch, 324 * TCP_SIGNATURE_ENABLE. 325 * 326 * To allow per-service granularity requires that we have a means 327 * of mapping port to SPI. The mandated way of doing this is to 328 * use SPD entries to specify packet flows which get the TCP-MD5 329 * treatment, however the code to do this is currently unstable 330 * and unsuitable for production use. 331 * 332 * Therefore we use this compromise in the meantime. 333 */ 334static int 335tcpsignature_init(struct secasvar *sav, struct xformsw *xsp) 336{ 337 int keylen; 338 339 if (sav->alg_auth != SADB_X_AALG_TCP_MD5) { 340 DPRINTF(("%s: unsupported authentication algorithm %u\n", 341 __func__, sav->alg_auth)); 342 return (EINVAL); 343 } 344 if (sav->key_auth == NULL) { 345 DPRINTF(("%s: no authentication key present\n", __func__)); 346 return (EINVAL); 347 } 348 keylen = _KEYLEN(sav->key_auth); 349 if ((keylen < TCP_KEYLEN_MIN) || (keylen > TCP_KEYLEN_MAX)) { 350 DPRINTF(("%s: invalid key length %u\n", __func__, keylen)); 351 return (EINVAL); 352 } 353 sav->tdb_xform = xsp; 354 return (0); 355} 356 357/* 358 * Called when the SA is deleted. 359 */ 360static int 361tcpsignature_zeroize(struct secasvar *sav) 362{ 363 364 if (sav->key_auth != NULL) 365 bzero(sav->key_auth->key_data, _KEYLEN(sav->key_auth)); 366 sav->tdb_xform = NULL; 367 return (0); 368} 369 370static struct xformsw tcpsignature_xformsw = { 371 .xf_type = XF_TCPSIGNATURE, 372 .xf_name = "TCP-MD5", 373 .xf_init = tcpsignature_init, 374 .xf_zeroize = tcpsignature_zeroize, 375}; 376 377static const struct tcpmd5_methods tcpmd5_methods = { 378 .input = tcp_ipsec_input, 379 .output = tcp_ipsec_output, 380 .pcbctl = tcp_ipsec_pcbctl, 381}; 382 383#ifndef KLD_MODULE 384/* TCP-MD5 support is build in the kernel */ 385static const struct tcpmd5_support tcpmd5_ipsec = { 386 .enabled = IPSEC_MODULE_ENABLED, 387 .methods = &tcpmd5_methods 388}; 389const struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec; 390#endif /* !KLD_MODULE */ 391 392static int 393tcpmd5_modevent(module_t mod, int type, void *data) 394{ 395 396 switch (type) { 397 case MOD_LOAD: 398 xform_attach(&tcpsignature_xformsw); 399#ifdef KLD_MODULE 400 tcpmd5_support_enable(&tcpmd5_methods); 401#endif 402 break; 403 case MOD_UNLOAD: 404#ifdef KLD_MODULE 405 tcpmd5_support_disable(); 406#endif 407 xform_detach(&tcpsignature_xformsw); 408 break; 409 default: 410 return (EOPNOTSUPP); 411 } 412 return (0); 413} 414 415static moduledata_t tcpmd5_mod = { 416 "tcpmd5", 417 tcpmd5_modevent, 418 0 419}; 420 421DECLARE_MODULE(tcpmd5, tcpmd5_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); 422MODULE_VERSION(tcpmd5, 1); 423#ifdef KLD_MODULE 424MODULE_DEPEND(tcpmd5, ipsec_support, 1, 1, 1); 425#endif 426