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