mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-14 13:56:01 +03:00
The Status Engine allow the measurements of how many ModSecurity instances are running around the world, as long as information on how many times it had been restarted and so on. Everytime that the server is started it perform a DNS query that is redirected to our servers, that query constains information about the ModSecurity version and it dependencies versions. It also sends a unique indetification generate locally for the server, avoiding counting the same server twice while generating the statistics. This id is a sha-1 hash of the machine name + mac address of the first network adapter. In this commit it is enabled by default, in the release will be a configuration option to disable this functionality. It also important to cite that the information gather by this query will be also available in ModSecurity website public open via a JSON stateless API. There will be an fancy heatmap as well.
434 lines
12 KiB
C
434 lines
12 KiB
C
/*
|
||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||
* Copyright (c) 2004-2013 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 "msc_status_engine.h"
|
||
#include "apr_sha1.h"
|
||
|
||
#ifdef WIN32
|
||
#include <winsock2.h>
|
||
#include <iphlpapi.h>
|
||
#endif
|
||
|
||
#ifdef DARWIN
|
||
#include <arpa/inet.h>
|
||
#include <ifaddrs.h>
|
||
#include <net/if.h>
|
||
#include <net/if_dl.h>
|
||
#include <netinet/in.h>
|
||
#include <sys/utsname.h>
|
||
#include <sys/types.h>
|
||
#include <sys/socket.h>
|
||
#if ! defined(IFT_ETHER)
|
||
#define IFT_ETHER 0x6/* Ethernet CSMACD */
|
||
#endif
|
||
#endif
|
||
|
||
#ifdef __gnu_linux__
|
||
#include <sys/utsname.h>
|
||
#include <linux/if.h>
|
||
#include <linux/sockios.h>
|
||
#endif
|
||
|
||
// Bese32 encode, based on:
|
||
// https://code.google.com/p/google-authenticator/source/browse/libpam/base32.c
|
||
void DSOLOCAL msc_status_engine_base32_encode(char *encoded,
|
||
const char *data, int len) {
|
||
int buffer;
|
||
int count = 0;
|
||
char *result = encoded;
|
||
int length = strlen(data);
|
||
|
||
buffer = data[0];
|
||
|
||
if (length > 0) {
|
||
int next = 1;
|
||
int bitsLeft = 8;
|
||
while (count < len && (bitsLeft > 0 || next < length)) {
|
||
int index;
|
||
if (bitsLeft < 5) {
|
||
if (next < length) {
|
||
buffer <<= 8;
|
||
buffer |= data[next++] & 0xff;
|
||
bitsLeft += 8;
|
||
} else {
|
||
int pad = 5 - bitsLeft;
|
||
buffer <<= pad;
|
||
bitsLeft += pad;
|
||
}
|
||
}
|
||
index = 0x1f & (buffer >> (bitsLeft - 5));
|
||
bitsLeft -= 5;
|
||
result[count++] = msc_status_engine_basis_32[index];
|
||
}
|
||
}
|
||
if (count < len) {
|
||
result[count] = '\000';
|
||
}
|
||
}
|
||
|
||
void DSOLOCAL msc_status_engine_fill_with_dots(char *encoded_with_dots,
|
||
const char *data, int len, int space)
|
||
{
|
||
int i;
|
||
int count = 0;
|
||
|
||
for (i = 0; i < strlen(data) && i < len; i++) {
|
||
if (i % space == 0 && i != 0) {
|
||
encoded_with_dots[count++] = '.';
|
||
}
|
||
encoded_with_dots[count++] = data[i];
|
||
}
|
||
encoded_with_dots[count] = '\0';
|
||
}
|
||
|
||
|
||
// Based on:
|
||
// http://stackoverflow.com/questions/16858782/how-to-obtain-almost-unique-system-identifier-in-a-cross-platform-way
|
||
int DSOLOCAL msc_status_engine_machine_name(char *machine_name, size_t len) {
|
||
memset(machine_name, '\0', sizeof(char) * len);
|
||
|
||
#ifdef WIN32
|
||
DWORD size = 1024;
|
||
GetComputerName(machine_name, &size);
|
||
|
||
apr_snprintf(machine_name, len-1, "%s", computerName[0]);
|
||
machine_name[len - 1] = '\0';
|
||
#else
|
||
static struct utsname u;
|
||
|
||
if ( uname( &u ) < 0 ) {
|
||
goto failed;
|
||
}
|
||
|
||
apr_snprintf(machine_name, len-1, "%s", u.nodename);
|
||
#endif
|
||
|
||
return 0;
|
||
|
||
failed:
|
||
return -1;
|
||
}
|
||
|
||
int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac)
|
||
{
|
||
#ifdef DARWIN
|
||
struct ifaddrs* ifaphead;
|
||
struct ifaddrs* ifap;
|
||
int i = 0;
|
||
|
||
if ( getifaddrs( &ifaphead ) != 0 ) {
|
||
goto failed;
|
||
}
|
||
|
||
// iterate over the net interfaces
|
||
for ( ifap = ifaphead; ifap; ifap = ifap->ifa_next ) {
|
||
struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifap->ifa_addr;
|
||
if ( sdl && ( sdl->sdl_family == AF_LINK ) && ( sdl->sdl_type == IFT_ETHER )
|
||
&& mac[0] && mac[1] && mac[2] && i < 6) {
|
||
for (i = 0; i<6; i++) {
|
||
sprintf(mac, "%s%s%02x", mac, (i == 0)?"":":",
|
||
(unsigned char)LLADDR(sdl)[i]);
|
||
}
|
||
}
|
||
}
|
||
|
||
freeifaddrs( ifaphead );
|
||
#endif
|
||
|
||
#if __gnu_linux__
|
||
struct ifconf conf;
|
||
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP );
|
||
struct ifreq* ifr;
|
||
if ( sock < 0 ) {
|
||
goto failed;
|
||
}
|
||
|
||
char ifconfbuf[ 128 * sizeof(struct ifreq) ];
|
||
memset( ifconfbuf, 0, sizeof( ifconfbuf ));
|
||
conf.ifc_buf = ifconfbuf;
|
||
conf.ifc_len = sizeof( ifconfbuf );
|
||
if ( ioctl( sock, SIOCGIFCONF, &conf )) {
|
||
close(sock);
|
||
goto failed;
|
||
}
|
||
|
||
for ( ifr = conf.ifc_req; ifr < conf.ifc_req + conf.ifc_len; ifr++ ) {
|
||
if ( ioctl( sock, SIOCGIFFLAGS, ifr )) {
|
||
continue; // failed to get flags, skip it
|
||
}
|
||
|
||
if ( ioctl( sock, SIOCGIFHWADDR, ifr ) == 0 ) {
|
||
int i = 0;
|
||
if (!ifr->ifr_addr.sa_data[0] && !ifr->ifr_addr.sa_data[1]
|
||
&& !ifr->ifr_addr.sa_data[2]) {
|
||
continue;
|
||
}
|
||
|
||
for (i = 0; i<6; i++) {
|
||
sprintf(mac, "%s%s%02x", mac, (i == 0)?"":":",
|
||
(unsigned char)ifr->ifr_addr.sa_data[i]);
|
||
}
|
||
}
|
||
}
|
||
close( sock );
|
||
#endif
|
||
|
||
#if WIN32
|
||
PIP_ADAPTER_INFO pAdapterInfo;
|
||
PIP_ADAPTER_INFO pAdapter = NULL;
|
||
DWORD dwRetVal = 0;
|
||
UINT i;
|
||
|
||
ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
|
||
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof (IP_ADAPTER_INFO));
|
||
if (!pAdapterInfo) {
|
||
goto failed;
|
||
}
|
||
|
||
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
|
||
free(pAdapterInfo);
|
||
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
|
||
if (!pAdapterInfo) {
|
||
goto failed;
|
||
}
|
||
}
|
||
|
||
dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
|
||
if (dwRetVal != NO_ERROR) {
|
||
free(pAdapterInfo);
|
||
goto failed;
|
||
}
|
||
|
||
pAdapter = pAdapterInfo;
|
||
while (pAdapter && !mac[0] && !mac[1] && !mac[2])
|
||
{
|
||
if (pAdapter->AddressLength > 4)
|
||
{
|
||
for (i = 0; i < pAdapter->AddressLength && i < 6; i++)
|
||
{
|
||
sprintf(mac, "%s%s%02x", mac, (i == 0) ? "" : ":",
|
||
(unsigned char)pAdapter->Address[i]);
|
||
}
|
||
}
|
||
pAdapter = pAdapter->Next;
|
||
}
|
||
|
||
free(pAdapterInfo);
|
||
#endif
|
||
|
||
done:
|
||
return 0;
|
||
|
||
failed:
|
||
return -1;
|
||
}
|
||
|
||
int DSOLOCAL msc_status_engine_unique_id (unsigned char *digest)
|
||
{
|
||
unsigned char local_digest[APR_SHA1_DIGESTSIZE];
|
||
apr_sha1_ctx_t context;
|
||
char *input;
|
||
int i = 0;
|
||
unsigned char *mac_address = NULL;
|
||
char *machine_name = NULL;
|
||
int ret = 0;
|
||
|
||
mac_address = malloc(sizeof(char)*(6+5+1));
|
||
if (!mac_address) {
|
||
ret = -1;
|
||
goto failed_mac_address;
|
||
}
|
||
memset(mac_address, 0, sizeof(char)*(6+5+1));
|
||
|
||
if (msc_status_engine_mac_address(mac_address)) {
|
||
ret = -1;
|
||
goto failed_set_mac_address;
|
||
}
|
||
|
||
machine_name = malloc(sizeof(char)*201);
|
||
if (!machine_name) {
|
||
ret = -1;
|
||
goto failed_machine_name;
|
||
}
|
||
|
||
if (msc_status_engine_machine_name(machine_name, 200)) {
|
||
ret = -1;
|
||
goto failed_set_machine_name;
|
||
}
|
||
|
||
input = malloc(sizeof(char)*(strlen(machine_name) +
|
||
strlen(mac_address)+1));
|
||
if (!input) {
|
||
goto failed_input;
|
||
}
|
||
|
||
apr_snprintf(input, strlen(machine_name)+strlen(mac_address)+1,
|
||
"%s%s", machine_name, mac_address);
|
||
|
||
apr_sha1_init(&context);
|
||
apr_sha1_update(&context, input, strlen(input));
|
||
apr_sha1_final(local_digest, &context);
|
||
|
||
for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) {
|
||
sprintf(digest, "%s%02x", digest, local_digest[i]);
|
||
}
|
||
|
||
free(input);
|
||
failed_input:
|
||
failed_set_machine_name:
|
||
free(machine_name);
|
||
failed_machine_name:
|
||
failed_set_mac_address:
|
||
free(mac_address);
|
||
failed_mac_address:
|
||
|
||
return ret;
|
||
}
|
||
|
||
int msc_status_engine_call (void) {
|
||
char *apr = NULL;
|
||
const char *apr_loaded = NULL;
|
||
char pcre[7];
|
||
const char *pcre_loaded = NULL;
|
||
char *lua = NULL;
|
||
char *lua_loaded = NULL;
|
||
char *libxml = NULL;
|
||
char *libxml_loaded = NULL;
|
||
char *modsec = NULL;
|
||
const char *apache = NULL;
|
||
char *id = NULL;
|
||
char *beacon_string = NULL;
|
||
int beacon_string_len = 0;
|
||
char *beacon_string_encoded = NULL;
|
||
char *beacon_string_ready = NULL;
|
||
char *beacon_string_encoded_splitted = NULL;
|
||
int ret = 0;
|
||
|
||
apr = APR_VERSION_STRING;
|
||
apr_loaded = apr_version_string();
|
||
|
||
pcre_loaded = pcre_version();
|
||
apr_snprintf(pcre, 7, "%2d.%2d ", PCRE_MAJOR, PCRE_MINOR);
|
||
#ifdef WITH_LUA
|
||
lua = LUA_VERSION;
|
||
#endif
|
||
lua_loaded = 0;
|
||
|
||
libxml = LIBXML_DOTTED_VERSION;
|
||
libxml_loaded = 0;
|
||
|
||
modsec = MODSEC_VERSION;
|
||
apache = apache_get_server_version();
|
||
|
||
id = malloc(sizeof(char)*((2*APR_SHA1_DIGESTSIZE)+1));
|
||
if (!id) {
|
||
ret = -1;
|
||
goto failed_id;
|
||
}
|
||
|
||
memset(id, '\0', sizeof(char)*((2*APR_SHA1_DIGESTSIZE)+1));
|
||
if (msc_status_engine_unique_id(id)) {
|
||
sprintf(id, "unique id failed");
|
||
}
|
||
|
||
beacon_string_len = (modsec ? strlen(modsec) : 6) + (apache ? strlen(apache) : 6) +
|
||
(apr ? strlen(apr) : 6) + (apr_loaded ? strlen(apr_loaded) : 6) +
|
||
(pcre ? strlen(pcre) : 6) + (pcre_loaded ? strlen(pcre_loaded) : 6) +
|
||
(lua ? strlen(lua) : 6) + (lua_loaded ? strlen(lua_loaded) : 6) +
|
||
(libxml ? strlen(libxml) : 6) + (libxml_loaded ? strlen(libxml_loaded) : 6) +
|
||
(id ? strlen(id) : 6);
|
||
|
||
#ifdef WIN32
|
||
beacon_string = malloc(sizeof(char)*(beacon_string_len+1+10+4));
|
||
if (!beacon_string) {
|
||
goto failed_beacon_string;
|
||
}
|
||
|
||
apr_snprintf(beacon_string, beacon_string_len+1+10+4,
|
||
"%s,%s/IIS,%s/%s,%s/%s,%s/%s,%s/%s,%s",
|
||
modsec, apache, apr, apr_loaded, pcre, pcre_loaded, lua, lua_loaded,
|
||
libxml, libxml_loaded, id);
|
||
#else
|
||
beacon_string = malloc(sizeof(char)*(beacon_string_len+1+10));
|
||
if (!beacon_string) {
|
||
goto failed_beacon_string;
|
||
}
|
||
|
||
apr_snprintf(beacon_string, beacon_string_len+1+10,
|
||
"%s,%s,%s/%s,%s/%s,%s/%s,%s/%s,%s",
|
||
modsec, apache, apr, apr_loaded, pcre, pcre_loaded, lua, lua_loaded,
|
||
libxml, libxml_loaded, id);
|
||
#endif
|
||
|
||
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
|
||
"ModSecurity: StatusEngine call: \"%s\"", beacon_string);
|
||
|
||
beacon_string_encoded = malloc(sizeof(char)*(strlen(beacon_string)*3));
|
||
if (!beacon_string_encoded) {
|
||
goto failed_beacon_string_encoded;
|
||
}
|
||
msc_status_engine_base32_encode(beacon_string_encoded, beacon_string,
|
||
strlen(beacon_string)*3);
|
||
|
||
beacon_string_encoded_splitted = malloc (sizeof(char) *
|
||
((strlen(beacon_string_encoded)/STATUS_ENGINE_DNS_IN_BETWEEN_DOTS) +
|
||
strlen(beacon_string_encoded) + 1 + 1));
|
||
if (!beacon_string_encoded_splitted) {
|
||
goto failed_beacon_string_encoded_splitted;
|
||
}
|
||
|
||
msc_status_engine_fill_with_dots(beacon_string_encoded_splitted,
|
||
beacon_string_encoded,
|
||
(strlen(beacon_string_encoded)/STATUS_ENGINE_DNS_IN_BETWEEN_DOTS) +
|
||
strlen(beacon_string_encoded) + 1 + 1,
|
||
STATUS_ENGINE_DNS_IN_BETWEEN_DOTS);
|
||
|
||
beacon_string_ready = malloc (sizeof(char) *
|
||
strlen(beacon_string_encoded_splitted) +
|
||
strlen(STATUS_ENGINE_DNS_SUFFIX) + 10 + 1);
|
||
|
||
if (!beacon_string_ready) {
|
||
goto failed_beacon_string_ready;
|
||
}
|
||
|
||
apr_snprintf(beacon_string_ready, strlen(beacon_string_encoded_splitted) +
|
||
strlen(STATUS_ENGINE_DNS_SUFFIX) + 12 + 1, "%s.%lu.%s",
|
||
beacon_string_encoded_splitted, time(NULL),
|
||
STATUS_ENGINE_DNS_SUFFIX);
|
||
|
||
if (gethostbyname(beacon_string_ready)) {
|
||
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
|
||
"ModSecurity: StatusEngine call successfully sent. For more " \
|
||
"information visit: http://%s/", STATUS_ENGINE_DNS_SUFFIX);
|
||
} else {
|
||
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
|
||
"ModSecurity: StatusEngine call failed. Query: %s",
|
||
beacon_string_ready);
|
||
}
|
||
|
||
free(beacon_string_ready);
|
||
failed_beacon_string_ready:
|
||
free(beacon_string_encoded_splitted);
|
||
failed_beacon_string_encoded_splitted:
|
||
free(beacon_string_encoded);
|
||
failed_beacon_string_encoded:
|
||
free(beacon_string);
|
||
failed_beacon_string:
|
||
free(id);
|
||
failed_id:
|
||
|
||
return ret;
|
||
}
|
||
|