]> granicus.if.org Git - apache/blob - modules/generators/mod_cgi.c
Call apr_file_write_full instead to achieve the same effect.
[apache] / modules / generators / mod_cgi.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /*
60  * http_script: keeps all script-related ramblings together.
61  * 
62  * Compliant to CGI/1.1 spec
63  * 
64  * Adapted by rst from original NCSA code by Rob McCool
65  *
66  * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for
67  * custom error responses, and DOCUMENT_ROOT because we found it useful.
68  * It also adds SERVER_ADMIN - useful for scripts to know who to mail when 
69  * they fail.
70  */
71
72 #include "apr.h"
73 #include "apr_strings.h"
74 #include "apr_thread_proc.h"    /* for RLIMIT stuff */
75 #include "apr_optional.h"
76 #include "apr_buckets.h"
77 #include "apr_lib.h"
78
79 #define APR_WANT_STRFUNC
80 #include "apr_want.h"
81
82 #define CORE_PRIVATE
83
84 #include "util_filter.h"
85 #include "ap_config.h"
86 #include "httpd.h"
87 #include "http_config.h"
88 #include "http_request.h"
89 #include "http_core.h"
90 #include "http_protocol.h"
91 #include "http_main.h"
92 #include "http_log.h"
93 #include "util_script.h"
94 #include "ap_mpm.h"
95 #include "mod_core.h"
96 #include "../filters/mod_include.h"
97 #include "mod_cgi.h"
98
99 module AP_MODULE_DECLARE_DATA cgi_module;
100
101 static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgi_pfn_reg_with_ssi;
102 static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgi_pfn_gtv;
103 static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgi_pfn_ps;
104 static APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command;
105
106 typedef enum {RUN_AS_SSI, RUN_AS_CGI} prog_types;
107
108 typedef struct {
109     apr_int32_t          in_pipe;
110     apr_int32_t          out_pipe;
111     apr_int32_t          err_pipe;
112     apr_cmdtype_e        cmd_type;
113     prog_types           prog_type;
114     apr_bucket_brigade **bb;
115     include_ctx_t       *ctx;
116     ap_filter_t         *next;
117 } exec_info;
118
119 /* Read and discard the data in the brigade produced by a CGI script */
120 static void discard_script_output(apr_bucket_brigade *bb);
121
122 /* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
123  * in ScriptAliased directories, which means we need to know if this
124  * request came through ScriptAlias or not... so the Alias module
125  * leaves a note for us.
126  */
127
128 static int is_scriptaliased(request_rec *r)
129 {
130     const char *t = apr_table_get(r->notes, "alias-forced-type");
131     return t && (!strcasecmp(t, "cgi-script"));
132 }
133
134 /* Configuration stuff */
135
136 #define DEFAULT_LOGBYTES 10385760
137 #define DEFAULT_BUFBYTES 1024
138
139 typedef struct {
140     const char *logname;
141     long logbytes;
142     int bufbytes;
143 } cgi_server_conf;
144
145 static void *create_cgi_config(apr_pool_t *p, server_rec *s)
146 {
147     cgi_server_conf *c =
148     (cgi_server_conf *) apr_pcalloc(p, sizeof(cgi_server_conf));
149
150     c->logname = NULL;
151     c->logbytes = DEFAULT_LOGBYTES;
152     c->bufbytes = DEFAULT_BUFBYTES;
153
154     return c;
155 }
156
157 static void *merge_cgi_config(apr_pool_t *p, void *basev, void *overridesv)
158 {
159     cgi_server_conf *base = (cgi_server_conf *) basev,
160                     *overrides = (cgi_server_conf *) overridesv;
161
162     return overrides->logname ? overrides : base;
163 }
164
165 static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg)
166 {
167     server_rec *s = cmd->server;
168     cgi_server_conf *conf = ap_get_module_config(s->module_config,
169                                                  &cgi_module);
170
171     conf->logname = ap_server_root_relative(cmd->pool, arg);
172
173     if (!conf->logname) {
174         return apr_pstrcat(cmd->pool, "Invalid ScriptLog path ",
175                            arg, NULL);
176     }
177
178     return NULL;
179 }
180
181 static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy,
182                                         const char *arg)
183 {
184     server_rec *s = cmd->server;
185     cgi_server_conf *conf = ap_get_module_config(s->module_config,
186                                                  &cgi_module);
187
188     conf->logbytes = atol(arg);
189     return NULL;
190 }
191
192 static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy,
193                                         const char *arg)
194 {
195     server_rec *s = cmd->server;
196     cgi_server_conf *conf = ap_get_module_config(s->module_config,
197                                                  &cgi_module);
198
199     conf->bufbytes = atoi(arg);
200     return NULL;
201 }
202
203 static const command_rec cgi_cmds[] =
204 {
205 AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF,
206      "the name of a log for script debugging info"),
207 AP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF,
208      "the maximum length (in bytes) of the script debug log"),
209 AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF,
210      "the maximum size (in bytes) to record of a POST request"),
211     {NULL}
212 };
213
214 static int log_scripterror(request_rec *r, cgi_server_conf * conf, int ret,
215                            apr_status_t rv, char *error)
216 {
217     apr_file_t *f = NULL;
218     apr_finfo_t finfo;
219     char time_str[APR_CTIME_LEN];
220     int log_flags = rv ? APLOG_ERR : APLOG_ERR;
221
222     ap_log_rerror(APLOG_MARK, log_flags, rv, r, 
223                   "%s: %s", error, r->filename);
224
225     /* XXX Very expensive mainline case! Open, then getfileinfo! */
226     if (!conf->logname ||
227         ((apr_stat(&finfo, conf->logname,
228                    APR_FINFO_SIZE, r->pool) == APR_SUCCESS) &&
229          (finfo.size > conf->logbytes)) ||
230         (apr_file_open(&f, conf->logname,
231                        APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT,
232                        r->pool) != APR_SUCCESS)) {
233         return ret;
234     }
235
236     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
237     apr_ctime(time_str, apr_time_now());
238     apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri,
239                     r->args ? "?" : "", r->args ? r->args : "", r->protocol);
240     /* "%% 500 /usr/local/apache/cgi-bin */
241     apr_file_printf(f, "%%%% %d %s\n", ret, r->filename);
242
243     apr_file_printf(f, "%%error\n%s\n", error);
244
245     apr_file_close(f);
246     return ret;
247 }
248
249 /* Soak up stderr from a script and redirect it to the error log. 
250  */
251 static void log_script_err(request_rec *r, apr_file_t *script_err)
252 {
253     char argsbuffer[HUGE_STRING_LEN];
254     char *newline;
255
256     while (apr_file_gets(argsbuffer, HUGE_STRING_LEN,
257                          script_err) == APR_SUCCESS) {
258         newline = strchr(argsbuffer, '\n');
259         if (newline) {
260             *newline = '\0';
261         }
262         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
263                       "%s", argsbuffer);            
264     }
265 }
266
267 static int log_script(request_rec *r, cgi_server_conf * conf, int ret,
268                       char *dbuf, const char *sbuf, apr_bucket_brigade *bb, 
269                       apr_file_t *script_err)
270 {
271     const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in);
272     const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
273     char argsbuffer[HUGE_STRING_LEN];
274     apr_file_t *f = NULL;
275     apr_bucket *e;
276     const char *buf;
277     apr_size_t len;
278     apr_status_t rv;
279     int first;
280     int i;
281     apr_finfo_t finfo;
282     char time_str[APR_CTIME_LEN];
283
284     /* XXX Very expensive mainline case! Open, then getfileinfo! */
285     if (!conf->logname ||
286         ((apr_stat(&finfo, conf->logname,
287                    APR_FINFO_SIZE, r->pool) == APR_SUCCESS) &&
288          (finfo.size > conf->logbytes)) ||
289         (apr_file_open(&f, conf->logname,
290                        APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT,
291                        r->pool) != APR_SUCCESS)) {
292         /* Soak up script output */
293         discard_script_output(bb);
294         log_script_err(r, script_err);
295         return ret;
296     }
297
298     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
299     apr_ctime(time_str, apr_time_now());
300     apr_file_printf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri,
301                     r->args ? "?" : "", r->args ? r->args : "", r->protocol);
302     /* "%% 500 /usr/local/apache/cgi-bin" */
303     apr_file_printf(f, "%%%% %d %s\n", ret, r->filename);
304
305     apr_file_puts("%request\n", f);
306     for (i = 0; i < hdrs_arr->nelts; ++i) {
307         if (!hdrs[i].key)
308             continue;
309         apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
310     }
311     if ((r->method_number == M_POST || r->method_number == M_PUT) &&
312         *dbuf) {
313         apr_file_printf(f, "\n%s\n", dbuf);
314     }
315
316     apr_file_puts("%response\n", f);
317     hdrs_arr = apr_table_elts(r->err_headers_out);
318     hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
319
320     for (i = 0; i < hdrs_arr->nelts; ++i) {
321         if (!hdrs[i].key)
322             continue;
323         apr_file_printf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
324     }
325
326     if (sbuf && *sbuf)
327         apr_file_printf(f, "%s\n", sbuf);
328
329     first = 1;
330     APR_BRIGADE_FOREACH(e, bb) {
331         if (APR_BUCKET_IS_EOS(e)) {
332             break;
333         }
334         rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
335         if (!APR_STATUS_IS_SUCCESS(rv) || (len == 0)) {
336             break;
337         }
338         if (first) {
339             apr_file_puts("%stdout\n", f);
340             first = 0;
341         }
342         apr_file_write(f, buf, &len);
343         apr_file_puts("\n", f);
344     }
345
346     if (apr_file_gets(argsbuffer, HUGE_STRING_LEN, script_err) == APR_SUCCESS) {
347         apr_file_puts("%stderr\n", f);
348         apr_file_puts(argsbuffer, f);
349         while (apr_file_gets(argsbuffer, HUGE_STRING_LEN,
350                              script_err) == APR_SUCCESS) {
351             apr_file_puts(argsbuffer, f);
352         }
353         apr_file_puts("\n", f);
354     }
355
356     apr_brigade_destroy(bb);
357     apr_file_close(script_err);
358
359     apr_file_close(f);
360     return ret;
361 }
362
363
364 /* This is the special environment used for running the "exec cmd="
365  *   variety of SSI directives.
366  */
367 static void add_ssi_vars(request_rec *r, ap_filter_t *next)
368 {
369     apr_table_t *e = r->subprocess_env;
370
371     if (r->path_info && r->path_info[0] != '\0') {
372         request_rec *pa_req;
373
374         apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool,
375                                                            r->path_info));
376
377         pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info),
378                                        r, next);
379         if (pa_req->filename) {
380             apr_table_setn(e, "PATH_TRANSLATED",
381                            apr_pstrcat(r->pool, pa_req->filename,
382                                        pa_req->path_info, NULL));
383         }
384         ap_destroy_sub_req(pa_req);
385     }
386
387     if (r->args) {
388         char *arg_copy = apr_pstrdup(r->pool, r->args);
389
390         apr_table_setn(e, "QUERY_STRING", r->args);
391         ap_unescape_url(arg_copy);
392         apr_table_setn(e, "QUERY_STRING_UNESCAPED",
393                        ap_escape_shell_cmd(r->pool, arg_copy));
394     }
395 }
396
397 static apr_status_t run_cgi_child(apr_file_t **script_out,
398                                   apr_file_t **script_in,
399                                   apr_file_t **script_err, 
400                                   const char *command,
401                                   const char * const argv[],
402                                   request_rec *r,
403                                   apr_pool_t *p,
404                                   exec_info *e_info)
405 {
406     const char * const *env;
407     apr_procattr_t *procattr;
408     apr_proc_t *procnew;
409     apr_status_t rc = APR_SUCCESS;
410
411 #if defined(RLIMIT_CPU)  || defined(RLIMIT_NPROC) || \
412     defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
413
414     core_dir_config *conf = ap_get_module_config(r->per_dir_config,
415                                                  &core_module);
416 #endif
417
418
419 #ifdef DEBUG_CGI
420 #ifdef OS2
421     /* Under OS/2 need to use device con. */
422     FILE *dbg = fopen("con", "w");
423 #else
424     FILE *dbg = fopen("/dev/tty", "w");
425 #endif
426     int i;
427 #endif
428
429     RAISE_SIGSTOP(CGI_CHILD);
430 #ifdef DEBUG_CGI
431     fprintf(dbg, "Attempting to exec %s as CGI child (argv0 = %s)\n",
432             r->filename, argv[0]);
433 #endif
434
435     if (e_info->prog_type == RUN_AS_CGI) {
436         ap_add_cgi_vars(r);
437     }
438     else /* SSIs want a controlled environment and a special path. */
439     {
440         add_ssi_vars(r, e_info->next);
441     }
442     env = (const char * const *)ap_create_environment(p, r->subprocess_env);
443
444 #ifdef DEBUG_CGI
445     fprintf(dbg, "Environment: \n");
446     for (i = 0; env[i]; ++i)
447         fprintf(dbg, "'%s'\n", env[i]);
448 #endif
449
450     /* Transmute ourselves into the script.
451      * NB only ISINDEX scripts get decoded arguments.
452      */
453     if (((rc = apr_procattr_create(&procattr, p)) != APR_SUCCESS) ||
454         ((rc = apr_procattr_io_set(procattr,
455                                    e_info->in_pipe,
456                                    e_info->out_pipe,
457                                    e_info->err_pipe)) != APR_SUCCESS) ||
458         ((rc = apr_procattr_dir_set(procattr, 
459                         ap_make_dirstr_parent(r->pool,
460                                               r->filename))) != APR_SUCCESS) ||
461 #ifdef RLIMIT_CPU
462         ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_CPU,
463                                       conf->limit_cpu)) != APR_SUCCESS) ||
464 #endif
465 #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
466         ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_MEM,
467                                       conf->limit_mem)) != APR_SUCCESS) ||
468 #endif
469 #ifdef RLIMIT_NPROC
470         ((rc = apr_procattr_limit_set(procattr, APR_LIMIT_NPROC,
471                                       conf->limit_nproc)) != APR_SUCCESS) ||
472 #endif
473         ((rc = apr_procattr_cmdtype_set(procattr,
474                                         e_info->cmd_type)) != APR_SUCCESS)) {
475         /* Something bad happened, tell the world. */
476         ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
477                       "couldn't set child process attributes: %s", r->filename);
478     }
479     else {
480         procnew = apr_pcalloc(p, sizeof(*procnew));
481         if (e_info->prog_type == RUN_AS_SSI) {
482             SPLIT_AND_PASS_PRETAG_BUCKETS(*(e_info->bb), e_info->ctx,
483                                           e_info->next, rc);
484             if (rc != APR_SUCCESS) {
485                 return rc;
486             }
487         }
488
489         rc = ap_os_create_privileged_process(r, procnew, command, argv, env,
490                                              procattr, p);
491     
492         if (rc != APR_SUCCESS) {
493             /* Bad things happened. Everyone should have cleaned up. */
494             ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
495                           "couldn't create child process: %d: %s", rc,
496                           r->filename);
497         }
498         else {
499             apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
500
501             *script_in = procnew->out;
502             if (!*script_in)
503                 return APR_EBADF;
504             apr_file_pipe_timeout_set(*script_in, (int)(r->server->timeout *
505                                                         APR_USEC_PER_SEC));
506
507             if (e_info->prog_type == RUN_AS_CGI) {
508                 *script_out = procnew->in;
509                 if (!*script_out)
510                     return APR_EBADF;
511                 apr_file_pipe_timeout_set(*script_out,
512                                           (int)(r->server->timeout *
513                                                 APR_USEC_PER_SEC));
514
515                 *script_err = procnew->err;
516                 if (!*script_err)
517                     return APR_EBADF;
518                 apr_file_pipe_timeout_set(*script_err,
519                                           (int)(r->server->timeout *
520                                                 APR_USEC_PER_SEC));
521             }
522         }
523     }
524 #ifdef DEBUG_CGI
525     fclose(dbg);
526 #endif
527     return (rc);
528 }
529
530
531 static apr_status_t default_build_command(const char **cmd, const char ***argv,
532                                           request_rec *r, apr_pool_t *p,
533                                           int process_cgi, apr_cmdtype_e * type)
534 {
535     int numwords, x, idx;
536     char *w;
537     const char *args = NULL;
538
539     if (process_cgi) {
540         /* Allow suexec's "/" check to succeed */
541         const char *argv0 = strrchr(r->filename, '/');
542         if (argv0 != NULL)
543             argv0++;
544         else
545             argv0 = r->filename;
546         *cmd = argv0;
547         args = r->args;
548         /* Do not process r->args if they contain an '=' assignment 
549          */
550         if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) {
551             args = r->args;
552         }
553     }
554
555     if (!args) {
556         numwords = 1;
557     }
558     else {
559         /* count the number of keywords */
560         for (x = 0, numwords = 2; args[x]; x++) {
561             if (args[x] == '+') {
562                 ++numwords;
563             }
564         }
565     }
566     /* Everything is - 1 to account for the first parameter 
567      * which is the program name.
568      */ 
569     if (numwords > APACHE_ARG_MAX - 1) {
570         numwords = APACHE_ARG_MAX - 1;    /* Truncate args to prevent overrun */
571     }
572     *argv = apr_palloc(p, (numwords + 2) * sizeof(char *));
573     (*argv)[0] = *cmd;
574     for (x = 1, idx = 1; x < numwords; x++) {
575         w = ap_getword_nulls(p, &args, '+');
576         ap_unescape_url(w);
577         (*argv)[idx++] = ap_escape_shell_cmd(p, w);
578     }
579     (*argv)[idx] = NULL;
580
581     return APR_SUCCESS;
582 }
583
584 static void discard_script_output(apr_bucket_brigade *bb)
585 {
586     apr_bucket *e;
587     const char *buf;
588     apr_size_t len;
589     apr_status_t rv;
590     APR_BRIGADE_FOREACH(e, bb) {
591         if (APR_BUCKET_IS_EOS(e)) {
592             break;
593         }
594         rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
595         if (!APR_STATUS_IS_SUCCESS(rv)) {
596             break;
597         }
598     }
599 }
600
601 static int cgi_handler(request_rec *r)
602 {
603     int retval, nph, dbpos = 0;
604     const char *argv0;
605     const char *command;
606     const char **argv;
607     char *dbuf = NULL;
608     apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL;
609     apr_bucket_brigade *bb;
610     apr_bucket *b;
611     char argsbuffer[HUGE_STRING_LEN];
612     int is_included;
613     apr_pool_t *p;
614     cgi_server_conf *conf;
615     apr_status_t rv;
616     exec_info e_info;
617
618     if(strcmp(r->handler,CGI_MAGIC_TYPE) && strcmp(r->handler,"cgi-script"))
619         return DECLINED;
620
621     is_included = !strcmp(r->protocol, "INCLUDED");
622
623     p = r->main ? r->main->pool : r->pool;
624
625     if (r->method_number == M_OPTIONS) {
626         /* 99 out of 100 CGI scripts, this is all they support */
627         r->allowed |= (AP_METHOD_BIT << M_GET);
628         r->allowed |= (AP_METHOD_BIT << M_POST);
629         return DECLINED;
630     }
631
632     argv0 = apr_filename_of_pathname(r->filename);
633     nph = !(strncmp(argv0, "nph-", 4));
634     conf = ap_get_module_config(r->server->module_config, &cgi_module);
635
636     if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r))
637         return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
638                                "Options ExecCGI is off in this directory");
639     if (nph && is_included)
640         return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
641                                "attempt to include NPH CGI script");
642
643     if (r->finfo.filetype == 0)
644         return log_scripterror(r, conf, HTTP_NOT_FOUND, 0,
645                                "script not found or unable to stat");
646     if (r->finfo.filetype == APR_DIR)
647         return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
648                                "attempt to invoke directory as script");
649
650     if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) &&
651         r->path_info && *r->path_info)
652     {
653         /* default to accept */
654         return log_scripterror(r, conf, HTTP_NOT_FOUND, 0,
655                                "AcceptPathInfo off disallows user's path");
656     }
657 /*
658     if (!ap_suexec_enabled) {
659         if (!ap_can_exec(&r->finfo))
660             return log_scripterror(r, conf, HTTP_FORBIDDEN, 0,
661                                    "file permissions deny server execution");
662     }
663
664 */
665     if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
666         return retval;
667
668     ap_add_common_vars(r);
669
670     e_info.cmd_type  = APR_PROGRAM;
671     e_info.in_pipe   = APR_CHILD_BLOCK;
672     e_info.out_pipe  = APR_CHILD_BLOCK;
673     e_info.err_pipe  = APR_CHILD_BLOCK;
674     e_info.prog_type = RUN_AS_CGI;
675     e_info.bb        = NULL;
676     e_info.ctx       = NULL;
677     e_info.next      = NULL;
678
679     /* build the command line */
680     if ((rv = cgi_build_command(&command, &argv, r, p, 1, &e_info.cmd_type)) 
681             != APR_SUCCESS) {
682         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
683                       "don't know how to spawn child process: %s", 
684                       r->filename);
685         return HTTP_INTERNAL_SERVER_ERROR;
686     }
687
688     /* run the script in its own process */
689     if ((rv = run_cgi_child(&script_out, &script_in, &script_err,
690                             command, argv, r, p, &e_info)) != APR_SUCCESS) {
691         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
692                       "couldn't spawn child process: %s", r->filename);
693         return HTTP_INTERNAL_SERVER_ERROR;
694     }
695
696     /* Transfer any put/post args, CERN style...
697      * Note that we already ignore SIGPIPE in the core server.
698      */
699     if (ap_should_client_block(r)) {
700         int len_read, dbsize;
701         apr_size_t bytes_written;
702         apr_status_t rv;
703
704         if (conf->logname) {
705             dbuf = apr_pcalloc(r->pool, conf->bufbytes + 1);
706             dbpos = 0;
707         }
708
709         while ((len_read =
710                 ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) {
711             if (conf->logname) {
712                 if ((dbpos + len_read) > conf->bufbytes) {
713                     dbsize = conf->bufbytes - dbpos;
714                 }
715                 else {
716                     dbsize = len_read;
717                 }
718                 memcpy(dbuf + dbpos, argsbuffer, dbsize);
719                 dbpos += dbsize;
720             }
721             /* Keep writing data to the child until done or too much time
722              * elapses with no progress or an error occurs.
723              */
724             rv = apr_file_write_full(script_out, argsbuffer, len_read,
725                                      &bytes_written);
726             if (rv != APR_SUCCESS) {
727                 /* silly script stopped reading, soak up remaining message */
728                 while (ap_get_client_block(r, argsbuffer,
729                                            HUGE_STRING_LEN) > 0) {
730                     /* dump it */
731                 }
732                 break;
733             }
734         }
735         apr_file_flush(script_out);
736     }
737
738     apr_file_close(script_out);
739
740     /* Handle script return... */
741     if (script_in && !nph) {
742         conn_rec *c = r->connection;
743         const char *location;
744         char sbuf[MAX_STRING_LEN];
745         int ret;
746
747         bb = apr_brigade_create(r->pool, c->bucket_alloc);
748         b = apr_bucket_pipe_create(script_in, c->bucket_alloc);
749         APR_BRIGADE_INSERT_TAIL(bb, b);
750         b = apr_bucket_eos_create(c->bucket_alloc);
751         APR_BRIGADE_INSERT_TAIL(bb, b);
752
753         if ((ret = ap_scan_script_header_err_brigade(r, bb, sbuf))) {
754             return log_script(r, conf, ret, dbuf, sbuf, bb, script_err);
755         }
756
757         location = apr_table_get(r->headers_out, "Location");
758
759         if (location && location[0] == '/' && r->status == 200) {
760             discard_script_output(bb);
761             apr_brigade_destroy(bb);
762             log_script_err(r, script_err);
763             /* This redirect needs to be a GET no matter what the original
764              * method was.
765              */
766             r->method = apr_pstrdup(r->pool, "GET");
767             r->method_number = M_GET;
768
769             /* We already read the message body (if any), so don't allow
770              * the redirected request to think it has one.  We can ignore 
771              * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
772              */
773             apr_table_unset(r->headers_in, "Content-Length");
774
775             ap_internal_redirect_handler(location, r);
776             return OK;
777         }
778         else if (location && r->status == 200) {
779             /* XX Note that if a script wants to produce its own Redirect
780              * body, it now has to explicitly *say* "Status: 302"
781              */
782             discard_script_output(bb);
783             apr_brigade_destroy(bb);
784             return HTTP_MOVED_TEMPORARILY;
785         }
786
787         if (!r->header_only) {
788             ap_pass_brigade(r->output_filters, bb);
789         }
790
791         log_script_err(r, script_err);
792         apr_file_close(script_err);
793     }
794
795     if (script_in && nph) {
796         conn_rec *c = r->connection;
797         struct ap_filter_t *cur;
798         
799         /* get rid of all filters up through protocol...  since we
800          * haven't parsed off the headers, there is no way they can
801          * work
802          */
803
804         cur = r->proto_output_filters;
805         while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) {
806             cur = cur->next;
807         }
808         r->output_filters = r->proto_output_filters = cur;
809
810         bb = apr_brigade_create(r->pool, c->bucket_alloc);
811         b = apr_bucket_pipe_create(script_in, c->bucket_alloc);
812         APR_BRIGADE_INSERT_TAIL(bb, b);
813         b = apr_bucket_eos_create(c->bucket_alloc);
814         APR_BRIGADE_INSERT_TAIL(bb, b);
815         ap_pass_brigade(r->output_filters, bb);
816     }
817
818     return OK;                      /* NOT r->status, even if it has changed. */
819 }
820
821 /*============================================================================
822  *============================================================================
823  * This is the beginning of the cgi filter code moved from mod_include. This
824  *   is the code required to handle the "exec" SSI directive.
825  *============================================================================
826  *============================================================================*/
827 static int include_cgi(char *s, request_rec *r, ap_filter_t *next,
828                        apr_bucket *head_ptr, apr_bucket **inserted_head)
829 {
830     request_rec *rr = ap_sub_req_lookup_uri(s, r, next);
831     int rr_status;
832     apr_bucket  *tmp_buck, *tmp2_buck;
833
834     if (rr->status != HTTP_OK) {
835         ap_destroy_sub_req(rr);
836         return -1;
837     }
838
839     /* No hardwired path info or query allowed */
840
841     if ((rr->path_info && rr->path_info[0]) || rr->args) {
842         ap_destroy_sub_req(rr);
843         return -1;
844     }
845     if (rr->finfo.filetype != APR_REG) {
846         ap_destroy_sub_req(rr);
847         return -1;
848     }
849
850     /* Script gets parameters of the *document*, for back compatibility */
851
852     rr->path_info = r->path_info;       /* hard to get right; see mod_cgi.c */
853     rr->args = r->args;
854
855     /* Force sub_req to be treated as a CGI request, even if ordinary
856      * typing rules would have called it something else.
857      */
858
859     ap_set_content_type(rr, CGI_MAGIC_TYPE);
860
861     /* Run it. */
862
863     rr_status = ap_run_sub_req(rr);
864     if (ap_is_HTTP_REDIRECT(rr_status)) {
865         apr_size_t len_loc;
866         const char *location = apr_table_get(rr->headers_out, "Location");
867         conn_rec *c = r->connection;
868
869         location = ap_escape_html(rr->pool, location);
870         len_loc = strlen(location);
871
872         /* XXX: if most of this stuff is going to get copied anyway,
873          * it'd be more efficient to pstrcat it into a single pool buffer
874          * and a single pool bucket */
875
876         tmp_buck = apr_bucket_immortal_create("<A HREF=\"",
877                                               sizeof("<A HREF=\"") - 1,
878                                               c->bucket_alloc);
879         APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
880         tmp2_buck = apr_bucket_heap_create(location, len_loc, NULL,
881                                            c->bucket_alloc);
882         APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
883         tmp2_buck = apr_bucket_immortal_create("\">", sizeof("\">") - 1,
884                                                c->bucket_alloc);
885         APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
886         tmp2_buck = apr_bucket_heap_create(location, len_loc, NULL,
887                                            c->bucket_alloc);
888         APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
889         tmp2_buck = apr_bucket_immortal_create("</A>", sizeof("</A>") - 1,
890                                                c->bucket_alloc);
891         APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
892
893         if (*inserted_head == NULL) {
894             *inserted_head = tmp_buck;
895         }
896     }
897
898     ap_destroy_sub_req(rr);
899
900     return 0;
901 }
902
903
904 static int include_cmd(include_ctx_t *ctx, apr_bucket_brigade **bb,
905                        const char *command, request_rec *r, ap_filter_t *f)
906 {
907     exec_info      e_info;
908     const char   **argv;
909     apr_file_t    *script_out = NULL, *script_in = NULL, *script_err = NULL;
910     apr_bucket_brigade *bcgi;
911     apr_bucket *b;
912     apr_status_t rv;
913
914     e_info.cmd_type  = APR_SHELLCMD;
915     e_info.in_pipe   = APR_NO_PIPE;
916     e_info.out_pipe  = APR_FULL_BLOCK;
917     e_info.err_pipe  = APR_NO_PIPE;
918     e_info.prog_type = RUN_AS_SSI;
919     e_info.bb        = bb;
920     e_info.ctx       = ctx;
921     e_info.next      = f->next;
922
923     if ((rv = cgi_build_command(&command, &argv, r, r->pool, 0,
924                                 &e_info.cmd_type)) != APR_SUCCESS) {
925         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
926                       "don't know how to spawn cmd child process: %s", 
927                       r->filename);
928         return HTTP_INTERNAL_SERVER_ERROR;
929     }
930
931     /* run the script in its own process */
932     if ((rv = run_cgi_child(&script_out, &script_in, &script_err,
933                             command, argv, r, r->pool,
934                             &e_info)) != APR_SUCCESS) {
935         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
936                       "couldn't spawn child process: %s", r->filename);
937         return HTTP_INTERNAL_SERVER_ERROR;
938     }
939
940     bcgi = apr_brigade_create(r->pool, f->c->bucket_alloc);
941     b = apr_bucket_pipe_create(script_in, f->c->bucket_alloc);
942     APR_BRIGADE_INSERT_TAIL(bcgi, b);
943     ap_pass_brigade(f->next, bcgi);
944
945     /* We can't close the pipe here, because we may return before the
946      * full CGI has been sent to the network.  That's okay though,
947      * because we can rely on the pool to close the pipe for us.
948      */
949
950     return 0;
951 }
952
953 static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb,
954                        request_rec *r, ap_filter_t *f, apr_bucket *head_ptr,
955                        apr_bucket **inserted_head)
956 {
957     char *tag     = NULL;
958     char *tag_val = NULL;
959     char *file = r->filename;
960     apr_bucket  *tmp_buck;
961     char parsed_string[MAX_STRING_LEN];
962
963     *inserted_head = NULL;
964     if (ctx->flags & FLAG_PRINTING) {
965         if (ctx->flags & FLAG_NO_EXEC) {
966             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
967                           "exec used but not allowed in %s", r->filename);
968             CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
969         }
970         else {
971             while (1) {
972                 cgi_pfn_gtv(ctx, &tag, &tag_val, 1);
973                 if (tag_val == NULL) {
974                     if (tag == NULL) {
975                         return 0;
976                     }
977                     else {
978                         return 1;
979                     }
980                 }
981                 if (!strcmp(tag, "cmd")) {
982                     cgi_pfn_ps(r, ctx, tag_val, parsed_string,
983                                sizeof(parsed_string), 1);
984                     if (include_cmd(ctx, bb, parsed_string, r, f) == -1) {
985                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
986                                     "execution failure for parameter \"%s\" "
987                                     "to tag exec in file %s", tag, r->filename);
988                         CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr,
989                                             *inserted_head);
990                     }
991                 }
992                 else if (!strcmp(tag, "cgi")) {
993                     apr_status_t retval = APR_SUCCESS;
994
995                     cgi_pfn_ps(r, ctx, tag_val, parsed_string,
996                                sizeof(parsed_string), 0);
997
998                     SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next, retval);
999                     if (retval != APR_SUCCESS) {
1000                         return retval;
1001                     }
1002
1003                     if (include_cgi(parsed_string, r, f->next, head_ptr,
1004                                     inserted_head) == -1) {
1005                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1006                                       "invalid CGI ref \"%s\" in %s",
1007                                       tag_val, file);
1008                         CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr,
1009                                             *inserted_head);
1010                     }
1011                 }
1012                 else {
1013                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1014                                   "unknown parameter \"%s\" to tag exec in %s",
1015                                   tag, file);
1016                     CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr,
1017                                         *inserted_head);
1018                 }
1019             }
1020         }
1021     }
1022     return 0;
1023 }
1024
1025
1026 /*============================================================================
1027  *============================================================================
1028  * This is the end of the cgi filter code moved from mod_include.
1029  *============================================================================
1030  *============================================================================*/
1031
1032
1033 static int cgi_post_config(apr_pool_t *p, apr_pool_t *plog,
1034                                 apr_pool_t *ptemp, server_rec *s)
1035 {
1036     cgi_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
1037     cgi_pfn_gtv          = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value);
1038     cgi_pfn_ps           = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string);
1039
1040     if ((cgi_pfn_reg_with_ssi) && (cgi_pfn_gtv) && (cgi_pfn_ps)) {
1041         /* Required by mod_include filter. This is how mod_cgi registers
1042          *   with mod_include to provide processing of the exec directive.
1043          */
1044         cgi_pfn_reg_with_ssi("exec", handle_exec);
1045     }
1046
1047     /* This is the means by which unusual (non-unix) os's may find alternate
1048      * means to run a given command (e.g. shebang/registry parsing on Win32)
1049      */
1050     cgi_build_command    = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command);
1051     if (!cgi_build_command) {
1052         cgi_build_command = default_build_command;
1053     }
1054     return OK;
1055 }
1056
1057 static void register_hooks(apr_pool_t *p)
1058 {
1059     static const char * const aszPre[] = { "mod_include.c", NULL };
1060     ap_hook_handler(cgi_handler, NULL, NULL, APR_HOOK_MIDDLE);
1061     ap_hook_post_config(cgi_post_config, aszPre, NULL, APR_HOOK_REALLY_FIRST);
1062 }
1063
1064 module AP_MODULE_DECLARE_DATA cgi_module =
1065 {
1066     STANDARD20_MODULE_STUFF,
1067     NULL,                        /* dir config creater */
1068     NULL,                        /* dir merger --- default is to override */
1069     create_cgi_config,           /* server config */
1070     merge_cgi_config,            /* merge server config */
1071     cgi_cmds,                    /* command apr_table_t */
1072     register_hooks               /* register hooks */
1073 };