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