mirror of
https://github.com/ntop/ntopng.git
synced 2026-05-07 22:18:04 +00:00
1404 lines
40 KiB
C++
1404 lines
40 KiB
C++
/*
|
|
*
|
|
* (C) 2013-23 - 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"
|
|
|
|
#ifndef _GETOPT_H
|
|
#define _GETOPT_H
|
|
#endif
|
|
|
|
#ifndef LIB_VERSION
|
|
#define LIB_VERSION "1.4.7"
|
|
#endif
|
|
|
|
struct keyval string_to_replace[MAX_NUM_HTTP_REPLACEMENTS] = { { NULL, NULL } }; /* TODO remove */
|
|
|
|
extern luaL_Reg *ntop_interface_reg;
|
|
extern luaL_Reg *ntop_reg;
|
|
extern luaL_Reg *ntop_network_reg;
|
|
extern luaL_Reg *ntop_flow_reg;
|
|
extern luaL_Reg *ntop_host_reg;
|
|
|
|
#define HTTP_MAX_UPLOAD_DATA_LEN 25000000 /* ~25MB (see also upload_pcap.template) */
|
|
|
|
/* ******************************* */
|
|
|
|
struct ntopngLuaContext* getUserdata(struct lua_State *vm) {
|
|
if(vm) {
|
|
struct ntopngLuaContext *userdata;
|
|
|
|
lua_getglobal(vm, "userdata");
|
|
userdata = (struct ntopngLuaContext*) lua_touserdata(vm, lua_gettop(vm));
|
|
lua_pop(vm, 1); // undo the push done by lua_getglobal
|
|
|
|
return(userdata);
|
|
} else
|
|
return(NULL);
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
#ifdef DUMP_STACK
|
|
static void stackDump(lua_State *L) {
|
|
int i;
|
|
int top = lua_gettop(L);
|
|
|
|
for(i = 1; i <= top; i++) { /* repeat for each level */
|
|
int t = lua_type(L, i);
|
|
|
|
switch(t) {
|
|
case LUA_TSTRING: /* strings */
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "%u) %s", i, lua_tostring(L, i));
|
|
break;
|
|
|
|
case LUA_TBOOLEAN: /* booleans */
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "%u) %s", i, lua_toboolean(L, i) ? "true" : "false");
|
|
break;
|
|
|
|
case LUA_TNUMBER: /* numbers */
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "%u) %g", i, lua_tonumber(L, i));
|
|
break;
|
|
|
|
default: /* other values */
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "%u) %s", i, lua_typename(L, t));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* ******************************* */
|
|
|
|
LuaEngine::LuaEngine(lua_State *vm) {
|
|
std::bad_alloc bax;
|
|
void *ctx;
|
|
|
|
loaded_script_path = NULL;
|
|
is_system_vm = false;
|
|
|
|
L = luaL_newstate();
|
|
|
|
if(!L) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to create a new Lua state.");
|
|
throw bax;
|
|
}
|
|
|
|
ctx = (void*)calloc(1, sizeof(struct ntopngLuaContext));
|
|
|
|
if(!ctx) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR,
|
|
"Unable to create a context for the new Lua state.");
|
|
lua_close(L);
|
|
throw bax;
|
|
}
|
|
|
|
lua_pushlightuserdata(L, ctx);
|
|
lua_setglobal(L, "userdata");
|
|
|
|
if(vm)
|
|
setThreadedActivityData(vm);
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
LuaEngine::~LuaEngine() {
|
|
if(L) {
|
|
struct ntopngLuaContext *ctx;
|
|
|
|
#ifdef DUMP_STACK
|
|
stackDump(L);
|
|
#endif
|
|
|
|
ctx = getLuaVMContext(L);
|
|
|
|
if(ctx) {
|
|
#ifndef HAVE_NEDGE
|
|
if(ctx->snmpBatch) delete ctx->snmpBatch;
|
|
|
|
for(u_int8_t slot_id=0; slot_id<MAX_NUM_ASYNC_SNMP_ENGINES; slot_id++) {
|
|
if(ctx->snmpAsyncEngine[slot_id] != NULL)
|
|
delete ctx->snmpAsyncEngine[slot_id];
|
|
}
|
|
#endif
|
|
|
|
if(ctx->pkt_capture.end_capture > 0) {
|
|
ctx->pkt_capture.end_capture = 0; /* Force stop */
|
|
pthread_join(ctx->pkt_capture.captureThreadLoop, NULL);
|
|
}
|
|
|
|
if((ctx->iface != NULL) && ctx->live_capture.pcaphdr_sent)
|
|
ctx->iface->deregisterLiveCapture(ctx);
|
|
|
|
if(ctx->addr_tree != NULL)
|
|
delete ctx->addr_tree;
|
|
|
|
if(ctx->sqlite_hosts_filter)
|
|
free(ctx->sqlite_hosts_filter);
|
|
|
|
if(ctx->sqlite_flows_filter)
|
|
free(ctx->sqlite_flows_filter);
|
|
|
|
#if defined(NTOPNG_PRO)
|
|
if(ctx->bin)
|
|
delete ctx->bin;
|
|
#endif
|
|
free(ctx);
|
|
}
|
|
|
|
lua_close(L);
|
|
}
|
|
|
|
if(loaded_script_path) free(loaded_script_path);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
void LuaEngine::luaRegister(lua_State *L, const char *class_name, luaL_Reg *class_methods) {
|
|
const luaL_Reg _meta[] = { { NULL, NULL } };
|
|
int lib_id, meta_id;
|
|
|
|
/* newclass = {} */
|
|
lua_createtable(L, 0, 0);
|
|
lib_id = lua_gettop(L);
|
|
|
|
/* metatable = {} */
|
|
luaL_newmetatable(L, class_name);
|
|
meta_id = lua_gettop(L);
|
|
luaL_setfuncs(L, _meta, 0);
|
|
|
|
/* metatable.__index = class_methods */
|
|
lua_newtable(L);
|
|
luaL_setfuncs(L, class_methods, 0);
|
|
lua_setfield(L, meta_id, "__index");
|
|
|
|
/* class.__metatable = metatable */
|
|
lua_setmetatable(L, lib_id);
|
|
|
|
/* _G["Foo"] = newclass */
|
|
lua_setglobal(L, class_name);
|
|
}
|
|
|
|
/* ******************************* */
|
|
|
|
int ntop_lua_return_value(lua_State* vm, const char *function_name, int val) {
|
|
bool show_warning;
|
|
|
|
switch(val) {
|
|
case CONST_LUA_OK:
|
|
show_warning = true;
|
|
break;
|
|
|
|
default:
|
|
show_warning = false; /* CONST_LUA_PARAM_ERROR and CONST_LUA_ERROR */
|
|
break;
|
|
}
|
|
|
|
if(lua_gettop(vm) == 0) {
|
|
if(show_warning) {
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "[INTERNAL ERROR] Invalid lua VM state returned by %s()", function_name);
|
|
ntop->getTrace()->traceEvent(TRACE_ERROR, "[INTERNAL ERROR] Please report it here https://github.com/ntop/ntopng/issues");
|
|
}
|
|
|
|
lua_pushnil(vm); /* Add dummy push to make sure the stack has a return value */
|
|
}
|
|
|
|
return(val);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
static int ntop_lua_http_print(lua_State* vm) {
|
|
struct mg_connection *conn;
|
|
char *printtype;
|
|
int t;
|
|
|
|
conn = getLuaVMUserdata(vm, conn);
|
|
|
|
/* ntop->getTrace()->traceEvent(TRACE_DEBUG, "%s() called", __FUNCTION__); */
|
|
|
|
/* Handle binary blob */
|
|
if((lua_type(vm, 2) == LUA_TSTRING)
|
|
&& (printtype = (char*)lua_tostring(vm, 2)) != NULL)
|
|
if(!strncmp(printtype, "blob", 4)) {
|
|
char *str = NULL;
|
|
|
|
if(ntop_lua_check(vm, __FUNCTION__, 1, LUA_TSTRING) != CONST_LUA_OK) return(CONST_LUA_ERROR);
|
|
if((str = (char*)lua_tostring(vm, 1)) != NULL) {
|
|
int len = strlen(str);
|
|
|
|
if(len <= 1)
|
|
mg_printf(conn, "%c", str[0]);
|
|
else
|
|
return(CONST_LUA_PARAM_ERROR);
|
|
}
|
|
|
|
lua_pushnil(vm);
|
|
return(CONST_LUA_OK);
|
|
}
|
|
|
|
switch(t = lua_type(vm, 1)) {
|
|
case LUA_TNIL:
|
|
mg_printf(conn, "%s", "nil");
|
|
break;
|
|
|
|
case LUA_TBOOLEAN:
|
|
{
|
|
int v = lua_toboolean(vm, 1);
|
|
|
|
mg_printf(conn, "%s", v ? "true" : "false");
|
|
}
|
|
break;
|
|
|
|
case LUA_TSTRING:
|
|
{
|
|
char *str = (char*)lua_tostring(vm, 1);
|
|
|
|
if(str && (strlen(str) > 0))
|
|
mg_printf(conn, "%s", str);
|
|
}
|
|
break;
|
|
|
|
case LUA_TNUMBER:
|
|
{
|
|
char str[64];
|
|
|
|
snprintf(str, sizeof(str), "%f", (float)lua_tonumber(vm, 1));
|
|
mg_printf(conn, "%s", str);
|
|
}
|
|
break;
|
|
|
|
case LUA_TTABLE:
|
|
{
|
|
lua_pushnil(vm);
|
|
|
|
while(lua_next(vm, -2) != 0) {
|
|
const char *key = lua_tostring(vm, -2);
|
|
|
|
if(lua_isstring(vm, -1))
|
|
mg_printf(conn, "%s = %s", key, lua_tostring(vm, -1));
|
|
else if(lua_isnumber(vm, -1))
|
|
mg_printf(conn, "%s = %d", key, (int)lua_tonumber(vm, -1));
|
|
else if(lua_istable(vm, -1)) {
|
|
mg_printf(conn, "%s", key);
|
|
// PrintTable(vm);
|
|
}
|
|
lua_pop(vm, 1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(): Lua type %d is not handled",
|
|
__FUNCTION__, t);
|
|
return(CONST_LUA_ERROR);
|
|
}
|
|
|
|
lua_pushnil(vm);
|
|
return(CONST_LUA_OK);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
int ntop_lua_cli_print(lua_State* vm) {
|
|
int t;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "%s() called", __FUNCTION__);
|
|
|
|
switch(t = lua_type(vm, 1)) {
|
|
case LUA_TSTRING:
|
|
{
|
|
char *str = (char*)lua_tostring(vm, 1);
|
|
|
|
if(str && (strlen(str) > 0))
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "%s", str);
|
|
}
|
|
break;
|
|
|
|
case LUA_TNUMBER:
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "%f", (float)lua_tonumber(vm, 1));
|
|
break;
|
|
|
|
default:
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(): Lua type %d is not handled",
|
|
__FUNCTION__, t);
|
|
return(CONST_LUA_ERROR);
|
|
}
|
|
|
|
lua_pushnil(vm);
|
|
return(CONST_LUA_OK);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
#if defined(NTOPNG_PRO) || defined(HAVE_NEDGE)
|
|
|
|
static int __ntop_lua_handlefile(lua_State* L, char *script_path, bool ex) {
|
|
int rc;
|
|
LuaHandler *lh = new LuaHandler(L, script_path);
|
|
|
|
#ifdef DEBUG_LUA_LOADING
|
|
ntop->getTrace()->traceEvent(TRACE_NORMAL, "Loading %s", script_path);
|
|
#endif
|
|
|
|
rc = lh->luaL_dofileM(ex);
|
|
delete lh;
|
|
return rc;
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
/* This function is called by Lua scripts when the call require(...) */
|
|
static int ntop_lua_require(lua_State* L) {
|
|
char *script_name;
|
|
|
|
if(lua_type(L, 1) != LUA_TSTRING ||
|
|
(script_name = (char*)lua_tostring(L, 1)) == NULL)
|
|
return 0;
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "%s(%s)", __FUNCTION__, script_name);
|
|
|
|
lua_getglobal( L, "package" );
|
|
lua_getfield( L, -1, "path" );
|
|
|
|
string cur_path = lua_tostring( L, -1 ), parsed, script_path = "";
|
|
stringstream input_stringstream(cur_path);
|
|
while(getline(input_stringstream, parsed, ';')) {
|
|
/* Example: package.path = dirs.installdir .. "/scripts/lua/modules/?.lua;" .. package.path */
|
|
unsigned found = parsed.find_last_of("?");
|
|
if(found) {
|
|
string s = parsed.substr(0, found) + script_name + ".lua";
|
|
size_t first_dot = s.find("."), last_dot = s.rfind(".");
|
|
|
|
/*
|
|
Lua transforms file names when directories are used.
|
|
Example: i18n/version.lua -> i18n.version.lua
|
|
|
|
So we need to revert this logic back and the code
|
|
below is doing exactly this
|
|
*/
|
|
if((first_dot != string::npos)
|
|
&& (last_dot != string::npos)
|
|
&& (first_dot != last_dot))
|
|
s.replace(first_dot, 1, "/");
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "[%s] Searching %s", __FUNCTION__, s.c_str());
|
|
|
|
if(Utils::file_exists(s.c_str())) {
|
|
script_path = s;
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "[%s] Found %s", __FUNCTION__, s.c_str());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(script_path == "" ||
|
|
__ntop_lua_handlefile(L, (char *)script_path.c_str(), false)) {
|
|
if(lua_type(L, -1) == LUA_TSTRING) {
|
|
const char *err = lua_tostring(L, -1);
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure [%s][%s]",
|
|
script_path.c_str(), err ? err : "");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
static int ntop_lua_xfile(lua_State* L, bool ex) {
|
|
char *script_path;
|
|
int ret;
|
|
|
|
if(lua_type(L, 1) != LUA_TSTRING ||
|
|
(script_path = (char*)lua_tostring(L, 1)) == NULL)
|
|
return 0;
|
|
|
|
ret = __ntop_lua_handlefile(L, script_path, ex);
|
|
|
|
if(ret && (!lua_isnil(L, -1))) {
|
|
const char *msg = lua_tostring(L, -1);
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure %s", msg);
|
|
}
|
|
|
|
return !ret;
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
static int ntop_lua_dofile(lua_State* L) {
|
|
return ntop_lua_xfile(L, true);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
static int ntop_lua_loadfile(lua_State* L) {
|
|
return ntop_lua_xfile(L, false);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ****************************************** */
|
|
|
|
void LuaEngine::lua_register_classes(lua_State *L, bool http_mode) {
|
|
if(!L) return;
|
|
|
|
/* ntop add-ons */
|
|
luaRegister(L, "interface", ntop_interface_reg);
|
|
luaRegister(L, "ntop", ntop_reg);
|
|
luaRegister(L, "network", ntop_network_reg);
|
|
luaRegister(L, "flow", ntop_flow_reg);
|
|
luaRegister(L, "host", ntop_host_reg);
|
|
|
|
if(http_mode) {
|
|
/* Overload the standard Lua print() with ntop_lua_http_print that dumps data on HTTP server */
|
|
lua_register(L, "print", ntop_lua_http_print);
|
|
} else
|
|
lua_register(L, "print", ntop_lua_cli_print);
|
|
|
|
#if defined(NTOPNG_PRO) || defined(HAVE_NEDGE)
|
|
if(ntop->getPro()->has_valid_license()) {
|
|
lua_register(L, "ntopRequire", ntop_lua_require);
|
|
/* Lua 5.2.x uses package.loaders */
|
|
luaL_dostring(L, "package.loaders = { ntopRequire }");
|
|
/* Lua 5.3.x uses package.searchers */
|
|
luaL_dostring(L, "package.searchers = { ntopRequire }");
|
|
lua_register(L, "dofile", ntop_lua_dofile);
|
|
lua_register(L, "loadfile", ntop_lua_loadfile);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
#if 0
|
|
/**
|
|
* Iterator over key-value pairs where the value
|
|
* maybe made available in increments and/or may
|
|
* not be zero-terminated. Used for processing
|
|
* POST data.
|
|
*
|
|
* @param cls user-specified closure
|
|
* @param kind type of the value
|
|
* @param key 0-terminated key for the value
|
|
* @param filename name of the uploaded file, NULL if not known
|
|
* @param content_type mime-type of the data, NULL if not known
|
|
* @param transfer_encoding encoding of the data, NULL if not known
|
|
* @param data pointer to size bytes of data at the
|
|
* specified offset
|
|
* @param off offset of data in the overall value
|
|
* @param size number of bytes in data available
|
|
* @return MHD_YES to continue iterating,
|
|
* MHD_NO to abort the iteration
|
|
*/
|
|
static int post_iterator(void *cls,
|
|
enum MHD_ValueKind kind,
|
|
const char *key,
|
|
const char *filename,
|
|
const char *content_type,
|
|
const char *transfer_encoding,
|
|
const char *data, uint64_t off, size_t size)
|
|
{
|
|
struct Request *request = cls;
|
|
char tmp[1024];
|
|
u_int len = min(size, sizeof(tmp)-1);
|
|
|
|
memcpy(tmp, &data[off], len);
|
|
tmp[len] = '\0';
|
|
|
|
fprintf(stdout, "[POST] [%s][%s]\n", key, tmp);
|
|
return MHD_YES;
|
|
}
|
|
#endif
|
|
|
|
/* ****************************************** */
|
|
|
|
/* Loads a script into the engine from within ntopng (no HTTP GUI). */
|
|
int LuaEngine::load_script(char *script_path, NetworkInterface *iface) {
|
|
int rc = 0;
|
|
bool initialized;
|
|
|
|
if(!L) return(-1);
|
|
|
|
if(loaded_script_path)
|
|
free(loaded_script_path), initialized = true;
|
|
else
|
|
initialized = false;
|
|
|
|
try {
|
|
if(!initialized) {
|
|
luaL_openlibs(L); /* Load base libraries */
|
|
lua_register_classes(L, false); /* Load custom classes */
|
|
} else
|
|
lua_settop(L, lua_gettop(L)); /* Reset the stack */
|
|
|
|
if(iface) {
|
|
/* Select the specified interface */
|
|
getLuaVMUservalue(L, iface) = iface;
|
|
}
|
|
|
|
if(isSystemVM())
|
|
getLuaVMUservalue(L, capabilities) = (u_int64_t)-1; /* All set */
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(ntop->getPro()->has_valid_license())
|
|
rc = __ntop_lua_handlefile(L, script_path, false /* Do not execute */);
|
|
else
|
|
#endif
|
|
rc = luaL_loadfile(L, script_path);
|
|
|
|
if(rc != 0) {
|
|
const char *err = lua_tostring(L, -1);
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure [%s][%s]", script_path, err ? err : "");
|
|
rc = -1;
|
|
}
|
|
} catch(...) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure [%s]", script_path);
|
|
rc = -2;
|
|
}
|
|
|
|
loaded_script_path = strdup(script_path);
|
|
|
|
return(rc);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
/*
|
|
Run a loaded Lua script (via LuaEngine::load_script) from within ntopng (no HTTP GUI).
|
|
The script is invoked without any additional parameters. run_loaded_script can be called
|
|
multiple times to run the same script again.
|
|
*/
|
|
int LuaEngine::run_loaded_script() {
|
|
int rv = 0;
|
|
|
|
if(!loaded_script_path)
|
|
return(-1);
|
|
|
|
/* Copy the lua_chunk to be able to possibly run it again next time */
|
|
lua_pushvalue(L, -1);
|
|
|
|
/* Perform the actual call */
|
|
if(lua_pcall(L, 0, LUA_MULTRET /* Allow the script to be called multiple times */, 0) != 0) {
|
|
if(lua_type(L, -1) == LUA_TSTRING) {
|
|
const char *err = lua_tostring(L, -1);
|
|
if(!loaded_script_path && !err)
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure [%s][%s]", loaded_script_path, err);
|
|
|
|
}
|
|
|
|
rv = -2;
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return(rv);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
/* http://www.geekhideout.com/downloads/urlcode.c */
|
|
|
|
#if 0
|
|
/* Converts an integer value to its hex character*/
|
|
static char to_hex(char code) {
|
|
static char hex[] = "0123456789abcdef";
|
|
return hex[code & 15];
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
/* Returns a url-encoded version of str */
|
|
/* IMPORTANT: be sure to free() the returned string after use */
|
|
static char* http_encode(char *str) {
|
|
char *pstr = str, *buf = (char*)malloc(strlen(str) * 3 + 1), *pbuf = buf;
|
|
while (*pstr) {
|
|
if(isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
|
|
*pbuf++ = *pstr;
|
|
else if(*pstr == ' ')
|
|
*pbuf++ = '+';
|
|
else
|
|
*pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
|
|
pstr++;
|
|
}
|
|
*pbuf = '\0';
|
|
return buf;
|
|
}
|
|
#endif
|
|
|
|
/* ****************************************** */
|
|
|
|
#ifdef NOT_USED
|
|
|
|
void LuaEngine::purifyHTTPParameter(char *param) {
|
|
char *ampersand;
|
|
bool utf8_found = false;
|
|
|
|
if((ampersand = strchr(param, '%')) != NULL) {
|
|
/* We allow only a few chars, removing all the others */
|
|
|
|
if((ampersand[1] != 0) && (ampersand[2] != 0)) {
|
|
char c;
|
|
char b = ampersand[3];
|
|
|
|
ampersand[3] = '\0';
|
|
c = (char)strtol(&ersand[1], NULL, 16);
|
|
ampersand[3] = b;
|
|
|
|
switch(c) {
|
|
case '/':
|
|
case ':':
|
|
case '(':
|
|
case ')':
|
|
case '{':
|
|
case '}':
|
|
case '[':
|
|
case ']':
|
|
case '?':
|
|
case '!':
|
|
case '$':
|
|
case ',':
|
|
case '^':
|
|
case '*':
|
|
case '_':
|
|
case '&':
|
|
case ' ':
|
|
case '=':
|
|
case '<':
|
|
case '>':
|
|
case '@':
|
|
case '#':
|
|
break;
|
|
|
|
default:
|
|
if(((u_char)c == 0xC3) && (ampersand[3] == '%')) {
|
|
/* Latin-1 within UTF-8 */
|
|
b = ampersand[6];
|
|
ampersand[6] = '\0';
|
|
c = (char)strtol(&ersand[4], NULL, 16);
|
|
ampersand[6] = b;
|
|
|
|
/* Align to ASCII encoding */
|
|
c |= 0x40;
|
|
utf8_found = true;
|
|
}
|
|
|
|
if(!Utils::isPrintableChar(c)) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Discarded char '0x%02x' in URI [%s]", c, param);
|
|
ampersand[0] = '\0';
|
|
return;
|
|
}
|
|
}
|
|
|
|
purifyHTTPParameter(utf8_found ? &ersand[6] : &ersand[3]);
|
|
} else
|
|
ampersand[0] = '\0';
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* ****************************************** */
|
|
|
|
bool LuaEngine::switchInterface(struct lua_State *vm, const char *ifid, const char *observation_point_id,
|
|
const char *user, const char *group,
|
|
const char *session) {
|
|
NetworkInterface *iface = NULL;
|
|
char iface_key[64], ifname_key[64], obs_id_key[64];
|
|
char iface_id[16];
|
|
|
|
iface = ntop->getNetworkInterface(ifid, vm);
|
|
|
|
if(iface == NULL)
|
|
return false;
|
|
|
|
if(user != NULL) {
|
|
if(!strlen(session) && strcmp(user, NTOP_NOLOGIN_USER))
|
|
return false;
|
|
|
|
/* Non-admins cannot switch to the system interface */
|
|
if(iface == ntop->getSystemInterface()
|
|
&& strcmp(user, NTOP_NOLOGIN_USER)
|
|
&& strncmp(group, CONST_USER_GROUP_ADMIN, strlen(CONST_USER_GROUP_ADMIN)))
|
|
return false;
|
|
|
|
snprintf(iface_key, sizeof(iface_key), NTOPNG_PREFS_PREFIX ".%s.iface", user);
|
|
snprintf(ifname_key, sizeof(ifname_key), NTOPNG_PREFS_PREFIX ".%s.ifname", user);
|
|
} else { // Login disabled
|
|
snprintf(iface_key, sizeof(iface_key), NTOPNG_PREFS_PREFIX ".iface");
|
|
snprintf(ifname_key, sizeof(ifname_key), NTOPNG_PREFS_PREFIX ".ifname");
|
|
}
|
|
|
|
snprintf(iface_id, sizeof(iface_id), "%d", iface->get_id());
|
|
ntop->getRedis()->set(iface_key, iface_id, 0);
|
|
ntop->getRedis()->set(ifname_key, iface->get_name(), 0);
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"Switch interface requested via POST [Interface: %s][user: %s]",
|
|
iface->get_name(), user);
|
|
|
|
getLuaVMUservalue(vm, iface) = iface;
|
|
|
|
/* Finally, also set the observation point */
|
|
if(observation_point_id[0]) {
|
|
u_int16_t obs_id = (u_int16_t)strtoll(observation_point_id, NULL, 10);
|
|
|
|
if(iface->hasObservationPointId(obs_id)) {
|
|
snprintf(obs_id_key, sizeof(obs_id_key), NTOPNG_PREFS_PREFIX ".%s.observationPointId", user);
|
|
|
|
ntop->getRedis()->set(obs_id_key, observation_point_id);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
void LuaEngine::setInterface(const char * user, char * const ifname,
|
|
u_int16_t ifname_len, bool * const is_allowed) const {
|
|
NetworkInterface *iface = NULL;
|
|
char key[CONST_MAX_LEN_REDIS_KEY];
|
|
u_int16_t observationPointId = 0;
|
|
|
|
ifname[0] = '\0';
|
|
|
|
if((user == NULL) || (user[0] == '\0'))
|
|
user = NTOP_NOLOGIN_USER;
|
|
|
|
if(is_allowed) *is_allowed = false;
|
|
|
|
// check if the user is restricted to browse only a given interface
|
|
if(snprintf(key, sizeof(key), CONST_STR_USER_ALLOWED_IFNAME, user)
|
|
&& ntop->getRedis()->get(key, ifname, ifname_len) == 0
|
|
&& ifname[0] != '\0') {
|
|
/* If here is only one allowed interface for the user.
|
|
The interface must exists otherwise we hould have prevented the login */
|
|
if(is_allowed) *is_allowed = true;
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Allowed interface found. [Interface: %s][user: %s]",
|
|
ifname, user);
|
|
} else if(snprintf(key, sizeof(key), NTOPNG_PREFS_PREFIX ".%s.ifname", user)
|
|
&& (ntop->getRedis()->get(key, ifname, ifname_len) < 0
|
|
|| (!ntop->isExistingInterface(ifname)))) {
|
|
/* No allowed interface and no default (or not existing) set interface */
|
|
snprintf(ifname, ifname_len, "%s",
|
|
ntop->getFirstInterface()->get_name());
|
|
ntop->getRedis()->set(key, ifname, 3600 /* 1h */);
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG,
|
|
"No interface interface found. Using default. [Interface: %s][user: %s]",
|
|
ifname, user);
|
|
}
|
|
|
|
if((iface = ntop->getNetworkInterface(ifname, NULL /* allowed user interface check already enforced */)) != NULL) {
|
|
/* The specified interface still exists */
|
|
lua_push_str_table_entry(L, "ifname", iface->get_name());
|
|
snprintf(ifname, ifname_len, "%s", iface->get_name());
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Interface found [Interface: %s][user: %s]", iface->get_name(), user);
|
|
}
|
|
|
|
if(iface && iface->haveObservationPointsDefined()) {
|
|
char buf[16];
|
|
|
|
snprintf(key, sizeof(key), NTOPNG_PREFS_PREFIX ".%s.observationPointId", user);
|
|
|
|
if(ntop->getRedis()->get(key, buf, sizeof(buf)) != -1) {
|
|
observationPointId = atoi(buf);
|
|
|
|
if(!iface->hasObservationPointId(observationPointId))
|
|
observationPointId = iface->getFirstObservationPointId(); /* Not existing */
|
|
} else
|
|
observationPointId = iface->getFirstObservationPointId(); /* Not found */
|
|
}
|
|
|
|
/* Set the observationPointId in the VM */
|
|
getLuaVMUservalue(L, observationPointId) = observationPointId;
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "observationPointId: %u", observationPointId);
|
|
|
|
lua_push_uint32_table_entry(L, "observationPointId", observationPointId);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
bool LuaEngine::setParamsTable(lua_State* vm,
|
|
const struct mg_request_info *request_info,
|
|
const char* table_name,
|
|
const char* query) const {
|
|
char *where;
|
|
char *tok;
|
|
char *query_string = query ? strdup(query) : NULL;
|
|
bool ret = false;
|
|
|
|
lua_newtable(vm);
|
|
|
|
if(query_string
|
|
&& strcmp(request_info->uri, CAPTIVE_PORTAL_INFO_URL) /* Ignore informative portal */
|
|
) {
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "[HTTP] %s", query_string);
|
|
|
|
tok = strtok_r(query_string, "&", &where);
|
|
|
|
while(tok != NULL) {
|
|
char *_equal;
|
|
|
|
if(strncmp(tok, "csrf", strlen("csrf")) != 0 /* Do not put csrf into the params table */
|
|
&& strncmp(tok, "switch_interface", strlen("switch_interface")) != 0
|
|
&& (_equal = strchr(tok, '='))){
|
|
char *decoded_buf;
|
|
int len;
|
|
|
|
_equal[0] = '\0';
|
|
_equal = &_equal[1];
|
|
len = strlen(_equal);
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "%s = %s", tok, _equal);
|
|
|
|
if((decoded_buf = (char*)malloc(len+1)) != NULL) {
|
|
bool rsp = false;
|
|
|
|
Utils::urlDecode(_equal, decoded_buf, len + 1);
|
|
|
|
rsp = Utils::purifyHTTPparam(tok, true, false, false);
|
|
/* don't purify decoded_buf, it's purified in lua */
|
|
|
|
if(rsp) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "[HTTP] Invalid '%s'", query);
|
|
ret = true;
|
|
}
|
|
|
|
/* Now make sure that decoded_buf is not a file path */
|
|
if(strchr(decoded_buf, CONST_PATH_SEP)
|
|
&& Utils::file_exists(decoded_buf)
|
|
&& !Utils::dir_exists(decoded_buf)
|
|
&& strcmp(tok, "pid_name") /* This is the only exception */
|
|
)
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Discarded '%s'='%s' as argument is a valid file path",
|
|
tok, decoded_buf);
|
|
else
|
|
lua_push_str_table_entry(vm, tok, decoded_buf);
|
|
|
|
free(decoded_buf);
|
|
} else
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
|
|
}
|
|
|
|
tok = strtok_r(NULL, "&", &where);
|
|
} /* while */
|
|
}
|
|
|
|
if(query_string) free(query_string);
|
|
|
|
if(table_name)
|
|
lua_setglobal(L, table_name);
|
|
else
|
|
lua_setglobal(L, (char*)"_GET"); /* Default */
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
void build_redirect(const char *url, const char * query_string,
|
|
char *buf, size_t bufsize) {
|
|
snprintf(buf, bufsize, "HTTP/1.1 302 Found\r\n"
|
|
"Server: ntopng %s (%s)\r\n"
|
|
"Location: %s%s%s\r\n\r\n"
|
|
"<html>\n"
|
|
"<head>\n"
|
|
"<title>Moved</title>\n"
|
|
"</head>\n"
|
|
"<body>\n"
|
|
"<h1>Moved</h1>\n"
|
|
"<p>This page has moved to <a href=\"%s\">%s</a>.</p>\n"
|
|
"</body>\n"
|
|
"</html>\n",
|
|
PACKAGE_VERSION, PACKAGE_MACHINE,
|
|
url,
|
|
query_string ? "?" : "",
|
|
query_string ? query_string : "",
|
|
url, url);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
int LuaEngine::handle_script_request(struct mg_connection *conn,
|
|
const struct mg_request_info *request_info,
|
|
char *script_path, bool *attack_attempt,
|
|
const char *user,
|
|
const char *group,
|
|
const char *session_csrf,
|
|
bool localuser) {
|
|
NetworkInterface *iface = NULL;
|
|
char key[64], ifname[MAX_INTERFACE_NAME_LEN];
|
|
bool is_interface_allowed;
|
|
AddressTree ptree;
|
|
int rc, post_data_len;
|
|
const char * content_type;
|
|
u_int8_t valid_csrf = 1;
|
|
char *post_data = NULL;
|
|
char csrf[64] = { '\0' };
|
|
char switch_interface[2] = { '\0' };
|
|
char addr_buf[64];
|
|
char session_buf[64];
|
|
char ifid_buf[32], obs_id_buf[16], session_key[32];
|
|
const char* origin_header;
|
|
bool send_redirect = false;
|
|
IpAddress client_addr;
|
|
int num_uploaded_files = 0;
|
|
|
|
*attack_attempt = false;
|
|
|
|
if(!L) return(-1);
|
|
|
|
luaL_openlibs(L); /* Load base libraries */
|
|
lua_register_classes(L, true); /* Load custom classes */
|
|
|
|
getLuaVMUservalue(L, conn) = conn;
|
|
|
|
content_type = mg_get_header(conn, "Content-Type");
|
|
Utils::make_session_key(session_key, sizeof(session_key));
|
|
mg_get_cookie(conn, session_key, session_buf, sizeof(session_buf));
|
|
|
|
/* Check for POST requests */
|
|
if((strcmp(request_info->request_method, "POST") == 0) && (content_type != NULL)) {
|
|
int content_len = mg_get_content_len(conn) + 1;
|
|
bool is_file_upload = (strncmp(content_type, "multipart/form-data", 19) == 0) ? true : false;
|
|
|
|
if((!is_file_upload) && (content_len > HTTP_MAX_POST_DATA_LEN)) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING,
|
|
"Too much data submitted with the form. [data len: %u][max len: %u][URI: %s]",
|
|
content_len, HTTP_MAX_POST_DATA_LEN, request_info->uri);
|
|
valid_csrf = 0;
|
|
} else {
|
|
if(is_file_upload) {
|
|
if((!is_file_upload) && (content_len > HTTP_MAX_UPLOAD_DATA_LEN)) {
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING,
|
|
"You are uploading a file that is too big [len: %u][max len: %u][URI: %s]",
|
|
content_len, HTTP_MAX_UPLOAD_DATA_LEN, request_info->uri);
|
|
valid_csrf = 0;
|
|
} else {
|
|
char fname[1024], upload_dir[512];
|
|
|
|
snprintf(upload_dir, sizeof(upload_dir), "%s/tmp/upload", ntop->get_working_dir());
|
|
ntop->fixPath(upload_dir);
|
|
|
|
if(!Utils::mkdir_tree(upload_dir))
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to create directory %s", upload_dir);
|
|
else {
|
|
/* NOTE: mongoose is currently unable to handle multiple fields in a single upload */
|
|
num_uploaded_files = mg_upload(conn, upload_dir, fname, sizeof(fname));
|
|
|
|
if(num_uploaded_files > 0) {
|
|
char uploaded_file[2048];
|
|
|
|
snprintf(uploaded_file, sizeof(uploaded_file), "%s/%s", upload_dir, fname);
|
|
ntop->fixPath(uploaded_file);
|
|
|
|
lua_newtable(L);
|
|
lua_push_str_table_entry(L, "uploaded_file", uploaded_file);
|
|
|
|
if(request_info->query_string) {
|
|
char *d = strdup(request_info->query_string);
|
|
|
|
if(d != NULL) {
|
|
char *where;
|
|
char *tok = strtok_r(d, "&", &where);
|
|
|
|
while(tok != NULL) {
|
|
char *tok_where;
|
|
char *k = strtok_r(tok, "=", &tok_where);
|
|
|
|
if(k != NULL) {
|
|
char *v = strtok_r(NULL, "=", &tok_where);
|
|
|
|
if(v != NULL)
|
|
lua_push_str_table_entry(L, k, v);
|
|
}
|
|
|
|
tok = strtok_r(NULL, "&", &where);
|
|
} /* while */
|
|
}
|
|
}
|
|
|
|
lua_setglobal(L, "_POST");
|
|
valid_csrf = 1; /* Dummy */
|
|
}
|
|
}
|
|
}
|
|
} else if((post_data = (char*)malloc(content_len * sizeof(char))) == NULL
|
|
|| (post_data_len = mg_read(conn, post_data, content_len)) == 0) {
|
|
valid_csrf = 0;
|
|
} else {
|
|
post_data[post_data_len] = '\0';
|
|
|
|
if(!strcmp(session_csrf, NTOP_CSRF_TOKEN_NO_SESSION)) {
|
|
/* Authentication has taken place with direct username:password submission, without the use of a session,
|
|
hence, this request cannot be the result of a CSRF attack which, by construction, relies on a valid session. */
|
|
valid_csrf = 1;
|
|
} else {
|
|
/* If here, authentication has taken place using a session, thus CSRF is mandatory in POST request and must
|
|
be checked for validity.
|
|
Note that session_csrf is trusted, that it it comes from ntopng, whereas csrf read from the POST is untrusted. */
|
|
if(strstr(content_type, "application/json") == content_type) {
|
|
/*
|
|
post_data is JSON which is only allowed when using the REST API
|
|
*/
|
|
if((strstr(request_info->uri, REST_API_PREFIX) ||
|
|
strstr(request_info->uri, REST_API_PRO_PREFIX)) &&
|
|
strstr(request_info->uri, "/get/")) {
|
|
/*
|
|
REST API URI, GET, no CSRF required
|
|
*/
|
|
} else {
|
|
/*
|
|
REST API URIs not containing /get/, e.g., /set/, /add/, /delete/, are assumed to
|
|
change the status of ntopng and thus CSRF checks MUST be enforced.
|
|
In this case, post_data is decoded as JSON to enforce the check.
|
|
*/
|
|
json_object *o = NULL, *csrf_o;
|
|
|
|
if(!(o = json_tokener_parse(post_data))
|
|
|| !json_object_object_get_ex(o, "csrf", &csrf_o)
|
|
|| !strncpy(csrf, json_object_get_string(csrf_o), sizeof(csrf)-1)
|
|
|| strncmp(session_csrf, csrf, NTOP_CSRF_TOKEN_LENGTH)) {
|
|
/*
|
|
Either the CSRF token has not been submitted as part of the JSON, or
|
|
the submitted token is invalid.
|
|
*/
|
|
valid_csrf = 0;
|
|
}
|
|
|
|
if (o) json_object_put(o);
|
|
}
|
|
} else {
|
|
/*
|
|
post_data is assumed to be application/x-www-form-urlencoded
|
|
CSRF token is searched and validated using mg_get_var
|
|
*/
|
|
mg_get_var(post_data, post_data_len, "csrf", csrf, sizeof(csrf));
|
|
|
|
if(strncmp(session_csrf, csrf, NTOP_CSRF_TOKEN_LENGTH))
|
|
valid_csrf = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(num_uploaded_files == 0) {
|
|
/* Empty CSRF only allowed for nologin user. Such user has no associated
|
|
* session so it has an empty CSRF. */
|
|
if(valid_csrf) {
|
|
if(strstr(content_type, "application/x-www-form-urlencoded") == content_type)
|
|
*attack_attempt = setParamsTable(L, request_info, "_POST", post_data); /* CSRF is valid here, now fill the _POST table with POST parameters */
|
|
else {
|
|
/* application/json */
|
|
|
|
lua_newtable(L);
|
|
lua_push_str_table_entry(L, "payload", post_data); /* This payload is NOT parsed, checked or verified against attacks */
|
|
lua_setglobal(L, "_POST");
|
|
}
|
|
|
|
/* Check for interface switch requests */
|
|
mg_get_var(post_data, post_data_len, "switch_interface", switch_interface, sizeof(switch_interface));
|
|
if(strlen(switch_interface) > 0 && request_info->query_string) {
|
|
/* Read the interface id */
|
|
mg_get_var(request_info->query_string, strlen(request_info->query_string), "ifid", ifid_buf, sizeof(ifid_buf));
|
|
/* Read the observation point id */
|
|
mg_get_var(request_info->query_string, strlen(request_info->query_string), "observationPointId", obs_id_buf, sizeof(obs_id_buf));
|
|
if(strlen(ifid_buf) > 0) {
|
|
switchInterface(L, ifid_buf, obs_id_buf, user, group, session_buf);
|
|
|
|
/* Sending a redirect is needed to prevent the current lua script
|
|
* from receiving the POST request, as it could exchange the request
|
|
* as a configuration save request. */
|
|
send_redirect = true;
|
|
}
|
|
}
|
|
} else {
|
|
*attack_attempt = setParamsTable(L, request_info, "_POST", NULL /* Empty */);
|
|
}
|
|
|
|
if(post_data)
|
|
free(post_data);
|
|
}
|
|
} else
|
|
*attack_attempt = setParamsTable(L, request_info, "_POST", NULL /* Empty */);
|
|
|
|
if(send_redirect) {
|
|
char buf[2048], uri[512];
|
|
|
|
snprintf(uri, sizeof(uri), "%s%s", ntop->getPrefs()->get_http_prefix(), request_info->uri);
|
|
build_redirect(uri, request_info->query_string, buf, sizeof(buf));
|
|
|
|
/* Redirect the page and terminate this request */
|
|
mg_printf(conn, "%s", buf);
|
|
return(CONST_LUA_OK);
|
|
}
|
|
|
|
/* Put the GET params into the environment */
|
|
if(request_info->query_string)
|
|
*attack_attempt = setParamsTable(L, request_info, "_GET", request_info->query_string);
|
|
else
|
|
*attack_attempt = setParamsTable(L, request_info, "_GET", NULL /* Empty */);
|
|
|
|
/* _SERVER */
|
|
lua_newtable(L);
|
|
lua_push_str_table_entry(L, "REQUEST_METHOD", (char*)request_info->request_method);
|
|
lua_push_str_table_entry(L, "URI", (char*)request_info->uri ? (char*)request_info->uri : (char*)"");
|
|
lua_push_str_table_entry(L, "REFERER", (char*)mg_get_header(conn, "Referer") ? (char*)mg_get_header(conn, "Referer") : (char*)"");
|
|
|
|
const char *host = mg_get_header(conn, "Host");
|
|
|
|
if(host) {
|
|
lua_pushfstring(L, "%s://%s", (request_info->is_ssl) ? "https" : "http", host);
|
|
lua_pushstring(L, "HTTP_HOST");
|
|
lua_insert(L, -2);
|
|
lua_settable(L, -3);
|
|
}
|
|
|
|
if(request_info->remote_user) lua_push_str_table_entry(L, "REMOTE_USER", (char*)request_info->remote_user);
|
|
if(request_info->query_string) lua_push_str_table_entry(L, "QUERY_STRING", (char*)request_info->query_string);
|
|
|
|
/* Additional headers can be added eventually */
|
|
origin_header = mg_get_header(conn, "Origin");
|
|
if(origin_header) lua_push_str_table_entry(L, "Origin", origin_header);
|
|
|
|
for(int i=0; ((request_info->http_headers[i].name != NULL)
|
|
&& request_info->http_headers[i].name[0] != '\0'); i++)
|
|
lua_push_str_table_entry(L,
|
|
request_info->http_headers[i].name,
|
|
(char*)request_info->http_headers[i].value);
|
|
|
|
client_addr.set(mg_get_client_address(conn));
|
|
lua_push_str_table_entry(L, "REMOTE_ADDR", (char*) client_addr.print(addr_buf, sizeof(addr_buf)));
|
|
|
|
lua_setglobal(L, (char*)"_SERVER");
|
|
|
|
/* NOTE: ntopng cannot rely on user provided cookies for security data (e.g. user or group),
|
|
* use the session data instead! */
|
|
char *_cookies;
|
|
|
|
/* Cookies */
|
|
lua_newtable(L);
|
|
if((_cookies = (char*)mg_get_header(conn, "Cookie")) != NULL) {
|
|
char *cookies = strdup(_cookies);
|
|
char *tok, *where;
|
|
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "=> '%s'", cookies);
|
|
tok = strtok_r(cookies, "=", &where);
|
|
while(tok != NULL) {
|
|
char *val;
|
|
|
|
while(tok[0] == ' ') tok++;
|
|
|
|
if((val = strtok_r(NULL, ";", &where)) != NULL) {
|
|
lua_push_str_table_entry(L, tok, val);
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "'%s'='%s'", tok, val);
|
|
} else
|
|
break;
|
|
|
|
tok = strtok_r(NULL, "=", &where);
|
|
}
|
|
|
|
free(cookies);
|
|
}
|
|
lua_setglobal(L, "_COOKIE"); /* Like in php */
|
|
|
|
/*
|
|
Read user capabilities
|
|
*/
|
|
u_int64_t capabilities = 0;
|
|
char allowed_nets[MAX_USER_NETS_VAL_LEN];
|
|
|
|
if(strncmp(group, CONST_USER_GROUP_ADMIN, strlen(CONST_USER_GROUP_ADMIN)) == 0) {
|
|
/*
|
|
Administrators have all the possible capabilitites
|
|
*/
|
|
capabilities = (u_int64_t)-1;
|
|
snprintf(allowed_nets, sizeof(allowed_nets), CONST_DEFAULT_ALL_NETS);
|
|
|
|
} else {
|
|
/*
|
|
Non-administrators only have a subset of capabilities - stored on redis
|
|
*/
|
|
bool allow_pcap_download = false, allow_historical_flows = false, allow_alerts = false;
|
|
|
|
char val[32];
|
|
snprintf(key, sizeof(key), CONST_STR_USER_CAPABILITIES, user);
|
|
if((ntop->getRedis()->get(key, val, sizeof(val)) != -1) && (val[0] != '\0')) {
|
|
capabilities = strtol(val, NULL, 10);
|
|
}
|
|
|
|
/*
|
|
Read user allowed networks - stored on redis
|
|
*/
|
|
snprintf(key, sizeof(key), CONST_STR_USER_NETS, user);
|
|
if(ntop->getRedis()->get(key, allowed_nets, sizeof(allowed_nets)) == -1) {
|
|
/*
|
|
Set the default (allow all), if no allowed networks are found
|
|
*/
|
|
snprintf(allowed_nets, sizeof(allowed_nets), CONST_DEFAULT_ALL_NETS);
|
|
}
|
|
|
|
ntop->getUserCapabilities(user, &allow_pcap_download, &allow_historical_flows, &allow_alerts);
|
|
|
|
if(allow_historical_flows)
|
|
capabilities |= (1 << capability_historical_flows);
|
|
|
|
if(allow_alerts)
|
|
capabilities |= (1 << capability_alerts);
|
|
|
|
if(allow_pcap_download)
|
|
capabilities |= (1 << capability_pcap_download);
|
|
}
|
|
|
|
/* Put the _SESSION params into the environment */
|
|
lua_newtable(L);
|
|
|
|
lua_push_str_table_entry(L, "session", session_buf);
|
|
lua_push_str_table_entry(L, "user", (char*)user);
|
|
lua_push_str_table_entry(L, "group", (char*)group);
|
|
lua_push_bool_table_entry(L, "localuser", localuser);
|
|
lua_push_uint64_table_entry(L, "capabilities", capabilities);
|
|
lua_push_str_table_entry(L, "allowed_nets", (char*)allowed_nets);
|
|
|
|
// now it's time to set the interface.
|
|
setInterface(user, ifname, sizeof(ifname), &is_interface_allowed);
|
|
|
|
if(!valid_csrf)
|
|
lua_push_bool_table_entry(L, "INVALID_CSRF", true);
|
|
|
|
lua_setglobal(L, "_SESSION"); /* Like in php */
|
|
|
|
if(user[0] != '\0') {
|
|
char val[16];
|
|
getLuaVMUservalue(L, user) = (char*)user;
|
|
|
|
/* Populate a patricia tree with the user allowed networks */
|
|
ptree.addAddresses(allowed_nets);
|
|
|
|
getLuaVMUservalue(L, allowedNets) = &ptree;
|
|
// ntop->getTrace()->traceEvent(TRACE_WARNING, "SET [p: %p][val: %s][user: %s]", &ptree, val, user);
|
|
|
|
snprintf(key, sizeof(key), CONST_STR_USER_LANGUAGE, user);
|
|
if((ntop->getRedis()->get(key, val, sizeof(val)) != -1)
|
|
&& (val[0] != '\0')) {
|
|
lua_pushstring(L, val);
|
|
} else
|
|
lua_pushstring(L, NTOP_DEFAULT_USER_LANG);
|
|
|
|
lua_setglobal(L, CONST_USER_LANGUAGE);
|
|
}
|
|
|
|
getLuaVMUservalue(L, group) = (char*)(group ? (group) : "");
|
|
getLuaVMUservalue(L, localuser) = localuser;
|
|
getLuaVMUservalue(L, csrf) = (char*)session_csrf;
|
|
getLuaVMUservalue(L, capabilities) = capabilities;
|
|
|
|
iface = ntop->getNetworkInterface(ifname); /* Can't be null */
|
|
/* 'select' ther interface that has already been set into the _SESSION */
|
|
getLuaVMUservalue(L,iface) = iface;
|
|
|
|
if(is_interface_allowed)
|
|
getLuaVMUservalue(L, allowed_ifname) = iface->get_name();
|
|
|
|
#ifdef NTOPNG_PRO
|
|
if(ntop->getPro()->has_valid_license())
|
|
rc = __ntop_lua_handlefile(L, script_path, true);
|
|
else
|
|
#endif
|
|
rc = luaL_dofile(L, script_path);
|
|
|
|
if(rc != 0) {
|
|
const char *err = lua_tostring(L, -1);
|
|
|
|
ntop->getTrace()->traceEvent(TRACE_WARNING, "Script failure [%s][%s]", script_path, err);
|
|
return(redirect_to_error_page(conn, request_info, "internal_error", script_path, (char*)err));
|
|
}
|
|
|
|
return(CONST_LUA_OK);
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
void LuaEngine::setHost(Host* h) {
|
|
struct ntopngLuaContext *c = getLuaVMContext(L);
|
|
|
|
if(c) {
|
|
c->host = h;
|
|
|
|
if(h)
|
|
c->iface = h->getInterface();
|
|
}
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
void LuaEngine::setNetwork(NetworkStats* ns) {
|
|
struct ntopngLuaContext *c = getLuaVMContext(L);
|
|
|
|
if(c) {
|
|
c->network = ns;
|
|
}
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
void LuaEngine::setFlow(Flow* f) {
|
|
struct ntopngLuaContext *c = getLuaVMContext(L);
|
|
|
|
if(c) {
|
|
c->flow = f;
|
|
c->iface = f->getInterface();
|
|
}
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
void LuaEngine::setThreadedActivityData(lua_State* from) {
|
|
struct ntopngLuaContext *cur_ctx, *from_ctx;
|
|
lua_State *cur_state = getState();
|
|
|
|
if(from
|
|
&& (cur_ctx = getLuaVMContext(cur_state))
|
|
&& (from_ctx = getLuaVMContext(from))) {
|
|
cur_ctx->deadline = from_ctx->deadline;
|
|
cur_ctx->threaded_activity = from_ctx->threaded_activity;
|
|
cur_ctx->threaded_activity_stats = from_ctx->threaded_activity_stats;
|
|
}
|
|
}
|
|
|
|
/* ****************************************** */
|
|
|
|
void LuaEngine::setThreadedActivityData(const ThreadedActivity *ta, ThreadedActivityStats *tas, time_t deadline) {
|
|
struct ntopngLuaContext *cur_ctx;
|
|
lua_State *cur_state = getState();
|
|
|
|
if((cur_ctx = getLuaVMContext(cur_state))) {
|
|
cur_ctx->deadline = deadline;
|
|
cur_ctx->threaded_activity = ta;
|
|
cur_ctx->threaded_activity_stats = tas;
|
|
}
|
|
|
|
}
|
|
|