You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by "William A. Rowe, Jr." <wr...@lnd.com> on 2000/06/17 23:14:01 UTC

[patch 2.0] Win32 cgi interpreter from the registry

Ok,

  I'd looked at this (along with alot of other things) for quite a
while, and I agree with the group that these sorts of tweaks won't
work well in the 1.3 tree.

  I'll finish testing and commit unless I hear bold objections.
But it will be a few days, I want to see if that new error I just
posted is any ways related to this patch.

  And is it just me, or is the http_core config a pretty deep pit for
alot of odd stuff?  I'm looking at script_interpreter_source and 
thinking it really belonged in cgi, except that cgi doesn't 'do' 
directory configs... but perhaps it aught to?

  Thoughts?

Bill


Index: src/include/http_core.h
===================================================================
RCS file: /home/cvs/apache-2.0/src/include/http_core.h,v
retrieving revision 1.15
diff -u -r1.15 http_core.h
--- src/include/http_core.h	2000/06/17 11:13:04	1.15
+++ src/include/http_core.h	2000/06/17 21:08:14
@@ -162,9 +162,12 @@
  */
 typedef enum { eFileTypeUNKNOWN, eFileTypeBIN, eFileTypeEXE16, eFileTypeEXE32, 
                eFileTypeSCRIPT } file_type_e;
-typedef enum { INTERPRETER_SOURCE_UNSET, INTERPRETER_SOURCE_REGISTRY, 
-               INTERPRETER_SOURCE_SHEBANG } interpreter_source_e;
-API_EXPORT(file_type_e) ap_get_win32_interpreter(const request_rec *, char **);
+typedef enum { INTERPRETER_SOURCE_UNSET, INTERPRETER_SOURCE_REGISTRY_STRICT, 
+               INTERPRETER_SOURCE_REGISTRY, INTERPRETER_SOURCE_SHEBANG 
+             } interpreter_source_e;
+API_EXPORT(file_type_e) ap_get_win32_interpreter(const request_rec *, 
+                                                 char **interpreter,
+                                                 char **arguments);
 #endif
 
 #ifdef CORE_PRIVATE
Index: src/main/http_core.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/main/http_core.c,v
retrieving revision 1.77
diff -u -r1.77 http_core.c
--- src/main/http_core.c	2000/06/17 16:29:47	1.77
+++ src/main/http_core.c	2000/06/17 21:08:25
@@ -770,69 +770,127 @@
 }
 
 #ifdef WIN32
-static char* get_interpreter_from_win32_registry(ap_pool_t *p, const char* ext) 
+DWORD get_win32_registry_default_value(ap_pool_t *p, HKEY hkey, 
+                                       char* relativepath, char **value)
 {
-    char extension_path[] = "SOFTWARE\\Classes\\";
-    char executable_path[] = "\\SHELL\\OPEN\\COMMAND";
-
     HKEY hkeyOpen;
     DWORD type;
+    DWORD size = 0;
+    DWORD result = RegOpenKeyEx(hkey, relativepath, 0, 
+                                KEY_QUERY_VALUE, &hkeyOpen);
+    
+    if (result != ERROR_SUCCESS) 
+        return result;
+
+    /* Read to NULL buffer to determine value size */
+    result = RegQueryValueEx(hkeyOpen, "", 0, &type, NULL, &size);
+    
+   if (result == ERROR_SUCCESS) {
+        if ((size < 2) || (type != REG_SZ && type != REG_EXPAND_SZ)) {
+            result = ERROR_INVALID_PARAMETER;
+        }
+        else {
+            *value = ap_palloc(p, size);
+            /* Read value based on size query above */
+            result = RegQueryValueEx(hkeyOpen, "", 0, &type, *value, &size);
+        }
+    }
+
+    /* TODO: This might look fine, but we need to provide some warning
+     * somewhere that some environment variables may -not- be translated,
+     * seeing as we may have chopped the environment table down somewhat.
+     */
+    if ((result == ERROR_SUCCESS) && (type == REG_EXPAND_SZ)) 
+    {
+        char *tmp = *value;
+        size = ExpandEnvironmentStrings(tmp, *value, 0);
+        if (size) {
+            *value = ap_palloc(p, size);
+            size = ExpandEnvironmentStrings(tmp, *value, size);
+        }
+    }
+
+    RegCloseKey(hkeyOpen);
+    return result;
+}
+
+static char* get_interpreter_from_win32_registry(ap_pool_t *p, const char* ext,
+                                                 char** arguments) 
+{
+    char execcgi_path[] = "SHELL\\EXECCGI\\COMMAND";
+    char execopen_path[] = "SHELL\\OPEN\\COMMAND";
+    char typeName[MAX_PATH];
+    int cmdOfName = FALSE;
+    HKEY hkeyName;
+    HKEY hkeyType;
+    DWORD type;
     int size;
     int result;
-    char *keyName;
     char *buffer;
     char *s;
-
+    
     if (!ext)
         return NULL;
     /* 
      * Future optimization:
-     * When the registry is successfully searched, store the interpreter
-     * string in a ap_table_t to make subsequent look-ups faster
+     * When the registry is successfully searched, store the strings for
+     * interpreter and arguments in an ext hash to speed up subsequent look-ups
      */
 
-    /* Open the key associated with the script extension */
-    keyName = ap_pstrcat(p, extension_path, ext, NULL);
+    /* Open the key associated with the script filetype extension */
+    result = RegOpenKeyEx(HKEY_CLASSES_ROOT, ext, 0, KEY_QUERY_VALUE, 
+                          &hkeyType);
 
-    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, 
-                          &hkeyOpen);
-
     if (result != ERROR_SUCCESS) 
         return NULL;
 
-    /* Read to NULL buffer to find value size */
-    size = 0;
-    result = RegQueryValueEx(hkeyOpen, "", NULL, &type, NULL, &size);
-
-    if (result == ERROR_SUCCESS) {
-        buffer = ap_palloc(p, size);
-        result = RegQueryValueEx(hkeyOpen, "", NULL, &type, buffer, &size);
+    /* Retrieve the name of the script filetype extension */
+    size = sizeof(typeName);
+    result = RegQueryValueEx(hkeyType, "", NULL, &type, typeName, &size);
+    
+    if (result == ERROR_SUCCESS && type == REG_SZ && typeName[0]) {
+        /* Open the key associated with the script filetype extension */
+        result = RegOpenKeyEx(HKEY_CLASSES_ROOT, typeName, 0, 
+                              KEY_QUERY_VALUE, &hkeyName);
+
+        if (result == ERROR_SUCCESS)
+            cmdOfName = TRUE;
     }
-
-    RegCloseKey(hkeyOpen);
-
-    if (result != ERROR_SUCCESS)
-        return NULL;
 
-    /* Open the key associated with the interpreter path */
-    keyName = ap_pstrcat(p, extension_path, buffer, executable_path, NULL);
+    /* Open the key for the script command path by:
+     * 
+     *   1) the 'named' filetype key for ExecCGI/Command
+     *   2) the extension's type key for ExecCGI/Command
+     *   3) the 'named' filetype key for Open/Command
+     *   4) the extension's type key for Open/Command
+     */
+    
+    if (cmdOfName) {
+        result = get_win32_registry_default_value(p, hkeyName, 
+                                                  execcgi_path, &buffer);
+    }
 
-    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, 
-                          &hkeyOpen);
+    if (!cmdOfName || (result != ERROR_SUCCESS)) {
+        result = get_win32_registry_default_value(p, hkeyType, 
+                                                  execcgi_path, &buffer);
+    }
 
-    if (result != ERROR_SUCCESS)
-        return NULL;
+    /* TODO: Die here on registry-strict */
 
-    /* Read to NULL buffer to find value size */
-    size = 0;
-    result = RegQueryValueEx(hkeyOpen, "", 0, &type, NULL, &size);
+    if (cmdOfName && (result != ERROR_SUCCESS)) {
+        result = get_win32_registry_default_value(p, hkeyName, 
+                                                  execopen_path, &buffer);
+    }
 
-    if (result == ERROR_SUCCESS) {
-        buffer = ap_palloc(p, size);
-        result = RegQueryValueEx(hkeyOpen, "", 0, &type, buffer, &size);
+    if (result != ERROR_SUCCESS) {
+        result = get_win32_registry_default_value(p, hkeyType, 
+                                                  execopen_path, &buffer);
     }
+    
+    if (cmdOfName)
+        RegCloseKey(hkeyName);
 
-    RegCloseKey(hkeyOpen);
+    RegCloseKey(hkeyType);
 
     if (result != ERROR_SUCCESS)
         return NULL;
@@ -840,7 +898,7 @@
     /*
      * The canonical way shell command entries are entered in the Win32 
      * registry is as follows:
-     *   shell [options] "%1"
+     *   shell [options] "%1" [args]
      * where
      *   shell - full path name to interpreter or shell to run.
      *           E.g., c:\usr\local\ntreskit\perl\bin\perl.exe
@@ -848,21 +906,34 @@
      *              E.g., \C
      *   "%1" - Place holder for file to run the shell against. 
      *          Typically quoted.
+     *   options - additional arguments
+     *              E.g., /hidden
      *
-     * If we find a %1 or a quoted %1, lop it off. 
+     * If we find a %1 or a quoted %1, lop off the remainder to arguments. 
      */
     if (buffer && *buffer) {
         if ((s = strstr(buffer, "\"%1")))
+        {
             *s = '\0';
+            *arguments = s + 4;
+        }
         else if ((s = strstr(buffer, "%1"))) 
+        {
             *s = '\0';
+            *arguments = buffer + 2;
+        }
+        else
+            *arguments = strchr(buffer, '\0');
+        while (**arguments && isspace(**arguments))
+            ++*arguments;
     }
 
     return buffer;
 }
 
 API_EXPORT (file_type_e) ap_get_win32_interpreter(const  request_rec *r, 
-                                                  char** interpreter )
+                                                  char** interpreter,
+                                                  char** arguments)
 {
     HANDLE hFile;
     DWORD nBytesRead;
@@ -890,23 +961,43 @@
     }
     ext = strrchr(exename, '.');
 
-    if (ext && (!strcasecmp(ext,".bat") || !strcasecmp(ext,".cmd"))) {
-        return eFileTypeEXE32;
+    if (ext && (!strcasecmp(ext,".bat") || !strcasecmp(ext,".cmd"))) 
+    {
+        char *comspec = getenv("COMSPEC");
+        if (comspec) {
+            *interpreter = ap_pstrcat(r->pool, "\"", comspec, "\" /c ", NULL);
+            return eFileTypeSCRIPT;
+        }
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r->server,
+         "Failed to start a '%s' file as a script.\n\t"
+         "COMSPEC variable is missing from the environment.", ext);
+        return eFileTypeUNKNOWN;
+        
     }
 
     /* If the file has an extension and it is not .com and not .exe and
      * we've been instructed to search the registry, then do it!
      */
     if (ext && strcasecmp(ext,".exe") && strcasecmp(ext,".com") &&
-        d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY) {
+        (d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY ||
+         d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY_STRICT)) {
          /* Check the registry */
-        *interpreter = get_interpreter_from_win32_registry(r->pool, ext);
+        *interpreter = get_interpreter_from_win32_registry(r->pool, ext, 
+                                                           arguments);
         if (*interpreter)
             return eFileTypeSCRIPT;
-        else {
+        else if (d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY_STRICT) {
+            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r->server,
+             "ScriptInterpreterSource config directive set to \"registry-strict\".\n\t"
+             "Interpreter not found for files of type '%s'.", ext);
+             return eFileTypeUNKNOWN;
+        }
+        else
+        {
             ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r->server,
              "ScriptInterpreterSource config directive set to \"registry\".\n\t"
-             "Registry was searched but interpreter not found. Trying the shebang line.");
+             "Interpreter not found for files of type '%s', "
+             "trying \"script\" method...", ext);
         }
     }        
 
@@ -2279,10 +2370,13 @@
 {
     if (!strcasecmp(arg, "registry")) {
         d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY;
+    } else if (!strcasecmp(arg, "registry-strict")) {
+        d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY_STRICT;
     } else if (!strcasecmp(arg, "script")) {
         d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
     } else {
-        d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, cmd->server,
+                     "%s option %s is invalid", cmd->cmd->name, arg);
     }
     return NULL;
 }
Index: src/modules/standard/mod_cgi.c
===================================================================
RCS file: /home/cvs/apache-2.0/src/modules/standard/mod_cgi.c,v
retrieving revision 1.49
diff -u -r1.49 mod_cgi.c
--- src/modules/standard/mod_cgi.c	2000/06/12 04:17:45	1.49
+++ src/modules/standard/mod_cgi.c	2000/06/17 21:08:34
@@ -436,15 +436,16 @@
     return APR_SUCCESS;
 }
 
-static ap_status_t build_command_line(char **c, request_rec *r, ap_pool_t *p)
+static ap_status_t build_command_line(char **cmd, request_rec *r, ap_pool_t *p)
 {
 #ifdef WIN32
     char *quoted_filename = NULL;
     char *interpreter = NULL;
+    char *arguments = NULL;
     file_type_e fileType;
 
-    *c = NULL;
-    fileType = ap_get_win32_interpreter(r, &interpreter);
+    *cmd = NULL;
+    fileType = ap_get_win32_interpreter(r, &interpreter, &arguments);
 
     if (fileType == eFileTypeUNKNOWN) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, r,
@@ -459,13 +460,20 @@
      */
     quoted_filename = ap_pstrcat(p, "\"", r->filename, "\"", NULL);
     if (interpreter && *interpreter) {
-        *c = ap_pstrcat(p, interpreter, " ", quoted_filename, " ", NULL);
+        if (arguments && *arguments)
+            *cmd = ap_pstrcat(p, interpreter, " ", quoted_filename, " ", 
+                              arguments, NULL);
+        else
+            *cmd = ap_pstrcat(p, interpreter, " ", quoted_filename, " ", NULL);
     }
+    else if (arguments && *arguments) {
+        *cmd = ap_pstrcat(p, quoted_filename, " ", arguments, NULL);
+    }
     else {
-        *c = ap_pstrcat(p, quoted_filename, " ", NULL);
+        *cmd = ap_pstrcat(p, quoted_filename, NULL);
     }
 #else
-    *c = ap_pstrcat(p, r->filename, NULL);
+    *cmd = ap_pstrcat(p, r->filename, NULL);
 #endif
     return APR_SUCCESS;
 }

Re: [patch 2.0] Win32 cgi interpreter from the registry

Posted by jlwpc1 <jl...@earthlink.net>.
From: William A. Rowe, Jr. <wr...@lnd.com>

> > Is accessing the registry with a char * the best way to 
> > access an Unicode registry?  
> 
> A unicode registry (NT, 2000) is best handled (skip the wchar.h
> stuff) with the RegXxxW() calls, I agree, and implemented that
> in my ATL components.  That doesn't imply it will blend with the
> current version of Apache in the least.  There are implicit A2W
> and W2A conversions calling the RegXxxA() calls, as the existing
> and future calls perform, and the actual results are passed to an
> xplatform module expressing the interpreter as char*.  So it's
> truly six of one, half dozen of the other.
> 

On each of the registry functions (and _any_ NT/Windows 2000 function) you are forcing the OS to alloc memory (for _all_  char * type actions) which is used by the OS to change this "char *" to Unicode. Then the OS passes on the Unicode to the function.  The OS next destroys the memory used to change the char * to Unicode. Once the OS needs to return "stuff"  from the function back to Apache, the OS once again repeats the memory change steps all over again. The OS returns non-unicode "stuff" and then destorys the memory.
The OS never calls anything but Unicode functions. No six of one, half a dozen of the other at all.   :)
  
If Apache declares tchar.h _most_ , if not all, of this (is it funcA() or funcW() ) will be handled during compile times. Please perhaps in another message, explain to us why tchar.h is not used - even with a C run time Apache?  This isn't just for you OtherBill. :)

> This patch solves several specific issues:
> 
> 1) 1.3.x 

> 2) 1.3.x 

[cut]

See what I mean by smaller messages  - I'm not talking about 1.3 anything.  I thought I saw 2.0!  1.3 is done - just maintain it (and improve it like in places you and others have).  But how 1.3 works has very little to do with how 2.0 will work! This is the "problem" and should be another message.  :)

Short messages are good.

> > I still prefer outlines over code until discussed fully.  
> > Words are easier to add buts to and an outline would have 
> > saved you a lot of time.
> 
> It's only wasted time if the issue you are addressing wasn't a
> seperate, and very large patch in and of itself.  

Why isn't Apache using WideCharToMultiByte() and MultiByteToWideChar() or tchar.h?

Talking about how to do a function never wastes as much time as "code then fix".

> 
> Patches address one issue only.

There should be no patch (not patches in a message) until there is "a talked about outline".
If you can not outline and talk about the function - how can you program the func() "Windows properly"?  Unix should also do this but if they choose not too "okay" but Windows needs to look at the "whole issue", everything any programmer does effects the whole Windows "way". 

I'm a talkin' about Apache Windows Server 2.0 but even 1.3 should start doing this!

JLW







RE: [patch 2.0] Win32 cgi interpreter from the registry

Posted by "William A. Rowe, Jr." <wr...@lnd.com>.
> From: William A. Rowe, Jr. <wr...@lnd.com>
> To: <ne...@apache.org>
> 
> Is accessing the registry with a char * the best way to 
> access an Unicode registry?  

A unicode registry (NT, 2000) is best handled (skip the wchar.h
stuff) with the RegXxxW() calls, I agree, and implemented that
in my ATL components.  That doesn't imply it will blend with the
current version of Apache in the least.  There are implicit A2W
and W2A conversions calling the RegXxxA() calls, as the existing
and future calls perform, and the actual results are passed to an
xplatform module expressing the interpreter as char*.  So it's
truly six of one, half dozen of the other.

This patch solves several specific issues:

1) 1.3.x truncated any open/command arguments following the %1 arg.
2) 1.3.x did not expand environment strings (%userprofile% etc.)
   *) This patch may still not do so, if we are running with a
      subset of the 'normal' environment for security reasons.
3) 1.3.x used the shell/open/command, and always followed registry
   failure with a unix sh style shebang test.
4) This proposal adds shell/execcgi/command as the preference, and
   will ultimately be the only mechanism recognized by:
5) The new ScriptInterpreterSource registry-strict, which dismisses
   the shell/open/command and shebang processing for operators who
   are interested in explicit authorization of file types allowed
   in the context of subscriber created scripts.  I have one more
   tweak, but the end result is shell/execcgi/command processing
   only is permitted. 

No 5. should have been considered seperately.

> I still prefer outlines over code until discussed fully.  
> Words are easier to add buts to and an outline would have 
> saved you a lot of time.

It's only wasted time if the issue you are addressing wasn't a
seperate, and very large patch in and of itself.  

Patches address one issue only.  In this case, it is the extraction
from the registry.  It is also incorporated the new registry-strict
directive, which I should have proposed as a seperate patch.  They
can be considered and would be applied seperately.

Bill

Re: [patch 2.0] Win32 cgi interpreter from the registry

Posted by jlwpc1 <jl...@earthlink.net>.
----- Original Message ----- 
From: William A. Rowe, Jr. <wr...@lnd.com>
To: <ne...@apache.org>
Sent: Saturday, June 17, 2000 4:14 PM
Subject: [patch 2.0] Win32 cgi interpreter from the registry 

[cut]

> 
>   Thoughts?
> 
> Bill
> 

Is accessing the registry with a char * the best way to access an Unicode registry?  

Defining tchar.h (Unix can define them all NOPs) is needed for Apache Windows 2.0 one process IOCP (NT/Windows2000) multi-threaded (NT/Windows 2000/Win9x) Server but until then use WideCharToMultiByte() and MultiByteToWideChar() during registry access.

I still prefer outlines over code until discussed fully.  Words are easier to add buts to and an outline would have saved you a lot of time.

Thanks,
JLW





Re: [patch 2.0] Win32 cgi interpreter from the registry

Posted by Greg Stein <gs...@lyra.org>.
On Sat, Jun 17, 2000 at 04:14:01PM -0500, William A. Rowe, Jr. wrote:
> Ok,
> 
>   I'd looked at this (along with alot of other things) for quite a
> while, and I agree with the group that these sorts of tweaks won't
> work well in the 1.3 tree.
> 
>   I'll finish testing and commit unless I hear bold objections.
> But it will be a few days, I want to see if that new error I just
> posted is any ways related to this patch.

No opinion...

>   And is it just me, or is the http_core config a pretty deep pit for
> alot of odd stuff?  I'm looking at script_interpreter_source and 
> thinking it really belonged in cgi, except that cgi doesn't 'do' 
> directory configs... but perhaps it aught to?

Yes, I think it is kind of a mess and could use a bit of revamping.

I've been toying with the idea of a mod_http which would shift a bunch of
the HTTP handling outside of main/. For example, why is If-* header
processing done in http_protocol (ap_meets_conditions()), rather than as
part of the normal request steps done during request processing? Why is
there any authentication code in http_core? etc.

One of these days... :-)

Cheers,
-g

-- 
Greg Stein, http://www.lyra.org/