diff --git a/CHANGELOG b/CHANGELOG index 08b4d4809..b61544210 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,5 @@ # Nmap Changelog ($Id$); -*-text-*- -o [Nmap] Updated --exclude and --excludefile to now support IPV6 addresses with - netmasks. [Colin] - o [NSE] Added a MySQL audit script and a rulebase that supports auditing a subset of the MySQL CIS 1.0.2 Benchmark. [Patrik] @@ -14,7 +11,7 @@ o [NSE] Added minimal Service Location Protocol (SLP) library and the script broadcast-novell-locate that detects servers running eDirectory. [Patrik] o [ncat] ncat now listens on localhost and ::1 when you do ncat -l. If you - specify an address or use -4,-6 it works as before. [Colin] + specify an address or use -4,-6 it works as before. o [NSE] Added the Simple Mail Transfer Protocol (SMTP) library. [Djalal] diff --git a/Makefile.in b/Makefile.in index 23ab6b7c4..6c8b485a7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -132,7 +132,7 @@ netutil_build: libnetutil/Makefile @echo Compiling libnetutil; cd libnetutil && $(MAKE) -ncat_build: $(NCATDIR)/Makefile nbase_build nsock_build netutil_build $(NCATDIR)/ncat.h +ncat_build: $(NCATDIR)/Makefile nbase_build nsock_build $(NCATDIR)/ncat.h cd $(NCATDIR) && $(MAKE) lua_build: $(LIBLUADIR)/Makefile diff --git a/libnetutil/Makefile.in b/libnetutil/Makefile.in index c6d888750..d54cb6676 100644 --- a/libnetutil/Makefile.in +++ b/libnetutil/Makefile.in @@ -14,8 +14,8 @@ LIBPCAPDIR = @libpcapdir@ TARGET = libnetutil.a -SRCS = $(srcdir)/netutil.cc $(srcdir)/addrset.cc -OBJS = netutil.o addrset.o +SRCS = $(srcdir)/netutil.cc +OBJS = netutil.o all: $(TARGET) diff --git a/libnetutil/addrset.cc b/libnetutil/addrset.cc deleted file mode 100644 index d7101529b..000000000 --- a/libnetutil/addrset.cc +++ /dev/null @@ -1,594 +0,0 @@ -/*************************************************************************** - * addrset.c -- Address set (addrset) management. * - ***********************IMPORTANT NMAP LICENSE TERMS************************ - * * - * The Nmap Security Scanner is (C) 1996-2011 Insecure.Com LLC. Nmap is * - * also a registered trademark of Insecure.Com LLC. This program is free * - * software; you may redistribute and/or modify it under the terms of the * - * GNU General Public License as published by the Free Software * - * Foundation; Version 2 with the clarifications and exceptions described * - * below. This guarantees your right to use, modify, and redistribute * - * this software under certain conditions. If you wish to embed Nmap * - * technology into proprietary software, we sell alternative licenses * - * (contact sales@insecure.com). Dozens of software vendors already * - * license Nmap technology such as host discovery, port scanning, OS * - * detection, and version detection. * - * * - * Note that the GPL places important restrictions on "derived works", yet * - * it does not provide a detailed definition of that term. To avoid * - * misunderstandings, we consider an application to constitute a * - * "derivative work" for the purpose of this license if it does any of the * - * following: * - * o Integrates source code from Nmap * - * o Reads or includes Nmap copyrighted data files, such as * - * nmap-os-db or nmap-service-probes. * - * o Executes Nmap and parses the results (as opposed to typical shell or * - * execution-menu apps, which simply display raw Nmap output and so are * - * not derivative works.) * - * o Integrates/includes/aggregates Nmap into a proprietary executable * - * installer, such as those produced by InstallShield. * - * o Links to a library or executes a program that does any of the above * - * * - * The term "Nmap" should be taken to also include any portions or derived * - * works of Nmap. This list is not exclusive, but is meant to clarify our * - * interpretation of derived works with some common examples. Our * - * interpretation applies only to Nmap--we don't speak for other people's * - * GPL works. * - * * - * If you have any questions about the GPL licensing restrictions on using * - * Nmap in non-GPL works, we would be happy to help. As mentioned above, * - * we also offer alternative license to integrate Nmap into proprietary * - * applications and appliances. These contracts have been sold to dozens * - * of software vendors, and generally include a perpetual license as well * - * as providing for priority support and updates as well as helping to * - * fund the continued development of Nmap technology. Please email * - * sales@insecure.com for further information. * - * * - * As a special exception to the GPL terms, Insecure.Com LLC grants * - * permission to link the code of this program with any version of the * - * OpenSSL library which is distributed under a license identical to that * - * listed in the included docs/licenses/OpenSSL.txt file, and distribute * - * linked combinations including the two. You must obey the GNU GPL in all * - * respects for all of the code used other than OpenSSL. If you modify * - * this file, you may extend this exception to your version of the file, * - * but you are not obligated to do so. * - * * - * If you received these files with a written license agreement or * - * contract stating terms other than the terms above, then that * - * alternative license agreement takes precedence over these comments. * - * * - * Source is provided to this software because we believe users have a * - * right to know exactly what a program is going to do before they run it. * - * This also allows you to audit the software for security holes (none * - * have been found so far). * - * * - * Source code also allows you to port Nmap to new platforms, fix bugs, * - * and add new features. You are highly encouraged to send your changes * - * to nmap-dev@insecure.org for possible incorporation into the main * - * distribution. By sending these changes to Fyodor or one of the * - * Insecure.Org development mailing lists, it is assumed that you are * - * offering the Nmap Project (Insecure.Com LLC) the unlimited, * - * non-exclusive right to reuse, modify, and relicense the code. Nmap * - * will always be available Open Source, but this is important because the * - * inability to relicense code has caused devastating problems for other * - * Free Software projects (such as KDE and NASM). We also occasionally * - * relicense the code to third parties as discussed above. If you wish to * - * specify special license conditions of your contributions, just say so * - * when you send them. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * General Public License v2.0 for more details at * - * http://www.gnu.org/licenses/gpl-2.0.html , or in the COPYING file * - * included with Nmap. * - * * - ***************************************************************************/ - -/* The code in this file has tests in the file ncat/tests/test-addrset.sh. Run that - program after making any big changes. Also, please add tests for any new - features. */ - -#include "nbase.h" -#include "netutil.h" - -#include "addrset.h" - -extern "C" { - -void addrset_init(struct addrset *set) -{ - set->head = NULL; -} - -void addrset_free(struct addrset *set) -{ - struct addrset_elem *elem, *next; - - for (elem = set->head; elem != NULL; elem = next) { - next = elem->next; - free(elem); - } -} - -/* A debugging function to print out the contents of an addrset_elem. For IPv4 - this is the four bit vectors. For IPv6 it is the address and netmask. */ -void addrset_elem_print(const struct addrset_elem *elem) -{ - int i, j; - - if (elem->type == ADDRSET_TYPE_IPV4_BITVECTOR) { - for (i = 0; i < 4; i++) { - for (j = 0; j < sizeof(octet_bitvector) / sizeof(bitvector_t); j++) - printf("%08lX ", elem->u.ipv4.bits[i][j]); - printf("\n"); - } -#ifdef HAVE_IPV6 - } else if (elem->type == ADDRSET_TYPE_IPV6_NETMASK) { - for (i = 0; i < 16; i += 2) { - if (i > 0) - printf(":"); - printf("%02X", elem->u.ipv6.addr.s6_addr[i]); - printf("%02X", elem->u.ipv6.addr.s6_addr[i + 1]); - } - printf(" "); - for (i = 0; i < 16; i += 2) { - if (i > 0) - printf(":"); - printf("%02X", elem->u.ipv6.mask.s6_addr[i]); - printf("%02X", elem->u.ipv6.mask.s6_addr[i + 1]); - } - printf("\n"); -#endif - } - printf("---\n"); -} - -/* This is a wrapper around getaddrinfo that automatically handles hints for - IPv4/IPv6, TCP/UDP, and whether name resolution is allowed. */ -static int resolve_name(const char *name, struct addrinfo **result, int af, int use_dns) -{ - struct addrinfo hints = { 0 }; - int rc; - - hints.ai_protocol = IPPROTO_TCP; - - /* First do a non-DNS lookup for any address family (just checks for a valid - numeric address). We recognize numeric addresses no matter the setting of - af. This is also the last step if use_dns is false. */ - hints.ai_flags |= AI_NUMERICHOST; - hints.ai_family = AF_UNSPEC; - *result = NULL; - rc = getaddrinfo(name, NULL, &hints, result); - if (rc == 0 || !use_dns) - return rc; - - /* Do a DNS lookup now. When we look up a name we only want addresses - corresponding to the value of af. */ - hints.ai_flags &= ~AI_NUMERICHOST; - hints.ai_family = af; - *result = NULL; - rc = getaddrinfo(name, NULL, &hints, result); - - return rc; -} - -/* This is an address family-agnostic version of inet_ntop. */ -static char *address_to_string(const struct sockaddr *sa, size_t sa_len, - char *buf, size_t len) -{ - getnameinfo(sa, sa_len, buf, len, NULL, 0, NI_NUMERICHOST); - - return buf; -} - -/* Break an IPv4 address into an array of octets. */ -static void in_addr_to_octets(const struct in_addr *ia, uint8_t octets[4]) -{ - octets[0] = (ia->s_addr & 0xFF); - octets[1] = (ia->s_addr & (0xFF << 8)) >> 8; - octets[2] = (ia->s_addr & (0xFF << 16)) >> 16; - octets[3] = (ia->s_addr & (0xFF << 24)) >> 24; -} - -#define BITVECTOR_BITS (sizeof(bitvector_t) * CHAR_BIT) -#define BIT_SET(v, n) ((v)[(n) / BITVECTOR_BITS] |= 1UL << ((n) % BITVECTOR_BITS)) -#define BIT_IS_SET(v, n) (((v)[(n) / BITVECTOR_BITS] & 1UL << ((n) % BITVECTOR_BITS)) != 0) - -static int parse_ipv4_ranges(struct addrset_elem *elem, const char *spec); -static void apply_ipv4_netmask_bits(struct addrset_elem *elem, int bits); -#ifdef HAVE_IPV6 -static void make_ipv6_netmask(struct in6_addr *mask, int bits); -#endif - -/* Add a host specification into the address set. Returns 1 on success, 0 on - error. */ - -int addrset_add_spec(struct addrset *set, const char *spec, int af, int dns) -{ - char *local_spec; - char *netmask_s; - char *tail; - long netmask_bits; - struct addrinfo *addrs, *addr; - struct addrset_elem *elem; - int rc; - - /* Make a copy of the spec to mess with. */ - local_spec = strdup(spec); - if (local_spec == NULL) - return 0; - - /* Read the CIDR netmask bits, if present. */ - netmask_s = strchr(local_spec, '/'); - if (netmask_s == NULL) { - /* A negative value means unspecified; default depends on the address - family. */ - netmask_bits = -1; - } else { - *netmask_s = '\0'; - netmask_s++; - errno = 0; - netmask_bits = parse_long(netmask_s, &tail); - if (errno != 0 || *tail != '\0' || tail == netmask_s) { - netutil_error("Error parsing netmask in \"%s\".\n", spec); - free(local_spec); - return 0; - } - } - - elem = (struct addrset_elem *) safe_malloc(sizeof(*elem)); - memset(elem->u.ipv4.bits, 0, sizeof(elem->u.ipv4.bits)); - - /* Check if this is an IPv4 address, with optional ranges and wildcards. */ - if (parse_ipv4_ranges(elem, local_spec)) { - if (netmask_bits > 32) { - netutil_error("Illegal netmask in \"%s\". Must be between 0 and 32.\n", spec); - free(local_spec); - free(elem); - return 0; - } - apply_ipv4_netmask_bits(elem, netmask_bits); - - elem->type = ADDRSET_TYPE_IPV4_BITVECTOR; - elem->next = set->head; - set->head = elem; - free(local_spec); - return 1; - } else { - free(elem); - } - - /* When all else fails, resolve the name. */ - rc = resolve_name(local_spec, &addrs, af, dns); - if (rc != 0) { - netutil_error("Error resolving name \"%s\": %s\n", local_spec, gai_strerror(rc)); - free(local_spec); - return 0; - } - if (addrs == NULL) - netutil_error("Warning: no addresses found for %s.\n", local_spec); - free(local_spec); - - /* Walk the list of addresses and add them all to the set with netmasks. */ - for (addr = addrs; addr != NULL; addr = addr->ai_next) { - char addr_string[128]; - - elem = (struct addrset_elem *) safe_malloc(sizeof(*elem)); - memset(elem->u.ipv4.bits, 0, sizeof(elem->u.ipv4.bits)); - - address_to_string(addr->ai_addr, addr->ai_addrlen, addr_string, sizeof(addr_string)); - - /* Note: it is possible that in this loop we are dealing with addresses - of more than one family (e.g., IPv4 and IPv6). But we have at most - one netmask value for all of them. Whatever netmask we have is - applied blindly to whatever addresses there are, which may not be - what you want if a /24 is applied to IPv6 and will cause an error if - a /120 is applied to IPv4. */ - if (addr->ai_family == AF_INET) { - const struct sockaddr_in *sin = (struct sockaddr_in *) addr->ai_addr; - uint8_t octets[4]; - - elem->type = ADDRSET_TYPE_IPV4_BITVECTOR; - - in_addr_to_octets(&sin->sin_addr, octets); - BIT_SET(elem->u.ipv4.bits[0], octets[0]); - BIT_SET(elem->u.ipv4.bits[1], octets[1]); - BIT_SET(elem->u.ipv4.bits[2], octets[2]); - BIT_SET(elem->u.ipv4.bits[3], octets[3]); - - if (netmask_bits > 32) { - netutil_error("Illegal netmask in \"%s\". Must be between 0 and 32.\n", spec); - free(elem); - return 0; - } - apply_ipv4_netmask_bits(elem, netmask_bits); - - -#ifdef HAVE_IPV6 - } else if (addr->ai_family == AF_INET6) { - const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr->ai_addr; - - elem->type = ADDRSET_TYPE_IPV6_NETMASK; - - elem->u.ipv6.addr = sin6->sin6_addr; - - if (netmask_bits > 128) { - netutil_error("Illegal netmask in \"%s\". Must be between 0 and 128.\n", spec); - free(elem); - return 0; - } - make_ipv6_netmask(&elem->u.ipv6.mask, netmask_bits); -#endif - } else { - free(elem); - continue; - } - - elem->next = set->head; - set->head = elem; - } - - if (addrs != NULL) - freeaddrinfo(addrs); - - return 1; -} - -/* Add whitespace-separated host specifications from fd into the address set. - Returns 1 on success, 0 on error. */ -int addrset_add_file(struct addrset *set, FILE *fd, int af, int dns) -{ - char buf[1024]; - int c, i; - - for (;;) { - /* Skip whitespace. */ - while ((c = getc(fd)) != EOF) { - if (!isspace(c)) - break; - } - if (c == EOF) - break; - ungetc(c, fd); - - i = 0; - while ((c = getc(fd)) != EOF) { - if (isspace(c)) - break; - if (i + 1 > sizeof(buf) - 1) { - /* Truncate the specification to give a little context. */ - buf[11] = '\0'; - netutil_error("Host specification starting with \"%s\" is too long.\n", buf); - return 0; - } - buf[i++] = c; - } - buf[i] = '\0'; - - if (!addrset_add_spec(set, buf, af, dns)) - return 0; - } - - return 1; -} - -/* Parse an IPv4 address with optional ranges and wildcards into bit vectors. - Each octet must match the regular expression '(\*|#?(-#?)?(,#?(-#?)?)*)', - where '#' stands for an integer between 0 and 255. Return 1 on success, 0 on - error. */ -static int parse_ipv4_ranges(struct addrset_elem *elem, const char *spec) -{ - const char *p; - int octet_index, i; - - p = spec; - octet_index = 0; - while (*p != '\0' && octet_index < 4) { - if (*p == '*') { - for (i = 0; i < 256; i++) - BIT_SET(elem->u.ipv4.bits[octet_index], i); - p++; - } else { - for (;;) { - long start, end; - char *tail; - - errno = 0; - start = parse_long(p, &tail); - /* Is this a range open on the left? */ - if (tail == p) { - if (*p == '-') - start = 0; - else - return 0; - } - if (errno != 0 || start < 0 || start > 255) - return 0; - p = tail; - - /* Look for a range. */ - if (*p == '-') { - p++; - errno = 0; - end = parse_long(p, &tail); - /* Is this range open on the right? */ - if (tail == p) - end = 255; - if (errno != 0 || end < 0 || end > 255 || end < start) - return 0; - p = tail; - } else { - end = start; - } - - /* Fill in the range in the bit vector. */ - for (i = start; i <= end; i++) - BIT_SET(elem->u.ipv4.bits[octet_index], i); - - if (*p != ',') - break; - p++; - } - } - octet_index++; - if (octet_index < 4) { - if (*p != '.') - return 0; - p++; - } - } - if (*p != '\0' || octet_index < 4) - return 0; - - return 1; -} - -/* Expand a single-octet bit vector to include any additional addresses that - result when mask is applied. */ -static void apply_ipv4_netmask_octet(octet_bitvector bits, uint8_t mask) -{ - int i, j; - uint32_t chunk_size; - - /* Process the bit vector in chunks, first of size 1, then of size 2, up to - size 128. Check the next bit of the mask. If it is 1, do nothing. - Otherwise, pair up the chunks (first with the second, third with the - fourth, etc.). For each pair of chunks, set a bit in one chunk if it is - set in the other. chunk_size also serves as an index into the mask. */ - for (chunk_size = 1; chunk_size < 256; chunk_size <<= 1) { - if ((mask & chunk_size) != 0) - continue; - for (i = 0; i < 256; i += chunk_size * 2) { - for (j = 0; j < chunk_size; j++) { - if (BIT_IS_SET(bits, i + j)) - BIT_SET(bits, i + j + chunk_size); - else if (BIT_IS_SET(bits, i + j + chunk_size)) - BIT_SET(bits, i + j); - } - } - } -} - -/* Expand an addrset_elem's IPv4 bit vectors to include any additional addresses - that result when the given netmask is applied. The mask is in network byte - order. */ -static void apply_ipv4_netmask(struct addrset_elem *elem, uint32_t mask) -{ - mask = ntohl(mask); - /* Apply the mask one octet at a time. It's done this way because ranges - span exactly one octet. */ - apply_ipv4_netmask_octet(elem->u.ipv4.bits[0], (mask & 0xFF000000) >> 24); - apply_ipv4_netmask_octet(elem->u.ipv4.bits[1], (mask & 0x00FF0000) >> 16); - apply_ipv4_netmask_octet(elem->u.ipv4.bits[2], (mask & 0x0000FF00) >> 8); - apply_ipv4_netmask_octet(elem->u.ipv4.bits[3], (mask & 0x000000FF)); -} - -/* Expand an addrset_elem's IPv4 bit vectors to include any additional addresses - that result from the application of a CIDR-style netmask with the given - number of bits. If bits is negative it is taken to be 32. */ -static void apply_ipv4_netmask_bits(struct addrset_elem *elem, int bits) -{ - uint32_t mask; - - if (bits > 32) - return; - if (bits < 0) - bits = 32; - - if (bits == 0) - mask = htonl(0x00000000); - else - mask = htonl(0xFFFFFFFF << (32 - bits)); - apply_ipv4_netmask(elem, mask); -} - -#ifdef HAVE_IPV6 -/* Fill in an in6_addr with a CIDR-style netmask with the given number of bits. - If bits is negative it is taken to be 128. The netmask is written in network - byte order. */ -static void make_ipv6_netmask(struct in6_addr *mask, int bits) -{ - int i; - - memset(mask, 0, sizeof(*mask)); - - if (bits > 128) - return; - if (bits < 0) - bits = 128; - - if (bits == 0) - return; - - i = 0; - /* 0 < bits <= 128, so this loop goes at most 15 times. */ - for ( ; bits > 8; bits -= 8) - mask->s6_addr[i++] = 0xFF; - mask->s6_addr[i] = 0xFF << (8 - bits); -} -#endif - -static int match_ipv4_bits(const octet_bitvector bits[4], const struct sockaddr *sa) -{ - uint8_t octets[4]; - - if (sa->sa_family != AF_INET) - return 0; - - in_addr_to_octets(&((const struct sockaddr_in *) sa)->sin_addr, octets); - - return BIT_IS_SET(bits[0], octets[0]) - && BIT_IS_SET(bits[1], octets[1]) - && BIT_IS_SET(bits[2], octets[2]) - && BIT_IS_SET(bits[3], octets[3]); -} - -#ifdef HAVE_IPV6 -static int match_ipv6_netmask(const struct in6_addr *addr, - const struct in6_addr *mask, const struct sockaddr *sa) -{ - const uint8_t *a = addr->s6_addr; - const uint8_t *m = mask->s6_addr; - const uint8_t *b = ((const struct sockaddr_in6 *) sa)->sin6_addr.s6_addr; - int i; - - if (sa->sa_family != AF_INET6) - return 0; - - for (i = 0; i < 16; i++) { - if ((a[i] & m[i]) != (b[i] & m[i])) - return 0; - } - - return 1; -} -#endif - -static int addrset_elem_match(const struct addrset_elem *elem, const struct sockaddr *sa) -{ - switch (elem->type) { - case ADDRSET_TYPE_IPV4_BITVECTOR: - return match_ipv4_bits(elem->u.ipv4.bits, sa); -#ifdef HAVE_IPV6 - case ADDRSET_TYPE_IPV6_NETMASK: - return match_ipv6_netmask(&elem->u.ipv6.addr, &elem->u.ipv6.mask, sa); -#endif - } - - return 0; -} - -int addrset_contains(const struct addrset *set, const struct sockaddr *sa) -{ - struct addrset_elem *elem; - - for (elem = set->head; elem != NULL; elem = elem->next) { - if (addrset_elem_match(elem, sa)) - return 1; - } - - return 0; -} - -} diff --git a/libnetutil/addrset.h b/libnetutil/addrset.h deleted file mode 100644 index 95dcfaa11..000000000 --- a/libnetutil/addrset.h +++ /dev/null @@ -1,162 +0,0 @@ -/*************************************************************************** - * addrset.h * - ***********************IMPORTANT NMAP LICENSE TERMS************************ - * * - * The Nmap Security Scanner is (C) 1996-2011 Insecure.Com LLC. Nmap is * - * also a registered trademark of Insecure.Com LLC. This program is free * - * software; you may redistribute and/or modify it under the terms of the * - * GNU General Public License as published by the Free Software * - * Foundation; Version 2 with the clarifications and exceptions described * - * below. This guarantees your right to use, modify, and redistribute * - * this software under certain conditions. If you wish to embed Nmap * - * technology into proprietary software, we sell alternative licenses * - * (contact sales@insecure.com). Dozens of software vendors already * - * license Nmap technology such as host discovery, port scanning, OS * - * detection, and version detection. * - * * - * Note that the GPL places important restrictions on "derived works", yet * - * it does not provide a detailed definition of that term. To avoid * - * misunderstandings, we consider an application to constitute a * - * "derivative work" for the purpose of this license if it does any of the * - * following: * - * o Integrates source code from Nmap * - * o Reads or includes Nmap copyrighted data files, such as * - * nmap-os-db or nmap-service-probes. * - * o Executes Nmap and parses the results (as opposed to typical shell or * - * execution-menu apps, which simply display raw Nmap output and so are * - * not derivative works.) * - * o Integrates/includes/aggregates Nmap into a proprietary executable * - * installer, such as those produced by InstallShield. * - * o Links to a library or executes a program that does any of the above * - * * - * The term "Nmap" should be taken to also include any portions or derived * - * works of Nmap. This list is not exclusive, but is meant to clarify our * - * interpretation of derived works with some common examples. Our * - * interpretation applies only to Nmap--we don't speak for other people's * - * GPL works. * - * * - * If you have any questions about the GPL licensing restrictions on using * - * Nmap in non-GPL works, we would be happy to help. As mentioned above, * - * we also offer alternative license to integrate Nmap into proprietary * - * applications and appliances. These contracts have been sold to dozens * - * of software vendors, and generally include a perpetual license as well * - * as providing for priority support and updates as well as helping to * - * fund the continued development of Nmap technology. Please email * - * sales@insecure.com for further information. * - * * - * As a special exception to the GPL terms, Insecure.Com LLC grants * - * permission to link the code of this program with any version of the * - * OpenSSL library which is distributed under a license identical to that * - * listed in the included docs/licenses/OpenSSL.txt file, and distribute * - * linked combinations including the two. You must obey the GNU GPL in all * - * respects for all of the code used other than OpenSSL. If you modify * - * this file, you may extend this exception to your version of the file, * - * but you are not obligated to do so. * - * * - * If you received these files with a written license agreement or * - * contract stating terms other than the terms above, then that * - * alternative license agreement takes precedence over these comments. * - * * - * Source is provided to this software because we believe users have a * - * right to know exactly what a program is going to do before they run it. * - * This also allows you to audit the software for security holes (none * - * have been found so far). * - * * - * Source code also allows you to port Nmap to new platforms, fix bugs, * - * and add new features. You are highly encouraged to send your changes * - * to nmap-dev@insecure.org for possible incorporation into the main * - * distribution. By sending these changes to Fyodor or one of the * - * Insecure.Org development mailing lists, it is assumed that you are * - * offering the Nmap Project (Insecure.Com LLC) the unlimited, * - * non-exclusive right to reuse, modify, and relicense the code. Nmap * - * will always be available Open Source, but this is important because the * - * inability to relicense code has caused devastating problems for other * - * Free Software projects (such as KDE and NASM). We also occasionally * - * relicense the code to third parties as discussed above. If you wish to * - * specify special license conditions of your contributions, just say so * - * when you send them. * - * * - * This program is distributed in the hope that it will be useful, but * - * WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * - * General Public License v2.0 for more details at * - * http://www.gnu.org/licenses/gpl-2.0.html , or in the COPYING file * - * included with Nmap. * - * * - ***************************************************************************/ - - -#ifndef _NETUTIL_ADDRSET_H -#define _NETUTIL_ADDRSET_H - -//#define HAVE_IPV6 1 -#include -#include -#include -#include -#include -#include -#ifndef WIN32 -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -struct addrset_elem; - -/* A set of addresses. Used to match against allow/deny lists. */ -struct addrset { - /* Linked list of struct addset_elem. */ - struct addrset_elem *head; -}; - -/* We use bit vectors to represent what values are allowed in an IPv4 octet. - Each vector is built up of an array of bitvector_t (any convenient integer - type). */ -typedef unsigned long bitvector_t; -/* A 256-element bit vector, representing legal values for one octet. */ -typedef bitvector_t octet_bitvector[(256 - 1) / (sizeof(unsigned long) * CHAR_BIT) + 1]; - -enum addrset_elem_type { - ADDRSET_TYPE_IPV4_BITVECTOR, -#ifdef HAVE_IPV6 - ADDRSET_TYPE_IPV6_NETMASK, -#endif -}; - -/* A chain of tests for set inclusion. If one test is passed, the address is in - the set. */ -struct addrset_elem { - enum addrset_elem_type type; - union { - struct { - /* A bit vector for each address octet. */ - octet_bitvector bits[4]; - } ipv4; -#ifdef HAVE_IPV6 - struct { - struct in6_addr addr; - struct in6_addr mask; - } ipv6; -#endif - } u; - struct addrset_elem *next; -}; - -extern void addrset_init(struct addrset *set); - -extern void addrset_free(struct addrset *set); - -extern int addrset_add_spec(struct addrset *set, const char *spec, int af, int dns); - -extern int addrset_add_file(struct addrset *set, FILE *fd, int af, int dns); - -extern int addrset_contains(const struct addrset *set, const struct sockaddr *sa); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/nmap.cc b/nmap.cc index 5dc4a0d07..e6bb5e952 100644 --- a/nmap.cc +++ b/nmap.cc @@ -475,7 +475,7 @@ int nmap_main(int argc, char *argv[]) { /* Only NSE scripts can add targets */ NewTargets *new_targets = NULL; #endif - addrset exclude_group; + TargetGroup *exclude_group = NULL; char myname[MAXHOSTNAMELEN + 1]; #if (defined(IN_ADDR_DEEPSTRUCT) || defined( SOLARIS)) /* Note that struct in_addr in solaris is 3 levels deep just to store an @@ -1645,17 +1645,20 @@ int nmap_main(int argc, char *argv[]) { /* lets load our exclude list */ if (excludefd != NULL) { - load_exclude_file(&exclude_group, excludefd); + exclude_group = load_exclude_file(excludefd); fclose(excludefd); } if (exclude_spec != NULL) { /* Simultaneous --excludefile and --exclude are not supported. */ - load_exclude_string(&exclude_group ,exclude_spec); + assert(exclude_group == NULL); + exclude_group = load_exclude_string(exclude_spec); free(exclude_spec); } - if (o.debugging > 3) - dumpExclude(&exclude_group); + if (exclude_group != NULL) { + if (o.debugging > 3) + dumpExclude(exclude_group); + } #ifndef NOLUA if (o.scriptupdatedb) { @@ -1689,7 +1692,7 @@ int nmap_main(int argc, char *argv[]) { ideal_scan_group_sz = determineScanGroupSize(o.numhosts_scanned, &ports); while(Targets.size() < ideal_scan_group_sz) { o.current_scantype = HOST_DISCOVERY; - currenths = nexthost(hstate, &exclude_group, &ports, o.pingtype); + currenths = nexthost(hstate, exclude_group, &ports, o.pingtype); if (!currenths) { /* Try to refill with any remaining expressions */ /* First free the old ones */ @@ -1727,7 +1730,7 @@ int nmap_main(int argc, char *argv[]) { num_host_exp_groups); /* Try one last time -- with new expressions */ - currenths = nexthost(hstate, &exclude_group, &ports, o.pingtype); + currenths = nexthost(hstate, exclude_group, &ports, o.pingtype); if (!currenths) break; } @@ -1968,8 +1971,9 @@ int nmap_main(int argc, char *argv[]) { #endif delete hstate; - - addrset_free(&exclude_group); + if (exclude_group) + delete[] exclude_group; + hstate = NULL; /* Free host expressions */ diff --git a/targets.cc b/targets.cc index 9816432de..d1aa581bc 100644 --- a/targets.cc +++ b/targets.cc @@ -101,7 +101,7 @@ #include "nmap_dns.h" #include "nmap_tty.h" #include "utils.h" -#include "libnetutil/addrset.h" + using namespace std; extern NmapOps o; @@ -156,40 +156,133 @@ void returnhost(HostGroupState *hs) { /* Is the host passed as Target to be excluded? Much of this logic had to be rewritten from wam's original code to allow for the objects */ static int hostInExclude(struct sockaddr *checksock, size_t checksocklen, - const addrset *exclude_group) { - if (exclude_group == NULL) + TargetGroup *exclude_group) { + unsigned long tmpTarget; /* ip we examine */ + int i=0; /* a simple index */ + char targets_type; /* what is the address type of the Target Group */ + struct sockaddr_storage ss; + struct sockaddr_in *sin = (struct sockaddr_in *) &ss; + size_t slen; /* needed for funct but not used */ + unsigned long mask = 0; /* our trusty netmask, which we convert to nbo */ + struct sockaddr_in *checkhost_in; + + if ((TargetGroup *)0 == exclude_group) return 0; - if (checksock == NULL) - return 0; + checkhost_in = NULL; + if (checksock->sa_family == AF_INET) { + assert(checksocklen >= sizeof(struct sockaddr_in)); + checkhost_in = (struct sockaddr_in *) checksock; + } - if (addrset_contains(exclude_group,checksock)) - return 1; + /* First find out what type of addresses are in the target group */ + targets_type = exclude_group[i].get_targets_type(); + + /* Lets go through the targets until we reach our uninitialized placeholder */ + while (exclude_group[i].get_targets_type() != TargetGroup::TYPE_NONE) { + /* while there are still hosts in the target group */ + while (exclude_group[i].get_next_host(&ss, &slen) == 0) { + tmpTarget = sin->sin_addr.s_addr; + + /* For Netmasks simply compare the network bits and move to the next + * group if it does not compare, we don't care about the individual addrs */ + if (targets_type == TargetGroup::IPV4_NETMASK) { + if (checkhost_in == NULL) + break; + mask = htonl((unsigned long) (0-1) << (32-exclude_group[i].get_mask())); + if ((tmpTarget & mask) == (checkhost_in->sin_addr.s_addr & mask)) { + exclude_group[i].rewind(); + return 1; + } else { + break; + } + } + /* For ranges we need to be a little more slick, if we don't find a match + * we should skip the rest of the addrs in the octet, thank wam for this + * optimization */ + else if (targets_type == TargetGroup::IPV4_RANGES) { + if (checkhost_in == NULL) + break; + if (tmpTarget == checkhost_in->sin_addr.s_addr) { + exclude_group[i].rewind(); + return 1; + } else { + /* note these are in network byte order */ + if ((tmpTarget & 0x000000ff) != (checkhost_in->sin_addr.s_addr & 0x000000ff)) + exclude_group[i].skip_range(TargetGroup::FIRST_OCTET); + else if ((tmpTarget & 0x0000ff00) != (checkhost_in->sin_addr.s_addr & 0x0000ff00)) + exclude_group[i].skip_range(TargetGroup::SECOND_OCTET); + else if ((tmpTarget & 0x00ff0000) != (checkhost_in->sin_addr.s_addr & 0x00ff0000)) + exclude_group[i].skip_range(TargetGroup::THIRD_OCTET); + + continue; + } + } +#if HAVE_IPV6 + else if (targets_type == TargetGroup::IPV6_ADDRESS) { + fatal("exclude file not supported for IPV6 -- If it is important to you, send a mail to fyodor@insecure.org so I can guage support\n"); + } +#endif + } + exclude_group[i++].rewind(); + } + + /* we did not find the host */ return 0; } +/* Convert a vector of host specifications to an array (allocated with new[]) of + TargetGroups. The size of the returned array is one greater than the number + of host specs, to leave on uninitialized member at the end. */ +static TargetGroup *specs_to_targetgroups(const std::vector &specs) { + TargetGroup *excludelist; + unsigned int i; + + excludelist = new TargetGroup[specs.size() + 1]; + + for (i = 0; i < specs.size(); i++) { + if (excludelist[i].parse_expr(specs[i].c_str(), o.af()) == 0) { + if (o.debugging > 1) + error("Loaded exclude target of: %s", specs[i].c_str()); + } + } + + return excludelist; +} + /* Load an exclude list from a file for --excludefile. */ -int load_exclude_file(addrset *excludelist, FILE *fp) { - addrset_init(excludelist); +TargetGroup* load_exclude_file(FILE *fp) { + std::vector specs; char host_spec[1024]; size_t n; while ((n = read_host_from_file(fp, host_spec, sizeof(host_spec))) > 0) { if (n >= sizeof(host_spec)) fatal("One of your exclude file specifications was too long to read (>= %u chars)", (unsigned int) sizeof(host_spec)); - addrset_add_spec(excludelist, host_spec, o.af(), 1); + specs.push_back(host_spec); } - return 1; + return specs_to_targetgroups(specs); } /* Load a comma-separated exclude list from a string, the argument to --exclude. */ -int load_exclude_string(addrset *excludelist, const char *s) { - addrset_init(excludelist); - addrset_add_spec(excludelist, s, o.af(), 1); +TargetGroup* load_exclude_string(const char *s) { + std::vector specs; + const char *begin, *p; - return 1; + p = s; + while (*p != '\0') { + begin = p; + while (*p != '\0' && *p != ',') + p++; + specs.push_back(std::string(begin, p - begin)); + if (*p == '\0') + break; + p++; + } + + return specs_to_targetgroups(specs); } @@ -199,11 +292,43 @@ int load_exclude_string(addrset *excludelist, const char *s) { the Target Group Object. Rather than writing a bunch of methods to return private attributes, which would only be used for debugging, I went for the method below. */ -int dumpExclude(addrset *exclude_group) { - /* When we updated the exclude code to use addrset from libnetutil (originally - from ncat) there was no simple available debugging function. Thus we are - zeroing this and if it is needed look in addrset.cc for the lower level - debug function -Colin */ +int dumpExclude(TargetGroup *exclude_group) { + int i=0, debug_save=0, type=TargetGroup::TYPE_NONE; + unsigned int mask = 0; + struct sockaddr_storage ss; + struct sockaddr_in *sin = (struct sockaddr_in *) &ss; + size_t slen; + + /* shut off debugging for now, this is a debug routine in itself, we don't + want to see all the debug messages inside of the object */ + debug_save = o.debugging; + o.debugging = 0; + + while ((type = exclude_group[i].get_targets_type()) != TargetGroup::TYPE_NONE) { + switch (type) { + case TargetGroup::IPV4_NETMASK: + exclude_group[i].get_next_host(&ss, &slen); + mask = exclude_group[i].get_mask(); + error("exclude host group %d is %s/%d", i, inet_ntoa(sin->sin_addr), mask); + break; + + case TargetGroup::IPV4_RANGES: + while (exclude_group[i].get_next_host(&ss, &slen) == 0) + error("exclude host group %d is %s", i, inet_ntoa(sin->sin_addr)); + break; + + case TargetGroup::IPV6_ADDRESS: + fatal("IPV6 addresses are not supported in the exclude file\n"); + break; + + default: + fatal("Unknown target type in exclude file.\n"); + } + exclude_group[i++].rewind(); + } + + /* return debugging to what it was */ + o.debugging = debug_save; return 1; } @@ -290,7 +415,7 @@ static bool target_needs_new_hostgroup(const HostGroupState *hs, const Target *t return false; } -Target *nexthost(HostGroupState *hs, const addrset *exclude_group, +Target *nexthost(HostGroupState *hs, TargetGroup *exclude_group, struct scan_lists *ports, int pingtype) { int i; struct sockaddr_storage ss; diff --git a/targets.h b/targets.h index 34825934b..4f00559a4 100644 --- a/targets.h +++ b/targets.h @@ -115,7 +115,6 @@ #endif #include "nmap.h" -#include "libnetutil/addrset.h" #include "global_structures.h" class HostGroupState; @@ -155,14 +154,15 @@ struct pingtech { rawprotoscan: 1; }; + /* Ports is the list of ports the user asked to be scanned (0 terminated), you can just pass NULL (it is only a stupid optimization that needs it) */ -Target *nexthost(HostGroupState *hs,const addrset *exclude_group, +Target *nexthost(HostGroupState *hs, TargetGroup *exclude_group, struct scan_lists *ports, int pingtype); -int load_exclude_file(addrset *exclude_group, FILE *fp); -int load_exclude_string(addrset *exclude_group, const char *s); +TargetGroup* load_exclude_file(FILE *fp); +TargetGroup* load_exclude_string(const char *s); /* a debugging routine to dump an exclude list to stdout. */ -int dumpExclude(addrset *exclude_group); +int dumpExclude(TargetGroup*exclude_group); /* Returns the last host obtained by nexthost. It will be given again the next time you call nexthost(). */ void returnhost(HostGroupState *hs);