/* * Copyright (c) 1996, 1998-2005, 2007-2013 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ #include #include #include #include #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif /* STDC_HEADERS */ #ifdef HAVE_STRING_H # include #endif /* HAVE_STRING_H */ #ifdef HAVE_STRINGS_H # include #endif /* HAVE_STRINGS_H */ #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ #include #include #include "sudoers.h" #include "interfaces.h" static bool addr_matches_if(const char *n) { union sudo_in_addr_un addr; struct interface *ifp; #ifdef HAVE_STRUCT_IN6_ADDR unsigned int j; #endif unsigned int family; debug_decl(addr_matches_if, SUDO_DEBUG_MATCH) #ifdef HAVE_STRUCT_IN6_ADDR if (inet_pton(AF_INET6, n, &addr.ip6) == 1) { family = AF_INET6; } else #endif /* HAVE_STRUCT_IN6_ADDR */ if (inet_pton(AF_INET, n, &addr.ip4) == 1) { family = AF_INET; } else { debug_return_bool(false); } SLIST_FOREACH(ifp, get_interfaces(), entries) { if (ifp->family != family) continue; switch (family) { case AF_INET: if (ifp->addr.ip4.s_addr == addr.ip4.s_addr || (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr) == addr.ip4.s_addr) debug_return_bool(true); break; #ifdef HAVE_STRUCT_IN6_ADDR case AF_INET6: if (memcmp(ifp->addr.ip6.s6_addr, addr.ip6.s6_addr, sizeof(addr.ip6.s6_addr)) == 0) debug_return_bool(true); for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) { if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j]) break; } if (j == sizeof(addr.ip6.s6_addr)) debug_return_bool(true); break; #endif /* HAVE_STRUCT_IN6_ADDR */ } } debug_return_bool(false); } static bool addr_matches_if_netmask(const char *n, const char *m) { unsigned int i; union sudo_in_addr_un addr, mask; struct interface *ifp; #ifdef HAVE_STRUCT_IN6_ADDR unsigned int j; #endif unsigned int family; const char *errstr; debug_decl(addr_matches_if, SUDO_DEBUG_MATCH) #ifdef HAVE_STRUCT_IN6_ADDR if (inet_pton(AF_INET6, n, &addr.ip6) == 1) family = AF_INET6; else #endif /* HAVE_STRUCT_IN6_ADDR */ if (inet_pton(AF_INET, n, &addr.ip4) == 1) { family = AF_INET; } else { debug_return_bool(false); } if (family == AF_INET) { if (strchr(m, '.')) { if (inet_pton(AF_INET, m, &mask.ip4) != 1) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "IPv4 netmask %s: %s", m, "invalid value"); debug_return_bool(false); } } else { i = strtonum(m, 0, 32, &errstr); if (errstr != NULL) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "IPv4 netmask %s: %s", m, errstr); debug_return_bool(false); } if (i == 0) mask.ip4.s_addr = 0; else if (i == 32) mask.ip4.s_addr = 0xffffffff; else mask.ip4.s_addr = 0xffffffff - (1 << (32 - i)) + 1; mask.ip4.s_addr = htonl(mask.ip4.s_addr); } addr.ip4.s_addr &= mask.ip4.s_addr; } #ifdef HAVE_STRUCT_IN6_ADDR else { if (inet_pton(AF_INET6, m, &mask.ip6) != 1) { j = strtonum(m, 0, 128, &errstr); if (errstr != NULL) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "IPv6 netmask %s: %s", m, errstr); debug_return_bool(false); } for (i = 0; i < sizeof(addr.ip6.s6_addr); i++) { if (j < i * 8) mask.ip6.s6_addr[i] = 0; else if (i * 8 + 8 <= j) mask.ip6.s6_addr[i] = 0xff; else mask.ip6.s6_addr[i] = 0xff00 >> (j - i * 8); addr.ip6.s6_addr[i] &= mask.ip6.s6_addr[i]; } } } #endif /* HAVE_STRUCT_IN6_ADDR */ SLIST_FOREACH(ifp, get_interfaces(), entries) { if (ifp->family != family) continue; switch (family) { case AF_INET: if ((ifp->addr.ip4.s_addr & mask.ip4.s_addr) == addr.ip4.s_addr) debug_return_bool(true); break; #ifdef HAVE_STRUCT_IN6_ADDR case AF_INET6: for (j = 0; j < sizeof(addr.ip6.s6_addr); j++) { if ((ifp->addr.ip6.s6_addr[j] & mask.ip6.s6_addr[j]) != addr.ip6.s6_addr[j]) break; } if (j == sizeof(addr.ip6.s6_addr)) debug_return_bool(true); break; #endif /* HAVE_STRUCT_IN6_ADDR */ } } debug_return_bool(false); } /* * Returns true if "n" is one of our ip addresses or if * "n" is a network that we are on, else returns false. */ bool addr_matches(char *n) { char *m; bool rc; debug_decl(addr_matches, SUDO_DEBUG_MATCH) /* If there's an explicit netmask, use it. */ if ((m = strchr(n, '/'))) { *m++ = '\0'; rc = addr_matches_if_netmask(n, m); *(m - 1) = '/'; } else rc = addr_matches_if(n); sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, "IP address %s matches local host: %s", n, rc ? "true" : "false"); debug_return_bool(rc); }