Imported Upstream version 0.7.1
[hcoop/zz_old/debian/suphp.git] / src / apache2 / mod_suphp.c
1 /*
2 suPHP - (c)2002-2005 Sebastian Marsching <sebastian@marsching.com>
3
4 This file is part of suPHP.
5
6 suPHP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 suPHP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with suPHP; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "apr.h"
22 #include "apr_strings.h"
23 #include "apr_thread_proc.h"
24 #include "apr_buckets.h"
25 #include "apr_poll.h"
26
27 #define CORE_PRIVATE
28
29 #include "httpd.h"
30 #include "http_config.h"
31 #include "http_core.h"
32 #include "http_log.h"
33
34 #include "util_script.h"
35 #include "util_filter.h"
36
37 /* needed for get_suexec_identity hook */
38 #include "unixd.h"
39
40 module AP_MODULE_DECLARE_DATA suphp_module;
41
42
43 /*********************
44 Auxiliary functions
45 *********************/
46
47 static apr_status_t suphp_log_script_err(request_rec *r, apr_file_t *script_err)
48 {
49 char argsbuffer[HUGE_STRING_LEN];
50 char *newline;
51 apr_status_t rv;
52
53 while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN,
54 script_err)) == APR_SUCCESS) {
55 newline = strchr(argsbuffer, '\n');
56 if (newline) {
57 *newline = '\0';
58 }
59 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
60 "%s", argsbuffer);
61 }
62
63 return rv;
64 }
65
66 char *suphp_brigade_read(apr_pool_t *p, apr_bucket_brigade *bb, int bytes)
67 {
68 char *target_buf;
69 char *next_byte;
70 char *last_byte;
71 apr_bucket *b;
72
73 if (bytes == 0) {
74 return NULL;
75 }
76
77 target_buf = (char *) apr_palloc(p, bytes + 1);
78 next_byte = target_buf;
79 last_byte = target_buf + bytes;
80
81 for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
82 char *buf;
83 apr_size_t size;
84 apr_size_t i;
85 if (apr_bucket_read(b, &buf, &size, APR_BLOCK_READ) == APR_SUCCESS) {
86 for (i = 0; i < size; i++) {
87 *next_byte = *buf;
88 next_byte++;
89 buf++;
90 if (next_byte == last_byte) {
91 *next_byte = 0;
92 return target_buf;
93 }
94 }
95 }
96 }
97 next_byte = 0;
98 return target_buf;
99 }
100
101
102 /**************************
103 Configuration processing
104 **************************/
105
106 #define SUPHP_CONFIG_MODE_SERVER 1
107 #define SUPHP_CONFIG_MODE_DIRECTORY 2
108
109 #define SUPHP_ENGINE_OFF 0
110 #define SUPHP_ENGINE_ON 1
111 #define SUPHP_ENGINE_UNDEFINED 2
112
113 #ifndef SUPHP_PATH_TO_SUPHP
114 #define SUPHP_PATH_TO_SUPHP "/usr/sbin/suphp"
115 #endif
116
117 typedef struct {
118 int engine; // Status of suPHP_Engine
119 char *php_config;
120 int cmode; // Server of directory configuration?
121 #ifdef SUPHP_USE_USERGROUP
122 char *target_user;
123 char *target_group;
124 #endif
125 apr_table_t *handlers;
126 char *php_path;
127 } suphp_conf;
128
129
130 static void *suphp_create_dir_config(apr_pool_t *p, char *dir)
131 {
132 suphp_conf *cfg = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
133
134 cfg->php_config = NULL;
135 cfg->engine = SUPHP_ENGINE_UNDEFINED;
136 cfg->php_path = NULL;
137 cfg->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
138
139 #ifdef SUPHP_USE_USERGROUP
140 cfg->target_user = NULL;
141 cfg->target_group = NULL;
142 #endif
143
144 /* Create table with 0 initial elements */
145 /* This size may be increased for performance reasons */
146 cfg->handlers = apr_table_make(p, 0);
147
148 return (void *) cfg;
149 }
150
151
152 static void *suphp_merge_dir_config(apr_pool_t *p, void *base,
153 void *overrides)
154 {
155 suphp_conf *parent = (suphp_conf *) base;
156 suphp_conf *child = (suphp_conf *) overrides;
157 suphp_conf *merged = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
158
159 merged->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
160
161 if (child->php_config)
162 merged->php_config = apr_pstrdup(p, child->php_config);
163 else if (parent->php_config)
164 merged->php_config = apr_pstrdup(p, parent->php_config);
165 else
166 merged->php_config = NULL;
167
168 if (child->engine != SUPHP_ENGINE_UNDEFINED)
169 merged->engine = child->engine;
170 else
171 merged->engine = parent->engine;
172
173 #ifdef SUPHP_USE_USERGROUP
174 if (child->target_user)
175 merged->target_user = apr_pstrdup(p, child->target_user);
176 else if (parent->target_user)
177 merged->target_user = apr_pstrdup(p, parent->target_user);
178 else
179 merged->target_user = NULL;
180
181 if (child->target_group)
182 merged->target_group = apr_pstrdup(p, child->target_group);
183 else if (parent->target_group)
184 merged->target_group = apr_pstrdup(p, parent->target_group);
185 else
186 merged->target_group = NULL;
187 #endif
188
189 merged->handlers = apr_table_overlay(p, child->handlers, parent->handlers);
190
191 return (void *) merged;
192 }
193
194
195 static void *suphp_create_server_config(apr_pool_t *p, server_rec *s)
196 {
197 suphp_conf *cfg = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
198
199 cfg->engine = SUPHP_ENGINE_UNDEFINED;
200 cfg->php_path = NULL;
201 cfg->cmode = SUPHP_CONFIG_MODE_SERVER;
202
203 /* Create table with 0 initial elements */
204 /* This size may be increased for performance reasons */
205 cfg->handlers = apr_table_make(p, 0);
206
207 return (void *) cfg;
208 }
209
210
211 static void *suphp_merge_server_config(apr_pool_t *p, void *base,
212 void *overrides)
213 {
214 suphp_conf *parent = (suphp_conf *) base;
215 suphp_conf *child = (suphp_conf *) overrides;
216 suphp_conf *merged = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
217
218 if (child->engine != SUPHP_ENGINE_UNDEFINED)
219 merged->engine = child->engine;
220 else
221 merged->engine = parent->engine;
222
223 if (child->php_path != NULL)
224 merged->php_path = apr_pstrdup(p, child->php_path);
225 else
226 merged->php_path = apr_pstrdup(p, parent->php_path);
227
228 #ifdef SUPHP_USE_USERGROUP
229 if (child->target_user)
230 merged->target_user = apr_pstrdup(p, child->target_user);
231 else if (parent->target_user)
232 merged->target_user = apr_pstrdup(p, parent->target_user);
233 else
234 merged->target_user = NULL;
235
236 if (child->target_group)
237 merged->target_group = apr_pstrdup(p, child->target_group);
238 else if (parent->target_group)
239 merged->target_group = apr_pstrdup(p, parent->target_group);
240 else
241 merged->target_group = NULL;
242 #endif
243
244 merged->handlers = apr_table_overlay(p, child->handlers, parent->handlers);
245
246 return (void*) merged;
247 }
248
249
250 /******************
251 Command handlers
252 ******************/
253
254 static const char *suphp_handle_cmd_engine(cmd_parms *cmd, void *mconfig,
255 int flag)
256 {
257 server_rec *s = cmd->server;
258 suphp_conf *cfg;
259
260 if (mconfig)
261 cfg = (suphp_conf *) mconfig;
262 else
263 cfg = (suphp_conf *) ap_get_module_config(s->module_config, &suphp_module);
264
265 if (flag)
266 cfg->engine = SUPHP_ENGINE_ON;
267 else
268 cfg->engine = SUPHP_ENGINE_OFF;
269
270 return NULL;
271 }
272
273
274 static const char *suphp_handle_cmd_config(cmd_parms *cmd, void *mconfig,
275 const char *arg)
276 {
277 server_rec *s = cmd->server;
278 suphp_conf *cfg;
279
280 if (mconfig)
281 cfg = (suphp_conf *) mconfig;
282 else
283 cfg = (suphp_conf *) ap_get_module_config(s->module_config, &suphp_module);
284
285 cfg->php_config = apr_pstrdup(cmd->pool, arg);
286
287 return NULL;
288 }
289
290
291 #ifdef SUPHP_USE_USERGROUP
292 static const char *suphp_handle_cmd_user_group(cmd_parms *cmd, void *mconfig,
293 const char *arg1, const char *arg2)
294 {
295 suphp_conf *cfg = (suphp_conf *) mconfig;
296
297 cfg->target_user = apr_pstrdup(cmd->pool, arg1);
298 cfg->target_group = apr_pstrdup(cmd->pool, arg2);
299
300 return NULL;
301 }
302 #endif
303
304
305 static const char *suphp_handle_cmd_add_handler(cmd_parms *cmd, void *mconfig,
306 const char *arg)
307 {
308 suphp_conf *cfg;
309 if (mconfig)
310 cfg = (suphp_conf *) mconfig;
311 else
312 cfg = (suphp_conf *) ap_get_module_config(cmd->server->module_config, &suphp_module);
313
314 // Mark active handler with '1'
315 apr_table_set(cfg->handlers, arg, "1");
316
317 return NULL;
318 }
319
320
321 static const char *suphp_handle_cmd_remove_handler(cmd_parms *cmd,
322 void *mconfig,
323 const char *arg)
324 {
325 suphp_conf *cfg;
326 if (mconfig)
327 cfg = (suphp_conf *) mconfig;
328 else
329 cfg = (suphp_conf *) ap_get_module_config(cmd->server->module_config, &suphp_module);
330
331 // Mark deactivated handler with '0'
332 apr_table_set(cfg->handlers, arg, "0");
333
334 return NULL;
335 }
336
337
338 static const char *suphp_handle_cmd_phppath(cmd_parms *cmd, void* mconfig, const char *arg)
339 {
340 server_rec *s = cmd->server;
341 suphp_conf *cfg;
342
343 cfg = (suphp_conf *) ap_get_module_config(s->module_config, &suphp_module);
344
345 cfg->php_path = apr_pstrdup(cmd->pool, arg);
346
347 return NULL;
348 }
349
350
351 static const command_rec suphp_cmds[] =
352 {
353 AP_INIT_FLAG("suPHP_Engine", suphp_handle_cmd_engine, NULL, RSRC_CONF | ACCESS_CONF,
354 "Whether suPHP is on or off, default is off"),
355 AP_INIT_TAKE1("suPHP_ConfigPath", suphp_handle_cmd_config, NULL, OR_OPTIONS,
356 "Wheres the php.ini resides, default is the PHP default"),
357 #ifdef SUPHP_USE_USERGROUP
358 AP_INIT_TAKE2("suPHP_UserGroup", suphp_handle_cmd_user_group, NULL, RSRC_CONF | ACCESS_CONF,
359 "User and group scripts shall be run as"),
360 #endif
361 AP_INIT_ITERATE("suPHP_AddHandler", suphp_handle_cmd_add_handler, NULL, RSRC_CONF | ACCESS_CONF, "Tells mod_suphp to handle these MIME-types"),
362 AP_INIT_ITERATE("suPHP_RemoveHandler", suphp_handle_cmd_remove_handler, NULL, RSRC_CONF | ACCESS_CONF, "Tells mod_suphp not to handle these MIME-types"),
363 AP_INIT_TAKE1("suPHP_PHPPath", suphp_handle_cmd_phppath, NULL, RSRC_CONF, "Path to the PHP binary used to render source view"),
364 {NULL}
365 };
366
367 /*****************************************
368 Code for reading script's stdout/stderr
369 based on mod_cgi's code
370 *****************************************/
371
372 #if APR_FILES_AS_SOCKETS
373
374 static const apr_bucket_type_t bucket_type_suphp;
375
376 struct suphp_bucket_data {
377 apr_pollset_t *pollset;
378 request_rec *r;
379 };
380
381 static apr_bucket *suphp_bucket_create(request_rec *r, apr_file_t *out, apr_file_t *err, apr_bucket_alloc_t *list)
382 {
383 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
384 apr_status_t rv;
385 apr_pollfd_t fd;
386 struct suphp_bucket_data *data = apr_palloc(r->pool, sizeof(*data));
387
388 APR_BUCKET_INIT(b);
389 b->free = apr_bucket_free;
390 b->list = list;
391 b->type = &bucket_type_suphp;
392 b->length = (apr_size_t) (-1);
393 b->start = (-1);
394
395 /* Create the pollset */
396 rv = apr_pollset_create(&data->pollset, 2, r->pool, 0);
397 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
398
399 fd.desc_type = APR_POLL_FILE;
400 fd.reqevents = APR_POLLIN;
401 fd.p = r->pool;
402 fd.desc.f = out; /* script's stdout */
403 fd.client_data = (void *) 1;
404 rv = apr_pollset_add(data->pollset, &fd);
405 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
406
407 fd.desc.f = err; /* script's stderr */
408 fd.client_data = (void *) 2;
409 rv = apr_pollset_add(data->pollset, &fd);
410 AP_DEBUG_ASSERT(rv == APR_SUCCESS);
411
412 data->r = r;
413 b->data = data;
414 return b;
415 }
416
417 static apr_bucket *suphp_bucket_dup(struct suphp_bucket_data *data, apr_bucket_alloc_t *list)
418 {
419 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
420 APR_BUCKET_INIT(b);
421 b->free = apr_bucket_free;
422 b->list = list;
423 b->type = &bucket_type_suphp;
424 b->length = (apr_size_t) (-1);
425 b->start = (-1);
426 b->data = data;
427 return b;
428 }
429
430 /* This utility method is needed, because APR's implementation for the
431 pipe bucket cannot handle or special bucket type */
432 static apr_status_t suphp_read_fd(apr_bucket *b, apr_file_t *fd, const char **str, apr_size_t *len)
433 {
434 char *buf;
435 apr_status_t rv;
436
437 *str = NULL;
438 *len = APR_BUCKET_BUFF_SIZE;
439 buf = apr_bucket_alloc(*len, b->list);
440
441 rv = apr_file_read(fd, buf, len);
442
443 if (*len > 0) {
444 /* Got data */
445 struct suphp_bucket_data *data = b->data;
446 apr_bucket_heap *h;
447
448 /* Change the current bucket to refer to what we read
449 and append the pipe bucket after it */
450 b = apr_bucket_heap_make(b, buf, *len, apr_bucket_free);
451 /* Here, b->data is the new heap bucket data */
452 h = b->data;
453 h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
454 *str = buf;
455 APR_BUCKET_INSERT_AFTER(b, suphp_bucket_dup(data, b->list));
456 } else {
457 /* Got no data */
458 apr_bucket_free(buf);
459 b = apr_bucket_immortal_make(b, "", 0);
460 /* Here, b->data is the reference to the empty string */
461 *str = b->data;
462 }
463 return rv;
464 }
465
466 /* Poll on stdout and stderr to make sure the process does not block
467 because of a full system (stderr) buffer */
468 static apr_status_t suphp_bucket_read(apr_bucket *b, const char **str, apr_size_t *len, apr_read_type_e block) {
469 struct suphp_bucket_data *data = b->data;
470 apr_interval_time_t timeout;
471 apr_status_t rv;
472 int gotdata = 0;
473
474 timeout = (block == APR_NONBLOCK_READ) ? 0 : data->r->server->timeout;
475
476 do {
477 const apr_pollfd_t *results;
478 apr_int32_t num;
479
480 rv = apr_pollset_poll(data->pollset, timeout, &num, &results);
481 if (APR_STATUS_IS_TIMEUP(rv)) {
482 return (timeout == 0) ? APR_EAGAIN : rv;
483 } else if (APR_STATUS_IS_EINTR(rv)) {
484 continue;
485 } else if (rv != APR_SUCCESS) {
486 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r, "Poll failed waiting for suPHP child process");
487 return rv;
488 }
489
490 while (num > 0) {
491 if (results[0].client_data == (void *) 1) {
492 /* handle stdout */
493 rv = suphp_read_fd(b, results[0].desc.f, str, len);
494 if (APR_STATUS_IS_EOF(rv)) {
495 rv = APR_SUCCESS;
496 }
497 gotdata = 1;
498 } else {
499 /* handle stderr */
500 apr_status_t rv2 = suphp_log_script_err(data->r, results[0].desc.f);
501 if (APR_STATUS_IS_EOF(rv2)) {
502 apr_pollset_remove(data->pollset, &results[0]);
503 }
504 }
505 num--;
506 results++;
507 }
508 } while (!gotdata);
509
510 return rv;
511 }
512
513 static const apr_bucket_type_t bucket_type_suphp = {
514 "SUPHP", 5, APR_BUCKET_DATA,
515 apr_bucket_destroy_noop,
516 suphp_bucket_read,
517 apr_bucket_setaside_notimpl,
518 apr_bucket_split_notimpl,
519 apr_bucket_copy_notimpl
520 };
521
522 #endif
523
524 static void suphp_discard_output(apr_bucket_brigade *bb) {
525 apr_bucket *b;
526 const char *buf;
527 apr_size_t len;
528 apr_status_t rv;
529 for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
530 if (APR_BUCKET_IS_EOS(b)) {
531 break;
532 }
533 rv = apr_bucket_read(b, &buf, &len, APR_BLOCK_READ);
534 if (rv != APR_SUCCESS) {
535 break;
536 }
537 }
538 }
539
540
541 /******************
542 Hooks / handlers
543 ******************/
544
545 static int suphp_script_handler(request_rec *r);
546 static int suphp_source_handler(request_rec *r);
547
548 static int suphp_handler(request_rec *r)
549 {
550 suphp_conf *sconf, *dconf;
551
552 sconf = ap_get_module_config(r->server->module_config, &suphp_module);
553 dconf = ap_get_module_config(r->per_dir_config, &suphp_module);
554
555 /* only handle request if mod_suphp is active for this handler */
556 /* check only first byte of value (second has to be \0) */
557 if (apr_table_get(dconf->handlers, r->handler) != NULL)
558 {
559 if (*(apr_table_get(dconf->handlers, r->handler)) != '0')
560 {
561 return suphp_script_handler(r);
562 }
563 }
564 else
565 {
566 if ((apr_table_get(sconf->handlers, r->handler) != NULL)
567 && (*(apr_table_get(sconf->handlers, r->handler)) != '0'))
568 {
569 return suphp_script_handler(r);
570 }
571 }
572
573 if (!strcmp(r->handler, "x-httpd-php-source")
574 || !strcmp(r->handler, "application/x-httpd-php-source"))
575 {
576 return suphp_source_handler(r);
577 }
578
579 return DECLINED;
580 }
581
582 static int suphp_source_handler(request_rec *r)
583 {
584 suphp_conf *conf;
585 apr_status_t rv;
586 apr_pool_t *p;
587 apr_file_t *file;
588 apr_proc_t *proc;
589 apr_procattr_t *procattr;
590 char **argv;
591 char **env;
592 apr_bucket_brigade *bb;
593 apr_bucket *b;
594 char *phpexec;
595
596 p = r->main ? r->main->pool : r->pool;
597
598 if (strcmp(r->method, "GET"))
599 {
600 return DECLINED;
601 }
602
603 conf = ap_get_module_config(r->server->module_config, &suphp_module);
604 phpexec = apr_pstrdup(p, conf->php_path);
605 if (phpexec == NULL)
606 {
607 return DECLINED;
608 }
609
610 // Try to open file for reading to see whether is is accessible
611 rv = apr_file_open(&file, apr_pstrdup(p, r->filename), APR_READ, APR_OS_DEFAULT, p);
612 if (rv == APR_SUCCESS)
613 {
614 apr_file_close(file);
615 file = NULL;
616 }
617 else if (rv == EACCES)
618 {
619 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "access to %s denied", r->filename);
620 return HTTP_FORBIDDEN;
621 }
622 else if (rv == ENOENT)
623 {
624 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "File does not exist: %s", r->filename);
625 return HTTP_NOT_FOUND;
626 }
627 else
628 {
629 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "Could not open file: %s", r->filename);
630 return HTTP_INTERNAL_SERVER_ERROR;
631 }
632
633 env = ap_create_environment(p, r->subprocess_env);
634
635 /* set attributes for new process */
636
637 if (((rv = apr_procattr_create(&procattr, p)) != APR_SUCCESS)
638 || ((rv = apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK)) != APR_SUCCESS)
639 || ((rv = apr_procattr_dir_set(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS)
640 || ((apr_procattr_cmdtype_set(procattr, APR_PROGRAM)) != APR_SUCCESS)
641 || ((apr_procattr_error_check_set(procattr, 1)) != APR_SUCCESS)
642 || ((apr_procattr_detach_set(procattr, 0)) != APR_SUCCESS))
643 {
644 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
645 "couldn't set child process attributes: %s", r->filename);
646 return HTTP_INTERNAL_SERVER_ERROR;
647 }
648
649
650 /* create new process */
651
652 argv = apr_palloc(p, 4 * sizeof(char *));
653 argv[0] = phpexec;
654 argv[1] = "-s";
655 argv[2] = apr_pstrdup(p, r->filename);
656 argv[3] = NULL;
657
658 env = ap_create_environment(p, r->subprocess_env);
659
660 proc = apr_pcalloc(p, sizeof(*proc));
661 rv = apr_proc_create(proc, phpexec, (const char *const *)argv, (const char *const *)env, procattr, p);
662 if (rv != APR_SUCCESS)
663 {
664 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
665 "couldn't create child process: %s for %s", phpexec, r->filename);
666 return HTTP_INTERNAL_SERVER_ERROR;
667 }
668 apr_pool_note_subprocess(p, proc, APR_KILL_AFTER_TIMEOUT);
669
670 if (!proc->out)
671 return APR_EBADF;
672 apr_file_pipe_timeout_set(proc->out, r->server->timeout);
673
674 if (!proc->in)
675 return APR_EBADF;
676 apr_file_pipe_timeout_set(proc->in, r->server->timeout);
677
678 if (!proc->err)
679 return APR_EBADF;
680 apr_file_pipe_timeout_set(proc->err, r->server->timeout);
681
682 /* discard input */
683
684 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
685
686 apr_file_flush(proc->in);
687 apr_file_close(proc->in);
688
689 rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
690 if (rv != APR_SUCCESS)
691 {
692 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
693 "couldn't get input from filters: %s", r->filename);
694 return HTTP_INTERNAL_SERVER_ERROR;
695 }
696 suphp_discard_output(bb);
697 apr_brigade_cleanup(bb);
698
699 /* get output from script */
700
701 #if APR_FILES_AS_SOCKETS
702 apr_file_pipe_timeout_set(proc->out, 0);
703 apr_file_pipe_timeout_set(proc->err, 0);
704 b = suphp_bucket_create(r, proc->out, proc->err, r->connection->bucket_alloc);
705 #else
706 b = apr_bucket_pipe_create(proc->out, r->connection->bucket_alloc);
707 #endif
708 APR_BRIGADE_INSERT_TAIL(bb, b);
709
710 b = apr_bucket_eos_create(r->connection->bucket_alloc);
711 APR_BRIGADE_INSERT_TAIL(bb, b);
712
713 /* send output to browser (through filters) */
714
715 r->content_type = "text/html";
716 rv = ap_pass_brigade(r->output_filters, bb);
717
718 /* write errors to logfile */
719
720 if (rv == APR_SUCCESS && !r->connection->aborted)
721 {
722 suphp_log_script_err(r, proc->err);
723 apr_file_close(proc->err);
724 }
725
726 return OK;
727 }
728
729 static int suphp_script_handler(request_rec *r)
730 {
731 apr_pool_t *p;
732 suphp_conf *sconf;
733 suphp_conf *dconf;
734 core_dir_config *core_conf;
735
736 apr_finfo_t finfo;
737
738 apr_procattr_t *procattr;
739
740 apr_proc_t *proc;
741
742 char **argv;
743 char **env;
744 apr_status_t rv;
745 int len = 0;
746 #if MAX_STRING_LEN < 1024
747 char strbuf[1024];
748 #else
749 char strbuf[MAX_STRING_LEN];
750 #endif
751 char *tmpbuf;
752 int nph = 0;
753 int eos_reached = 0;
754 char *auth_user = NULL;
755 char *auth_pass = NULL;
756
757 #ifdef SUPHP_USE_USERGROUP
758 char *ud_user = NULL;
759 char *ud_group = NULL;
760 #endif
761
762 apr_bucket_brigade *bb;
763 apr_bucket *b;
764
765 /* load configuration */
766
767 p = r->main ? r->main->pool : r->pool;
768 sconf = ap_get_module_config(r->server->module_config, &suphp_module);
769 dconf = ap_get_module_config(r->per_dir_config, &suphp_module);
770 core_conf = (core_dir_config *) ap_get_module_config(r->per_dir_config, &core_module);
771
772 /* check if suPHP is enabled for this request */
773
774 if (((sconf->engine != SUPHP_ENGINE_ON)
775 && (dconf->engine != SUPHP_ENGINE_ON))
776 || ((sconf->engine == SUPHP_ENGINE_ON)
777 && (dconf->engine == SUPHP_ENGINE_OFF)))
778 return DECLINED;
779
780 /* check if file is existing and acessible */
781
782 rv = apr_stat(&finfo, apr_pstrdup(p, r->filename), APR_FINFO_NORM, p);
783
784 if (rv == APR_SUCCESS)
785 ; /* do nothing */
786 else if (rv == EACCES)
787 {
788 return HTTP_FORBIDDEN;
789 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "access to %s denied", r->filename);
790 }
791 else if (rv == ENOENT)
792 {
793 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "File does not exist: %s", r->filename);
794 return HTTP_NOT_FOUND;
795 }
796 else
797 {
798 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "could not get fileinfo: %s", r->filename);
799 return HTTP_NOT_FOUND;
800 }
801
802 if (!(r->finfo.protection & APR_UREAD))
803 {
804 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Insufficient permissions: %s", r->filename);
805 return HTTP_FORBIDDEN;
806 }
807
808 #ifdef SUPHP_USE_USERGROUP
809 if ((sconf->target_user == NULL || sconf->target_group == NULL)
810 && (dconf->target_user == NULL || dconf->target_group == NULL))
811 {
812 /* Check for userdir request */
813 ap_unix_identity_t *userdir_id = NULL;
814 userdir_id = ap_run_get_suexec_identity(r);
815 if (userdir_id != NULL && userdir_id->userdir) {
816 ud_user = apr_psprintf(r->pool, "#%ld", (long) userdir_id->uid);
817 ud_group = apr_psprintf(r->pool, "#%ld", (long) userdir_id->gid);
818 } else {
819 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
820 "No user or group set - set suPHP_UserGroup");
821 return HTTP_INTERNAL_SERVER_ERROR;
822 }
823 }
824 #endif
825
826 /* prepare argv for new process */
827
828 argv = apr_palloc(p, 2 * sizeof(char *));
829 argv[0] = SUPHP_PATH_TO_SUPHP;
830 argv[1] = NULL;
831
832 /* prepare environment for new process */
833
834 ap_add_common_vars(r);
835 ap_add_cgi_vars(r);
836
837 apr_table_unset(r->subprocess_env, "SUPHP_PHP_CONFIG");
838 apr_table_unset(r->subprocess_env, "SUPHP_AUTH_USER");
839 apr_table_unset(r->subprocess_env, "SUPHP_AUTH_PW");
840
841 #ifdef SUPHP_USE_USERGROUP
842 apr_table_unset(r->subprocess_env, "SUPHP_USER");
843 apr_table_unset(r->subprocess_env, "SUPHP_GROUP");
844 #endif
845
846 if (dconf->php_config)
847 {
848 apr_table_setn(r->subprocess_env, "SUPHP_PHP_CONFIG", apr_pstrdup(p, dconf->php_config));
849 }
850
851 apr_table_setn(r->subprocess_env, "SUPHP_HANDLER", r->handler);
852
853 if (r->headers_in)
854 {
855 const char *auth = NULL;
856 auth = apr_table_get(r->headers_in, "Authorization");
857 if (auth && auth[0] != 0 && strncmp(auth, "Basic ", 6) == 0)
858 {
859 char *user;
860 char *pass;
861 user = ap_pbase64decode(p, auth + 6);
862 if (user)
863 {
864 pass = strchr(user, ':');
865 if (pass)
866 {
867 *pass++ = '\0';
868 auth_user = apr_pstrdup(r->pool, user);
869 auth_pass = apr_pstrdup(r->pool, pass);
870 }
871 }
872 }
873 }
874
875 if (auth_user && auth_pass)
876 {
877 apr_table_setn(r->subprocess_env, "SUPHP_AUTH_USER", auth_user);
878 apr_table_setn(r->subprocess_env, "SUPHP_AUTH_PW", auth_pass);
879 }
880
881 #ifdef SUPHP_USE_USERGROUP
882 if (dconf->target_user)
883 {
884 apr_table_setn(r->subprocess_env, "SUPHP_USER",
885 apr_pstrdup(r->pool, dconf->target_user));
886 }
887 else if (sconf->target_user)
888 {
889 apr_table_setn(r->subprocess_env, "SUPHP_USER",
890 apr_pstrdup(r->pool, sconf->target_user));
891 }
892 else
893 {
894 apr_table_setn(r->subprocess_env, "SUPHP_USER",
895 apr_pstrdup(r->pool, ud_user));
896 }
897
898 if (dconf->target_group)
899 {
900 apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
901 apr_pstrdup(r->pool, dconf->target_group));
902 }
903 else if (sconf->target_group)
904 {
905 apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
906 apr_pstrdup(r->pool, sconf->target_group));
907 }
908 else
909 {
910 apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
911 apr_pstrdup(r->pool, ud_group));
912 }
913 #endif
914
915 env = ap_create_environment(p, r->subprocess_env);
916
917 /* set attributes for new process */
918
919 if (((rv = apr_procattr_create(&procattr, p)) != APR_SUCCESS)
920 || ((rv = apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK)) != APR_SUCCESS)
921 || ((rv = apr_procattr_dir_set(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS)
922
923 /* set resource limits */
924
925 #ifdef RLIMIT_CPU
926 || ((rv = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, core_conf->limit_cpu)) != APR_SUCCESS)
927 #endif
928 #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
929 || ((rv = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, core_conf->limit_mem)) != APR_SUCCESS)
930 #endif
931 #ifdef RLIMIT_NPROC
932 || ((apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, core_conf->limit_nproc)) != APR_SUCCESS)
933 #endif
934
935 || ((apr_procattr_cmdtype_set(procattr, APR_PROGRAM)) != APR_SUCCESS)
936 || ((apr_procattr_error_check_set(procattr, 1)) != APR_SUCCESS)
937 || ((apr_procattr_detach_set(procattr, 0)) != APR_SUCCESS))
938 {
939 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
940 "couldn't set child process attributes: %s", r->filename);
941 return HTTP_INTERNAL_SERVER_ERROR;
942 }
943
944 /* create new process */
945
946
947 proc = apr_pcalloc(p, sizeof(*proc));
948 rv = apr_proc_create(proc, SUPHP_PATH_TO_SUPHP, (const char *const *)argv, (const char *const *)env, procattr, p);
949 if (rv != APR_SUCCESS)
950 {
951 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
952 "couldn't create child process: %s for %s", SUPHP_PATH_TO_SUPHP, r->filename);
953 return HTTP_INTERNAL_SERVER_ERROR;
954 }
955 apr_pool_note_subprocess(p, proc, APR_KILL_AFTER_TIMEOUT);
956
957 if (!proc->out)
958 return APR_EBADF;
959 apr_file_pipe_timeout_set(proc->out, r->server->timeout);
960
961 if (!proc->in)
962 return APR_EBADF;
963 apr_file_pipe_timeout_set(proc->in, r->server->timeout);
964
965 if (!proc->err)
966 return APR_EBADF;
967 apr_file_pipe_timeout_set(proc->err, r->server->timeout);
968
969 /* send request body to script */
970
971 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
972 do
973 {
974 apr_bucket *bucket;
975 rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
976
977 if (rv != APR_SUCCESS)
978 {
979 return rv;
980 }
981
982 for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket = APR_BUCKET_NEXT(bucket))
983 {
984 const char *data;
985 apr_size_t len;
986 int child_stopped_reading = 0;
987
988 if (APR_BUCKET_IS_EOS(bucket))
989 {
990 eos_reached = 1;
991 break;
992 }
993
994 if (APR_BUCKET_IS_FLUSH(bucket) || child_stopped_reading)
995 {
996 continue;
997 }
998
999 apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
1000
1001 rv = apr_file_write_full(proc->in, data, len, NULL);
1002 if (rv != APR_SUCCESS)
1003 {
1004 child_stopped_reading = 1;
1005 }
1006 }
1007 apr_brigade_cleanup(bb);
1008 }
1009 while (!eos_reached);
1010
1011 apr_file_flush(proc->in);
1012 apr_file_close(proc->in);
1013
1014 /* get output from script and check if non-parsed headers are used */
1015
1016 #if APR_FILES_AS_SOCKETS
1017 apr_file_pipe_timeout_set(proc->out, 0);
1018 apr_file_pipe_timeout_set(proc->err, 0);
1019 b = suphp_bucket_create(r, proc->out, proc->err, r->connection->bucket_alloc);
1020 #else
1021 b = apr_bucket_pipe_create(proc->out, r->connection->bucket_alloc);
1022 #endif
1023
1024 APR_BRIGADE_INSERT_TAIL(bb, b);
1025
1026 b = apr_bucket_eos_create(r->connection->bucket_alloc);
1027 APR_BRIGADE_INSERT_TAIL(bb, b);
1028
1029 tmpbuf = suphp_brigade_read(p, bb, 8);
1030 if (strlen(tmpbuf) == 8 && !(strncmp(tmpbuf, "HTTP/1.0", 8) && strncmp(tmpbuf, "HTTP/1.1", 8)))
1031 {
1032 nph = 1;
1033 }
1034
1035 if (!nph)
1036 {
1037 /* normal cgi headers, so we have to create the real headers ourselves */
1038
1039 int ret;
1040 const char *location;
1041
1042 ret = ap_scan_script_header_err_brigade(r, bb, strbuf);
1043 if (ret == HTTP_NOT_MODIFIED)
1044 {
1045 return ret;
1046 }
1047 else if (ret != APR_SUCCESS)
1048 {
1049 suphp_discard_output(bb);
1050 apr_brigade_destroy(bb);
1051 suphp_log_script_err(r, proc->err);
1052
1053 /* ap_scan_script_header_err_brigade does logging itself,
1054 so simply return */
1055
1056 return HTTP_INTERNAL_SERVER_ERROR;
1057 }
1058
1059 location = apr_table_get(r->headers_out, "Location");
1060 if (location && location[0] == '/' && r->status == 200)
1061 {
1062 /* empty brigade (script output) and modify headers */
1063
1064 suphp_discard_output(bb);
1065 apr_brigade_destroy(bb);
1066 suphp_log_script_err(r, proc->err);
1067 r->method = apr_pstrdup(r->pool, "GET");
1068 r->method_number = M_GET;
1069 apr_table_unset(r->headers_in, "Content-Length");
1070
1071 ap_internal_redirect_handler(location, r);
1072 return OK;
1073 }
1074 else if (location && r->status == 200)
1075 {
1076 /* empty brigade (script output) */
1077 suphp_discard_output(bb);
1078 apr_brigade_destroy(bb);
1079 suphp_log_script_err(r, proc->err);
1080 return HTTP_MOVED_TEMPORARILY;
1081 }
1082
1083 /* send output to browser (through filters) */
1084
1085 rv = ap_pass_brigade(r->output_filters, bb);
1086
1087 /* write errors to logfile */
1088
1089 if (rv == APR_SUCCESS && !r->connection->aborted)
1090 suphp_log_script_err(r, proc->err);
1091
1092 apr_file_close(proc->err);
1093 }
1094
1095 if (proc->out && nph)
1096 {
1097 /* use non-parsed headers (direct output) */
1098
1099 struct ap_filter_t *cur;
1100
1101 /* get rid of output filters */
1102
1103 cur = r->proto_output_filters;
1104 while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION)
1105 {
1106 cur = cur->next;
1107 }
1108 r->output_filters = r->proto_output_filters = cur;
1109
1110 /* send output to browser (directly) */
1111
1112 rv = ap_pass_brigade(r->output_filters, bb);
1113
1114 /* log errors */
1115 if (rv == APR_SUCCESS && !r->connection->aborted)
1116 suphp_log_script_err(r, proc->err);
1117
1118 apr_file_close(proc->err);
1119 }
1120
1121 return OK;
1122 }
1123
1124 static void suphp_register_hooks(apr_pool_t *p)
1125 {
1126 ap_hook_handler(suphp_handler, NULL, NULL, APR_HOOK_MIDDLE);
1127 }
1128
1129
1130 /********************
1131 Module declaration
1132 ********************/
1133
1134 module AP_MODULE_DECLARE_DATA suphp_module =
1135 {
1136 STANDARD20_MODULE_STUFF,
1137 suphp_create_dir_config,
1138 suphp_merge_dir_config,
1139 suphp_create_server_config,
1140 suphp_merge_server_config,
1141 suphp_cmds,
1142 suphp_register_hooks
1143 };