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_remote_rules.h"
16 #include "msc_status_engine.h"
17
18 #include <apr_thread_pool.h>
19
20 #ifdef WITH_CURL
21 #include <curl/curl.h>
22 #endif
23
24 #include <apu.h>
25
26 #ifdef WITH_REMOTE_RULES
27 #ifdef WITH_APU_CRYPTO
28 #include <apr_crypto.h>
29 #endif
30 #include <apr_sha1.h>
31 #endif
32
33 #ifndef AP_MAX_ARGC
34 #define AP_MAX_ARGC 64
35 #endif
36
37 /**
38 * @brief Find command in a list.
39 *
40 * This is a duplicate of `ap_find_command', which is part of the standalone module.
41 * Apache versions does not include the standalone, thus, this is necessary for
42 * the Apache versions. Once it is here it may not be necessary to be part of
43 * the standalone module, but, for this version both function will co-exist
44 * avoiding problems with 3rd parties that are extending the standalone module.
45 *
46 * @note Prefer this function instead of `ap_finc_command` which is part of the
47 * standalone module.
48 *
49 * @param parms char pointer, function name.
50 * @param cmds pointer to command_rec[].
51 * @retval NULL if command was not found.
52 *
53 */
msc_remote_find_command(const char * name,const command_rec * cmds)54 const command_rec *msc_remote_find_command(const char *name, const command_rec *cmds)
55 {
56 while (cmds->name) {
57 if (!strcasecmp(name, cmds->name))
58 return cmds;
59
60 ++cmds;
61 }
62
63 return NULL;
64 }
65
66
67 /**
68 * @brief Insert a new SecRule to be processed by ModSecurity
69 *
70 * This is a duplicate of `invoke_cmd', which is part of the standalone module.
71 * Apache versions does not include the standalone, thus, this is necessary for
72 * the Apache versions. Once it is here it may not be necessary to be part of
73 * the standalone module, but, for this version both function will co-exist
74 * avoiding problems with 3rd parties that are extending the standalone module.
75 *
76 * @note Prefer this function instead of `invoke_cmd` which is part of the
77 * standalone module.
78 *
79 * @param cmd pointer to command_rec structure.
80 * @param parms pointer to cmd_params strucutre.
81 * @param mconfig pointer to main config structure.
82 * @param args SecRule arguments.
83 * @retval NULL if everything worked as expected otherwise an error message.
84 *
85 */
msc_remote_invoke_cmd(const command_rec * cmd,cmd_parms * parms,void * mconfig,const char * args)86 const char *msc_remote_invoke_cmd(const command_rec *cmd, cmd_parms *parms,
87 void *mconfig, const char *args)
88 {
89 char *w, *w2, *w3;
90 const char *errmsg = NULL;
91
92 if ((parms->override & cmd->req_override) == 0)
93 return apr_pstrcat(parms->pool, cmd->name, " not allowed here", NULL);
94
95 parms->info = cmd->cmd_data;
96 parms->cmd = cmd;
97
98 switch (cmd->args_how) {
99 case RAW_ARGS:
100 #ifdef RESOLVE_ENV_PER_TOKEN
101 args = ap_resolve_env(parms->pool,args);
102 #endif
103 return cmd->AP_RAW_ARGS(parms, mconfig, args);
104
105 case TAKE_ARGV:
106 {
107 char *argv[AP_MAX_ARGC];
108 int argc = 0;
109
110 do {
111 w = ap_getword_conf(parms->pool, &args);
112 if (*w == '\0' && *args == '\0') {
113 break;
114 }
115 argv[argc] = w;
116 argc++;
117 } while (argc < AP_MAX_ARGC && *args != '\0');
118
119 return cmd->AP_TAKE_ARGV(parms, mconfig, argc, argv);
120 }
121
122 case NO_ARGS:
123 if (*args != 0)
124 return apr_pstrcat(parms->pool, cmd->name, " takes no arguments",
125 NULL);
126
127 return cmd->AP_NO_ARGS(parms, mconfig);
128 case TAKE1:
129 w = ap_getword_conf(parms->pool, &args);
130
131 if (*w == '\0' || *args != 0)
132 return apr_pstrcat(parms->pool, cmd->name, " takes one argument",
133 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
134
135 return cmd->AP_TAKE1(parms, mconfig, w);
136 case TAKE2:
137 w = ap_getword_conf(parms->pool, &args);
138 w2 = ap_getword_conf(parms->pool, &args);
139
140 if (*w == '\0' || *w2 == '\0' || *args != 0)
141 return apr_pstrcat(parms->pool, cmd->name, " takes two arguments",
142 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
143
144 return cmd->AP_TAKE2(parms, mconfig, w, w2);
145 case TAKE12:
146 w = ap_getword_conf(parms->pool, &args);
147 w2 = ap_getword_conf(parms->pool, &args);
148
149 if (*w == '\0' || *args != 0)
150 return apr_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments",
151 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
152
153 return cmd->AP_TAKE2(parms, mconfig, w, *w2 ? w2 : NULL);
154 case TAKE3:
155 w = ap_getword_conf(parms->pool, &args);
156 w2 = ap_getword_conf(parms->pool, &args);
157 w3 = ap_getword_conf(parms->pool, &args);
158
159 if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0)
160 return apr_pstrcat(parms->pool, cmd->name,
161 " takes three arguments",
162 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
163
164 return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
165 case TAKE23:
166
167 w = ap_getword_conf(parms->pool, &args);
168 w2 = ap_getword_conf(parms->pool, &args);
169 w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
170
171 if (*w == '\0' || *w2 == '\0' || *args != 0)
172 return apr_pstrcat(parms->pool, cmd->name,
173 " takes two or three arguments",
174 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
175
176 return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
177 case TAKE123:
178 w = ap_getword_conf(parms->pool, &args);
179 w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
180 w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
181
182 if (*w == '\0' || *args != 0)
183 return apr_pstrcat(parms->pool, cmd->name,
184 " takes one, two or three arguments",
185 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
186
187 return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
188 case TAKE13:
189 w = ap_getword_conf(parms->pool, &args);
190 w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
191 w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
192
193 if (*w == '\0' || (w2 && *w2 && !w3) || *args != 0)
194 return apr_pstrcat(parms->pool, cmd->name,
195 " takes one or three arguments",
196 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
197
198 return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
199 case ITERATE:
200 while (*(w = ap_getword_conf(parms->pool, &args)) != '\0') {
201
202 errmsg = cmd->AP_TAKE1(parms, mconfig, w);
203
204 if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
205 return errmsg;
206 }
207
208 return errmsg;
209 case ITERATE2:
210 w = ap_getword_conf(parms->pool, &args);
211
212 if (*w == '\0' || *args == 0)
213 return apr_pstrcat(parms->pool, cmd->name,
214 " requires at least two arguments",
215 cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
216
217 while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0') {
218
219 errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2);
220
221 if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
222 return errmsg;
223 }
224
225 return errmsg;
226 case FLAG:
227 w = ap_getword_conf(parms->pool, &args);
228
229 if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off")))
230 return apr_pstrcat(parms->pool, cmd->name, " must be On or Off",
231 NULL);
232
233 return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0);
234 default:
235 return apr_pstrcat(parms->pool, cmd->name,
236 " is improperly configured internally (server bug)",
237 NULL);
238 }
239 }
240
241 /**
242 * @brief Fetch an URL and fill the content into a memory buffer.
243 *
244 * Fill an msc_curl_memory_buffer_t structure with the content of an given
245 * URL.
246 *
247 * @note While fetching the content, it will present the ModSecurity instance
248 * to the remote server, trough: ModSecurity Unique ID, ModSecurity
249 * status line and also, if given, key that can be used to
250 * authentication. Such data is presented in the following HTTP headers:
251 * - ModSec-status
252 * - ModSec-unique-id
253 * - ModSec-key
254 *
255 * @warning Cleanup the memory after use it.
256 *
257 * @param mp pointer to the memory pool.
258 * @param uri URI to be fetched.
259 * @param key KEY to be present as ModSec-key.
260 * @param chunk pointer to an msc_curl_memory_buffer_t struct.
261 * @param error_msg pointer an char pointer, filled is something went wrong.
262 *
263 * @retval n>=0 everything went fine.
264 * @retval n<-1 Something wrong happened, further details on error_msg.
265 * n=-2 Download failed, but operation should not be aborted.
266 * n=-3 ModSecurity was not compiled with curl support.
267 *
268 */
msc_remote_download_content(apr_pool_t * mp,const char * uri,const char * key,struct msc_curl_memory_buffer_t * chunk,char ** error_msg)269 int msc_remote_download_content(apr_pool_t *mp, const char *uri, const char *key,
270 struct msc_curl_memory_buffer_t *chunk, char **error_msg)
271 {
272 #ifdef WITH_CURL
273 CURL *curl;
274 CURLcode res;
275
276 char id[(APR_SHA1_DIGESTSIZE*2) + 1];
277 char *apr_id = NULL;
278 char *beacon_str = NULL;
279 char *beacon_apr = NULL;
280 int beacon_str_len = 0;
281 int ret = 0;
282
283 chunk->size = 0;
284
285 memset(id, '\0', sizeof(id));
286 if (msc_status_engine_unique_id(id))
287 {
288 sprintf(id, "no unique id");
289 }
290
291 apr_id = apr_psprintf(mp, "ModSec-unique-id: %s", id);
292
293 curl = curl_easy_init();
294
295 beacon_str_len = msc_beacon_string(NULL, 0);
296
297 beacon_str = malloc(sizeof(char) * beacon_str_len + 1);
298 if (beacon_str == NULL)
299 {
300 beacon_str = "Failed to retrieve beacon string";
301 beacon_apr = apr_psprintf(mp, "ModSec-status: %s", beacon_str);
302 }
303 else
304 {
305 msc_beacon_string(beacon_str, beacon_str_len);
306 beacon_apr = apr_psprintf(mp, "ModSec-status: %s", beacon_str);
307 free(beacon_str);
308 }
309
310 if (curl)
311 {
312 struct curl_slist *headers_chunk = NULL;
313 #ifdef WIN32
314 char *buf = malloc(sizeof(TCHAR) * (2048 + 1));
315 if (buf == NULL) { /* malloc failed... */
316 *error_msg = apr_psprintf(mp, "Unable to allocate memory");
317 ret = -2;
318 goto failed;
319 }
320 char *ptr = NULL;
321 DWORD res_len;
322 #endif
323 curl_easy_setopt(curl, CURLOPT_URL, uri);
324
325 headers_chunk = curl_slist_append(headers_chunk, apr_id);
326 headers_chunk = curl_slist_append(headers_chunk, beacon_apr);
327 if (key != NULL)
328 {
329 char *header_key = NULL;
330 header_key = apr_psprintf(mp, "ModSec-key: %s", key);
331 headers_chunk = curl_slist_append(headers_chunk, header_key);
332 }
333
334 /* Make it TLS 1.x only. */
335 curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
336
337 #ifdef WIN32
338 res_len = SearchPathA(NULL, "curl-ca-bundle.crt", NULL, (2048 + 1), buf, &ptr);
339 if (res_len > 0) {
340 curl_easy_setopt(curl, CURLOPT_CAINFO, strdup(buf));
341 }
342 free(buf);
343 #endif
344
345 /* those are the default options, but lets make sure */
346 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
347 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
348
349 /* send all data to this function */
350 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, msc_curl_write_memory_cb);
351
352 /* we pass our 'chunk' struct to the callback function */
353 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk);
354
355 curl_easy_setopt(curl, CURLOPT_USERAGENT, "modesecurity");
356 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_chunk);
357
358 /* We want Curl to return error in case there is an HTTP error code */
359 curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
360
361 res = curl_easy_perform(curl);
362
363 if (res != CURLE_OK)
364 {
365 if (remote_rules_fail_action == REMOTE_RULES_WARN_ON_FAIL)
366 {
367 if (remote_rules_fail_message == NULL)
368 {
369 remote_rules_fail_message = "";
370 }
371
372 remote_rules_fail_message = apr_psprintf(mp, "%sFailed to " \
373 "download: \"%s\" error: %s. ",
374 remote_rules_fail_message, uri,
375 curl_easy_strerror(res));
376
377 ret = -2;
378 goto failed;
379 }
380 else
381 {
382 *error_msg = apr_psprintf(mp, "Failed to download: \"%s\" " \
383 "error: %s ",
384 uri, curl_easy_strerror(res));
385
386 ret = -1;
387 goto failed;
388 }
389 }
390
391 curl_slist_free_all(headers_chunk);
392 }
393
394 failed:
395 curl_easy_cleanup(curl);
396
397 return ret;
398 #else
399 return -3;
400 #endif
401 }
402
403
404 /**
405 * @brief Setup an apr_crypto_key_t from a given password and salt.
406 *
407 * apr_crypto_* demands the key to be in a format of an apr_crypto_key_t which
408 * is an structure that may be defined depending on the crypto provider, thus,
409 * making necessary for us to create this structure using apr internal
410 * functions.
411 *
412 * @warning We trust that the paramenter used in apr, such as the algorithm,
413 * key size and other parameters won't change, if they do it may
414 * break the interoperability with this function with others
415 * implementations, as the key will end up with a different value
416 * than the one expected.
417 *
418 * @param pool pointer to the memory pool to be used.
419 * @param key password to be used while creating the key.
420 * @param apr_key pointer to the pointer of an apr_crypto_key_t structure.
421 * @param f pointer to apr_crypto_t.
422 * @param salt string to be used as salt of the key generation
423 * @param error_msg pointer to char pointer, which is filled if something
424 * went wrong.
425 *
426 * @retval n>=0 everything went fine.
427 * @retval n<-1 Something wrong happened, check error_msg for further details.
428 *
429 */
430 #ifdef WITH_APU_CRYPTO
msc_remote_enc_key_setup(apr_pool_t * pool,const char * key,apr_crypto_key_t ** apr_key,apr_crypto_t * f,unsigned char * salt,char ** error_msg)431 int msc_remote_enc_key_setup(apr_pool_t *pool,
432 const char *key,
433 apr_crypto_key_t **apr_key,
434 apr_crypto_t *f,
435 unsigned char *salt,
436 char **error_msg)
437 {
438 apr_size_t key_len = strlen(key);
439 apr_size_t salt_len = 16; //FIXME: salt_len should not be hard coded.
440
441 const int do_pad = 1;
442 apr_status_t rv;
443
444 rv = apr_crypto_passphrase(
445 (apr_crypto_key_t **) apr_key,
446 NULL,
447 (const char *) key,
448 (apr_size_t) key_len,
449 (const unsigned char *) salt,
450 (apr_size_t) salt_len,
451 APR_KEY_AES_256,
452 APR_MODE_CBC,
453 (const int) do_pad,
454 (const int) 4096,
455 (const apr_crypto_t *) f,
456 (apr_pool_t *) pool);
457
458 if (rv == APR_ENOKEY)
459 {
460 *error_msg = "Internal error - apr_crypto_passphrase: Missing key";
461 return -1;
462 }
463 else if (rv == APR_EPADDING)
464 {
465 *error_msg = "Internal error - apr_crypto_passphrase: APR_EPADDING";
466 return -1;
467 }
468 else if (rv == APR_EKEYTYPE)
469 {
470 *error_msg = "Internal error - apr_crypto_passphrase: APR_EKEYTYPE";
471 return -1;
472 }
473 else if (rv != APR_SUCCESS)
474 {
475 *error_msg = "Internal error - apr_crypto_passphrase: Unknown error";
476 return -1;
477 }
478
479 return 0;
480 }
481 #endif
482
483 /**
484 * @brief Decrypt an buffer into a memory buffer.
485 *
486 * Decrypt an msc_curl_memory_buffer_t structure into another
487 * msc_curl_memory_buffer_t.
488 *
489 * Using the key provided, it creates and apr_key and uses it to decript the
490 * content provided on chunk. The plain text content is saved under
491 * chunk_plain.
492 *
493 * @warning Cleanup memory after usage.
494 *
495 * @param pool pointer to the memory pool to be used.
496 * @param key pointer to the char array to be used as the key.
497 * @param chunk msc_curl_memory_buffer_t that contains the encrypted content.
498 * @param plain_text unsigned char which will hold the content of an url
499 * @param plain_text_len size of the plain_text buffer
500 * @param error_msg pointer to char pointer that is filled if something went
501 * wrong.
502 *
503 * @retval n>=0 everything went fine.
504 * @retval n<-1 Something wrong happened, further details on error_msg.
505 *
506 */
507 #ifdef WITH_APU_CRYPTO
msc_remote_decrypt(apr_pool_t * pool,const char * key,struct msc_curl_memory_buffer_t * chunk,unsigned char ** plain_text,apr_size_t * plain_text_len,char ** error_msg)508 int msc_remote_decrypt(apr_pool_t *pool,
509 const char *key,
510 struct msc_curl_memory_buffer_t *chunk,
511 unsigned char **plain_text,
512 apr_size_t *plain_text_len,
513 char **error_msg)
514 {
515 apr_crypto_key_t *apr_key = NULL;
516 apr_crypto_t *f = NULL;
517 const apr_crypto_driver_t *driver = NULL;
518 const apu_err_t *err = NULL;
519 apr_status_t rv;
520 unsigned char *iv = NULL;
521 unsigned char *ciphered_text = NULL;
522 unsigned char *salt = NULL;
523
524 apr_crypto_block_t *block = NULL;
525 apr_size_t block_size = 0;
526 apr_size_t len = 0;
527
528 // FIXME: size should not be hardcoded.
529 // at least size of IV + Salt
530 if (chunk->size < 16+16+1)
531 {
532 *error_msg = "Failed to download rules from a remote server: " \
533 "Unexpected content.";
534 return -1;
535 }
536 iv = chunk->memory;
537 salt = chunk->memory + 16;
538 ciphered_text = chunk->memory + (16 + 16);
539
540 rv = apr_crypto_init(pool);
541 if (rv != APR_SUCCESS)
542 {
543 *error_msg = "Internal error: failed to init crypto";
544 return -1;
545 }
546
547 rv = apr_crypto_get_driver(&driver, APU_CRYPTO_RECOMMENDED_DRIVER, NULL,
548 &err, pool);
549
550 if (rv != APR_SUCCESS || driver == NULL)
551 {
552 *error_msg = "Internal error - apr_crypto_get_driver: Unknown error";
553 return -1;
554 }
555
556 rv = apr_crypto_make(&f, driver, NULL, pool);
557 if (rv != APR_SUCCESS)
558 {
559 *error_msg = "Internal error - apr_crypto_make: Unknown error";
560 return -1;
561 }
562
563 msc_remote_enc_key_setup(pool, key, &apr_key, f, salt, error_msg);
564 if (*error_msg != NULL)
565 {
566 return -1;
567 }
568
569 rv = apr_crypto_block_decrypt_init(&block, &block_size, iv, apr_key, pool);
570 if (rv == APR_ENOKEY)
571 {
572 *error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
573 "Missing key";
574 return -1;
575 }
576 else if (rv == APR_ENOIV)
577 {
578 *error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
579 "Missing IV";
580 return -1;
581 }
582 else if (rv == APR_EKEYTYPE)
583 {
584 *error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
585 "Wrong key type";
586 return -1;
587 }
588 else if (rv == APR_EKEYLENGTH)
589 {
590 *error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
591 "Wrong key length";
592 return -1;
593 }
594 else if (rv != APR_SUCCESS)
595 {
596 *error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
597 "Unknown error";
598 return -1;
599 }
600
601 //FIXME: size should not be hardcoded like that.
602 // 32 = iv + salt size.
603 rv = apr_crypto_block_decrypt(plain_text, plain_text_len,
604 ciphered_text, (apr_size_t) chunk->size - (16 + 16) ,
605 block);
606
607 if (rv != APR_SUCCESS)
608 {
609 *error_msg = "Internal error - apr_crypto_block_decrypt: Failed to " \
610 "decrypt";
611 return -1;
612 }
613
614 /* finalise the decryption */
615
616 rv = apr_crypto_block_decrypt_finish(*plain_text + *plain_text_len, &len,
617 block);
618 if (rv != APR_SUCCESS)
619 {
620 *error_msg = "Internal error - apr_crypto_block_decrypt_finish: " \
621 "Failed to decrypt";
622 return -1;
623 }
624
625 apr_crypto_block_cleanup(block);
626 apr_crypto_cleanup(f);
627
628 // Shutdown the apr_crypto seems to be the correct thing to do.
629 // However it seems to add instability especially if mod_ssl is enabled.
630 // apr_crypto_shutdown(driver);
631
632 return 0;
633 }
634 #endif
635
636 /**
637 * @brief Add SecRules from a given URI.
638 *
639 * Fetch the URI and using the key provided into params decrypt and install
640 * the downloaded set of rules.
641 *
642 * @warning Cleanup the memory may be necessary.
643 *
644 * @param orig_parms origin parms used at SecRemoteRule
645 * @param remote_rules_server pointer to the filled msc_remote_rules_server
646 * structure.
647 * @param error_msg pointer to char pointer that will be filled if something
648 * went wrong.
649 *
650 *
651 * @retval n>=0 everything went fine.
652 * @retval n<-1 Something wrong happened, further details on error_msg.
653 *
654 */
msc_remote_add_rules_from_uri(cmd_parms * orig_parms,msc_remote_rules_server * remote_rules_server,char ** error_msg)655 int msc_remote_add_rules_from_uri(cmd_parms *orig_parms,
656 msc_remote_rules_server *remote_rules_server,
657 char **error_msg)
658 {
659
660 #ifdef WITH_REMOTE_RULES
661 struct msc_curl_memory_buffer_t downloaded_content;
662 unsigned char *plain_text = NULL;
663 int len = 0;
664 int start = 0;
665 int end = 0;
666 int added_rules = 0;
667 int res = 0;
668 apr_size_t plain_text_len = 0;
669
670 apr_pool_t *mp = orig_parms->pool;
671
672 downloaded_content.size = 0;
673 downloaded_content.memory = NULL;
674
675 res = msc_remote_download_content(mp, remote_rules_server->uri,
676 remote_rules_server->key, &downloaded_content, error_msg);
677 if (*error_msg != NULL)
678 {
679 return -1;
680 }
681 /* error_msg is not filled when the user set SecRemoteRulesFailAction
682 * to warn
683 */
684 if (res != 0)
685 {
686 return res;
687 }
688
689 if (remote_rules_server->crypto == 1)
690 {
691 #ifdef WITH_APU_CRYPTO
692 msc_remote_decrypt(mp, remote_rules_server->key, &downloaded_content,
693 &plain_text,
694 &plain_text_len,
695 error_msg);
696
697 if (*error_msg != NULL)
698 {
699 msc_remote_clean_chunk(&downloaded_content);
700 return -1;
701 }
702 #else
703 *error_msg = "ModSecurity was not compiled with crypto support.\n";
704 msc_remote_clean_chunk(&downloaded_content);
705 return -1;
706 #endif
707 msc_remote_clean_chunk(&downloaded_content);
708 }
709 else
710 {
711 plain_text = downloaded_content.memory;
712 plain_text_len = strlen(plain_text);
713 }
714
715 len = 0;
716 plain_text_len = strlen(plain_text);
717 while (len < plain_text_len)
718 {
719 if (plain_text[len] == '\n')
720 {
721 const char *rule = NULL;
722 int tmp = len;
723 char *cmd_name = NULL;
724 char *word = NULL;
725 const command_rec *cmd;
726
727 ap_directive_t *newdir;
728 cmd_parms *parms = apr_pcalloc(mp, sizeof (cmd_parms));
729
730 rule = plain_text + start;
731 end = len;
732 plain_text[len] = '\0';
733
734 memcpy(parms, orig_parms, sizeof(cmd_parms));
735
736 if (*rule == '#' || *rule == '\0')
737 {
738 goto next;
739 }
740
741 cmd_name = ap_getword_conf(mp, &rule);
742 cmd = msc_remote_find_command(cmd_name, security2_module.cmds);
743
744 if (cmd == NULL)
745 {
746 *error_msg = apr_pstrcat(mp, "Unknown command in config: ",
747 cmd_name, NULL);
748 return -1;
749 }
750
751 newdir = apr_pcalloc(mp, sizeof(ap_directive_t));
752 newdir->filename = "remote server";
753 newdir->line_num = -1;
754 newdir->directive = cmd_name;
755 newdir->args = apr_pstrdup(mp, rule);
756 parms->directive = newdir;
757
758 #ifdef WIN32
759 // some config commands fail in APR when there are file
760 // permission issues or other OS-specific problems
761 //
762 __try
763 {
764 #endif
765 *error_msg = (char *) msc_remote_invoke_cmd(cmd, parms,
766 remote_rules_server->context, rule);
767 if (*error_msg != NULL)
768 {
769 return -1;
770 }
771
772 added_rules++;
773 #ifdef WIN32
774 }
775 __except(EXCEPTION_EXECUTE_HANDLER)
776 {
777 *error_msg = "Command failed to execute (check file/folder" \
778 "permissions, syntax, etc.).";
779 return -1;
780 }
781 #endif
782
783 next:
784 start = end + 1;
785 }
786 len++;
787 }
788
789 remote_rules_server->amount_of_rules = added_rules;
790
791 if (remote_rules_server->crypto != 1)
792 {
793 msc_remote_clean_chunk(&downloaded_content);
794 }
795 #else
796 *error_msg = "SecRemoteRules was not enabled during ModSecurity " \
797 "compilation.";
798 return -1;
799 #endif
800 }
801
802
msc_remote_clean_chunk(struct msc_curl_memory_buffer_t * chunk)803 int msc_remote_clean_chunk(struct msc_curl_memory_buffer_t *chunk)
804 {
805 if (chunk->size == 0)
806 {
807 goto end;
808 }
809
810 if (chunk->memory == NULL)
811 {
812 goto end;
813 }
814
815 free(chunk->memory);
816 chunk->size = 0;
817
818 end:
819 return 0;
820 }
821
822