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