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