1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 2016 by the Massachusetts Institute of Technology. 4 * All Rights Reserved. 5 * 6 * Export of this software from the United States of America may 7 * require a specific license from the United States Government. 8 * It is the responsibility of any person or organization contemplating 9 * export to obtain such a license before exporting. 10 * 11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 12 * distribute this software and its documentation for any purpose and 13 * without fee is hereby granted, provided that the above copyright 14 * notice appear in all copies and that both that copyright notice and 15 * this permission notice appear in supporting documentation, and that 16 * the name of M.I.T. not be used in advertising or publicity pertaining 17 * to distribution of the software without specific, written prior 18 * permission. Furthermore if you modify this software you must label 19 * your software as modified software and not distribute it in such a 20 * fashion that it might be confused with the original M.I.T. software. 21 * M.I.T. makes no representations about the suitability of 22 * this software for any purpose. It is provided "as is" without express 23 * or implied warranty. 24 */ 25 26 /* macOS requires this define for IPV6_PKTINFO. */ 27 #define __APPLE_USE_RFC_3542 28 29 #include "udppktinfo.h" 30 31 #include <netinet/in.h> 32 #include <sys/socket.h> 33 34 #if defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO) 35 #define HAVE_IP_PKTINFO 36 #endif 37 38 #if defined(IPV6_PKTINFO) && defined(HAVE_STRUCT_IN6_PKTINFO) 39 #define HAVE_IPV6_PKTINFO 40 #endif 41 42 #if defined(HAVE_IP_PKTINFO) || defined(IP_SENDSRCADDR) || \ 43 defined(HAVE_IPV6_PKTINFO) 44 #define HAVE_PKTINFO_SUPPORT 45 #endif 46 47 /* Use RFC 3542 API below, but fall back from IPV6_RECVPKTINFO to IPV6_PKTINFO 48 * for RFC 2292 implementations. */ 49 #if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO) 50 #define IPV6_RECVPKTINFO IPV6_PKTINFO 51 #endif 52 53 /* Parallel, though not standardized. */ 54 #if !defined(IP_RECVPKTINFO) && defined(IP_PKTINFO) 55 #define IP_RECVPKTINFO IP_PKTINFO 56 #endif /* IP_RECVPKTINFO */ 57 58 #if defined(CMSG_SPACE) && defined(HAVE_STRUCT_CMSGHDR) && \ 59 defined(HAVE_PKTINFO_SUPPORT) 60 union pktinfo { 61 #ifdef HAVE_STRUCT_IN6_PKTINFO 62 struct in6_pktinfo pi6; 63 #endif 64 #ifdef HAVE_STRUCT_IN_PKTINFO 65 struct in_pktinfo pi4; 66 #endif 67 #ifdef IP_RECVDSTADDR 68 struct in_addr iaddr; 69 #endif 70 char c; 71 }; 72 #endif /* HAVE_IPV6_PKTINFO && HAVE_STRUCT_CMSGHDR && HAVE_PKTINFO_SUPPORT */ 73 74 #ifdef HAVE_IP_PKTINFO 75 76 #define set_ipv4_pktinfo set_ipv4_recvpktinfo 77 static inline krb5_error_code 78 set_ipv4_recvpktinfo(int sock) 79 { 80 int sockopt = 1; 81 return setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO, &sockopt, 82 sizeof(sockopt)); 83 } 84 85 #elif defined(IP_RECVDSTADDR) /* HAVE_IP_PKTINFO */ 86 87 #define set_ipv4_pktinfo set_ipv4_recvdstaddr 88 static inline krb5_error_code 89 set_ipv4_recvdstaddr(int sock) 90 { 91 int sockopt = 1; 92 return setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &sockopt, 93 sizeof(sockopt)); 94 } 95 96 #else /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */ 97 #define set_ipv4_pktinfo(s) EINVAL 98 #endif /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */ 99 100 #ifdef HAVE_IPV6_PKTINFO 101 102 #define set_ipv6_pktinfo set_ipv6_recvpktinfo 103 static inline krb5_error_code 104 set_ipv6_recvpktinfo(int sock) 105 { 106 int sockopt = 1; 107 return setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &sockopt, 108 sizeof(sockopt)); 109 } 110 111 #else /* HAVE_IPV6_PKTINFO */ 112 #define set_ipv6_pktinfo(s) EINVAL 113 #endif /* HAVE_IPV6_PKTINFO */ 114 115 /* 116 * Set pktinfo option on a socket. Takes a socket and the socket address family 117 * as arguments. 118 * 119 * Returns 0 on success, EINVAL if pktinfo is not supported for the address 120 * family. 121 */ 122 krb5_error_code 123 set_pktinfo(int sock, int family) 124 { 125 switch (family) { 126 case AF_INET: 127 return set_ipv4_pktinfo(sock); 128 case AF_INET6: 129 return set_ipv6_pktinfo(sock); 130 default: 131 return EINVAL; 132 } 133 } 134 135 #if defined(HAVE_PKTINFO_SUPPORT) && defined(CMSG_SPACE) 136 137 /* 138 * Check if a socket is bound to a wildcard address. 139 * Returns 1 if it is, 0 if it's bound to a specific address, or -1 on error 140 * with errno set to the error. 141 */ 142 static int 143 is_socket_bound_to_wildcard(int sock) 144 { 145 struct sockaddr_storage bound_addr; 146 socklen_t bound_addr_len = sizeof(bound_addr); 147 struct sockaddr *sa = ss2sa(&bound_addr); 148 149 if (getsockname(sock, sa, &bound_addr_len) < 0) 150 return -1; 151 152 if (!sa_is_inet(sa)) { 153 errno = EINVAL; 154 return -1; 155 } 156 157 return sa_is_wildcard(sa); 158 } 159 160 #ifdef HAVE_IP_PKTINFO 161 162 static inline struct in_pktinfo * 163 cmsg2pktinfo(struct cmsghdr *cmsgptr) 164 { 165 return (struct in_pktinfo *)(void *)CMSG_DATA(cmsgptr); 166 } 167 168 #define check_cmsg_v4_pktinfo check_cmsg_ip_pktinfo 169 static int 170 check_cmsg_ip_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr *to, 171 socklen_t *tolen, aux_addressing_info *auxaddr) 172 { 173 struct in_pktinfo *pktinfo; 174 175 if (cmsgptr->cmsg_level == IPPROTO_IP && 176 cmsgptr->cmsg_type == IP_PKTINFO && 177 *tolen >= sizeof(struct sockaddr_in)) { 178 179 memset(to, 0, sizeof(struct sockaddr_in)); 180 pktinfo = cmsg2pktinfo(cmsgptr); 181 sa2sin(to)->sin_addr = pktinfo->ipi_addr; 182 sa2sin(to)->sin_family = AF_INET; 183 *tolen = sizeof(struct sockaddr_in); 184 return 1; 185 } 186 return 0; 187 } 188 189 #elif defined(IP_RECVDSTADDR) /* HAVE_IP_PKTINFO */ 190 191 static inline struct in_addr * 192 cmsg2sin(struct cmsghdr *cmsgptr) 193 { 194 return (struct in_addr *)(void *)CMSG_DATA(cmsgptr); 195 } 196 197 #define check_cmsg_v4_pktinfo check_cmsg_ip_recvdstaddr 198 static int 199 check_cmsg_ip_recvdstaddr(struct cmsghdr *cmsgptr, struct sockaddr *to, 200 socklen_t *tolen, aux_addressing_info * auxaddr) 201 { 202 if (cmsgptr->cmsg_level == IPPROTO_IP && 203 cmsgptr->cmsg_type == IP_RECVDSTADDR && 204 *tolen >= sizeof(struct sockaddr_in)) { 205 struct in_addr *sin_addr; 206 207 memset(to, 0, sizeof(struct sockaddr_in)); 208 sin_addr = cmsg2sin(cmsgptr); 209 sa2sin(to)->sin_addr = *sin_addr; 210 sa2sin(to)->sin_family = AF_INET; 211 *tolen = sizeof(struct sockaddr_in); 212 return 1; 213 } 214 return 0; 215 } 216 217 #else /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */ 218 #define check_cmsg_v4_pktinfo(c, t, l, a) 0 219 #endif /* HAVE_IP_PKTINFO || IP_RECVDSTADDR */ 220 221 #ifdef HAVE_IPV6_PKTINFO 222 223 static inline struct in6_pktinfo * 224 cmsg2pktinfo6(struct cmsghdr *cmsgptr) 225 { 226 return (struct in6_pktinfo *)(void *)CMSG_DATA(cmsgptr); 227 } 228 229 #define check_cmsg_v6_pktinfo check_cmsg_ipv6_pktinfo 230 static int 231 check_cmsg_ipv6_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr *to, 232 socklen_t *tolen, aux_addressing_info *auxaddr) 233 { 234 struct in6_pktinfo *pktinfo; 235 236 if (cmsgptr->cmsg_level == IPPROTO_IPV6 && 237 cmsgptr->cmsg_type == IPV6_PKTINFO && 238 *tolen >= sizeof(struct sockaddr_in6)) { 239 240 memset(to, 0, sizeof(struct sockaddr_in6)); 241 pktinfo = cmsg2pktinfo6(cmsgptr); 242 sa2sin6(to)->sin6_addr = pktinfo->ipi6_addr; 243 sa2sin6(to)->sin6_family = AF_INET6; 244 *tolen = sizeof(struct sockaddr_in6); 245 auxaddr->ipv6_ifindex = pktinfo->ipi6_ifindex; 246 return 1; 247 } 248 return 0; 249 } 250 #else /* HAVE_IPV6_PKTINFO */ 251 #define check_cmsg_v6_pktinfo(c, t, l, a) 0 252 #endif /* HAVE_IPV6_PKTINFO */ 253 254 static int 255 check_cmsg_pktinfo(struct cmsghdr *cmsgptr, struct sockaddr *to, 256 socklen_t *tolen, aux_addressing_info *auxaddr) 257 { 258 return check_cmsg_v4_pktinfo(cmsgptr, to, tolen, auxaddr) || 259 check_cmsg_v6_pktinfo(cmsgptr, to, tolen, auxaddr); 260 } 261 262 /* 263 * Receive a message from a socket. 264 * 265 * Arguments: 266 * sock 267 * buf - The buffer to store the message in. 268 * len - buf length 269 * flags 270 * from - Set to the address that sent the message 271 * fromlen 272 * to - Set to the address that the message was sent to if possible. 273 * May not be set in certain cases such as if pktinfo support is 274 * missing. May be NULL. 275 * tolen 276 * auxaddr - Miscellaneous address information. 277 * 278 * Returns 0 on success, otherwise an error code. 279 */ 280 krb5_error_code 281 recv_from_to(int sock, void *buf, size_t len, int flags, 282 struct sockaddr *from, socklen_t * fromlen, 283 struct sockaddr *to, socklen_t * tolen, 284 aux_addressing_info *auxaddr) 285 286 { 287 int r; 288 struct iovec iov; 289 char cmsg[CMSG_SPACE(sizeof(union pktinfo))]; 290 struct cmsghdr *cmsgptr; 291 struct msghdr msg; 292 293 /* Don't use pktinfo if the socket isn't bound to a wildcard address. */ 294 r = is_socket_bound_to_wildcard(sock); 295 if (r < 0) 296 return errno; 297 298 if (!to || !tolen || !r) 299 return recvfrom(sock, buf, len, flags, from, fromlen); 300 301 /* Clobber with something recognizable in case we can't extract the address 302 * but try to use it anyways. */ 303 memset(to, 0x40, *tolen); 304 305 iov.iov_base = buf; 306 iov.iov_len = len; 307 memset(&msg, 0, sizeof(msg)); 308 msg.msg_name = from; 309 msg.msg_namelen = *fromlen; 310 msg.msg_iov = &iov; 311 msg.msg_iovlen = 1; 312 msg.msg_control = cmsg; 313 msg.msg_controllen = sizeof(cmsg); 314 315 r = recvmsg(sock, &msg, flags); 316 if (r < 0) 317 return r; 318 *fromlen = msg.msg_namelen; 319 320 /* 321 * On Darwin (and presumably all *BSD with KAME stacks), CMSG_FIRSTHDR 322 * doesn't check for a non-zero controllen. RFC 3542 recommends making 323 * this check, even though the (new) spec for CMSG_FIRSTHDR says it's 324 * supposed to do the check. 325 */ 326 if (msg.msg_controllen) { 327 cmsgptr = CMSG_FIRSTHDR(&msg); 328 while (cmsgptr) { 329 if (check_cmsg_pktinfo(cmsgptr, to, tolen, auxaddr)) 330 return r; 331 cmsgptr = CMSG_NXTHDR(&msg, cmsgptr); 332 } 333 } 334 /* No info about destination addr was available. */ 335 *tolen = 0; 336 return r; 337 } 338 339 #ifdef HAVE_IP_PKTINFO 340 341 #define set_msg_from_ipv4 set_msg_from_ip_pktinfo 342 static krb5_error_code 343 set_msg_from_ip_pktinfo(struct msghdr *msg, struct cmsghdr *cmsgptr, 344 struct sockaddr *from, socklen_t fromlen, 345 aux_addressing_info *auxaddr) 346 { 347 struct in_pktinfo *p = cmsg2pktinfo(cmsgptr); 348 const struct sockaddr_in *from4 = sa2sin(from); 349 350 if (fromlen != sizeof(struct sockaddr_in)) 351 return EINVAL; 352 cmsgptr->cmsg_level = IPPROTO_IP; 353 cmsgptr->cmsg_type = IP_PKTINFO; 354 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); 355 p->ipi_spec_dst = from4->sin_addr; 356 357 msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); 358 return 0; 359 } 360 361 #elif defined(IP_SENDSRCADDR) /* HAVE_IP_PKTINFO */ 362 363 #define set_msg_from_ipv4 set_msg_from_ip_sendsrcaddr 364 static krb5_error_code 365 set_msg_from_ip_sendsrcaddr(struct msghdr *msg, struct cmsghdr *cmsgptr, 366 struct sockaddr *from, socklen_t fromlen, 367 aux_addressing_info *auxaddr) 368 { 369 struct in_addr *sin_addr = cmsg2sin(cmsgptr); 370 const struct sockaddr_in *from4 = sa2sin(from); 371 if (fromlen != sizeof(struct sockaddr_in)) 372 return EINVAL; 373 cmsgptr->cmsg_level = IPPROTO_IP; 374 cmsgptr->cmsg_type = IP_SENDSRCADDR; 375 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); 376 msg->msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); 377 *sin_addr = from4->sin_addr; 378 return 0; 379 } 380 381 #else /* HAVE_IP_PKTINFO || IP_SENDSRCADDR */ 382 #define set_msg_from_ipv4(m, c, f, l, a) EINVAL 383 #endif /* HAVE_IP_PKTINFO || IP_SENDSRCADDR */ 384 385 #ifdef HAVE_IPV6_PKTINFO 386 387 #define set_msg_from_ipv6 set_msg_from_ipv6_pktinfo 388 static krb5_error_code 389 set_msg_from_ipv6_pktinfo(struct msghdr *msg, struct cmsghdr *cmsgptr, 390 struct sockaddr *from, socklen_t fromlen, 391 aux_addressing_info *auxaddr) 392 { 393 struct in6_pktinfo *p = cmsg2pktinfo6(cmsgptr); 394 const struct sockaddr_in6 *from6 = sa2sin6(from); 395 396 if (fromlen != sizeof(struct sockaddr_in6)) 397 return EINVAL; 398 cmsgptr->cmsg_level = IPPROTO_IPV6; 399 cmsgptr->cmsg_type = IPV6_PKTINFO; 400 cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 401 402 p->ipi6_addr = from6->sin6_addr; 403 /* 404 * Because of the possibility of asymmetric routing, we 405 * normally don't want to specify an interface. However, 406 * macOS doesn't like sending from a link-local address 407 * (which can come up in testing at least, if you wind up 408 * with a "foo.local" name) unless we do specify the 409 * interface. 410 */ 411 if (IN6_IS_ADDR_LINKLOCAL(&from6->sin6_addr)) 412 p->ipi6_ifindex = auxaddr->ipv6_ifindex; 413 /* otherwise, already zero */ 414 415 msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); 416 return 0; 417 } 418 419 #else /* HAVE_IPV6_PKTINFO */ 420 #define set_msg_from_ipv6(m, c, f, l, a) EINVAL 421 #endif /* HAVE_IPV6_PKTINFO */ 422 423 static krb5_error_code 424 set_msg_from(int family, struct msghdr *msg, struct cmsghdr *cmsgptr, 425 struct sockaddr *from, socklen_t fromlen, 426 aux_addressing_info *auxaddr) 427 { 428 switch (family) { 429 case AF_INET: 430 return set_msg_from_ipv4(msg, cmsgptr, from, fromlen, auxaddr); 431 case AF_INET6: 432 return set_msg_from_ipv6(msg, cmsgptr, from, fromlen, auxaddr); 433 } 434 435 return EINVAL; 436 } 437 438 /* 439 * Send a message to an address. 440 * 441 * Arguments: 442 * sock 443 * buf - The message to send. 444 * len - buf length 445 * flags 446 * to - The address to send the message to. 447 * tolen 448 * from - The address to attempt to send the message from. May be NULL. 449 * fromlen 450 * auxaddr - Miscellaneous address information. 451 * 452 * Returns 0 on success, otherwise an error code. 453 */ 454 krb5_error_code 455 send_to_from(int sock, void *buf, size_t len, int flags, 456 const struct sockaddr *to, socklen_t tolen, struct sockaddr *from, 457 socklen_t fromlen, aux_addressing_info *auxaddr) 458 { 459 int r; 460 struct iovec iov; 461 struct msghdr msg; 462 struct cmsghdr *cmsgptr; 463 char cbuf[CMSG_SPACE(sizeof(union pktinfo))]; 464 465 /* Don't use pktinfo if the socket isn't bound to a wildcard address. */ 466 r = is_socket_bound_to_wildcard(sock); 467 if (r < 0) 468 return errno; 469 470 if (from == NULL || fromlen == 0 || from->sa_family != to->sa_family || !r) 471 goto use_sendto; 472 473 iov.iov_base = buf; 474 iov.iov_len = len; 475 /* Truncation? */ 476 if (iov.iov_len != len) 477 return EINVAL; 478 memset(cbuf, 0, sizeof(cbuf)); 479 memset(&msg, 0, sizeof(msg)); 480 msg.msg_name = (void *)to; 481 msg.msg_namelen = tolen; 482 msg.msg_iov = &iov; 483 msg.msg_iovlen = 1; 484 msg.msg_control = cbuf; 485 /* CMSG_FIRSTHDR needs a non-zero controllen, or it'll return NULL on 486 * Linux. */ 487 msg.msg_controllen = sizeof(cbuf); 488 cmsgptr = CMSG_FIRSTHDR(&msg); 489 msg.msg_controllen = 0; 490 491 if (set_msg_from(from->sa_family, &msg, cmsgptr, from, fromlen, auxaddr)) 492 goto use_sendto; 493 return sendmsg(sock, &msg, flags); 494 495 use_sendto: 496 return sendto(sock, buf, len, flags, to, tolen); 497 } 498 499 #else /* HAVE_PKTINFO_SUPPORT && CMSG_SPACE */ 500 501 krb5_error_code 502 recv_from_to(int sock, void *buf, size_t len, int flags, 503 struct sockaddr *from, socklen_t *fromlen, 504 struct sockaddr *to, socklen_t *tolen, 505 aux_addressing_info *auxaddr) 506 { 507 if (to && tolen) { 508 /* Clobber with something recognizable in case we try to use the 509 * address. */ 510 memset(to, 0x40, *tolen); 511 *tolen = 0; 512 } 513 514 return recvfrom(sock, buf, len, flags, from, fromlen); 515 } 516 517 krb5_error_code 518 send_to_from(int sock, void *buf, size_t len, int flags, 519 const struct sockaddr *to, socklen_t tolen, 520 struct sockaddr *from, socklen_t fromlen, 521 aux_addressing_info *auxaddr) 522 { 523 return sendto(sock, buf, len, flags, to, tolen); 524 } 525 526 #endif /* HAVE_PKTINFO_SUPPORT && CMSG_SPACE */ 527