mirror of
https://github.com/nmap/nmap.git
synced 2026-08-04 06:40:48 +00:00
Merge 9b62266784 into 5340f729b6
This commit is contained in:
commit
cd7e215b3f
10 changed files with 571 additions and 17 deletions
|
|
@ -50,7 +50,7 @@ CC = @CC@
|
|||
CPPFLAGS = @CPPFLAGS@
|
||||
CFLAGS = @CFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
LIBS = @LIBS@ @PTHREAD_LIBS@
|
||||
DEFS = @DEFS@ -DNCAT_DATADIR="\"$(pkgdatadir)\""
|
||||
# With GCC, add extra security checks to source code.
|
||||
DEFS += -D_FORTIFY_SOURCE=2
|
||||
|
|
@ -73,8 +73,8 @@ SHTOOL = ../shtool
|
|||
# usual directory structure into a different tree.
|
||||
DESTDIR =
|
||||
|
||||
SRCS = ncat_main.c ncat_connect.c ncat_core.c ncat_posix.c ncat_listen.c ncat_proxy.c ncat_ssl.c base64.c http.c util.c sys_wrap.c
|
||||
OBJS = ncat_main.o ncat_connect.o ncat_core.o ncat_posix.o ncat_listen.o ncat_proxy.o ncat_ssl.o base64.o http.o util.o sys_wrap.o
|
||||
SRCS = ncat_main.c ncat_connect.c ncat_core.c ncat_posix.c ncat_listen.c ncat_proxy.c ncat_ssl.c allowdeny_watcher.c base64.c http.c util.c sys_wrap.c
|
||||
OBJS = ncat_main.o ncat_connect.o ncat_core.o ncat_posix.o ncat_listen.o ncat_proxy.o ncat_ssl.o allowdeny_watcher.o base64.o http.o util.o sys_wrap.o
|
||||
DATAFILES =
|
||||
|
||||
ifneq ($(HAVE_OPENSSL),)
|
||||
|
|
@ -138,16 +138,16 @@ config.h:
|
|||
$(SHELL) ./config.status; \
|
||||
fi
|
||||
|
||||
test/addrset: test/addrset.o ncat_core.o sys_wrap.o util.o ncat_posix.o $(LUA_OBJS) @LUA_DEPENDS@
|
||||
test/addrset: test/addrset.o ncat_core.o sys_wrap.o util.o ncat_posix.o allowdeny_watcher.o $(LUA_OBJS) @LUA_DEPENDS@
|
||||
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(NSOCKLIB) $(NBASELIB) $(OPENSSL_LIBS) $(PCAP_LIBS) $(LUA_LIBS) $(LIBS)
|
||||
|
||||
test/test-uri: test/test-uri.o base64.o http.o ncat_core.o sys_wrap.o util.o ncat_posix.o $(LUA_OBJS) @LUA_DEPENDS@
|
||||
test/test-uri: test/test-uri.o base64.o http.o ncat_core.o sys_wrap.o util.o ncat_posix.o allowdeny_watcher.o $(LUA_OBJS) @LUA_DEPENDS@
|
||||
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(NSOCKLIB) $(NBASELIB) $(OPENSSL_LIBS) $(PCAP_LIBS) $(LUA_LIBS) $(LIBS)
|
||||
|
||||
test/test-cmdline-split: test/test-cmdline-split.o ncat_posix.o ncat_core.o sys_wrap.o util.o $(LUA_OBJS) @LUA_DEPENDS@
|
||||
test/test-cmdline-split: test/test-cmdline-split.o ncat_posix.o ncat_core.o sys_wrap.o util.o allowdeny_watcher.o $(LUA_OBJS) @LUA_DEPENDS@
|
||||
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(NSOCKLIB) $(NBASELIB) $(OPENSSL_LIBS) $(PCAP_LIBS) $(LUA_LIBS) $(LIBS)
|
||||
|
||||
test/test-wildcard: test/test-wildcard.o ncat_core.o ncat_ssl.o sys_wrap.o util.o ncat_posix.o $(LUA_OBJS) @LUA_DEPENDS@
|
||||
test/test-wildcard: test/test-wildcard.o ncat_core.o ncat_ssl.o sys_wrap.o util.o ncat_posix.o allowdeny_watcher.o $(LUA_OBJS) @LUA_DEPENDS@
|
||||
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(NSOCKLIB) $(NBASELIB) $(OPENSSL_LIBS) $(PCAP_LIBS) $(LUA_LIBS) $(LIBS)
|
||||
|
||||
.PHONY: uninstall all clean distclean build-lua
|
||||
|
|
@ -191,7 +191,7 @@ distclean-lua:
|
|||
distclean: clean @LUA_DIST_CLEAN@
|
||||
-rm -f Makefile makefile.dep $(CONFIG_CLEAN_FILES)
|
||||
|
||||
TESTS = ./test-addrset.sh ./test-cmdline-split ./test-uri
|
||||
TESTS = ./test-addrset.sh ./test-cmdline-split ./test-uri ./test-watch-allow-deny.sh
|
||||
ifneq ($(HAVE_OPENSSL),)
|
||||
TESTS += ./test-wildcard
|
||||
endif
|
||||
|
|
|
|||
367
ncat/allowdeny_watcher.c
Normal file
367
ncat/allowdeny_watcher.c
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
/***************************************************************************
|
||||
* allowdeny_watcher.c -- Cross-platform watcher for Ncat ACL files *
|
||||
***********************IMPORTANT NMAP LICENSE TERMS************************
|
||||
*
|
||||
* The Nmap Security Scanner is (C) 1996-2025 Nmap Software LLC ("The Nmap
|
||||
* Project"). Nmap is also a registered trademark of the Nmap Project.
|
||||
*
|
||||
* This program is distributed under the terms of the Nmap Public Source
|
||||
* License (NPSL). The exact license text applying to a particular Nmap
|
||||
* release or source code control revision is contained in the LICENSE file
|
||||
* distributed with that version of Nmap or source code control revision.
|
||||
* More Nmap copyright/legal information is available from
|
||||
* https://nmap.org/book/man-legal.html, and further information on the NPSL
|
||||
* license itself can be found at https://nmap.org/npsl/ . This header
|
||||
* summarizes some key points from the Nmap license, but is no substitute
|
||||
* for the actual license text.
|
||||
*
|
||||
* Nmap is generally free for end users to download and use themselves,
|
||||
* including commercial use. It is available from https://nmap.org.
|
||||
*
|
||||
* The Nmap license generally prohibits companies from using and
|
||||
* redistributing Nmap in commercial products, but we sell a special Nmap
|
||||
* OEM Edition with a more permissive license and special features for this
|
||||
* purpose. See https://nmap.org/oem/
|
||||
*
|
||||
* If you have received a written Nmap license agreement or contract stating
|
||||
* terms other than these (such as an Nmap OEM license), you may choose to
|
||||
* use and redistribute Nmap under those terms instead.
|
||||
*
|
||||
* The official Nmap Windows builds include the Npcap software
|
||||
* (https://npcap.com) for packet capture and transmission. It is under
|
||||
* separate license terms which forbid redistribution without special
|
||||
* permission. So the official Nmap Windows builds may not be redistributed
|
||||
* without special permission (such as an Nmap OEM license).
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Source code also allows you to port Nmap to new platforms, fix bugs, and
|
||||
* add new features. You are highly encouraged to submit your changes as a
|
||||
* Github PR or by email to the dev@nmap.org mailing list for possible
|
||||
* incorporation into the main distribution. Unless you specify otherwise,
|
||||
* it is understood that you are offering us very broad rights to use your
|
||||
* submissions as described in the Nmap Public Source License Contributor
|
||||
* Agreement. This is important because we fund the project by selling
|
||||
* licenses with various terms, and also because the inability to relicense
|
||||
* code has caused devastating problems for other Free Software projects
|
||||
* (such as KDE and NASM).
|
||||
*
|
||||
* The free version of Nmap 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. Warranties,
|
||||
* indemnification and commercial support are all available through the
|
||||
* Npcap OEM program--see https://nmap.org/oem/
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#include "allowdeny_watcher.h"
|
||||
#include "ncat_core.h"
|
||||
#include "util.h"
|
||||
#include "sys_wrap.h"
|
||||
|
||||
#ifdef WIN32
|
||||
/* Windows headers */
|
||||
#include <windows.h>
|
||||
#else /* POSIX */
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#if defined(__linux__)
|
||||
#include <sys/inotify.h>
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#include <sys/event.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Internal helpers */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct watcher_paths {
|
||||
char *allow_path;
|
||||
char *deny_path;
|
||||
};
|
||||
|
||||
#ifndef WIN32
|
||||
pthread_mutex_t g_allowdeny_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void reload_rules(const struct watcher_paths *wp)
|
||||
{
|
||||
/* Build new addrsets */
|
||||
struct addrset *new_allow = NULL;
|
||||
struct addrset *new_deny = NULL;
|
||||
|
||||
if (wp->allow_path) {
|
||||
FILE *fd = fopen(wp->allow_path, "r");
|
||||
if (fd) {
|
||||
new_allow = addrset_new();
|
||||
if (!addrset_add_file(new_allow, fd, o.af, !o.nodns)) {
|
||||
loguser("[WARN] Error parsing allow file %s, keeping old set.\n", wp->allow_path);
|
||||
addrset_free(new_allow);
|
||||
new_allow = NULL;
|
||||
}
|
||||
fclose(fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (wp->deny_path) {
|
||||
FILE *fd = fopen(wp->deny_path, "r");
|
||||
if (fd) {
|
||||
new_deny = addrset_new();
|
||||
if (!addrset_add_file(new_deny, fd, o.af, !o.nodns)) {
|
||||
loguser("[WARN] Error parsing deny file %s, keeping old set.\n", wp->deny_path);
|
||||
addrset_free(new_deny);
|
||||
new_deny = NULL;
|
||||
}
|
||||
fclose(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/* Swap in atomically under mutex */
|
||||
pthread_mutex_lock(&g_allowdeny_mutex);
|
||||
if (new_allow) {
|
||||
addrset_free(o.allowset);
|
||||
o.allowset = new_allow;
|
||||
}
|
||||
if (new_deny) {
|
||||
addrset_free(o.denyset);
|
||||
o.denyset = new_deny;
|
||||
}
|
||||
pthread_mutex_unlock(&g_allowdeny_mutex);
|
||||
|
||||
/* Log reload time */
|
||||
time_t now = time(NULL);
|
||||
char tsbuf[32];
|
||||
struct tm tmval;
|
||||
localtime_r(&now, &tmval);
|
||||
strftime(tsbuf, sizeof(tsbuf), "%Y-%m-%d %H:%M:%S", &tmval);
|
||||
|
||||
loguser("[INFO] Re-loaded allow/deny rules (modified at %s)\n", tsbuf);
|
||||
}
|
||||
|
||||
/* ---------------------- LINUX INOTIFY IMPLEMENTATION -------------------- */
|
||||
#if defined(__linux__)
|
||||
static void *watcher_thread(void *arg)
|
||||
{
|
||||
struct watcher_paths *wp = (struct watcher_paths *)arg;
|
||||
|
||||
int infd = inotify_init1(IN_NONBLOCK);
|
||||
if (infd < 0) {
|
||||
loguser("[WARN] Failed to init inotify: %s\n", strerror(errno));
|
||||
free(wp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int wd_allow = -1, wd_deny = -1;
|
||||
if (wp->allow_path)
|
||||
wd_allow = inotify_add_watch(infd, wp->allow_path, IN_CLOSE_WRITE | IN_MOVED_TO | IN_MOVE_SELF | IN_DELETE_SELF);
|
||||
if (wp->deny_path)
|
||||
wd_deny = inotify_add_watch(infd, wp->deny_path, IN_CLOSE_WRITE | IN_MOVED_TO | IN_MOVE_SELF | IN_DELETE_SELF);
|
||||
|
||||
if (wd_allow < 0 && wp->allow_path)
|
||||
loguser("[WARN] Cannot watch %s: %s\n", wp->allow_path, strerror(errno));
|
||||
if (wd_deny < 0 && wp->deny_path)
|
||||
loguser("[WARN] Cannot watch %s: %s\n", wp->deny_path, strerror(errno));
|
||||
|
||||
/* Main loop */
|
||||
const size_t bufsize = 4096;
|
||||
char *buf = (char *)safe_malloc(bufsize);
|
||||
|
||||
while (1) {
|
||||
ssize_t len = read(infd, buf, bufsize);
|
||||
if (len <= 0) {
|
||||
if (errno == EAGAIN || errno == EINTR) {
|
||||
/* Sleep briefly to avoid busy loop */
|
||||
usleep(200 * 1000);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (char *p = buf; p < buf + len;) {
|
||||
struct inotify_event *ev = (struct inotify_event *)p;
|
||||
if (ev->mask & (IN_CLOSE_WRITE | IN_MOVED_TO | IN_MOVE_SELF | IN_DELETE_SELF)) {
|
||||
reload_rules(wp);
|
||||
}
|
||||
|
||||
p += sizeof(struct inotify_event) + ev->len;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
close(infd);
|
||||
free(wp);
|
||||
return NULL;
|
||||
}
|
||||
#endif /* linux */
|
||||
|
||||
/* ---------------------- BSD KQUEUE IMPLEMENTATION ---------------------- */
|
||||
#if !defined(WIN32) && (defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__))
|
||||
static void *watcher_thread(void *arg)
|
||||
{
|
||||
struct watcher_paths *wp = (struct watcher_paths *)arg;
|
||||
|
||||
int kq = kqueue();
|
||||
if (kq < 0) {
|
||||
loguser("[WARN] kqueue init failed: %s\n", strerror(errno));
|
||||
free(wp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Watch the *directory* containing the file instead of the file itself.
|
||||
This reliably catches atomic-replace (mv) sequences which may not
|
||||
generate NOTE_WRITE for the original vnode. */
|
||||
int fd_allow = -1, fd_deny = -1;
|
||||
|
||||
if (wp->allow_path) {
|
||||
char *adir = Strdup(wp->allow_path);
|
||||
char *slash = strrchr(adir, '/');
|
||||
if (slash)
|
||||
*slash = '\0';
|
||||
fd_allow = open(adir[0] ? adir : ".", O_EVTONLY);
|
||||
free(adir);
|
||||
}
|
||||
|
||||
if (wp->deny_path) {
|
||||
char *ddir = Strdup(wp->deny_path);
|
||||
char *slash = strrchr(ddir, '/');
|
||||
if (slash)
|
||||
*slash = '\0';
|
||||
fd_deny = open(ddir[0] ? ddir : ".", O_EVTONLY);
|
||||
free(ddir);
|
||||
}
|
||||
|
||||
struct kevent evlist[2];
|
||||
int nev = 0;
|
||||
const uint32_t flags = NOTE_WRITE | NOTE_DELETE | NOTE_EXTEND | NOTE_RENAME;
|
||||
if (fd_allow >= 0)
|
||||
EV_SET(&evlist[nev++], fd_allow, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, flags, 0, NULL);
|
||||
if (fd_deny >= 0)
|
||||
EV_SET(&evlist[nev++], fd_deny, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, flags, 0, NULL);
|
||||
|
||||
if (nev == 0) {
|
||||
close(kq);
|
||||
free(wp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (kevent(kq, evlist, nev, NULL, 0, NULL) < 0) {
|
||||
loguser("[WARN] kevent register failed: %s\n", strerror(errno));
|
||||
close(kq);
|
||||
free(wp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
struct kevent ev;
|
||||
int n = kevent(kq, NULL, 0, &ev, 1, NULL);
|
||||
if (n == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (n > 0) {
|
||||
reload_rules(wp);
|
||||
}
|
||||
}
|
||||
|
||||
if (fd_allow >= 0) close(fd_allow);
|
||||
if (fd_deny >= 0) close(fd_deny);
|
||||
close(kq);
|
||||
free(wp);
|
||||
return NULL;
|
||||
}
|
||||
#endif /* BSD */
|
||||
|
||||
#endif /* !WIN32 */
|
||||
|
||||
/* ---------------------- WINDOWS IMPLEMENTATION ------------------------- */
|
||||
#ifdef WIN32
|
||||
static DWORD WINAPI watcher_thread_win(LPVOID param)
|
||||
{
|
||||
struct watcher_paths *wp = (struct watcher_paths *)param;
|
||||
|
||||
/* Extract directory path */
|
||||
char dir[MAX_PATH];
|
||||
strncpy(dir, wp->allow_path ? wp->allow_path : wp->deny_path, MAX_PATH - 1);
|
||||
dir[MAX_PATH-1] = '\0';
|
||||
char *lastSep = strrchr(dir, '\\');
|
||||
if (!lastSep) lastSep = strrchr(dir, '/');
|
||||
if (lastSep) *lastSep = '\0';
|
||||
|
||||
HANDLE hDir = CreateFileA(dir, FILE_LIST_DIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (hDir == INVALID_HANDLE_VALUE) {
|
||||
loguser("[WARN] Unable to watch directory %s (err=%lu)\n", dir, GetLastError());
|
||||
free(wp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BYTE buf[1024];
|
||||
DWORD bytesReturned;
|
||||
while (1) {
|
||||
if (!ReadDirectoryChangesW(hDir, buf, sizeof(buf), FALSE,
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME,
|
||||
&bytesReturned, NULL, NULL)) {
|
||||
Sleep(200);
|
||||
continue;
|
||||
}
|
||||
reload_rules(wp);
|
||||
}
|
||||
|
||||
CloseHandle(hDir);
|
||||
free(wp);
|
||||
return 0;
|
||||
}
|
||||
#endif /* WIN32 */
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Public API */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
int start_allowdeny_watcher(const char *allow_path, const char *deny_path)
|
||||
{
|
||||
/* If the feature is not requested, simply do nothing. Caller will ensure
|
||||
not to call us when flag is absent, but be tolerant. */
|
||||
if (allow_path == NULL && deny_path == NULL)
|
||||
return 0;
|
||||
|
||||
struct watcher_paths *wp = (struct watcher_paths *)safe_malloc(sizeof(*wp));
|
||||
wp->allow_path = allow_path ? Strdup(allow_path) : NULL;
|
||||
wp->deny_path = deny_path ? Strdup(deny_path) : NULL;
|
||||
|
||||
#if defined(__linux__)
|
||||
pthread_t tid;
|
||||
if (pthread_create(&tid, NULL, watcher_thread, wp) != 0) {
|
||||
bye("Failed to create watcher thread: %s", strerror(errno));
|
||||
}
|
||||
pthread_detach(tid);
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
pthread_t tid;
|
||||
if (pthread_create(&tid, NULL, watcher_thread, wp) != 0) {
|
||||
bye("Failed to create watcher thread: %s", strerror(errno));
|
||||
}
|
||||
pthread_detach(tid);
|
||||
#elif defined(WIN32)
|
||||
HANDLE th = CreateThread(NULL, 0, watcher_thread_win, wp, 0, NULL);
|
||||
if (th == NULL) {
|
||||
loguser("[WARN] Failed to start watcher thread (err=%lu)\n", GetLastError());
|
||||
free(wp);
|
||||
return -1;
|
||||
}
|
||||
CloseHandle(th);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
87
ncat/allowdeny_watcher.h
Normal file
87
ncat/allowdeny_watcher.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/***************************************************************************
|
||||
* allowdeny_watcher.h -- Platform-independent watcher for allow/deny ACL *
|
||||
***********************IMPORTANT NMAP LICENSE TERMS************************
|
||||
*
|
||||
* The Nmap Security Scanner is (C) 1996-2025 Nmap Software LLC ("The Nmap
|
||||
* Project"). Nmap is also a registered trademark of the Nmap Project.
|
||||
*
|
||||
* This program is distributed under the terms of the Nmap Public Source
|
||||
* License (NPSL). The exact license text applying to a particular Nmap
|
||||
* release or source code control revision is contained in the LICENSE
|
||||
* file distributed with that version of Nmap or source code control
|
||||
* revision. More Nmap copyright/legal information is available from
|
||||
* https://nmap.org/book/man-legal.html, and further information on the NPSL
|
||||
* license itself can be found at https://nmap.org/npsl/ . This header
|
||||
* summarizes some key points from the Nmap license, but is no substitute
|
||||
* for the actual license text.
|
||||
*
|
||||
* Nmap is generally free for end users to download and use themselves,
|
||||
* including commercial use. It is available from https://nmap.org.
|
||||
*
|
||||
* The Nmap license generally prohibits companies from using and
|
||||
* redistributing Nmap in commercial products, but we sell a special Nmap
|
||||
* OEM Edition with a more permissive license and special features for this
|
||||
* purpose. See https://nmap.org/oem/
|
||||
*
|
||||
* If you have received a written Nmap license agreement or contract stating
|
||||
* terms other than these (such as an Nmap OEM license), you may choose to
|
||||
* use and redistribute Nmap under those terms instead.
|
||||
*
|
||||
* The official Nmap Windows builds include the Npcap software
|
||||
* (https://npcap.com) for packet capture and transmission. It is under
|
||||
* separate license terms which forbid redistribution without special
|
||||
* permission. So the official Nmap Windows builds may not be redistributed
|
||||
* without special permission (such as an Nmap OEM license).
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Source code also allows you to port Nmap to new platforms, fix bugs, and
|
||||
* add new features. You are highly encouraged to submit your changes as a
|
||||
* GitHub PR or by email to the dev@nmap.org mailing list for possible
|
||||
* incorporation into the main distribution. Unless you specify otherwise,
|
||||
* it is understood that you are offering us very broad rights to use your
|
||||
* submissions as described in the Nmap Public Source License Contributor
|
||||
* Agreement. This is important because we fund the project by selling
|
||||
* licenses with various terms, and also because the inability to relicense
|
||||
* code has caused devastating problems for other Free Software projects
|
||||
* (such as KDE and NASM).
|
||||
*
|
||||
* The free version of Nmap 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. Warranties,
|
||||
* indemnification and commercial support are all available through the
|
||||
* Npcap OEM program--see https://nmap.org/oem/
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ALLOWDENY_WATCHER_H
|
||||
#define ALLOWDENY_WATCHER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Start background watcher thread that monitors the --allow/--deny host files.
|
||||
*
|
||||
* allow_path: Path to the file supplied to --allowfile, or NULL if none.
|
||||
* deny_path : Path to the file supplied to --denyfile, or NULL if none.
|
||||
*
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int start_allowdeny_watcher(const char *allow_path, const char *deny_path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
#include <pthread.h>
|
||||
|
||||
/* Global mutex guarding access to o.allowset / o.denyset */
|
||||
extern pthread_mutex_t g_allowdeny_mutex;
|
||||
#endif
|
||||
|
||||
#endif /* ALLOWDENY_WATCHER_H */
|
||||
|
|
@ -64,6 +64,12 @@ AC_SEARCH_LIBS(gethostbyname, nsl)
|
|||
AC_SEARCH_LIBS(dlopen, dl)
|
||||
AC_CHECK_FUNCS([dup2 gettimeofday inet_ntoa memset mkstemp select socket strcasecmp strchr strdup strerror strncasecmp strtol])
|
||||
|
||||
# Detect pthread library (for allow/deny watcher thread)
|
||||
AC_SEARCH_LIBS([pthread_create], [pthread], [PTHREAD_LIBS="$ac_cv_search_pthread_create"], [PTHREAD_LIBS=""])
|
||||
# Sanitize possible 'none required' response which indicates pthread symbols are in libc
|
||||
AS_IF([test "x$PTHREAD_LIBS" = "xnone required"], [PTHREAD_LIBS=""])
|
||||
AC_SUBST([PTHREAD_LIBS])
|
||||
|
||||
# If they didn't specify it, we try to find it
|
||||
if test "$use_openssl" = "yes" -a -z "$specialssldir" ; then
|
||||
AC_CHECK_HEADER(openssl/ssl.h,,
|
||||
|
|
|
|||
|
|
@ -79,11 +79,12 @@ Options taking a time assume seconds\&. Append \*(Aqms\*(Aq for milliseconds,
|
|||
\-\-send\-only Only send data, ignoring received; quit on EOF
|
||||
\-\-recv\-only Only receive data, never send anything
|
||||
\-\-no\-shutdown Continue half\-duplex when receiving EOF on stdin
|
||||
\-q <time> After EOF on stdin, wait <time> then quit\&.
|
||||
\-q <time> After EOF on stdin, wait <time> then quit\&.
|
||||
\-\-allow Allow only given hosts to connect to Ncat
|
||||
\-\-allowfile A file of hosts allowed to connect to Ncat
|
||||
\-\-deny Deny given hosts from connecting to Ncat
|
||||
\-\-denyfile A file of hosts denied from connecting to Ncat
|
||||
\-\-watch\-allow\-deny Re\-read allow/deny files automatically on change
|
||||
\-\-broker Enable Ncat\*(Aqs connection brokering mode
|
||||
\-\-chat Start a simple Ncat chat server
|
||||
\-\-proxy <addr[:port]> Specify address of host to proxy through
|
||||
|
|
@ -442,6 +443,22 @@ takes precedence\&. Host specifications follow the same syntax used by Nmap\&.
|
|||
This is the same functionality as
|
||||
\fB\-\-deny\fR, except that excluded hosts are provided in a new\-line delimited deny file, rather than directly on the command line\&.
|
||||
.RE
|
||||
.PP
|
||||
\fB\-\-watch\-allow\-deny\fR (Watch allow/deny files)
|
||||
.RS 4
|
||||
When this option is given in listen mode, Ncat keeps an eye on the files that
|
||||
were provided with
|
||||
\fB\-\-allowfile\fR
|
||||
and
|
||||
\fB\-\-denyfile\fR.
|
||||
If either file is modified, Ncat automatically reloads the in\-memory access
|
||||
control lists so that newly accepted connections are evaluated against the
|
||||
updated rules. Existing connections are unaffected. The implementation uses
|
||||
efficient, event–driven file notification facilities on each platform
|
||||
(\fBinotify\fR on Linux, \fBkqueue\fR on BSD/macOS, and
|
||||
\fBReadDirectoryChangesW\fR on Windows), so it has no impact on performance
|
||||
when the option is not selected.
|
||||
.RE
|
||||
.SH "TIMING OPTIONS"
|
||||
.PP
|
||||
These options accept a
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ void options_init(void)
|
|||
o.allowset = addrset_new();
|
||||
o.denyset = addrset_new();
|
||||
o.httpserver = 0;
|
||||
o.watch_allow_deny = 0;
|
||||
|
||||
o.nsock_engine = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,8 @@ struct options {
|
|||
struct addrset *allowset;
|
||||
struct addrset *denyset;
|
||||
int httpserver;
|
||||
/* When true, automatically reload allow/deny files on modification. */
|
||||
int watch_allow_deny;
|
||||
int nsock_engine;
|
||||
/* Output messages useful for testing to stderr? */
|
||||
int test;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@
|
|||
#include "ncat.h"
|
||||
#include "util.h"
|
||||
#include "sys_wrap.h"
|
||||
#include "allowdeny_watcher.h"
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
|
|
@ -202,7 +203,8 @@ static void host_list_to_set(struct addrset *set, struct host_list_node *list)
|
|||
char *spec, *commalist;
|
||||
|
||||
commalist = node->spec;
|
||||
while ((spec = strtok(commalist, ",")) != NULL) {
|
||||
while ((spec = strtok(commalist, ",")) != NULL)
|
||||
{
|
||||
commalist = NULL;
|
||||
if (!addrset_add_spec(set, spec, o.af, !o.nodns))
|
||||
bye("error in host specification \"%s\".", node->spec);
|
||||
|
|
@ -224,6 +226,10 @@ int main(int argc, char *argv[])
|
|||
struct host_list_node *allow_host_list = NULL;
|
||||
struct host_list_node *deny_host_list = NULL;
|
||||
|
||||
/* Remember first allow/deny filename (if any) for watcher */
|
||||
char *allowfile_path = NULL;
|
||||
char *denyfile_path = NULL;
|
||||
|
||||
unsigned short proxyport;
|
||||
/* vsock ports are 32 bits, so port variables must be at least that wide. */
|
||||
unsigned int max_port = 65535;
|
||||
|
|
@ -299,6 +305,7 @@ int main(int argc, char *argv[])
|
|||
{"ssl-ciphers", optional_argument, NULL, 0},
|
||||
{"ssl-alpn", optional_argument, NULL, 0},
|
||||
#endif
|
||||
{"watch-allow-deny",no_argument, NULL, 0},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
|
|
@ -483,10 +490,14 @@ int main(int argc, char *argv[])
|
|||
} else if (strcmp(long_options[option_index].name, "allowfile") == 0) {
|
||||
o.allow = 1;
|
||||
host_list_add_filename(&allow_host_list, optarg);
|
||||
if (!allowfile_path) allowfile_path = optarg;
|
||||
} else if (strcmp(long_options[option_index].name, "deny") == 0) {
|
||||
host_list_add_spec(&deny_host_list, optarg);
|
||||
} else if (strcmp(long_options[option_index].name, "denyfile") == 0) {
|
||||
host_list_add_filename(&deny_host_list, optarg);
|
||||
if (!denyfile_path) denyfile_path = optarg;
|
||||
} else if (strcmp(long_options[option_index].name, "watch-allow-deny") == 0) {
|
||||
o.watch_allow_deny = 1;
|
||||
} else if (strcmp(long_options[option_index].name, "append-output") == 0) {
|
||||
o.append = 1;
|
||||
} else if (strcmp(long_options[option_index].name, "sctp") == 0) {
|
||||
|
|
@ -626,6 +637,7 @@ int main(int argc, char *argv[])
|
|||
" --allowfile A file of hosts allowed to connect to Ncat\n"
|
||||
" --deny Deny given hosts from connecting to Ncat\n"
|
||||
" --denyfile A file of hosts denied from connecting to Ncat\n"
|
||||
" --watch-allow-deny Reload allow/deny files automatically on change\n"
|
||||
" --broker Enable Ncat's connection brokering mode\n"
|
||||
" --chat Start a simple Ncat chat server\n"
|
||||
" --proxy <addr[:port]> Specify address of host to proxy through\n"
|
||||
|
|
@ -833,6 +845,11 @@ int main(int argc, char *argv[])
|
|||
host_list_to_set(o.denyset, deny_host_list);
|
||||
host_list_free(deny_host_list);
|
||||
|
||||
/* Launch background watcher if requested and at least one filename is supplied */
|
||||
if (o.watch_allow_deny) {
|
||||
start_allowdeny_watcher(allowfile_path, denyfile_path);
|
||||
}
|
||||
|
||||
int rc;
|
||||
int num_ports = 0;
|
||||
if (srcport != -1 && o.listen) {
|
||||
|
|
|
|||
45
ncat/test/test-watch-allow-deny.sh
Executable file
45
ncat/test/test-watch-allow-deny.sh
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
#!/bin/sh
|
||||
# Test script for Ncat --watch-allow-deny / auto-reload feature
|
||||
# Part of the Nmap Project - see LICENSE for terms
|
||||
|
||||
set -e
|
||||
|
||||
PORT=41001
|
||||
TMP_ALLOW=$(mktemp ncat_allow_XXXX)
|
||||
trap 'rm -f "$TMP_ALLOW"; kill $SERVER_PID 2>/dev/null || true' EXIT
|
||||
|
||||
# Initial allow rule permits localhost
|
||||
printf "127.0.0.1\n" > "$TMP_ALLOW"
|
||||
|
||||
# Start Ncat server in background
|
||||
../ncat -l $PORT --keep-open --recv-only --allowfile "$TMP_ALLOW" --watch-allow-deny 2>/dev/null &
|
||||
SERVER_PID=$!
|
||||
|
||||
# Give it a moment to start
|
||||
sleep 1
|
||||
|
||||
# First connection should succeed
|
||||
if ! ../ncat 127.0.0.1 $PORT -z 2>/dev/null; then
|
||||
echo "Initial connection unexpectedly failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Overwrite via temp move to avoid partial read races
|
||||
TMP2=$(mktemp ncat_allow2_XXXX)
|
||||
printf "192.0.2.1\n" > "$TMP2"
|
||||
mv "$TMP2" "$TMP_ALLOW"
|
||||
# Wait up to ~10 s (20 × 0.5 s)
|
||||
tries=20
|
||||
while [ $tries -gt 0 ]; do
|
||||
if printf "ping\n" | ../ncat 127.0.0.1 $PORT -w1 2>/dev/null; then
|
||||
# Still allowed -> retry after short delay
|
||||
sleep 0.5
|
||||
tries=$((tries-1))
|
||||
else
|
||||
echo "PASS watch-allow-deny"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Connection still allowed after watcher delay"
|
||||
exit 1
|
||||
26
ncat/util.c
26
ncat/util.c
|
|
@ -72,6 +72,9 @@
|
|||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#ifndef WIN32
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_STAT_H
|
||||
#include <sys/stat.h>
|
||||
|
|
@ -538,14 +541,23 @@ unsigned char *buildsrcrte(struct in_addr dstaddr, struct in_addr routes[],
|
|||
|
||||
int allow_access(const union sockaddr_u *su)
|
||||
{
|
||||
/* A host not in the allow set is denied, but only if the --allow or
|
||||
--allowfile option was given. */
|
||||
if (o.allow && !addrset_contains(o.allowset, &su->sockaddr))
|
||||
return 0;
|
||||
if (addrset_contains(o.denyset, &su->sockaddr))
|
||||
return 0;
|
||||
#ifndef WIN32
|
||||
extern pthread_mutex_t g_allowdeny_mutex;
|
||||
pthread_mutex_lock(&g_allowdeny_mutex);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
int allowed = 1;
|
||||
|
||||
/* A host not in the allow set is denied, but only if --allow/--allowfile was used. */
|
||||
if (o.allow && !addrset_contains(o.allowset, &su->sockaddr))
|
||||
allowed = 0;
|
||||
if (addrset_contains(o.denyset, &su->sockaddr))
|
||||
allowed = 0;
|
||||
|
||||
#ifndef WIN32
|
||||
pthread_mutex_unlock(&g_allowdeny_mutex);
|
||||
#endif
|
||||
return allowed;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue