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