From: Shane Caraveo Date: Sat, 25 Jan 2003 22:01:53 +0000 (+0000) Subject: fix bug 21261 X-Git-Tag: PHP_5_0_dev_before_13561_fix~35 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1a4c82e0149351c530e6196b548b1cf2a95e4410;p=php fix bug 21261 putenv on some platforms requires we provide the memory cleanup env parsing logic fix bug 21367 missed optargs in a previous patch --- diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index 161835cd2f..19f623f389 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -137,7 +137,7 @@ long fix_pathinfo=1; #define TRANSLATE_SLASHES(path) #endif -#define OPTSTRING "aCc:d:ef:g:hilmnqsw?vz:" +#define OPTSTRING "abCc:d:ef:g:hilmnqsw?vz:" static int print_module_info(zend_module_entry *module, void *arg TSRMLS_DC) { @@ -360,11 +360,11 @@ static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC) return getenv(name); } -static int _sapi_cgibin_putenv(char *name, char *value TSRMLS_DC) +static char *_sapi_cgibin_putenv(char *name, char *value TSRMLS_DC) { int len=0; char *buf = NULL; - if (!name) return -1; + if (!name) return NULL; len = strlen(name) + (value?strlen(value):0) + sizeof("=") + 2; buf = (char *)emalloc(len); if (value) { @@ -372,7 +372,6 @@ static int _sapi_cgibin_putenv(char *name, char *value TSRMLS_DC) } else { snprintf(buf,len-1,"%s=", name); } - #if PHP_FASTCGI /* when php is started by mod_fastcgi, no regular environment is provided to PHP. It is always sent to PHP at the start @@ -382,14 +381,20 @@ static int _sapi_cgibin_putenv(char *name, char *value TSRMLS_DC) FCGX_Request *request = (FCGX_Request *)SG(server_context); FCGX_PutEnv(request,buf); efree(buf); - return 0; + return sapi_cgibin_getenv(name,0 TSRMLS_CC); } #endif /* if cgi, or fastcgi and not found in fcgi env - check the regular environment */ - putenv(buf); + check the regular environment + this leaks, but it's only cgi anyway, we'll fix + it for 5.0 + */ + if (value) + putenv(strdup(buf)); + else + putenv(buf); efree(buf); - return 0; + return getenv(name); } static char *sapi_cgi_read_cookies(TSRMLS_D) @@ -535,94 +540,159 @@ static void php_cgi_usage(char *argv0) /* }}} */ /* {{{ init_request_info + + initializes request_info structure + + specificly in this section we handle proper translations + for: + + PATH_INFO + derived from the portion of the URI path following + the script name but preceding any query data + may be empty + + PATH_TRANSLATED + derived by taking any path-info component of the + request URI and performing any virtual-to-physical + translation appropriate to map it onto the server's + document repository structure + + empty if PATH_INFO is empty + + The env var PATH_TRANSLATED **IS DIFFERENT** than the + request_info.path_translated variable, the latter should + match SCRIPT_FILENAME instead. + + SCRIPT_NAME + set to a URL path that could identify the CGI script + rather than the interpreter. PHP_SELF is set to this. + + REQUEST_URI + uri section following the domain:port part of a URI + + SCRIPT_FILENAME + The virtual-to-physical translation of SCRIPT_NAME (as per + PATH_TRANSLATED) + + These settings are documented at + http://cgi-spec.golux.com/ + + + Based on the following URL request: + + http://localhost/info.php/test?a=b + + should produce, which btw is the same as if + we were running under mod_cgi on apache (ie. not + using ScriptAlias directives): + + PATH_INFO=/test + PATH_TRANSLATED=/docroot/test + SCRIPT_NAME=/info.php + REQUEST_URI=/info.php/test?a=b + SCRIPT_FILENAME=/docroot/info.php + QUERY_STRING=a=b + + but what we get is (cgi/mod_fastcgi under apache): + + PATH_INFO=/info.php/test + PATH_TRANSLATED=/docroot/info.php/test + SCRIPT_NAME=/php/php-cgi (from the Action setting I suppose) + REQUEST_URI=/info.php/test?a=b + SCRIPT_FILENAME=/path/to/php/bin/php-cgi (Action setting translated) + QUERY_STRING=a=b + + Comments in the code below refer to using the above URL in a request + */ static void init_request_info(TSRMLS_D) { - char *content_length = sapi_cgibin_getenv("CONTENT_LENGTH",0 TSRMLS_CC); - char *content_type = sapi_cgibin_getenv("CONTENT_TYPE",0 TSRMLS_CC); - const char *auth; - char *env_path_translated; - - SG(request_info).path_translated = NULL; - /* - * If for some reason the CGI interface is not setting the - * PATH_TRANSLATED correctly, SG(request_info).path_translated is NULL. - * We still call php_fopen_primary_script, because if you set doc_root - * or user_dir configuration directives, SCRIPT_NAME is used to construct - * the filename as a side effect of php_fopen_primary_script. - * - * Fixup path stuff to conform to CGI spec - * - * http://localhost/info.php/test?a=b - * - * should produce, which btw is the same as if - * we were running under mod_cgi on apache (ie. not - * using ScriptAlias directives): - * - * PATH_INFO=/test - * PATH_TRANSLATED=/docroot/test - * SCRIPT_NAME=/info.php - * REQUEST_URI=/info.php/test?a=b - * SCRIPT_FILENAME=/docroot/info.php - * QUERY_STRING=a=b - * - * cgi/mod_fastcgi under apache produce: - * - * PATH_INFO=/info.php/test - * PATH_TRANSLATED=/docroot/info.php/test - * SCRIPT_NAME=/php/php-cgi (from the Action setting I suppose) - * REQUEST_URI=/info.php/test?a=b - * SCRIPT_FILENAME=/path/to/php/bin/php-cgi (Action setting translated) - * QUERY_STRING=a=b - * - * Comments in the code below refer to using the above URL in a request - * - */ + char *env_script_filename = sapi_cgibin_getenv("SCRIPT_FILENAME",0 TSRMLS_CC); + char *env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED",0 TSRMLS_CC); + char *script_path_translated = env_script_filename; - env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED",0 TSRMLS_CC); + /* some broken servers do not have script_filename or argv0 + an example, IIS configured in some ways. then they do more + broken stuff and set path_translated to the cgi script location */ + if (!script_path_translated && env_path_translated) + script_path_translated = env_path_translated; - if(env_path_translated) { -#ifdef __riscos__ - /* Convert path to unix format*/ - __riscosify_control|=__RISCOSIFY_DONT_CHECK_DIR; - env_path_translated=__unixify(env_path_translated,0,NULL,1,0); -#endif + /* initialize the defaults */ + SG(request_info).path_translated = NULL; + SG(request_info).request_method = NULL; + SG(request_info).query_string = NULL; + SG(request_info).request_uri = NULL; + SG(request_info).content_type = NULL; + SG(request_info).content_length = 0; + SG(sapi_headers).http_response_code = 200; + /* script_path_translated being set is a good indication that + we are running in a cgi environment, since it is always + null otherwise. otherwise, the filename + of the script will be retreived later via argc/argv */ + if (script_path_translated) { + const char *auth; + char *content_length = sapi_cgibin_getenv("CONTENT_LENGTH",0 TSRMLS_CC); + char *content_type = sapi_cgibin_getenv("CONTENT_TYPE",0 TSRMLS_CC); #if ENABLE_PATHINFO_CHECK - /* - * if the file doesn't exist, try to extract PATH_INFO out - * of it by stat'ing back through the '/' - * this fixes url's like /info.php/test - * - * ini cgi.fix_pathinfo is on by default, but can be turned off - * if someone is running a server that does this correctly - */ + struct stat st; + char *env_path_info = sapi_cgibin_getenv("PATH_INFO",0 TSRMLS_CC); + char *env_script_name = sapi_cgibin_getenv("SCRIPT_NAME",0 TSRMLS_CC); + char *env_redirect_url = sapi_cgibin_getenv("REDIRECT_URL",0 TSRMLS_CC); + char *env_document_root = sapi_cgibin_getenv("DOCUMENT_ROOT",0 TSRMLS_CC); + if (fix_pathinfo) { - struct stat st; - char *env_script_name = sapi_cgibin_getenv("SCRIPT_NAME",0 TSRMLS_CC); - char *env_path_info = sapi_cgibin_getenv("PATH_INFO",0 TSRMLS_CC); - char *env_redirect_url = sapi_cgibin_getenv("REDIRECT_URL",0 TSRMLS_CC); - if (env_path_info) env_path_info = estrdup(env_path_info); - - if (env_redirect_url || - (env_script_name && env_path_info && - strcmp(env_path_info,env_script_name)==0)) { - /* - * if PHP is setup under a ScriptAlias in Apache, the - * redirect_url variable will be set. In this case, script_* - * points to the executable, not the script. We have to - * reset this stuff and clear PATH_INFO since it is also wrong. - * This unfortunately is Apache specific. IIS sets PATH_INFO - * and SCRIPT_NAME to the same thing if there is no *real* PATH_INFO. - */ - _sapi_cgibin_putenv("SCRIPT_FILENAME",env_path_translated TSRMLS_CC); - _sapi_cgibin_putenv("PATH_INFO",NULL TSRMLS_CC); - if (env_redirect_url) - _sapi_cgibin_putenv("SCRIPT_NAME",env_redirect_url TSRMLS_CC); + + /* save the originals first for anything we change later */ + if (env_path_translated) + _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED",env_path_translated TSRMLS_CC); + if (env_path_info) + _sapi_cgibin_putenv("ORIG_PATH_INFO",env_path_info TSRMLS_CC); + if (env_script_name) + _sapi_cgibin_putenv("ORIG_SCRIPT_NAME",env_script_name TSRMLS_CC); + if (env_script_filename) + _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME",env_script_filename TSRMLS_CC); + + if (!env_document_root) { + /* IIS version of DOCUMENT_ROOT, not avail in cgi, but is in fastcgi */ + env_document_root = sapi_cgibin_getenv("APPL_PHYSICAL_PATH",0 TSRMLS_CC); + /* ini version of document root */ + if (!env_document_root) + env_document_root = PG(doc_root); + + /* set the document root, this makes a more + consistent env for php scripts */ + if (env_document_root) { + env_document_root = _sapi_cgibin_putenv("DOCUMENT_ROOT",env_document_root TSRMLS_CC); + /* fix docroot */ + TRANSLATE_SLASHES(env_document_root); + } } - if (stat( env_path_translated, &st ) == -1 ) { - char *pt = estrdup(env_path_translated); + if (env_redirect_url) { + /* + pretty much apache specific. If we have a redirect_url + then our script_filename and script_name point to the + php executable + */ + script_path_translated = env_path_translated; + /* we correct SCRIPT_NAME now in case we don't have PATH_INFO */ + env_script_name = _sapi_cgibin_putenv("SCRIPT_NAME",env_redirect_url TSRMLS_CC); + } + +#ifdef __riscos__ + /* Convert path to unix format*/ + __riscosify_control|=__RISCOSIFY_DONT_CHECK_DIR; + script_path_translated=__unixify(script_path_translated,0,NULL,1,0); +#endif + + /* + * if the file doesn't exist, try to extract PATH_INFO out + * of it by stat'ing back through the '/' + * this fixes url's like /info.php/test + */ + if (stat( script_path_translated, &st ) == -1 ) { + char *pt = estrdup(script_path_translated); int len = strlen(pt); char *ptr; @@ -644,24 +714,23 @@ static void init_request_info(TSRMLS_D) * we have to play the game of hide and seek to figure * out what SCRIPT_NAME should be */ - char *env_document_root = sapi_cgibin_getenv("DOCUMENT_ROOT",0 TSRMLS_CC); int slen = len - strlen(pt); int pilen = strlen( env_path_info ); char *path_info = env_path_info + pilen - slen; - _sapi_cgibin_putenv("PATH_INFO",path_info TSRMLS_CC); - _sapi_cgibin_putenv("SCRIPT_FILENAME",pt TSRMLS_CC); + env_path_info = _sapi_cgibin_putenv("PATH_INFO",path_info TSRMLS_CC); + script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME",pt TSRMLS_CC); TRANSLATE_SLASHES(pt); /* figure out docroot SCRIPT_FILENAME minus SCRIPT_NAME */ - if (!env_document_root) - env_document_root = PG(doc_root); + if (env_document_root) { int l = strlen(env_document_root); int path_translated_len = 0; char *path_translated = NULL; + if (env_document_root[l-1]=='/') --l; /* we have docroot, so we should have: * DOCUMENT_ROOT=/docroot @@ -669,17 +738,17 @@ static void init_request_info(TSRMLS_D) * * SCRIPT_NAME is the portion of the path beyond docroot */ - _sapi_cgibin_putenv("SCRIPT_NAME",pt+l TSRMLS_CC); + env_script_name = _sapi_cgibin_putenv("SCRIPT_NAME",pt+l TSRMLS_CC); /* * PATH_TRANSATED = DOCUMENT_ROOT + PATH_INFO */ - path_translated_len = l + strlen(path_info) + 2; + path_translated_len = l + strlen(env_path_info) + 2; path_translated = (char *)emalloc(path_translated_len); *path_translated = 0; - strcat(path_translated,env_document_root); - strcat(path_translated,path_info); - _sapi_cgibin_putenv("PATH_TRANSLATED",path_translated TSRMLS_CC); + strncat(path_translated,env_document_root,l); + strcat(path_translated,env_path_info); + env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED",path_translated TSRMLS_CC); efree(path_translated); } else if (env_script_name && strstr(pt,env_script_name)) { @@ -687,74 +756,49 @@ static void init_request_info(TSRMLS_D) * PATH_TRANSATED = PATH_TRANSATED - SCRIPT_NAME + PATH_INFO */ int ptlen = strlen(pt)-strlen(env_script_name); - int path_translated_len = ptlen + strlen(path_info) + 2; + int path_translated_len = ptlen + strlen(env_path_info) + 2; char *path_translated = NULL; path_translated = (char *)emalloc(path_translated_len); *path_translated = 0; strncat(path_translated,pt,ptlen); - strcat(path_translated,path_info); - _sapi_cgibin_putenv("PATH_TRANSLATED",path_translated TSRMLS_CC); + strcat(path_translated,env_path_info); + env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED",path_translated TSRMLS_CC); efree(path_translated); } break; } } + if (!ptr) { + /* + * if we stripped out all the '/' and still didn't find + * a valid path... we will fail, badly. of course we would + * have failed anyway... we output 'no input file' now. + */ + script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME",NULL TSRMLS_CC); + SG(sapi_headers).http_response_code = 404; + } if (pt) efree(pt); - /* - * if we stripped out all the '/' and still didn't find - * a valid path... we will fail, badly. of course we would - * have failed anyway... is there a nice way to error? - */ + } else { + /* make sure path_info/translated are empty */ + script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME",script_path_translated TSRMLS_CC); + _sapi_cgibin_putenv("PATH_INFO",NULL TSRMLS_CC); + _sapi_cgibin_putenv("PATH_TRANSLATED",NULL TSRMLS_CC); } - if (env_path_info) efree(env_path_info); - } else -#endif - { - /* old broken logic here, but at least reverts to - * previous behaviour if the fixup is ignored, or there - * is a working server. - * - * 1. DISCARD_PATH IS BAD - * 2. PATH_INFO will never be right - */ -#if DISCARD_PATH - env_path_translated = sapi_cgibin_getenv("SCRIPT_FILENAME",0 TSRMLS_CC); -#else - env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED",0 TSRMLS_CC); -#endif - SG(request_info).path_translated = env_path_translated; } - } - - SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD",0 TSRMLS_CC); - SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING",0 TSRMLS_CC); - SG(request_info).request_uri = sapi_cgibin_getenv("SCRIPT_NAME",0 TSRMLS_CC); +#endif - if (!SG(request_info).request_uri) { - /* this is old logic, and completely incorrect by CGI spec - * this is used to generate PHP_SELF, which should actually - * match SCRIPT_NAME. This is being left so PHP will be as broken - * as it was before if a server does not set SCRIPT_NAME. - */ - SG(request_info).request_uri = sapi_cgibin_getenv("PATH_INFO",0 TSRMLS_CC); - } - if (!SG(request_info).path_translated) { - /* if this didn't get set above, do it now, default to script_filename */ - SG(request_info).path_translated = sapi_cgibin_getenv("SCRIPT_FILENAME",0 TSRMLS_CC); + SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD",0 TSRMLS_CC); + SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING",0 TSRMLS_CC); + SG(request_info).request_uri = sapi_cgibin_getenv("SCRIPT_NAME",0 TSRMLS_CC); + if (script_path_translated) + SG(request_info).path_translated = estrdup(script_path_translated); + SG(request_info).content_type = (content_type ? content_type : "" ); + SG(request_info).content_length = (content_length?atoi(content_length):0); + + /* The CGI RFC allows servers to pass on unvalidated Authorization data */ + auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION",0 TSRMLS_CC); + php_handle_auth_data(auth TSRMLS_CC); } - if (!SG(request_info).path_translated) { - /* server didn't set script_filename, default to path_translated */ - SG(request_info).path_translated = sapi_cgibin_getenv("PATH_TRANSLATED",0 TSRMLS_CC); - } - if (SG(request_info).path_translated) - SG(request_info).path_translated = estrdup(SG(request_info).path_translated); - SG(request_info).content_type = (content_type ? content_type : "" ); - SG(request_info).content_length = (content_length?atoi(content_length):0); - SG(sapi_headers).http_response_code = 200; - - /* The CGI RFC allows servers to pass on unvalidated Authorization data */ - auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION",0 TSRMLS_CC); - php_handle_auth_data(auth TSRMLS_CC); } /* }}} */ @@ -915,31 +959,20 @@ int main(int argc, char *argv[]) case 'n': cgi_sapi_module.php_ini_ignore = 1; break; - } - - } - ap_php_optind = orig_optind; - ap_php_optarg = orig_optarg; - } - #if PHP_FASTCGI - if (!cgi && !fastcgi) { - /* if we're started on command line, check to see if - we are being started as an 'external' fastcgi - server by accepting a bindpath parameter. */ - while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) { - switch (c) { + /* if we're started on command line, check to see if + we are being started as an 'external' fastcgi + server by accepting a bindpath parameter. */ case 'b': - bindpath= strdup(ap_php_optarg); + if (!fastcgi) bindpath= strdup(ap_php_optarg); break; +#endif } } ap_php_optind = orig_optind; ap_php_optarg = orig_optarg; } -#endif - #ifdef ZTS compiler_globals = ts_resource(compiler_globals_id); @@ -1186,11 +1219,7 @@ consult the installation file that came with this distribution, or visit \n\ #if PHP_FASTCGI && !fastcgi #endif - ) { /* never execute the arguments if you are a CGI */ - if (SG(request_info).argv0) { - free(SG(request_info).argv0); - SG(request_info).argv0 = NULL; - } + ) { if (cgi_sapi_module.php_ini_path_override && cgi_sapi_module.php_ini_ignore) { no_headers = 1; @@ -1365,6 +1394,7 @@ consult the installation file that came with this distribution, or visit \n\ if (fastcgi) { file_handle.type = ZEND_HANDLE_FILENAME; file_handle.filename = SG(request_info).path_translated; + file_handle.handle.fp = NULL; } else { #endif file_handle.filename = "-";