diff --git a/apache2/Makefile.am b/apache2/Makefile.am index 8708882a..dcd7d433 100644 --- a/apache2/Makefile.am +++ b/apache2/Makefile.am @@ -8,6 +8,7 @@ mod_security2_la_SOURCES = acmp.c \ libinjection/libinjection_sqli.c \ mod_security2.c \ modsecurity.c \ + msc_status_engine.c \ msc_crypt.c \ msc_geo.c \ msc_gsb.c \ diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 8ff9c8bd..552848f2 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -37,6 +37,7 @@ #include "msc_lua.h" #endif +#include "msc_status_engine.h" /* ModSecurity structure */ @@ -722,6 +723,8 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, "Original server signature: %s", real_server_signature); } + + msc_status_engine_call(); } srand((unsigned int)(time(NULL) * getpid())); diff --git a/apache2/msc_status_engine.c b/apache2/msc_status_engine.c new file mode 100644 index 00000000..6797ca85 --- /dev/null +++ b/apache2/msc_status_engine.c @@ -0,0 +1,433 @@ +/* +* 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 +#include +#endif + +#ifdef DARWIN +#include +#include +#include +#include +#include +#include +#include +#include +#if ! defined(IFT_ETHER) +#define IFT_ETHER 0x6/* Ethernet CSMACD */ +#endif +#endif + +#ifdef __gnu_linux__ +#include +#include +#include +#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; +} + diff --git a/apache2/msc_status_engine.h b/apache2/msc_status_engine.h new file mode 100644 index 00000000..0b3398b9 --- /dev/null +++ b/apache2/msc_status_engine.h @@ -0,0 +1,32 @@ +/* +* 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. +*/ + +#ifndef MSC_STATUS_ENGINE_H +#define MSC_STATUS_ENGINE_H + +#include "apr.h" +#include "apr_pools.h" +#include "apr_version.h" +#include "apr_optional.h" +#include "msc_pcre.h" + +#define STATUS_ENGINE_DNS_IN_BETWEEN_DOTS 13 + +#define STATUS_ENGINE_DNS_SUFFIX "status.modsecurity.org" + +static const char msc_status_engine_basis_32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +int msc_status_engine_call(void); + +#endif diff --git a/standalone/Makefile.am b/standalone/Makefile.am index f75c2d87..c555cc2d 100644 --- a/standalone/Makefile.am +++ b/standalone/Makefile.am @@ -9,6 +9,7 @@ standalone_la_SOURCES = ../apache2/acmp.c \ ../apache2/libinjection/libinjection_sqli.c \ ../apache2/mod_security2.c \ ../apache2/modsecurity.c \ + ../apache2/msc_status_engine.c \ ../apache2/msc_crypt.c \ ../apache2/msc_geo.c \ ../apache2/msc_gsb.c \