mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-20 09:03:24 +00:00
3821 lines
111 KiB
C++
3821 lines
111 KiB
C++
/*
|
|
*
|
|
* (C) 2013-22 - ntop.org
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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 for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
*/
|
|
|
|
#include "ntop_includes.h"
|
|
|
|
#ifdef __linux__
|
|
#include <sys/inotify.h>
|
|
#define EVENT_SIZE ( sizeof (struct inotify_event) )
|
|
#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#include <shlobj.h> /* SHGetFolderPath() */
|
|
#else
|
|
#include <ifaddrs.h>
|
|
#include <sys/file.h>
|
|
#endif
|
|
|
|
Ntop *ntop;
|
|
|
|
static const char* dirs[] = {
|
|
NULL, /* Populated at runtime */
|
|
NULL, /* Populated at runtime for WIN32 builds */
|
|
CONST_ALT_INSTALL_DIR,
|
|
CONST_ALT2_INSTALL_DIR,
|
|
#ifndef WIN32
|
|
CONST_DEFAULT_INSTALL_DIR, /* Last is the <path> specified with ./configure --prefix <path>, defaulting to /usr/local */
|
|
#endif
|
|
NULL
|
|
};
|
|
|
|
extern struct keyval string_to_replace[]; /* LuaEngine.cpp */
|
|
|
|
/* ******************************************* */
|
|
|
|
Ntop::Ntop(const char *appName) {
|
|
// WTF: it's weird why do you want a global instance of ntop.
|
|
ntop = this;
|
|
globals = new (std::nothrow) NtopGlobals();
|
|
extract = new (std::nothrow) TimelineExtract();
|
|
address = new (std::nothrow) AddressResolution();
|
|
offline = false;
|
|
pa = NULL;
|
|
#ifdef WIN32
|
|
myTZname = strdup(_tzname[0] ? _tzname[0] : "CET");
|
|
#else
|
|
myTZname = strdup(tzname[0] ? tzname[0] : "CET");
|
|
#endif
|
|
custom_ndpi_protos = NULL;
|
|
prefs = NULL, redis = NULL;
|
|
#ifndef HAVE_NEDGE
|
|
export_interface = NULL;
|
|
zmqPublisher = NULL;
|
|
#endif
|
|
trackers_automa = NULL;
|
|
num_cpus = -1;
|
|
num_defined_interfaces = 0;
|
|
num_dump_interfaces = 0;
|
|
iface = NULL;
|
|
start_time = last_modified_static_file_epoch = 0, epoch_buf[0] = '\0'; /* It will be initialized by start() */
|
|
last_stats_reset = 0;
|
|
old_iface_to_purge = NULL;
|
|
|
|
setZoneInfo();
|
|
|
|
/* Checks loader */
|
|
flowChecksReloadInProgress = true; /* Lazy, will be reloaded the first time this condition is evaluated */
|
|
hostChecksReloadInProgress = true;
|
|
flow_checks_loader = NULL;
|
|
host_checks_loader = NULL;
|
|
|
|
/* Flow alerts exclusions */
|
|
#ifdef NTOPNG_PRO
|
|
alertExclusionsReloadInProgress = true;
|
|
alert_exclusions = alert_exclusions_shadow = NULL;
|
|
#endif
|
|
|
|
/* Host Pools reload - Interfaces initialize their pools inside the constructor */
|
|
hostPoolsReloadInProgress = false;
|
|
|
|
httpd = NULL, geo = NULL, mac_manufacturers = NULL;
|
|
memset(&cpu_stats, 0, sizeof(cpu_stats));
|
|
cpu_load = 0;
|
|
system_interface = NULL;
|
|
purgeLoop_started = false;
|
|
interfacesShuttedDown = false;
|
|
#ifndef WIN32
|
|
cping = NULL, default_ping = NULL;
|
|
#endif
|
|
privileges_dropped = false;
|
|
can_send_icmp = Utils::isPingSupported();
|
|
|
|
for(int i = 0; i < CONST_MAX_NUM_NETWORKS; i++)
|
|
local_network_names[i] = local_network_aliases[i] = NULL;
|
|
|
|
#ifndef WIN32
|
|
if(can_send_icmp) {
|
|
cping = new (std::nothrow) ContinuousPing();
|
|
default_ping = new (std::nothrow)Ping(NULL /* System interface */);
|
|
}
|
|
#endif
|
|
|
|
internal_alerts_queue = new (std::nothrow) FifoSerializerQueue(INTERNAL_ALERTS_QUEUE_SIZE);
|
|
|
|
resolvedHostsBloom = new (std::nothrow) Bloom(NUM_HOSTS_RESOLVED_BITS);
|
|
|
|
#ifdef WIN32
|
|
if(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, working_dir) != S_OK) {
|
|
strncpy(working_dir, "C:\\Windows\\Temp\\ntopng", sizeof(working_dir)); // Fallback: it should never happen
|
|
working_dir[sizeof(working_dir) - 1] = '\0';
|
|
} else {
|
|
int l = strlen(working_dir);
|
|
|
|
snprintf(&working_dir[l], sizeof(working_dir), "%s", "\\ntopng");
|
|
}
|
|
|
|
// Get the full path and filename of this program
|
|
if(GetModuleFileName(NULL, startup_dir, sizeof(startup_dir)) == 0) {
|
|
startup_dir[0] = '\0';
|
|
} else {
|
|
for(int i=(int)strlen(startup_dir)-1; i>0; i--) {
|
|
if(startup_dir[i] == '\\') {
|
|
startup_dir[i] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
snprintf(install_dir, sizeof(install_dir), "%s", startup_dir);
|
|
|
|
dirs[0] = startup_dir;
|
|
dirs[1] = install_dir;
|
|
#else
|
|
/* Note: working_dir folder will be created lazily, avoid creating it now */
|
|
if (Utils::dir_exists(CONST_OLD_DEFAULT_DATA_DIR)) /* keep using the old dir */
|
|
snprintf(working_dir, sizeof(working_dir), CONST_OLD_DEFAULT_DATA_DIR);
|
|
else
|
|
snprintf(working_dir, sizeof(working_dir), CONST_DEFAULT_DATA_DIR);
|
|
|
|
//umask(0);
|
|
|
|
if(getcwd(startup_dir, sizeof(startup_dir)) == NULL)
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Occurred while checking the current directory (errno=%d)", errno);
|
|
|
|
dirs[0] = startup_dir;
|
|
|
|
install_dir[0] = '\0';
|
|
|
|
for(int i = 0; i < (int)COUNT_OF(dirs); i++) {
|
|
if(dirs[i]) {
|
|
char path[MAX_PATH+32];
|
|
struct stat statbuf;
|
|
|
|
snprintf(path, sizeof(path), "%s/scripts/lua/index.lua", dirs[i]);
|
|
fixPath(path);
|
|
|
|
if(stat(path, &statbuf) == 0) {
|
|
snprintf(install_dir, sizeof(install_dir), "%s", dirs[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
setScriptsDir();
|
|
|
|
#ifdef NTOPNG_PRO
|
|
pro = new (std::nothrow) NtopPro();
|
|
#else
|
|
pro = NULL;
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
inotify_fd = -1;
|
|
#endif
|
|
|
|
#ifndef HAVE_NEDGE
|
|
refresh_ips_rules = false;
|
|
#endif
|
|
|
|
// printf("--> %s [%s]\n", startup_dir, appName);
|
|
|
|
initTimezone();
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "System Timezone offset: %+ld", time_offset);
|
|
|
|
initAllowedProtocolPresets();
|
|
|
|
udp_socket = Utils::openSocket(AF_INET, SOCK_DGRAM, 0, "Ntop UDP");
|
|
|
|
#ifndef WIN32
|
|
setservent(1);
|
|
|
|
startupLockFile = -1;
|
|
#endif
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
#ifndef WIN32
|
|
|
|
void Ntop::lockNtopInstance() {
|
|
char lockPath[MAX_PATH+8];
|
|
struct flock lock;
|
|
struct stat st;
|
|
|
|
if((stat(working_dir, &st) != 0) || !S_ISDIR(st.st_mode)) {
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Working dir does not exist yet");
|
|
return;
|
|
}
|
|
|
|
snprintf(lockPath, sizeof(lockPath), "%s/.lock", working_dir);
|
|
|
|
lock.l_type = F_WRLCK; /* read/write (exclusive versus shared) lock */
|
|
lock.l_whence = SEEK_SET; /* base for seek offsets */
|
|
lock.l_start = 0; /* 1st byte in file */
|
|
lock.l_len = 0; /* 0 here means 'until EOF' */
|
|
lock.l_pid = getpid(); /* process id */
|
|
|
|
if((startupLockFile = open(lockPath, O_RDWR | O_CREAT, 0666)) < 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to open lock file %s [%s]",
|
|
lockPath, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if(fcntl(startupLockFile, F_SETLK, &lock) < 0) { /** F_SETLK doesn't block, F_SETLKW does **/
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Another ntopng instance is running...");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ******************************************* */
|
|
|
|
/*
|
|
Setup timezone differences
|
|
|
|
We call it all the time as daylight can change
|
|
during the night and thus we need to have it "fresh"
|
|
*/
|
|
|
|
void Ntop::initTimezone() {
|
|
#ifdef WIN32
|
|
time_offset = -_timezone;
|
|
#else
|
|
time_t t = time(NULL);
|
|
struct tm *l = localtime(&t);
|
|
|
|
time_offset = l->tm_gmtoff;
|
|
#endif
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
Ntop::~Ntop() {
|
|
int num_local_networks = local_network_tree.getNumAddresses();
|
|
|
|
for(int i = 0; i < num_local_networks; i++) {
|
|
if (local_network_names[i] != NULL) free(local_network_names[i]);
|
|
if (local_network_aliases[i] != NULL) free(local_network_aliases[i]);
|
|
}
|
|
|
|
if(httpd)
|
|
delete httpd; /* Stop the http server before tearing down network interfaces */
|
|
|
|
if(purgeLoop_started)
|
|
pthread_join(purgeLoop, NULL);
|
|
|
|
for(int i = 0; i < num_defined_interfaces; i++) {
|
|
if(iface[i]) {
|
|
delete iface[i];
|
|
iface[i] = NULL;
|
|
}
|
|
}
|
|
|
|
if(zoneinfo) free(zoneinfo);
|
|
|
|
delete []iface;
|
|
|
|
if(system_interface) delete system_interface;
|
|
|
|
if(extract) delete extract;
|
|
|
|
#ifndef WIN32
|
|
if(cping) delete cping;
|
|
if(default_ping) delete default_ping;
|
|
|
|
for(std::map<std::string /* ifname */, Ping*>::iterator it = ping.begin(); it != ping.end(); ++it)
|
|
delete it->second;
|
|
#endif
|
|
|
|
Utils::closeSocket(udp_socket);
|
|
|
|
if(trackers_automa) ndpi_free_automa(trackers_automa);
|
|
if(custom_ndpi_protos) free(custom_ndpi_protos);
|
|
if(old_iface_to_purge) delete old_iface_to_purge;
|
|
|
|
delete address;
|
|
|
|
if(pa) delete pa;
|
|
if(geo) delete geo;
|
|
if(mac_manufacturers) delete mac_manufacturers;
|
|
#ifndef HAVE_NEDGE
|
|
if(zmqPublisher) delete zmqPublisher;
|
|
#endif
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(pro) delete pro;
|
|
if(alert_exclusions) delete alert_exclusions;
|
|
if(alert_exclusions_shadow) delete alert_exclusions_shadow;
|
|
#endif
|
|
|
|
#if defined(HAVE_CLICKHOUSE) && defined(HAVE_MYSQL)
|
|
if(clickhouseImport)
|
|
delete clickhouseImport;
|
|
#endif
|
|
|
|
if(resolvedHostsBloom) delete resolvedHostsBloom;
|
|
delete internal_alerts_queue;
|
|
|
|
if(redis) { delete redis; redis = NULL; }
|
|
if(prefs) { delete prefs; prefs = NULL; }
|
|
if(globals) { delete globals; globals = NULL; }
|
|
|
|
if(flow_checks_loader) delete flow_checks_loader;
|
|
if(host_checks_loader) delete host_checks_loader;
|
|
|
|
if(myTZname) free(myTZname);
|
|
|
|
#ifdef __linux__
|
|
if(inotify_fd > 0) close(inotify_fd);
|
|
#endif
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::registerPrefs(Prefs *_prefs, bool quick_registration) {
|
|
char value[32];
|
|
struct stat buf;
|
|
|
|
prefs = _prefs;
|
|
|
|
if(!quick_registration) {
|
|
if(stat(prefs->get_data_dir(), &buf)
|
|
|| (!(buf.st_mode & S_IFDIR)) /* It's not a directory */
|
|
// || (!(buf.st_mode & S_IWRITE)) /* It's not writable */
|
|
) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Invalid directory %s specified",
|
|
prefs->get_data_dir());
|
|
exit(-1);
|
|
}
|
|
|
|
if(stat(prefs->get_callbacks_dir(), &buf)
|
|
|| (!(buf.st_mode & S_IFDIR)) /* It's not a directory */
|
|
|| (!(buf.st_mode & S_IREAD)) /* It's not readable */) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Invalid directory %s specified",
|
|
prefs->get_callbacks_dir());
|
|
exit(-1);
|
|
}
|
|
|
|
if(prefs->get_local_networks()) {
|
|
setLocalNetworks(prefs->get_local_networks());
|
|
} else {
|
|
/* Add defaults */
|
|
/* http://www.networksorcery.com/enp/protocol/ip/multicast.htm */
|
|
setLocalNetworks((char*)CONST_DEFAULT_LOCAL_NETS);
|
|
}
|
|
}
|
|
|
|
/* Initialize redis and populate some default values */
|
|
Utils::initRedis(&redis, prefs->get_redis_host(), prefs->get_redis_password(),
|
|
prefs->get_redis_port(), prefs->get_redis_db_id(), quick_registration);
|
|
if(redis) redis->setDefaults();
|
|
|
|
if(!quick_registration) {
|
|
/* Initialize another redis instance for the trace of events */
|
|
ntop->getTrace()->initRedis(prefs->get_redis_host(), prefs->get_redis_password(),
|
|
prefs->get_redis_port(), prefs->get_redis_db_id());
|
|
|
|
if(ntop->getRedis() == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to initialize redis. Quitting...");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
#ifdef NTOPNG_PRO
|
|
pro->init_license();
|
|
|
|
if(!ntop->getPro()->has_unlimited_enterprise_l_license())
|
|
prefs->toggle_dump_flows_direct(false);
|
|
#endif
|
|
|
|
if(quick_registration) return;
|
|
|
|
checkReloadAlertExclusions();
|
|
|
|
system_interface = new (std::nothrow) NetworkInterface(SYSTEM_INTERFACE_NAME, SYSTEM_INTERFACE_NAME);
|
|
|
|
/* License check could have increased the number of interfaces available */
|
|
resetNetworkInterfaces();
|
|
|
|
/* Read the old last_stats_reset */
|
|
if(ntop->getRedis()->get((char*)LAST_RESET_TIME, value, sizeof(value)) >= 0)
|
|
last_stats_reset = atol(value);
|
|
|
|
/* Now we can enable the periodic activities */
|
|
pa = new (std::nothrow) PeriodicActivities();
|
|
|
|
#if defined(HAVE_CLICKHOUSE) && defined(HAVE_MYSQL)
|
|
if(prefs->do_dump_flows_on_clickhouse())
|
|
clickhouseImport = new (std::nothrow) ClickHouseImport();
|
|
else
|
|
clickhouseImport = NULL;
|
|
#endif
|
|
|
|
redis->setInitializationComplete();
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::resetNetworkInterfaces() {
|
|
if(iface) delete []iface;
|
|
|
|
if((iface = new (std::nothrow) NetworkInterface*[MAX_NUM_DEFINED_INTERFACES]()) == NULL)
|
|
throw "Not enough memory";
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Interfaces Available: %u", MAX_NUM_DEFINED_INTERFACES);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::createExportInterface() {
|
|
#ifndef HAVE_NEDGE
|
|
if(prefs->get_export_endpoint())
|
|
export_interface = new (std::nothrow) ExportInterface(prefs->get_export_endpoint());
|
|
else
|
|
export_interface = NULL;
|
|
#endif
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::start() {
|
|
struct timeval begin, end;
|
|
u_long usec_diff;
|
|
char daybuf[64], buf[128];
|
|
time_t when = time(NULL);
|
|
int i = 0;
|
|
|
|
getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Welcome to %s %s v.%s (%s) - (C) 1998-22 ntop.org",
|
|
#ifdef HAVE_NEDGE
|
|
"ntopng edge",
|
|
#else
|
|
"ntopng",
|
|
#endif
|
|
PACKAGE_MACHINE, PACKAGE_VERSION, NTOPNG_GIT_RELEASE);
|
|
|
|
if(PACKAGE_OS[0] != '\0')
|
|
getTrace()->traceEvent(TRACE_NORMAL, "Built on %s", PACKAGE_OS);
|
|
|
|
last_modified_static_file_epoch = start_time = time(NULL);
|
|
snprintf(epoch_buf, sizeof(epoch_buf), "%u", (u_int32_t)start_time);
|
|
|
|
string_to_replace[i].key = CONST_HTTP_PREFIX_STRING, string_to_replace[i].val = ntop->getPrefs()->get_http_prefix(); i++;
|
|
string_to_replace[i].key = CONST_NTOP_STARTUP_EPOCH, string_to_replace[i].val = ntop->getStartTimeString(); i++;
|
|
string_to_replace[i].key = CONST_NTOP_PRODUCT_NAME, string_to_replace[i].val =
|
|
#ifdef HAVE_NEDGE
|
|
ntop->getPro()->get_product_name()
|
|
#else
|
|
(char*)"ntopng"
|
|
#endif
|
|
; i++;
|
|
string_to_replace[i].key = NULL, string_to_replace[i].val = NULL;
|
|
|
|
strftime(daybuf, sizeof(daybuf), CONST_DB_DAY_FORMAT, localtime(&when));
|
|
snprintf(buf, sizeof(buf), "ntopng.%s.hostkeys", daybuf);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(!pro->forced_community_edition())
|
|
pro->printLicenseInfo();
|
|
|
|
am.init();
|
|
#endif
|
|
|
|
FlowRiskAlerts::checkUndefinedRisks();
|
|
|
|
prefs->loadInstanceNameDefaults();
|
|
|
|
loadLocalInterfaceAddress();
|
|
|
|
address->startResolveAddressLoop();
|
|
|
|
system_interface->allocateStructures();
|
|
|
|
for(int i=0; i<num_defined_interfaces; i++)
|
|
iface[i]->allocateStructures();
|
|
|
|
#ifdef __linux__
|
|
inotify_fd = inotify_init();
|
|
|
|
if(inotify_fd < 0)
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "inotify_init failed[%d]: %s", errno, strerror(errno));
|
|
else {
|
|
uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY;
|
|
char path[MAX_PATH];
|
|
|
|
/* Watch some directories. TODO: recursive watch */
|
|
snprintf(path, sizeof(path), "%s/system", ntop->get_callbacks_dir());
|
|
inotify_add_watch(inotify_fd, path, mask);
|
|
|
|
snprintf(path, sizeof(path), "%s/interface", ntop->get_callbacks_dir());
|
|
inotify_add_watch(inotify_fd, path, mask);
|
|
|
|
snprintf(path, sizeof(path), "%s/lua/modules", prefs->get_scripts_dir());
|
|
inotify_add_watch(inotify_fd, path, mask);
|
|
|
|
#ifdef NTOPNG_PRO
|
|
snprintf(path, sizeof(path), "%s/lua/pro/modules", prefs->get_scripts_dir());
|
|
inotify_add_watch(inotify_fd, path, mask);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/* Note: must start periodic activities loop only *after* interfaces have been
|
|
* completely initialized.
|
|
*
|
|
* Note: this will also run the startup.lua script sequentially.
|
|
* After this call, startup.lua has completed. */
|
|
pa->startPeriodicActivitiesLoop();
|
|
|
|
if(get_HTTPserver())
|
|
get_HTTPserver()->start_accepting_requests();
|
|
|
|
#ifdef HAVE_NEDGE
|
|
/* TODO: enable start/stop of the captive portal webserver directly from Lua */
|
|
if(get_HTTPserver() && prefs->isCaptivePortalEnabled())
|
|
get_HTTPserver()->startCaptiveServer();
|
|
#endif
|
|
|
|
checkReloadHostPools();
|
|
checkReloadFlowChecks();
|
|
checkReloadHostChecks();
|
|
|
|
for(int i=0; i<num_defined_interfaces; i++)
|
|
iface[i]->startPacketPolling();
|
|
|
|
startPurgeLoop();
|
|
|
|
// sleep(2);
|
|
|
|
for(int i=0; i<num_defined_interfaces; i++)
|
|
iface[i]->checkPointCounters(true); /* Reset drop counters */
|
|
|
|
/* Align to the next 5-th second of the clock to make sure
|
|
housekeeping starts alinged (and remains aligned when
|
|
the housekeeping frequency is a multiple of 5 seconds) */
|
|
gettimeofday(&begin, NULL);
|
|
_usleep((5 - begin.tv_sec % 5) * 1e6 - begin.tv_usec);
|
|
|
|
Utils::setThreadName("ntopng-main");
|
|
|
|
while((!globals->isShutdown()) && (!globals->isShutdownRequested())) {
|
|
const u_int32_t nap_usec = ntop->getPrefs()->get_housekeeping_frequency() * 1e6;
|
|
|
|
gettimeofday(&begin, NULL);
|
|
|
|
runHousekeepingTasks();
|
|
|
|
/*
|
|
Check if it is time to signal the shutdown, depending on the configuration.
|
|
NOTE: Shutdown when done is only meaningful for pcap-dump interfaces when
|
|
the file has been read.
|
|
*/
|
|
checkShutdownWhenDone();
|
|
|
|
gettimeofday(&end, NULL);
|
|
usec_diff = Utils::usecTimevalDiff(&end, &begin);
|
|
|
|
if(usec_diff >= nap_usec)
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Houkeeping activities (main loop) took %.3fs", (float)usec_diff/1e6);
|
|
|
|
#ifdef __linux__
|
|
if (inotify_fd > 0) {
|
|
while (usec_diff < nap_usec) {
|
|
int maxfd = 0;
|
|
fd_set rset;
|
|
struct timeval tv;
|
|
int rc;
|
|
|
|
//ntop->getTrace()->traceEvent(TRACE_NORMAL, "Sleeping %i microsecods", (nap_usec - usec_diff));
|
|
|
|
FD_ZERO(&rset);
|
|
FD_SET(inotify_fd, &rset);
|
|
maxfd = inotify_fd;
|
|
|
|
tv.tv_sec = 0, tv.tv_usec = (nap_usec - usec_diff);
|
|
|
|
rc = select(maxfd + 1, &rset, NULL, NULL, &tv);
|
|
|
|
if(rc > 0) {
|
|
if(FD_ISSET(inotify_fd, &rset)) {
|
|
char buffer[EVENT_BUF_LEN];
|
|
|
|
/* Consume the event */
|
|
rc = read(inotify_fd, buffer, sizeof(buffer));
|
|
|
|
if(rc < 0)
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "read() returned %d", rc);
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Directory changed");
|
|
}
|
|
}
|
|
|
|
gettimeofday(&end, NULL);
|
|
usec_diff = Utils::usecTimevalDiff(&end, &begin);
|
|
|
|
if (rc < 0)
|
|
break; /* fall back to sleeping to avoid spinning on a failing select */
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if(usec_diff < nap_usec)
|
|
_usleep(nap_usec-usec_diff);
|
|
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isLocalAddress(int family, void *addr, int16_t *network_id, u_int8_t *network_mask_bits) {
|
|
u_int8_t nmask_bits;
|
|
*network_id = this->localNetworkLookup(family, addr, &nmask_bits);
|
|
|
|
if(*network_id != -1 && network_mask_bits)
|
|
*network_mask_bits = nmask_bits;
|
|
|
|
return(((*network_id) == -1) ? false : true);
|
|
};
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::getLocalNetworkIp(int16_t local_network_id, IpAddress **network_ip, u_int8_t *network_prefix) {
|
|
char *network_address, *slash;
|
|
*network_ip = new (std::nothrow) IpAddress();
|
|
*network_prefix = 0;
|
|
|
|
if (local_network_id >= 0)
|
|
network_address = strdup(getLocalNetworkName(local_network_id));
|
|
else
|
|
network_address = strdup((char*)"0.0.0.0/0"); /* Remote networks */
|
|
|
|
if((slash = strchr(network_address, '/'))) {
|
|
*network_prefix = atoi(slash + 1);
|
|
*slash = '\0';
|
|
}
|
|
|
|
if(*network_ip)
|
|
(*network_ip)->set(network_address);
|
|
if(network_address)
|
|
free(network_address);
|
|
};
|
|
|
|
/* ******************************************* */
|
|
|
|
#ifdef WIN32
|
|
|
|
#include <ws2tcpip.h>
|
|
#include <iphlpapi.h>
|
|
|
|
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
|
|
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
|
|
|
|
/* Note: could also use malloc() and free() */
|
|
|
|
char* Ntop::getIfName(int if_id, char *name, u_int name_len) {
|
|
// Declare and initialize variables
|
|
PIP_INTERFACE_INFO pInfo = NULL;
|
|
ULONG ulOutBufLen = 0;
|
|
DWORD dwRetVal = 0;
|
|
int iReturn = 1;
|
|
int i;
|
|
|
|
name[0] = '\0';
|
|
|
|
// Make an initial call to GetInterfaceInfo to get
|
|
// the necessary size in the ulOutBufLen variable
|
|
dwRetVal = GetInterfaceInfo(NULL, &ulOutBufLen);
|
|
if(dwRetVal == ERROR_INSUFFICIENT_BUFFER) {
|
|
pInfo = (IP_INTERFACE_INFO *)MALLOC(ulOutBufLen);
|
|
if(pInfo == NULL) {
|
|
return(name);
|
|
}
|
|
}
|
|
|
|
// Make a second call to GetInterfaceInfo to get
|
|
// the actual data we need
|
|
dwRetVal = GetInterfaceInfo(pInfo, &ulOutBufLen);
|
|
if(dwRetVal == NO_ERROR) {
|
|
for(i = 0; i < pInfo->NumAdapters; i++) {
|
|
if(pInfo->Adapter[i].Index == if_id) {
|
|
int j, k, begin = 0;
|
|
|
|
for(j = 0, k = 0; (k < name_len) && (pInfo->Adapter[i].Name[j] != '\0'); j++) {
|
|
if(begin) {
|
|
if((char)pInfo->Adapter[i].Name[j] == '}') break;
|
|
name[k++] = (char)pInfo->Adapter[i].Name[j];
|
|
} else if((char)pInfo->Adapter[i].Name[j] == '{')
|
|
begin = 1;
|
|
}
|
|
|
|
name[k] = '\0';
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
FREE(pInfo);
|
|
return(name);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::loadLocalInterfaceAddress() {
|
|
const int bufsize = 128;
|
|
char buf[bufsize];
|
|
|
|
#ifdef WIN32
|
|
PMIB_IPADDRTABLE pIPAddrTable;
|
|
DWORD dwSize = 0;
|
|
DWORD dwRetVal = 0;
|
|
IN_ADDR IPAddr;
|
|
char buf2[bufsize];
|
|
|
|
/* Variables used to return error message */
|
|
LPVOID lpMsgBuf;
|
|
|
|
// Before calling AddIPAddress we use GetIpAddrTable to get
|
|
// an adapter to which we can add the IP.
|
|
pIPAddrTable = (MIB_IPADDRTABLE *)MALLOC(sizeof(MIB_IPADDRTABLE));
|
|
|
|
if(pIPAddrTable) {
|
|
// Make an initial call to GetIpAddrTable to get the
|
|
// necessary size into the dwSize variable
|
|
if(GetIpAddrTable(pIPAddrTable, &dwSize, 0) ==
|
|
ERROR_INSUFFICIENT_BUFFER) {
|
|
FREE(pIPAddrTable);
|
|
pIPAddrTable = (MIB_IPADDRTABLE *)MALLOC(dwSize);
|
|
|
|
}
|
|
if(pIPAddrTable == NULL) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Make a second call to GetIpAddrTable to get the
|
|
// actual data we want
|
|
if((dwRetVal = GetIpAddrTable(pIPAddrTable, &dwSize, 0)) != NO_ERROR) {
|
|
if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
|
(LPTSTR)& lpMsgBuf, 0, NULL)) {
|
|
LocalFree(lpMsgBuf);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
for(int ifIdx = 0; ifIdx < (int)pIPAddrTable->dwNumEntries; ifIdx++) {
|
|
char name[256];
|
|
|
|
getIfName(pIPAddrTable->table[ifIdx].dwIndex, name, sizeof(name));
|
|
|
|
for(int id = 0; id < num_defined_interfaces; id++) {
|
|
if((name[0] != '\0') && (strstr(iface[id]->get_name(), name) != NULL)) {
|
|
u_int32_t bits = Utils::numberOfSetBits((u_int32_t)pIPAddrTable->table[ifIdx].dwMask);
|
|
|
|
IPAddr.S_un.S_addr = (u_long)pIPAddrTable->table[ifIdx].dwAddr;
|
|
snprintf(buf, bufsize, "%s/32", inet_ntoa(IPAddr));
|
|
local_interface_addresses.addAddress(buf);
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Adding %s as IPv4 NIC addr. [%s]",
|
|
buf, iface[id]->get_description());
|
|
iface[id]->addInterfaceAddress(buf);
|
|
|
|
IPAddr.S_un.S_addr = (u_long)(pIPAddrTable->table[ifIdx].dwAddr & pIPAddrTable->table[ifIdx].dwMask);
|
|
snprintf(buf2, bufsize, "%s/%u", inet_ntoa(IPAddr), bits);
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Adding %s as IPv4 local nw [%s]",
|
|
buf2, iface[id]->get_description());
|
|
addLocalNetworkList(buf2);
|
|
iface[id]->addInterfaceNetwork(buf2, buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* TODO: add IPv6 support */
|
|
if(pIPAddrTable) {
|
|
FREE(pIPAddrTable);
|
|
pIPAddrTable = NULL;
|
|
}
|
|
#else
|
|
struct ifaddrs *local_addresses, *ifa;
|
|
/* buf must be big enough for an IPv6 address(e.g. 3ffe:2fa0:1010:ca22:020a:95ff:fe8a:1cf8) */
|
|
char buf_orig[bufsize+32];
|
|
char net_buf[bufsize+32];
|
|
int sock = Utils::openSocket(AF_INET, SOCK_STREAM, 0, "loadLocalInterfaceAddress");
|
|
|
|
if(getifaddrs(&local_addresses) != 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to read interface addresses");
|
|
Utils::closeSocket(sock);
|
|
return;
|
|
}
|
|
|
|
for(ifa = local_addresses; ifa != NULL; ifa = ifa->ifa_next) {
|
|
struct ifreq ifr;
|
|
u_int32_t netmask;
|
|
int cidr, ifId = -1;
|
|
|
|
if((ifa->ifa_addr == NULL)
|
|
|| ((ifa->ifa_addr->sa_family != AF_INET)
|
|
&& (ifa->ifa_addr->sa_family != AF_INET6))
|
|
|| ((ifa->ifa_flags & IFF_UP) == 0))
|
|
continue;
|
|
|
|
for(int i = 0; i < num_defined_interfaces; i++) {
|
|
if(strstr(iface[i]->get_name(), ifa->ifa_name)) {
|
|
ifId = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(ifId == -1)
|
|
continue;
|
|
|
|
if(ifa->ifa_addr->sa_family == AF_INET) {
|
|
struct sockaddr_in* s4 =(struct sockaddr_in *)(ifa->ifa_addr);
|
|
u_int32_t nm;
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
ifr.ifr_addr.sa_family = AF_INET;
|
|
strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name)-1);
|
|
ioctl(sock, SIOCGIFNETMASK, &ifr);
|
|
netmask = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
|
|
|
|
cidr = 0, nm = netmask;
|
|
|
|
while(nm) {
|
|
cidr += (nm & 0x01);
|
|
nm >>= 1;
|
|
}
|
|
|
|
if(inet_ntop(ifa->ifa_addr->sa_family, (void *)&(s4->sin_addr), buf, sizeof(buf)) != NULL) {
|
|
char buf_orig2[bufsize+32];
|
|
|
|
snprintf(buf_orig2, sizeof(buf_orig2), "%s/%d", buf, 32);
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Adding %s as IPv4 interface address for %s",
|
|
buf_orig2, iface[ifId]->get_name());
|
|
local_interface_addresses.addAddress(buf_orig2);
|
|
iface[ifId]->addInterfaceAddress(buf_orig2);
|
|
|
|
/* Set to zero non network bits */
|
|
s4->sin_addr.s_addr = htonl(ntohl(s4->sin_addr.s_addr) & ntohl(netmask));
|
|
inet_ntop(ifa->ifa_addr->sa_family, (void *)&(s4->sin_addr), buf, sizeof(buf));
|
|
snprintf(net_buf, sizeof(net_buf), "%s/%d", buf, cidr);
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Adding %s as IPv4 local network for %s",
|
|
net_buf, iface[ifId]->get_name());
|
|
iface[ifId]->addInterfaceNetwork(net_buf, buf_orig2);
|
|
addLocalNetworkList(net_buf);
|
|
}
|
|
} else if(ifa->ifa_addr->sa_family == AF_INET6) {
|
|
struct sockaddr_in6 *s6 =(struct sockaddr_in6 *)(ifa->ifa_netmask);
|
|
u_int8_t *b = (u_int8_t *)&(s6->sin6_addr);
|
|
|
|
cidr = 0;
|
|
|
|
for(int i=0; i<16; i++) {
|
|
u_int8_t num_bits = __builtin_popcount(b[i]);
|
|
|
|
if(num_bits == 0) break;
|
|
cidr += num_bits;
|
|
}
|
|
|
|
s6 = (struct sockaddr_in6 *)(ifa->ifa_addr);
|
|
if(inet_ntop(ifa->ifa_addr->sa_family,(void *)&(s6->sin6_addr), buf, sizeof(buf)) != NULL) {
|
|
snprintf(buf_orig, sizeof(buf_orig), "%s/%d", buf, 128);
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Adding %s as IPv6 interface address for %s",
|
|
buf_orig, iface[ifId]->get_name());
|
|
local_interface_addresses.addAddresses(buf_orig);
|
|
iface[ifId]->addInterfaceAddress(buf_orig);
|
|
|
|
for(int i = cidr, j = 0; i > 0; i -= 8, ++j)
|
|
s6->sin6_addr.s6_addr[j] &= i >= 8 ? 0xff : (u_int32_t)(( 0xffU << ( 8 - i ) ) & 0xffU );
|
|
|
|
inet_ntop(ifa->ifa_addr->sa_family,(void *)&(s6->sin6_addr), buf, sizeof(buf));
|
|
snprintf(net_buf, sizeof(net_buf), "%s/%d", buf, cidr);
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Adding %s as IPv6 local network for %s",
|
|
net_buf, iface[ifId]->get_name());
|
|
|
|
iface[ifId]->addInterfaceNetwork(net_buf, buf_orig);
|
|
addLocalNetworkList(net_buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
freeifaddrs(local_addresses);
|
|
|
|
Utils::closeSocket(sock);
|
|
#endif
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Local Interface Addresses (System Host)");
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Local Networks");
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::loadGeolocation() {
|
|
if(geo != NULL) delete geo;
|
|
geo = new (std::nothrow) Geolocation();
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::loadMacManufacturers(char *dir) {
|
|
if(mac_manufacturers != NULL) delete mac_manufacturers;
|
|
if((mac_manufacturers = new (std::nothrow) MacManufacturers(dir)) == NULL)
|
|
throw "Not enough memory";
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::setWorkingDir(char *dir) {
|
|
snprintf(working_dir, sizeof(working_dir), "%s", dir);
|
|
removeTrailingSlash(working_dir);
|
|
setScriptsDir();
|
|
};
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::removeTrailingSlash(char *str) {
|
|
int len = (int)strlen(str)-1;
|
|
|
|
if((len > 0)
|
|
&& ((str[len] == '/') || (str[len] == '\\')))
|
|
str[len] = '\0';
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::setCustomnDPIProtos(char *path) {
|
|
if(path != NULL) {
|
|
if(custom_ndpi_protos != NULL) free(custom_ndpi_protos);
|
|
custom_ndpi_protos = strdup(path);
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::lua_periodic_activities_stats(NetworkInterface *iface, lua_State* vm) {
|
|
if(pa)
|
|
pa->lua(iface, vm);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::lua_alert_queues_stats(lua_State* vm) {
|
|
lua_newtable(vm);
|
|
|
|
if(getInternalAlertsQueue()) getInternalAlertsQueue()->lua(vm, "internal_alerts_queue");
|
|
|
|
lua_pushstring(vm, "alert_queues");
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::recipients_are_empty() {
|
|
return recipients.empty();
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::recipients_enqueue(AlertFifoItem *notification, AlertEntity alert_entity) {
|
|
return recipients.enqueue(notification, alert_entity);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::recipient_enqueue(u_int16_t recipient_id, const AlertFifoItem* const notification) {
|
|
return recipients.enqueue(recipient_id, notification);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::recipient_dequeue(u_int16_t recipient_id, AlertFifoItem *notification) {
|
|
return recipients.dequeue(recipient_id, notification);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::recipient_stats(u_int16_t recipient_id, lua_State* vm) {
|
|
recipients.lua(recipient_id, vm);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
time_t Ntop::recipient_last_use(u_int16_t recipient_id) {
|
|
return recipients.last_use(recipient_id);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::recipient_delete(u_int16_t recipient_id) {
|
|
recipients.delete_recipient(recipient_id);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::recipient_register(u_int16_t recipient_id, AlertLevel minimum_severity,
|
|
Bitmap128 enabled_categories, Bitmap128 enabled_host_pools, Bitmap128 enabled_entities) {
|
|
recipients.register_recipient(recipient_id, minimum_severity, enabled_categories, enabled_host_pools, enabled_entities);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::getUsers(lua_State* vm) {
|
|
char **usernames;
|
|
char *username;
|
|
char *key, *val;
|
|
int rc, i;
|
|
size_t len;
|
|
|
|
lua_newtable(vm);
|
|
|
|
if((rc = ntop->getRedis()->keys("ntopng.user.*.password", &usernames)) <= 0)
|
|
return;
|
|
|
|
if((key = (char*)malloc(CONST_MAX_LEN_REDIS_VALUE)) == NULL)
|
|
return;
|
|
else if((val = (char*)malloc(CONST_MAX_LEN_REDIS_VALUE)) == NULL) {
|
|
free(key);
|
|
return;
|
|
}
|
|
|
|
for(i = 0; i < rc; i++) {
|
|
if(usernames[i] == NULL) goto next_username; /* safety check */
|
|
if((username = strchr(usernames[i], '.')) == NULL) goto next_username;
|
|
if((username = strchr(username+1, '.')) == NULL) goto next_username;
|
|
len = strlen(++username);
|
|
|
|
if(len < sizeof(".password")) goto next_username;
|
|
username[len - sizeof(".password") + 1] = '\0';
|
|
|
|
lua_newtable(vm);
|
|
|
|
snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_FULL_NAME, username);
|
|
if(ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
|
|
lua_push_str_table_entry(vm, "full_name", val);
|
|
else
|
|
lua_push_str_table_entry(vm, "full_name", (char*) "unknown");
|
|
|
|
snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_PASSWORD, username);
|
|
if(ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
|
|
lua_push_str_table_entry(vm, "password", val);
|
|
else
|
|
lua_push_str_table_entry(vm, "password", (char*) "unknown");
|
|
|
|
snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_GROUP, username);
|
|
if(ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
|
|
lua_push_str_table_entry(vm, "group", val);
|
|
else
|
|
lua_push_str_table_entry(vm, "group", (char*)NTOP_UNKNOWN_GROUP);
|
|
|
|
snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_LANGUAGE, username);
|
|
if(ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
|
|
lua_push_str_table_entry(vm, "language", val);
|
|
else
|
|
lua_push_str_table_entry(vm, "language", (char*)"");
|
|
|
|
snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_ALLOW_PCAP, username);
|
|
if(ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
|
|
lua_push_bool_table_entry(vm, "allow_pcap_download", true);
|
|
|
|
snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_ALLOW_HISTORICAL_FLOW, username);
|
|
if(ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
|
|
lua_push_bool_table_entry(vm, "allow_historical_flow", true);
|
|
|
|
snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_NETS, username);
|
|
if(ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
|
|
lua_push_str_table_entry(vm, CONST_ALLOWED_NETS, val);
|
|
else
|
|
lua_push_str_table_entry(vm, CONST_ALLOWED_NETS, (char*)"");
|
|
|
|
snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_ALLOWED_IFNAME, username);
|
|
if((ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
|
|
&& val[0] != '\0')
|
|
lua_push_str_table_entry(vm, CONST_ALLOWED_IFNAME, val);
|
|
else
|
|
lua_push_str_table_entry(vm, CONST_ALLOWED_IFNAME, (char*)"");
|
|
|
|
snprintf(key, CONST_MAX_LEN_REDIS_VALUE, CONST_STR_USER_HOST_POOL_ID, username);
|
|
if(ntop->getRedis()->get(key, val, CONST_MAX_LEN_REDIS_VALUE) >= 0)
|
|
lua_push_uint64_table_entry(vm, "host_pool_id", atoi(val));
|
|
|
|
lua_pushstring(vm, username);
|
|
lua_insert(vm, -2);
|
|
lua_settable(vm, -3);
|
|
|
|
next_username:
|
|
|
|
if(usernames[i])
|
|
free(usernames[i]);
|
|
}
|
|
|
|
free(usernames);
|
|
free(key), free(val);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
/**
|
|
* @brief Check if the current user is an administrator
|
|
*
|
|
* @param vm The lua state.
|
|
* @return true if the current user is an administrator, false otherwise.
|
|
*/
|
|
bool Ntop::isUserAdministrator(lua_State* vm) {
|
|
struct mg_connection *conn;
|
|
char *username, *group;
|
|
|
|
if(!ntop->getPrefs()->is_users_login_enabled())
|
|
return(true); /* login disabled for all users, everyone's an admin */
|
|
|
|
if((conn = getLuaVMUservalue(vm,conn)) == NULL) {
|
|
/* this is an internal script (e.g. periodic script), admin check should pass */
|
|
return(true);
|
|
} else if(HTTPserver::authorized_localhost_user_login(conn))
|
|
return(true); /* login disabled from localhost, everyone's connecting from localhost is an admin */
|
|
|
|
if((username = getLuaVMUserdata(vm, user)) == NULL) {
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(%s): NO", __FUNCTION__, "???");
|
|
return(false); /* Unknown */
|
|
}
|
|
|
|
if(!strncmp(username, NTOP_NOLOGIN_USER, strlen(username)))
|
|
return(true);
|
|
|
|
if((group = getLuaVMUserdata(vm,group)) != NULL) {
|
|
return(!strcmp(group, NTOP_NOLOGIN_USER) ||
|
|
!strcmp(group, CONST_ADMINISTRATOR_USER));
|
|
} else {
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(%s): NO", __FUNCTION__, username);
|
|
return(false); /* Unknown */
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::getAllowedInterface(lua_State* vm) {
|
|
char *allowed_ifname;
|
|
|
|
allowed_ifname = getLuaVMUserdata(vm, allowed_ifname);
|
|
|
|
lua_pushstring(vm, allowed_ifname != NULL ? allowed_ifname : (char*)"");
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::getAllowedNetworks(lua_State* vm) {
|
|
char key[64], val[64];
|
|
const char *username = getLuaVMUservalue(vm, user);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_NETS, username ? username : "");
|
|
lua_pushstring(vm, (ntop->getRedis()->get(key, val, sizeof(val)) >= 0) ? val : CONST_DEFAULT_ALL_NETS);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
// NOTE: ifname must be of size MAX_INTERFACE_NAME_LEN
|
|
bool Ntop::getInterfaceAllowed(lua_State* vm, char *ifname) const {
|
|
char *allowed_ifname;
|
|
|
|
allowed_ifname = getLuaVMUserdata(vm, allowed_ifname);
|
|
|
|
if(ifname == NULL)
|
|
return false;
|
|
|
|
if((allowed_ifname == NULL) || (allowed_ifname[0] == '\0')) {
|
|
ifname = NULL;
|
|
return false;
|
|
}
|
|
|
|
strncpy(ifname, allowed_ifname, MAX_INTERFACE_NAME_LEN-1);
|
|
ifname[MAX_INTERFACE_NAME_LEN - 1] = '\0';
|
|
return true;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isInterfaceAllowed(lua_State* vm, const char *ifname) const {
|
|
char *allowed_ifname;
|
|
bool ret;
|
|
|
|
if(vm == NULL || ifname == NULL)
|
|
return true; /* Always return true when no lua state is passed */
|
|
|
|
allowed_ifname = getLuaVMUserdata(vm, allowed_ifname);
|
|
|
|
if((allowed_ifname == NULL) || (allowed_ifname[0] == '\0')) {
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"No allowed interface found for %s", ifname);
|
|
// this is a lua script called within ntopng (no HTTP UI and user interaction, e.g. startup.lua)
|
|
ret = true;
|
|
} else {
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"Allowed interface %s, requested %s",
|
|
allowed_ifname, ifname);
|
|
ret = !strncmp(allowed_ifname, ifname, strlen(allowed_ifname));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isLocalUser(lua_State* vm) {
|
|
struct mg_connection *conn;
|
|
|
|
if((conn = getLuaVMUservalue(vm,conn)) == NULL) {
|
|
/* this is an internal script (e.g. periodic script), admin check should pass */
|
|
return(true);
|
|
}
|
|
|
|
return getLuaVMUservalue(vm,localuser);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isInterfaceAllowed(lua_State* vm, int ifid) const {
|
|
return isInterfaceAllowed(vm, prefs->get_if_name(ifid));
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isPcapDownloadAllowed(lua_State* vm, const char *ifname) {
|
|
bool allow_pcap_download = false;
|
|
|
|
if(isUserAdministrator(vm))
|
|
return true;
|
|
|
|
if(isInterfaceAllowed(vm, ifname)) {
|
|
char *username = getLuaVMUserdata(vm,user);
|
|
bool allow_historical_flow;
|
|
|
|
ntop->getUserCapabilities(username, &allow_pcap_download, &allow_historical_flow);
|
|
}
|
|
|
|
return(allow_pcap_download);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
char *Ntop::preparePcapDownloadFilter(lua_State* vm, char *filter) {
|
|
char *username;
|
|
char *restricted_filter = NULL;
|
|
char key[64], val[MAX_USER_NETS_VAL_LEN], val_cpy[MAX_USER_NETS_VAL_LEN];
|
|
char *tmp, *net;
|
|
int filter_len, len = 0, off = 0, num_nets = 0;
|
|
|
|
if(isUserAdministrator(vm)) /* keep the original filter */
|
|
goto no_restriction;
|
|
|
|
username = getLuaVMUserdata(vm,user);
|
|
if (username == NULL || username[0] == '\0')
|
|
return(NULL);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_NETS, username);
|
|
|
|
if(ntop->getRedis()->get(key, val, sizeof(val)) == -1)
|
|
goto no_restriction; /* no subnet configured for this user */
|
|
|
|
if (strlen(val) == 0)
|
|
goto no_restriction; /* no subnet configured for this user */
|
|
|
|
/* compute final filter length */
|
|
|
|
if (filter != NULL) filter_len = strlen(filter);
|
|
else filter_len = 0;
|
|
|
|
if (filter_len > 0)
|
|
len = filter_len + strlen("() and ()");
|
|
|
|
tmp = NULL;
|
|
strcpy(val_cpy, val);
|
|
net = strtok_r(val_cpy, ",", &tmp);
|
|
while(net != NULL) {
|
|
len += strlen(" or net ") + strlen(net);
|
|
net = strtok_r(NULL, ",", &tmp);
|
|
}
|
|
|
|
/* build final/restricted filter */
|
|
|
|
restricted_filter = (char*)malloc(len+1);
|
|
if (restricted_filter == NULL)
|
|
return(NULL);
|
|
|
|
if (filter_len > 0)
|
|
off += snprintf(&restricted_filter[off], len-off, "(");
|
|
|
|
tmp = NULL;
|
|
net = strtok_r(val, ",", &tmp);
|
|
while(net != NULL) {
|
|
if (strcmp(net, "0.0.0.0/0") != 0
|
|
&& strcmp(net, "::/0") != 0) {
|
|
if (num_nets > 0) off += snprintf(&restricted_filter[off], len-off, " or ");
|
|
off += snprintf(&restricted_filter[off], len-off, "net %s", net);
|
|
num_nets++;
|
|
}
|
|
net = strtok_r(NULL, ",", &tmp);
|
|
}
|
|
|
|
if (filter_len > 0)
|
|
off += snprintf(&restricted_filter[off], len-off, ") and (%s)", filter);
|
|
|
|
return(restricted_filter);
|
|
|
|
no_restriction:
|
|
return(strdup(filter == NULL ? "" : filter));
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::checkUserInterfaces(const char * user) const {
|
|
char ifbuf[MAX_INTERFACE_NAME_LEN];
|
|
|
|
/* Check if the user has an allowed interface and that interface has not yet been
|
|
instantiated in ntopng (e.g, this can happen with dynamic interfaces after ntopng
|
|
has been restarted.) */
|
|
getUserAllowedIfname(user, ifbuf, sizeof(ifbuf));
|
|
if(ifbuf[0] != '\0' && !isExistingInterface(ifbuf))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::getUserPasswordHashLocal(const char * user, char *password_hash) const {
|
|
char key[64], val[64];
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, user);
|
|
|
|
if(ntop->getRedis()->get(key, val, sizeof(val)) < 0) {
|
|
return(false);
|
|
}
|
|
|
|
sprintf(password_hash, "%s", val);
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::getUserGroupLocal(const char * user, char *group) const {
|
|
char key[64], val[64];
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_GROUP, user);
|
|
|
|
strncpy(group, ((ntop->getRedis()->get(key, val, sizeof(val)) >= 0) ? val : NTOP_UNKNOWN_GROUP), NTOP_GROUP_MAXLEN);
|
|
group[NTOP_GROUP_MAXLEN - 1] = '\0';
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isLocalAuthEnabled() const {
|
|
char val[64];
|
|
|
|
if((ntop->getRedis()->get((char*)PREF_NTOP_LOCAL_AUTH, val, sizeof(val)) >= 0) && val[0] == '0')
|
|
return(false);
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::checkUserPasswordLocal(const char * user, const char * password, char *group) const {
|
|
char val[64], password_hash[33];
|
|
|
|
if(!isLocalAuthEnabled())
|
|
return(false);
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Checking Local auth");
|
|
|
|
if((!strcmp(user, "admin")) &&
|
|
(ntop->getRedis()->get((char*)TEMP_ADMIN_PASSWORD, val, sizeof(val)) >= 0) &&
|
|
(val[0] != '\0') &&
|
|
(!strcmp(val, password)))
|
|
goto valid_local_user;
|
|
|
|
if (!getUserPasswordHashLocal(user, val)) {
|
|
return(false);
|
|
} else {
|
|
mg_md5(password_hash, password, NULL);
|
|
|
|
if(strcmp(password_hash, val) != 0) {
|
|
return(false);
|
|
}
|
|
}
|
|
|
|
valid_local_user:
|
|
getUserGroupLocal(user, group);
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
// Return 1 if username/password is allowed, 0 otherwise.
|
|
bool Ntop::checkUserPassword(const char * user, const char * password, char *group, bool *localuser) const {
|
|
char val[64];
|
|
*localuser = false;
|
|
|
|
if(!user || user[0] == '\0' || !password || password[0] == '\0')
|
|
return(false);
|
|
|
|
#if defined(NTOPNG_PRO) && defined(HAVE_LDAP)
|
|
if(ntop->getPro()->has_valid_license()) {
|
|
if(ntop->getRedis()->get((char*)PREF_NTOP_LDAP_AUTH, val, sizeof(val)) >= 0 && val[0] == '1') {
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Checking LDAP auth");
|
|
|
|
bool ldap_ret = false;
|
|
bool is_admin;
|
|
char *ldapServer = NULL, *ldapAccountType = NULL, *ldapAnonymousBind = NULL,
|
|
*bind_dn = NULL, *bind_pwd = NULL, *user_group = NULL,
|
|
*search_path = NULL, *admin_group = NULL;
|
|
|
|
if( !(ldapServer = (char*)calloc(sizeof(char), MAX_LDAP_LEN))
|
|
|| !(ldapAccountType = (char*)calloc(sizeof(char), MAX_LDAP_LEN)) /* either 'posix' or 'samaccount' */
|
|
|| !(ldapAnonymousBind = (char*)calloc(sizeof(char), MAX_LDAP_LEN)) /* either '1' or '0' */
|
|
|| !(bind_dn = (char*)calloc(sizeof(char), MAX_LDAP_LEN))
|
|
|| !(bind_pwd = (char*)calloc(sizeof(char), MAX_LDAP_LEN))
|
|
|| !(user_group = (char*)calloc(sizeof(char), MAX_LDAP_LEN))
|
|
|| !(search_path = (char*)calloc(sizeof(char), MAX_LDAP_LEN))
|
|
|| !(admin_group = (char*)calloc(sizeof(char), MAX_LDAP_LEN))) {
|
|
static bool ldap_nomem = false;
|
|
|
|
if(!ldap_nomem) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to allocate memory for the LDAP authentication");
|
|
ldap_nomem = true;
|
|
}
|
|
|
|
goto ldap_auth_out;
|
|
}
|
|
|
|
ntop->getRedis()->get((char*)PREF_LDAP_SERVER, ldapServer, MAX_LDAP_LEN);
|
|
ntop->getRedis()->get((char*)PREF_LDAP_ACCOUNT_TYPE, ldapAccountType, MAX_LDAP_LEN);
|
|
ntop->getRedis()->get((char*)PREF_LDAP_BIND_ANONYMOUS, ldapAnonymousBind, MAX_LDAP_LEN);
|
|
ntop->getRedis()->get((char*)PREF_LDAP_BIND_DN, bind_dn, MAX_LDAP_LEN);
|
|
ntop->getRedis()->get((char*)PREF_LDAP_BIND_PWD, bind_pwd, MAX_LDAP_LEN);
|
|
ntop->getRedis()->get((char*)PREF_LDAP_SEARCH_PATH, search_path, MAX_LDAP_LEN);
|
|
ntop->getRedis()->get((char*)PREF_LDAP_USER_GROUP, user_group, MAX_LDAP_LEN);
|
|
ntop->getRedis()->get((char*)PREF_LDAP_ADMIN_GROUP, admin_group, MAX_LDAP_LEN);
|
|
|
|
if(ldapServer[0]) {
|
|
ldap_ret = LdapAuthenticator::validUserLogin(ldapServer, ldapAccountType,
|
|
(atoi(ldapAnonymousBind) == 0) ? false : true,
|
|
bind_dn[0] != '\0' ? bind_dn : NULL,
|
|
bind_pwd[0] != '\0' ? bind_pwd : NULL,
|
|
search_path[0] != '\0' ? search_path : NULL,
|
|
user,
|
|
password[0] != '\0' ? password : NULL,
|
|
user_group[0] != '\0' ? user_group : NULL,
|
|
admin_group[0] != '\0' ? admin_group : NULL,
|
|
&is_admin);
|
|
|
|
if(ldap_ret) {
|
|
strncpy(group, is_admin ? CONST_USER_GROUP_ADMIN : CONST_USER_GROUP_UNPRIVILEGED, NTOP_GROUP_MAXLEN);
|
|
group[NTOP_GROUP_MAXLEN - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
ldap_auth_out:
|
|
if(ldapServer) free(ldapServer);
|
|
if(ldapAnonymousBind) free(ldapAnonymousBind);
|
|
if(bind_dn) free(bind_dn);
|
|
if(bind_pwd) free(bind_pwd);
|
|
if(user_group) free(user_group);
|
|
if(search_path) free(search_path);
|
|
if(admin_group) free(admin_group);
|
|
|
|
if(ldap_ret)
|
|
return(true);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_RADIUS
|
|
/*
|
|
NOTE
|
|
|
|
Use https://idblender.com/tools/public-radius for testing
|
|
the implementation with a public server
|
|
*/
|
|
|
|
if(ntop->getRedis()->get((char*)PREF_NTOP_RADIUS_AUTH, val, sizeof(val)) >= 0 && val[0] == '1') {
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Checking RADIUS auth");
|
|
|
|
int result;
|
|
bool radius_ret = false;
|
|
char dict_path[MAX_RADIUS_LEN];
|
|
char *radiusServer = NULL, *radiusSecret = NULL, *authServer = NULL,
|
|
*radiusAdminGroup = NULL, *radiusUnprivCapabilitiesGroup = NULL;
|
|
rc_handle *rh = NULL;
|
|
VALUE_PAIR *send = NULL, *received = NULL;
|
|
bool has_unprivileged_capabilities = false;
|
|
|
|
if(!password || !password[0])
|
|
return false;
|
|
|
|
if(!(radiusServer = (char*)calloc(sizeof(char), MAX_RADIUS_LEN)) ||
|
|
!(radiusSecret = (char*)calloc(sizeof(char), MAX_SECRET_LENGTH + 1)) ||
|
|
!(radiusAdminGroup = (char*)calloc(sizeof(char), MAX_RADIUS_LEN)) ||
|
|
!(radiusUnprivCapabilitiesGroup = (char*)calloc(sizeof(char), MAX_RADIUS_LEN)) ||
|
|
!(authServer = (char*)calloc(sizeof(char), MAX_RADIUS_LEN))) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: unable to allocate memory");
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
ntop->getRedis()->get((char*)PREF_RADIUS_SERVER, radiusServer, MAX_RADIUS_LEN);
|
|
ntop->getRedis()->get((char*)PREF_RADIUS_SECRET, radiusSecret, MAX_SECRET_LENGTH + 1);
|
|
ntop->getRedis()->get((char*)PREF_RADIUS_ADMIN_GROUP, radiusAdminGroup, MAX_RADIUS_LEN);
|
|
ntop->getRedis()->get((char*)PREF_RADIUS_UNPRIV_CAP_GROUP, radiusUnprivCapabilitiesGroup, MAX_RADIUS_LEN);
|
|
|
|
if(!radiusServer[0] || !radiusSecret[0]) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: no radius server or secret set !");
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
snprintf(authServer, MAX_RADIUS_LEN - 1, "%s:%s", radiusServer, radiusSecret);
|
|
|
|
/* NOTE: this is an handle to the radius lib. It will be passed to multiple functions and cleaned up at the end.
|
|
* https://github.com/FreeRADIUS/freeradius-client/blob/master/src/radembedded.c
|
|
*/
|
|
rh = rc_new();
|
|
if(rh == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: unable to allocate memory");
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
/* ********* */
|
|
|
|
rh = rc_config_init(rh);
|
|
|
|
if(rh == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: failed to init configuration");
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
/* RADIUS only auth */
|
|
if(rc_add_config(rh, "auth_order", "radius", "config", 0) != 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: Unable to set auth_order");
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
if(rc_add_config(rh, "radius_retries", "3", "config", 0) != 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: Unable to set retries config");
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
if(rc_add_config(rh, "radius_timeout", "5", "config", 0) != 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: Unable to set timeout config");
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
snprintf(dict_path, sizeof(dict_path), "%s/other/radcli_dictionary.txt", ntop->getPrefs()->get_docs_dir());
|
|
if(rc_add_config(rh, "dictionary", dict_path, "config", 0) != 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: Unable to set dictionary config");
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
if(rc_add_config(rh, "authserver", authServer, "config", 0) != 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: Unable to set authserver config: \"%s\"", authServer);
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
#ifdef HAVE_RC_TEST_CONFIG
|
|
/* Necessary since radcli release 1.2.10 */
|
|
if(rc_test_config(rh, "ntopng") != 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: rc_test_config failed");
|
|
goto radius_auth_out;
|
|
}
|
|
#endif
|
|
|
|
/* ********* */
|
|
|
|
if(rc_read_dictionary(rh, rc_conf_str(rh, "dictionary")) != 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: unable to read dictionary");
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
if(rc_avpair_add(rh, &send, PW_USER_NAME, user, -1, 0) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: unable to set username");
|
|
goto radius_auth_out;
|
|
}
|
|
if(rc_avpair_add(rh, &send, PW_USER_PASSWORD, password, -1, 0) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Radius: unable to set password");
|
|
goto radius_auth_out;
|
|
}
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Radius: performing auth for user %s", user);
|
|
|
|
result = rc_auth(rh, 0, send, &received, NULL);
|
|
if(result == OK_RC) {
|
|
bool is_admin = false;
|
|
|
|
if((radiusAdminGroup[0] != '\0') || (radiusUnprivCapabilitiesGroup[0] != '\0')) {
|
|
VALUE_PAIR *vp = received;
|
|
char name[sizeof(vp->name)];
|
|
char value[sizeof(vp->strvalue)];
|
|
|
|
while(vp != NULL) {
|
|
if(rc_avpair_tostr(rh, vp, name, sizeof(name), value, sizeof(value)) == 0) {
|
|
/* The "Filter-Id" attribute is used to set user privileges */
|
|
if(strcmp(name, "Filter-Id") == 0) {
|
|
if(strcmp(value, radiusAdminGroup) == 0)
|
|
is_admin = true;
|
|
else if(strcmp(value, radiusUnprivCapabilitiesGroup) == 0)
|
|
has_unprivileged_capabilities = true;
|
|
|
|
break; /* We care only about "Filter-Id" */
|
|
}
|
|
}
|
|
|
|
vp = vp->next;
|
|
}
|
|
}
|
|
|
|
if(has_unprivileged_capabilities) {
|
|
changeUserPermission(user, true, 86400 /* 1 day */);
|
|
changeUserHistoricalFlowPermission(user, true, 86400 /* 1 day */);
|
|
} else {
|
|
char key[64];
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_PCAP, user);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_HISTORICAL_FLOW, user);
|
|
ntop->getRedis()->del(key);
|
|
}
|
|
|
|
strncpy(group, is_admin ? CONST_USER_GROUP_ADMIN : CONST_USER_GROUP_UNPRIVILEGED, NTOP_GROUP_MAXLEN);
|
|
group[NTOP_GROUP_MAXLEN - 1] = '\0';
|
|
radius_ret = true;
|
|
} else {
|
|
/* Do not display messages for user 'admin' */
|
|
|
|
if(strcmp(user, "admin")) {
|
|
switch(result) {
|
|
case TIMEOUT_RC:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Radius Authentication timeout for user \"%s\"", user);
|
|
break;
|
|
case REJECT_RC:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Radius Authentication rejected for user \"%s\"", user);
|
|
break;
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Radius Authentication failure[%d]: user \"%s\"", result, user);
|
|
}
|
|
}
|
|
}
|
|
|
|
radius_auth_out:
|
|
if(send) rc_avpair_free(send);
|
|
if(received) rc_avpair_free(received);
|
|
if(rh) rc_destroy(rh);
|
|
if(radiusAdminGroup) free(radiusAdminGroup);
|
|
if(radiusUnprivCapabilitiesGroup) free(radiusUnprivCapabilitiesGroup);
|
|
if(radiusServer) free(radiusServer);
|
|
if(radiusSecret) free(radiusSecret);
|
|
if(authServer) free(authServer);
|
|
|
|
if(radius_ret)
|
|
return(true);
|
|
}
|
|
#endif
|
|
|
|
if(ntop->getRedis()->get((char*)PREF_NTOP_HTTP_AUTH, val, sizeof(val)) >= 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Checking HTTP auth");
|
|
|
|
if(val[0] == '1') {
|
|
int postLen;
|
|
char *httpUrl = NULL, *postData = NULL, *returnData = NULL;
|
|
bool http_ret = false;
|
|
int rc = 0;
|
|
HTTPTranferStats stats;
|
|
HTTPAuthenticator auth;
|
|
|
|
memset(&auth, 0, sizeof(auth));
|
|
if(!password || !password[0])
|
|
return false;
|
|
|
|
postLen = 100 + strlen(user) + strlen(password);
|
|
if(!(httpUrl = (char*)calloc(sizeof(char), MAX_HTTP_AUTHENTICATOR_LEN)) ||
|
|
!(postData = (char*)calloc(sizeof(char), postLen + 1)) ||
|
|
!(returnData = (char*)calloc(sizeof(char), MAX_HTTP_AUTHENTICATOR_RETURN_DATA_LEN + 1))) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "HTTP: unable to allocate memory");
|
|
goto http_auth_out;
|
|
}
|
|
ntop->getRedis()->get((char*)PREF_HTTP_AUTHENTICATOR_URL, httpUrl, MAX_HTTP_AUTHENTICATOR_LEN);
|
|
if(!httpUrl[0]) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "HTTP: no http url set !");
|
|
goto http_auth_out;
|
|
}
|
|
snprintf(postData, postLen, "{\"user\": \"%s\", \"password\": \"%s\"}",
|
|
user, password);
|
|
|
|
if(Utils::postHTTPJsonData(NULL, // no digest user
|
|
NULL, // no digest password
|
|
httpUrl,
|
|
postData, 0, &stats,
|
|
returnData, MAX_HTTP_AUTHENTICATOR_RETURN_DATA_LEN, &rc)) {
|
|
if(rc == 200) {
|
|
// parse JSON
|
|
if(!Utils::parseAuthenticatorJson(&auth, returnData)) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "HTTP: unable to parse json answer data !");
|
|
goto http_auth_out;
|
|
}
|
|
|
|
strncpy(group, auth.admin ? CONST_USER_GROUP_ADMIN : CONST_USER_GROUP_UNPRIVILEGED, NTOP_GROUP_MAXLEN);
|
|
group[NTOP_GROUP_MAXLEN - 1] = '\0';
|
|
if(auth.allowedNets != NULL) {
|
|
if(!Ntop::changeAllowedNets((char*)user, auth.allowedNets)) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "HTTP: unable to set allowed nets for user %s", user);
|
|
goto http_auth_out;
|
|
}
|
|
}
|
|
if(auth.allowedIfname != NULL) {
|
|
if(!Ntop::changeAllowedIfname((char*)user, auth.allowedIfname)) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "HTTP: unable to set allowed ifname for user %s", user);
|
|
goto http_auth_out;
|
|
}
|
|
}
|
|
if(auth.language != NULL) {
|
|
if(!Ntop::changeUserLanguage((char*)user, auth.language)) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "HTTP: unable to set language for user %s", user);
|
|
goto http_auth_out;
|
|
}
|
|
}
|
|
|
|
http_ret = true;
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "HTTP: authentication rejected [code=%d]", rc);
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "HTTP: could not contact the HTTP authenticator");
|
|
|
|
http_auth_out:
|
|
Utils::freeAuthenticator(&auth);
|
|
if(httpUrl) free(httpUrl);
|
|
if(postData) free(postData);
|
|
if(returnData) free(returnData);
|
|
if(http_ret)
|
|
return(true);
|
|
}
|
|
}
|
|
|
|
/* Check local auth */
|
|
if (checkUserPasswordLocal(user, password, group)) {
|
|
/* mark the user as local */
|
|
*localuser = true;
|
|
return(true);
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
static int getLoginAttempts(struct mg_connection *conn) {
|
|
char ipbuf[32], key[128], val[16];
|
|
int cur_attempts = 0;
|
|
IpAddress client_addr;
|
|
|
|
client_addr.set(mg_get_client_address(conn));
|
|
snprintf(key, sizeof(key), CONST_STR_FAILED_LOGIN_KEY, client_addr.print(ipbuf, sizeof(ipbuf)));
|
|
|
|
if((ntop->getRedis()->get(key, val, sizeof(val)) >= 0) && val[0])
|
|
cur_attempts = atoi(val);
|
|
|
|
return(cur_attempts);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isBlacklistedLogin(struct mg_connection *conn) const {
|
|
return(getLoginAttempts(conn) >= MAX_FAILED_LOGIN_ATTEMPTS);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::checkGuiUserPassword(struct mg_connection *conn,
|
|
const char * user, const char * password,
|
|
char *group, bool *localuser) const {
|
|
char *remote_ip, ipbuf[64], key[128], val[16];
|
|
int cur_attempts = 0;
|
|
bool rv;
|
|
IpAddress client_addr;
|
|
|
|
client_addr.set(mg_get_client_address(conn));
|
|
|
|
if(ntop->isCaptivePortalUser(user)) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "User %s is not a gui user. Login is denied.", user);
|
|
return false;
|
|
}
|
|
|
|
remote_ip = client_addr.print(ipbuf, sizeof(ipbuf));
|
|
|
|
if((cur_attempts = getLoginAttempts(conn)) >= MAX_FAILED_LOGIN_ATTEMPTS) {
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Login denied for '%s' from blacklisted IP %s", user, remote_ip);
|
|
return false;
|
|
}
|
|
|
|
rv = checkUserPassword(user, password, group, localuser);
|
|
snprintf(key, sizeof(key), CONST_STR_FAILED_LOGIN_KEY, remote_ip);
|
|
|
|
if(!rv) {
|
|
cur_attempts++;
|
|
snprintf(val, sizeof(val), "%d", cur_attempts);
|
|
ntop->getRedis()->set(key, val, FAILED_LOGIN_ATTEMPTS_INTERVAL);
|
|
|
|
if(cur_attempts >= MAX_FAILED_LOGIN_ATTEMPTS)
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "IP %s is now blacklisted from login for %d seconds",
|
|
remote_ip, FAILED_LOGIN_ATTEMPTS_INTERVAL);
|
|
|
|
HTTPserver::traceLogin(user, false);
|
|
} else
|
|
ntop->getRedis()->del(key);
|
|
|
|
return(rv);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::checkCaptiveUserPassword(const char * user, const char * password, char *group) const {
|
|
bool localuser = false;
|
|
bool rv;
|
|
|
|
if(!ntop->isCaptivePortalUser(user)) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "User %s is not a captive portal user. Login is denied.", user);
|
|
return false;
|
|
}
|
|
|
|
rv = checkUserPassword(user, password, group, &localuser);
|
|
|
|
return(rv);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::mustChangePassword(const char *user) {
|
|
char val[8];
|
|
|
|
if ((strcmp(user, "admin") == 0)
|
|
&& (ntop->getRedis()->get((char *)CONST_DEFAULT_PASSWORD_CHANGED, val, sizeof(val)) < 0
|
|
|| val[0] == '0'))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
/* NOTE: the admin vs local user checks must be performed by the caller */
|
|
bool Ntop::resetUserPassword(char *username, char *old_password, char *new_password) {
|
|
char key[64];
|
|
char password_hash[33];
|
|
char group[NTOP_GROUP_MAXLEN];
|
|
|
|
if((old_password != NULL) && (old_password[0] != '\0')) {
|
|
bool localuser = false;
|
|
|
|
if(!checkUserPassword(username, old_password, group, &localuser))
|
|
return(false);
|
|
|
|
if(!localuser)
|
|
return(false);
|
|
}
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, username);
|
|
mg_md5(password_hash, new_password, NULL);
|
|
|
|
if(ntop->getRedis()->set(key, password_hash, 0) < 0)
|
|
return(false);
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::changeUserFullName(const char * username, const char * full_name) const {
|
|
char key[64];
|
|
|
|
if (username == NULL || username[0] == '\0' || full_name == NULL ||
|
|
!existsUser(username))
|
|
return false;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"Changing full name to %s for %s",
|
|
full_name, username);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_FULL_NAME, username);
|
|
ntop->getRedis()->set(key, full_name, 0);
|
|
|
|
return (ntop->getRedis()->set(key, (char*) full_name, 0) >= 0);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::changeUserRole(char *username, char *usertype) const {
|
|
if(usertype != NULL) {
|
|
char key[64];
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
|
|
|
|
if(ntop->getRedis()->set(key, usertype, 0) < 0)
|
|
return(false);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::changeAllowedNets(char *username, char *allowed_nets) const {
|
|
if(allowed_nets != NULL) {
|
|
char key[64];
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_NETS, username);
|
|
|
|
if(ntop->getRedis()->set(key, allowed_nets, 0) < 0)
|
|
return(false);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::changeAllowedIfname(char *username, char *allowed_ifname) const {
|
|
/* Add as exception :// */
|
|
char *column_slash = strstr(allowed_ifname, ":__");
|
|
|
|
if (username == NULL || username[0] == '\0')
|
|
return false;
|
|
|
|
if(column_slash)
|
|
column_slash[1] = column_slash[2] = '/';
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"Changing allowed ifname to %s for %s",
|
|
allowed_ifname, username);
|
|
|
|
char key[64];
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_IFNAME, username);
|
|
|
|
if(allowed_ifname != NULL && allowed_ifname[0] != '\0') {
|
|
return (ntop->getRedis()->set(key, allowed_ifname, 0) >= 0);
|
|
} else {
|
|
ntop->getRedis()->del(key);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::changeUserHostPool(const char * username, const char * host_pool_id) const {
|
|
if (username == NULL || username[0] == '\0')
|
|
return false;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"Changing host pool id to %s for %s",
|
|
host_pool_id, username);
|
|
|
|
char key[64];
|
|
snprintf(key, sizeof(key), CONST_STR_USER_HOST_POOL_ID, username);
|
|
|
|
if(host_pool_id != NULL && host_pool_id[0] != '\0') {
|
|
return (ntop->getRedis()->set(key, (char*)host_pool_id, 0) >= 0);
|
|
} else {
|
|
ntop->getRedis()->del(key);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::changeUserLanguage(const char * username, const char * language) const {
|
|
if (username == NULL || username[0] == '\0')
|
|
return false;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"Changing user language %s for %s",
|
|
language, username);
|
|
|
|
char key[64];
|
|
snprintf(key, sizeof(key), CONST_STR_USER_LANGUAGE, username);
|
|
|
|
if(language != NULL && language[0] != '\0')
|
|
return (ntop->getRedis()->set(key, (char*)language, 0) >= 0);
|
|
else
|
|
ntop->getRedis()->del(key);
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::changeUserPermission(const char * username,
|
|
bool allow_pcap_download,
|
|
u_int32_t ttl) const {
|
|
char key[64];
|
|
|
|
if (username == NULL || username[0] == '\0')
|
|
return false;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"Changing user permission [allow-pcap-download: %s] for %s",
|
|
allow_pcap_download ? "true" : "false", username);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_PCAP, username);
|
|
|
|
if(allow_pcap_download)
|
|
return (ntop->getRedis()->set(key, "1", ttl) >= 0);
|
|
else
|
|
ntop->getRedis()->del(key);
|
|
|
|
return(true);
|
|
}
|
|
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::changeUserHistoricalFlowPermission(const char * username,
|
|
bool allow_historical_flow,
|
|
u_int32_t ttl) const {
|
|
char key[64];
|
|
|
|
if (username == NULL || username[0] == '\0')
|
|
return false;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"Changing user permission [allow-historical-flow: %s] for %s",
|
|
allow_historical_flow ? "true" : "false", username);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_HISTORICAL_FLOW, username);
|
|
|
|
if(allow_historical_flow)
|
|
return (ntop->getRedis()->set(key, "1", ttl) >= 0);
|
|
else
|
|
ntop->getRedis()->del(key);
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::getUserCapabilities(const char * username,
|
|
bool *allow_pcap_download,
|
|
bool *allow_historical_flow) const {
|
|
char key[64], val[2];
|
|
|
|
*allow_pcap_download = *allow_historical_flow = false;
|
|
|
|
if(username == NULL || username[0] == '\0')
|
|
return(false);
|
|
|
|
/* ************** */
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_PCAP, username);
|
|
|
|
if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0)
|
|
if(strcmp(val, "1") == 0) *allow_pcap_download = true;
|
|
|
|
/* ************** */
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_HISTORICAL_FLOW, username);
|
|
|
|
if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0)
|
|
if(strcmp(val, "1") == 0) *allow_historical_flow = true;
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
/*
|
|
Assigns a unique user id to be assigned to a new ntopng user. Callers must lock.
|
|
Assigned user id, if assignment is successful, is returned in the first param.
|
|
*/
|
|
bool Ntop::assignUserId(u_int8_t *new_user_id) {
|
|
char cur_id_buf[8];
|
|
int cur_id;
|
|
|
|
for(cur_id = 0; cur_id < NTOP_MAX_NUM_USERS; cur_id++) {
|
|
snprintf(cur_id_buf, sizeof(cur_id_buf), "%d", cur_id);
|
|
|
|
if(!ntop->getRedis()->sismember(PREF_NTOP_USER_IDS, cur_id_buf))
|
|
break;
|
|
}
|
|
|
|
if(cur_id == NTOP_MAX_NUM_USERS)
|
|
return false; /* No more ids available */
|
|
|
|
if(ntop->getRedis()->sadd(PREF_NTOP_USER_IDS, cur_id_buf) < 0)
|
|
return false; /* Unable to add the newly assigned user id to the set of all user ids */
|
|
|
|
*new_user_id = cur_id;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Assigned user id %u", *new_user_id);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::existsUser(const char * username) const {
|
|
char key[CONST_MAX_LEN_REDIS_KEY], val[2] /* Don't care about the content */;
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
|
|
if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0)
|
|
return(true); // user already exists
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::addUser(char *username, char *full_name, char *password, char *host_role,
|
|
char *allowed_networks, char *allowed_ifname, char *host_pool_id,
|
|
char *language, bool allow_pcap_download, bool allow_historical_flow) {
|
|
char key[CONST_MAX_LEN_REDIS_KEY];
|
|
char password_hash[33];
|
|
char new_user_id_buf[8];
|
|
u_int8_t new_user_id = 0;
|
|
|
|
users_m.lock(__FILE__, __LINE__);
|
|
|
|
if(existsUser(username) /* User already existing */
|
|
|| !assignUserId(&new_user_id) /* Unable to assign a user id */) {
|
|
users_m.unlock(__FILE__, __LINE__);
|
|
return(false);
|
|
}
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_FULL_NAME, username);
|
|
ntop->getRedis()->set(key, full_name, 0);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
|
|
ntop->getRedis()->set(key, (char*) host_role, 0);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ID, username);
|
|
snprintf(new_user_id_buf, sizeof(new_user_id_buf), "%d", new_user_id);
|
|
ntop->getRedis()->set(key, new_user_id_buf, 0);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, username);
|
|
mg_md5(password_hash, password, NULL);
|
|
ntop->getRedis()->set(key, password_hash, 0);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_NETS, username);
|
|
ntop->getRedis()->set(key, allowed_networks, 0);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_THEME, username);
|
|
ntop->getRedis()->set(key, "", 0);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_DATE_FORMAT, username);
|
|
ntop->getRedis()->set(key, "", 0);
|
|
|
|
if(language && language[0] != '\0') {
|
|
snprintf(key, sizeof(key), CONST_STR_USER_LANGUAGE, username);
|
|
ntop->getRedis()->set(key, language, 0);
|
|
}
|
|
|
|
if(allow_pcap_download) {
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_PCAP, username);
|
|
ntop->getRedis()->set(key, "1", 0);
|
|
}
|
|
|
|
if(allow_historical_flow) {
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_HISTORICAL_FLOW, username);
|
|
ntop->getRedis()->set(key, "1", 0);
|
|
}
|
|
|
|
if(allowed_ifname && allowed_ifname[0] != '\0'){
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Setting allowed ifname: %s", allowed_ifname);
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_IFNAME, username);
|
|
ntop->getRedis()->set(key, allowed_ifname, 0);
|
|
}
|
|
|
|
if(host_pool_id && host_pool_id[0] != '\0') {
|
|
snprintf(key, sizeof(key), CONST_STR_USER_HOST_POOL_ID, username);
|
|
ntop->getRedis()->set(key, host_pool_id, 0);
|
|
}
|
|
|
|
users_m.unlock(__FILE__, __LINE__);
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::addUserAPIToken(const char * username, const char *api_token) {
|
|
char key[CONST_MAX_LEN_REDIS_KEY];
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_API_TOKEN, username);
|
|
ntop->getRedis()->set(key, api_token);
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isCaptivePortalUser(const char * username) {
|
|
char key[64], val[64];
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
|
|
|
|
if((ntop->getRedis()->get(key, val, sizeof(val)) >= 0)
|
|
&& (!strcmp(val, CONST_USER_GROUP_CAPTIVE_PORTAL))) {
|
|
return(true);
|
|
}
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::deleteUser(char *username) {
|
|
char user_id_buf[8];
|
|
char key[64];
|
|
|
|
users_m.lock(__FILE__, __LINE__);
|
|
|
|
if(!existsUser(username)) {
|
|
users_m.unlock(__FILE__, __LINE__);
|
|
return(false);
|
|
}
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ID, username);
|
|
|
|
/* Dispose the currently assigned user id so that it can be recycled */
|
|
if((ntop->getRedis()->get(key, user_id_buf, sizeof(user_id_buf)) >= 0)) {
|
|
ntop->getRedis()->srem(PREF_NTOP_USER_IDS, user_id_buf);
|
|
}
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_FULL_NAME, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ID, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_NETS, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_IFNAME, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_LANGUAGE, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOW_PCAP, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_HOST_POOL_ID, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_THEME, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_DATE_FORMAT, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
/*
|
|
Delete the API Token, first from the hash of all tokens,
|
|
then from the user
|
|
*/
|
|
char api_token[NTOP_SESSION_ID_LENGTH];
|
|
if(getUserAPIToken(username, api_token, sizeof(api_token))) {
|
|
ntop->getRedis()->hashDel(NTOPNG_API_TOKEN_PREFIX, api_token);
|
|
}
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_API_TOKEN, username);
|
|
ntop->getRedis()->del(key);
|
|
|
|
users_m.unlock(__FILE__, __LINE__);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::getUserHostPool(char *username, u_int16_t *host_pool_id) {
|
|
char key[64], val[64];
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_HOST_POOL_ID, username ? username : "");
|
|
if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) {
|
|
if(host_pool_id)
|
|
*host_pool_id = atoi(val);
|
|
return true;
|
|
}
|
|
|
|
if(host_pool_id)
|
|
*host_pool_id = NO_HOST_POOL_ID;
|
|
return false;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::getUserAllowedIfname(const char * username, char *buf, size_t buflen) const {
|
|
char key[64];
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_IFNAME, username ? username : "");
|
|
|
|
if(ntop->getRedis()->get(key, buf, buflen) >= 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::getUserAPIToken(const char * username, char *buf, size_t buflen) const {
|
|
char key[CONST_MAX_LEN_REDIS_KEY];
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_API_TOKEN, username);
|
|
|
|
if(ntop->getRedis()->get(key, buf, buflen) >= 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::fixPath(char *str, bool replaceDots) {
|
|
for(int i=0; str[i] != '\0'; i++) {
|
|
#ifdef WIN32
|
|
/*
|
|
Allowed windows path and file characters:
|
|
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#win32_file_namespaces
|
|
*/
|
|
if (str[i] == '/')
|
|
str[i] = '\\';
|
|
else if (str[i] == '\\')
|
|
continue;
|
|
else if ((i == 1) && (str[i] == ':')) // c:\\...
|
|
continue;
|
|
else if (str[i] == ':' || str[i] == '"' || str[i] == '|' || str[i] == '?' || str[i] == '*')
|
|
str[i] = '_';
|
|
#endif
|
|
|
|
if(replaceDots) {
|
|
if((i > 0) && (str[i] == '.') && (str[i-1] == '.')) {
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "Invalid path detected %s", str);
|
|
str[i-1] = '_', str[i] = '_'; /* Invalidate the path */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
char* Ntop::getValidPath(char *__path) {
|
|
char _path[MAX_PATH+8];
|
|
struct stat buf;
|
|
|
|
#ifdef WIN32
|
|
const char *install_dir = (const char *)get_install_dir();
|
|
#endif
|
|
bool has_drive_colon = 0;
|
|
|
|
if(strncmp(__path, "./", 2) == 0) {
|
|
snprintf(_path, sizeof(_path), "%s/%s", startup_dir, &__path[2]);
|
|
fixPath(_path);
|
|
|
|
if(stat(_path, &buf) == 0) {
|
|
free(__path);
|
|
return(strdup(_path));
|
|
}
|
|
}
|
|
|
|
#ifdef WIN32
|
|
has_drive_colon = (isalpha((int)__path[0]) && (__path[1] == ':' && (__path[2] == '\\' || __path[2] == '/')));
|
|
#endif
|
|
|
|
if((__path[0] == '/') || (__path[0] == '\\') || has_drive_colon) {
|
|
/* Absolute paths */
|
|
|
|
if(stat(__path, &buf) == 0) {
|
|
return(__path);
|
|
}
|
|
} else
|
|
snprintf(_path, MAX_PATH, "%s", __path);
|
|
|
|
/* relative paths */
|
|
for(int i = 0; i < (int)COUNT_OF(dirs); i++) {
|
|
if(dirs[i]
|
|
/*
|
|
Ignore / as when you start ntopng as a
|
|
service you might have /scripts or /httpdocs
|
|
on your filesystem fooling ntopng
|
|
initialization and thus breaking averything
|
|
*/
|
|
&& strcmp(dirs[i], "/")
|
|
) {
|
|
char path[2*MAX_PATH];
|
|
|
|
snprintf(path, sizeof(path), "%s/%s", dirs[i], _path);
|
|
fixPath(path);
|
|
|
|
if(stat(path, &buf) == 0) {
|
|
free(__path);
|
|
return(strdup(path));
|
|
}
|
|
}
|
|
}
|
|
|
|
free(__path);
|
|
return(strdup(""));
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::daemonize() {
|
|
#ifndef WIN32
|
|
int childpid;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL,
|
|
"Parent process is exiting (this is normal)");
|
|
|
|
signal(SIGPIPE, SIG_IGN);
|
|
signal(SIGHUP, SIG_IGN);
|
|
/*
|
|
IMPORTANT
|
|
|
|
SIGCHLD should NOT be masked as otherwise
|
|
with popen()/pclose() we receive an error
|
|
when closing the pipe on FreeBSD
|
|
|
|
signal(SIGCHLD, SIG_IGN);
|
|
*/
|
|
signal(SIGQUIT, SIG_IGN);
|
|
|
|
if((childpid = fork()) < 0)
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Occurred while daemonizing (errno=%d)",
|
|
errno);
|
|
else {
|
|
if(!childpid) {
|
|
/* child */
|
|
int rc;
|
|
|
|
//ntop->getTrace()->traceEvent(TRACE_NORMAL, "Bye bye: I'm becoming a daemon...");
|
|
rc = chdir("/");
|
|
if(rc != 0)
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Error while moving to / directory");
|
|
|
|
setsid(); /* detach from the terminal */
|
|
|
|
fclose(stdin);
|
|
fclose(stdout);
|
|
/* fclose(stderr); */
|
|
|
|
/*
|
|
* clear any inherited file mode creation mask
|
|
*/
|
|
//umask(0);
|
|
|
|
/*
|
|
* Use line buffered stdout
|
|
*/
|
|
/* setlinebuf (stdout); */
|
|
setvbuf(stdout, (char *)NULL, _IOLBF, 0);
|
|
} else /* father */
|
|
exit(0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::setLocalNetworks(char *_nets) {
|
|
char *nets;
|
|
u_int len;
|
|
|
|
if(_nets == NULL) return;
|
|
|
|
len = (u_int)strlen(_nets);
|
|
|
|
if((len > 2)
|
|
&& (_nets[0] == '"')
|
|
&& (_nets[len-1] == '"')) {
|
|
nets = strdup(&_nets[1]);
|
|
nets[len-2] = '\0';
|
|
} else
|
|
nets = strdup(_nets);
|
|
|
|
addLocalNetworkList(nets);
|
|
free(nets);
|
|
};
|
|
|
|
/* ******************************************* */
|
|
|
|
NetworkInterface* Ntop::getInterfaceById(int if_id) {
|
|
if(if_id == SYSTEM_INTERFACE_ID)
|
|
return(system_interface);
|
|
|
|
for(int i=0; i<num_defined_interfaces; i++) {
|
|
if(iface[i] && iface[i]->get_id() == if_id)
|
|
return(iface[i]);
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isExistingInterface(const char * name) const {
|
|
if(name == NULL) return(false);
|
|
|
|
for(int i=0; i<num_defined_interfaces; i++) {
|
|
if(!strcmp(iface[i]->get_name(), name))
|
|
return(true);
|
|
}
|
|
|
|
if(!strcmp(name, getSystemInterface()->get_name()))
|
|
return(true);
|
|
|
|
return(false);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
NetworkInterface* Ntop::getNetworkInterface(const char *name, lua_State* vm) {
|
|
char allowed_ifname[MAX_INTERFACE_NAME_LEN] = {0};
|
|
char *bad_num = NULL;
|
|
int if_id;
|
|
|
|
if(vm && getInterfaceAllowed(vm, allowed_ifname)) {
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Forcing allowed interface. [requested: %s][selected: %s]",
|
|
name, allowed_ifname);
|
|
return getNetworkInterface(allowed_ifname);
|
|
}
|
|
|
|
if(name == NULL)
|
|
return(NULL);
|
|
|
|
/* This method accepts both interface names or Ids.
|
|
* Due to bad Lua number formatting, a float number may be received. */
|
|
if_id = strtof(name, &bad_num);
|
|
|
|
if((if_id == SYSTEM_INTERFACE_ID) || !strcmp(name, SYSTEM_INTERFACE_NAME))
|
|
return(getSystemInterface());
|
|
|
|
if((bad_num == NULL) || (*bad_num == '\0')) {
|
|
/* name is a number */
|
|
return(getInterfaceById(if_id));
|
|
}
|
|
|
|
/* if here, name is a string */
|
|
for(int i = 0; i<num_defined_interfaces; i++) {
|
|
if (!strcmp(name, iface[i]->get_name())) {
|
|
NetworkInterface *ret_iface = isInterfaceAllowed(vm, iface[i]->get_name()) ? iface[i] : NULL;
|
|
|
|
if(ret_iface)
|
|
return(ret_iface);
|
|
}
|
|
}
|
|
|
|
return(NULL);
|
|
};
|
|
|
|
/* ******************************************* */
|
|
|
|
int Ntop::getInterfaceIdByName(lua_State *vm, const char * name) {
|
|
NetworkInterface * res = getNetworkInterface(name, vm);
|
|
|
|
if(res)
|
|
return res->get_id();
|
|
|
|
return(-1);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
/* NOTE: the interface is deleted when this method returns false */
|
|
bool Ntop::registerInterface(NetworkInterface *_if) {
|
|
bool rv = true;
|
|
|
|
/* Needed as can be called concurrently by NetworkInterface::registerSubInterface */
|
|
m.lock(__FILE__, __LINE__);
|
|
|
|
for(int i = 0; i < num_defined_interfaces; i++) {
|
|
if(strcmp(iface[i]->get_name(), _if->get_name()) == 0) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING,
|
|
"Skipping duplicated interface %s", _if->get_description());
|
|
|
|
rv = false;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if(num_defined_interfaces < MAX_NUM_DEFINED_INTERFACES) {
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Registered interface %s [id: %d]",
|
|
_if->get_description(), _if->get_id());
|
|
iface[num_defined_interfaces++] = _if;
|
|
|
|
rv = true;
|
|
goto out;
|
|
} else {
|
|
static bool too_many_interfaces_error = false;
|
|
if(!too_many_interfaces_error) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Too many interfaces defined");
|
|
too_many_interfaces_error = true;
|
|
}
|
|
|
|
rv = false;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if(!rv)
|
|
delete _if;
|
|
|
|
m.unlock(__FILE__, __LINE__);
|
|
|
|
return(rv);
|
|
};
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::initInterface(NetworkInterface *_if) {
|
|
/* Initialization related to flow-dump */
|
|
if(ntop->getPrefs()->do_dump_flows()) {
|
|
if(_if->initFlowDump(num_dump_interfaces))
|
|
num_dump_interfaces++;
|
|
_if->startDBLoop();
|
|
}
|
|
|
|
/* Other initialization activities */
|
|
_if->initFlowChecksLoop();
|
|
_if->initHostChecksLoop();
|
|
_if->checkDisaggregationMode();
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Ntop::addToPool(char *host_or_mac, u_int16_t user_pool_id) {
|
|
char key[128], pool_buf[16];
|
|
|
|
#ifdef HOST_POOLS_DEBUG
|
|
ntop->getTrace()->traceEvent(TRACE_INFO,
|
|
"Adding %s as host pool member [pool id: %i]",
|
|
host_or_mac,
|
|
user_pool_id);
|
|
#endif
|
|
|
|
snprintf(pool_buf, sizeof(pool_buf), "%u", user_pool_id);
|
|
snprintf(key, sizeof(key), HOST_POOL_MEMBERS_KEY, pool_buf);
|
|
ntop->getRedis()->sadd(key, host_or_mac); /* New member added */
|
|
|
|
reloadHostPools();
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::checkReloadHostPools() {
|
|
if(hostPoolsReloadInProgress /* Check if a reload has been requested */) {
|
|
/* Leave this BEFORE the actual swap and new allocation to guarantee changes are always seen */
|
|
hostPoolsReloadInProgress = false;
|
|
|
|
for(int i = 0; i < get_num_interfaces(); i++) {
|
|
NetworkInterface *iface;
|
|
|
|
if((iface = ntop->getInterface(i)) != NULL)
|
|
iface->reloadHostPools();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::checkReloadAlertExclusions() {
|
|
#ifdef NTOPNG_PRO
|
|
if(alert_exclusions_shadow) { /* Dispose old memory if necessary */
|
|
delete alert_exclusions_shadow;
|
|
alert_exclusions_shadow = NULL;
|
|
}
|
|
|
|
if(alertExclusionsReloadInProgress /* Check if a reload has been requested */
|
|
|| !alert_exclusions /* Control groups are not allocated */) {
|
|
alertExclusionsReloadInProgress = false; /* Leave this BEFORE the actual swap and new allocation to guarantee changes are always seen */
|
|
|
|
alert_exclusions_shadow = alert_exclusions; /* Save the existing instance */
|
|
alert_exclusions = new (std::nothrow) AlertExclusions(); /* Allocate a new instance */
|
|
|
|
if(!alert_exclusions)
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to allocate memory for control groups.");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::checkReloadFlowChecks() {
|
|
if (!ntop->getPrefs()->is_pro_edition() /* Community mode */ &&
|
|
flow_checks_loader && flow_checks_loader->getChecksEdition() != ntopng_edition_community) {
|
|
/* Force a reload when switching to community (demo mode) */
|
|
reloadFlowChecks();
|
|
}
|
|
|
|
if(flowChecksReloadInProgress /* Reload requested from the UI upon configuration changes */) {
|
|
FlowChecksLoader *old, *tmp_flow_checks_loader = new (std::nothrow) FlowChecksLoader();
|
|
|
|
if(!tmp_flow_checks_loader) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to allocate memory for flow checks.");
|
|
return;
|
|
}
|
|
|
|
tmp_flow_checks_loader->initialize();
|
|
old = flow_checks_loader;
|
|
|
|
/* Pass the newly allocated loader to all interfaces so they will update their checks */
|
|
for(int i = 0; i < get_num_interfaces(); i++)
|
|
iface[i]->reloadFlowChecks(tmp_flow_checks_loader);
|
|
|
|
flow_checks_loader = tmp_flow_checks_loader;
|
|
|
|
if(old) {
|
|
sleep(2); /* Make sure nobody is using the old one */
|
|
|
|
delete old;
|
|
}
|
|
|
|
flowChecksReloadInProgress = false;
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::checkReloadHostChecks() {
|
|
if (!ntop->getPrefs()->is_pro_edition() /* Community mode */ &&
|
|
host_checks_loader && host_checks_loader->getChecksEdition() != ntopng_edition_community) {
|
|
/* Force a reload when switching to community (demo mode) */
|
|
reloadHostChecks();
|
|
}
|
|
|
|
if(hostChecksReloadInProgress /* Reload requested from the UI upon configuration changes */) {
|
|
HostChecksLoader *old, *tmp_host_checks_loader = new (std::nothrow) HostChecksLoader();
|
|
|
|
if(!tmp_host_checks_loader) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to allocate memory for host checks.");
|
|
return;
|
|
}
|
|
|
|
tmp_host_checks_loader->initialize();
|
|
old = host_checks_loader;
|
|
|
|
/* Pass the newly allocated loader to all interfaces so they will update their checks */
|
|
for(int i = 0; i < get_num_interfaces(); i++)
|
|
iface[i]->reloadHostChecks(tmp_host_checks_loader);
|
|
|
|
host_checks_loader = tmp_host_checks_loader;
|
|
|
|
if(old) {
|
|
sleep(2); /* Make sure nobody is using the old one */
|
|
|
|
delete old;
|
|
}
|
|
|
|
hostChecksReloadInProgress = false;
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
/* NOTE: the multiple isShutdown checks below are necessary to reduce the shutdown time */
|
|
void Ntop::runHousekeepingTasks() {
|
|
checkReloadHostPools();
|
|
checkReloadAlertExclusions();
|
|
checkReloadFlowChecks();
|
|
checkReloadHostChecks();
|
|
|
|
for(int i = 0; i < get_num_interfaces(); i++) {
|
|
if (!iface[i]->isStartingUp()) iface[i]->runHousekeepingTasks();
|
|
}
|
|
|
|
#ifdef NTOPNG_PRO
|
|
pro->runHousekeepingTasks();
|
|
#endif
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::runShutdownTasks() {
|
|
/* Final shut down tasks for all interfaces */
|
|
for(int i=0; i<num_defined_interfaces; i++) {
|
|
if(!iface[i]->isView())
|
|
iface[i]->runShutdownTasks();
|
|
}
|
|
|
|
for(int i=0; i<num_defined_interfaces; i++) {
|
|
if(iface[i]->isView())
|
|
iface[i]->runShutdownTasks();
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
/*
|
|
Checks if all the activities are completed (e.g., all packets processed, notifications sent)
|
|
and possibly sends a shutdown signal to terminate.
|
|
NOTE: Only effective when ntopng is started with --shutdown-when-done. Without that options
|
|
ntopng keeps running and doesn't terminate.
|
|
*/
|
|
void Ntop::checkShutdownWhenDone() {
|
|
if(ntop->getPrefs()->shutdownWhenDone()) {
|
|
for(int i = 0; i < get_num_interfaces(); i++) {
|
|
NetworkInterface *iface = getInterface(i);
|
|
|
|
/* Check all the interfaces reading from pcap files if they are done with their activities. */
|
|
if(iface->read_from_pcap_dump() && !iface->read_from_pcap_dump_done())
|
|
/* iface isn't done yet */
|
|
return;
|
|
}
|
|
|
|
/* Here all interface reading from pcap files are done. */
|
|
|
|
if(!recipients_are_empty()) {
|
|
/* Recipients are still processing notifications, wait until they're done. */
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Waiting for pending notifications..");
|
|
return;
|
|
}
|
|
|
|
/* When they are done, signal ntopng to shutdown */
|
|
|
|
/* One extra housekeeping before executing tests (this assumes all flows have been walked) */
|
|
runHousekeepingTasks();
|
|
|
|
runShutdownTasks();
|
|
|
|
/* Test Script (Runtime Analysis) */
|
|
if(ntop->getPrefs()->get_test_runtime_script_path()) {
|
|
const char *test_runtime_script_path = ntop->getPrefs()->get_test_runtime_script_path();
|
|
|
|
/* Execute as Bash script */
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "> Running Runtime Script '%s'", test_runtime_script_path);
|
|
Utils::exec(test_runtime_script_path);
|
|
}
|
|
|
|
/* Perform shutdown operations on all active interfaces - this also flushes all active flows */
|
|
ntop->shutdownInterfaces();
|
|
|
|
/* Make sure all flushed flows are also dumped to the database for post analysis (e.g. historical data) */
|
|
#if defined(HAVE_CLICKHOUSE) && defined(HAVE_MYSQL)
|
|
if(clickhouseImport)
|
|
importClickHouseDumps(true);
|
|
#endif
|
|
|
|
/* Test Script (Post Analysis) */
|
|
if(ntop->getPrefs()->get_test_post_script_path()) {
|
|
const char *test_post_script_path = ntop->getPrefs()->get_test_post_script_path();
|
|
|
|
/* Execute as Bash script */
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "> Running Post Script '%s'", test_post_script_path);
|
|
Utils::exec(test_post_script_path);
|
|
}
|
|
|
|
ntop->getGlobals()->shutdown();
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::shutdownPeriodicActivities() {
|
|
if(pa) {
|
|
while(pa->isRunning()) sleep(1);
|
|
|
|
delete pa;
|
|
pa = NULL;
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::shutdownInterfaces() {
|
|
/* First, shutdown all view interfaces so they can release counters from the viewed interfaces */
|
|
|
|
if(interfacesShuttedDown) return;
|
|
|
|
for(int i=0; i<num_defined_interfaces; i++) {
|
|
if(iface[i]->isView()) {
|
|
EthStats *stats = iface[i]->getStats();
|
|
|
|
stats->print();
|
|
iface[i]->shutdown();
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Polling shut down [interface: %s]",
|
|
iface[i]->get_description());
|
|
}
|
|
}
|
|
|
|
/* Now, shutdown all other non-view interfaces */
|
|
for(int i=0; i<num_defined_interfaces; i++) {
|
|
if(!iface[i]->isView()) {
|
|
EthStats *stats = iface[i]->getStats();
|
|
|
|
stats->print();
|
|
iface[i]->shutdown();
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Polling shut down [interface: %s]",
|
|
iface[i]->get_description());
|
|
}
|
|
}
|
|
|
|
interfacesShuttedDown = true;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::shutdownAll() {
|
|
ThreadedActivity *shutdown_activity;
|
|
|
|
/* Wait until currently executing periodic activities are completed,
|
|
* Periodic activites should not run during interfaces shutdown */
|
|
ntop->shutdownPeriodicActivities();
|
|
|
|
/* Perform shutdown operations on all active interfaces,
|
|
* including purging all active flows, hosts, etc */
|
|
ntop->shutdownInterfaces();
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Executing shutdown script [%s]", SHUTDOWN_SCRIPT_PATH);
|
|
|
|
/* Exec shutdown script before shutting down ntopng */
|
|
if((shutdown_activity = new (std::nothrow) ThreadedActivity(SHUTDOWN_SCRIPT_PATH))) {
|
|
/* Don't call run() as by the time the script will be run the delete below will free the memory */
|
|
shutdown_activity->runSystemScript(time(NULL));
|
|
delete shutdown_activity;
|
|
}
|
|
|
|
#if defined(HAVE_CLICKHOUSE) && defined(HAVE_MYSQL)
|
|
/* Dump flows flushed during shutdown */
|
|
/* Commented out: this is done on restart to speed up the shutdown
|
|
if(clickhouseImport)
|
|
importClickHouseDumps(true);
|
|
*/
|
|
#endif
|
|
|
|
/* Complete the shutdown */
|
|
ntop->getGlobals()->shutdown();
|
|
|
|
#ifndef WIN32
|
|
/*
|
|
PID file cannot be deleted as it is under `/var/run` which, in turn, is a symlink to `/run`, which is not writable by user `ntopng`.
|
|
As user `ntopng` has no write privileges on `/run`, the PID file cannot be deleted from inside this process. Deletion is performed
|
|
as part of the ExecStopPost in the systemd ntopng.service file
|
|
*/
|
|
#if 0
|
|
if(ntop->getPrefs()->get_pid_path() != NULL) {
|
|
int rc = unlink(ntop->getPrefs()->get_pid_path());
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Deleted PID %s: [rc: %d][%s]",
|
|
ntop->getPrefs()->get_pid_path(),
|
|
rc, strerror(errno));
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
void Ntop::purgeLoopBody() {
|
|
while(!globals->isShutdown()) {
|
|
for(u_int i = 0; i < get_num_interfaces(); i++) {
|
|
NetworkInterface *cur_iface = getInterface(i);
|
|
if(cur_iface) cur_iface->purgeQueuedIdleEntries();
|
|
}
|
|
|
|
/* Safe to sleep 100ms as entries are marked as idle by interfaces purgeIdle
|
|
which runs every second on 1/24th of the hash table. This is run faster that 1 second
|
|
as there may be idle entries not deleted (number of uses greater than 0) */
|
|
_usleep(100000);
|
|
}
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
static void* purgeLoop(void *arg) {
|
|
ntop->purgeLoopBody();
|
|
return NULL;
|
|
}
|
|
|
|
/* **************************************************** */
|
|
|
|
/*
|
|
Thread which iterates on all available interfaces and perform the delete operations on idle hash table entries
|
|
*/
|
|
bool Ntop::startPurgeLoop() {
|
|
if(!purgeLoop_started) {
|
|
pthread_create(&purgeLoop, NULL, ::purgeLoop, NULL);
|
|
purgeLoop_started = true;
|
|
}
|
|
|
|
return purgeLoop_started;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::loadTrackers() {
|
|
FILE *fd;
|
|
char line[MAX_PATH];
|
|
|
|
snprintf(line, sizeof(line), "%s/other/trackers.txt", prefs->get_docs_dir());
|
|
|
|
if((fd = fopen(line, "r")) != NULL) {
|
|
if((trackers_automa = ndpi_init_automa()) == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to initialize trackers");
|
|
fclose(fd);
|
|
return;
|
|
}
|
|
|
|
while(fgets(line, MAX_PATH, fd) != NULL) {
|
|
char *str = strdup(line);
|
|
if (str)
|
|
ndpi_add_string_to_automa(trackers_automa, str);
|
|
}
|
|
|
|
fclose(fd);
|
|
ndpi_finalize_automa(trackers_automa);
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to load trackers file %s", line);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isATrackerHost(char *host) {
|
|
return trackers_automa && ndpi_match_string(trackers_automa, host) > 0;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::initAllowedProtocolPresets() {
|
|
for(u_int i=0; i<device_max_type; i++) {
|
|
DeviceProtocolBitmask *b = ntop->getDeviceAllowedProtocols((DeviceType) i);
|
|
NDPI_BITMASK_SET_ALL(b->clientAllowed);
|
|
NDPI_BITMASK_SET_ALL(b->serverAllowed);
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::refreshAllowedProtocolPresets(DeviceType device_type, bool client, lua_State *L, int index) {
|
|
DeviceProtocolBitmask *b = ntop->getDeviceAllowedProtocols(device_type);
|
|
|
|
lua_pushnil(L);
|
|
|
|
if (b == NULL)
|
|
return;
|
|
|
|
if (client) NDPI_BITMASK_RESET(b->clientAllowed);
|
|
else NDPI_BITMASK_RESET(b->serverAllowed);
|
|
|
|
while(lua_next(L, index) != 0) {
|
|
u_int key_proto = lua_tointeger(L, -2);
|
|
int t = lua_type(L, -1);
|
|
|
|
if((int)key_proto < 0) continue;
|
|
|
|
switch (t) {
|
|
case LUA_TNUMBER:
|
|
{
|
|
u_int value_action = lua_tointeger(L, -1);
|
|
if (value_action) {
|
|
if (client) NDPI_BITMASK_ADD(b->clientAllowed, key_proto);
|
|
else NDPI_BITMASK_ADD(b->serverAllowed, key_proto);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Internal error: type %d not handled", t);
|
|
break;
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
#ifdef NTOPNG_PRO
|
|
|
|
bool Ntop::addIPToLRUMatches(u_int32_t client_ip,
|
|
u_int16_t user_pool_id,
|
|
char *label, char *ifname) {
|
|
for(int i=0; i<num_defined_interfaces; i++) {
|
|
if(iface[i]->is_bridge_interface() && (strcmp(iface[i]->get_name(), ifname) == 0)) {
|
|
iface[i]->addIPToLRUMatches(client_ip, user_pool_id, label);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::addToNotifiedInformativeCaptivePortal(u_int32_t client_ip) {
|
|
for(int i = 0; i < num_defined_interfaces; i++) {
|
|
if(iface[i]->is_bridge_interface()) /* TODO: handle multiple interfaces separately */
|
|
iface[i]->addToNotifiedInformativeCaptivePortal(client_ip);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ******************************************* */
|
|
|
|
DeviceProtoStatus Ntop::getDeviceAllowedProtocolStatus(DeviceType dev_type,
|
|
ndpi_protocol proto, u_int16_t pool_id,
|
|
bool as_client) {
|
|
/* Check if this application protocol is allowd for the specified device type */
|
|
DeviceProtocolBitmask *bitmask = getDeviceAllowedProtocols(dev_type);
|
|
NDPI_PROTOCOL_BITMASK *direction_bitmask = as_client ? (&bitmask->clientAllowed) : (&bitmask->serverAllowed);
|
|
|
|
#ifdef HAVE_NEDGE
|
|
/* On nEdge the concept of device protocol policies is only applied to unassigned devices on LAN */
|
|
if(pool_id != NO_HOST_POOL_ID)
|
|
return device_proto_allowed;
|
|
#endif
|
|
|
|
/* Always allow network critical protocols */
|
|
if(Utils::isCriticalNetworkProtocol(proto.master_protocol) ||
|
|
Utils::isCriticalNetworkProtocol(proto.app_protocol))
|
|
return device_proto_allowed;
|
|
|
|
if((proto.master_protocol != NDPI_PROTOCOL_UNKNOWN) &&
|
|
(!NDPI_ISSET(direction_bitmask, proto.master_protocol))) {
|
|
return device_proto_forbidden_master;
|
|
} else if((!NDPI_ISSET(direction_bitmask, proto.app_protocol))) {
|
|
/* We consider NDPI_PROTOCOL_UNKNOWN as a protocol to be allowed */
|
|
return device_proto_forbidden_app;
|
|
}
|
|
|
|
return device_proto_allowed;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::resetStats() {
|
|
char buf[32];
|
|
last_stats_reset = time(NULL);
|
|
|
|
snprintf(buf, sizeof(buf), "%ld", last_stats_reset);
|
|
|
|
/* Saving this is essential to reset inactive hosts across ntopng restarts */
|
|
getRedis()->set(LAST_RESET_TIME, buf);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::refreshCPULoad() {
|
|
if(Utils::getCPULoad(&cpu_stats))
|
|
cpu_load = cpu_stats.load;
|
|
else
|
|
cpu_load = -1;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::getCPULoad(float *out) {
|
|
bool rv;
|
|
|
|
if(cpu_load >= 0) {
|
|
*out = cpu_load;
|
|
rv = true;
|
|
} else
|
|
rv = false;
|
|
|
|
return(rv);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::initnDPIReload() {
|
|
bool rc = false;
|
|
|
|
for(u_int i = 0; i<get_num_interfaces(); i++)
|
|
if(getInterface(i)) rc |= getInterface(i)->initnDPIReload();
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isnDPIReloadInProgress() {
|
|
bool rc = false;
|
|
|
|
for(u_int i = 0; i<get_num_interfaces(); i++)
|
|
if(getInterface(i)) rc |= getInterface(i)->isnDPIReloadInProgress();
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::finalizenDPIReload() {
|
|
for(u_int i = 0; i<get_num_interfaces(); i++)
|
|
if(getInterface(i)) getInterface(i)->finalizenDPIReload();
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::nDPILoadIPCategory(char *what, ndpi_protocol_category_t id, char *list_name) {
|
|
char *persistent_name = getPersistentCustomListName(list_name);
|
|
bool success = true;
|
|
|
|
for(u_int i = 0; i<get_num_interfaces(); i++) {
|
|
if(getInterface(i)) {
|
|
if (!getInterface(i)->nDPILoadIPCategory(what, id, persistent_name))
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::nDPILoadHostnameCategory(char *what, ndpi_protocol_category_t id, char *list_name) {
|
|
char *persistent_name = getPersistentCustomListName(list_name);
|
|
bool success = true;
|
|
|
|
for(u_int i = 0; i<get_num_interfaces(); i++) {
|
|
if(getInterface(i)) {
|
|
if (!getInterface(i)->nDPILoadHostnameCategory(what, id, persistent_name))
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
int Ntop::nDPILoadMaliciousJA3Signatures(const char *file_path) {
|
|
int rc = 0;
|
|
|
|
for(u_int i = 0; i<get_num_interfaces(); i++)
|
|
if(getInterface(i)) rc = getInterface(i)->nDPILoadMaliciousJA3Signatures(file_path);
|
|
|
|
return(rc /* last one returned */);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
ndpi_protocol_category_t Ntop::get_ndpi_proto_category(u_int protoid) {
|
|
for(u_int i = 0; i<get_num_interfaces(); i++)
|
|
if(getInterface(i)) return(getInterface(i)->get_ndpi_proto_category(protoid));
|
|
|
|
return(NDPI_PROTOCOL_CATEGORY_UNSPECIFIED);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::setnDPIProtocolCategory(u_int16_t protoId, ndpi_protocol_category_t protoCategory) {
|
|
for(u_int i = 0; i<get_num_interfaces(); i++)
|
|
if(getInterface(i)) getInterface(i)->setnDPIProtocolCategory(protoId, protoCategory);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Ntop::setLastInterfacenDPIReload(time_t now) {
|
|
for(u_int i = 0; i<get_num_interfaces(); i++)
|
|
if(getInterface(i)) getInterface(i)->setLastInterfacenDPIReload(now);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
bool Ntop::needsnDPICleanup() {
|
|
bool rc = false;
|
|
|
|
for(u_int i = 0; i<get_num_interfaces(); i++)
|
|
if(getInterface(i)) rc |= getInterface(i)->needsnDPICleanup();
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
u_int16_t Ntop::getnDPIProtoByName(const char *name) {
|
|
NetworkInterface *iface = getFirstInterface();
|
|
|
|
if(iface)
|
|
return iface->getnDPIProtoByName(name);
|
|
|
|
return(NDPI_PROTOCOL_UNKNOWN);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Ntop::setnDPICleanupNeeded(bool needed) {
|
|
for(u_int i = 0; i<get_num_interfaces(); i++)
|
|
if(getInterface(i)) getInterface(i)->setnDPICleanupNeeded(needed);
|
|
}
|
|
|
|
/* *************************************** */
|
|
|
|
void Ntop::setScriptsDir() {
|
|
#ifdef WIN32
|
|
snprintf(scripts_dir, sizeof(scripts_dir), "%s\\scripts", get_working_dir());
|
|
#else
|
|
snprintf(scripts_dir, sizeof(scripts_dir), "%s/scripts", get_working_dir());
|
|
#endif
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
inline int16_t Ntop::localNetworkLookup(int family, void *addr, u_int8_t *network_mask_bits) {
|
|
return(local_network_tree.findAddress(family, addr, network_mask_bits));
|
|
}
|
|
|
|
/* **************************************** */
|
|
|
|
u_int16_t Ntop::getLocalNetworkId(const char *address_str) {
|
|
u_int16_t i;
|
|
|
|
for(i = 0; i< local_network_tree.getNumAddresses(); i++) {
|
|
if(!strcmp(address_str, local_network_names[i]))
|
|
return(i);
|
|
}
|
|
|
|
return((u_int16_t)-1);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::addLocalNetwork(char *_net) {
|
|
char *net, *position_ptr;
|
|
char alias[64] = "";
|
|
|
|
int id = local_network_tree.getNumAddresses(), pos = 0;
|
|
|
|
if(id >= CONST_MAX_NUM_NETWORKS) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Too many networks defined (%d): ignored %s",
|
|
id, _net);
|
|
return(false);
|
|
}
|
|
|
|
// Getting the pointer and the position to the "=" indicator
|
|
position_ptr = strstr(_net, "=");
|
|
pos = (position_ptr == NULL ? 0 : position_ptr - _net);
|
|
|
|
if(pos) {
|
|
// "=" indicator is present inside the string
|
|
// Separating the alias from the network
|
|
net = strndup(_net, pos);
|
|
memcpy(alias, position_ptr + 1, strlen(_net) - pos - 1);
|
|
} else {
|
|
net = strdup(_net);
|
|
}
|
|
|
|
if(net == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
return(false);
|
|
}
|
|
|
|
// Adding the Network to the local Networks
|
|
if (!local_network_tree.addAddresses(net)) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Failure adding address");
|
|
free(net);
|
|
return(false);
|
|
}
|
|
|
|
local_network_names[id] = net;
|
|
|
|
// Adding, if available, the alias
|
|
if(pos) {
|
|
char out[128] = { '\0' };
|
|
u_int len = ndpi_min(strlen(alias), sizeof(out)-2);
|
|
|
|
for(u_int i=0, j=0; i<len; i++) {
|
|
if(isprint(alias[i]))
|
|
out[j++] = alias[i];
|
|
}
|
|
|
|
local_network_aliases[id] = strdup(out);
|
|
}
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "Added Local Network %s", net);
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::getLocalNetworkAlias(lua_State *vm, u_int16_t network_id) {
|
|
char *alias = local_network_aliases[network_id];
|
|
|
|
// Checking if the network has an alias
|
|
if(!alias)
|
|
return false;
|
|
|
|
lua_pushstring(vm, alias);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
/* Format: 131.114.21.0/24,10.0.0.0/255.0.0.0 */
|
|
void Ntop::addLocalNetworkList(const char *rule) {
|
|
char *tmp, *net = strtok_r((char *) rule, ",", &tmp);
|
|
|
|
while(net != NULL) {
|
|
if(!addLocalNetwork(net)) return;
|
|
net = strtok_r(NULL, ",", &tmp);
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::luaFlowCheckInfo(lua_State *vm, std::string check_name) const {
|
|
FlowChecksLoader *fcl = flow_checks_loader;
|
|
if(fcl) return fcl->luaCheckInfo(vm, check_name);
|
|
|
|
return false;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::luaClickHouseStats(lua_State *vm) const {
|
|
#if defined(HAVE_CLICKHOUSE) && defined(HAVE_MYSQL)
|
|
if(clickhouseImport) {
|
|
clickhouseImport->lua(vm);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
lua_pushnil(vm);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::luaHostCheckInfo(lua_State *vm, std::string check_name) const {
|
|
HostChecksLoader *hcl = host_checks_loader;
|
|
if(hcl) return hcl->luaCheckInfo(vm, check_name);
|
|
|
|
return false;
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::isDbCreated() {
|
|
for(int i = 0; i < MAX_NUM_INTERFACE_IDS; i++) {
|
|
NetworkInterface *iface = ntop->getInterface(i);
|
|
|
|
if(iface && (!iface->isDbCreated()))
|
|
return(false);
|
|
}
|
|
|
|
return(true);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
#ifndef HAVE_NEDGE
|
|
|
|
bool Ntop::broadcastIPSMessage(char *msg) {
|
|
bool rc = false;
|
|
|
|
if(prefs->getZMQPublishEventsURL() == NULL)
|
|
return(false);
|
|
|
|
/* Jeopardized users_m lock :-) */
|
|
users_m.lock(__FILE__, __LINE__);
|
|
|
|
if(zmqPublisher == NULL) {
|
|
try {
|
|
zmqPublisher = new ZMQPublisher(prefs->getZMQPublishEventsURL());
|
|
} catch(...) {
|
|
zmqPublisher = NULL;
|
|
}
|
|
}
|
|
|
|
if(!zmqPublisher) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to create ZMQ publisher");
|
|
users_m.unlock(__FILE__, __LINE__);
|
|
return(false);
|
|
}
|
|
|
|
if(msg)
|
|
rc = zmqPublisher->sendIPSMessage(msg);
|
|
|
|
users_m.unlock(__FILE__, __LINE__);
|
|
|
|
return(rc);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ******************************************* */
|
|
|
|
#ifndef WIN32
|
|
|
|
/* ******************************************* */
|
|
|
|
Ping* Ntop::getPing(char *ifname) {
|
|
if(!can_send_icmp) return(NULL);
|
|
|
|
if((ifname == NULL) || (ifname[0] == '\0'))
|
|
return(default_ping);
|
|
else {
|
|
std::map<std::string /* ifname */, Ping*>::iterator it = ping.find(ifname);
|
|
|
|
if(it == ping.end()) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to find ping for interface %s", ifname);
|
|
return(default_ping);
|
|
} else
|
|
return(it->second);
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::initPing() {
|
|
if(!can_send_icmp) return;
|
|
|
|
for(int i=0; i<num_defined_interfaces; i++) {
|
|
switch(iface[i]->getIfType()) {
|
|
case interface_type_PF_RING:
|
|
case interface_type_PCAP:
|
|
{
|
|
char *name = iface[i]->get_name();
|
|
Ping *p = new (std::nothrow)Ping(name);
|
|
|
|
if(p) {
|
|
ping[std::string(name)] = p;
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Created pinger for %s", name);
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING,
|
|
"Unable to create ping for interface %s", name);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Skipping pinger for %s [ifType: %u]",
|
|
iface[i]->get_name(), iface[i]->getIfType());
|
|
/* Nothing to do for other interface types */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::collectResponses(lua_State* vm) {
|
|
lua_newtable(vm);
|
|
|
|
default_ping->collectResponses(vm, false /* IPv4 */);
|
|
default_ping->collectResponses(vm, true /* IPv6 */);
|
|
|
|
for(std::map<std::string /* ifname */, Ping*>::iterator it = ping.begin(); it != ping.end(); ++it) {
|
|
it->second->collectResponses(vm, false /* IPv4 */);
|
|
it->second->collectResponses(vm, true /* IPv6 */);
|
|
}
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::collectContinuousResponses(lua_State* vm) {
|
|
lua_newtable(vm);
|
|
|
|
cping->collectResponses(vm, false /* IPv4 */);
|
|
cping->collectResponses(vm, true /* IPv6 */);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ******************************************* */
|
|
|
|
/*
|
|
This method is needed to have a string that is not deallocated
|
|
after a call, but that is persistent inside nDPI
|
|
*/
|
|
char* Ntop::getPersistentCustomListName(char *list_name) {
|
|
std::string key(list_name);
|
|
std::map<std::string, bool>::iterator it = cachedCustomLists.find(key);
|
|
|
|
if(it == cachedCustomLists.end()) {
|
|
/* Not found */
|
|
cachedCustomLists[key] = true;
|
|
it = cachedCustomLists.find(key);
|
|
}
|
|
|
|
return((char*)it->first.c_str());
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::setZoneInfo() {
|
|
#ifndef WIN32
|
|
|
|
#ifdef __FreeBSD__
|
|
FILE *fd = fopen("/var/db/zoneinfo", "r");
|
|
|
|
zoneinfo = NULL;
|
|
|
|
if(fd != NULL) {
|
|
char timezone[64];
|
|
|
|
if(fgets(timezone, sizeof(timezone), fd)) {
|
|
int len = strlen(timezone);
|
|
|
|
if(len > 0) timezone[len-1] = '\0';
|
|
zoneinfo = strdup(timezone);
|
|
}
|
|
|
|
fclose(fd);
|
|
} else {
|
|
/* Last resort */
|
|
const char *command_buf = "find /usr/share/zoneinfo -type f | xargs md5sum | grep `md5sum -q /etc/localtime` | tail -1 | cut -d '/' -f 5-";
|
|
FILE *fp;
|
|
|
|
if((fp = popen(command_buf, "r")) != NULL) {
|
|
char line[256];
|
|
|
|
if(fgets(line, sizeof(line), fp) != NULL)
|
|
zoneinfo = strdup(line);
|
|
|
|
pclose(fp);
|
|
}
|
|
}
|
|
|
|
#else
|
|
char buf[64];
|
|
ssize_t rc = readlink("/etc/localtime", buf, sizeof(buf));
|
|
u_int num_slash = 0;
|
|
|
|
zoneinfo = NULL;
|
|
|
|
if(rc > 0) {
|
|
buf[rc] = '\0';
|
|
|
|
rc--;
|
|
|
|
while(rc > 0) {
|
|
if(buf[rc] == '/') {
|
|
if(++num_slash == 2)
|
|
break;
|
|
}
|
|
|
|
rc--;
|
|
}
|
|
|
|
if(num_slash == 2) {
|
|
rc++;
|
|
zoneinfo = strdup(&buf[rc]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#else
|
|
zoneinfo = getWindowsTimezone();
|
|
|
|
#endif /* WIN32 */
|
|
|
|
if(zoneinfo == NULL) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to find timezone: using UTC");
|
|
zoneinfo = strdup("Europe/London"); /* UTC */
|
|
}
|
|
|
|
if(zoneinfo)
|
|
ntop->getTrace()->traceEvent(TRACE_INFO, "ntopng timezone set to %s", zoneinfo);
|
|
}
|
|
|
|
|
|
/* ******************************************* */
|
|
|
|
// #define DEBUG_SPEEDTEST
|
|
#include "../third-party/speedtest.c"
|
|
|
|
void Ntop::speedtest(lua_State *vm) {
|
|
json_object *rc;
|
|
|
|
/*
|
|
We need to make sure that only one caller
|
|
at time calls speedtest as
|
|
- the speedtest code is not reentrant
|
|
- running multiple tests concurrently reports wrong results
|
|
as clients compete for the same bandwidth
|
|
*/
|
|
|
|
speedtest_m.lock(__FILE__, __LINE__);
|
|
|
|
rc = ::speedtest();
|
|
|
|
if(rc) {
|
|
lua_pushstring(vm, json_object_to_json_string(rc));
|
|
json_object_put(rc); /* Free memory */
|
|
} else
|
|
lua_pushnil(vm);
|
|
|
|
speedtest_m.unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
bool Ntop::createPcapInterface(const char *path, int *iface_id) {
|
|
#ifndef HAVE_NEDGE
|
|
NetworkInterface *new_iface, *old_iface = NULL;
|
|
#endif
|
|
bool ret;
|
|
#ifndef HAVE_NEDGE
|
|
u_int slot_id;
|
|
|
|
if(old_iface_to_purge != NULL) {
|
|
delete old_iface_to_purge;
|
|
old_iface_to_purge = NULL;
|
|
}
|
|
|
|
if(*iface_id != -1) {
|
|
for(int i=0; i<num_defined_interfaces; i++) {
|
|
if(iface[i]->get_id() == *iface_id) {
|
|
old_iface = iface[i], slot_id = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
errno = 0;
|
|
new_iface = new PcapInterface((const char*)path,
|
|
(u_int8_t)ntop->get_num_interfaces(),
|
|
true /* delete pcap when done */);
|
|
|
|
if(old_iface == NULL) {
|
|
/* Allocate new interface */
|
|
|
|
if(registerInterface(new_iface)) {
|
|
initInterface(new_iface);
|
|
new_iface->allocateStructures();
|
|
new_iface->startPacketPolling();
|
|
*iface_id = new_iface->get_id();
|
|
}
|
|
} else {
|
|
NetworkInterface *old = iface[slot_id];
|
|
|
|
initInterface(new_iface);
|
|
new_iface->allocateStructures();
|
|
new_iface->startPacketPolling();
|
|
|
|
m.lock(__FILE__, __LINE__);
|
|
|
|
iface[slot_id] = new_iface; /* Swap interfaces */
|
|
|
|
old->shutdown();
|
|
|
|
/* Wait until the interface is shutdown */
|
|
while(old->isRunning()) sleep(1);
|
|
|
|
/* Trick to avoid crashing while using the same interface we want to free */
|
|
old_iface_to_purge = old;
|
|
|
|
*iface_id = new_iface->get_id();
|
|
m.unlock(__FILE__, __LINE__);
|
|
}
|
|
|
|
ret = true;
|
|
|
|
} catch(int err) {
|
|
getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to open interface %s with pcap [%d]: %s",
|
|
path, err, strerror(err));
|
|
|
|
ret = false;
|
|
}
|
|
#else
|
|
ret = false;
|
|
#endif
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* ******************************************* */
|
|
|
|
void Ntop::incBlacklisHits(std::string listname) {
|
|
blStats.incHits(listname);
|
|
}
|