1 /*
2 * ModSecurity for Apache 2.x, http://www.modsecurity.org/
3 * Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/)
4 *
5 * You may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * If any of the files related to licensing are missing or if you have any
11 * other questions related to licensing please contact Trustwave Holdings, Inc.
12 * directly using the email address security@modsecurity.org.
13 */
14 
15 #include "msc_status_engine.h"
16 #include "apr_sha1.h"
17 #include "modsecurity_config.h"
18 
19 #ifdef WIN32
20 #include <winsock2.h>
21 #include <iphlpapi.h>
22 #else
23 #include <sys/ioctl.h>
24 #include <netdb.h>
25 #endif
26 
27 #ifdef DARWIN
28 #include <arpa/inet.h>
29 #include <ifaddrs.h>
30 #include <net/if.h>
31 #include <net/if_dl.h>
32 #include <netinet/in.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #ifndef IFT_ETHER
36 #define IFT_ETHER 0x6 /* Ethernet CSMACD */
37 #endif
38 #endif
39 
40 #if (defined(__linux__) || defined(__gnu_linux__))
41 #include <linux/if.h>
42 #include <linux/sockios.h>
43 #endif
44 #ifdef HAVE_SYS_UTSNAME_H
45 #include <sys/utsname.h>
46 #endif
47 
48 // Bese32 encode, based on:
49 // https://code.google.com/p/google-authenticator/source/browse/libpam/base32.c
msc_status_engine_base32_encode(char * encoded,const char * data,int len)50 int DSOLOCAL msc_status_engine_base32_encode(char *encoded,
51     const char *data, int len) {
52     int buffer;
53     int count = 0;
54     char *result = encoded;
55     int length = strlen(data);
56 
57     buffer = data[0];
58 
59     if (encoded == NULL && len == 0) {
60         len = length * 3;
61         count++;
62     }
63 
64     if (length > 0) {
65         int next = 1;
66         int bitsLeft = 8;
67         while (count < len && (bitsLeft > 0 || next < length)) {
68             int index;
69             if (bitsLeft < 5) {
70                 if (next < length) {
71                     buffer <<= 8;
72                     buffer |= data[next++] & 0xff;
73                     bitsLeft += 8;
74                 } else {
75                     int pad = 5 - bitsLeft;
76                     buffer <<= pad;
77                     bitsLeft += pad;
78                 }
79             }
80             index = 0x1f & (buffer >> (bitsLeft - 5));
81             bitsLeft -= 5;
82             if (encoded != NULL) {
83                 result[count] = msc_status_engine_basis_32[index];
84             }
85             count++;
86         }
87     }
88     if (count < len && encoded != NULL) {
89         result[count] = '\000';
90     }
91 
92     return count;
93 }
94 
msc_status_engine_fill_with_dots(char * encoded_with_dots,const char * data,int len,int space)95 int DSOLOCAL msc_status_engine_fill_with_dots(char *encoded_with_dots,
96     const char *data, int len, int space)
97 {
98     int i;
99     int count = 0;
100 
101     if (encoded_with_dots == NULL) {
102         if (len == 0 && data != NULL) {
103             len = strlen(data);
104         }
105         else if (len == 0 && data == NULL) {
106             count = -1;
107             goto return_length;
108         }
109 
110         count = len/space + len + 1;
111         goto return_length;
112     }
113 
114     for (i = 0; i < strlen(data) && i < len; i++) {
115         if (i % space == 0 && i != 0) {
116             encoded_with_dots[count++] = '.';
117         }
118         encoded_with_dots[count++] = data[i];
119     }
120     encoded_with_dots[count] = '\0';
121 
122 return_length:
123     return count;
124 }
125 
126 
127 // Based on:
128 // http://stackoverflow.com/questions/16858782/how-to-obtain-almost-unique-system-identifier-in-a-cross-platform-way
msc_status_engine_machine_name(char * machine_name,size_t len)129 int DSOLOCAL msc_status_engine_machine_name(char *machine_name, size_t len) {
130 #ifdef WIN32
131     DWORD lenComputerName = len;
132 #endif
133 
134     memset(machine_name, '\0', sizeof(char) * len);
135 
136 #ifdef WIN32
137     if (GetComputerName(machine_name, &lenComputerName) == 0) {
138         goto failed;
139     }
140 #endif
141 
142 #ifdef HAVE_SYS_UTSNAME_H
143    static struct utsname u;
144 
145    if ( uname( &u ) < 0 ) {
146       goto failed;
147    }
148 
149    apr_snprintf(machine_name, len-1, "%s", u.nodename);
150 #endif
151 
152     return 0;
153 
154 failed:
155     return -1;
156 }
157 
msc_status_engine_mac_address(unsigned char * mac)158 int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac)
159 {
160 #ifdef DARWIN
161     struct ifaddrs* ifaphead;
162     struct ifaddrs* ifap;
163     int i = 0;
164 
165     if ( getifaddrs( &ifaphead ) != 0 ) {
166         goto failed;
167     }
168 
169     // iterate over the net interfaces
170     for ( ifap = ifaphead; ifap; ifap = ifap->ifa_next ) {
171         struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifap->ifa_addr;
172         if ( sdl && ( sdl->sdl_family == AF_LINK ) && ( sdl->sdl_type == IFT_ETHER )
173                 && mac[0] && mac[1] && mac[2] && i < 6) {
174 
175             apr_snprintf(mac, MAC_ADDRESS_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
176                 (unsigned char)LLADDR(sdl)[0],
177                 (unsigned char)LLADDR(sdl)[1],
178                 (unsigned char)LLADDR(sdl)[2],
179                 (unsigned char)LLADDR(sdl)[3],
180                 (unsigned char)LLADDR(sdl)[4],
181                 (unsigned char)LLADDR(sdl)[5]);
182             goto end;
183         }
184     }
185 
186     freeifaddrs( ifaphead );
187 #endif
188 
189 #if (defined(__linux__) || defined(__gnu_linux__))
190     struct ifconf conf;
191     int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP );
192     struct ifreq* ifr;
193     if ( sock < 0 ) {
194         goto failed;
195     }
196 
197     char ifconfbuf[ 128 * sizeof(struct ifreq) ];
198     memset( ifconfbuf, 0, sizeof( ifconfbuf ));
199     conf.ifc_buf = ifconfbuf;
200     conf.ifc_len = sizeof( ifconfbuf );
201     if ( ioctl( sock, SIOCGIFCONF, &conf )) {
202         close(sock);
203         goto failed;
204     }
205 
206     for ( ifr = conf.ifc_req; ifr < conf.ifc_req + conf.ifc_len; ifr++ ) {
207         if ( ioctl( sock, SIOCGIFFLAGS, ifr )) {
208             continue;  // failed to get flags, skip it
209         }
210 
211         if ( ioctl( sock, SIOCGIFHWADDR, ifr ) == 0 ) {
212             int i = 0;
213             if (!ifr->ifr_addr.sa_data[0] && !ifr->ifr_addr.sa_data[1]
214                 && !ifr->ifr_addr.sa_data[2]) {
215                 continue;
216             }
217 
218             apr_snprintf(mac, MAC_ADDRESS_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
219                 (unsigned char)ifr->ifr_addr.sa_data[0],
220                 (unsigned char)ifr->ifr_addr.sa_data[1],
221                 (unsigned char)ifr->ifr_addr.sa_data[2],
222                 (unsigned char)ifr->ifr_addr.sa_data[3],
223                 (unsigned char)ifr->ifr_addr.sa_data[4],
224                 (unsigned char)ifr->ifr_addr.sa_data[5]);
225 
226             goto end;
227         }
228     }
229     close( sock );
230 #endif
231 
232 #if WIN32
233     PIP_ADAPTER_INFO pAdapterInfo;
234     PIP_ADAPTER_INFO pAdapter = NULL;
235     DWORD dwRetVal = 0;
236 
237     ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
238     pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof (IP_ADAPTER_INFO));
239     if (!pAdapterInfo) {
240         goto failed;
241     }
242 
243     if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
244         free(pAdapterInfo);
245         pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
246         if (!pAdapterInfo) {
247             goto failed;
248         }
249     }
250 
251     dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
252     if (dwRetVal != NO_ERROR) {
253         free(pAdapterInfo);
254         goto failed;
255     }
256 
257     pAdapter = pAdapterInfo;
258     while (pAdapter && !mac[0] && !mac[1] && !mac[2])
259     {
260         if (pAdapter->AddressLength > 4)
261         {
262             apr_snprintf(mac, MAC_ADDRESS_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
263                 (unsigned char)pAdapter->Address[0],
264                 (unsigned char)pAdapter->Address[1],
265                 (unsigned char)pAdapter->Address[2],
266                 (unsigned char)pAdapter->Address[3],
267                 (unsigned char)pAdapter->Address[4],
268                 (unsigned char)pAdapter->Address[5]);
269             goto end;
270         }
271         pAdapter = pAdapter->Next;
272     }
273 
274     free(pAdapterInfo);
275 #endif
276 
277 end:
278     return 0;
279 failed:
280     return -1;
281 }
282 
msc_status_engine_unique_id(unsigned char * digest)283 int DSOLOCAL msc_status_engine_unique_id (unsigned char *digest)
284 {
285     unsigned char hex_digest[APR_SHA1_DIGESTSIZE];
286     unsigned char *mac_address = NULL;
287     char *machine_name = NULL;
288     int ret = 0;
289     int i = 0;
290     apr_sha1_ctx_t context;
291 
292     mac_address = malloc(sizeof(char)*(MAC_ADDRESS_SIZE));
293     if (!mac_address) {
294         ret = -1;
295         goto failed_mac_address;
296     }
297     memset(mac_address, '\0', sizeof(char)*(MAC_ADDRESS_SIZE));
298 
299     if (msc_status_engine_mac_address(mac_address)) {
300         ret = -1;
301         goto failed_set_mac_address;
302     }
303 
304     machine_name = malloc(sizeof(char)*MAX_MACHINE_NAME_SIZE);
305     if (!machine_name) {
306         ret = -1;
307         goto failed_machine_name;
308     }
309     memset(machine_name, '\0', sizeof(char)*(MAX_MACHINE_NAME_SIZE));
310     if (msc_status_engine_machine_name(machine_name, MAC_ADDRESS_SIZE)) {
311         ret = -1;
312         goto failed_set_machine_name;
313     }
314 
315     apr_sha1_init(&context);
316     apr_sha1_update(&context, machine_name, strlen(machine_name));
317     apr_sha1_update(&context, mac_address, strlen(mac_address));
318     apr_sha1_final(hex_digest, &context);
319 
320     for (i = 0; i < APR_SHA1_DIGESTSIZE; i++)
321     {
322         sprintf(digest, "%s%02x", digest, hex_digest[i]);
323     }
324 
325 failed_set_machine_name:
326     free(machine_name);
327 failed_machine_name:
328 failed_set_mac_address:
329     free(mac_address);
330 failed_mac_address:
331     return ret;
332 }
333 
msc_beacon_string(char * beacon_string,int beacon_string_max_len)334 int DSOLOCAL msc_beacon_string (char *beacon_string, int beacon_string_max_len) {
335     char *apr = NULL;
336     const char *apr_loaded = NULL;
337     char pcre[7];
338     const char *pcre_loaded = NULL;
339     char *lua = NULL;
340     char *libxml = NULL;
341     char *modsec = NULL;
342     const char *apache = NULL;
343     char id[(APR_SHA1_DIGESTSIZE*2) + 1];
344     int beacon_string_len = -1;
345 
346     apr = APR_VERSION_STRING;
347     apr_loaded = apr_version_string();
348     apr_snprintf(pcre, 7, "%d.%d", PCRE_MAJOR, PCRE_MINOR);
349     pcre_loaded = pcre_version();
350 #ifdef WITH_LUA
351     lua = LUA_VERSION;
352 #endif
353     libxml = LIBXML_DOTTED_VERSION;
354     modsec = MODSEC_VERSION;
355 #ifdef VERSION_IIS
356     apache = "IIS";
357 #elif VERSION_NGINX
358     apache = "nginx";
359 #else
360     apache = real_server_signature;
361 #endif
362 
363     /* 6 represents: strlen("(null)") */
364     beacon_string_len = (modsec ? strlen(modsec) : 6) +
365         (apache ? strlen(apache) : 6) + (apr ? strlen(apr) : 6) +
366         (apr_loaded ? strlen(apr_loaded) : 6) + (pcre ? strlen(pcre) : 6) +
367         (pcre_loaded ? strlen(pcre_loaded) : 6) + (lua ? strlen(lua) : 6) +
368         (libxml ? strlen(libxml) : 6) + (APR_SHA1_DIGESTSIZE * 2);
369 
370     beacon_string_len = beacon_string_len + /* null terminator: */ 1 +
371             /* comma: */ 6 +
372             /* slash: */ 2;
373 
374     if (beacon_string == NULL || beacon_string_max_len == 0) {
375         goto return_length;
376     }
377 
378     memset(id, '\0', sizeof(id));
379     if (msc_status_engine_unique_id(id)) {
380         sprintf(id, "no unique id");
381     }
382 
383     apr_snprintf(beacon_string, beacon_string_max_len,
384         "%.25s,%.25s,%s/%s,%s/%s,%s,%s,%s",
385         modsec, apache, apr, apr_loaded, pcre, pcre_loaded, lua, libxml, id);
386 
387 return_length:
388     return beacon_string_len;
389 }
390 
msc_status_engine_prepare_hostname(char * hostname,const char * plain_data,int max_length)391 int DSOLOCAL msc_status_engine_prepare_hostname (char *hostname, const char *plain_data,
392         int max_length)
393 {
394     int str_enc_len = 0;
395     int str_enc_spl_len = 0;
396     char *tmp = NULL;
397     int length = -1;
398     time_t ltime;
399 
400     str_enc_len = msc_status_engine_base32_encode(NULL, plain_data, 0);
401 
402     str_enc_spl_len = msc_status_engine_fill_with_dots(NULL, NULL, str_enc_len,
403             STATUS_ENGINE_DNS_IN_BETWEEN_DOTS);
404     if (str_enc_spl_len < 0) {
405         goto failed_enc_spl_len;
406     }
407 
408     length = str_enc_spl_len + strlen(STATUS_ENGINE_DNS_SUFFIX) +
409         /* epoch: */ 10 + /* dots: */ 2 + /* terminator: */ 1 -
410         /* removed unsed terminators from str_enc and str_enc_spl: */ 2;
411 
412     if (hostname == NULL || max_length == 0) {
413         goto return_length;
414     }
415 
416     memset(hostname, '\0', sizeof(char) * max_length);
417 
418     msc_status_engine_base32_encode(hostname, plain_data, str_enc_len);
419 
420     tmp = strdup(hostname);
421     if (tmp == NULL) {
422         length = -1;
423         goto failed_strdup;
424     }
425 
426     str_enc_spl_len = msc_status_engine_fill_with_dots(hostname, tmp, max_length,
427         STATUS_ENGINE_DNS_IN_BETWEEN_DOTS);
428     if (str_enc_spl_len < 0) {
429         length = -1;
430         goto failed_enc_spl;
431     }
432 
433     time ( &ltime );
434     apr_snprintf(hostname, max_length, "%s.%ld.%s", hostname,
435             (long) ltime, STATUS_ENGINE_DNS_SUFFIX);
436 
437 failed_enc_spl:
438     free(tmp);
439 failed_strdup:
440 return_length:
441 failed_enc_spl_len:
442     return length;
443 }
444 
msc_status_engine_call(void)445 int msc_status_engine_call (void) {
446     char *beacon_str = NULL;
447     int beacon_str_len = 0;
448     char *hostname = NULL;
449     int hostname_len = 0;
450     int ret = -1;
451 
452     /* Retrieve the beacon string */
453     beacon_str_len = msc_beacon_string(NULL, 0);
454 
455     beacon_str = malloc(sizeof(char) * beacon_str_len);
456     if (beacon_str == NULL) {
457         goto failed_beacon_string_malloc;
458     }
459     msc_beacon_string(beacon_str, beacon_str_len);
460 
461     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
462             "ModSecurity: StatusEngine call: \"%s\"", beacon_str);
463 
464     /* Get beacon string in the format of a hostname */
465     hostname_len = msc_status_engine_prepare_hostname(NULL, beacon_str, 0);
466     if (hostname_len < 0) {
467         goto failed_hostname_len;
468     }
469 
470     hostname = malloc(sizeof(char) * hostname_len);
471     if (hostname == NULL) {
472         goto failed_hostname_malloc;
473     }
474     hostname_len = msc_status_engine_prepare_hostname(hostname, beacon_str,
475             hostname_len);
476     if (hostname_len < 0) {
477         goto failed_hostname;
478     }
479 
480     /* Perform the DNS query. */
481     if (gethostbyname(hostname)) {
482         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
483             "ModSecurity: StatusEngine call successfully sent. For more " \
484             "information visit: http://%s/", STATUS_ENGINE_DNS_SUFFIX);
485     } else {
486         ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
487             "ModSecurity: StatusEngine call failed. Query: %s",
488             hostname);
489     }
490 
491     ret = 0;
492 
493 failed_hostname:
494     free(hostname);
495 failed_hostname_malloc:
496 failed_hostname_len:
497     free(beacon_str);
498 failed_beacon_string_malloc:
499 
500     return ret;
501 }
502 
503