ModSecurity/src/modsecurity.cc
Felipe Zimmerle 1518015687
Adds new method for rule merge
IMPORTANT: SecDefaultAction specified on a child configuration will
overwrite the ones specified on the parent; Previously it was
concatenating.
2020-11-16 10:01:38 -03:00

500 lines
14 KiB
C++

/*
* ModSecurity, http://www.modsecurity.org/
* Copyright (c) 2015 - 2020 Trustwave Holdings, Inc. (http://www.trustwave.com/)
*
* 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
*
* If any of the files related to licensing are missing or if you have any
* other questions related to licensing please contact Trustwave Holdings, Inc.
* directly using the email address security@modsecurity.org.
*
*/
#include "modsecurity/modsecurity.h"
#include "src/config.h"
#ifdef WITH_YAJL
#include <yajl/yajl_tree.h>
#include <yajl/yajl_gen.h>
#endif
#ifdef WITH_LIBXML2
#include <libxml/xmlschemas.h>
#include <libxml/xpath.h>
#endif
#ifdef MSC_WITH_CURL
#include <curl/curl.h>
#endif
#include <ctime>
#include <iostream>
#include "modsecurity/rule.h"
#include "modsecurity/rule_message.h"
#include "src/collection/backend/in_memory-per_process.h"
#include "src/collection/backend/lmdb.h"
#include "src/unique_id.h"
#include "src/utils/regex.h"
#include "src/utils/geo_lookup.h"
#include "src/actions/transformations/transformation.h"
namespace modsecurity {
/**
* @name ModSecurity
* @brief Initilizes ModSecurity CPP API
*
* ModSecurity initializer.
*
* Example Usage:
* @code
*
* using ModSecurity::ModSecurity;
*
* ModSecurity *msc = new ModSecurity();
*
* @endcode
*/
ModSecurity::ModSecurity()
:
#ifdef WITH_LMDB
m_global_collection(new collection::backend::LMDB("GLOBAL")),
m_resource_collection(new collection::backend::LMDB("RESOURCE")),
m_ip_collection(new collection::backend::LMDB("IP")),
m_session_collection(new collection::backend::LMDB("SESSION")),
m_user_collection(new collection::backend::LMDB("USER")),
#else
m_global_collection(new collection::backend::InMemoryPerProcess("GLOBAL")),
m_resource_collection(
new collection::backend::InMemoryPerProcess("RESOURCE")),
m_ip_collection(new collection::backend::InMemoryPerProcess("IP")),
m_session_collection(
new collection::backend::InMemoryPerProcess("SESSION")),
m_user_collection(new collection::backend::InMemoryPerProcess("USER")),
#endif
m_connector(""),
m_whoami(""),
m_logCb(NULL),
m_logProperties(0) {
UniqueId::uniqueId();
srand(time(NULL));
#ifdef MSC_WITH_CURL
curl_global_init(CURL_GLOBAL_ALL);
#endif
#ifdef WITH_LIBXML2
xmlInitParser();
#endif
}
ModSecurity::~ModSecurity() {
#ifdef MSC_WITH_CURL
curl_global_cleanup();
#endif
#ifdef WITH_GEOIP
Utils::GeoLookup::getInstance().cleanUp();
#endif
#ifdef WITH_LIBXML2
xmlCleanupParser();
#endif
delete m_global_collection;
delete m_resource_collection;
delete m_ip_collection;
delete m_session_collection;
delete m_user_collection;
}
/**
* @name whoAmI
* @brief Return information about this ModSecurity version and platform.
*
* Platform and version are two questions that community will ask prior to
* provide support. Making it available internally and to the connector as
* well.
*
* @note This information maybe will be used by a log parser. If you want to
* update it, make it in a fashion that won't break the existent parsers.
* (e.g. adding extra information _only_ to the end of the string)
*/
const std::string& ModSecurity::whoAmI() {
std::string platform("Unknown platform");
#if AIX
platform = "AIX";
#elif LINUX
platform = "Linux";
#elif OPENBSD
platform = "OpenBSD";
#elif SOLARIS
platform = "Solaris";
#elif HPUX
platform = "HPUX";
#elif MACOSX
platform = "MacOSX";
#elif FREEBSD
platform = "FreeBSD";
#elif NETBSD
platform = "NetBSD";
#elif WIN32
platform = "Windows";
#endif
if (m_whoami.empty()) {
m_whoami = "ModSecurity v" MODSECURITY_VERSION " (" + platform + ")";
}
return m_whoami;
}
/**
* @name setConnectorInformation
* @brief Set information about the connector that is using the library.
*
* For the purpose of log it is necessary for modsecurity to understand which
* 'connector' is consuming the API.
*
* @note It is strongly recommended to set a information in the following
* pattern:
*
* ConnectorName vX.Y.Z-tag (something else)
*
* For instance: ModSecurity-nginx v0.0.1-alpha (Whee)
*
* @param connector Information about the connector.
*
*/
void ModSecurity::setConnectorInformation(const std::string &connector) {
m_connector = connector;
}
/**
* @name getConnectorInformation
* @brief Returns the connector information.
*
* Returns whatever was set by 'setConnectorInformation'. Check
* setConnectorInformation documentation to understand the expected format.
*
* @retval "" Nothing was informed about the connector.
* @retval !="" Connector information.
*/
const std::string& ModSecurity::getConnectorInformation() const {
return m_connector;
}
void ModSecurity::serverLog(void *data, RuleMessage *rm) {
if (m_logCb == NULL) {
std::cerr << "Server log callback is not set -- " << rm->errorLog();
std::cerr << std::endl;
return;
}
if (rm == NULL) {
return;
}
if (m_logProperties & TextLogProperty) {
std::string &&d = rm->log();
const void *a = static_cast<const void *>(d.c_str());
m_logCb(data, a);
return;
}
if (m_logProperties & RuleMessageLogProperty) {
const void *a = static_cast<const void *>(rm);
if (m_logProperties & IncludeFullHighlightLogProperty) {
m_logCb(data, a);
return;
}
m_logCb(data, a);
return;
}
}
int ModSecurity::processContentOffset(const char *content, size_t len,
const char *matchString, std::string *json, const char **err) {
#ifdef WITH_YAJL
Utils::Regex variables("v([0-9]+),([0-9]+)");
Utils::Regex operators("o([0-9]+),([0-9]+)");
Utils::Regex transformations("t:(?:(?!t:).)+");
yajl_gen g;
std::string varValue;
const unsigned char *buf;
size_t jsonSize;
std::list<Utils::SMatch> vars = variables.searchAll(matchString);
std::list<Utils::SMatch> ops = operators.searchAll(matchString);
std::list<Utils::SMatch> trans = transformations.searchAll(matchString);
g = yajl_gen_alloc(NULL);
if (g == NULL) {
*err = "Failed to allocate memory for the JSON creation.";
return -1;
}
yajl_gen_config(g, yajl_gen_beautify, 0);
yajl_gen_map_open(g);
yajl_gen_string(g, reinterpret_cast<const unsigned char*>("match"),
strlen("match"));
yajl_gen_array_open(g);
yajl_gen_map_open(g);
yajl_gen_string(g, reinterpret_cast<const unsigned char*>("variable"),
strlen("variable"));
yajl_gen_map_open(g);
yajl_gen_string(g, reinterpret_cast<const unsigned char*>("highlight"),
strlen("highlight"));
yajl_gen_array_open(g);
while (vars.size() > 3) {
std::string value;
yajl_gen_map_open(g);
vars.pop_back();
const std::string &startingAt = vars.back().str();
vars.pop_back();
const std::string &size = vars.back().str();
vars.pop_back();
yajl_gen_string(g,
reinterpret_cast<const unsigned char*>("startingAt"),
strlen("startingAt"));
yajl_gen_string(g,
reinterpret_cast<const unsigned char*>(startingAt.c_str()),
startingAt.size());
yajl_gen_string(g, reinterpret_cast<const unsigned char*>("size"),
strlen("size"));
yajl_gen_string(g,
reinterpret_cast<const unsigned char*>(size.c_str()),
size.size());
yajl_gen_map_close(g);
if (stoi(startingAt) >= len) {
*err = "Offset is out of the content limits.";
return -1;
}
value = std::string(content, stoi(startingAt), stoi(size));
if (varValue.size() > 0) {
varValue.append(" " + value);
} else {
varValue.append(value);
}
}
yajl_gen_array_close(g);
yajl_gen_string(g, reinterpret_cast<const unsigned char*>("value"),
strlen("value"));
yajl_gen_array_open(g);
yajl_gen_map_open(g);
yajl_gen_string(g, reinterpret_cast<const unsigned char*>("value"),
strlen("value"));
yajl_gen_string(g, reinterpret_cast<const unsigned char*>(varValue.c_str()),
varValue.size());
yajl_gen_map_close(g);
while (!trans.empty()) {
modsecurity::actions::transformations::Transformation *t;
std::string varValueRes;
yajl_gen_map_open(g);
yajl_gen_string(g,
reinterpret_cast<const unsigned char*>("transformation"),
strlen("transformation"));
yajl_gen_string(g,
reinterpret_cast<const unsigned char*>(trans.back().str().c_str()),
trans.back().str().size());
t = modsecurity::actions::transformations::Transformation::instantiate(
trans.back().str().c_str());
varValueRes = t->evaluate(varValue, NULL);
varValue.assign(varValueRes);
trans.pop_back();
yajl_gen_string(g, reinterpret_cast<const unsigned char*>("value"),
strlen("value"));
yajl_gen_string(g, reinterpret_cast<const unsigned char*>(
varValue.c_str()),
varValue.size());
yajl_gen_map_close(g);
delete t;
}
yajl_gen_array_close(g);
yajl_gen_string(g, reinterpret_cast<const unsigned char*>("operator"),
strlen("operator"));
yajl_gen_map_open(g);
while (ops.size() > 3) {
std::string value;
yajl_gen_string(g, reinterpret_cast<const unsigned char*>("highlight"),
strlen("highlight"));
yajl_gen_map_open(g);
ops.pop_back();
std::string startingAt = ops.back().str();
ops.pop_back();
std::string size = ops.back().str();
ops.pop_back();
yajl_gen_string(g,
reinterpret_cast<const unsigned char*>("startingAt"),
strlen("startingAt"));
yajl_gen_string(g,
reinterpret_cast<const unsigned char*>(startingAt.c_str()),
startingAt.size());
yajl_gen_string(g, reinterpret_cast<const unsigned char*>("size"),
strlen("size"));
yajl_gen_string(g,
reinterpret_cast<const unsigned char*>(size.c_str()),
size.size());
yajl_gen_map_close(g);
if (stoi(startingAt) >= varValue.size()) {
*err = "Offset is out of the variable limits.";
return -1;
}
yajl_gen_string(g,
reinterpret_cast<const unsigned char*>("value"),
strlen("value"));
value = std::string(varValue, stoi(startingAt), stoi(size));
yajl_gen_string(g,
reinterpret_cast<const unsigned char*>(value.c_str()),
value.size());
}
yajl_gen_map_close(g);
yajl_gen_map_close(g);
yajl_gen_array_close(g);
yajl_gen_map_close(g);
yajl_gen_array_close(g);
yajl_gen_map_close(g);
yajl_gen_get_buf(g, &buf, &jsonSize);
json->assign(reinterpret_cast<const char*>(buf), jsonSize);
json->append("\n");
yajl_gen_free(g);
return 0;
#else
*err = "Without YAJL support, we cannot generate JSON.";
return -1;
#endif
}
void ModSecurity::setServerLogCb(ModSecLogCb cb) {
setServerLogCb(cb, TextLogProperty);
}
void ModSecurity::setServerLogCb(ModSecLogCb cb, int properties) {
m_logCb = (ModSecLogCb) cb;
m_logProperties = properties;
}
/**
* @name msc_set_log_cb
* @brief Set the log callback functiond
*
* It is neccessary to indicate to libModSecurity which function within the
* connector should be called when logging is required.
*
* @oarm msc The current ModSecurity instance
* @param ModSecLogCb The callback function to which a reference to the log msgs
* will be passed.
*
*/
extern "C" void msc_set_log_cb(ModSecurity *msc, ModSecLogCb cb) {
msc->setServerLogCb(cb);
}
/**
* @name msc_set_connector_info
* @brief Set information about the connector that is using the library.
*
* For the purpose of log it is necessary for modsecurity to understand which
* 'connector' is consuming the API.
*
* @note It is strongly recommended to set a information in the following
* pattern:
*
* ConnectorName vX.Y.Z-tag (something else)
*
* For instance: ModSecurity-nginx v0.0.1-alpha
*
* @param connector Information about the connector.
*
*/
extern "C" void msc_set_connector_info(ModSecurity *msc,
const char *connector) {
msc->setConnectorInformation(std::string(connector));
}
/**
* @name msc_who_am_i
* @brief Return information about this ModSecurity version and platform.
*
* Platform and version are two questions that community will ask prior to
* provide support. Making it available internally and to the connector as
* well.
*
* @note This information maybe will be used by a log parser. If you want to
* update it, make it in a fashion that won't break the existent parsers.
* (e.g. adding extra information _only_ to the end of the string)
*/
extern "C" const char *msc_who_am_i(ModSecurity *msc) {
return msc->whoAmI().c_str();
}
/**
* @name msc_cleanup
* @brief Cleanup ModSecurity C API
*
* Cleanup ModSecurity instance.
*
*/
extern "C" void msc_cleanup(ModSecurity *msc) {
delete msc;
}
/**
* @name msc_init
* @brief Initilizes ModSecurity C API
*
* ModSecurity initializer.
*
* Example Usage:
* @code
*
* ModSecurity msc = msc_init();
*
* @endcode
*/
extern "C" ModSecurity *msc_init(void) {
return new ModSecurity();
}
} // namespace modsecurity