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 ( <ime );
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