1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2003 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 "httpd.h"
73 #include "http_config.h"
74 #include "http_request.h"
75 #include "http_core.h"
76 #include "http_protocol.h"
77 #include "http_main.h"
78 #include "http_log.h"
79 #include "util_script.h"
80 #include "http_conf_globals.h"
81 
82 module MODULE_VAR_EXPORT cgi_module;
83 
84 /* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
85  * in ScriptAliased directories, which means we need to know if this
86  * request came through ScriptAlias or not... so the Alias module
87  * leaves a note for us.
88  */
89 
is_scriptaliased(request_rec * r)90 static int is_scriptaliased(request_rec *r)
91 {
92     const char *t = ap_table_get(r->notes, "alias-forced-type");
93     return t && (!strcasecmp(t, "cgi-script"));
94 }
95 
96 /* Configuration stuff */
97 
98 #define DEFAULT_LOGBYTES 10385760
99 #define DEFAULT_BUFBYTES 1024
100 
101 typedef struct {
102     char *logname;
103     long logbytes;
104     int bufbytes;
105 } cgi_server_conf;
106 
create_cgi_config(pool * p,server_rec * s)107 static void *create_cgi_config(pool *p, server_rec *s)
108 {
109     cgi_server_conf *c =
110     (cgi_server_conf *) ap_pcalloc(p, sizeof(cgi_server_conf));
111 
112     c->logname = NULL;
113     c->logbytes = DEFAULT_LOGBYTES;
114     c->bufbytes = DEFAULT_BUFBYTES;
115 
116     return c;
117 }
118 
merge_cgi_config(pool * p,void * basev,void * overridesv)119 static void *merge_cgi_config(pool *p, void *basev, void *overridesv)
120 {
121     cgi_server_conf *base = (cgi_server_conf *) basev, *overrides = (cgi_server_conf *) overridesv;
122 
123     return overrides->logname ? overrides : base;
124 }
125 
set_scriptlog(cmd_parms * cmd,void * dummy,char * arg)126 static const char *set_scriptlog(cmd_parms *cmd, void *dummy, char *arg)
127 {
128     server_rec *s = cmd->server;
129     cgi_server_conf *conf =
130     (cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module);
131 
132     conf->logname = arg;
133     return NULL;
134 }
135 
set_scriptlog_length(cmd_parms * cmd,void * dummy,char * arg)136 static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, char *arg)
137 {
138     server_rec *s = cmd->server;
139     cgi_server_conf *conf =
140     (cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module);
141 
142     conf->logbytes = atol(arg);
143     return NULL;
144 }
145 
set_scriptlog_buffer(cmd_parms * cmd,void * dummy,char * arg)146 static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy, char *arg)
147 {
148     server_rec *s = cmd->server;
149     cgi_server_conf *conf =
150     (cgi_server_conf *) ap_get_module_config(s->module_config, &cgi_module);
151 
152     conf->bufbytes = atoi(arg);
153     return NULL;
154 }
155 
156 static const command_rec cgi_cmds[] =
157 {
158     {"ScriptLog", set_scriptlog, NULL, RSRC_CONF, TAKE1,
159      "the name of a log for script debugging info"},
160     {"ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF, TAKE1,
161      "the maximum length (in bytes) of the script debug log"},
162     {"ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, TAKE1,
163      "the maximum size (in bytes) to record of a POST request"},
164     {NULL}
165 };
166 
log_scripterror(request_rec * r,cgi_server_conf * conf,int ret,int show_errno,char * error)167 static int log_scripterror(request_rec *r, cgi_server_conf * conf, int ret,
168 			   int show_errno, char *error)
169 {
170     FILE *f;
171     struct stat finfo;
172 
173     ap_log_rerror(APLOG_MARK, show_errno|APLOG_ERR, r,
174 		"%s: %s", error, r->filename);
175 
176     if (!conf->logname ||
177 	((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0)
178 	 &&   (finfo.st_size > conf->logbytes)) ||
179          ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname),
180 		      "a")) == NULL)) {
181 	return ret;
182     }
183 
184     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
185     fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,
186 	    r->args ? "?" : "", r->args ? r->args : "", r->protocol);
187     /* "%% 500 /usr/local/apache/cgi-bin */
188     fprintf(f, "%%%% %d %s\n", ret, r->filename);
189 
190     fprintf(f, "%%error\n%s\n", error);
191 
192     ap_pfclose(r->pool, f);
193     return ret;
194 }
195 
log_script(request_rec * r,cgi_server_conf * conf,int ret,char * dbuf,const char * sbuf,BUFF * script_in,BUFF * script_err)196 static int log_script(request_rec *r, cgi_server_conf * conf, int ret,
197 		  char *dbuf, const char *sbuf, BUFF *script_in, BUFF *script_err)
198 {
199     array_header *hdrs_arr = ap_table_elts(r->headers_in);
200     table_entry *hdrs = (table_entry *) hdrs_arr->elts;
201     char argsbuffer[HUGE_STRING_LEN];
202     FILE *f;
203     int i;
204     struct stat finfo;
205 
206     if (!conf->logname ||
207 	((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0)
208 	 &&   (finfo.st_size > conf->logbytes)) ||
209          ((f = ap_pfopen(r->pool, ap_server_root_relative(r->pool, conf->logname),
210 		      "a")) == NULL)) {
211 	/* Soak up script output */
212 	while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
213 	    continue;
214 	while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
215 	    continue;
216 	return ret;
217     }
218 
219     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgi-bin/printenv HTTP/1.0" */
220     fprintf(f, "%%%% [%s] %s %s%s%s %s\n", ap_get_time(), r->method, r->uri,
221 	    r->args ? "?" : "", r->args ? r->args : "", r->protocol);
222     /* "%% 500 /usr/local/apache/cgi-bin" */
223     fprintf(f, "%%%% %d %s\n", ret, r->filename);
224 
225     fputs("%request\n", f);
226     for (i = 0; i < hdrs_arr->nelts; ++i) {
227 	if (!hdrs[i].key)
228 	    continue;
229 	fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
230     }
231     if ((r->method_number == M_POST || r->method_number == M_PUT)
232 	&& dbuf && *dbuf) {
233 	fprintf(f, "\n%s\n", dbuf);
234     }
235 
236     fputs("%response\n", f);
237     hdrs_arr = ap_table_elts(r->err_headers_out);
238     hdrs = (table_entry *) hdrs_arr->elts;
239 
240     for (i = 0; i < hdrs_arr->nelts; ++i) {
241 	if (!hdrs[i].key)
242 	    continue;
243 	fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val);
244     }
245 
246     if (sbuf && *sbuf)
247 	fprintf(f, "%s\n", sbuf);
248 
249     if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) {
250 	fputs("%stdout\n", f);
251 	fputs(argsbuffer, f);
252 	while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0)
253 	    fputs(argsbuffer, f);
254 	fputs("\n", f);
255     }
256 
257     if (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
258 	fputs("%stderr\n", f);
259 	fputs(argsbuffer, f);
260 	while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0)
261 	    fputs(argsbuffer, f);
262 	fputs("\n", f);
263     }
264 
265     ap_bclose(script_in);
266     ap_bclose(script_err);
267 
268     ap_pfclose(r->pool, f);
269     return ret;
270 }
271 
272 /****************************************************************
273  *
274  * Actual CGI handling...
275  */
276 
277 
278 struct cgi_child_stuff {
279     request_rec *r;
280     int nph;
281     int debug;
282     char *argv0;
283 };
284 
cgi_child(void * child_stuff,child_info * pinfo)285 static int cgi_child(void *child_stuff, child_info *pinfo)
286 {
287     struct cgi_child_stuff *cld = (struct cgi_child_stuff *) child_stuff;
288     request_rec *r = cld->r;
289     char *argv0 = cld->argv0;
290     int child_pid;
291 
292 #ifdef DEBUG_CGI
293     FILE *dbg = fopen("/dev/tty", "w");
294     int i;
295 #endif
296 
297     char **env;
298 
299     RAISE_SIGSTOP(CGI_CHILD);
300 #ifdef DEBUG_CGI
301     fprintf(dbg, "Attempting to exec %s as %sCGI child (argv0 = %s)\n",
302 	    r->filename, cld->nph ? "NPH " : "", argv0);
303 #endif
304 
305     ap_add_cgi_vars(r);
306     env = ap_create_environment(r->pool, r->subprocess_env);
307 
308 #ifdef DEBUG_CGI
309     fprintf(dbg, "Environment: \n");
310     for (i = 0; env[i]; ++i)
311 	fprintf(dbg, "'%s'\n", env[i]);
312 #endif
313 
314     ap_chdir_file(r->filename);
315     if (!cld->debug)
316 	ap_error_log2stderr(r->server);
317 
318     /* Transumute outselves into the script.
319      * NB only ISINDEX scripts get decoded arguments.
320      */
321 
322     ap_cleanup_for_exec();
323 
324     child_pid = ap_call_exec(r, pinfo, argv0, env, 0);
325 
326     /* Uh oh.  Still here.  Where's the kaboom?  There was supposed to be an
327      * EARTH-shattering kaboom!
328      *
329      * Oh, well.  Muddle through as best we can...
330      *
331      * Note that only stderr is available at this point, so don't pass in
332      * a server to aplog_error.
333      */
334 
335     ap_log_error(APLOG_MARK, APLOG_ERR, NULL, "exec of %s failed", r->filename);
336     exit(0);
337     /* NOT REACHED */
338     return (0);
339 }
340 
cgi_handler(request_rec * r)341 static int cgi_handler(request_rec *r)
342 {
343     int retval, nph, dbpos = 0;
344     char *argv0, *dbuf = NULL;
345     BUFF *script_out, *script_in, *script_err;
346     char argsbuffer[HUGE_STRING_LEN];
347     int is_included = !strcmp(r->protocol, "INCLUDED");
348     void *sconf = r->server->module_config;
349     cgi_server_conf *conf =
350     (cgi_server_conf *) ap_get_module_config(sconf, &cgi_module);
351 
352     struct cgi_child_stuff cld;
353 
354     if (r->method_number == M_OPTIONS) {
355 	/* 99 out of 100 CGI scripts, this is all they support */
356 	r->allowed |= (1 << M_GET);
357 	r->allowed |= (1 << M_POST);
358 	return DECLINED;
359     }
360 
361     if ((argv0 = strrchr(r->filename, '/')) != NULL)
362 	argv0++;
363     else
364 	argv0 = r->filename;
365 
366     nph = !(strncmp(argv0, "nph-", 4));
367 
368     if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r))
369 	return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
370 			       "Options ExecCGI is off in this directory");
371     if (nph && is_included)
372 	return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
373 			       "attempt to include NPH CGI script");
374 
375     if (r->finfo.st_mode == 0)
376 	return log_scripterror(r, conf, NOT_FOUND, APLOG_NOERRNO,
377 			       "script not found or unable to stat");
378     if (S_ISDIR(r->finfo.st_mode))
379 	return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
380 			       "attempt to invoke directory as script");
381     if (!ap_suexec_enabled) {
382 	if (!ap_can_exec(&r->finfo))
383 	    return log_scripterror(r, conf, FORBIDDEN, APLOG_NOERRNO,
384 				   "file permissions deny server execution");
385     }
386 
387     if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
388 	return retval;
389 
390     ap_add_common_vars(r);
391     cld.argv0 = argv0;
392     cld.r = r;
393     cld.nph = nph;
394     cld.debug = conf->logname ? 1 : 0;
395 
396     /*
397      * we spawn out of r->main if it's there so that we can avoid
398      * waiting for free_proc_chain to cleanup in the middle of an
399      * SSI request -djg
400      */
401     if (!ap_bspawn_child(r->main ? r->main->pool : r->pool, cgi_child,
402 			 (void *) &cld, kill_after_timeout,
403 			 &script_out, &script_in, &script_err)) {
404 	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
405 		    "couldn't spawn child process: %s", r->filename);
406 	return HTTP_INTERNAL_SERVER_ERROR;
407     }
408 
409     /* Transfer any put/post args, CERN style...
410      * Note that we already ignore SIGPIPE in the core server.
411      */
412 
413     if (ap_should_client_block(r)) {
414 	int dbsize, len_read;
415 
416 	if (conf->logname) {
417 	    dbuf = ap_pcalloc(r->pool, conf->bufbytes + 1);
418 	    dbpos = 0;
419 	}
420 
421 	ap_hard_timeout("copy script args", r);
422 
423 	while ((len_read =
424 		ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) {
425 	    if (conf->logname) {
426 		if ((dbpos + len_read) > conf->bufbytes) {
427 		    dbsize = conf->bufbytes - dbpos;
428 		}
429 		else {
430 		    dbsize = len_read;
431 		}
432 		memcpy(dbuf + dbpos, argsbuffer, dbsize);
433 		dbpos += dbsize;
434 	    }
435 	    ap_reset_timeout(r);
436 	    if (ap_bwrite(script_out, argsbuffer, len_read) < len_read) {
437 		/* silly script stopped reading, soak up remaining message */
438 		while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) {
439 		    /* dump it */
440 		}
441 		break;
442 	    }
443 	}
444 
445 	ap_bflush(script_out);
446 
447 	ap_kill_timeout(r);
448     }
449 
450     ap_bclose(script_out);
451 
452     /* Handle script return... */
453     if (script_in && !nph) {
454 	const char *location;
455 	char sbuf[MAX_STRING_LEN];
456 	int ret;
457 
458 	if ((ret = ap_scan_script_header_err_buff(r, script_in, sbuf))) {
459 	    return log_script(r, conf, ret, dbuf, sbuf, script_in, script_err);
460 	}
461 
462 	location = ap_table_get(r->headers_out, "Location");
463 
464 	if (location && location[0] == '/' && r->status == 200) {
465 
466 	    /* Soak up all the script output */
467 	    ap_hard_timeout("read from script", r);
468 	    while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_in) > 0) {
469 		continue;
470 	    }
471 	    while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
472 	        continue;
473 	    }
474 	    ap_kill_timeout(r);
475 
476 
477 	    /* This redirect needs to be a GET no matter what the original
478 	     * method was.
479 	     */
480 	    r->method = ap_pstrdup(r->pool, "GET");
481 	    r->method_number = M_GET;
482 
483 	    /* We already read the message body (if any), so don't allow
484 	     * the redirected request to think it has one.  We can ignore
485 	     * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR.
486 	     */
487 	    ap_table_unset(r->headers_in, "Content-Length");
488 
489 	    ap_internal_redirect_handler(location, r);
490 	    return OK;
491 	}
492 	else if (location && r->status == 200) {
493 	    /* XX Note that if a script wants to produce its own Redirect
494 	     * body, it now has to explicitly *say* "Status: 302"
495 	     */
496 	    return REDIRECT;
497 	}
498 
499 	ap_send_http_header(r);
500 	if (!r->header_only) {
501 	    ap_send_fb(script_in, r);
502 	}
503 	ap_bclose(script_in);
504 
505 	ap_soft_timeout("soaking script stderr", r);
506 	while (ap_bgets(argsbuffer, HUGE_STRING_LEN, script_err) > 0) {
507 	    continue;
508 	}
509 	ap_kill_timeout(r);
510 	ap_bclose(script_err);
511     }
512 
513     if (script_in && nph) {
514 	ap_send_fb(script_in, r);
515     }
516 
517     return OK;			/* NOT r->status, even if it has changed. */
518 }
519 
520 static const handler_rec cgi_handlers[] =
521 {
522     {CGI_MAGIC_TYPE, cgi_handler},
523     {"cgi-script", cgi_handler},
524     {NULL}
525 };
526 
527 module MODULE_VAR_EXPORT cgi_module =
528 {
529     STANDARD_MODULE_STUFF,
530     NULL,			/* initializer */
531     NULL,			/* dir config creater */
532     NULL,			/* dir merger --- default is to override */
533     create_cgi_config,		/* server config */
534     merge_cgi_config,		/* merge server config */
535     cgi_cmds,			/* command table */
536     cgi_handlers,		/* handlers */
537     NULL,			/* filename translation */
538     NULL,			/* check_user_id */
539     NULL,			/* check auth */
540     NULL,			/* check access */
541     NULL,			/* type_checker */
542     NULL,			/* fixups */
543     NULL,			/* logger */
544     NULL,			/* header parser */
545     NULL,			/* child_init */
546     NULL,			/* child_exit */
547     NULL			/* post read-request */
548 };
549