mirror of
				https://github.com/falcosecurity/falco.git
				synced 2025-10-30 16:50:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			304 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| Copyright (C) 2019 The Falco Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| #include <json/json.h>
 | |
| 
 | |
| #include "formats.h"
 | |
| #include "falco_engine.h"
 | |
| #include "banned.h" // This raises a compilation error when certain functions are used
 | |
| 
 | |
| 
 | |
| sinsp* falco_formats::s_inspector = NULL;
 | |
| falco_engine *falco_formats::s_engine = NULL;
 | |
| bool falco_formats::s_json_output = false;
 | |
| bool falco_formats::s_json_include_output_property = true;
 | |
| sinsp_evt_formatter_cache *falco_formats::s_formatters = NULL;
 | |
| 
 | |
| const static struct luaL_reg ll_falco [] =
 | |
| {
 | |
| 	{"formatter", &falco_formats::formatter},
 | |
| 	{"free_formatter", &falco_formats::free_formatter},
 | |
| 	{"free_formatters", &falco_formats::free_formatters},
 | |
| 	{"format_event", &falco_formats::format_event},
 | |
| 	{"resolve_tokens", &falco_formats::resolve_tokens},
 | |
| 	{NULL,NULL}
 | |
| };
 | |
| 
 | |
| void falco_formats::init(sinsp* inspector,
 | |
| 			 falco_engine *engine,
 | |
| 			 lua_State *ls,
 | |
| 			 bool json_output,
 | |
| 			 bool json_include_output_property)
 | |
| {
 | |
| 	s_inspector = inspector;
 | |
| 	s_engine = engine;
 | |
| 	s_json_output = json_output;
 | |
| 	s_json_include_output_property = json_include_output_property;
 | |
| 	if(!s_formatters)
 | |
| 	{
 | |
| 		s_formatters = new sinsp_evt_formatter_cache(s_inspector);
 | |
| 	}
 | |
| 
 | |
| 	luaL_openlib(ls, "formats", ll_falco, 0);
 | |
| }
 | |
| 
 | |
| int falco_formats::formatter(lua_State *ls)
 | |
| {
 | |
| 	string source = luaL_checkstring(ls, -2);
 | |
| 	string format = luaL_checkstring(ls, -1);
 | |
| 
 | |
| 	try
 | |
| 	{
 | |
| 		if(source == "syscall")
 | |
| 		{
 | |
| 			sinsp_evt_formatter* formatter;
 | |
| 			formatter = new sinsp_evt_formatter(s_inspector, format);
 | |
| 			lua_pushlightuserdata(ls, formatter);
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			json_event_formatter *formatter;
 | |
| 			formatter = new json_event_formatter(s_engine->json_factory(), format);
 | |
| 			lua_pushlightuserdata(ls, formatter);
 | |
| 		}
 | |
| 	}
 | |
| 	catch(sinsp_exception& e)
 | |
| 	{
 | |
| 		luaL_error(ls, "Invalid output format '%s': '%s'", format.c_str(), e.what());
 | |
| 	}
 | |
| 	catch(falco_exception& e)
 | |
| 	{
 | |
| 		luaL_error(ls, "Invalid output format '%s': '%s'", format.c_str(), e.what());
 | |
| 	}
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| int falco_formats::free_formatter(lua_State *ls)
 | |
| {
 | |
| 	if (!lua_islightuserdata(ls, -1) ||
 | |
| 	    !lua_isstring(ls, -2))
 | |
| 
 | |
| 	{
 | |
| 		luaL_error(ls, "Invalid argument passed to free_formatter");
 | |
| 	}
 | |
| 
 | |
| 	string source = luaL_checkstring(ls, -2);
 | |
| 
 | |
| 	if(source == "syscall")
 | |
| 	{
 | |
| 		sinsp_evt_formatter *formatter = (sinsp_evt_formatter *) lua_topointer(ls, -1);
 | |
| 		delete(formatter);
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		json_event_formatter *formatter = (json_event_formatter *) lua_topointer(ls, -1);
 | |
| 		delete(formatter);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int falco_formats::free_formatters(lua_State *ls)
 | |
| {
 | |
| 	if(s_formatters)
 | |
| 	{
 | |
| 		delete(s_formatters);
 | |
| 		s_formatters = NULL;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int falco_formats::format_event (lua_State *ls)
 | |
| {
 | |
| 	string line;
 | |
| 	string json_line;
 | |
| 
 | |
| 	if (!lua_isstring(ls, -1) ||
 | |
| 	    !lua_isstring(ls, -2) ||
 | |
| 	    !lua_isstring(ls, -3) ||
 | |
| 	    !lua_isstring(ls, -4) ||
 | |
| 	    !lua_islightuserdata(ls, -5)) {
 | |
| 		lua_pushstring(ls, "Invalid arguments passed to format_event()");
 | |
| 		lua_error(ls);
 | |
| 	}
 | |
| 	gen_event* evt = (gen_event*)lua_topointer(ls, 1);
 | |
| 	const char *rule = (char *) lua_tostring(ls, 2);
 | |
| 	const char *source = (char *) lua_tostring(ls, 3);
 | |
| 	const char *level = (char *) lua_tostring(ls, 4);
 | |
| 	const char *format = (char *) lua_tostring(ls, 5);
 | |
| 
 | |
| 	string sformat = format;
 | |
| 
 | |
| 	if(strcmp(source, "syscall") == 0)
 | |
| 	{
 | |
| 		try {
 | |
| 			// This is "output"
 | |
| 			s_formatters->tostring((sinsp_evt *) evt, sformat, &line);
 | |
| 
 | |
| 			if(s_json_output)
 | |
| 			{
 | |
| 				sinsp_evt::param_fmt cur_fmt = s_inspector->get_buffer_format();
 | |
| 				switch(cur_fmt)
 | |
| 				{
 | |
| 					case sinsp_evt::PF_NORMAL:
 | |
| 						s_inspector->set_buffer_format(sinsp_evt::PF_JSON);
 | |
| 						break;
 | |
| 					case sinsp_evt::PF_EOLS:
 | |
| 						s_inspector->set_buffer_format(sinsp_evt::PF_JSONEOLS);
 | |
| 						break;
 | |
| 					case sinsp_evt::PF_HEX:
 | |
| 						s_inspector->set_buffer_format(sinsp_evt::PF_JSONHEX);
 | |
| 						break;
 | |
| 					case sinsp_evt::PF_HEXASCII:
 | |
| 						s_inspector->set_buffer_format(sinsp_evt::PF_JSONHEXASCII);
 | |
| 						break;
 | |
| 					case sinsp_evt::PF_BASE64:
 | |
| 						s_inspector->set_buffer_format(sinsp_evt::PF_JSONBASE64);
 | |
| 						break;
 | |
| 					default:
 | |
| 						// do nothing
 | |
| 						break;
 | |
| 				}
 | |
| 				// This is output fields
 | |
| 				s_formatters->tostring((sinsp_evt *) evt, sformat, &json_line);
 | |
| 
 | |
| 				// The formatted string might have a leading newline. If it does, remove it.
 | |
| 				if (json_line[0] == '\n')
 | |
| 				{
 | |
| 					json_line.erase(0, 1);
 | |
| 				}
 | |
| 				s_inspector->set_buffer_format(cur_fmt);
 | |
| 			}
 | |
| 		}
 | |
| 		catch (sinsp_exception& e)
 | |
| 		{
 | |
| 			string err = "Invalid output format '" + sformat + "': '" + string(e.what()) + "'";
 | |
| 			lua_pushstring(ls, err.c_str());
 | |
| 			lua_error(ls);
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		try {
 | |
| 
 | |
| 			json_event_formatter formatter(s_engine->json_factory(), sformat);
 | |
| 
 | |
| 			line = formatter.tostring((json_event *) evt);
 | |
| 
 | |
| 			if(s_json_output)
 | |
| 			{
 | |
| 				json_line = formatter.tojson((json_event *) evt);
 | |
| 			}
 | |
| 		}
 | |
| 		catch (exception &e)
 | |
| 		{
 | |
| 			string err = "Invalid output format '" + sformat + "': '" + string(e.what()) + "'";
 | |
| 			lua_pushstring(ls, err.c_str());
 | |
| 			lua_error(ls);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// For JSON output, the formatter returned a json-as-text
 | |
| 	// object containing all the fields in the original format
 | |
| 	// message as well as the event time in ns. Use this to build
 | |
| 	// a more detailed object containing the event time, rule,
 | |
| 	// severity, full output, and fields.
 | |
| 	if (s_json_output) {
 | |
| 		Json::Value event;
 | |
| 		Json::FastWriter writer;
 | |
| 		string full_line;
 | |
| 
 | |
| 		// Convert the time-as-nanoseconds to a more json-friendly ISO8601.
 | |
| 		time_t evttime = evt->get_ts()/1000000000;
 | |
| 		char time_sec[20]; // sizeof "YYYY-MM-DDTHH:MM:SS"
 | |
| 		char time_ns[12]; // sizeof ".sssssssssZ"
 | |
| 		string iso8601evttime;
 | |
| 
 | |
| 		strftime(time_sec, sizeof(time_sec), "%FT%T", gmtime(&evttime));
 | |
| 		snprintf(time_ns, sizeof(time_ns), ".%09luZ", evt->get_ts() % 1000000000);
 | |
| 		iso8601evttime = time_sec;
 | |
| 		iso8601evttime += time_ns;
 | |
| 		event["time"] = iso8601evttime;
 | |
| 		event["rule"] = rule;
 | |
| 		event["priority"] = level;
 | |
| 
 | |
| 		if(s_json_include_output_property)
 | |
| 		{
 | |
| 			// This is the filled-in output line.
 | |
| 			event["output"] = line;
 | |
| 		}
 | |
| 
 | |
| 		full_line = writer.write(event);
 | |
| 
 | |
| 		// Json::FastWriter may add a trailing newline. If it
 | |
| 		// does, remove it.
 | |
| 		if (full_line[full_line.length()-1] == '\n')
 | |
| 		{
 | |
| 			full_line.resize(full_line.length()-1);
 | |
| 		}
 | |
| 
 | |
| 		// Cheat-graft the output from the formatter into this
 | |
| 		// string. Avoids an unnecessary json parse just to
 | |
| 		// merge the formatted fields at the object level.
 | |
| 		full_line.pop_back();
 | |
| 		full_line.append(", \"output_fields\": ");
 | |
| 		full_line.append(json_line);
 | |
| 		full_line.append("}");
 | |
| 		line = full_line;
 | |
| 	}
 | |
| 
 | |
| 	lua_pushstring(ls, line.c_str());
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| int falco_formats::resolve_tokens(lua_State *ls)
 | |
| {
 | |
| 	if(!lua_isstring(ls, -1) ||
 | |
| 	   !lua_isstring(ls, -2) ||
 | |
| 	   !lua_islightuserdata(ls, -3))
 | |
| 	{
 | |
| 		lua_pushstring(ls, "Invalid arguments passed to resolve_tokens()");
 | |
| 		lua_error(ls);
 | |
| 	}
 | |
| 	gen_event *evt = (gen_event *)lua_topointer(ls, 1);
 | |
| 	string source = luaL_checkstring(ls, 2);
 | |
| 	const char *format = (char *)lua_tostring(ls, 3);
 | |
| 	string sformat = format;
 | |
| 
 | |
| 	map<string, string> values;
 | |
| 	if(source == "syscall")
 | |
| 	{
 | |
| 		s_formatters->resolve_tokens((sinsp_evt *)evt, sformat, values);
 | |
| 	}
 | |
| 	// k8s_audit
 | |
| 	else
 | |
| 	{
 | |
| 		json_event_formatter json_formatter(s_engine->json_factory(), sformat);
 | |
| 		values = json_formatter.tomap((json_event*) evt);
 | |
| 	}
 | |
| 
 | |
| 	lua_newtable(ls);
 | |
| 	for(auto const& v : values)
 | |
| 	{
 | |
| 		lua_pushstring(ls, v.first.c_str());
 | |
| 		lua_pushstring(ls, v.second.c_str());
 | |
| 		lua_settable(ls, -3);
 | |
| 	}
 | |
| 
 | |
| 	return 1;
 | |
| }
 |