/* * * (C) 2013-17 - 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 WIN32 #include /* SHGetFolderPath() */ #else #include #endif Ntop *ntop; static const char* dirs[] = { NULL, #ifndef WIN32 CONST_DEFAULT_INSTALL_DIR, #else NULL, #endif CONST_ALT_INSTALL_DIR, CONST_ALT2_INSTALL_DIR, NULL }; extern struct keyval string_to_replace[]; /* Lua.cpp */ /* ******************************************* */ Ntop::Ntop(char *appName) { ntop = this; globals = new NtopGlobals(); pa = new PeriodicActivities(); address = new AddressResolution(); httpbl = NULL, flashstart = NULL; custom_ndpi_protos = NULL; prefs = NULL, redis = NULL; elastic_search = NULL; logstash = NULL; num_cpus = -1; num_defined_interfaces = 0; export_interface = NULL; start_time = 0, epoch_buf[0] = '\0'; /* It will be initialized by start() */ memset(iface, 0, sizeof(iface)); httpd = NULL, runtimeprefs = NULL, geo = NULL, mac_manufacturers = NULL, hostBlacklistShadow = hostBlacklist = NULL; #ifdef WIN32 if (SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, working_dir) != S_OK) { strcpy(working_dir, "C:\\Windows\\Temp\\ntopng"); // Fallback: it should never happen } 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 /* Folder will be created lazily, avoid creating it now */ snprintf(working_dir, sizeof(working_dir), "%s/ntopng", CONST_DEFAULT_WRITABLE_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; dirs[i] != NULL; i++) { char path[MAX_PATH]; 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 #ifdef NTOPNG_PRO pro = new NtopPro(); #ifndef WIN32 nagios_manager = NULL; #endif flow_checker = new FlowChecker(); if((pro == NULL) || (flow_checker == NULL)) { throw "Not enough memory"; } #else pro = NULL; #endif // printf("--> %s [%s]\n", startup_dir, appName); initTimezone(); ntop->getTrace()->traceEvent(TRACE_INFO, "System Timezone offset: %+ld", time_offset); udp_socket = socket(AF_INET, SOCK_DGRAM, 0); #ifndef WIN32 setservent(1); #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); time_offset = localtime(&t)->tm_gmtoff; #endif } /* ******************************************* */ Ntop::~Ntop() { if(httpd) delete httpd; /* Stop the http server before tearing down network interfaces */ for(int i=0; ishutdown(); delete(iface[i]); } if(udp_socket != -1) closesocket(udp_socket); if(httpbl) delete httpbl; if(flashstart) delete flashstart; if(custom_ndpi_protos) delete(custom_ndpi_protos); if(elastic_search) delete(elastic_search); if(logstash) delete(logstash); if(hostBlacklist) delete hostBlacklist; if(hostBlacklistShadow) delete hostBlacklistShadow; delete address; delete pa; if(geo) delete geo; if(mac_manufacturers) delete mac_manufacturers; if(redis) delete redis; delete globals; delete prefs; delete runtimeprefs; #ifdef NTOPNG_PRO if(pro) delete pro; #ifdef WIN32 #ifndef WIN32 if(nagios_manager) delete nagios_manager; #endif #endif if(flow_checker) delete flow_checker; #endif } /* ******************************************* */ void Ntop::registerPrefs(Prefs *_prefs, bool quick_registration) { struct stat statbuf; prefs = _prefs; if(!quick_registration) { if(stat(prefs->get_data_dir(), &statbuf) || (!(statbuf.st_mode & S_IFDIR)) /* It's not a directory */ || (!(statbuf.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(), &statbuf) || (!(statbuf.st_mode & S_IFDIR)) /* It's not a directory */ || (!(statbuf.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); } } memset(iface, 0, sizeof(iface)); initRedis(); if(ntop->getRedis() == NULL) { ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to initialize redis. Quitting..."); exit(-1); } initElasticSearch(); initLogstash(); #ifdef NTOPNG_PRO if(((ntop->getPrefs()->get_http_port() != 80) && (ntop->getPrefs()->get_alt_http_port() != 80)) || ((ntop->getPrefs()->get_http_port() == 80) && (ntop->getPrefs()->get_alt_http_port() == 0))) { redis->del((char*)CONST_PREFS_CAPTIVE_PORTAL); } pro->init_license(); #endif } /* ******************************************* */ #ifdef NTOPNG_PRO void Ntop::registerNagios(void) { #ifndef WIN32 if(nagios_manager) { delete nagios_manager; nagios_manager = NULL; } nagios_manager = new NagiosManager(); #endif } #endif /* ******************************************* */ void Ntop::initRedis() { if(redis) delete(redis); redis = new Redis(prefs->get_redis_host(), prefs->get_redis_password(), prefs->get_redis_port(), prefs->get_redis_db_id()); } /* ******************************************* */ void Ntop::initLogstash(){ if(logstash) delete(logstash); logstash = new Logstash(); } void Ntop::initElasticSearch() { if(elastic_search) delete(elastic_search); elastic_search = new ElasticSearch(); } /* ******************************************* */ void Ntop::createExportInterface() { if(prefs->get_export_endpoint()) export_interface = new ExportInterface(prefs->get_export_endpoint()); else export_interface = NULL; } /* ******************************************* */ void Ntop::start() { char daybuf[64], buf[32]; time_t when = time(NULL); int i = 0; getTrace()->traceEvent(TRACE_NORMAL, "Welcome to ntopng %s v.%s - (C) 1998-17 ntop.org", PACKAGE_MACHINE, PACKAGE_VERSION); if(PACKAGE_OS[0] != '\0') getTrace()->traceEvent(TRACE_NORMAL, "Built on %s", PACKAGE_OS); 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 = 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); pa->startPeriodicActivitiesLoop(); if(httpbl) httpbl->startLoop(); else if(flashstart) flashstart->startLoop(); runtimeprefs = new RuntimePrefs(); #ifdef NTOPNG_PRO pro->printLicenseInfo(); #endif prefs->loadInstanceNameDefaults(); loadLocalInterfaceAddress(); for(int i=0; iallocateNetworkStats(); iface[i]->startPacketPolling(); } sleep(2); address->startResolveAddressLoop(); while(!globals->isShutdown()) { struct timeval begin, end; u_long usec_diff; u_long nap = ntop->getPrefs()->get_housekeeping_frequency() * 1e6; gettimeofday(&begin, NULL); runHousekeepingTasks(); gettimeofday(&end, NULL); usec_diff = (end.tv_sec * 1e6) + end.tv_usec - (begin.tv_sec * 1e6) - begin.tv_usec; if(usec_diff < nap) nap -= usec_diff; ntop->getTrace()->traceEvent(TRACE_DEBUG, "Sleeping %i microsecods before doing the chores.", nap); _usleep(nap); } } /* ******************************************* */ bool Ntop::isLocalAddress(int family, void *addr, int16_t *network_id) { *network_id = address->findAddress(family, addr); return(((*network_id) == -1) ? false : true); }; /* ******************************************* */ IpAddress* Ntop::getLocalNetworkIp(int16_t local_network_id) { IpAddress *network_ip = new IpAddress(); char *network_address, *slash; 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, '/'))) *slash = '\0'; if(network_ip) network_ip->set(network_address); if(network_address) free(network_address); return(network_ip); }; /* ******************************************* */ #ifdef WIN32 #include #include #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; /* 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 & pIPAddrTable->table[ifIdx].dwMask); snprintf(buf, bufsize, "%s/%u", inet_ntoa(IPAddr), bits); ntop->getTrace()->traceEvent(TRACE_NORMAL, "Adding %s as IPv4 local network for %", buf, iface[id]->get_name()); address->setLocalNetwork(buf); 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 interface address for %s", buf, iface[id]->get_name()); iface[id]->addInterfaceAddress(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]; int sock = socket(AF_INET, SOCK_STREAM, 0); if(getifaddrs(&local_addresses) != 0) { ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to read interface addresses"); 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; iget_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)); 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[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(buf_orig2, sizeof(buf_orig2), "%s/%d", buf, cidr); ntop->getTrace()->traceEvent(TRACE_NORMAL, "Adding %s as IPv4 local network for %s", buf_orig2, iface[ifId]->get_name()); address->setLocalNetwork(buf_orig2); } } 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, bufsize, "%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(buf_orig, bufsize, "%s/%d", buf, cidr); ntop->getTrace()->traceEvent(TRACE_NORMAL, "Adding %s as IPv6 local network for %s", buf_orig, iface[ifId]->get_name()); address->setLocalNetwork(buf_orig); } } } freeifaddrs(local_addresses); closesocket(sock); #endif ntop->getTrace()->traceEvent(TRACE_INFO, "Local Interface Addresses (System Host)"); local_interface_addresses.dump(); ntop->getTrace()->traceEvent(TRACE_INFO, "Local Networks"); address->dump(); if(0) { IpAddress a; a.set((char*)"192.12.193.113"); a.dump(); a.set((char*)"192.12.193.11"); a.dump(); a.set((char*)"2a00:d40:1:3:192:12:193:11"); a.dump(); a.set((char*)"2a00:d40:1:3:192:12:193:12"); a.dump(); exit(0); } } /* ******************************************* */ void Ntop::loadGeolocation(char *dir) { if(geo != NULL) delete geo; geo = new Geolocation(dir); } /* ******************************************* */ void Ntop::loadMacManufacturers(char *dir) { if(mac_manufacturers != NULL) delete mac_manufacturers; if((mac_manufacturers = new MacManufacturers(dir)) == NULL) throw "Not enough memory"; } /* ******************************************* */ void Ntop::setWorkingDir(char *dir) { snprintf(working_dir, sizeof(working_dir), "%s", dir); removeTrailingSlash(working_dir); }; /* ******************************************* */ 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::getUsers(lua_State* vm) { char **usernames; char *username, *holder; char key[CONST_MAX_LEN_REDIS_KEY], val[CONST_MAX_LEN_REDIS_VALUE]; int rc, i; lua_newtable(vm); if((rc = ntop->getRedis()->keys("ntopng.user.*.password", &usernames)) <= 0) { return; } for(i = 0; i < rc; i++) { if(usernames[i] == NULL) continue; /* safety check */ if(strtok_r(usernames[i], ".", &holder) == NULL) continue; if(strtok_r(NULL, ".", &holder) == NULL) continue; if((username = strtok_r(NULL, ".", &holder)) == NULL) continue; lua_newtable(vm); snprintf(key, sizeof(key), CONST_STR_USER_FULL_NAME, username); if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) lua_push_str_table_entry(vm, "full_name", val); else lua_push_str_table_entry(vm, "full_name", (char*) "unknown"); snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, username); if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) lua_push_str_table_entry(vm, "password", val); else lua_push_str_table_entry(vm, "password", (char*) "unknown"); snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username); if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) lua_push_str_table_entry(vm, "group", val); else lua_push_str_table_entry(vm, "group", (char*)"unknown"); snprintf(key, sizeof(key), CONST_STR_USER_NETS, username); if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) lua_push_str_table_entry(vm, CONST_ALLOWED_NETS, val); else lua_push_str_table_entry(vm, CONST_ALLOWED_NETS, (char*)""); snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_IFNAME, username); if((ntop->getRedis()->get(key, val, sizeof(val)) >= 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, sizeof(key), CONST_STR_USER_HOST_POOL_ID, username); if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) lua_push_int_table_entry(vm, "host_pool_id", atoi(val)); snprintf(key, sizeof(key), CONST_STR_USER_EXPIRE, username); if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) lua_push_float_table_entry(vm, "limited_lifetime", atoi(val)); lua_pushstring(vm, username); lua_insert(vm, -2); lua_settable(vm, -3); free(usernames[i]); } free(usernames); } /* ******************************************* */ void Ntop::getUserGroup(lua_State* vm) { char key[64], val[64]; char username[33]; struct mg_connection *conn; lua_getglobal(vm, CONST_HTTP_CONN); if((conn = (struct mg_connection*)lua_touserdata(vm, lua_gettop(vm))) == NULL) { ntop->getTrace()->traceEvent(TRACE_ERROR, "INTERNAL ERROR: null HTTP connection"); lua_pushstring(vm, (char*)"unknown"); return; } mg_get_cookie(conn, CONST_USER, username, sizeof(username)); if(!strncmp(username, NTOP_NOLOGIN_USER, sizeof(username))) { lua_pushstring(vm, CONST_USER_GROUP_ADMIN); return; } #if defined(NTOPNG_PRO) && defined(HAVE_LDAP) snprintf(key, sizeof(key), PREF_USER_TYPE_LOG, username); if (ntop->getRedis()->get(key, val, sizeof(val)) >= 0){ if( !strcmp(val,"ldap") ) { snprintf(key, sizeof(key), PREF_LDAP_GROUP_OF_USER, username); lua_pushstring(vm, (ntop->getRedis()->get(key, val, sizeof(val)) >= 0) ? val : (char*)"unknown"); return; } } #endif snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username); lua_pushstring(vm, (ntop->getRedis()->get(key, val, sizeof(val)) >= 0) ? val : (char*)"unknown"); } /* ******************************************* */ void Ntop::getAllowedNetworks(lua_State* vm) { char key[64], val[64]; char username[33]; struct mg_connection *conn; lua_getglobal(vm, CONST_HTTP_CONN); if((conn = (struct mg_connection*)lua_touserdata(vm, lua_gettop(vm))) == NULL) { ntop->getTrace()->traceEvent(TRACE_ERROR, "INTERNAL ERROR: null HTTP connection"); lua_pushstring(vm, (char*)""); return; } mg_get_cookie(conn, CONST_USER, username, sizeof(username)); snprintf(key, sizeof(key), CONST_STR_USER_NETS, username); lua_pushstring(vm, (ntop->getRedis()->get(key, val, sizeof(val)) >= 0) ? val : (char*)""); } /* ******************************************* */ bool Ntop::getInterfaceAllowed(lua_State* vm, char *ifname) const { char *allowed_ifname; if(ifname == NULL) { return false; } lua_getglobal(vm, CONST_ALLOWED_IFNAME); if((allowed_ifname = (char*)lua_touserdata(vm, lua_gettop(vm))) == NULL || allowed_ifname[0] == '\0') { ifname = NULL; return false; } strncpy(ifname, allowed_ifname, strlen(allowed_ifname)); 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 */ } lua_getglobal(vm, CONST_ALLOWED_IFNAME); if((allowed_ifname = (char*)lua_touserdata(vm, lua_gettop(vm))) == NULL || allowed_ifname[0] == '\0') { ntop->getTrace()->traceEvent(TRACE_DEBUG, "No allowed interface found for %s", ifname); // this is a lua script called within nopng (no HTTP UI and user interaction) ret = true; } else { ntop->getTrace()->traceEvent(TRACE_DEBUG, "Allowed interface %s, requested %s", allowed_ifname, ifname); ret = !strncmp(allowed_ifname, ifname, strlen(allowed_ifname)); } lua_pop(vm, 1); return ret; } /* ******************************************* */ bool Ntop::isInterfaceAllowed(lua_State* vm, int ifid) const { return isInterfaceAllowed(vm, prefs->get_if_name(ifid)); } /* ******************************************* */ // Return 1 if username/password is allowed, 0 otherwise. bool Ntop::checkUserPassword(const char *user, const char *password) { char key[64], val[64], password_hash[33]; #if defined(NTOPNG_PRO) && defined(HAVE_LDAP) bool localAuth = true; #endif if((user == NULL) || (user[0] == '\0')) return(false); #if defined(NTOPNG_PRO) && defined(HAVE_LDAP) if(ntop->getPro()->has_valid_license()) { if(ntop->getRedis()->get((char*)PREF_NTOP_AUTHENTICATION_TYPE, val, sizeof(val)) >= 0) { if(!strcmp(val, "ldap") /* LDAP only */) localAuth = false; if(strncmp(val, "ldap", 4) == 0) { bool is_admin; char ldapServer[64] = { 0 }, ldapAccountType[64] = { 0 }, ldapAnonymousBind[32] = { 0 }, bind_dn[128] = { 0 }, bind_pwd[64] = { 0 }, group[64] = { 0 }, search_path[128] = { 0 }, admin_group[64] = { 0 }; if(!password || !password[0]) return false; ntop->getRedis()->get((char*)PREF_LDAP_SERVER, ldapServer, sizeof(ldapServer)); ntop->getRedis()->get((char*)PREF_LDAP_ACCOUNT_TYPE, ldapAccountType, sizeof(ldapAccountType)); ntop->getRedis()->get((char*)PREF_LDAP_BIND_ANONYMOUS, ldapAnonymousBind, sizeof(ldapAnonymousBind)); ntop->getRedis()->get((char*)PREF_LDAP_BIND_DN, bind_dn, sizeof(bind_dn)); ntop->getRedis()->get((char*)PREF_LDAP_BIND_PWD, bind_pwd, sizeof(bind_pwd)); ntop->getRedis()->get((char*)PREF_LDAP_SEARCH_PATH, search_path, sizeof(search_path)); ntop->getRedis()->get((char*)PREF_LDAP_USER_GROUP, group, sizeof(group)); ntop->getRedis()->get((char*)PREF_LDAP_ADMIN_GROUP, admin_group, sizeof(admin_group)); if(ldapServer[0]) { bool 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, group[0] != '\0' ? group : NULL, admin_group[0] != '\0' ? admin_group : NULL, &is_admin); if(ret) { snprintf(key, sizeof(key), PREF_LDAP_GROUP_OF_USER, user); ntop->getRedis()->set(key, is_admin ? (char*)CONST_USER_GROUP_ADMIN : (char*)CONST_USER_GROUP_UNPRIVILEGED, 0); snprintf(key, sizeof(key), PREF_USER_TYPE_LOG, user); ntop->getRedis()->set(key, (char*)"ldap", 0); return(true); } } } } } if(!localAuth) return(false); #endif snprintf(key, sizeof(key), CONST_STR_USER_PASSWORD, user); if(ntop->getRedis()->get(key, val, sizeof(val)) < 0) { return(false); } else { mg_md5(password_hash, password, NULL); if(strcmp(password_hash, val) == 0) { #if defined(NTOPNG_PRO) && defined(HAVE_LDAP) snprintf(key, sizeof(key), PREF_USER_TYPE_LOG, user); ntop->getRedis()->set(key, (char*)"local", 0); #endif return(true); } else { return(false); } } } /* ******************************************* */ 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; } /* ******************************************* */ bool Ntop::resetUserPassword(char *username, char *old_password, char *new_password) { char key[64]; char password_hash[33]; if((old_password != NULL) && (old_password[0] != '\0')) { if(!checkUserPassword(username, old_password)) 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::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 * const username, const char * const 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::addUser(char *username, char *full_name, char *password, char *host_role, char *allowed_networks, char *allowed_ifname, char *host_pool_id) { char key[64], val[64]; char password_hash[33]; snprintf(key, sizeof(key), CONST_STR_USER_FULL_NAME, username); if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) return(false); // user already exists 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_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); 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); } return(true); } /* ******************************************* */ bool Ntop::addUserLifetime(const char * const username, u_int32_t lifetime_secs) { char key[64], val[64], lifetime_val[16]; snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username); if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) { snprintf(lifetime_val, sizeof(lifetime_val), "%u", lifetime_secs); snprintf(key, sizeof(key), CONST_STR_USER_EXPIRE, username); ntop->getRedis()->set(key, lifetime_val, 0); return(true); } return(false); } /* ******************************************* */ bool Ntop::clearUserLifetime(const char * const username) { char key[64], val[64]; snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username); if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) { snprintf(key, sizeof(key), CONST_STR_USER_EXPIRE, username); ntop->getRedis()->del(key); return(true); } return(false); } /* ******************************************* */ bool Ntop::hasUserLimitedLifetime(const char * const username, int32_t *lifetime_secs) { char key[64], val[64]; snprintf(key, sizeof(key), CONST_STR_USER_EXPIRE, username); if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0 && val[0] != '\0' /* Caching may set an empty string as default value */) { if(lifetime_secs) *lifetime_secs = atoi(val); return(true); } return(false); } /* ******************************************* */ bool Ntop::isCaptivePortalUser(const char * const 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 key[64]; 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_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_HOST_POOL_ID, username); ntop->getRedis()->del(key); snprintf(key, sizeof(key), CONST_STR_USER_EXPIRE, username); ntop->getRedis()->del(key); 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(char *username, char *buf, size_t buflen) { 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; } /* ******************************************* */ 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]; struct stat buf; #ifdef WIN32 const char *install_dir = (const char *)get_install_dir(); #endif if(strncmp(__path, "./", 2) == 0) { snprintf(_path, MAX_PATH, "%s/%s", startup_dir, &__path[2]); fixPath(_path); if(stat(_path, &buf) == 0) { free(__path); return(strdup(_path)); } } if((__path[0] == '/') || (__path[0] == '\\')) { /* Absolute paths */ if(stat(__path, &buf) == 0) { return(__path); } } else snprintf(_path, MAX_PATH, "%s", __path); /* relative paths */ for(int i=0; dirs[i] != NULL; i++) { char path[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; signal(SIGHUP, SIG_IGN); 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..."); #if 1 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); #endif } else { /* father */ ntop->getTrace()->traceEvent(TRACE_NORMAL, "Parent process is exiting (this is normal)"); 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); ntop->getTrace()->traceEvent(TRACE_NORMAL, "Setting local networks to %s", nets); address->setLocalNetworks(nets); free(nets); }; /* ******************************************* */ NetworkInterface* Ntop::getInterfaceById(int if_id) { for(int i=0; iget_id() == if_id) return(iface[i]); } return(NULL); } /* ******************************************* */ bool Ntop::isExistingInterface(char *name) { if(name == NULL) return(false); for(int i=0; iget_name(), name)) return(true); } return(false); } /* ******************************************* */ NetworkInterface* Ntop::getNetworkInterface(lua_State* vm, const char *name) { if (name == NULL) return NULL; /* This method accepts both interface names or Ids */ int if_id = atoi(name); char str[8]; snprintf(str, sizeof(str), "%d", if_id); if (strcmp(name, str) == 0) { /* name is a number */ return(getInterfaceById(if_id)); } /* if here, name is a string */ for (int i = 0; iget_name())) { NetworkInterface *ret_iface = isInterfaceAllowed(vm, iface[i]->get_name()) ? iface[i] : NULL; if (ret_iface != NULL) return(ret_iface); else goto iface_not_found; } } /* FIX: remove this for at some point, when endpoint is passed */ for (int i = 0; igetScriptName(); if (script != NULL && strcmp(script, name) == 0) return(iface[i]); } iface_not_found: /* Not found */ //if(!strcmp(name, "any")) return(getFirstInterface()); /* FIX: remove at some point */ return(NULL); }; /* ******************************************* */ int Ntop::getInterfaceIdByName(char *name) { if(name == NULL) { return(getFirstInterface()->get_id()); } else { /* This method accepts both interface names or Ids */ int if_id = atoi(name); char str[8]; snprintf(str, sizeof(str), "%d", if_id); if(strcmp(name, str) == 0) { /* name is a number */ NetworkInterface *iface = getNetworkInterface(if_id); if(iface != NULL) return(iface->get_id()); else return(-1); } for(int i=0; iget_name(), name) == 0) { return(iface[i]->get_id()); } } return(-1); } } /* ******************************************* */ void Ntop::reloadInterfacesLuaInterpreter() { for(int i=0; iforceLuaInterpreterReload(); } /* ******************************************* */ void Ntop::registerInterface(NetworkInterface *_if) { _if->checkAggregationMode(); for(int i=0; iget_name(), _if->get_name()) == 0) { ntop->getTrace()->traceEvent(TRACE_WARNING, "Skipping duplicated interface %s", _if->get_name()); delete _if; return; } } if(num_defined_interfaces < MAX_NUM_DEFINED_INTERFACES) { ntop->getTrace()->traceEvent(TRACE_NORMAL, "Registered interface %s [id: %d]", _if->get_name(), _if->get_id()); iface[num_defined_interfaces++] = _if; _if->startDBLoop(); } else ntop->getTrace()->traceEvent(TRACE_ERROR, "Too many interfaces defined"); }; /* ******************************************* */ void Ntop::runHousekeepingTasks() { if(globals->isShutdown()) return; for(int i=0; irunHousekeepingTasks(); /* ES stats are updated once as the present implementation is not per-interface */ if (ntop->getPrefs()->do_dump_flows_on_es()) { struct timeval tv; gettimeofday(&tv, NULL); ntop->getElasticSearch()->updateStats(&tv); } if(ntop->getPrefs()->do_dump_flows_on_ls()){ struct timeval tv; gettimeofday(&tv, NULL); ntop->getLogstash()->updateStats(&tv); } } /* ******************************************* */ void Ntop::shutdown() { for(int i=0; igetStats(); stats->print(); iface[i]->shutdown(); ntop->getTrace()->traceEvent(TRACE_NORMAL, "Interface %s [running: %d]", iface[i]->get_name(), iface[i]->isRunning()); } } /* ******************************************* */ void Ntop::allocHostBlacklist() { if(hostBlacklistShadow != NULL) { delete hostBlacklistShadow; hostBlacklistShadow = NULL; } hostBlacklistShadow = new AddressTree(); } /* ******************************************* */ void Ntop::swapHostBlacklist() { AddressTree *cp = hostBlacklist; hostBlacklist = hostBlacklistShadow; hostBlacklistShadow = cp; } /* ******************************************* */ void Ntop::addToHostBlacklist(char *net) { if(hostBlacklistShadow) { ntop->getTrace()->traceEvent(TRACE_INFO, "Loading blacklist %s", net); hostBlacklistShadow->addAddresses(net); } } /* ******************************************* */ bool Ntop::isBlacklistedIP(IpAddress *ip) { bool rc = (hostBlacklist && ip->findAddress(hostBlacklist)) ? true : false; // if(rc) ntop->getTrace()->traceEvent(TRACE_NORMAL, "*** Found blacklist [%p]", n); return(rc); } /* ******************************************* */ #ifdef NTOPNG_PRO bool Ntop::addIPToLRUMatches(u_int32_t client_ip, u_int16_t user_pool_id, char *label, int32_t lifetime_secs, char *ifname) { for(int i=0; iis_bridge_interface() && (strcmp(iface[i]->get_name(), ifname) == 0)) { iface[i]->addIPToLRUMatches(client_ip, user_pool_id, label, lifetime_secs); return true; } } return false; } #endif