PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/modsecurity-apache_2.7.3/apache2/apache2_util.c

https://bitbucket.org/jmchen/nginx_module
C | 392 lines | 241 code | 74 blank | 77 comment | 91 complexity | c131880623192f8f217d9d134d749d13 MD5 | raw file
  1. /*
  2. * ModSecurity for Apache 2.x, http://www.modsecurity.org/
  3. * Copyright (c) 2004-2011 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. #include "modsecurity.h"
  15. #include "apache2.h"
  16. #include "http_core.h"
  17. #include "util_script.h"
  18. /**
  19. * Sends a brigade with an error bucket down the filter chain.
  20. */
  21. apr_status_t send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status) {
  22. apr_bucket_brigade *brigade = NULL;
  23. apr_bucket *bucket = NULL;
  24. /* Set the status line explicitly for the error document */
  25. f->r->status_line = ap_get_status_line(status);
  26. brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc);
  27. if (brigade == NULL) return APR_EGENERAL;
  28. bucket = ap_bucket_error_create(status, NULL, f->r->pool, f->r->connection->bucket_alloc);
  29. if (bucket == NULL) return APR_EGENERAL;
  30. APR_BRIGADE_INSERT_TAIL(brigade, bucket);
  31. bucket = apr_bucket_eos_create(f->r->connection->bucket_alloc);
  32. if (bucket == NULL) return APR_EGENERAL;
  33. APR_BRIGADE_INSERT_TAIL(brigade, bucket);
  34. ap_pass_brigade(f->next, brigade);
  35. /* NOTE:
  36. * It may not matter what we do from the filter as it may be too
  37. * late to even generate an error (already sent to client). Nick Kew
  38. * recommends to return APR_EGENERAL in hopes that the handler in control
  39. * will notice and do The Right Thing. So, that is what we do now.
  40. */
  41. return APR_EGENERAL;
  42. }
  43. /**
  44. * Execute system command. First line of the output will be returned in
  45. * the "output" parameter.
  46. */
  47. int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output) {
  48. apr_procattr_t *procattr = NULL;
  49. apr_proc_t *procnew = NULL;
  50. apr_status_t rc = APR_SUCCESS;
  51. const char *const *env = NULL;
  52. apr_file_t *script_out = NULL;
  53. request_rec *r = msr->r;
  54. if (argv == NULL) {
  55. argv = apr_pcalloc(r->pool, 3 * sizeof(char *));
  56. argv[0] = command;
  57. argv[1] = NULL;
  58. }
  59. ap_add_cgi_vars(r);
  60. ap_add_common_vars(r);
  61. /* PHP hack, getting around its silly security checks. */
  62. apr_table_add(r->subprocess_env, "PATH_TRANSLATED", command);
  63. apr_table_add(r->subprocess_env, "REDIRECT_STATUS", "302");
  64. env = (const char * const *)ap_create_environment(r->pool, r->subprocess_env);
  65. if (env == NULL) {
  66. msr_log(msr, 1, "Exec: Unable to create environment.");
  67. return -1;
  68. }
  69. procnew = apr_pcalloc(r->pool, sizeof(*procnew));
  70. if (procnew == NULL) {
  71. msr_log(msr, 1, "Exec: Unable to allocate %lu bytes.", (unsigned long)sizeof(*procnew));
  72. return -1;
  73. }
  74. apr_procattr_create(&procattr, r->pool);
  75. if (procattr == NULL) {
  76. msr_log(msr, 1, "Exec: Unable to create procattr.");
  77. return -1;
  78. }
  79. apr_procattr_io_set(procattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE);
  80. apr_procattr_cmdtype_set(procattr, APR_SHELLCMD);
  81. if (msr->txcfg->debuglog_level >= 9) {
  82. msr_log(msr, 9, "Exec: %s", log_escape_nq(r->pool, command));
  83. }
  84. rc = apr_proc_create(procnew, command, argv, env, procattr, r->pool);
  85. if (rc != APR_SUCCESS) {
  86. msr_log(msr, 1, "Exec: Execution failed: %s (%s)", log_escape_nq(r->pool, command),
  87. get_apr_error(r->pool, rc));
  88. return -1;
  89. }
  90. apr_pool_note_subprocess(r->pool, procnew, APR_KILL_AFTER_TIMEOUT);
  91. script_out = procnew->out;
  92. if (!script_out) {
  93. msr_log(msr, 1, "Exec: Failed to get script output pipe.");
  94. return -1;
  95. }
  96. apr_file_pipe_timeout_set(script_out, r->server->timeout);
  97. /* Now read from the pipe. */
  98. {
  99. char buf[260] = "";
  100. char *p = buf;
  101. apr_size_t nbytes = 255;
  102. apr_status_t rc2;
  103. rc2 = apr_file_read(script_out, buf, &nbytes);
  104. if (rc2 == APR_SUCCESS) {
  105. buf[nbytes] = 0;
  106. /* if there is more than one line ignore them */
  107. while(*p != 0) {
  108. if (*p == 0x0a) *p = 0;
  109. p++;
  110. }
  111. if (msr->txcfg->debuglog_level >= 4) {
  112. msr_log(msr, 4, "Exec: First line from script output: \"%s\"",
  113. log_escape(r->pool, buf));
  114. }
  115. if (output != NULL) *output = apr_pstrdup(r->pool, buf);
  116. /* Soak up the remaining data. */
  117. nbytes = 255;
  118. while(apr_file_read(script_out, buf, &nbytes) == APR_SUCCESS) nbytes = 255;
  119. } else {
  120. msr_log(msr, 1, "Exec: Execution failed while reading output: %s (%s)",
  121. log_escape_nq(r->pool, command),
  122. get_apr_error(r->pool, rc2));
  123. return -1;
  124. }
  125. }
  126. apr_proc_wait(procnew, NULL, NULL, APR_WAIT);
  127. return 1;
  128. }
  129. /**
  130. * Returns a new string that contains the error
  131. * message for the given return code.
  132. */
  133. char *get_apr_error(apr_pool_t *p, apr_status_t rc) {
  134. char *text = apr_pcalloc(p, 201);
  135. if (text == NULL) return NULL;
  136. apr_strerror(rc, text, 200);
  137. return text;
  138. }
  139. /**
  140. * Retrieve named environment variable.
  141. */
  142. char *get_env_var(request_rec *r, char *name) {
  143. char *result = (char *)apr_table_get(r->notes, name);
  144. if (result == NULL) {
  145. result = (char *)apr_table_get(r->subprocess_env, name);
  146. }
  147. if (result == NULL) {
  148. result = getenv(name);
  149. }
  150. return result;
  151. }
  152. /**
  153. * Extended internal log helper function. Use msr_log instead. If fixup is
  154. * true, the message will be stripped of any trailing newline and any
  155. * required bytes will be escaped.
  156. */
  157. static void internal_log_ex(request_rec *r, directory_config *dcfg, modsec_rec *msr,
  158. int level, int fixup, const char *text, va_list ap)
  159. {
  160. apr_size_t nbytes, nbytes_written;
  161. apr_file_t *debuglog_fd = NULL;
  162. int filter_debug_level = 0;
  163. char str1[1024] = "";
  164. char str2[1256] = "";
  165. /* Find the logging FD and determine the logging level from configuration. */
  166. if (dcfg != NULL) {
  167. if ((dcfg->debuglog_fd != NULL)&&(dcfg->debuglog_fd != NOT_SET_P)) {
  168. debuglog_fd = dcfg->debuglog_fd;
  169. }
  170. if (dcfg->debuglog_level != NOT_SET) {
  171. filter_debug_level = dcfg->debuglog_level;
  172. }
  173. }
  174. /* Return immediately if we don't have where to write
  175. * or if the log level of the message is higher than
  176. * wanted in the log.
  177. */
  178. if ((level > 3)&&( (debuglog_fd == NULL) || (level > filter_debug_level) )) return;
  179. /* Construct the message. */
  180. apr_vsnprintf(str1, sizeof(str1), text, ap);
  181. if (fixup) {
  182. int len = strlen(str1);
  183. /* Strip line ending. */
  184. if (len && str1[len - 1] == '\n') {
  185. str1[len - 1] = '\0';
  186. }
  187. if (len > 1 && str1[len - 2] == '\r') {
  188. str1[len - 2] = '\0';
  189. }
  190. }
  191. /* Construct the log entry. */
  192. apr_snprintf(str2, sizeof(str2),
  193. "[%s] [%s/sid#%pp][rid#%pp][%s][%d] %s\n",
  194. current_logtime(msr->mp), ap_get_server_name(r), (r->server),
  195. r, ((r->uri == NULL) ? "" : log_escape_nq(msr->mp, r->uri)),
  196. level, (fixup ? log_escape_nq(msr->mp, str1) : str1));
  197. /* Write to the debug log. */
  198. if ((debuglog_fd != NULL)&&(level <= filter_debug_level)) {
  199. nbytes = strlen(str2);
  200. apr_file_write_full(debuglog_fd, str2, nbytes, &nbytes_written);
  201. }
  202. /* Send message levels 1-3 to the Apache error log and
  203. * add it to the message list in the audit log. */
  204. if (level <= 3) {
  205. char *unique_id = (char *)get_env_var(r, "UNIQUE_ID");
  206. char *hostname = (char *)msr->hostname;
  207. if (unique_id != NULL) {
  208. unique_id = apr_psprintf(msr->mp, " [unique_id \"%s\"]",
  209. log_escape(msr->mp, unique_id));
  210. }
  211. else unique_id = "";
  212. if (hostname != NULL) {
  213. hostname = apr_psprintf(msr->mp, " [hostname \"%s\"]",
  214. log_escape(msr->mp, hostname));
  215. }
  216. else hostname = "";
  217. #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
  218. ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
  219. "[client %s] ModSecurity: %s%s [uri \"%s\"]%s", r->useragent_ip ? r->useragent_ip : r->connection->client_ip, str1,
  220. hostname, log_escape(msr->mp, r->uri), unique_id);
  221. #else
  222. ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
  223. "[client %s] ModSecurity: %s%s [uri \"%s\"]%s", r->connection->remote_ip, str1,
  224. hostname, log_escape(msr->mp, r->uri), unique_id);
  225. #endif
  226. /* Add this message to the list. */
  227. if (msr != NULL) {
  228. /* Force relevency if this is an alert */
  229. msr->is_relevant++;
  230. *(const char **)apr_array_push(msr->alerts) = apr_pstrdup(msr->mp, str1);
  231. }
  232. }
  233. return;
  234. }
  235. /**
  236. * Logs one message at the given level to the debug log (and to the
  237. * Apache error log if the message is important enough.
  238. */
  239. void msr_log(modsec_rec *msr, int level, const char *text, ...) {
  240. va_list ap;
  241. va_start(ap, text);
  242. internal_log_ex(msr->r, msr->txcfg, msr, level, 0, text, ap);
  243. va_end(ap);
  244. }
  245. /**
  246. * Logs one message at level 3 to the debug log and to the
  247. * Apache error log. This is intended for error callbacks.
  248. */
  249. void msr_log_error(modsec_rec *msr, const char *text, ...) {
  250. va_list ap;
  251. va_start(ap, text);
  252. internal_log_ex(msr->r, msr->txcfg, msr, 3, 1, text, ap);
  253. va_end(ap);
  254. }
  255. /**
  256. * Logs one message at level 4 to the debug log and to the
  257. * Apache error log. This is intended for warning callbacks.
  258. *
  259. * The 'text' will first be escaped.
  260. */
  261. void msr_log_warn(modsec_rec *msr, const char *text, ...) {
  262. va_list ap;
  263. va_start(ap, text);
  264. internal_log_ex(msr->r, msr->txcfg, msr, 4, 1, text, ap);
  265. va_end(ap);
  266. }
  267. /**
  268. * Converts an Apache error log message into one line of text.
  269. */
  270. char *format_error_log_message(apr_pool_t *mp, error_message_t *em) {
  271. char *s_file = "", *s_line = "", *s_level = "";
  272. char *s_status = "", *s_message = "";
  273. char *msg = NULL;
  274. if (em == NULL) return NULL;
  275. if (em->file != NULL) {
  276. s_file = apr_psprintf(mp, "[file \"%s\"] ",
  277. log_escape(mp, (char *)em->file));
  278. if (s_file == NULL) return NULL;
  279. }
  280. if (em->line > 0) {
  281. s_line = apr_psprintf(mp, "[line %d] ", em->line);
  282. if (s_line == NULL) return NULL;
  283. }
  284. s_level = apr_psprintf(mp, "[level %d] ", em->level);
  285. if (s_level == NULL) return NULL;
  286. if (em->status != 0) {
  287. s_status = apr_psprintf(mp, "[status %d] ", em->status);
  288. if (s_status == NULL) return NULL;
  289. }
  290. if (em->message != NULL) {
  291. s_message = log_escape_nq(mp, em->message);
  292. if (s_message == NULL) return NULL;
  293. }
  294. msg = apr_psprintf(mp, "%s%s%s%s%s", s_file, s_line, s_level, s_status, s_message);
  295. if (msg == NULL) return NULL;
  296. return msg;
  297. }
  298. /**
  299. * Determines the reponse protocol Apache will use (or has used)
  300. * to respond to the given request.
  301. */
  302. const char *get_response_protocol(request_rec *r) {
  303. int proto_num = r->proto_num;
  304. if (r->assbackwards) {
  305. return NULL;
  306. }
  307. if (proto_num > HTTP_VERSION(1,0)
  308. && apr_table_get(r->subprocess_env, "downgrade-1.0"))
  309. {
  310. proto_num = HTTP_VERSION(1,0);
  311. }
  312. if (proto_num == HTTP_VERSION(1,0)
  313. && apr_table_get(r->subprocess_env, "force-response-1.0"))
  314. {
  315. return "HTTP/1.0";
  316. }
  317. return AP_SERVER_PROTOCOL;
  318. }