From fbfcde1463af7d1a282236c3db9f11d24abe1e57 Mon Sep 17 00:00:00 2001 From: Ryan Bloom Date: Mon, 5 Feb 2001 22:57:21 +0000 Subject: [PATCH] Move the logic for creating CGI processes from mod_include to mod_cgi(d). This removes a good deal of duplicate logic for creating CGI scripts. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@87984 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 5 + modules/filters/mod_include.c | 383 +++++----------------------------- modules/filters/mod_include.h | 31 ++- modules/generators/mod_cgi.c | 313 ++++++++++++++++++++++++--- modules/generators/mod_cgid.c | 325 +++++++++++++++++++++++++++-- 5 files changed, 680 insertions(+), 377 deletions(-) diff --git a/CHANGES b/CHANGES index 674fe385af..dd2b5bafc0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ Changes with Apache 2.0b1 + *) Move the CGI creation logic from mod_include to mod_cgi(d). This + should reduce the amount of duplicate code that is required to + create CGI processes. + [Paul J. Reder ] + *) ap_new_connection() closes the socket and returns NULL if a socket call fails. Usually this is due to a connection which has been reset. [Jeff Trawick] diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c index 6aeef3da82..fb40cb8c18 100644 --- a/modules/filters/mod_include.c +++ b/modules/filters/mod_include.c @@ -83,6 +83,7 @@ #include "http_main.h" #include "util_script.h" #include "http_core.h" +#include "apr_optional.h" #include "mod_include.h" #ifdef HAVE_STRING_H #include @@ -94,6 +95,8 @@ static apr_hash_t *include_hash; +static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register; + /* ------------------------ Environment function -------------------------- */ @@ -496,7 +499,7 @@ otilde\365oslash\370ugrave\371uacute\372yacute\375" /* 6 */ #define SKIP_TAG_WHITESPACE(ptr) while ((*ptr != '\0') && (apr_isspace (*ptr))) ptr++ -static void get_tag_and_value(include_ctx_t *ctx, char **tag, +static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag, char **tag_val, int dodecode) { char *c = ctx->curr_tag_pos; @@ -574,8 +577,8 @@ static void get_tag_and_value(include_ctx_t *ctx, char **tag, /* * Do variable substitution on strings */ -static void parse_string(request_rec *r, const char *in, char *out, - size_t length, int leave_name) +static void ap_ssi_parse_string(request_rec *r, const char *in, char *out, + size_t length, int leave_name) { char ch; char *next = out; @@ -601,7 +604,6 @@ static void parse_string(request_rec *r, const char *in, char *out, break; case '$': { -/* pjr hack char var[MAX_STRING_LEN]; */ const char *start_of_var_name; char *end_of_var_name; /* end of var name + 1 */ const char *expansion, *temp_end, *val; @@ -637,17 +639,10 @@ static void parse_string(request_rec *r, const char *in, char *out, * pass a non-nul terminated string */ l = end_of_var_name - start_of_var_name; if (l != 0) { -/* pjr - this is a test hack to avoid a memcpy. Make sure that this works... -* l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l; -* memcpy(var, start_of_var_name, l); -* var[l] = '\0'; -* -* val = apr_table_get(r->subprocess_env, var); -*/ -/* pjr hack */ tmp_store = *end_of_var_name; -/* pjr hack */ *end_of_var_name = '\0'; -/* pjr hack */ val = apr_table_get(r->subprocess_env, start_of_var_name); -/* pjr hack */ *end_of_var_name = tmp_store; + tmp_store = *end_of_var_name; + *end_of_var_name = '\0'; + val = apr_table_get(r->subprocess_env, start_of_var_name); + *end_of_var_name = tmp_store; if (val) { expansion = val; @@ -685,68 +680,6 @@ static void parse_string(request_rec *r, const char *in, char *out, /* --------------------------- Action handlers ---------------------------- */ -static int include_cgi(char *s, request_rec *r, ap_filter_t *next, - apr_bucket *head_ptr, apr_bucket **inserted_head) -{ - request_rec *rr = ap_sub_req_lookup_uri(s, r, next); - int rr_status; - apr_bucket *tmp_buck, *tmp2_buck; - - if (rr->status != HTTP_OK) { - return -1; - } - - /* No hardwired path info or query allowed */ - - if ((rr->path_info && rr->path_info[0]) || rr->args) { - return -1; - } - if (rr->finfo.filetype == 0) { - return -1; - } - - /* Script gets parameters of the *document*, for back compatibility */ - - rr->path_info = r->path_info; /* hard to get right; see mod_cgi.c */ - rr->args = r->args; - - /* Force sub_req to be treated as a CGI request, even if ordinary - * typing rules would have called it something else. - */ - - rr->content_type = CGI_MAGIC_TYPE; - - /* Run it. */ - - rr_status = ap_run_sub_req(rr); - if (ap_is_HTTP_REDIRECT(rr_status)) { - apr_size_t len_loc, h_wrt; - const char *location = apr_table_get(rr->headers_out, "Location"); - - location = ap_escape_html(rr->pool, location); - len_loc = strlen(location); - - tmp_buck = apr_bucket_create_immortal("", sizeof("\">")); - APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck); - tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt); - APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck); - tmp2_buck = apr_bucket_create_immortal("", sizeof("")); - APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck); - - if (*inserted_head == NULL) { - *inserted_head = tmp_buck; - } - } - - ap_destroy_sub_req(rr); - - return 0; -} - /* ensure that path is relative, and does not contain ".." elements * ensentially ensure that it does not match the regex: * (^/|(^|/)\.\.(/|$)) @@ -798,7 +731,7 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r *inserted_head = NULL; if (ctx->flags & FLAG_PRINTING) { while (1) { - get_tag_and_value(ctx, &tag, &tag_val, 1); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); if (tag_val == NULL) { if (tag == NULL) { return (0); @@ -811,7 +744,7 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r request_rec *rr = NULL; char *error_fmt = NULL; - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); if (tag[0] == 'f') { /* be safe; only files in this directory or below allowed */ if (!is_only_below(parsed_string)) { @@ -884,10 +817,9 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r ap_set_module_config(rr->request_config, &includes_module, r); if (!error_fmt) { - SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx); -/* - rr->output_filters = f->next; -*/ if (ap_run_sub_req(rr)) { + SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next); + + if (ap_run_sub_req(rr)) { error_fmt = "unable to include \"%s\" in parsed file %s"; } } @@ -915,213 +847,6 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r return 0; } -typedef struct { -#ifdef TPF - TPF_FORK_CHILD t; -#endif - request_rec *r; - char *s; -} include_cmd_arg; - - - -static apr_status_t build_argv_list(char ***argv, request_rec *r, apr_pool_t *p) -{ - int numwords, x, idx; - char *w; - const char *args = r->args; - - if (!args || !args[0] || ap_strchr_c(args, '=')) { - numwords = 1; - } - else { - /* count the number of keywords */ - for (x = 0, numwords = 1; args[x]; x++) { - if (args[x] == '+') { - ++numwords; - } - } - } - /* Everything is - 1 to account for the first parameter which is the - * program name. We didn't used to have to do this, but APR wants it. - */ - if (numwords > APACHE_ARG_MAX - 1) { - numwords = APACHE_ARG_MAX - 1; /* Truncate args to prevent overrun */ - } - *argv = (char **) apr_palloc(p, (numwords + 2) * sizeof(char *)); - - for (x = 1, idx = 1; x < numwords; x++) { - w = ap_getword_nulls(p, &args, '+'); - ap_unescape_url(w); - (*argv)[idx++] = ap_escape_shell_cmd(p, w); - } - (*argv)[idx] = NULL; - - return APR_SUCCESS; -} - - - -static int include_cmd(include_ctx_t *ctx, apr_bucket_brigade **bb, char *s, - request_rec *r, ap_filter_t *f) -{ - include_cmd_arg arg; - apr_procattr_t *procattr; - apr_proc_t *procnew; - apr_status_t rc; - apr_table_t *env = r->subprocess_env; - char **argv; - apr_file_t *file = NULL; -#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ - defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS) - core_dir_config *conf; - conf = (core_dir_config *) ap_get_module_config(r->per_dir_config, - &core_module); -#endif - - arg.r = r; - arg.s = s; -#ifdef TPF - arg.t.filename = r->filename; - arg.t.subprocess_env = r->subprocess_env; - arg.t.prog_type = FORK_FILE; -#endif - - if (r->path_info && r->path_info[0] != '\0') { - request_rec *pa_req; - - apr_table_setn(env, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info)); - - pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, f->next); - if (pa_req->filename) { - apr_table_setn(env, "PATH_TRANSLATED", - apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info, - NULL)); - } - } - - if (r->args) { - char *arg_copy = apr_pstrdup(r->pool, r->args); - - apr_table_setn(env, "QUERY_STRING", r->args); - ap_unescape_url(arg_copy); - apr_table_setn(env, "QUERY_STRING_UNESCAPED", - ap_escape_shell_cmd(r->pool, arg_copy)); - } - - if (((rc = apr_createprocattr_init(&procattr, r->pool)) != APR_SUCCESS) || - ((rc = apr_setprocattr_io(procattr, APR_NO_PIPE, - APR_FULL_BLOCK, APR_NO_PIPE)) != APR_SUCCESS) || - ((rc = apr_setprocattr_dir(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS) || -#ifdef RLIMIT_CPU - ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_CPU, conf->limit_cpu)) != APR_SUCCESS) || -#endif -#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) - ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_MEM, conf->limit_mem)) != APR_SUCCESS) || -#endif -#ifdef RLIMIT_NPROC - ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_NPROC, conf->limit_nproc)) != APR_SUCCESS) || -#endif - ((rc = apr_setprocattr_cmdtype(procattr, APR_SHELLCMD)) != APR_SUCCESS)) { - /* Something bad happened, tell the world. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, - "couldn't initialize proc attributes: %s %s", r->filename, s); - rc = !APR_SUCCESS; - } - else { - SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx); - build_argv_list(&argv, r, r->pool); - argv[0] = apr_pstrdup(r->pool, s); - procnew = apr_pcalloc(r->pool, sizeof(*procnew)); - rc = apr_create_process(procnew, s, (const char * const *) argv, - (const char * const *)ap_create_environment(r->pool, env), - procattr, r->pool); - - if (rc != APR_SUCCESS) { - /* Bad things happened. Everyone should have cleaned up. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, - "couldn't create child process: %d: %s", rc, s); - } - else { - apr_bucket_brigade *bcgi; - apr_bucket *b; - - apr_note_subprocess(r->pool, procnew, kill_after_timeout); - /* Fill in BUFF structure for parents pipe to child's stdout */ - file = procnew->out; - if (!file) - return APR_EBADF; - bcgi = apr_brigade_create(r->pool); - b = apr_bucket_create_pipe(file); - APR_BRIGADE_INSERT_TAIL(bcgi, b); - ap_pass_brigade(f->next, bcgi); - - /* We can't close the pipe here, because we may return before the - * full CGI has been sent to the network. That's okay though, - * because we can rely on the pool to close the pipe for us. - */ - } - } - - return 0; -} - -static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r, - ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head) -{ - char *tag = NULL; - char *tag_val = NULL; - char *file = r->filename; - apr_bucket *tmp_buck; - char parsed_string[MAX_STRING_LEN]; - - *inserted_head = NULL; - if (ctx->flags & FLAG_PRINTING) { - if (ctx->flags & FLAG_NO_EXEC) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "exec used but not allowed in %s", r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - } - else { - while (1) { - get_tag_and_value(ctx, &tag, &tag_val, 1); - if (tag_val == NULL) { - if (tag == NULL) { - return (0); - } - else { - return 1; - } - } - if (!strcmp(tag, "cmd")) { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1); - if (include_cmd(ctx, bb, parsed_string, r, f) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "execution failure for parameter \"%s\" " - "to tag exec in file %s", tag, r->filename); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - } - /* just in case some stooge changed directories */ - } - else if (!strcmp(tag, "cgi")) { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); - SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx); - if (include_cgi(parsed_string, r, f->next, head_ptr, inserted_head) == -1) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "invalid CGI ref \"%s\" in %s", tag_val, file); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - } - } - else { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "unknown parameter \"%s\" to tag exec in %s", tag, file); - CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); - } - } - } - } - return 0; -} static int handle_echo(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head) @@ -1138,7 +863,7 @@ static int handle_echo(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *inserted_head = NULL; if (ctx->flags & FLAG_PRINTING) { while (1) { - get_tag_and_value(ctx, &tag, &tag_val, 1); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); if (tag_val == NULL) { if (tag != NULL) { return 1; @@ -1206,7 +931,7 @@ static int handle_config(include_ctx_t *ctx, apr_bucket_brigade **bb, request_re *inserted_head = NULL; if (ctx->flags & FLAG_PRINTING) { while (1) { - get_tag_and_value(ctx, &tag, &tag_val, 0); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0); if (tag_val == NULL) { if (tag == NULL) { return 0; /* Reached the end of the string. */ @@ -1216,20 +941,20 @@ static int handle_config(include_ctx_t *ctx, apr_bucket_brigade **bb, request_re } } if (!strcmp(tag, "errmsg")) { - parse_string(r, tag_val, ctx->error_str, MAX_STRING_LEN, 0); + ap_ssi_parse_string(r, tag_val, ctx->error_str, MAX_STRING_LEN, 0); ctx->error_length = strlen(ctx->error_str); } else if (!strcmp(tag, "timefmt")) { apr_time_t date = r->request_time; - parse_string(r, tag_val, ctx->time_str, MAX_STRING_LEN, 0); + ap_ssi_parse_string(r, tag_val, ctx->time_str, MAX_STRING_LEN, 0); apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, ctx->time_str, 0)); apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, ctx->time_str, 1)); apr_table_setn(env, "LAST_MODIFIED", ap_ht_time(r->pool, r->finfo.mtime, ctx->time_str, 0)); } else if (!strcmp(tag, "sizefmt")) { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); decodehtml(parsed_string); if (!strcmp(parsed_string, "bytes")) { ctx->flags |= FLAG_SIZE_IN_BYTES; @@ -1367,7 +1092,7 @@ static int handle_fsize(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *inserted_head = NULL; if (ctx->flags & FLAG_PRINTING) { while (1) { - get_tag_and_value(ctx, &tag, &tag_val, 1); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); if (tag_val == NULL) { if (tag == NULL) { return 0; @@ -1377,7 +1102,7 @@ static int handle_fsize(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec } } else { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); if (!find_file(r, "fsize", tag, parsed_string, &finfo)) { char buff[50]; @@ -1429,7 +1154,7 @@ static int handle_flastmod(include_ctx_t *ctx, apr_bucket_brigade **bb, request_ *inserted_head = NULL; if (ctx->flags & FLAG_PRINTING) { while (1) { - get_tag_and_value(ctx, &tag, &tag_val, 1); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); if (tag_val == NULL) { if (tag == NULL) { return 0; @@ -1439,7 +1164,7 @@ static int handle_flastmod(include_ctx_t *ctx, apr_bucket_brigade **bb, request_ } } else { - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) { char *t_val; @@ -1981,7 +1706,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error, sizeof (" Evaluate string\n")); debug_pos += sizeof (" Evaluate string\n"); #endif - parse_string(r, current->token.value, buffer, sizeof(buffer), 0); + ap_ssi_parse_string(r, current->token.value, buffer, sizeof(buffer), 0); apr_cpystrn(current->token.value, buffer, sizeof(current->token.value)); current->value = (current->token.value[0] != '\0'); current->done = 1; @@ -2006,7 +1731,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error, if (!current->left->done) { switch (current->left->token.type) { case token_string: - parse_string(r, current->left->token.value, + ap_ssi_parse_string(r, current->left->token.value, buffer, sizeof(buffer), 0); apr_cpystrn(current->left->token.value, buffer, sizeof(current->left->token.value)); @@ -2021,7 +1746,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error, if (!current->right->done) { switch (current->right->token.type) { case token_string: - parse_string(r, current->right->token.value, + ap_ssi_parse_string(r, current->right->token.value, buffer, sizeof(buffer), 0); apr_cpystrn(current->right->token.value, buffer, sizeof(current->right->token.value)); @@ -2070,11 +1795,11 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error, *was_error = 1; goto RETURN; } - parse_string(r, current->left->token.value, + ap_ssi_parse_string(r, current->left->token.value, buffer, sizeof(buffer), 0); apr_cpystrn(current->left->token.value, buffer, sizeof(current->left->token.value)); - parse_string(r, current->right->token.value, + ap_ssi_parse_string(r, current->right->token.value, buffer, sizeof(buffer), 0); apr_cpystrn(current->right->token.value, buffer, sizeof(current->right->token.value)); @@ -2141,11 +1866,11 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error, *was_error = 1; goto RETURN; } - parse_string(r, current->left->token.value, + ap_ssi_parse_string(r, current->left->token.value, buffer, sizeof(buffer), 0); apr_cpystrn(current->left->token.value, buffer, sizeof(current->left->token.value)); - parse_string(r, current->right->token.value, + ap_ssi_parse_string(r, current->right->token.value, buffer, sizeof(buffer), 0); apr_cpystrn(current->right->token.value, buffer, sizeof(current->right->token.value)); @@ -2307,7 +2032,7 @@ static int handle_if(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r } else { while (1) { - get_tag_and_value(ctx, &tag, &tag_val, 0); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0); if (tag == NULL) { if (expr == NULL) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, @@ -2375,7 +2100,7 @@ static int handle_elif(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *inserted_head = NULL; if (!ctx->if_nesting_level) { while (1) { - get_tag_and_value(ctx, &tag, &tag_val, 0); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0); if (tag == '\0') { LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, " elif"); @@ -2443,7 +2168,7 @@ static int handle_else(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *inserted_head = NULL; if (!ctx->if_nesting_level) { - get_tag_and_value(ctx, &tag, &tag_val, 1); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); if ((tag != NULL) || (tag_val != NULL)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "else directive does not take tags in %s", r->filename); @@ -2476,7 +2201,7 @@ static int handle_endif(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *inserted_head = NULL; if (!ctx->if_nesting_level) { - get_tag_and_value(ctx, &tag, &tag_val, 1); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); if ((tag != NULL) || (tag_val != NULL)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "endif directive does not take tags in %s", r->filename); @@ -2507,7 +2232,7 @@ static int handle_set(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec * *inserted_head = NULL; if (ctx->flags & FLAG_PRINTING) { while (1) { - get_tag_and_value(ctx, &tag, &tag_val, 1); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); if ((tag == NULL) && (tag_val == NULL)) { return 0; } @@ -2525,7 +2250,7 @@ static int handle_set(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec * CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); return (-1); } - parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); + ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); apr_table_setn(r->subprocess_env, apr_pstrdup(r->pool, var), apr_pstrdup(r->pool, parsed_string)); } @@ -2548,7 +2273,7 @@ static int handle_printenv(include_ctx_t *ctx, apr_bucket_brigade **bb, request_ apr_bucket *tmp_buck; if (ctx->flags & FLAG_PRINTING) { - get_tag_and_value(ctx, &tag, &tag_val, 1); + ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1); if ((tag == NULL) && (tag_val == NULL)) { apr_array_header_t *arr = apr_table_elts(r->subprocess_env); apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts; @@ -3016,7 +2741,7 @@ static int includes_filter(ap_filter_t *f, apr_bucket_brigade *b) return OK; } -void ap_register_include_handler(char *tag, handler func) +static void ap_register_include_handler(char *tag, include_handler func) { apr_hash_set(include_hash, tag, strlen(tag) + 1, (const void *)func); } @@ -3026,18 +2751,21 @@ static void include_post_config(apr_pool_t *p, apr_pool_t *plog, { include_hash = apr_make_hash(p); - ap_register_include_handler("if", handle_if); - ap_register_include_handler("set", handle_set); - ap_register_include_handler("else", handle_else); - ap_register_include_handler("elif", handle_elif); - ap_register_include_handler("exec", handle_exec); - ap_register_include_handler("echo", handle_echo); - ap_register_include_handler("endif", handle_endif); - ap_register_include_handler("fsize", handle_fsize); - ap_register_include_handler("config", handle_config); - ap_register_include_handler("include", handle_include); - ap_register_include_handler("flastmod", handle_flastmod); - ap_register_include_handler("printenv", handle_printenv); + ssi_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler); + + if(ssi_pfn_register) { + ssi_pfn_register("if", handle_if); + ssi_pfn_register("set", handle_set); + ssi_pfn_register("else", handle_else); + ssi_pfn_register("elif", handle_elif); + ssi_pfn_register("echo", handle_echo); + ssi_pfn_register("endif", handle_endif); + ssi_pfn_register("fsize", handle_fsize); + ssi_pfn_register("config", handle_config); + ssi_pfn_register("include", handle_include); + ssi_pfn_register("flastmod", handle_flastmod); + ssi_pfn_register("printenv", handle_printenv); + } } /* @@ -3052,6 +2780,9 @@ static const command_rec includes_cmds[] = static void register_hooks(apr_pool_t *p) { + APR_REGISTER_OPTIONAL_FN(ap_ssi_get_tag_and_value); + APR_REGISTER_OPTIONAL_FN(ap_ssi_parse_string); + APR_REGISTER_OPTIONAL_FN(ap_register_include_handler); ap_hook_post_config(include_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST); ap_register_output_filter("INCLUDES", includes_filter, AP_FTYPE_CONTENT); } diff --git a/modules/filters/mod_include.h b/modules/filters/mod_include.h index e44fffffe6..a682201449 100644 --- a/modules/filters/mod_include.h +++ b/modules/filters/mod_include.h @@ -59,8 +59,6 @@ #ifndef _MOD_INCLUDE_H #define _MOD_INCLUDE_H 1 - - #define STARTING_SEQUENCE "" @@ -177,29 +175,40 @@ typedef struct include_filter_ctx { #define CREATE_ERROR_BUCKET(cntx, t_buck, h_ptr, ins_head) \ { \ apr_size_t e_wrt; \ - t_buck = apr_bucket_create_heap(cntx->error_str, \ + t_buck = apr_bucket_create_heap(cntx->error_str, \ ctx->error_length, 1, &e_wrt); \ - APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck); \ + APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck); \ \ if (ins_head == NULL) { \ ins_head = t_buck; \ } \ } -#define SPLIT_AND_PASS_PRETAG_BUCKETS(brgd, cntxt) \ +#define SPLIT_AND_PASS_PRETAG_BUCKETS(brgd, cntxt, next) \ if ((APR_BRIGADE_EMPTY(cntxt->ssi_tag_brigade)) && \ - (cntxt->head_start_bucket != NULL)) { \ + (cntxt->head_start_bucket != NULL)) { \ apr_bucket_brigade *tag_plus; \ - \ + \ tag_plus = apr_brigade_split(brgd, cntxt->head_start_bucket); \ - ap_pass_brigade(f->next, brgd); \ - brgd = tag_plus; \ + ap_pass_brigade(next, brgd); \ + brgd = tag_plus; \ } -typedef int (*handler)(include_ctx_t *ctx, apr_bucket_brigade **bb, + +typedef int (*include_handler)(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head); -void ap_register_include_handler(char *tag, handler func); +APR_DECLARE_OPTIONAL_FN(void, ap_ssi_get_tag_and_value, (include_ctx_t *ctx, + char **tag, + char **tag_val, + int dodecode)) +APR_DECLARE_OPTIONAL_FN(void, ap_ssi_parse_string, (request_rec *r, + const char *in, + char *out, + size_t length, + int leave_name)) +APR_DECLARE_OPTIONAL_FN(void, ap_register_include_handler, (char *tag, + include_handler func)) #endif /* MOD_INCLUDE */ diff --git a/modules/generators/mod_cgi.c b/modules/generators/mod_cgi.c index 4e0767d09d..3d9cb30965 100644 --- a/modules/generators/mod_cgi.c +++ b/modules/generators/mod_cgi.c @@ -88,6 +88,8 @@ #include "util_script.h" #include "ap_mpm.h" #include "http_conf_globals.h" +#include "apr_optional.h" +#include "../filters/mod_include.h" #ifdef HAVE_STRING_H #include #endif @@ -97,6 +99,23 @@ module AP_MODULE_DECLARE_DATA cgi_module; +static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgi_pfn_reg_with_ssi; +static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgi_pfn_gtv; +static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgi_pfn_ps; + +typedef enum {RUN_AS_SSI, RUN_AS_CGI} prog_types; + +typedef struct { + apr_int32_t in_pipe; + apr_int32_t out_pipe; + apr_int32_t err_pipe; + apr_cmdtype_e cmd_type; + prog_types prog_type; + apr_bucket_brigade **bb; + include_ctx_t *ctx; + ap_filter_t *next; +} exec_info; + /* KLUDGE --- for back-combatibility, we don't have to check ExecCGI * in ScriptAliased directories, which means we need to know if this * request came through ScriptAlias or not... so the Alias module @@ -313,21 +332,52 @@ static int log_script(request_rec *r, cgi_server_conf * conf, int ret, return ret; } + +/* This is the special environment used for running the "exec cmd=" + * variety of SSI directives. + */ +static void add_ssi_vars(request_rec *r, ap_filter_t *next) +{ + apr_table_t *e = r->subprocess_env; + + if (r->path_info && r->path_info[0] != '\0') { + request_rec *pa_req; + + apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info)); + + pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, next); + if (pa_req->filename) { + apr_table_setn(e, "PATH_TRANSLATED", + apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info, NULL)); + } + } + + if (r->args) { + char *arg_copy = apr_pstrdup(r->pool, r->args); + + apr_table_setn(e, "QUERY_STRING", r->args); + ap_unescape_url(arg_copy); + apr_table_setn(e, "QUERY_STRING_UNESCAPED", ap_escape_shell_cmd(r->pool, arg_copy)); + } +} + static apr_status_t run_cgi_child(apr_file_t **script_out, apr_file_t **script_in, apr_file_t **script_err, const char *command, const char * const argv[], - request_rec *r, apr_pool_t *p) + request_rec *r, + apr_pool_t *p, + exec_info *e_info) { const char * const *env; apr_procattr_t *procattr; - apr_proc_t *procnew = apr_pcalloc(p, sizeof(*procnew)); + apr_proc_t *procnew; apr_status_t rc = APR_SUCCESS; #if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS) core_dir_config *conf; - conf = (core_dir_config *) ap_get_module_config(r->per_dir_config, &core_module); + conf = (core_dir_config *) ap_get_module_config(r->per_dir_config, &core_module); #endif @@ -347,7 +397,13 @@ static apr_status_t run_cgi_child(apr_file_t **script_out, r->filename, cld->nph ? "NPH " : "", argv0); #endif - ap_add_cgi_vars(r); + if (e_info->prog_type == RUN_AS_CGI) { + ap_add_cgi_vars(r); + } + else /* SSIs want a controlled environment and a special path. */ + { + add_ssi_vars(r, e_info->next); + } env = (const char * const *)ap_create_environment(p, r->subprocess_env); #ifdef DEBUG_CGI @@ -360,10 +416,10 @@ static apr_status_t run_cgi_child(apr_file_t **script_out, * NB only ISINDEX scripts get decoded arguments. */ if (((rc = apr_createprocattr_init(&procattr, p)) != APR_SUCCESS) || - ((rc = apr_setprocattr_io(procattr, - APR_CHILD_BLOCK, - APR_CHILD_BLOCK, - APR_CHILD_BLOCK)) != APR_SUCCESS) || + ((rc = apr_setprocattr_io(procattr, + e_info->in_pipe, + e_info->out_pipe, + e_info->err_pipe)) != APR_SUCCESS) || ((rc = apr_setprocattr_dir(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS) || #ifdef RLIMIT_CPU @@ -375,12 +431,17 @@ static apr_status_t run_cgi_child(apr_file_t **script_out, #ifdef RLIMIT_NPROC ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_NPROC, conf->limit_nproc)) != APR_SUCCESS) || #endif - ((rc = apr_setprocattr_cmdtype(procattr, APR_PROGRAM)) != APR_SUCCESS)) { + ((rc = apr_setprocattr_cmdtype(procattr, e_info->cmd_type)) != APR_SUCCESS)) { /* Something bad happened, tell the world. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "couldn't set child process attributes: %s", r->filename); } else { + procnew = apr_pcalloc(p, sizeof(*procnew)); + if (e_info->prog_type == RUN_AS_SSI) { + SPLIT_AND_PASS_PRETAG_BUCKETS(*(e_info->bb), e_info->ctx, e_info->next); + } + rc = ap_os_create_privileged_process(r, procnew, command, argv, env, procattr, p); if (rc != APR_SUCCESS) { @@ -394,20 +455,19 @@ static apr_status_t run_cgi_child(apr_file_t **script_out, *script_in = procnew->out; if (!script_in) return APR_EBADF; - apr_set_pipe_timeout(*script_in, - (int)(r->server->timeout * APR_USEC_PER_SEC)); - - *script_out = procnew->in; - if (!*script_out) - return APR_EBADF; - apr_set_pipe_timeout(*script_out, - (int)(r->server->timeout * APR_USEC_PER_SEC)); - - *script_err = procnew->err; - if (!*script_err) - return APR_EBADF; - apr_set_pipe_timeout(*script_err, - (int)(r->server->timeout * APR_USEC_PER_SEC)); + apr_set_pipe_timeout(*script_in, (int)(r->server->timeout * APR_USEC_PER_SEC)); + + if (e_info->prog_type == RUN_AS_CGI) { + *script_out = procnew->in; + if (!*script_out) + return APR_EBADF; + apr_set_pipe_timeout(*script_out, (int)(r->server->timeout * APR_USEC_PER_SEC)); + + *script_err = procnew->err; + if (!*script_err) + return APR_EBADF; + apr_set_pipe_timeout(*script_err, (int)(r->server->timeout * APR_USEC_PER_SEC)); + } } } return (rc); @@ -516,6 +576,7 @@ static int cgi_handler(request_rec *r) apr_pool_t *p; cgi_server_conf *conf; apr_status_t rv; + exec_info e_info; if(strcmp(r->handler,CGI_MAGIC_TYPE) && strcmp(r->handler,"cgi-script")) return DECLINED; @@ -600,9 +661,18 @@ static int cgi_handler(request_rec *r) } argv[0] = apr_pstrdup(p, command); + e_info.cmd_type = APR_PROGRAM; + e_info.in_pipe = APR_CHILD_BLOCK; + e_info.out_pipe = APR_CHILD_BLOCK; + e_info.err_pipe = APR_CHILD_BLOCK; + e_info.prog_type = RUN_AS_CGI; + e_info.bb = NULL; + e_info.ctx = NULL; + e_info.next = NULL; + /* run the script in its own process */ if ((rv = run_cgi_child(&script_out, &script_in, &script_err, - command, argv, r, p)) != APR_SUCCESS) { + command, argv, r, p, &e_info)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "couldn't spawn child process: %s", r->filename); return HTTP_INTERNAL_SERVER_ERROR; @@ -723,9 +793,204 @@ static int cgi_handler(request_rec *r) return OK; /* NOT r->status, even if it has changed. */ } +/*============================================================================ + *============================================================================ + * This is the beginning of the cgi filter code moved from mod_include. This + * is the code required to handle the "exec" SSI directive. + *============================================================================ + *============================================================================*/ +static int include_cgi(char *s, request_rec *r, ap_filter_t *next, + apr_bucket *head_ptr, apr_bucket **inserted_head) +{ + request_rec *rr = ap_sub_req_lookup_uri(s, r, next); + int rr_status; + apr_bucket *tmp_buck, *tmp2_buck; + + if (rr->status != HTTP_OK) { + return -1; + } + + /* No hardwired path info or query allowed */ + + if ((rr->path_info && rr->path_info[0]) || rr->args) { + return -1; + } + if (rr->finfo.protection == 0) { + return -1; + } + + /* Script gets parameters of the *document*, for back compatibility */ + + rr->path_info = r->path_info; /* hard to get right; see mod_cgi.c */ + rr->args = r->args; + + /* Force sub_req to be treated as a CGI request, even if ordinary + * typing rules would have called it something else. + */ + + rr->content_type = CGI_MAGIC_TYPE; + + /* Run it. */ + + rr_status = ap_run_sub_req(rr); + if (ap_is_HTTP_REDIRECT(rr_status)) { + apr_size_t len_loc, h_wrt; + const char *location = apr_table_get(rr->headers_out, "Location"); + + location = ap_escape_html(rr->pool, location); + len_loc = strlen(location); + + tmp_buck = apr_bucket_create_immortal("", sizeof("\">")); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck); + tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck); + tmp2_buck = apr_bucket_create_immortal("", sizeof("")); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck); + + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + } + + ap_destroy_sub_req(rr); + + return 0; +} + + +static int include_cmd(include_ctx_t *ctx, apr_bucket_brigade **bb, char *command, + request_rec *r, ap_filter_t *f) +{ + exec_info e_info; + const char **argv; + apr_file_t *script_out = NULL, *script_in = NULL, *script_err = NULL; + apr_bucket_brigade *bcgi; + apr_bucket *b; + + if (build_argv_list(&argv, r, r->pool) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, + "couldn't spawn cmd child process: %s", r->filename); + return HTTP_INTERNAL_SERVER_ERROR; + } + argv[0] = apr_pstrdup(r->pool, command); + + e_info.cmd_type = APR_SHELLCMD; + e_info.in_pipe = APR_NO_PIPE; + e_info.out_pipe = APR_FULL_BLOCK; + e_info.err_pipe = APR_NO_PIPE; + e_info.prog_type = RUN_AS_SSI; + e_info.bb = bb; + e_info.ctx = ctx; + e_info.next = f->next; + + /* run the script in its own process */ + if (run_cgi_child(&script_out, &script_in, &script_err, + command, argv, r, r->pool, &e_info) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, + "couldn't spawn child process: %s", r->filename); + return HTTP_INTERNAL_SERVER_ERROR; + } + + bcgi = apr_brigade_create(r->pool); + b = apr_bucket_create_pipe(script_in); + APR_BRIGADE_INSERT_TAIL(bcgi, b); + ap_pass_brigade(f->next, bcgi); + + /* We can't close the pipe here, because we may return before the + * full CGI has been sent to the network. That's okay though, + * because we can rely on the pool to close the pipe for us. + */ + + return 0; +} + +static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r, + ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + char *file = r->filename; + apr_bucket *tmp_buck; + char parsed_string[MAX_STRING_LEN]; + + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + if (ctx->flags & FLAG_NO_EXEC) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "exec used but not allowed in %s", r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + else { + while (1) { + cgi_pfn_gtv(ctx, &tag, &tag_val, 1); + if (tag_val == NULL) { + if (tag == NULL) { + return (0); + } + else { + return 1; + } + } + if (!strcmp(tag, "cmd")) { + cgi_pfn_ps(r, tag_val, parsed_string, sizeof(parsed_string), 1); + if (include_cmd(ctx, bb, parsed_string, r, f) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "execution failure for parameter \"%s\" " + "to tag exec in file %s", tag, r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + } + else if (!strcmp(tag, "cgi")) { + cgi_pfn_ps(r, tag_val, parsed_string, sizeof(parsed_string), 0); + SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next); + if (include_cgi(parsed_string, r, f->next, head_ptr, inserted_head) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "invalid CGI ref \"%s\" in %s", tag_val, file); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "unknown parameter \"%s\" to tag exec in %s", tag, file); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + } + } + } + return 0; +} + + +/*============================================================================ + *============================================================================ + * This is the end of the cgi filter code moved from mod_include. + *============================================================================ + *============================================================================*/ + + +static void cgi_post_config(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *s) +{ + cgi_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler); + cgi_pfn_gtv = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value); + cgi_pfn_ps = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string); + + if ((cgi_pfn_reg_with_ssi) && (cgi_pfn_gtv) && (cgi_pfn_ps)) { + /* Required by mod_include filter. This is how mod_cgi registers + * with mod_include to provide processing of the exec directive. + */ + cgi_pfn_reg_with_ssi("exec", handle_exec); + } +} + static void register_hooks(apr_pool_t *p) { ap_hook_handler(cgi_handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_post_config(cgi_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST); } module AP_MODULE_DECLARE_DATA cgi_module = diff --git a/modules/generators/mod_cgid.c b/modules/generators/mod_cgid.c index a718f71a65..63e35a48a2 100644 --- a/modules/generators/mod_cgid.c +++ b/modules/generators/mod_cgid.c @@ -92,6 +92,8 @@ #include "ap_mpm.h" #include "unixd.h" #include "mod_suexec.h" +#include "apr_optional.h" +#include "../filters/mod_include.h" #include #ifdef HAVE_SYS_SOCKET_H #include @@ -108,6 +110,12 @@ module AP_MODULE_DECLARE_DATA cgid_module; static void cgid_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_server); +static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r, + ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head); + +static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgid_pfn_reg_with_ssi; +static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgid_pfn_gtv; +static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgid_pfn_ps; static apr_pool_t *pcgi; static int total_modules = 0; @@ -132,6 +140,9 @@ static int is_scriptaliased(request_rec *r) #define SHELL_PATH "/bin/sh" +#define CGI_REQ 1 +#define SSI_REQ 2 + /* DEFAULT_CGID_LISTENBACKLOG controls the max depth on the unix socket's * pending connection queue. If a bunch of cgi requests arrive at about * the same time, connections from httpd threads/processes will back up @@ -228,7 +239,7 @@ static void cgid_maint(int reason, void *data, apr_wait_t status) #endif } -static void get_req(int fd, request_rec *r, char **filename, char **argv0, char ***env) +static void get_req(int fd, request_rec *r, char **filename, char **argv0, char ***env, int *req_type) { int i, len, j; unsigned char *data; @@ -239,6 +250,7 @@ static void get_req(int fd, request_rec *r, char **filename, char **argv0, char r->server = apr_pcalloc(r->pool, sizeof(server_rec)); + read(fd, req_type, sizeof(int)); read(fd, &j, sizeof(int)); read(fd, &len, sizeof(int)); data = apr_pcalloc(r->pool, len + 1); /* get a cleared byte for final '\0' */ @@ -321,9 +333,9 @@ static void get_req(int fd, request_rec *r, char **filename, char **argv0, char -static void send_req(int fd, request_rec *r, char *argv0, char **env) +static void send_req(int fd, request_rec *r, char *argv0, char **env, int req_type) { - int len; + int len, r_type = req_type; int i = 0; char *data; module *suexec_mod = ap_find_linked_module("mod_suexec.c"); @@ -335,6 +347,13 @@ static void send_req(int fd, request_rec *r, char *argv0, char **env) continue; } + /* Write the request type (SSI "exec cmd" or cgi). */ + if (write(fd, &r_type, sizeof(int)) < 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, + "write to cgi daemon process"); + } + + /* Write the number of entries in the environment. */ if (write(fd, &i, sizeof(int)) < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, "write to cgi daemon process"); @@ -345,18 +364,21 @@ static void send_req(int fd, request_rec *r, char *argv0, char **env) } data = apr_pstrcat(r->pool, data, r->args, NULL); len = strlen(data); + /* Write the length of the concatenated env string. */ if (write(fd, &len, sizeof(int)) < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, "write to cgi daemon process"); - } + } + /* Write the concatted env string. */ if (write(fd, data, len) < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, "write to cgi daemon process"); - } + } + /* Write module_index id value. */ if (write(fd, &core_module.module_index, sizeof(int)) < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, "write to cgi daemon process"); - } + } if (suexec_mod) { suexec_config_t *suexec_cfg = ap_get_module_config(r->per_dir_config, suexec_mod); @@ -409,7 +431,7 @@ static void send_req(int fd, request_rec *r, char *argv0, char **env) static int cgid_server(void *data) { struct sockaddr_un unix_addr; - int sd, sd2, rc; + int sd, sd2, rc, req_type; mode_t omask; apr_socklen_t len; server_rec *main_server = data; @@ -466,6 +488,10 @@ static int cgid_server(void *data) char *filename; char **env; const char * const *argv; + apr_int32_t in_pipe = APR_CHILD_BLOCK; + apr_int32_t out_pipe = APR_CHILD_BLOCK; + apr_int32_t err_pipe = APR_CHILD_BLOCK; + apr_cmdtype_e cmd_type = APR_PROGRAM; apr_pool_t *p; request_rec *r; apr_procattr_t *procattr = NULL; @@ -489,21 +515,29 @@ static int cgid_server(void *data) r = apr_pcalloc(p, sizeof(request_rec)); procnew = apr_pcalloc(p, sizeof(*procnew)); r->pool = p; - get_req(sd2, r, &filename, &argv0, &env); + get_req(sd2, r, &filename, &argv0, &env, &req_type); apr_put_os_file(&r->server->error_log, &errfileno, r->pool); apr_put_os_file(&inout, &sd2, r->pool); + if (req_type == SSI_REQ) { + in_pipe = APR_NO_PIPE; + out_pipe = APR_FULL_BLOCK; + err_pipe = APR_NO_PIPE; + cmd_type = APR_SHELLCMD; + } + if (((rc = apr_createprocattr_init(&procattr, p)) != APR_SUCCESS) || - ((rc = apr_setprocattr_io(procattr, - APR_CHILD_BLOCK, - APR_CHILD_BLOCK, - APR_CHILD_BLOCK)) != APR_SUCCESS) || - ((rc = apr_setprocattr_childin(procattr, inout, NULL)) != APR_SUCCESS) || + ((req_type == CGI_REQ) && + (((rc = apr_setprocattr_io(procattr, + in_pipe, + out_pipe, + err_pipe)) != APR_SUCCESS) || + ((rc = apr_setprocattr_childerr(procattr, r->server->error_log, NULL)) != APR_SUCCESS) || + ((rc = apr_setprocattr_childin(procattr, inout, NULL)) != APR_SUCCESS))) || ((rc = apr_setprocattr_childout(procattr, inout, NULL)) != APR_SUCCESS) || - ((rc = apr_setprocattr_childerr(procattr, r->server->error_log, NULL)) != APR_SUCCESS) || ((rc = apr_setprocattr_dir(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS) || - ((rc = apr_setprocattr_cmdtype(procattr, APR_PROGRAM)) != APR_SUCCESS)) { + ((rc = apr_setprocattr_cmdtype(procattr, cmd_type)) != APR_SUCCESS)) { /* Something bad happened, tell the world. */ ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, "couldn't set child process attributes: %s", r->filename); @@ -565,6 +599,17 @@ static void cgid_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, #if APR_HAS_OTHER_CHILD apr_register_other_child(procnew, cgid_maint, &procnew->pid, NULL, p); #endif + + cgid_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler); + cgid_pfn_gtv = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value); + cgid_pfn_ps = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string); + + if ((cgid_pfn_reg_with_ssi) && (cgid_pfn_gtv) && (cgid_pfn_ps)) { + /* Required by mod_include filter. This is how mod_cgid registers + * with mod_include to provide processing of the exec directive. + */ + cgid_pfn_reg_with_ssi("exec", handle_exec); + } } } @@ -847,7 +892,7 @@ static int cgid_handler(request_rec *r) "unable to connect to cgi daemon"); } - send_req(sd, r, argv0, env); + send_req(sd, r, argv0, env, CGI_REQ); /* We are putting the tempsock variable into a file so that we can use * a pipe bucket to send the data to the client. @@ -964,6 +1009,254 @@ static int cgid_handler(request_rec *r) return OK; /* NOT r->status, even if it has changed. */ } + + + +/*============================================================================ + *============================================================================ + * This is the beginning of the cgi filter code moved from mod_include. This + * is the code required to handle the "exec" SSI directive. + *============================================================================ + *============================================================================*/ +static int include_cgi(char *s, request_rec *r, ap_filter_t *next, + apr_bucket *head_ptr, apr_bucket **inserted_head) +{ + request_rec *rr = ap_sub_req_lookup_uri(s, r, next); + int rr_status; + apr_bucket *tmp_buck, *tmp2_buck; + + if (rr->status != HTTP_OK) { + return -1; + } + + /* No hardwired path info or query allowed */ + + if ((rr->path_info && rr->path_info[0]) || rr->args) { + return -1; + } + if (rr->finfo.protection == 0) { + return -1; + } + + /* Script gets parameters of the *document*, for back compatibility */ + + rr->path_info = r->path_info; /* hard to get right; see mod_cgi.c */ + rr->args = r->args; + + /* Force sub_req to be treated as a CGI request, even if ordinary + * typing rules would have called it something else. + */ + + rr->content_type = CGI_MAGIC_TYPE; + + /* Run it. */ + + rr_status = ap_run_sub_req(rr); + if (ap_is_HTTP_REDIRECT(rr_status)) { + apr_size_t len_loc, h_wrt; + const char *location = apr_table_get(rr->headers_out, "Location"); + + location = ap_escape_html(rr->pool, location); + len_loc = strlen(location); + + tmp_buck = apr_bucket_create_immortal("", sizeof("\">")); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck); + tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck); + tmp2_buck = apr_bucket_create_immortal("", sizeof("")); + APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck); + + if (*inserted_head == NULL) { + *inserted_head = tmp_buck; + } + } + + ap_destroy_sub_req(rr); + + return 0; +} + + +/* This is the special environment used for running the "exec cmd=" + * variety of SSI directives. + */ +static void add_ssi_vars(request_rec *r, ap_filter_t *next) +{ + apr_table_t *e = r->subprocess_env; + + if (r->path_info && r->path_info[0] != '\0') { + request_rec *pa_req; + + apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info)); + + pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, next); + if (pa_req->filename) { + apr_table_setn(e, "PATH_TRANSLATED", + apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info, NULL)); + } + } + + if (r->args) { + char *arg_copy = apr_pstrdup(r->pool, r->args); + + apr_table_setn(e, "QUERY_STRING", r->args); + ap_unescape_url(arg_copy); + apr_table_setn(e, "QUERY_STRING_UNESCAPED", ap_escape_shell_cmd(r->pool, arg_copy)); + } +} + +static int include_cmd(include_ctx_t *ctx, apr_bucket_brigade **bb, char *command, + request_rec *r, ap_filter_t *f) +{ + char **env; + const char *location; + int sd; + int retval; + apr_bucket_brigade *bcgi; + apr_bucket *b; + struct sockaddr_un unix_addr; + apr_file_t *tempsock = NULL; + void *sconf = r->server->module_config; + cgid_server_conf *conf = (cgid_server_conf *) ap_get_module_config(sconf, &cgid_module); + + add_ssi_vars(r, f->next); + env = ap_create_environment(r->pool, r->subprocess_env); + + if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + return log_scripterror(r, conf, HTTP_INTERNAL_SERVER_ERROR, 0, + "unable to create socket to cgi daemon"); + } + + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + strcpy(unix_addr.sun_path, conf->sockname); + + if (connect(sd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0) { + return log_scripterror(r, conf, HTTP_INTERNAL_SERVER_ERROR, 0, + "unable to connect to cgi daemon"); + } + + SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next); + + send_req(sd, r, command, env, SSI_REQ); + + /* We are putting the tempsock variable into a file so that we can use + * a pipe bucket to send the data to the client. + */ + apr_put_os_file(&tempsock, &sd, r->pool); + + if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) + return retval; + + location = apr_table_get(r->headers_out, "Location"); + + if (location && location[0] == '/' && r->status == 200) { + char argsbuffer[HUGE_STRING_LEN]; + + /* Soak up all the script output */ + while (apr_fgets(argsbuffer, HUGE_STRING_LEN, tempsock) > 0) { + continue; + } + /* This redirect needs to be a GET no matter what the original + * method was. + */ + r->method = apr_pstrdup(r->pool, "GET"); + r->method_number = M_GET; + + /* We already read the message body (if any), so don't allow + * the redirected request to think it has one. We can ignore + * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. + */ + apr_table_unset(r->headers_in, "Content-Length"); + + ap_internal_redirect_handler(location, r); + return OK; + } + else if (location && r->status == 200) { + /* XX Note that if a script wants to produce its own Redirect + * body, it now has to explicitly *say* "Status: 302" + */ + return HTTP_MOVED_TEMPORARILY; + } + + ap_send_http_header(r); + if (!r->header_only) { + bcgi = apr_brigade_create(r->pool); + b = apr_bucket_create_pipe(tempsock); + APR_BRIGADE_INSERT_TAIL(bcgi, b); + ap_pass_brigade(f->next, bcgi); + } + + return 0; +} + +static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r, + ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head) +{ + char *tag = NULL; + char *tag_val = NULL; + char *file = r->filename; + apr_bucket *tmp_buck; + char parsed_string[MAX_STRING_LEN]; + + *inserted_head = NULL; + if (ctx->flags & FLAG_PRINTING) { + if (ctx->flags & FLAG_NO_EXEC) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "exec used but not allowed in %s", r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + else { + while (1) { + cgid_pfn_gtv(ctx, &tag, &tag_val, 1); + if (tag_val == NULL) { + if (tag == NULL) { + return (0); + } + else { + return 1; + } + } + if (!strcmp(tag, "cmd")) { + cgid_pfn_ps(r, tag_val, parsed_string, sizeof(parsed_string), 1); + if (include_cmd(ctx, bb, parsed_string, r, f) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "execution failure for parameter \"%s\" " + "to tag exec in file %s", tag, r->filename); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + /* just in case some stooge changed directories */ + } + else if (!strcmp(tag, "cgi")) { + cgid_pfn_ps(r, tag_val, parsed_string, sizeof(parsed_string), 0); + SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next); + if (include_cgi(parsed_string, r, f->next, head_ptr, inserted_head) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "invalid CGI ref \"%s\" in %s", tag_val, file); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + } + else { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "unknown parameter \"%s\" to tag exec in %s", tag, file); + CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head); + } + } + } + } + return 0; +} +/*============================================================================ + *============================================================================ + * This is the end of the cgi filter code moved from mod_include. + *============================================================================ + *============================================================================*/ + + static void register_hook(apr_pool_t *p) { ap_hook_post_config(cgid_init, NULL, NULL, APR_HOOK_MIDDLE); -- 2.50.1