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