Skip to content

Commit 3e90bd6

Browse files
committed
Fixes Win32 cgi 500 errors when QUERY_ARGS or other strings include extended characters (non US-ASCII) in non-utf8 format. This brings Win32 back into CGI/1.1 compliance, and leaves charset decoding up to the cgi application itself. Accomplished this by utf-8 encoding of plain octets for user and header data, so that the apr_proc_create() translates back to single unicode characters. This won't necessarily translate into the correct octet depending on the codepage used by the cgi app. That's the next task. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@95865 13f79535-47bb-0310-9956-ffa450edef68
1 parent 481e752 commit 3e90bd6

2 files changed

Lines changed: 80 additions & 11 deletions

File tree

CHANGES

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
21
Changes with Apache 2.0.40
2+
3+
*) Fix Win32 cgi 500 errors when QUERY_ARGS or other strings include
4+
extended characters (non US-ASCII) in non-utf8 format. This brings
5+
Win32 back into CGI/1.1 compliance, and leaves charset decoding up
6+
to the cgi application itself. [William Rowe]
7+
8+
*) Major overhaul of mod_dav, mod_dav_fs and the experimental/cache
9+
modules to bring them up to the current apr/apr-util APIs.
10+
[William Rowe]
11+
312
*) Fix segfault in mod_mem_cache most frequently observed when
413
serving the same file to multiple clients on an MP machine.
514
[Bill Stoddard]

modules/arch/win32/mod_win32.c

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,44 @@ static const char *set_interpreter_source(cmd_parms *cmd, void *dv,
136136
return NULL;
137137
}
138138

139+
/* XXX: prep_string should translate the string into unicode,
140+
* such that it is compatible with whatever codepage the client
141+
* will read characters 80-ff. For the moment, use the unicode
142+
* values 0080-00ff. This isn't trivial, since the code page
143+
* varies between msdos and Windows applications.
144+
*/
145+
static void prep_string(const char ** str, apr_pool_t *p)
146+
{
147+
const char *ch = *str;
148+
char *ch2;
149+
int widen = 0;
150+
if (!ch) {
151+
return;
152+
}
153+
while (*ch) {
154+
if (*(ch++) & 0x80) {
155+
++widen;
156+
}
157+
}
158+
if (!widen) {
159+
return;
160+
}
161+
widen += (ch - *str) + 1;
162+
ch = *str;
163+
*str = ch2 = apr_palloc(p, widen);
164+
while (*ch) {
165+
if (*ch & 0x80) {
166+
/* sign extension won't hurt us here */
167+
*(ch2++) = 0xC0 | ((*ch >> 6) & 0x03);
168+
*(ch2++) = 0x80 | (*(ch++) & 0x3f);
169+
}
170+
else {
171+
*(ch2++) = *(ch++);
172+
}
173+
}
174+
*(ch2++) = '\0';
175+
}
176+
139177
/* Pretty unexciting ... yank a registry value, and explode any envvars
140178
* that the system has configured (e.g. %SystemRoot%/someapp.exe)
141179
*
@@ -294,7 +332,7 @@ static apr_array_header_t *split_argv(apr_pool_t *p, const char *interp,
294332

295333
while (*ch) {
296334
/* Skip on through Deep Space */
297-
if (isspace(*ch)) {
335+
if (apr_isspace(*ch)) {
298336
++ch; continue;
299337
}
300338
/* One Arg */
@@ -307,6 +345,7 @@ static apr_array_header_t *split_argv(apr_pool_t *p, const char *interp,
307345
break;
308346
}
309347
ap_unescape_url(w);
348+
prep_string(&w, p);
310349
arg = (const char**)apr_array_push(args);
311350
*arg = ap_escape_shell_cmd(p, w);
312351
}
@@ -352,7 +391,7 @@ static apr_array_header_t *split_argv(apr_pool_t *p, const char *interp,
352391
*arg = d;
353392
inquo = 0;
354393
while (*ch) {
355-
if (isspace(*ch) && !inquo) {
394+
if (apr_isspace(*ch) && !inquo) {
356395
++ch; break;
357396
}
358397
/* Get 'em backslashes */
@@ -373,7 +412,7 @@ static apr_array_header_t *split_argv(apr_pool_t *p, const char *interp,
373412
}
374413
/* Flip quote state */
375414
inquo = !inquo;
376-
if (isspace(*ch) && !inquo) {
415+
if (apr_isspace(*ch) && !inquo) {
377416
++ch; break;
378417
}
379418
/* All other '"'s are Munched */
@@ -399,6 +438,7 @@ static apr_array_header_t *split_argv(apr_pool_t *p, const char *interp,
399438
break;
400439
}
401440
ap_unescape_url(w);
441+
prep_string(&w, p);
402442
arg = (const char**)apr_array_push(args);
403443
*arg = ap_escape_shell_cmd(p, w);
404444
}
@@ -415,28 +455,33 @@ static apr_status_t ap_cgi_build_command(const char **cmd, const char ***argv,
415455
request_rec *r, apr_pool_t *p,
416456
cgi_exec_info_t *e_info)
417457
{
458+
const apr_table_entry_t *elts = (apr_table_entry_t *)r->subprocess_env->a.elts;
418459
const char *ext = NULL;
419460
const char *interpreter = NULL;
420461
win32_dir_conf *d;
421462
apr_file_t *fh;
422463
const char *args = "";
464+
int i;
423465

424466
d = (win32_dir_conf *)ap_get_module_config(r->per_dir_config,
425467
&win32_module);
426468

427469
if (e_info->cmd_type) {
428-
/* Handle the complete file name, we DON'T want to follow suexec, since
429-
* an unrooted command is as predictable as shooting craps in Win32.
430-
*
431-
* Notice that unlike most mime extension parsing, we have to use the
432-
* win32 parsing here, therefore the final extension is the only one
433-
* we will consider
470+
/* We have to consider that the client gets any QUERY_ARGS
471+
* without any charset interpretation, use prep_string to
472+
* create a string of the literal QUERY_ARGS bytes.
434473
*/
435474
*cmd = r->filename;
436475
if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) {
437476
args = r->args;
438477
}
439478
}
479+
/* Handle the complete file name, we DON'T want to follow suexec, since
480+
* an unrooted command is as predictable as shooting craps in Win32.
481+
* Notice that unlike most mime extension parsing, we have to use the
482+
* win32 parsing here, therefore the final extension is the only one
483+
* we will consider.
484+
*/
440485
ext = strrchr(apr_filename_of_pathname(*cmd), '.');
441486

442487
/* If the file has an extension and it is not .com and not .exe and
@@ -496,7 +541,7 @@ static apr_status_t ap_cgi_build_command(const char **cmd, const char ***argv,
496541
}
497542
if (i < sizeof(buffer)) {
498543
interpreter = buffer + 2;
499-
while (isspace(*interpreter)) {
544+
while (apr_isspace(*interpreter)) {
500545
++interpreter;
501546
}
502547
if (e_info->cmd_type != APR_SHELLCMD) {
@@ -532,6 +577,21 @@ static apr_status_t ap_cgi_build_command(const char **cmd, const char ***argv,
532577

533578
e_info->detached = 1;
534579

580+
/* XXX: Must fix r->subprocess_env to follow utf-8 conventions from
581+
* the client's octets so that win32 apr_proc_create is happy.
582+
* The -best- way is to determine if the .exe is unicode aware
583+
* (using 0x0080-0x00ff) or is linked as a command or windows
584+
* application (following the OEM or Ansi code page in effect.)
585+
*/
586+
for (i = 0; i < r->subprocess_env->a.nelts; ++i) {
587+
if (elts[i].key && *elts[i].key
588+
&& (strncmp(elts[i].key, "HTTP_", 5) == 0
589+
|| strncmp(elts[i].key, "SERVER_", 7) == 0
590+
|| strncmp(elts[i].key, "REQUEST_", 8) == 0
591+
|| strcmp(elts[i].key, "QUERY_STRING") == 0)) {
592+
prep_string((const char**) &elts[i].val, r->pool);
593+
}
594+
}
535595
return APR_SUCCESS;
536596
}
537597

0 commit comments

Comments
 (0)