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