/* * 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 #include #endif #ifdef WITH_LIBXML2 #include #include #endif #ifdef MSC_WITH_CURL #include #endif #include #include #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(d.c_str()); m_logCb(data, a); return; } if (m_logProperties & RuleMessageLogProperty) { const void *a = static_cast(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 vars = variables.searchAll(matchString); std::list ops = operators.searchAll(matchString); std::list 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("match"), strlen("match")); yajl_gen_array_open(g); yajl_gen_map_open(g); yajl_gen_string(g, reinterpret_cast("variable"), strlen("variable")); yajl_gen_map_open(g); yajl_gen_string(g, reinterpret_cast("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("startingAt"), strlen("startingAt")); yajl_gen_string(g, reinterpret_cast(startingAt.c_str()), startingAt.size()); yajl_gen_string(g, reinterpret_cast("size"), strlen("size")); yajl_gen_string(g, reinterpret_cast(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("value"), strlen("value")); yajl_gen_array_open(g); yajl_gen_map_open(g); yajl_gen_string(g, reinterpret_cast("value"), strlen("value")); yajl_gen_string(g, reinterpret_cast(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("transformation"), strlen("transformation")); yajl_gen_string(g, reinterpret_cast(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("value"), strlen("value")); yajl_gen_string(g, reinterpret_cast( varValue.c_str()), varValue.size()); yajl_gen_map_close(g); delete t; } yajl_gen_array_close(g); yajl_gen_string(g, reinterpret_cast("operator"), strlen("operator")); yajl_gen_map_open(g); while (ops.size() > 3) { std::string value; yajl_gen_string(g, reinterpret_cast("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("startingAt"), strlen("startingAt")); yajl_gen_string(g, reinterpret_cast(startingAt.c_str()), startingAt.size()); yajl_gen_string(g, reinterpret_cast("size"), strlen("size")); yajl_gen_string(g, reinterpret_cast(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("value"), strlen("value")); value = std::string(varValue, stoi(startingAt), stoi(size)); yajl_gen_string(g, reinterpret_cast(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(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