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 59 #include <net/vnet.h> 60 61 #include <netipsec/ipsec.h> 62 #include <netipsec/ipsec_support.h> 63 #include <netipsec/xform.h> 64 65 #ifdef INET6 66 #include <netinet/ip6.h> 67 #include <netipsec/ipsec6.h> 68 #endif 69 70 #include <netipsec/key.h> 71 #include <netipsec/key_debug.h> 72 73 #define TCP_SIGLEN 16 /* length of computed digest in bytes */ 74 #define TCP_KEYLEN_MIN 1 /* minimum length of TCP-MD5 key */ 75 #define TCP_KEYLEN_MAX 80 /* maximum length of TCP-MD5 key */ 76 77 static int 78 tcp_ipsec_pcbctl(struct inpcb *inp, struct sockopt *sopt) 79 { 80 struct tcpcb *tp; 81 int error, optval; 82 83 INP_WLOCK_ASSERT(inp); 84 if (sopt->sopt_name != TCP_MD5SIG) { 85 INP_WUNLOCK(inp); 86 return (ENOPROTOOPT); 87 } 88 89 tp = intotcpcb(inp); 90 if (sopt->sopt_dir == SOPT_GET) { 91 optval = (tp->t_flags & TF_SIGNATURE) ? 1 : 0; 92 INP_WUNLOCK(inp); 93 94 /* On success return with released INP_WLOCK */ 95 return (sooptcopyout(sopt, &optval, sizeof(optval))); 96 } 97 98 INP_WUNLOCK(inp); 99 100 error = sooptcopyin(sopt, &optval, sizeof(optval), sizeof(optval)); 101 if (error != 0) 102 return (error); 103 104 /* INP_WLOCK_RECHECK */ 105 INP_WLOCK(inp); 106 if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { 107 INP_WUNLOCK(inp); 108 return (ECONNRESET); 109 } 110 if (optval > 0) 111 tp->t_flags |= TF_SIGNATURE; 112 else 113 tp->t_flags &= ~TF_SIGNATURE; 114 115 /* On success return with acquired INP_WLOCK */ 116 return (error); 117 } 118 119 /* 120 * Callback function invoked by m_apply() to digest TCP segment data 121 * contained within an mbuf chain. 122 */ 123 static int 124 tcp_signature_apply(void *fstate, void *data, u_int len) 125 { 126 127 MD5Update(fstate, (u_char *)data, len); 128 return (0); 129 } 130 131 #ifdef INET 132 static int 133 ip_pseudo_compute(struct mbuf *m, MD5_CTX *ctx) 134 { 135 struct ippseudo ipp; 136 struct ip *ip; 137 138 ip = mtod(m, struct ip *); 139 ipp.ippseudo_src.s_addr = ip->ip_src.s_addr; 140 ipp.ippseudo_dst.s_addr = ip->ip_dst.s_addr; 141 ipp.ippseudo_p = IPPROTO_TCP; 142 ipp.ippseudo_pad = 0; 143 ipp.ippseudo_len = htons(m->m_pkthdr.len - (ip->ip_hl << 2)); 144 MD5Update(ctx, (char *)&ipp, sizeof(ipp)); 145 return (ip->ip_hl << 2); 146 } 147 #endif 148 149 #ifdef INET6 150 static int 151 ip6_pseudo_compute(struct mbuf *m, MD5_CTX *ctx) 152 { 153 struct ip6_pseudo { 154 struct in6_addr src, dst; 155 uint32_t len; 156 uint32_t nxt; 157 } ip6p __aligned(4); 158 struct ip6_hdr *ip6; 159 160 ip6 = mtod(m, struct ip6_hdr *); 161 ip6p.src = ip6->ip6_src; 162 ip6p.dst = ip6->ip6_dst; 163 ip6p.len = htonl(m->m_pkthdr.len - sizeof(*ip6)); /* XXX: ext headers */ 164 ip6p.nxt = htonl(IPPROTO_TCP); 165 MD5Update(ctx, (char *)&ip6p, sizeof(ip6p)); 166 return (sizeof(*ip6)); 167 } 168 #endif 169 170 static int 171 tcp_signature_compute(struct mbuf *m, struct tcphdr *th, 172 struct secasvar *sav, u_char *buf) 173 { 174 MD5_CTX ctx; 175 int len; 176 u_short csum; 177 178 MD5Init(&ctx); 179 /* Step 1: Update MD5 hash with IP(v6) pseudo-header. */ 180 switch (sav->sah->saidx.dst.sa.sa_family) { 181 #ifdef INET 182 case AF_INET: 183 len = ip_pseudo_compute(m, &ctx); 184 break; 185 #endif 186 #ifdef INET6 187 case AF_INET6: 188 len = ip6_pseudo_compute(m, &ctx); 189 break; 190 #endif 191 default: 192 return (EAFNOSUPPORT); 193 } 194 /* 195 * Step 2: Update MD5 hash with TCP header, excluding options. 196 * The TCP checksum must be set to zero. 197 */ 198 csum = th->th_sum; 199 th->th_sum = 0; 200 MD5Update(&ctx, (char *)th, sizeof(struct tcphdr)); 201 th->th_sum = csum; 202 /* 203 * Step 3: Update MD5 hash with TCP segment data. 204 * Use m_apply() to avoid an early m_pullup(). 205 */ 206 len += (th->th_off << 2); 207 if (m->m_pkthdr.len - len > 0) 208 m_apply(m, len, m->m_pkthdr.len - len, 209 tcp_signature_apply, &ctx); 210 /* 211 * Step 4: Update MD5 hash with shared secret. 212 */ 213 MD5Update(&ctx, sav->key_auth->key_data, _KEYLEN(sav->key_auth)); 214 MD5Final(buf, &ctx); 215 key_sa_recordxfer(sav, m); 216 return (0); 217 } 218 219 static void 220 setsockaddrs(const struct mbuf *m, union sockaddr_union *src, 221 union sockaddr_union *dst) 222 { 223 struct ip *ip; 224 225 IPSEC_ASSERT(m->m_len >= sizeof(*ip), ("unexpected mbuf len")); 226 227 ip = mtod(m, struct ip *); 228 switch (ip->ip_v) { 229 #ifdef INET 230 case IPVERSION: 231 ipsec4_setsockaddrs(m, src, dst); 232 break; 233 #endif 234 #ifdef INET6 235 case (IPV6_VERSION >> 4): 236 ipsec6_setsockaddrs(m, src, dst); 237 break; 238 #endif 239 default: 240 bzero(src, sizeof(*src)); 241 bzero(dst, sizeof(*dst)); 242 } 243 } 244 245 /* 246 * Compute TCP-MD5 hash of an *INBOUND* TCP segment. 247 * Parameters: 248 * m pointer to head of mbuf chain 249 * th pointer to TCP header 250 * buf pointer to storage for computed MD5 digest 251 * 252 * Return 0 if successful, otherwise return -1. 253 */ 254 static int 255 tcp_ipsec_input(struct mbuf *m, struct tcphdr *th, u_char *buf) 256 { 257 char tmpdigest[TCP_SIGLEN]; 258 struct secasindex saidx; 259 struct secasvar *sav; 260 261 setsockaddrs(m, &saidx.src, &saidx.dst); 262 saidx.proto = IPPROTO_TCP; 263 saidx.mode = IPSEC_MODE_TCPMD5; 264 saidx.reqid = 0; 265 sav = key_allocsa_tcpmd5(&saidx); 266 if (sav == NULL) { 267 KMOD_TCPSTAT_INC(tcps_sig_err_buildsig); 268 return (EACCES); 269 } 270 /* 271 * tcp_input() operates with TCP header fields in host 272 * byte order. We expect them in network byte order. 273 */ 274 tcp_fields_to_net(th); 275 tcp_signature_compute(m, th, sav, tmpdigest); 276 tcp_fields_to_host(th); 277 key_freesav(&sav); 278 if (bcmp(buf, tmpdigest, TCP_SIGLEN) != 0) { 279 KMOD_TCPSTAT_INC(tcps_sig_rcvbadsig); 280 return (EACCES); 281 } 282 KMOD_TCPSTAT_INC(tcps_sig_rcvgoodsig); 283 return (0); 284 } 285 286 /* 287 * Compute TCP-MD5 hash of an *OUTBOUND* TCP segment. 288 * Parameters: 289 * m pointer to head of mbuf chain 290 * th pointer to TCP header 291 * buf pointer to storage for computed MD5 digest 292 * 293 * Return 0 if successful, otherwise return error code. 294 */ 295 static int 296 tcp_ipsec_output(struct mbuf *m, struct tcphdr *th, u_char *buf) 297 { 298 struct secasindex saidx; 299 struct secasvar *sav; 300 301 setsockaddrs(m, &saidx.src, &saidx.dst); 302 saidx.proto = IPPROTO_TCP; 303 saidx.mode = IPSEC_MODE_TCPMD5; 304 saidx.reqid = 0; 305 sav = key_allocsa_tcpmd5(&saidx); 306 if (sav == NULL) { 307 KMOD_TCPSTAT_INC(tcps_sig_err_buildsig); 308 return (EACCES); 309 } 310 tcp_signature_compute(m, th, sav, buf); 311 key_freesav(&sav); 312 return (0); 313 } 314 315 /* 316 * Initialize a TCP-MD5 SA. Called when the SA is being set up. 317 * 318 * We don't need to set up the tdb prefixed fields, as we don't use the 319 * opencrypto code; we just perform a key length check. 320 * 321 * XXX: Currently we have used single 'magic' SPI and need to still 322 * support this. 323 * 324 * This allows per-host granularity without affecting the userland 325 * interface, which is a simple socket option toggle switch, 326 * TCP_SIGNATURE_ENABLE. 327 * 328 * To allow per-service granularity requires that we have a means 329 * of mapping port to SPI. The mandated way of doing this is to 330 * use SPD entries to specify packet flows which get the TCP-MD5 331 * treatment, however the code to do this is currently unstable 332 * and unsuitable for production use. 333 * 334 * Therefore we use this compromise in the meantime. 335 */ 336 static int 337 tcpsignature_init(struct secasvar *sav, struct xformsw *xsp) 338 { 339 int keylen; 340 341 if (sav->alg_auth != SADB_X_AALG_TCP_MD5) { 342 DPRINTF(("%s: unsupported authentication algorithm %u\n", 343 __func__, sav->alg_auth)); 344 return (EINVAL); 345 } 346 if (sav->key_auth == NULL) { 347 DPRINTF(("%s: no authentication key present\n", __func__)); 348 return (EINVAL); 349 } 350 keylen = _KEYLEN(sav->key_auth); 351 if ((keylen < TCP_KEYLEN_MIN) || (keylen > TCP_KEYLEN_MAX)) { 352 DPRINTF(("%s: invalid key length %u\n", __func__, keylen)); 353 return (EINVAL); 354 } 355 sav->tdb_xform = xsp; 356 return (0); 357 } 358 359 /* 360 * Called when the SA is deleted. 361 */ 362 static int 363 tcpsignature_zeroize(struct secasvar *sav) 364 { 365 366 if (sav->key_auth != NULL) 367 bzero(sav->key_auth->key_data, _KEYLEN(sav->key_auth)); 368 sav->tdb_xform = NULL; 369 return (0); 370 } 371 372 static struct xformsw tcpsignature_xformsw = { 373 .xf_type = XF_TCPSIGNATURE, 374 .xf_name = "TCP-MD5", 375 .xf_init = tcpsignature_init, 376 .xf_zeroize = tcpsignature_zeroize, 377 }; 378 379 static const struct tcpmd5_methods tcpmd5_methods = { 380 .input = tcp_ipsec_input, 381 .output = tcp_ipsec_output, 382 .pcbctl = tcp_ipsec_pcbctl, 383 }; 384 385 #ifndef KLD_MODULE 386 /* TCP-MD5 support is build in the kernel */ 387 static const struct tcpmd5_support tcpmd5_ipsec = { 388 .enabled = IPSEC_MODULE_ENABLED, 389 .methods = &tcpmd5_methods 390 }; 391 const struct tcpmd5_support * const tcp_ipsec_support = &tcpmd5_ipsec; 392 #endif /* !KLD_MODULE */ 393 394 static int 395 tcpmd5_modevent(module_t mod, int type, void *data) 396 { 397 398 switch (type) { 399 case MOD_LOAD: 400 xform_attach(&tcpsignature_xformsw); 401 #ifdef KLD_MODULE 402 tcpmd5_support_enable(&tcpmd5_methods); 403 #endif 404 break; 405 case MOD_UNLOAD: 406 #ifdef KLD_MODULE 407 tcpmd5_support_disable(); 408 #endif 409 xform_detach(&tcpsignature_xformsw); 410 break; 411 default: 412 return (EOPNOTSUPP); 413 } 414 return (0); 415 } 416 417 static moduledata_t tcpmd5_mod = { 418 "tcpmd5", 419 tcpmd5_modevent, 420 0 421 }; 422 423 DECLARE_MODULE(tcpmd5, tcpmd5_mod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY); 424 MODULE_VERSION(tcpmd5, 1); 425 #ifdef KLD_MODULE 426 MODULE_DEPEND(tcpmd5, ipsec_support, 1, 1, 1); 427 #endif 428