2 suPHP - (c)2002-2005 Sebastian Marsching <sebastian@marsching.com>
4 This file is part of suPHP.
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.
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.
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
22 #include "apr_strings.h"
23 #include "apr_thread_proc.h"
24 #include "apr_buckets.h"
30 #include "http_config.h"
31 #include "http_core.h"
34 #include "util_script.h"
35 #include "util_filter.h"
37 /* needed for get_suexec_identity hook */
40 module AP_MODULE_DECLARE_DATA suphp_module
;
43 /*********************
45 *********************/
47 static apr_status_t
suphp_log_script_err(request_rec
*r
, apr_file_t
*script_err
)
49 char argsbuffer
[HUGE_STRING_LEN
];
53 while ((rv
= apr_file_gets(argsbuffer
, HUGE_STRING_LEN
,
54 script_err
)) == APR_SUCCESS
) {
55 newline
= strchr(argsbuffer
, '\n');
59 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
,
66 char *suphp_brigade_read(apr_pool_t
*p
, apr_bucket_brigade
*bb
, int bytes
)
77 target_buf
= (char *) apr_palloc(p
, bytes
+ 1);
78 next_byte
= target_buf
;
79 last_byte
= target_buf
+ bytes
;
81 for (b
= APR_BRIGADE_FIRST(bb
); b
!= APR_BRIGADE_SENTINEL(bb
); b
= APR_BUCKET_NEXT(b
)) {
85 if (apr_bucket_read(b
, &buf
, &size
, APR_BLOCK_READ
) == APR_SUCCESS
) {
86 for (i
= 0; i
< size
; i
++) {
90 if (next_byte
== last_byte
) {
102 /**************************
103 Configuration processing
104 **************************/
106 #define SUPHP_CONFIG_MODE_SERVER 1
107 #define SUPHP_CONFIG_MODE_DIRECTORY 2
109 #define SUPHP_ENGINE_OFF 0
110 #define SUPHP_ENGINE_ON 1
111 #define SUPHP_ENGINE_UNDEFINED 2
113 #ifndef SUPHP_PATH_TO_SUPHP
114 #define SUPHP_PATH_TO_SUPHP "/usr/sbin/suphp"
118 int engine
; // Status of suPHP_Engine
120 int cmode
; // Server of directory configuration?
121 #ifdef SUPHP_USE_USERGROUP
125 apr_table_t
*handlers
;
130 static void *suphp_create_dir_config(apr_pool_t
*p
, char *dir
)
132 suphp_conf
*cfg
= (suphp_conf
*) apr_pcalloc(p
, sizeof(suphp_conf
));
134 cfg
->php_config
= NULL
;
135 cfg
->engine
= SUPHP_ENGINE_UNDEFINED
;
136 cfg
->php_path
= NULL
;
137 cfg
->cmode
= SUPHP_CONFIG_MODE_DIRECTORY
;
139 #ifdef SUPHP_USE_USERGROUP
140 cfg
->target_user
= NULL
;
141 cfg
->target_group
= NULL
;
144 /* Create table with 0 initial elements */
145 /* This size may be increased for performance reasons */
146 cfg
->handlers
= apr_table_make(p
, 0);
152 static void *suphp_merge_dir_config(apr_pool_t
*p
, void *base
,
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
));
159 merged
->cmode
= SUPHP_CONFIG_MODE_DIRECTORY
;
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
);
166 merged
->php_config
= NULL
;
168 if (child
->engine
!= SUPHP_ENGINE_UNDEFINED
)
169 merged
->engine
= child
->engine
;
171 merged
->engine
= parent
->engine
;
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
);
179 merged
->target_user
= NULL
;
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
);
186 merged
->target_group
= NULL
;
189 merged
->handlers
= apr_table_overlay(p
, child
->handlers
, parent
->handlers
);
191 return (void *) merged
;
195 static void *suphp_create_server_config(apr_pool_t
*p
, server_rec
*s
)
197 suphp_conf
*cfg
= (suphp_conf
*) apr_pcalloc(p
, sizeof(suphp_conf
));
199 cfg
->engine
= SUPHP_ENGINE_UNDEFINED
;
200 cfg
->php_path
= NULL
;
201 cfg
->cmode
= SUPHP_CONFIG_MODE_SERVER
;
203 /* Create table with 0 initial elements */
204 /* This size may be increased for performance reasons */
205 cfg
->handlers
= apr_table_make(p
, 0);
211 static void *suphp_merge_server_config(apr_pool_t
*p
, void *base
,
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
));
218 if (child
->engine
!= SUPHP_ENGINE_UNDEFINED
)
219 merged
->engine
= child
->engine
;
221 merged
->engine
= parent
->engine
;
223 if (child
->php_path
!= NULL
)
224 merged
->php_path
= apr_pstrdup(p
, child
->php_path
);
226 merged
->php_path
= apr_pstrdup(p
, parent
->php_path
);
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
);
234 merged
->target_user
= NULL
;
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
);
241 merged
->target_group
= NULL
;
244 merged
->handlers
= apr_table_overlay(p
, child
->handlers
, parent
->handlers
);
246 return (void*) merged
;
254 static const char *suphp_handle_cmd_engine(cmd_parms
*cmd
, void *mconfig
,
257 server_rec
*s
= cmd
->server
;
261 cfg
= (suphp_conf
*) mconfig
;
263 cfg
= (suphp_conf
*) ap_get_module_config(s
->module_config
, &suphp_module
);
266 cfg
->engine
= SUPHP_ENGINE_ON
;
268 cfg
->engine
= SUPHP_ENGINE_OFF
;
274 static const char *suphp_handle_cmd_config(cmd_parms
*cmd
, void *mconfig
,
277 server_rec
*s
= cmd
->server
;
281 cfg
= (suphp_conf
*) mconfig
;
283 cfg
= (suphp_conf
*) ap_get_module_config(s
->module_config
, &suphp_module
);
285 cfg
->php_config
= apr_pstrdup(cmd
->pool
, arg
);
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
)
295 suphp_conf
*cfg
= (suphp_conf
*) mconfig
;
297 cfg
->target_user
= apr_pstrdup(cmd
->pool
, arg1
);
298 cfg
->target_group
= apr_pstrdup(cmd
->pool
, arg2
);
305 static const char *suphp_handle_cmd_add_handler(cmd_parms
*cmd
, void *mconfig
,
310 cfg
= (suphp_conf
*) mconfig
;
312 cfg
= (suphp_conf
*) ap_get_module_config(cmd
->server
->module_config
, &suphp_module
);
314 // Mark active handler with '1'
315 apr_table_set(cfg
->handlers
, arg
, "1");
321 static const char *suphp_handle_cmd_remove_handler(cmd_parms
*cmd
,
327 cfg
= (suphp_conf
*) mconfig
;
329 cfg
= (suphp_conf
*) ap_get_module_config(cmd
->server
->module_config
, &suphp_module
);
331 // Mark deactivated handler with '0'
332 apr_table_set(cfg
->handlers
, arg
, "0");
338 static const char *suphp_handle_cmd_phppath(cmd_parms
*cmd
, void* mconfig
, const char *arg
)
340 server_rec
*s
= cmd
->server
;
343 cfg
= (suphp_conf
*) ap_get_module_config(s
->module_config
, &suphp_module
);
345 cfg
->php_path
= apr_pstrdup(cmd
->pool
, arg
);
351 static const command_rec suphp_cmds
[] =
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"),
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"),
367 /*****************************************
368 Code for reading script's stdout/stderr
369 based on mod_cgi's code
370 *****************************************/
372 #if APR_FILES_AS_SOCKETS
374 static const apr_bucket_type_t bucket_type_suphp
;
376 struct suphp_bucket_data
{
377 apr_pollset_t
*pollset
;
381 static apr_bucket
*suphp_bucket_create(request_rec
*r
, apr_file_t
*out
, apr_file_t
*err
, apr_bucket_alloc_t
*list
)
383 apr_bucket
*b
= apr_bucket_alloc(sizeof(*b
), list
);
386 struct suphp_bucket_data
*data
= apr_palloc(r
->pool
, sizeof(*data
));
389 b
->free
= apr_bucket_free
;
391 b
->type
= &bucket_type_suphp
;
392 b
->length
= (apr_size_t
) (-1);
395 /* Create the pollset */
396 rv
= apr_pollset_create(&data
->pollset
, 2, r
->pool
, 0);
397 AP_DEBUG_ASSERT(rv
== APR_SUCCESS
);
399 fd
.desc_type
= APR_POLL_FILE
;
400 fd
.reqevents
= APR_POLLIN
;
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
);
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
);
417 static apr_bucket
*suphp_bucket_dup(struct suphp_bucket_data
*data
, apr_bucket_alloc_t
*list
)
419 apr_bucket
*b
= apr_bucket_alloc(sizeof(*b
), list
);
421 b
->free
= apr_bucket_free
;
423 b
->type
= &bucket_type_suphp
;
424 b
->length
= (apr_size_t
) (-1);
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
)
438 *len
= APR_BUCKET_BUFF_SIZE
;
439 buf
= apr_bucket_alloc(*len
, b
->list
);
441 rv
= apr_file_read(fd
, buf
, len
);
445 struct suphp_bucket_data
*data
= b
->data
;
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 */
453 h
->alloc_len
= APR_BUCKET_BUFF_SIZE
; /* note the real buffer size */
455 APR_BUCKET_INSERT_AFTER(b
, suphp_bucket_dup(data
, b
->list
));
458 apr_bucket_free(buf
);
459 b
= apr_bucket_immortal_make(b
, "", 0);
460 /* Here, b->data is the reference to the empty string */
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
;
474 timeout
= (block
== APR_NONBLOCK_READ
) ? 0 : data
->r
->server
->timeout
;
477 const apr_pollfd_t
*results
;
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
)) {
485 } else if (rv
!= APR_SUCCESS
) {
486 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, data
->r
, "Poll failed waiting for suPHP child process");
491 if (results
[0].client_data
== (void *) 1) {
493 rv
= suphp_read_fd(b
, results
[0].desc
.f
, str
, len
);
494 if (APR_STATUS_IS_EOF(rv
)) {
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]);
513 static const apr_bucket_type_t bucket_type_suphp
= {
514 "SUPHP", 5, APR_BUCKET_DATA
,
515 apr_bucket_destroy_noop
,
517 apr_bucket_setaside_notimpl
,
518 apr_bucket_split_notimpl
,
519 apr_bucket_copy_notimpl
524 static void suphp_discard_output(apr_bucket_brigade
*bb
) {
529 for (b
= APR_BRIGADE_FIRST(bb
); b
!= APR_BRIGADE_SENTINEL(bb
); b
= APR_BUCKET_NEXT(b
)) {
530 if (APR_BUCKET_IS_EOS(b
)) {
533 rv
= apr_bucket_read(b
, &buf
, &len
, APR_BLOCK_READ
);
534 if (rv
!= APR_SUCCESS
) {
545 static int suphp_script_handler(request_rec
*r
);
546 static int suphp_source_handler(request_rec
*r
);
548 static int suphp_handler(request_rec
*r
)
550 suphp_conf
*sconf
, *dconf
;
552 sconf
= ap_get_module_config(r
->server
->module_config
, &suphp_module
);
553 dconf
= ap_get_module_config(r
->per_dir_config
, &suphp_module
);
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
)
559 if (*(apr_table_get(dconf
->handlers
, r
->handler
)) != '0')
561 return suphp_script_handler(r
);
566 if ((apr_table_get(sconf
->handlers
, r
->handler
) != NULL
)
567 && (*(apr_table_get(sconf
->handlers
, r
->handler
)) != '0'))
569 return suphp_script_handler(r
);
573 if (!strcmp(r
->handler
, "x-httpd-php-source")
574 || !strcmp(r
->handler
, "application/x-httpd-php-source"))
576 return suphp_source_handler(r
);
582 static int suphp_source_handler(request_rec
*r
)
589 apr_procattr_t
*procattr
;
592 apr_bucket_brigade
*bb
;
596 p
= r
->main
? r
->main
->pool
: r
->pool
;
598 if (strcmp(r
->method
, "GET"))
603 conf
= ap_get_module_config(r
->server
->module_config
, &suphp_module
);
604 phpexec
= apr_pstrdup(p
, conf
->php_path
);
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
)
614 apr_file_close(file
);
617 else if (rv
== EACCES
)
619 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, r
, "access to %s denied", r
->filename
);
620 return HTTP_FORBIDDEN
;
622 else if (rv
== ENOENT
)
624 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
, "File does not exist: %s", r
->filename
);
625 return HTTP_NOT_FOUND
;
629 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, r
, "Could not open file: %s", r
->filename
);
630 return HTTP_INTERNAL_SERVER_ERROR
;
633 env
= ap_create_environment(p
, r
->subprocess_env
);
635 /* set attributes for new process */
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
))
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
;
650 /* create new process */
652 argv
= apr_palloc(p
, 4 * sizeof(char *));
655 argv
[2] = apr_pstrdup(p
, r
->filename
);
658 env
= ap_create_environment(p
, r
->subprocess_env
);
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
)
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
;
668 apr_pool_note_subprocess(p
, proc
, APR_KILL_AFTER_TIMEOUT
);
672 apr_file_pipe_timeout_set(proc
->out
, r
->server
->timeout
);
676 apr_file_pipe_timeout_set(proc
->in
, r
->server
->timeout
);
680 apr_file_pipe_timeout_set(proc
->err
, r
->server
->timeout
);
684 bb
= apr_brigade_create(r
->pool
, r
->connection
->bucket_alloc
);
686 apr_file_flush(proc
->in
);
687 apr_file_close(proc
->in
);
689 rv
= ap_get_brigade(r
->input_filters
, bb
, AP_MODE_READBYTES
, APR_BLOCK_READ
, HUGE_STRING_LEN
);
690 if (rv
!= APR_SUCCESS
)
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
;
696 suphp_discard_output(bb
);
697 apr_brigade_cleanup(bb
);
699 /* get output from script */
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
);
706 b
= apr_bucket_pipe_create(proc
->out
, r
->connection
->bucket_alloc
);
708 APR_BRIGADE_INSERT_TAIL(bb
, b
);
710 b
= apr_bucket_eos_create(r
->connection
->bucket_alloc
);
711 APR_BRIGADE_INSERT_TAIL(bb
, b
);
713 /* send output to browser (through filters) */
715 r
->content_type
= "text/html";
716 rv
= ap_pass_brigade(r
->output_filters
, bb
);
718 /* write errors to logfile */
720 if (rv
== APR_SUCCESS
&& !r
->connection
->aborted
)
722 suphp_log_script_err(r
, proc
->err
);
723 apr_file_close(proc
->err
);
729 static int suphp_script_handler(request_rec
*r
)
734 core_dir_config
*core_conf
;
738 apr_procattr_t
*procattr
;
746 #if MAX_STRING_LEN < 1024
749 char strbuf
[MAX_STRING_LEN
];
754 char *auth_user
= NULL
;
755 char *auth_pass
= NULL
;
757 #ifdef SUPHP_USE_USERGROUP
758 char *ud_user
= NULL
;
759 char *ud_group
= NULL
;
762 apr_bucket_brigade
*bb
;
765 /* load configuration */
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
);
772 /* check if suPHP is enabled for this request */
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
)))
780 /* check if file is existing and acessible */
782 rv
= apr_stat(&finfo
, apr_pstrdup(p
, r
->filename
), APR_FINFO_NORM
, p
);
784 if (rv
== APR_SUCCESS
)
786 else if (rv
== EACCES
)
788 return HTTP_FORBIDDEN
;
789 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, r
, "access to %s denied", r
->filename
);
791 else if (rv
== ENOENT
)
793 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
, "File does not exist: %s", r
->filename
);
794 return HTTP_NOT_FOUND
;
798 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, rv
, r
, "could not get fileinfo: %s", r
->filename
);
799 return HTTP_NOT_FOUND
;
802 if (!(r
->finfo
.protection
& APR_UREAD
))
804 ap_log_rerror(APLOG_MARK
, APLOG_ERR
, 0, r
, "Insufficient permissions: %s", r
->filename
);
805 return HTTP_FORBIDDEN
;
808 #ifdef SUPHP_USE_USERGROUP
809 if ((sconf
->target_user
== NULL
|| sconf
->target_group
== NULL
)
810 && (dconf
->target_user
== NULL
|| dconf
->target_group
== NULL
))
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
);
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
;
826 /* prepare argv for new process */
828 argv
= apr_palloc(p
, 2 * sizeof(char *));
829 argv
[0] = SUPHP_PATH_TO_SUPHP
;
832 /* prepare environment for new process */
834 ap_add_common_vars(r
);
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");
841 #ifdef SUPHP_USE_USERGROUP
842 apr_table_unset(r
->subprocess_env
, "SUPHP_USER");
843 apr_table_unset(r
->subprocess_env
, "SUPHP_GROUP");
846 if (dconf
->php_config
)
848 apr_table_setn(r
->subprocess_env
, "SUPHP_PHP_CONFIG", apr_pstrdup(p
, dconf
->php_config
));
851 apr_table_setn(r
->subprocess_env
, "SUPHP_HANDLER", r
->handler
);
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)
861 user
= ap_pbase64decode(p
, auth
+ 6);
864 pass
= strchr(user
, ':');
868 auth_user
= apr_pstrdup(r
->pool
, user
);
869 auth_pass
= apr_pstrdup(r
->pool
, pass
);
875 if (auth_user
&& auth_pass
)
877 apr_table_setn(r
->subprocess_env
, "SUPHP_AUTH_USER", auth_user
);
878 apr_table_setn(r
->subprocess_env
, "SUPHP_AUTH_PW", auth_pass
);
881 #ifdef SUPHP_USE_USERGROUP
882 if (dconf
->target_user
)
884 apr_table_setn(r
->subprocess_env
, "SUPHP_USER",
885 apr_pstrdup(r
->pool
, dconf
->target_user
));
887 else if (sconf
->target_user
)
889 apr_table_setn(r
->subprocess_env
, "SUPHP_USER",
890 apr_pstrdup(r
->pool
, sconf
->target_user
));
894 apr_table_setn(r
->subprocess_env
, "SUPHP_USER",
895 apr_pstrdup(r
->pool
, ud_user
));
898 if (dconf
->target_group
)
900 apr_table_setn(r
->subprocess_env
, "SUPHP_GROUP",
901 apr_pstrdup(r
->pool
, dconf
->target_group
));
903 else if (sconf
->target_group
)
905 apr_table_setn(r
->subprocess_env
, "SUPHP_GROUP",
906 apr_pstrdup(r
->pool
, sconf
->target_group
));
910 apr_table_setn(r
->subprocess_env
, "SUPHP_GROUP",
911 apr_pstrdup(r
->pool
, ud_group
));
915 env
= ap_create_environment(p
, r
->subprocess_env
);
917 /* set attributes for new process */
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
)
923 /* set resource limits */
926 || ((rv
= apr_procattr_limit_set(procattr
, APR_LIMIT_CPU
, core_conf
->limit_cpu
)) != APR_SUCCESS
)
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
)
932 || ((apr_procattr_limit_set(procattr
, APR_LIMIT_NPROC
, core_conf
->limit_nproc
)) != APR_SUCCESS
)
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
))
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
;
944 /* create new process */
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
)
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
;
955 apr_pool_note_subprocess(p
, proc
, APR_KILL_AFTER_TIMEOUT
);
959 apr_file_pipe_timeout_set(proc
->out
, r
->server
->timeout
);
963 apr_file_pipe_timeout_set(proc
->in
, r
->server
->timeout
);
967 apr_file_pipe_timeout_set(proc
->err
, r
->server
->timeout
);
969 /* send request body to script */
971 bb
= apr_brigade_create(r
->pool
, r
->connection
->bucket_alloc
);
975 rv
= ap_get_brigade(r
->input_filters
, bb
, AP_MODE_READBYTES
, APR_BLOCK_READ
, HUGE_STRING_LEN
);
977 if (rv
!= APR_SUCCESS
)
982 for (bucket
= APR_BRIGADE_FIRST(bb
); bucket
!= APR_BRIGADE_SENTINEL(bb
); bucket
= APR_BUCKET_NEXT(bucket
))
986 int child_stopped_reading
= 0;
988 if (APR_BUCKET_IS_EOS(bucket
))
994 if (APR_BUCKET_IS_FLUSH(bucket
) || child_stopped_reading
)
999 apr_bucket_read(bucket
, &data
, &len
, APR_BLOCK_READ
);
1001 rv
= apr_file_write_full(proc
->in
, data
, len
, NULL
);
1002 if (rv
!= APR_SUCCESS
)
1004 child_stopped_reading
= 1;
1007 apr_brigade_cleanup(bb
);
1009 while (!eos_reached
);
1011 apr_file_flush(proc
->in
);
1012 apr_file_close(proc
->in
);
1014 /* get output from script and check if non-parsed headers are used */
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
);
1021 b
= apr_bucket_pipe_create(proc
->out
, r
->connection
->bucket_alloc
);
1024 APR_BRIGADE_INSERT_TAIL(bb
, b
);
1026 b
= apr_bucket_eos_create(r
->connection
->bucket_alloc
);
1027 APR_BRIGADE_INSERT_TAIL(bb
, b
);
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)))
1037 /* normal cgi headers, so we have to create the real headers ourselves */
1040 const char *location
;
1042 ret
= ap_scan_script_header_err_brigade(r
, bb
, strbuf
);
1043 if (ret
== HTTP_NOT_MODIFIED
)
1047 else if (ret
!= APR_SUCCESS
)
1049 suphp_discard_output(bb
);
1050 apr_brigade_destroy(bb
);
1051 suphp_log_script_err(r
, proc
->err
);
1053 /* ap_scan_script_header_err_brigade does logging itself,
1056 return HTTP_INTERNAL_SERVER_ERROR
;
1059 location
= apr_table_get(r
->headers_out
, "Location");
1060 if (location
&& location
[0] == '/' && r
->status
== 200)
1062 /* empty brigade (script output) and modify headers */
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");
1071 ap_internal_redirect_handler(location
, r
);
1074 else if (location
&& r
->status
== 200)
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
;
1083 /* send output to browser (through filters) */
1085 rv
= ap_pass_brigade(r
->output_filters
, bb
);
1087 /* write errors to logfile */
1089 if (rv
== APR_SUCCESS
&& !r
->connection
->aborted
)
1090 suphp_log_script_err(r
, proc
->err
);
1092 apr_file_close(proc
->err
);
1095 if (proc
->out
&& nph
)
1097 /* use non-parsed headers (direct output) */
1099 struct ap_filter_t
*cur
;
1101 /* get rid of output filters */
1103 cur
= r
->proto_output_filters
;
1104 while (cur
&& cur
->frec
->ftype
< AP_FTYPE_CONNECTION
)
1108 r
->output_filters
= r
->proto_output_filters
= cur
;
1110 /* send output to browser (directly) */
1112 rv
= ap_pass_brigade(r
->output_filters
, bb
);
1115 if (rv
== APR_SUCCESS
&& !r
->connection
->aborted
)
1116 suphp_log_script_err(r
, proc
->err
);
1118 apr_file_close(proc
->err
);
1124 static void suphp_register_hooks(apr_pool_t
*p
)
1126 ap_hook_handler(suphp_handler
, NULL
, NULL
, APR_HOOK_MIDDLE
);
1130 /********************
1132 ********************/
1134 module AP_MODULE_DECLARE_DATA suphp_module
=
1136 STANDARD20_MODULE_STUFF
,
1137 suphp_create_dir_config
,
1138 suphp_merge_dir_config
,
1139 suphp_create_server_config
,
1140 suphp_merge_server_config
,
1142 suphp_register_hooks