00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "apr.h"
00031 #include "apr_strings.h"
00032 #include "apr_thread_proc.h"
00033 #include "apr_optional.h"
00034 #include "apr_buckets.h"
00035 #include "apr_lib.h"
00036 #include "apr_poll.h"
00037
00038 #define APR_WANT_STRFUNC
00039 #include "apr_want.h"
00040
00041 #define CORE_PRIVATE
00042
00043 #include "util_filter.h"
00044 #include "ap_config.h"
00045 #include "httpd.h"
00046 #include "http_config.h"
00047 #include "http_request.h"
00048 #include "http_core.h"
00049 #include "http_protocol.h"
00050 #include "http_main.h"
00051 #include "http_log.h"
00052 #include "util_script.h"
00053 #include "ap_mpm.h"
00054 #include "mod_core.h"
00055 #include "mod_cgi.h"
00056
00057 module AP_MODULE_DECLARE_DATA cgi_module;
00058
00059 static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgi_pfn_reg_with_ssi;
00060 static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgi_pfn_gtv;
00061 static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgi_pfn_ps;
00062 static APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
00063
00064
00065 static void discard_script_output(apr_bucket_brigade *bb);
00066
00067
00068
00069
00070
00071
00072
00073 static int is_scriptaliased(request_rec *r)
00074 {
00075 const char *t = apr_table_get(r->notes, "alias-forced-type");
00076 return t && (!strcasecmp(t, "cgi-script"));
00077 }
00078
00079
00080
00081 #define DEFAULT_LOGBYTES 10385760
00082 #define DEFAULT_BUFBYTES 1024
00083
00084 typedef struct {
00085 const char *logname;
00086 long logbytes;
00087 apr_size_t bufbytes;
00088 } cgi_server_conf;
00089
00090 static void *create_cgi_config(apr_pool_t *p, server_rec *s)
00091 {
00092 cgi_server_conf *c =
00093 (cgi_server_conf *) apr_pcalloc(p, sizeof(cgi_server_conf));
00094
00095 c->logname = NULL;
00096 c->logbytes = DEFAULT_LOGBYTES;
00097 c->bufbytes = DEFAULT_BUFBYTES;
00098
00099 return c;
00100 }
00101
00102 static void *merge_cgi_config(apr_pool_t *p, void *basev, void *overridesv)
00103 {
00104 cgi_server_conf *base = (cgi_server_conf *) basev,
00105 *overrides = (cgi_server_conf *) overridesv;
00106
00107 return overrides->logname ? overrides : base;
00108 }
00109
00110 static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg)
00111 {
00112 server_rec *s = cmd->server;
00113 cgi_server_conf *conf = ap_get_module_config(s->module_config,
00114 &cgi_module);
00115
00116 conf->logname = ap_server_root_relative(cmd->pool, arg);
00117
00118 if (!conf->logname) {
00119 return apr_pstrcat(cmd->pool, "Invalid ScriptLog path ",
00120 arg, NULL);
00121 }
00122
00123 return NULL;
00124 }
00125
00126 static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy,
00127 const char *arg)
00128 {
00129 server_rec *s = cmd->server;
00130 cgi_server_conf *conf = ap_get_module_config(s->module_config,
00131 &cgi_module);
00132
00133 conf->logbytes = atol(arg);
00134 return NULL;
00135 }
00136
00137 static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy,
00138 const char *arg)
00139 {
00140 server_rec *s = cmd->server;
00141 cgi_server_conf *conf = ap_get_module_config(s->module_config,
00142 &cgi_module);
00143
00144 conf->bufbytes = atoi(arg);
00145 return NULL;
00146 }
00147
00148 static const command_rec cgi_cmds[] =
00149 {
00150 AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF,
00151 "the name of a log for script debugging info"),
00152 AP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF,
00153 "the maximum length (in bytes) of the script debug log"),
00154 AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF,
00155 "the maximum size (in bytes) to record of a POST request"),
00156 {NULL}
00157 };
00158
00159 static int log_scripterror(request_rec *r, cgi_server_conf * conf, int ret,
00160 apr_status_t rv, char *error)
00161 {
00162 apr_file_t *f = NULL;
00163 apr_finfo_t finfo;
00164 char time_str[APR_CTIME_LEN];
00165 int log_flags = rv ? APLOG_ERR : APLOG_ERR;
00166
00167 ap_log_rerror(APLOG_MARK, log_flags, rv, r,
00168 "%s: %s", error, r->filename);
00169
00170
00171 if (!conf->logname ||
00172 ((apr_stat(&finfo, conf->logname,
00173 APR_FINFO_SIZE, r->pool) == APR_SUCCESS) &&
00174 (finfo.size > conf->logbytes)) ||
00175 (apr_file_open(&f, conf->logname,
00176 APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT,
00177 r->pool) != APR_SUCCESS)) {
00178 return ret;
00179 }
00180
00181
00182 apr_ctime(time_str, apr_time_now());
00183 apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri,
00184 r->args ? "?" : "", r->args ? r->args : "", r->protocol);
00185
00186 apr_file_printf(f, "%%%% %d %s\n", ret, r->filename);
00187
00188 apr_file_printf(f, "%%error\n%s\n", error);
00189
00190 apr_file_close(f);
00191 return ret;
00192 }
00193
00194
00195
00196 static apr_status_t log_script_err(request_rec *r, apr_file_t *script_err)
00197 {
00198 char argsbuffer[HUGE_STRING_LEN];
00199 char *newline;
00200 apr_status_t rv;
00201
00202 while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN,
00203 script_err)) == APR_SUCCESS) {
00204 newline = strchr(argsbuffer, '\n');
00205 if (newline) {
00206 *newline = '\0';
00207 }
00208 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
00209 "%s", argsbuffer);
00210 }
00211
00212 return rv;
00213 }
00214
00215 static int log_script(request_rec *r, cgi_server_conf * conf, int ret,
00216 char *dbuf, const char *sbuf, apr_bucket_brigade *bb,
00217 apr_file_t *script_err)
00218 {
00219 const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in);
00220 const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
00221 char argsbuffer[HUGE_STRING_LEN];
00222 apr_file_t *f = NULL;
00223 apr_bucket *e;
00224 const char *buf;
00225 apr_size_t len;
00226 apr_status_t rv;
00227 int first;
00228 int i;
00229 apr_finfo_t finfo;
00230 char time_str[APR_CTIME_LEN];
00231
00232
00233 if (!conf->logname ||
00234 ((apr_stat(&finfo, conf->logname,
00235 APR_FINFO_SIZE, r->pool) == APR_SUCCESS) &&
00236 (finfo.size > conf->logbytes)) ||
00237 (apr_file_open(&f, conf->logname,
00238 APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT,
00239 r->pool) != APR_SUCCESS)) {
00240
00241 discard_script_output(bb);
00242 log_script_err(r, script_err);
00243 return ret;
00244 }
00245
00246
00247 apr_ctime(time_str, apr_time_now());
00248 apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri,
00249 r->args ? "?" : "", r->args ? r->args : "", r->protocol);
00250
00251 apr_file_printf(f, "%%%% %d %s\n", ret, r->filename);
00252
00253 apr_file_puts("%request\n", f);
00254 for (i = 0; i < hdrs_arr->nelts; ++i) {
00255 if (!hdrs[i].key)
00256 continue;
00257 apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
00258 }
00259 if ((r->method_number == M_POST || r->method_number == M_PUT) &&
00260 *dbuf) {
00261 apr_file_printf(f, "\n%s\n", dbuf);
00262 }
00263
00264 apr_file_puts("%response\n", f);
00265 hdrs_arr = apr_table_elts(r->err_headers_out);
00266 hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
00267
00268 for (i = 0; i < hdrs_arr->nelts; ++i) {
00269 if (!hdrs[i].key)
00270 continue;
00271 apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
00272 }
00273
00274 if (sbuf && *sbuf)
00275 apr_file_printf(f, "%s\n", sbuf);
00276
00277 first = 1;
00278 APR_BRIGADE_FOREACH(e, bb) {
00279 if (APR_BUCKET_IS_EOS(e)) {
00280 break;
00281 }
00282 rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
00283 if (rv != APR_SUCCESS || (len == 0)) {
00284 break;
00285 }
00286 if (first) {
00287 apr_file_puts("%stdout\n", f);
00288 first = 0;
00289 }
00290 apr_file_write(f, buf, &len);
00291 apr_file_puts("\n", f);
00292 }
00293
00294 if (apr_file_gets(argsbuffer, HUGE_STRING_LEN, script_err) == APR_SUCCESS) {
00295 apr_file_puts("%stderr\n", f);
00296 apr_file_puts(argsbuffer, f);
00297 while (apr_file_gets(argsbuffer, HUGE_STRING_LEN,
00298 script_err) == APR_SUCCESS) {
00299 apr_file_puts(argsbuffer, f);
00300 }
00301 apr_file_puts("\n", f);
00302 }
00303
00304 apr_brigade_destroy(bb);
00305 apr_file_close(script_err);
00306
00307 apr_file_close(f);
00308 return ret;
00309 }
00310
00311
00312
00313
00314
00315 static void add_ssi_vars(request_rec *r)
00316 {
00317 apr_table_t *e = r->subprocess_env;
00318
00319 if (r->path_info && r->path_info[0] != '\0') {
00320 request_rec *pa_req;
00321
00322 apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool,
00323 r->path_info));
00324
00325 pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info),
00326 r, NULL);
00327 if (pa_req->filename) {
00328 apr_table_setn(e, "PATH_TRANSLATED",
00329 apr_pstrcat(r->pool, pa_req->filename,
00330 pa_req->path_info, NULL));
00331 }
00332 ap_destroy_sub_req(pa_req);
00333 }
00334
00335 if (r->args) {
00336 char *arg_copy = apr_pstrdup(r->pool, r->args);
00337
00338 apr_table_setn(e, "QUERY_STRING", r->args);
00339 ap_unescape_url(arg_copy);
00340 apr_table_setn(e, "QUERY_STRING_UNESCAPED",
00341 ap_escape_shell_cmd(r->pool, arg_copy));
00342 }
00343 }
00344
00345 static void cgi_child_errfn(apr_pool_t *pool, apr_status_t err,
00346 const char *description)
00347 {
00348 apr_file_t *stderr_log;
00349 char errbuf[200];
00350
00351 apr_file_open_stderr(&stderr_log, pool);
00352
00353
00354
00355 apr_file_printf(stderr_log,
00356 "(%d)%s: %s\n",
00357 err,
00358 apr_strerror(err, errbuf, sizeof(errbuf)),
00359 #ifdef AP_UNSAFE_ERROR_LOG_UNESCAPED
00360 description
00361 #else
00362 ap_escape_logitem(pool, description)
00363 #endif
00364 );
00365 }
00366
00367 static apr_status_t run_cgi_child(apr_file_t **script_out,
00368 apr_file_t **script_in,
00369 apr_file_t **script_err,
00370 const char *command,
00371 const char * const argv[],
00372 request_rec *r,
00373 apr_pool_t *p,
00374 cgi_exec_info_t *e_info)
00375 {
00376 const char * const *env;
00377 apr_procattr_t *procattr;
00378 apr_proc_t *procnew;
00379 apr_status_t rc = APR_SUCCESS;
00380
00381 #if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \
00382 defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
00383
00384 core_dir_config *conf = ap_get_module_config(r->per_dir_config,
00385 &core_module);
00386 #endif
00387
00388 #ifdef DEBUG_CGI
00389 #ifdef OS2
00390
00391 FILE *dbg = fopen("con", "w");
00392 #else
00393 FILE *dbg = fopen("/dev/tty", "w");
00394 #endif
00395 int i;
00396 #endif
00397
00398 RAISE_SIGSTOP(CGI_CHILD);
00399 #ifdef DEBUG_CGI
00400 fprintf(dbg, "Attempting to exec %s as CGI child (argv0 = %s)\n",
00401 r->filename, argv[0]);
00402 #endif
00403
00404 env = (const char * const *)ap_create_environment(p, r->subprocess_env);
00405
00406 #ifdef DEBUG_CGI
00407 fprintf(dbg, "Environment: \n");
00408 for (i = 0; env[i]; ++i)
00409 fprintf(dbg, "'%s'\n", env[i]);
00410 #endif
00411
00412
00413
00414
00415 if (((rc = apr_procattr_create(&procattr, p)) != APR_SUCCESS) ||
00416 ((rc = apr_procattr_io_set(procattr,
00417 e_info->in_pipe,
00418 e_info->out_pipe,
00419 e_info->err_pipe)) != APR_SUCCESS) ||
00420 ((rc = apr_procattr_dir_set(procattr,
00421 ap_make_dirstr_parent(r->pool,
00422 r->filename))) != APR_SUCCESS) ||
00423 #ifdef RLIMIT_CPU
00424 ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_CPU,
00425 conf->limit_cpu)) != APR_SUCCESS) ||
00426 #endif
00427 #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
00428 ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_MEM,
00429 conf->limit_mem)) != APR_SUCCESS) ||
00430 #endif
00431 #ifdef RLIMIT_NPROC
00432 ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC,
00433 conf->limit_nproc)) != APR_SUCCESS) ||
00434 #endif
00435 ((rc = apr_procattr_cmdtype_set(procattr,
00436 e_info->cmd_type)) != APR_SUCCESS) ||
00437
00438 ((rc = apr_procattr_detach_set(procattr,
00439 e_info->detached)) != APR_SUCCESS) ||
00440 ((rc = apr_procattr_child_errfn_set(procattr, cgi_child_errfn)) != APR_SUCCESS)) {
00441
00442 ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
00443 "couldn't set child process attributes: %s", r->filename);
00444 }
00445 else {
00446 procnew = apr_pcalloc(p, sizeof(*procnew));
00447 if (e_info->prog_type == RUN_AS_SSI) {
00448 SPLIT_AND_PASS_PRETAG_BUCKETS(*(e_info->bb), e_info->ctx,
00449 e_info->next, rc);
00450 if (rc != APR_SUCCESS) {
00451 return rc;
00452 }
00453 }
00454
00455 rc = ap_os_create_privileged_process(r, procnew, command, argv, env,
00456 procattr, p);
00457
00458 if (rc != APR_SUCCESS) {
00459
00460 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, rc, r,
00461 "couldn't create child process: %d: %s", rc,
00462 apr_filename_of_pathname(r->filename));
00463 }
00464 else {
00465 apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
00466
00467 *script_in = procnew->out;
00468 if (!*script_in)
00469 return APR_EBADF;
00470 apr_file_pipe_timeout_set(*script_in, r->server->timeout);
00471
00472 if (e_info->prog_type == RUN_AS_CGI) {
00473 *script_out = procnew->in;
00474 if (!*script_out)
00475 return APR_EBADF;
00476 apr_file_pipe_timeout_set(*script_out, r->server->timeout);
00477
00478 *script_err = procnew->err;
00479 if (!*script_err)
00480 return APR_EBADF;
00481 apr_file_pipe_timeout_set(*script_err, r->server->timeout);
00482 }
00483 }
00484 }
00485 #ifdef DEBUG_CGI
00486 fclose(dbg);
00487 #endif
00488 return (rc);
00489 }
00490
00491
00492 static apr_status_t default_build_command(const char **cmd, const char ***argv,
00493 request_rec *r, apr_pool_t *p,
00494 cgi_exec_info_t *e_info)
00495 {
00496 int numwords, x, idx;
00497 char *w;
00498 const char *args = NULL;
00499
00500 if (e_info->process_cgi) {
00501 *cmd = r->filename;
00502
00503
00504 if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) {
00505 args = r->args;
00506 }
00507 }
00508
00509 if (!args) {
00510 numwords = 1;
00511 }
00512 else {
00513
00514 for (x = 0, numwords = 2; args[x]; x++) {
00515 if (args[x] == '+') {
00516 ++numwords;
00517 }
00518 }
00519 }
00520
00521
00522
00523 if (numwords > APACHE_ARG_MAX - 1) {
00524 numwords = APACHE_ARG_MAX - 1;
00525 }
00526 *argv = apr_palloc(p, (numwords + 2) * sizeof(char *));
00527 (*argv)[0] = *cmd;
00528 for (x = 1, idx = 1; x < numwords; x++) {
00529 w = ap_getword_nulls(p, &args, '+');
00530 ap_unescape_url(w);
00531 (*argv)[idx++] = ap_escape_shell_cmd(p, w);
00532 }
00533 (*argv)[idx] = NULL;
00534
00535 return APR_SUCCESS;
00536 }
00537
00538 static void discard_script_output(apr_bucket_brigade *bb)
00539 {
00540 apr_bucket *e;
00541 const char *buf;
00542 apr_size_t len;
00543 apr_status_t rv;
00544 APR_BRIGADE_FOREACH(e, bb) {
00545 if (APR_BUCKET_IS_EOS(e)) {
00546 break;
00547 }
00548 rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
00549 if (rv != APR_SUCCESS) {
00550 break;
00551 }
00552 }
00553 }
00554
00555 #if APR_FILES_AS_SOCKETS
00556
00557
00558
00559 static const apr_bucket_type_t bucket_type_cgi;
00560
00561 struct cgi_bucket_data {
00562 apr_pollset_t *pollset;
00563 request_rec *r;
00564 };
00565
00566
00567
00568 static apr_bucket *cgi_bucket_create(request_rec *r,
00569 apr_file_t *out, apr_file_t *err,
00570 apr_bucket_alloc_t *list)
00571 {
00572 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
00573 apr_status_t rv;
00574 apr_pollfd_t fd;
00575 struct cgi_bucket_data *data = apr_palloc(r->pool, sizeof *data);
00576
00577 APR_BUCKET_INIT(b);
00578 b->free = apr_bucket_free;
00579 b->list = list;
00580 b->type = &bucket_type_cgi;
00581 b->length = (apr_size_t)(-1);
00582 b->start = -1;
00583
00584
00585 rv = apr_pollset_create(&data->pollset, 2, r->pool, 0);
00586 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
00587
00588 fd.desc_type = APR_POLL_FILE;
00589 fd.reqevents = APR_POLLIN;
00590 fd.p = r->pool;
00591 fd.desc.f = out;
00592 fd.client_data = (void *)1;
00593 rv = apr_pollset_add(data->pollset, &fd);
00594 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
00595
00596 fd.desc.f = err;
00597 fd.client_data = (void *)2;
00598 rv = apr_pollset_add(data->pollset, &fd);
00599 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
00600
00601 data->r = r;
00602 b->data = data;
00603 return b;
00604 }
00605
00606
00607 static apr_bucket *cgi_bucket_dup(struct cgi_bucket_data *data,
00608 apr_bucket_alloc_t *list)
00609 {
00610 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
00611 APR_BUCKET_INIT(b);
00612 b->free = apr_bucket_free;
00613 b->list = list;
00614 b->type = &bucket_type_cgi;
00615 b->length = (apr_size_t)(-1);
00616 b->start = -1;
00617 b->data = data;
00618 return b;
00619 }
00620
00621
00622
00623 static apr_status_t cgi_read_stdout(apr_bucket *a, apr_file_t *out,
00624 const char **str, apr_size_t *len)
00625 {
00626 char *buf;
00627 apr_status_t rv;
00628
00629 *str = NULL;
00630 *len = APR_BUCKET_BUFF_SIZE;
00631 buf = apr_bucket_alloc(*len, a->list);
00632
00633 rv = apr_file_read(out, buf, len);
00634
00635 if (rv != APR_SUCCESS && rv != APR_EOF) {
00636 apr_bucket_free(buf);
00637 return rv;
00638 }
00639
00640 if (*len > 0) {
00641 struct cgi_bucket_data *data = a->data;
00642 apr_bucket_heap *h;
00643
00644
00645 a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free);
00646 h = a->data;
00647 h->alloc_len = APR_BUCKET_BUFF_SIZE;
00648 *str = buf;
00649 APR_BUCKET_INSERT_AFTER(a, cgi_bucket_dup(data, a->list));
00650 }
00651 else {
00652 apr_bucket_free(buf);
00653 a = apr_bucket_immortal_make(a, "", 0);
00654 *str = a->data;
00655 }
00656 return rv;
00657 }
00658
00659
00660
00661 static apr_status_t cgi_bucket_read(apr_bucket *b, const char **str,
00662 apr_size_t *len, apr_read_type_e block)
00663 {
00664 struct cgi_bucket_data *data = b->data;
00665 apr_interval_time_t timeout;
00666 apr_status_t rv;
00667 int gotdata = 0;
00668
00669 timeout = block == APR_NONBLOCK_READ ? 0 : data->r->server->timeout;
00670
00671 do {
00672 const apr_pollfd_t *results;
00673 apr_int32_t num;
00674
00675 rv = apr_pollset_poll(data->pollset, timeout, &num, &results);
00676 if (APR_STATUS_IS_TIMEUP(rv)) {
00677 return timeout == 0 ? APR_EAGAIN : rv;
00678 }
00679 else if (APR_STATUS_IS_EINTR(rv)) {
00680 continue;
00681 }
00682 else if (rv != APR_SUCCESS) {
00683 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r,
00684 "poll failed waiting for CGI child");
00685 return rv;
00686 }
00687
00688 for (; num; num--, results++) {
00689 if (results[0].client_data == (void *)1) {
00690
00691 rv = cgi_read_stdout(b, results[0].desc.f, str, len);
00692 if (APR_STATUS_IS_EOF(rv)) {
00693 rv = APR_SUCCESS;
00694 }
00695 gotdata = 1;
00696 } else {
00697
00698 apr_status_t rv2 = log_script_err(data->r, results[0].desc.f);
00699 if (APR_STATUS_IS_EOF(rv2)) {
00700 apr_pollset_remove(data->pollset, &results[0]);
00701 }
00702 }
00703 }
00704
00705 } while (!gotdata);
00706
00707 return rv;
00708 }
00709
00710 static const apr_bucket_type_t bucket_type_cgi = {
00711 "CGI", 5, APR_BUCKET_DATA,
00712 apr_bucket_destroy_noop,
00713 cgi_bucket_read,
00714 apr_bucket_setaside_notimpl,
00715 apr_bucket_split_notimpl,
00716 apr_bucket_copy_notimpl
00717 };
00718
00719 #endif
00720
00721 static int cgi_handler(request_rec *r)
00722 {
00723 int nph;
00724 apr_size_t dbpos = 0;
00725 const char *argv0;
00726 const char *command;
00727 const char **argv;
00728 char *dbuf = NULL;
00729 apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL;
00730 apr_bucket_brigade *bb;
00731 apr_bucket *b;
00732 int is_included;
00733 int seen_eos, child_stopped_reading;
00734 apr_pool_t *p;
00735 cgi_server_conf *conf;
00736 apr_status_t rv;
00737 cgi_exec_info_t e_info;
00738 conn_rec *c = r->connection;
00739
00740 if(strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script"))
00741 return DECLINED;
00742
00743 is_included = !strcmp(r->protocol, "INCLUDED");
00744
00745 p = r->main ? r->main->pool : r->pool;
00746
00747 if (r->method_number == M_OPTIONS) {
00748
00749 r->allowed |= (AP_METHOD_BIT << M_GET);
00750 r->allowed |= (AP_METHOD_BIT << M_POST);
00751 return DECLINED;
00752 }
00753
00754 argv0 = apr_filename_of_pathname(r->filename);
00755 nph = !(strncmp(argv0, "nph-", 4));
00756 conf = ap_get_module_config(r->server->module_config, &cgi_module);
00757
00758 if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r))
00759 return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
00760 "Options ExecCGI is off in this directory");
00761 if (nph && is_included)
00762 return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
00763 "attempt to include NPH CGI script");
00764
00765 if (r->finfo.filetype == 0)
00766 return log_scripterror(r, conf, HTTP_NOT_FOUND, 0,
00767 "script not found or unable to stat");
00768 if (r->finfo.filetype == APR_DIR)
00769 return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
00770 "attempt to invoke directory as script");
00771
00772 if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) &&
00773 r->path_info && *r->path_info)
00774 {
00775
00776 return log_scripterror(r, conf, HTTP_NOT_FOUND, 0,
00777 "AcceptPathInfo off disallows user's path");
00778 }
00779
00780
00781
00782
00783
00784
00785
00786
00787 ap_add_common_vars(r);
00788 ap_add_cgi_vars(r);
00789
00790 e_info.process_cgi = 1;
00791 e_info.cmd_type = APR_PROGRAM;
00792 e_info.detached = 0;
00793 e_info.in_pipe = APR_CHILD_BLOCK;
00794 e_info.out_pipe = APR_CHILD_BLOCK;
00795 e_info.err_pipe = APR_CHILD_BLOCK;
00796 e_info.prog_type = RUN_AS_CGI;
00797 e_info.bb = NULL;
00798 e_info.ctx = NULL;
00799 e_info.next = NULL;
00800
00801
00802 if ((rv = cgi_build_command(&command, &argv, r, p, &e_info)) != APR_SUCCESS) {
00803 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
00804 "don't know how to spawn child process: %s",
00805 r->filename);
00806 return HTTP_INTERNAL_SERVER_ERROR;
00807 }
00808
00809
00810 if ((rv = run_cgi_child(&script_out, &script_in, &script_err,
00811 command, argv, r, p, &e_info)) != APR_SUCCESS) {
00812 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
00813 "couldn't spawn child process: %s", r->filename);
00814 return HTTP_INTERNAL_SERVER_ERROR;
00815 }
00816
00817
00818
00819
00820 bb = apr_brigade_create(r->pool, c->bucket_alloc);
00821 seen_eos = 0;
00822 child_stopped_reading = 0;
00823 if (conf->logname) {
00824 dbuf = apr_palloc(r->pool, conf->bufbytes + 1);
00825 dbpos = 0;
00826 }
00827 do {
00828 apr_bucket *bucket;
00829
00830 rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
00831 APR_BLOCK_READ, HUGE_STRING_LEN);
00832
00833 if (rv != APR_SUCCESS) {
00834 return rv;
00835 }
00836
00837 APR_BRIGADE_FOREACH(bucket, bb) {
00838 const char *data;
00839 apr_size_t len;
00840
00841 if (APR_BUCKET_IS_EOS(bucket)) {
00842 seen_eos = 1;
00843 break;
00844 }
00845
00846
00847 if (APR_BUCKET_IS_FLUSH(bucket)) {
00848 continue;
00849 }
00850
00851
00852 if (child_stopped_reading) {
00853 continue;
00854 }
00855
00856
00857 apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
00858
00859 if (conf->logname && dbpos < conf->bufbytes) {
00860 int cursize;
00861
00862 if ((dbpos + len) > conf->bufbytes) {
00863 cursize = conf->bufbytes - dbpos;
00864 }
00865 else {
00866 cursize = len;
00867 }
00868 memcpy(dbuf + dbpos, data, cursize);
00869 dbpos += cursize;
00870 }
00871
00872
00873
00874
00875 rv = apr_file_write_full(script_out, data, len, NULL);
00876
00877 if (rv != APR_SUCCESS) {
00878
00879 child_stopped_reading = 1;
00880 }
00881 }
00882 apr_brigade_cleanup(bb);
00883 }
00884 while (!seen_eos);
00885
00886 if (conf->logname) {
00887 dbuf[dbpos] = '\0';
00888 }
00889
00890 apr_file_flush(script_out);
00891 apr_file_close(script_out);
00892
00893 AP_DEBUG_ASSERT(script_in != NULL);
00894
00895 apr_brigade_cleanup(bb);
00896
00897 #if APR_FILES_AS_SOCKETS
00898 apr_file_pipe_timeout_set(script_in, 0);
00899 apr_file_pipe_timeout_set(script_err, 0);
00900
00901 b = cgi_bucket_create(r, script_in, script_err, c->bucket_alloc);
00902 #else
00903 b = apr_bucket_pipe_create(script_in, c->bucket_alloc);
00904 #endif
00905 APR_BRIGADE_INSERT_TAIL(bb, b);
00906 b = apr_bucket_eos_create(c->bucket_alloc);
00907 APR_BRIGADE_INSERT_TAIL(bb, b);
00908
00909
00910 if (!nph) {
00911 const char *location;
00912 char sbuf[MAX_STRING_LEN];
00913 int ret;
00914
00915 if ((ret = ap_scan_script_header_err_brigade(r, bb, sbuf))) {
00916 return log_script(r, conf, ret, dbuf, sbuf, bb, script_err);
00917 }
00918
00919 location = apr_table_get(r->headers_out, "Location");
00920
00921 if (location && location[0] == '/' && r->status == 200) {
00922 discard_script_output(bb);
00923 apr_brigade_destroy(bb);
00924 apr_file_pipe_timeout_set(script_err, r->server->timeout);
00925 log_script_err(r, script_err);
00926
00927
00928
00929 r->method = apr_pstrdup(r->pool, "GET");
00930 r->method_number = M_GET;
00931
00932
00933
00934
00935
00936 apr_table_unset(r->headers_in, "Content-Length");
00937
00938 ap_internal_redirect_handler(location, r);
00939 return OK;
00940 }
00941 else if (location && r->status == 200) {
00942
00943
00944
00945 discard_script_output(bb);
00946 apr_brigade_destroy(bb);
00947 return HTTP_MOVED_TEMPORARILY;
00948 }
00949
00950 rv = ap_pass_brigade(r->output_filters, bb);
00951 }
00952 else {
00953 struct ap_filter_t *cur;
00954
00955
00956
00957
00958
00959
00960 cur = r->proto_output_filters;
00961 while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) {
00962 cur = cur->next;
00963 }
00964 r->output_filters = r->proto_output_filters = cur;
00965
00966 rv = ap_pass_brigade(r->output_filters, bb);
00967 }
00968
00969
00970
00971
00972
00973 if (rv == APR_SUCCESS && !r->connection->aborted) {
00974 apr_file_pipe_timeout_set(script_err, r->server->timeout);
00975 log_script_err(r, script_err);
00976 }
00977
00978 apr_file_close(script_err);
00979
00980 return OK;
00981 }
00982
00983
00984
00985
00986
00987
00988
00989 static int include_cgi(char *s, request_rec *r, ap_filter_t *next,
00990 apr_bucket *head_ptr, apr_bucket **inserted_head)
00991 {
00992 request_rec *rr = ap_sub_req_lookup_uri(s, r, next);
00993 int rr_status;
00994 apr_bucket *tmp_buck, *tmp2_buck;
00995
00996 if (rr->status != HTTP_OK) {
00997 ap_destroy_sub_req(rr);
00998 return -1;
00999 }
01000
01001
01002
01003 if ((rr->path_info && rr->path_info[0]) || rr->args) {
01004 ap_destroy_sub_req(rr);
01005 return -1;
01006 }
01007 if (rr->finfo.filetype != APR_REG) {
01008 ap_destroy_sub_req(rr);
01009 return -1;
01010 }
01011
01012
01013
01014 rr->path_info = r->path_info;
01015 rr->args = r->args;
01016
01017
01018
01019
01020
01021 ap_set_content_type(rr, CGI_MAGIC_TYPE);
01022
01023
01024
01025 rr_status = ap_run_sub_req(rr);
01026 if (ap_is_HTTP_REDIRECT(rr_status)) {
01027 apr_size_t len_loc;
01028 const char *location = apr_table_get(rr->headers_out, "Location");
01029 conn_rec *c = r->connection;
01030
01031 location = ap_escape_html(rr->pool, location);
01032 len_loc = strlen(location);
01033
01034
01035
01036
01037
01038 tmp_buck = apr_bucket_immortal_create("<A HREF=\"",
01039 sizeof("<A HREF=\"") - 1,
01040 c->bucket_alloc);
01041 APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
01042 tmp2_buck = apr_bucket_heap_create(location, len_loc, NULL,
01043 c->bucket_alloc);
01044 APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
01045 tmp2_buck = apr_bucket_immortal_create("\">", sizeof("\">") - 1,
01046 c->bucket_alloc);
01047 APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
01048 tmp2_buck = apr_bucket_heap_create(location, len_loc, NULL,
01049 c->bucket_alloc);
01050 APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
01051 tmp2_buck = apr_bucket_immortal_create("</A>", sizeof("</A>") - 1,
01052 c->bucket_alloc);
01053 APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
01054
01055 if (*inserted_head == NULL) {
01056 *inserted_head = tmp_buck;
01057 }
01058 }
01059
01060 ap_destroy_sub_req(rr);
01061
01062 return 0;
01063 }
01064
01065
01066 static int include_cmd(include_ctx_t *ctx, apr_bucket_brigade **bb,
01067 const char *command, request_rec *r, ap_filter_t *f)
01068 {
01069 cgi_exec_info_t e_info;
01070 const char **argv;
01071 apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL;
01072 apr_bucket_brigade *bcgi;
01073 apr_bucket *b;
01074 apr_status_t rv;
01075
01076 add_ssi_vars(r);
01077
01078 e_info.process_cgi = 0;
01079 e_info.cmd_type = APR_SHELLCMD;
01080 e_info.detached = 0;
01081 e_info.in_pipe = APR_NO_PIPE;
01082 e_info.out_pipe = APR_FULL_BLOCK;
01083 e_info.err_pipe = APR_NO_PIPE;
01084 e_info.prog_type = RUN_AS_SSI;
01085 e_info.bb = bb;
01086 e_info.ctx = ctx;
01087 e_info.next = f->next;
01088
01089 if ((rv = cgi_build_command(&command, &argv, r, r->pool, &e_info)) != APR_SUCCESS) {
01090 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
01091 "don't know how to spawn cmd child process: %s",
01092 r->filename);
01093 return HTTP_INTERNAL_SERVER_ERROR;
01094 }
01095
01096
01097 if ((rv = run_cgi_child(&script_out, &script_in, &script_err,
01098 command, argv, r, r->pool,
01099 &e_info)) != APR_SUCCESS) {
01100 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
01101 "couldn't spawn child process: %s", r->filename);
01102 return HTTP_INTERNAL_SERVER_ERROR;
01103 }
01104
01105 bcgi = apr_brigade_create(r->pool, f->c->bucket_alloc);
01106 b = apr_bucket_pipe_create(script_in, f->c->bucket_alloc);
01107 APR_BRIGADE_INSERT_TAIL(bcgi, b);
01108 ap_pass_brigade(f->next, bcgi);
01109
01110
01111
01112
01113
01114
01115 return 0;
01116 }
01117
01118 static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb,
01119 request_rec *r, ap_filter_t *f, apr_bucket *head_ptr,
01120 apr_bucket **inserted_head)
01121 {
01122 char *tag = NULL;
01123 char *tag_val = NULL;
01124 char *file = r->filename;
01125 apr_bucket *tmp_buck;
01126 char parsed_string[MAX_STRING_LEN];
01127
01128 *inserted_head = NULL;
01129 if (ctx->flags & FLAG_PRINTING) {
01130 if (ctx->flags & FLAG_NO_EXEC) {
01131 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
01132 "exec used but not allowed in %s", r->filename);
01133 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
01134 }
01135 else {
01136 while (1) {
01137 cgi_pfn_gtv(ctx, &tag, &tag_val, 1);
01138 if (tag_val == NULL) {
01139 if (tag == NULL) {
01140 return 0;
01141 }
01142 else {
01143 return 1;
01144 }
01145 }
01146 if (!strcmp(tag, "cmd")) {
01147 cgi_pfn_ps(r, ctx, tag_val, parsed_string,
01148 sizeof(parsed_string), 1);
01149 if (include_cmd(ctx, bb, parsed_string, r, f) == -1) {
01150 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
01151 "execution failure for parameter \"%s\" "
01152 "to tag exec in file %s", tag, r->filename);
01153 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr,
01154 *inserted_head);
01155 }
01156 }
01157 else if (!strcmp(tag, "cgi")) {
01158 apr_status_t retval = APR_SUCCESS;
01159
01160 cgi_pfn_ps(r, ctx, tag_val, parsed_string,
01161 sizeof(parsed_string), 0);
01162
01163 SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next, retval);
01164 if (retval != APR_SUCCESS) {
01165 return retval;
01166 }
01167
01168 if (include_cgi(parsed_string, r, f->next, head_ptr,
01169 inserted_head) == -1) {
01170 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
01171 "invalid CGI ref \"%s\" in %s",
01172 tag_val, file);
01173 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr,
01174 *inserted_head);
01175 }
01176 }
01177 else {
01178 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
01179 "unknown parameter \"%s\" to tag exec in %s",
01180 tag, file);
01181 CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr,
01182 *inserted_head);
01183 }
01184 }
01185 }
01186 }
01187 return 0;
01188 }
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198 static int cgi_post_config(apr_pool_t *p, apr_pool_t *plog,
01199 apr_pool_t *ptemp, server_rec *s)
01200 {
01201 cgi_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
01202 cgi_pfn_gtv = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value);
01203 cgi_pfn_ps = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string);
01204
01205 if ((cgi_pfn_reg_with_ssi) && (cgi_pfn_gtv) && (cgi_pfn_ps)) {
01206
01207
01208
01209 cgi_pfn_reg_with_ssi("exec", handle_exec);
01210 }
01211
01212
01213
01214
01215 cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command);
01216 if (!cgi_build_command) {
01217 cgi_build_command = default_build_command;
01218 }
01219 return OK;
01220 }
01221
01222 static void register_hooks(apr_pool_t *p)
01223 {
01224 static const char * const aszPre[] = { "mod_include.c", NULL };
01225 ap_hook_handler(cgi_handler, NULL, NULL, APR_HOOK_MIDDLE);
01226 ap_hook_post_config(cgi_post_config, aszPre, NULL, APR_HOOK_REALLY_FIRST);
01227 }
01228
01229 module AP_MODULE_DECLARE_DATA cgi_module =
01230 {
01231 STANDARD20_MODULE_STUFF,
01232 NULL,
01233 NULL,
01234 create_cgi_config,
01235 merge_cgi_config,
01236 cgi_cmds,
01237 register_hooks
01238 };