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