X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=server%2Futil_script.c;h=703d16009472b34d73e0b36e64c9c9d63380dfa9;hb=36ab7eb1eb554444ac1d6c06d1988ed33857b6f7;hp=f7788926b301996720be6c36b7ca95a926e46d4e;hpb=c76dd4ecefc5c521a70c2a26f3aa52eb85472961;p=apache diff --git a/server/util_script.c b/server/util_script.c index f7788926b3..703d160094 100644 --- a/server/util_script.c +++ b/server/util_script.c @@ -1,8 +1,9 @@ -/* Copyright 1999-2004 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -24,7 +25,6 @@ #include #endif -#define CORE_PRIVATE #include "ap_config.h" #include "httpd.h" #include "http_config.h" @@ -49,12 +49,13 @@ * where there don't have to be). */ -#define MALFORMED_MESSAGE "malformed header from script. Bad header=" -#define MALFORMED_HEADER_LENGTH_TO_SHOW 30 +/* we know core's module_index is 0 */ +#undef APLOG_MODULE_INDEX +#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX -static char *http2env(apr_pool_t *a, const char *w) +static char *http2env(request_rec *r, const char *w) { - char *res = (char *)apr_palloc(a, sizeof("HTTP_") + strlen(w)); + char *res = (char *)apr_palloc(r->pool, sizeof("HTTP_") + strlen(w)); char *cp = res; char c; @@ -65,18 +66,36 @@ static char *http2env(apr_pool_t *a, const char *w) *cp++ = '_'; while ((c = *w++) != 0) { - if (!apr_isalnum(c)) { + if (apr_isalnum(c)) { + *cp++ = apr_toupper(c); + } + else if (c == '-') { *cp++ = '_'; } else { - *cp++ = apr_toupper(c); + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "Not exporting header with invalid name as envvar: %s", + ap_escape_logitem(r->pool, w)); + return NULL; } } *cp = 0; - + return res; } +static void add_unless_null(apr_table_t *table, const char *name, const char *val) +{ + if (name && val) { + apr_table_addn(table, name, val); + } +} + +static void env2env(apr_table_t *table, const char *name) +{ + add_unless_null(table, name, getenv(name)); +} + AP_DECLARE(char **) ap_create_environment(apr_pool_t *p, apr_table_t *t) { const apr_array_header_t *env_arr = apr_table_elts(t); @@ -120,16 +139,12 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r) apr_table_t *e; server_rec *s = r->server; conn_rec *c = r->connection; - const char *rem_logname; - char *env_path; -#if defined(WIN32) || defined(OS2) || defined(BEOS) - char *env_temp; -#endif - const char *host; + const char *env_temp; const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in); const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts; int i; apr_port_t rport; + char *q; /* use a temporary apr_table_t which we'll overlap onto * r->subprocess_env later @@ -170,74 +185,73 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r) * in the environment with "ps -e". But, if you must... */ #ifndef SECURITY_HOLE_PASS_AUTHORIZATION - else if (!strcasecmp(hdrs[i].key, "Authorization") + else if (!strcasecmp(hdrs[i].key, "Authorization") || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) { continue; } #endif - else { - apr_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val); - } - } - - if (!(env_path = getenv("PATH"))) { - env_path = DEFAULT_PATH; - } - apr_table_addn(e, "PATH", apr_pstrdup(r->pool, env_path)); - -#ifdef WIN32 - if (env_temp = getenv("SystemRoot")) { - apr_table_addn(e, "SystemRoot", env_temp); - } - if (env_temp = getenv("COMSPEC")) { - apr_table_addn(e, "COMSPEC", env_temp); - } - if (env_temp = getenv("PATHEXT")) { - apr_table_addn(e, "PATHEXT", env_temp); - } - if (env_temp = getenv("WINDIR")) { - apr_table_addn(e, "WINDIR", env_temp); - } -#endif - -#ifdef OS2 - if ((env_temp = getenv("COMSPEC")) != NULL) { - apr_table_addn(e, "COMSPEC", env_temp); - } - if ((env_temp = getenv("ETC")) != NULL) { - apr_table_addn(e, "ETC", env_temp); - } - if ((env_temp = getenv("DPATH")) != NULL) { - apr_table_addn(e, "DPATH", env_temp); - } - if ((env_temp = getenv("PERLLIB_PREFIX")) != NULL) { - apr_table_addn(e, "PERLLIB_PREFIX", env_temp); - } -#endif - -#ifdef BEOS - if ((env_temp = getenv("LIBRARY_PATH")) != NULL) { - apr_table_addn(e, "LIBRARY_PATH", env_temp); - } + else + add_unless_null(e, http2env(r, hdrs[i].key), hdrs[i].val); + } + + env_temp = apr_table_get(r->subprocess_env, "PATH"); + if (env_temp == NULL) { + env_temp = getenv("PATH"); + } + if (env_temp == NULL) { + env_temp = DEFAULT_PATH; + } + apr_table_addn(e, "PATH", apr_pstrdup(r->pool, env_temp)); + +#if defined(WIN32) + env2env(e, "SystemRoot"); + env2env(e, "COMSPEC"); + env2env(e, "PATHEXT"); + env2env(e, "WINDIR"); +#elif defined(OS2) + env2env(e, "COMSPEC"); + env2env(e, "ETC"); + env2env(e, "DPATH"); + env2env(e, "PERLLIB_PREFIX"); +#elif defined(BEOS) + env2env(e, "LIBRARY_PATH"); +#elif defined(DARWIN) + env2env(e, "DYLD_LIBRARY_PATH"); +#elif defined(_AIX) + env2env(e, "LIBPATH"); +#elif defined(__HPUX__) + /* HPUX PARISC 2.0W knows both, otherwise redundancy is harmless */ + env2env(e, "SHLIB_PATH"); + env2env(e, "LD_LIBRARY_PATH"); +#else /* Some Unix */ + env2env(e, "LD_LIBRARY_PATH"); #endif apr_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r)); - apr_table_addn(e, "SERVER_SOFTWARE", ap_get_server_version()); + apr_table_addn(e, "SERVER_SOFTWARE", ap_get_server_banner()); apr_table_addn(e, "SERVER_NAME", - ap_escape_html(r->pool, ap_get_server_name(r))); + ap_escape_html(r->pool, ap_get_server_name_for_url(r))); apr_table_addn(e, "SERVER_ADDR", r->connection->local_ip); /* Apache */ apr_table_addn(e, "SERVER_PORT", apr_psprintf(r->pool, "%u", ap_get_server_port(r))); - host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST, NULL); - if (host) { - apr_table_addn(e, "REMOTE_HOST", host); - } - apr_table_addn(e, "REMOTE_ADDR", c->remote_ip); + add_unless_null(e, "REMOTE_HOST", + ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST, NULL)); + apr_table_addn(e, "REMOTE_ADDR", r->useragent_ip); apr_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r)); /* Apache */ + apr_table_setn(e, "REQUEST_SCHEME", ap_http_scheme(r)); + apr_table_addn(e, "CONTEXT_PREFIX", ap_context_prefix(r)); + apr_table_addn(e, "CONTEXT_DOCUMENT_ROOT", ap_context_document_root(r)); apr_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */ - apr_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */ + if (apr_table_get(r->notes, "proxy-noquery") && (q = ap_strchr(r->filename, '?'))) { + *q = '\0'; + apr_table_addn(e, "SCRIPT_FILENAME", apr_pstrdup(r->pool, r->filename)); + *q = '?'; + } + else { + apr_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */ + } - rport = c->remote_addr->port; + rport = c->client_addr->port; apr_table_addn(e, "REMOTE_PORT", apr_itoa(r->pool, rport)); if (r->user) { @@ -254,23 +268,17 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r) back = back->prev; } } - if (r->ap_auth_type) { - apr_table_addn(e, "AUTH_TYPE", r->ap_auth_type); - } - rem_logname = ap_get_remote_logname(r); - if (rem_logname) { - apr_table_addn(e, "REMOTE_IDENT", apr_pstrdup(r->pool, rem_logname)); + add_unless_null(e, "AUTH_TYPE", r->ap_auth_type); + env_temp = ap_get_remote_logname(r); + if (env_temp) { + apr_table_addn(e, "REMOTE_IDENT", apr_pstrdup(r->pool, env_temp)); } /* Apache custom error responses. If we have redirected set two new vars */ if (r->prev) { - if (r->prev->args) { - apr_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args); - } - if (r->prev->uri) { - apr_table_addn(e, "REDIRECT_URL", r->prev->uri); - } + add_unless_null(e, "REDIRECT_QUERY_STRING", r->prev->args); + add_unless_null(e, "REDIRECT_URL", r->prev->uri); } if (e != r->subprocess_env) { @@ -288,7 +296,11 @@ AP_DECLARE(int) ap_find_path_info(const char *uri, const char *path_info) int lu = strlen(uri); int lp = strlen(path_info); - while (lu-- && lp-- && uri[lu] == path_info[lp]); + while (lu-- && lp-- && uri[lu] == path_info[lp]) { + if (path_info[lp] == '/') { + while (lu && uri[lu-1] == '/') lu--; + } + } if (lu == -1) { lu = 0; @@ -336,7 +348,7 @@ AP_DECLARE(void) ap_add_cgi_vars(request_rec *r) apr_table_setn(e, "SERVER_PROTOCOL", r->protocol); apr_table_setn(e, "REQUEST_METHOD", r->method); apr_table_setn(e, "QUERY_STRING", r->args ? r->args : ""); - apr_table_setn(e, "REQUEST_URI", original_uri(r)); + apr_table_setn(e, "REQUEST_URI", original_uri(r)); /* Note that the code below special-cases scripts run from includes, * because it "knows" that the sub_request has been hacked to have the @@ -393,16 +405,22 @@ static int set_cookie_doo_doo(void *v, const char *key, const char *val) return 1; } -AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, +#define HTTP_UNSET (-HTTP_OK) +#define SCRIPT_LOG_MARK __FILE__,__LINE__,module_index + +AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer, int (*getsfunc) (char *, int, void *), - void *getsfunc_data) + void *getsfunc_data, + int module_index) { char x[MAX_STRING_LEN]; char *w, *l; int p; - int cgi_status = HTTP_OK; + int cgi_status = HTTP_UNSET; apr_table_t *merge; apr_table_t *cookie_table; + int trace_log = APLOG_R_MODULE_IS_LEVEL(r, module_index, APLOG_TRACE1); + int first_header = 1; if (buffer) { *buffer = '\0'; @@ -423,12 +441,22 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, while (1) { - if ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data) == 0) { - ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, - "Premature end of script headers: %s", + int rv = (*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data); + if (rv == 0) { + const char *msg = "Premature end of script headers"; + if (first_header) + msg = "End of script output before headers"; + ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, + "%s: %s", msg, apr_filepath_name_get(r->filename)); return HTTP_INTERNAL_SERVER_ERROR; } + else if (rv == -1) { + ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, + "Script timed out before returning headers: %s", + apr_filepath_name_get(r->filename)); + return HTTP_GATEWAY_TIME_OUT; + } /* Delete terminal (CR?)LF */ @@ -436,7 +464,7 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, /* Indeed, the host's '\n': '\012' for UNIX; '\015' for MacOS; '\025' for OS/390 -- whatever the script generates. - */ + */ if (p > 0 && w[p - 1] == '\n') { if (p > 1 && w[p - 2] == CR) { w[p - 2] = '\0'; @@ -461,7 +489,18 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, if (w[0] == '\0') { int cond_status = OK; - if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) { + /* PR#38070: This fails because it gets confused when a + * CGI Status header overrides ap_meets_conditions. + * + * We can fix that by dropping ap_meets_conditions when + * Status has been set. Since this is the only place + * cgi_status gets used, let's test it explicitly. + * + * The alternative would be to ignore CGI Status when + * ap_meets_conditions returns anything interesting. + * That would be safer wrt HTTP, but would break CGI. + */ + if ((cgi_status == HTTP_UNSET) && (r->method_number == M_GET)) { cond_status = ap_meets_conditions(r); } apr_table_overlap(r->err_headers_out, merge, @@ -475,6 +514,14 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, return cond_status; } + if (trace_log) { + if (first_header) + ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE4, 0, r, + "Headers from script '%s':", + apr_filepath_name_get(r->filename)); + ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE4, 0, r, " %s", w); + } + /* if we see a bogus header don't ignore it. Shout and scream */ #if APR_CHARSET_EBCDIC @@ -494,7 +541,7 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, ++maybeASCII; } if (maybeASCII > maybeEBCDIC) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + ap_log_error(SCRIPT_LOG_MARK, APLOG_ERR, 0, r->server, "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)", r->filename); inbytes_left = outbytes_left = cp - w; @@ -504,22 +551,16 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, } #endif /*APR_CHARSET_EBCDIC*/ if (!(l = strchr(w, ':'))) { - char malformed[(sizeof MALFORMED_MESSAGE) + 1 - + MALFORMED_HEADER_LENGTH_TO_SHOW]; - - strcpy(malformed, MALFORMED_MESSAGE); - strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW); - if (!buffer) { /* Soak up all the script output - may save an outright kill */ - while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) { + while ((*getsfunc)(w, MAX_STRING_LEN - 1, getsfunc_data) > 0) { continue; } } - ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, - "%s: %s", malformed, - apr_filepath_name_get(r->filename)); + ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, + "malformed header from script '%s': Bad header: %.30s", + apr_filepath_name_get(r->filename), w); return HTTP_INTERNAL_SERVER_ERROR; } @@ -548,6 +589,14 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, */ else if (!strcasecmp(w, "Status")) { r->status = cgi_status = atoi(l); + if (!ap_is_HTTP_VALID_RESPONSE(cgi_status)) + ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, + "Invalid status line from script '%s': %s", + apr_filepath_name_get(r->filename), l); + else + ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE1, 0, r, + "Status line from script '%s': %s", + apr_filepath_name_get(r->filename), l); r->status_line = apr_pstrdup(r->pool, l); } else if (!strcasecmp(w, "Location")) { @@ -562,6 +611,9 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, else if (!strcasecmp(w, "Transfer-Encoding")) { apr_table_set(r->headers_out, w, l); } + else if (!strcasecmp(w, "ETag")) { + apr_table_set(r->headers_out, w, l); + } /* * If the script gave us a Last-Modified header, we can't just * pass it on blindly because of restrictions on future values. @@ -576,11 +628,21 @@ AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, else { apr_table_add(merge, w, l); } + first_header = 0; } - + /* never reached - we leave this function within the while loop above */ return OK; } +AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, + int (*getsfunc) (char *, int, void *), + void *getsfunc_data) +{ + return ap_scan_script_header_err_core_ex(r, buffer, getsfunc, + getsfunc_data, + APLOG_MODULE_INDEX); +} + static int getsfunc_FILE(char *buf, int len, void *f) { return apr_file_gets(buf, len, (apr_file_t *) f) == APR_SUCCESS; @@ -589,9 +651,18 @@ static int getsfunc_FILE(char *buf, int len, void *f) AP_DECLARE(int) ap_scan_script_header_err(request_rec *r, apr_file_t *f, char *buffer) { - return ap_scan_script_header_err_core(r, buffer, getsfunc_FILE, f); + return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_FILE, f, + APLOG_MODULE_INDEX); +} + +AP_DECLARE(int) ap_scan_script_header_err_ex(request_rec *r, apr_file_t *f, + char *buffer, int module_index) +{ + return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_FILE, f, + module_index); } + static int getsfunc_BRIGADE(char *buf, int len, void *arg) { apr_bucket_brigade *bb = (apr_bucket_brigade *)arg; @@ -601,7 +672,8 @@ static int getsfunc_BRIGADE(char *buf, int len, void *arg) apr_status_t rv; int done = 0; - while ((dst < dst_end) && !done && !APR_BUCKET_IS_EOS(e)) { + while ((dst < dst_end) && !done && e != APR_BRIGADE_SENTINEL(bb) + && !APR_BUCKET_IS_EOS(e)) { const char *bucket_data; apr_size_t bucket_data_len; const char *src; @@ -611,7 +683,8 @@ static int getsfunc_BRIGADE(char *buf, int len, void *arg) rv = apr_bucket_read(e, &bucket_data, &bucket_data_len, APR_BLOCK_READ); if (rv != APR_SUCCESS || (bucket_data_len == 0)) { - return 0; + *dst = '\0'; + return APR_STATUS_IS_TIMEUP(rv) ? -1 : 0; } src = bucket_data; src_end = bucket_data + bucket_data_len; @@ -634,16 +707,27 @@ static int getsfunc_BRIGADE(char *buf, int len, void *arg) e = next; } *dst = 0; - return 1; + return done; } AP_DECLARE(int) ap_scan_script_header_err_brigade(request_rec *r, apr_bucket_brigade *bb, char *buffer) { - return ap_scan_script_header_err_core(r, buffer, getsfunc_BRIGADE, bb); + return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_BRIGADE, bb, + APLOG_MODULE_INDEX); +} + +AP_DECLARE(int) ap_scan_script_header_err_brigade_ex(request_rec *r, + apr_bucket_brigade *bb, + char *buffer, + int module_index) +{ + return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_BRIGADE, bb, + module_index); } + struct vastrs { va_list args; int arg; @@ -655,9 +739,11 @@ static int getsfunc_STRING(char *w, int len, void *pvastrs) struct vastrs *strs = (struct vastrs*) pvastrs; const char *p; int t; - - if (!strs->curpos || !*strs->curpos) + + if (!strs->curpos || !*strs->curpos) { + w[0] = '\0'; return 0; + } p = ap_strchr_c(strs->curpos, '\n'); if (p) ++p; @@ -674,7 +760,7 @@ static int getsfunc_STRING(char *w, int len, void *pvastrs) } else strs->curpos += t; - return t; + return t; } /* ap_scan_script_header_err_strs() accepts additional const char* args... @@ -682,8 +768,30 @@ static int getsfunc_STRING(char *w, int len, void *pvastrs) * character is returned to **arg, **data. (The first optional arg is * counted as 0.) */ -AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r, - char *buffer, +AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs_ex(request_rec *r, + char *buffer, + int module_index, + const char **termch, + int *termarg, ...) +{ + struct vastrs strs; + int res; + + va_start(strs.args, termarg); + strs.arg = 0; + strs.curpos = va_arg(strs.args, char*); + res = ap_scan_script_header_err_core_ex(r, buffer, getsfunc_STRING, + (void *) &strs, module_index); + if (termch) + *termch = strs.curpos; + if (termarg) + *termarg = strs.arg; + va_end(strs.args); + return res; +} + +AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r, + char *buffer, const char **termch, int *termarg, ...) { @@ -693,7 +801,8 @@ AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r, va_start(strs.args, termarg); strs.arg = 0; strs.curpos = va_arg(strs.args, char*); - res = ap_scan_script_header_err_core(r, buffer, getsfunc_STRING, (void *) &strs); + res = ap_scan_script_header_err_core_ex(r, buffer, getsfunc_STRING, + (void *) &strs, APLOG_MODULE_INDEX); if (termch) *termch = strs.curpos; if (termarg) @@ -701,3 +810,38 @@ AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r, va_end(strs.args); return res; } + +static void +argstr_to_table(char *str, apr_table_t *parms) +{ + char *key; + char *value; + char *strtok_state; + + if (str == NULL) { + return; + } + + key = apr_strtok(str, "&", &strtok_state); + while (key) { + value = strchr(key, '='); + if (value) { + *value = '\0'; /* Split the string in two */ + value++; /* Skip passed the = */ + } + else { + value = "1"; + } + ap_unescape_url(key); + ap_unescape_url(value); + apr_table_set(parms, key, value); + key = apr_strtok(NULL, "&", &strtok_state); + } +} + +AP_DECLARE(void) ap_args_to_table(request_rec *r, apr_table_t **table) +{ + apr_table_t *t = apr_table_make(r->pool, 10); + argstr_to_table(apr_pstrdup(r->pool, r->args), t); + *table = t; +}