1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); 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 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * http_script: keeps all script-related ramblings together. 19 * 20 * Compliant to CGI/1.1 spec 21 * 22 * Adapted by rst from original NCSA code by Rob McCool 23 * 24 * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for 25 * custom error responses, and DOCUMENT_ROOT because we found it useful. 26 * It also adds SERVER_ADMIN - useful for scripts to know who to mail when 27 * they fail. 28 */ 29 30#include "apr.h" 31#include "apr_strings.h" 32#include "apr_thread_proc.h" /* for RLIMIT stuff */ 33#include "apr_optional.h" 34#include "apr_buckets.h" 35#include "apr_lib.h" 36#include "apr_poll.h" 37 38#define APR_WANT_STRFUNC 39#define APR_WANT_MEMFUNC 40#include "apr_want.h" 41 42#include "util_filter.h" 43#include "ap_config.h" 44#include "httpd.h" 45#include "http_config.h" 46#include "http_request.h" 47#include "http_core.h" 48#include "http_protocol.h" 49#include "http_main.h" 50#include "http_log.h" 51#include "util_script.h" 52#include "ap_mpm.h" 53#include "mod_core.h" 54#include "mod_cgi.h" 55 56#if APR_HAVE_STRUCT_RLIMIT 57#if defined (RLIMIT_CPU) || defined (RLIMIT_NPROC) || defined (RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) 58#define AP_CGI_USE_RLIMIT 59#endif 60#endif 61 62module AP_MODULE_DECLARE_DATA cgi_module; 63 64static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgi_pfn_reg_with_ssi; 65static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgi_pfn_gtv; 66static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgi_pfn_ps; 67static APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command; 68 69/* Read and discard the data in the brigade produced by a CGI script */ 70static void discard_script_output(apr_bucket_brigade *bb); 71 72/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI 73 * in ScriptAliased directories, which means we need to know if this 74 * request came through ScriptAlias or not... so the Alias module 75 * leaves a note for us. 76 */ 77 78static int is_scriptaliased(request_rec *r) 79{ 80 const char *t = apr_table_get(r->notes, "alias-forced-type"); 81 return t && (!strcasecmp(t, "cgi-script")); 82} 83 84/* Configuration stuff */ 85 86#define DEFAULT_LOGBYTES 10385760 87#define DEFAULT_BUFBYTES 1024 88 89typedef struct { 90 const char *logname; 91 long logbytes; 92 apr_size_t bufbytes; 93} cgi_server_conf; 94 95static void *create_cgi_config(apr_pool_t *p, server_rec *s) 96{ 97 cgi_server_conf *c = 98 (cgi_server_conf *) apr_pcalloc(p, sizeof(cgi_server_conf)); 99 100 c->logname = NULL; 101 c->logbytes = DEFAULT_LOGBYTES; 102 c->bufbytes = DEFAULT_BUFBYTES; 103 104 return c; 105} 106 107static void *merge_cgi_config(apr_pool_t *p, void *basev, void *overridesv) 108{ 109 cgi_server_conf *base = (cgi_server_conf *) basev, 110 *overrides = (cgi_server_conf *) overridesv; 111 112 return overrides->logname ? overrides : base; 113} 114 115static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg) 116{ 117 server_rec *s = cmd->server; 118 cgi_server_conf *conf = ap_get_module_config(s->module_config, 119 &cgi_module); 120 121 conf->logname = ap_server_root_relative(cmd->pool, arg); 122 123 if (!conf->logname) { 124 return apr_pstrcat(cmd->pool, "Invalid ScriptLog path ", 125 arg, NULL); 126 } 127 128 return NULL; 129} 130 131static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, 132 const char *arg) 133{ 134 server_rec *s = cmd->server; 135 cgi_server_conf *conf = ap_get_module_config(s->module_config, 136 &cgi_module); 137 138 conf->logbytes = atol(arg); 139 return NULL; 140} 141 142static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy, 143 const char *arg) 144{ 145 server_rec *s = cmd->server; 146 cgi_server_conf *conf = ap_get_module_config(s->module_config, 147 &cgi_module); 148 149 conf->bufbytes = atoi(arg); 150 return NULL; 151} 152 153static const command_rec cgi_cmds[] = 154{ 155AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF, 156 "the name of a log for script debugging info"), 157AP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, 158 "the maximum length (in bytes) of the script debug log"), 159AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, 160 "the maximum size (in bytes) to record of a POST request"), 161 {NULL} 162}; 163 164static int log_scripterror(request_rec *r, cgi_server_conf * conf, int ret, 165 apr_status_t rv, char *error) 166{ 167 apr_file_t *f = NULL; 168 apr_finfo_t finfo; 169 char time_str[APR_CTIME_LEN]; 170 int log_flags = rv ? APLOG_ERR : APLOG_ERR; 171 172 ap_log_rerror(APLOG_MARK, log_flags, rv, r, 173 "%s: %s", error, r->filename); 174 175 /* XXX Very expensive mainline case! Open, then getfileinfo! */ 176 if (!conf->logname || 177 ((apr_stat(&finfo, conf->logname, 178 APR_FINFO_SIZE, r->pool) == APR_SUCCESS) && 179 (finfo.size > conf->logbytes)) || 180 (apr_file_open(&f, conf->logname, 181 APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, 182 r->pool) != APR_SUCCESS)) { 183 return ret; 184 } 185 186 /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ 187 apr_ctime(time_str, apr_time_now()); 188 apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, 189 r->args ? "?" : "", r->args ? r->args : "", r->protocol); 190 /* "%% 500 /usr/local/apache/cgi-bin */ 191 apr_file_printf(f, "%%%% %d %s\n", ret, r->filename); 192 193 apr_file_printf(f, "%%error\n%s\n", error); 194 195 apr_file_close(f); 196 return ret; 197} 198 199/* Soak up stderr from a script and redirect it to the error log. 200 */ 201static apr_status_t log_script_err(request_rec *r, apr_file_t *script_err) 202{ 203 char argsbuffer[HUGE_STRING_LEN]; 204 char *newline; 205 apr_status_t rv; 206 207 while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN, 208 script_err)) == APR_SUCCESS) { 209 newline = strchr(argsbuffer, '\n'); 210 if (newline) { 211 *newline = '\0'; 212 } 213 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01215) 214 "%s", argsbuffer); 215 } 216 217 return rv; 218} 219 220static int log_script(request_rec *r, cgi_server_conf * conf, int ret, 221 char *dbuf, const char *sbuf, apr_bucket_brigade *bb, 222 apr_file_t *script_err) 223{ 224 const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in); 225 const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts; 226 char argsbuffer[HUGE_STRING_LEN]; 227 apr_file_t *f = NULL; 228 apr_bucket *e; 229 const char *buf; 230 apr_size_t len; 231 apr_status_t rv; 232 int first; 233 int i; 234 apr_finfo_t finfo; 235 char time_str[APR_CTIME_LEN]; 236 237 /* XXX Very expensive mainline case! Open, then getfileinfo! */ 238 if (!conf->logname || 239 ((apr_stat(&finfo, conf->logname, 240 APR_FINFO_SIZE, r->pool) == APR_SUCCESS) && 241 (finfo.size > conf->logbytes)) || 242 (apr_file_open(&f, conf->logname, 243 APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, 244 r->pool) != APR_SUCCESS)) { 245 /* Soak up script output */ 246 discard_script_output(bb); 247 log_script_err(r, script_err); 248 return ret; 249 } 250 251 /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */ 252 apr_ctime(time_str, apr_time_now()); 253 apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, 254 r->args ? "?" : "", r->args ? r->args : "", r->protocol); 255 /* "%% 500 /usr/local/apache/cgi-bin" */ 256 apr_file_printf(f, "%%%% %d %s\n", ret, r->filename); 257 258 apr_file_puts("%request\n", f); 259 for (i = 0; i < hdrs_arr->nelts; ++i) { 260 if (!hdrs[i].key) 261 continue; 262 apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); 263 } 264 if ((r->method_number == M_POST || r->method_number == M_PUT) && 265 *dbuf) { 266 apr_file_printf(f, "\n%s\n", dbuf); 267 } 268 269 apr_file_puts("%response\n", f); 270 hdrs_arr = apr_table_elts(r->err_headers_out); 271 hdrs = (const apr_table_entry_t *) hdrs_arr->elts; 272 273 for (i = 0; i < hdrs_arr->nelts; ++i) { 274 if (!hdrs[i].key) 275 continue; 276 apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); 277 } 278 279 if (sbuf && *sbuf) 280 apr_file_printf(f, "%s\n", sbuf); 281 282 first = 1; 283 for (e = APR_BRIGADE_FIRST(bb); 284 e != APR_BRIGADE_SENTINEL(bb); 285 e = APR_BUCKET_NEXT(e)) 286 { 287 if (APR_BUCKET_IS_EOS(e)) { 288 break; 289 } 290 rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); 291 if (rv != APR_SUCCESS || (len == 0)) { 292 break; 293 } 294 if (first) { 295 apr_file_puts("%stdout\n", f); 296 first = 0; 297 } 298 apr_file_write(f, buf, &len); 299 apr_file_puts("\n", f); 300 } 301 302 if (apr_file_gets(argsbuffer, HUGE_STRING_LEN, script_err) == APR_SUCCESS) { 303 apr_file_puts("%stderr\n", f); 304 apr_file_puts(argsbuffer, f); 305 while (apr_file_gets(argsbuffer, HUGE_STRING_LEN, 306 script_err) == APR_SUCCESS) { 307 apr_file_puts(argsbuffer, f); 308 } 309 apr_file_puts("\n", f); 310 } 311 312 apr_brigade_destroy(bb); 313 apr_file_close(script_err); 314 315 apr_file_close(f); 316 return ret; 317} 318 319 320/* This is the special environment used for running the "exec cmd=" 321 * variety of SSI directives. 322 */ 323static void add_ssi_vars(request_rec *r) 324{ 325 apr_table_t *e = r->subprocess_env; 326 327 if (r->path_info && r->path_info[0] != '\0') { 328 request_rec *pa_req; 329 330 apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool, 331 r->path_info)); 332 333 pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), 334 r, NULL); 335 if (pa_req->filename) { 336 apr_table_setn(e, "PATH_TRANSLATED", 337 apr_pstrcat(r->pool, pa_req->filename, 338 pa_req->path_info, NULL)); 339 } 340 ap_destroy_sub_req(pa_req); 341 } 342 343 if (r->args) { 344 char *arg_copy = apr_pstrdup(r->pool, r->args); 345 346 apr_table_setn(e, "QUERY_STRING", r->args); 347 ap_unescape_url(arg_copy); 348 apr_table_setn(e, "QUERY_STRING_UNESCAPED", 349 ap_escape_shell_cmd(r->pool, arg_copy)); 350 } 351} 352 353static void cgi_child_errfn(apr_pool_t *pool, apr_status_t err, 354 const char *description) 355{ 356 apr_file_t *stderr_log; 357 358 apr_file_open_stderr(&stderr_log, pool); 359 /* Escape the logged string because it may be something that 360 * came in over the network. 361 */ 362 apr_file_printf(stderr_log, 363 "(%d)%pm: %s\n", 364 err, 365 &err, 366#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED 367 ap_escape_logitem(pool, 368#endif 369 description 370#ifndef AP_UNSAFE_ERROR_LOG_UNESCAPED 371 ) 372#endif 373 ); 374} 375 376static apr_status_t run_cgi_child(apr_file_t **script_out, 377 apr_file_t **script_in, 378 apr_file_t **script_err, 379 const char *command, 380 const char * const argv[], 381 request_rec *r, 382 apr_pool_t *p, 383 cgi_exec_info_t *e_info) 384{ 385 const char * const *env; 386 apr_procattr_t *procattr; 387 apr_proc_t *procnew; 388 apr_status_t rc = APR_SUCCESS; 389 390#ifdef AP_CGI_USE_RLIMIT 391 core_dir_config *conf = ap_get_core_module_config(r->per_dir_config); 392#endif 393 394#ifdef DEBUG_CGI 395#ifdef OS2 396 /* Under OS/2 need to use device con. */ 397 FILE *dbg = fopen("con", "w"); 398#else 399 FILE *dbg = fopen("/dev/tty", "w"); 400#endif 401 int i; 402#endif 403 404 RAISE_SIGSTOP(CGI_CHILD); 405#ifdef DEBUG_CGI 406 fprintf(dbg, "Attempting to exec %s as CGI child (argv0 = %s)\n", 407 r->filename, argv[0]); 408#endif 409 410 env = (const char * const *)ap_create_environment(p, r->subprocess_env); 411 412#ifdef DEBUG_CGI 413 fprintf(dbg, "Environment: \n"); 414 for (i = 0; env[i]; ++i) 415 fprintf(dbg, "'%s'\n", env[i]); 416 fclose(dbg); 417#endif 418 419 /* Transmute ourselves into the script. 420 * NB only ISINDEX scripts get decoded arguments. 421 */ 422 if (((rc = apr_procattr_create(&procattr, p)) != APR_SUCCESS) || 423 ((rc = apr_procattr_io_set(procattr, 424 e_info->in_pipe, 425 e_info->out_pipe, 426 e_info->err_pipe)) != APR_SUCCESS) || 427 ((rc = apr_procattr_dir_set(procattr, 428 ap_make_dirstr_parent(r->pool, 429 r->filename))) != APR_SUCCESS) || 430#if defined(RLIMIT_CPU) && defined(AP_CGI_USE_RLIMIT) 431 ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, 432 conf->limit_cpu)) != APR_SUCCESS) || 433#endif 434#if defined(AP_CGI_USE_RLIMIT) && (defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)) 435 ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, 436 conf->limit_mem)) != APR_SUCCESS) || 437#endif 438#if defined(RLIMIT_NPROC) && defined(AP_CGI_USE_RLIMIT) 439 ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, 440 conf->limit_nproc)) != APR_SUCCESS) || 441#endif 442 ((rc = apr_procattr_cmdtype_set(procattr, 443 e_info->cmd_type)) != APR_SUCCESS) || 444 445 ((rc = apr_procattr_detach_set(procattr, 446 e_info->detached)) != APR_SUCCESS) || 447 ((rc = apr_procattr_addrspace_set(procattr, 448 e_info->addrspace)) != APR_SUCCESS) || 449 ((rc = apr_procattr_child_errfn_set(procattr, cgi_child_errfn)) != APR_SUCCESS)) { 450 /* Something bad happened, tell the world. */ 451 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(01216) 452 "couldn't set child process attributes: %s", r->filename); 453 } 454 else { 455 procnew = apr_pcalloc(p, sizeof(*procnew)); 456 rc = ap_os_create_privileged_process(r, procnew, command, argv, env, 457 procattr, p); 458 459 if (rc != APR_SUCCESS) { 460 /* Bad things happened. Everyone should have cleaned up. */ 461 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, rc, r, 462 "couldn't create child process: %d: %s", rc, 463 apr_filepath_name_get(r->filename)); 464 } 465 else { 466 apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT); 467 468 *script_in = procnew->out; 469 if (!*script_in) 470 return APR_EBADF; 471 apr_file_pipe_timeout_set(*script_in, r->server->timeout); 472 473 if (e_info->prog_type == RUN_AS_CGI) { 474 *script_out = procnew->in; 475 if (!*script_out) 476 return APR_EBADF; 477 apr_file_pipe_timeout_set(*script_out, r->server->timeout); 478 479 *script_err = procnew->err; 480 if (!*script_err) 481 return APR_EBADF; 482 apr_file_pipe_timeout_set(*script_err, r->server->timeout); 483 } 484 } 485 } 486 return (rc); 487} 488 489 490static apr_status_t default_build_command(const char **cmd, const char ***argv, 491 request_rec *r, apr_pool_t *p, 492 cgi_exec_info_t *e_info) 493{ 494 int numwords, x, idx; 495 char *w; 496 const char *args = NULL; 497 498 if (e_info->process_cgi) { 499 *cmd = r->filename; 500 /* Do not process r->args if they contain an '=' assignment 501 */ 502 if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) { 503 args = r->args; 504 } 505 } 506 507 if (!args) { 508 numwords = 1; 509 } 510 else { 511 /* count the number of keywords */ 512 for (x = 0, numwords = 2; args[x]; x++) { 513 if (args[x] == '+') { 514 ++numwords; 515 } 516 } 517 } 518 /* Everything is - 1 to account for the first parameter 519 * which is the program name. 520 */ 521 if (numwords > APACHE_ARG_MAX - 1) { 522 numwords = APACHE_ARG_MAX - 1; /* Truncate args to prevent overrun */ 523 } 524 *argv = apr_palloc(p, (numwords + 2) * sizeof(char *)); 525 (*argv)[0] = *cmd; 526 for (x = 1, idx = 1; x < numwords; x++) { 527 w = ap_getword_nulls(p, &args, '+'); 528 ap_unescape_url(w); 529 (*argv)[idx++] = ap_escape_shell_cmd(p, w); 530 } 531 (*argv)[idx] = NULL; 532 533 return APR_SUCCESS; 534} 535 536static void discard_script_output(apr_bucket_brigade *bb) 537{ 538 apr_bucket *e; 539 const char *buf; 540 apr_size_t len; 541 apr_status_t rv; 542 543 for (e = APR_BRIGADE_FIRST(bb); 544 e != APR_BRIGADE_SENTINEL(bb); 545 e = APR_BUCKET_NEXT(e)) 546 { 547 if (APR_BUCKET_IS_EOS(e)) { 548 break; 549 } 550 rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); 551 if (rv != APR_SUCCESS) { 552 break; 553 } 554 } 555} 556 557#if APR_FILES_AS_SOCKETS 558 559/* A CGI bucket type is needed to catch any output to stderr from the 560 * script; see PR 22030. */ 561static const apr_bucket_type_t bucket_type_cgi; 562 563struct cgi_bucket_data { 564 apr_pollset_t *pollset; 565 request_rec *r; 566}; 567 568/* Create a CGI bucket using pipes from script stdout 'out' 569 * and stderr 'err', for request 'r'. */ 570static apr_bucket *cgi_bucket_create(request_rec *r, 571 apr_file_t *out, apr_file_t *err, 572 apr_bucket_alloc_t *list) 573{ 574 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); 575 apr_status_t rv; 576 apr_pollfd_t fd; 577 struct cgi_bucket_data *data = apr_palloc(r->pool, sizeof *data); 578 579 APR_BUCKET_INIT(b); 580 b->free = apr_bucket_free; 581 b->list = list; 582 b->type = &bucket_type_cgi; 583 b->length = (apr_size_t)(-1); 584 b->start = -1; 585 586 /* Create the pollset */ 587 rv = apr_pollset_create(&data->pollset, 2, r->pool, 0); 588 if (rv != APR_SUCCESS) { 589 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01217) 590 "apr_pollset_create(); check system or user limits"); 591 return NULL; 592 } 593 594 fd.desc_type = APR_POLL_FILE; 595 fd.reqevents = APR_POLLIN; 596 fd.p = r->pool; 597 fd.desc.f = out; /* script's stdout */ 598 fd.client_data = (void *)1; 599 rv = apr_pollset_add(data->pollset, &fd); 600 if (rv != APR_SUCCESS) { 601 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01218) 602 "apr_pollset_add(); check system or user limits"); 603 return NULL; 604 } 605 606 fd.desc.f = err; /* script's stderr */ 607 fd.client_data = (void *)2; 608 rv = apr_pollset_add(data->pollset, &fd); 609 if (rv != APR_SUCCESS) { 610 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01219) 611 "apr_pollset_add(); check system or user limits"); 612 return NULL; 613 } 614 615 data->r = r; 616 b->data = data; 617 return b; 618} 619 620/* Create a duplicate CGI bucket using given bucket data */ 621static apr_bucket *cgi_bucket_dup(struct cgi_bucket_data *data, 622 apr_bucket_alloc_t *list) 623{ 624 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); 625 APR_BUCKET_INIT(b); 626 b->free = apr_bucket_free; 627 b->list = list; 628 b->type = &bucket_type_cgi; 629 b->length = (apr_size_t)(-1); 630 b->start = -1; 631 b->data = data; 632 return b; 633} 634 635/* Handle stdout from CGI child. Duplicate of logic from the _read 636 * method of the real APR pipe bucket implementation. */ 637static apr_status_t cgi_read_stdout(apr_bucket *a, apr_file_t *out, 638 const char **str, apr_size_t *len) 639{ 640 char *buf; 641 apr_status_t rv; 642 643 *str = NULL; 644 *len = APR_BUCKET_BUFF_SIZE; 645 buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */ 646 647 rv = apr_file_read(out, buf, len); 648 649 if (rv != APR_SUCCESS && rv != APR_EOF) { 650 apr_bucket_free(buf); 651 return rv; 652 } 653 654 if (*len > 0) { 655 struct cgi_bucket_data *data = a->data; 656 apr_bucket_heap *h; 657 658 /* Change the current bucket to refer to what we read */ 659 a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); 660 h = a->data; 661 h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ 662 *str = buf; 663 APR_BUCKET_INSERT_AFTER(a, cgi_bucket_dup(data, a->list)); 664 } 665 else { 666 apr_bucket_free(buf); 667 a = apr_bucket_immortal_make(a, "", 0); 668 *str = a->data; 669 } 670 return rv; 671} 672 673/* Read method of CGI bucket: polls on stderr and stdout of the child, 674 * sending any stderr output immediately away to the error log. */ 675static apr_status_t cgi_bucket_read(apr_bucket *b, const char **str, 676 apr_size_t *len, apr_read_type_e block) 677{ 678 struct cgi_bucket_data *data = b->data; 679 apr_interval_time_t timeout; 680 apr_status_t rv; 681 int gotdata = 0; 682 683 timeout = block == APR_NONBLOCK_READ ? 0 : data->r->server->timeout; 684 685 do { 686 const apr_pollfd_t *results; 687 apr_int32_t num; 688 689 rv = apr_pollset_poll(data->pollset, timeout, &num, &results); 690 if (APR_STATUS_IS_TIMEUP(rv)) { 691 if (timeout) { 692 ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, data->r, APLOGNO(01220) 693 "Timeout waiting for output from CGI script %s", 694 data->r->filename); 695 return rv; 696 } 697 else { 698 return APR_EAGAIN; 699 } 700 } 701 else if (APR_STATUS_IS_EINTR(rv)) { 702 continue; 703 } 704 else if (rv != APR_SUCCESS) { 705 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r, APLOGNO(01221) 706 "poll failed waiting for CGI child"); 707 return rv; 708 } 709 710 for (; num; num--, results++) { 711 if (results[0].client_data == (void *)1) { 712 /* stdout */ 713 rv = cgi_read_stdout(b, results[0].desc.f, str, len); 714 if (APR_STATUS_IS_EOF(rv)) { 715 rv = APR_SUCCESS; 716 } 717 gotdata = 1; 718 } else { 719 /* stderr */ 720 apr_status_t rv2 = log_script_err(data->r, results[0].desc.f); 721 if (APR_STATUS_IS_EOF(rv2)) { 722 apr_pollset_remove(data->pollset, &results[0]); 723 } 724 } 725 } 726 727 } while (!gotdata); 728 729 return rv; 730} 731 732static const apr_bucket_type_t bucket_type_cgi = { 733 "CGI", 5, APR_BUCKET_DATA, 734 apr_bucket_destroy_noop, 735 cgi_bucket_read, 736 apr_bucket_setaside_notimpl, 737 apr_bucket_split_notimpl, 738 apr_bucket_copy_notimpl 739}; 740 741#endif 742 743static int cgi_handler(request_rec *r) 744{ 745 int nph; 746 apr_size_t dbpos = 0; 747 const char *argv0; 748 const char *command; 749 const char **argv; 750 char *dbuf = NULL; 751 apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL; 752 apr_bucket_brigade *bb; 753 apr_bucket *b; 754 int is_included; 755 int seen_eos, child_stopped_reading; 756 apr_pool_t *p; 757 cgi_server_conf *conf; 758 apr_status_t rv; 759 cgi_exec_info_t e_info; 760 conn_rec *c; 761 762 if (strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script")) { 763 return DECLINED; 764 } 765 766 c = r->connection; 767 768 is_included = !strcmp(r->protocol, "INCLUDED"); 769 770 p = r->main ? r->main->pool : r->pool; 771 772 argv0 = apr_filepath_name_get(r->filename); 773 nph = !(strncmp(argv0, "nph-", 4)); 774 conf = ap_get_module_config(r->server->module_config, &cgi_module); 775 776 if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r)) 777 return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, 778 "Options ExecCGI is off in this directory"); 779 if (nph && is_included) 780 return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, 781 "attempt to include NPH CGI script"); 782 783 if (r->finfo.filetype == APR_NOFILE) 784 return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, 785 "script not found or unable to stat"); 786 if (r->finfo.filetype == APR_DIR) 787 return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, 788 "attempt to invoke directory as script"); 789 790 if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) && 791 r->path_info && *r->path_info) 792 { 793 /* default to accept */ 794 return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, 795 "AcceptPathInfo off disallows user's path"); 796 } 797/* 798 if (!ap_suexec_enabled) { 799 if (!ap_can_exec(&r->finfo)) 800 return log_scripterror(r, conf, HTTP_FORBIDDEN, 0, 801 "file permissions deny server execution"); 802 } 803 804*/ 805 ap_add_common_vars(r); 806 ap_add_cgi_vars(r); 807 808 e_info.process_cgi = 1; 809 e_info.cmd_type = APR_PROGRAM; 810 e_info.detached = 0; 811 e_info.in_pipe = APR_CHILD_BLOCK; 812 e_info.out_pipe = APR_CHILD_BLOCK; 813 e_info.err_pipe = APR_CHILD_BLOCK; 814 e_info.prog_type = RUN_AS_CGI; 815 e_info.bb = NULL; 816 e_info.ctx = NULL; 817 e_info.next = NULL; 818 e_info.addrspace = 0; 819 820 /* build the command line */ 821 if ((rv = cgi_build_command(&command, &argv, r, p, &e_info)) != APR_SUCCESS) { 822 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01222) 823 "don't know how to spawn child process: %s", 824 r->filename); 825 return HTTP_INTERNAL_SERVER_ERROR; 826 } 827 828 /* run the script in its own process */ 829 if ((rv = run_cgi_child(&script_out, &script_in, &script_err, 830 command, argv, r, p, &e_info)) != APR_SUCCESS) { 831 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01223) 832 "couldn't spawn child process: %s", r->filename); 833 return HTTP_INTERNAL_SERVER_ERROR; 834 } 835 836 /* Transfer any put/post args, CERN style... 837 * Note that we already ignore SIGPIPE in the core server. 838 */ 839 bb = apr_brigade_create(r->pool, c->bucket_alloc); 840 seen_eos = 0; 841 child_stopped_reading = 0; 842 if (conf->logname) { 843 dbuf = apr_palloc(r->pool, conf->bufbytes + 1); 844 dbpos = 0; 845 } 846 do { 847 apr_bucket *bucket; 848 849 rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, 850 APR_BLOCK_READ, HUGE_STRING_LEN); 851 852 if (rv != APR_SUCCESS) { 853 if (APR_STATUS_IS_TIMEUP(rv)) { 854 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01224) 855 "Timeout during reading request entity data"); 856 return HTTP_REQUEST_TIME_OUT; 857 } 858 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01225) 859 "Error reading request entity data"); 860 return HTTP_INTERNAL_SERVER_ERROR; 861 } 862 863 for (bucket = APR_BRIGADE_FIRST(bb); 864 bucket != APR_BRIGADE_SENTINEL(bb); 865 bucket = APR_BUCKET_NEXT(bucket)) 866 { 867 const char *data; 868 apr_size_t len; 869 870 if (APR_BUCKET_IS_EOS(bucket)) { 871 seen_eos = 1; 872 break; 873 } 874 875 /* We can't do much with this. */ 876 if (APR_BUCKET_IS_FLUSH(bucket)) { 877 continue; 878 } 879 880 /* If the child stopped, we still must read to EOS. */ 881 if (child_stopped_reading) { 882 continue; 883 } 884 885 /* read */ 886 apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); 887 888 if (conf->logname && dbpos < conf->bufbytes) { 889 int cursize; 890 891 if ((dbpos + len) > conf->bufbytes) { 892 cursize = conf->bufbytes - dbpos; 893 } 894 else { 895 cursize = len; 896 } 897 memcpy(dbuf + dbpos, data, cursize); 898 dbpos += cursize; 899 } 900 901 /* Keep writing data to the child until done or too much time 902 * elapses with no progress or an error occurs. 903 */ 904 rv = apr_file_write_full(script_out, data, len, NULL); 905 906 if (rv != APR_SUCCESS) { 907 /* silly script stopped reading, soak up remaining message */ 908 child_stopped_reading = 1; 909 } 910 } 911 apr_brigade_cleanup(bb); 912 } 913 while (!seen_eos); 914 915 if (conf->logname) { 916 dbuf[dbpos] = '\0'; 917 } 918 /* Is this flush really needed? */ 919 apr_file_flush(script_out); 920 apr_file_close(script_out); 921 922 AP_DEBUG_ASSERT(script_in != NULL); 923 924 apr_brigade_cleanup(bb); 925 926#if APR_FILES_AS_SOCKETS 927 apr_file_pipe_timeout_set(script_in, 0); 928 apr_file_pipe_timeout_set(script_err, 0); 929 930 b = cgi_bucket_create(r, script_in, script_err, c->bucket_alloc); 931 if (b == NULL) 932 return HTTP_INTERNAL_SERVER_ERROR; 933#else 934 b = apr_bucket_pipe_create(script_in, c->bucket_alloc); 935#endif 936 APR_BRIGADE_INSERT_TAIL(bb, b); 937 b = apr_bucket_eos_create(c->bucket_alloc); 938 APR_BRIGADE_INSERT_TAIL(bb, b); 939 940 /* Handle script return... */ 941 if (!nph) { 942 const char *location; 943 char sbuf[MAX_STRING_LEN]; 944 int ret; 945 946 if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf, 947 APLOG_MODULE_INDEX))) 948 { 949 ret = log_script(r, conf, ret, dbuf, sbuf, bb, script_err); 950 951 /* 952 * ret could be HTTP_NOT_MODIFIED in the case that the CGI script 953 * does not set an explicit status and ap_meets_conditions, which 954 * is called by ap_scan_script_header_err_brigade, detects that 955 * the conditions of the requests are met and the response is 956 * not modified. 957 * In this case set r->status and return OK in order to prevent 958 * running through the error processing stack as this would 959 * break with mod_cache, if the conditions had been set by 960 * mod_cache itself to validate a stale entity. 961 * BTW: We circumvent the error processing stack anyway if the 962 * CGI script set an explicit status code (whatever it is) and 963 * the only possible values for ret here are: 964 * 965 * HTTP_NOT_MODIFIED (set by ap_meets_conditions) 966 * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) 967 * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the 968 * processing of the response of the CGI script, e.g broken headers 969 * or a crashed CGI process). 970 */ 971 if (ret == HTTP_NOT_MODIFIED) { 972 r->status = ret; 973 return OK; 974 } 975 976 return ret; 977 } 978 979 location = apr_table_get(r->headers_out, "Location"); 980 981 if (location && r->status == 200) { 982 /* For a redirect whether internal or not, discard any 983 * remaining stdout from the script, and log any remaining 984 * stderr output, as normal. */ 985 discard_script_output(bb); 986 apr_brigade_destroy(bb); 987 apr_file_pipe_timeout_set(script_err, r->server->timeout); 988 log_script_err(r, script_err); 989 } 990 991 if (location && location[0] == '/' && r->status == 200) { 992 /* This redirect needs to be a GET no matter what the original 993 * method was. 994 */ 995 r->method = "GET"; 996 r->method_number = M_GET; 997 998 /* We already read the message body (if any), so don't allow 999 * the redirected request to think it has one. We can ignore 1000 * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. 1001 */ 1002 apr_table_unset(r->headers_in, "Content-Length"); 1003 1004 ap_internal_redirect_handler(location, r); 1005 return OK; 1006 } 1007 else if (location && r->status == 200) { 1008 /* XXX: Note that if a script wants to produce its own Redirect 1009 * body, it now has to explicitly *say* "Status: 302" 1010 */ 1011 return HTTP_MOVED_TEMPORARILY; 1012 } 1013 1014 rv = ap_pass_brigade(r->output_filters, bb); 1015 } 1016 else /* nph */ { 1017 struct ap_filter_t *cur; 1018 1019 /* get rid of all filters up through protocol... since we 1020 * haven't parsed off the headers, there is no way they can 1021 * work 1022 */ 1023 1024 cur = r->proto_output_filters; 1025 while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { 1026 cur = cur->next; 1027 } 1028 r->output_filters = r->proto_output_filters = cur; 1029 1030 rv = ap_pass_brigade(r->output_filters, bb); 1031 } 1032 1033 /* don't soak up script output if errors occurred writing it 1034 * out... otherwise, we prolong the life of the script when the 1035 * connection drops or we stopped sending output for some other 1036 * reason */ 1037 if (rv == APR_SUCCESS && !r->connection->aborted) { 1038 apr_file_pipe_timeout_set(script_err, r->server->timeout); 1039 log_script_err(r, script_err); 1040 } 1041 1042 apr_file_close(script_err); 1043 1044 return OK; /* NOT r->status, even if it has changed. */ 1045} 1046 1047/*============================================================================ 1048 *============================================================================ 1049 * This is the beginning of the cgi filter code moved from mod_include. This 1050 * is the code required to handle the "exec" SSI directive. 1051 *============================================================================ 1052 *============================================================================*/ 1053static apr_status_t include_cgi(include_ctx_t *ctx, ap_filter_t *f, 1054 apr_bucket_brigade *bb, char *s) 1055{ 1056 request_rec *r = f->r; 1057 request_rec *rr = ap_sub_req_lookup_uri(s, r, f->next); 1058 int rr_status; 1059 1060 if (rr->status != HTTP_OK) { 1061 ap_destroy_sub_req(rr); 1062 return APR_EGENERAL; 1063 } 1064 1065 /* No hardwired path info or query allowed */ 1066 if ((rr->path_info && rr->path_info[0]) || rr->args) { 1067 ap_destroy_sub_req(rr); 1068 return APR_EGENERAL; 1069 } 1070 if (rr->finfo.filetype != APR_REG) { 1071 ap_destroy_sub_req(rr); 1072 return APR_EGENERAL; 1073 } 1074 1075 /* Script gets parameters of the *document*, for back compatibility */ 1076 rr->path_info = r->path_info; /* hard to get right; see mod_cgi.c */ 1077 rr->args = r->args; 1078 1079 /* Force sub_req to be treated as a CGI request, even if ordinary 1080 * typing rules would have called it something else. 1081 */ 1082 ap_set_content_type(rr, CGI_MAGIC_TYPE); 1083 1084 /* Run it. */ 1085 rr_status = ap_run_sub_req(rr); 1086 if (ap_is_HTTP_REDIRECT(rr_status)) { 1087 const char *location = apr_table_get(rr->headers_out, "Location"); 1088 1089 if (location) { 1090 char *buffer; 1091 1092 location = ap_escape_html(rr->pool, location); 1093 buffer = apr_pstrcat(ctx->pool, "<a href=\"", location, "\">", 1094 location, "</a>", NULL); 1095 1096 APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(buffer, 1097 strlen(buffer), ctx->pool, 1098 f->c->bucket_alloc)); 1099 } 1100 } 1101 1102 ap_destroy_sub_req(rr); 1103 1104 return APR_SUCCESS; 1105} 1106 1107static apr_status_t include_cmd(include_ctx_t *ctx, ap_filter_t *f, 1108 apr_bucket_brigade *bb, const char *command) 1109{ 1110 cgi_exec_info_t e_info; 1111 const char **argv; 1112 apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL; 1113 apr_status_t rv; 1114 request_rec *r = f->r; 1115 1116 add_ssi_vars(r); 1117 1118 e_info.process_cgi = 0; 1119 e_info.cmd_type = APR_SHELLCMD; 1120 e_info.detached = 0; 1121 e_info.in_pipe = APR_NO_PIPE; 1122 e_info.out_pipe = APR_FULL_BLOCK; 1123 e_info.err_pipe = APR_NO_PIPE; 1124 e_info.prog_type = RUN_AS_SSI; 1125 e_info.bb = &bb; 1126 e_info.ctx = ctx; 1127 e_info.next = f->next; 1128 e_info.addrspace = 0; 1129 1130 if ((rv = cgi_build_command(&command, &argv, r, r->pool, 1131 &e_info)) != APR_SUCCESS) { 1132 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01226) 1133 "don't know how to spawn cmd child process: %s", 1134 r->filename); 1135 return rv; 1136 } 1137 1138 /* run the script in its own process */ 1139 if ((rv = run_cgi_child(&script_out, &script_in, &script_err, 1140 command, argv, r, r->pool, 1141 &e_info)) != APR_SUCCESS) { 1142 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01227) 1143 "couldn't spawn child process: %s", r->filename); 1144 return rv; 1145 } 1146 1147 APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pipe_create(script_in, 1148 f->c->bucket_alloc)); 1149 ctx->flush_now = 1; 1150 1151 /* We can't close the pipe here, because we may return before the 1152 * full CGI has been sent to the network. That's okay though, 1153 * because we can rely on the pool to close the pipe for us. 1154 */ 1155 return APR_SUCCESS; 1156} 1157 1158static apr_status_t handle_exec(include_ctx_t *ctx, ap_filter_t *f, 1159 apr_bucket_brigade *bb) 1160{ 1161 char *tag = NULL; 1162 char *tag_val = NULL; 1163 request_rec *r = f->r; 1164 char *file = r->filename; 1165 char parsed_string[MAX_STRING_LEN]; 1166 1167 if (!ctx->argc) { 1168 ap_log_rerror(APLOG_MARK, 1169 (ctx->flags & SSI_FLAG_PRINTING) 1170 ? APLOG_ERR : APLOG_WARNING, 1171 0, r, "missing argument for exec element in %s", 1172 r->filename); 1173 } 1174 1175 if (!(ctx->flags & SSI_FLAG_PRINTING)) { 1176 return APR_SUCCESS; 1177 } 1178 1179 if (!ctx->argc) { 1180 SSI_CREATE_ERROR_BUCKET(ctx, f, bb); 1181 return APR_SUCCESS; 1182 } 1183 1184 if (ctx->flags & SSI_FLAG_NO_EXEC) { 1185 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01228) "exec used but not allowed " 1186 "in %s", r->filename); 1187 SSI_CREATE_ERROR_BUCKET(ctx, f, bb); 1188 return APR_SUCCESS; 1189 } 1190 1191 while (1) { 1192 cgi_pfn_gtv(ctx, &tag, &tag_val, SSI_VALUE_DECODED); 1193 if (!tag || !tag_val) { 1194 break; 1195 } 1196 1197 if (!strcmp(tag, "cmd")) { 1198 apr_status_t rv; 1199 1200 cgi_pfn_ps(ctx, tag_val, parsed_string, sizeof(parsed_string), 1201 SSI_EXPAND_LEAVE_NAME); 1202 1203 rv = include_cmd(ctx, f, bb, parsed_string); 1204 if (rv != APR_SUCCESS) { 1205 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01229) "execution failure " 1206 "for parameter \"%s\" to tag exec in file %s", 1207 tag, r->filename); 1208 SSI_CREATE_ERROR_BUCKET(ctx, f, bb); 1209 break; 1210 } 1211 } 1212 else if (!strcmp(tag, "cgi")) { 1213 apr_status_t rv; 1214 1215 cgi_pfn_ps(ctx, tag_val, parsed_string, sizeof(parsed_string), 1216 SSI_EXPAND_DROP_NAME); 1217 1218 rv = include_cgi(ctx, f, bb, parsed_string); 1219 if (rv != APR_SUCCESS) { 1220 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01230) "invalid CGI ref " 1221 "\"%s\" in %s", tag_val, file); 1222 SSI_CREATE_ERROR_BUCKET(ctx, f, bb); 1223 break; 1224 } 1225 } 1226 else { 1227 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01231) "unknown parameter " 1228 "\"%s\" to tag exec in %s", tag, file); 1229 SSI_CREATE_ERROR_BUCKET(ctx, f, bb); 1230 break; 1231 } 1232 } 1233 1234 return APR_SUCCESS; 1235} 1236 1237 1238/*============================================================================ 1239 *============================================================================ 1240 * This is the end of the cgi filter code moved from mod_include. 1241 *============================================================================ 1242 *============================================================================*/ 1243 1244 1245static int cgi_post_config(apr_pool_t *p, apr_pool_t *plog, 1246 apr_pool_t *ptemp, server_rec *s) 1247{ 1248 cgi_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler); 1249 cgi_pfn_gtv = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value); 1250 cgi_pfn_ps = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string); 1251 1252 if ((cgi_pfn_reg_with_ssi) && (cgi_pfn_gtv) && (cgi_pfn_ps)) { 1253 /* Required by mod_include filter. This is how mod_cgi registers 1254 * with mod_include to provide processing of the exec directive. 1255 */ 1256 cgi_pfn_reg_with_ssi("exec", handle_exec); 1257 } 1258 1259 /* This is the means by which unusual (non-unix) os's may find alternate 1260 * means to run a given command (e.g. shebang/registry parsing on Win32) 1261 */ 1262 cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command); 1263 if (!cgi_build_command) { 1264 cgi_build_command = default_build_command; 1265 } 1266 return OK; 1267} 1268 1269static void register_hooks(apr_pool_t *p) 1270{ 1271 static const char * const aszPre[] = { "mod_include.c", NULL }; 1272 ap_hook_handler(cgi_handler, NULL, NULL, APR_HOOK_MIDDLE); 1273 ap_hook_post_config(cgi_post_config, aszPre, NULL, APR_HOOK_REALLY_FIRST); 1274} 1275 1276AP_DECLARE_MODULE(cgi) = 1277{ 1278 STANDARD20_MODULE_STUFF, 1279 NULL, /* dir config creater */ 1280 NULL, /* dir merger --- default is to override */ 1281 create_cgi_config, /* server config */ 1282 merge_cgi_config, /* merge server config */ 1283 cgi_cmds, /* command apr_table_t */ 1284 register_hooks /* register hooks */ 1285}; 1286