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