Apache HTTPD
mod_win32.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifdef WIN32
18
19#include "apr_strings.h"
20#include "apr_portable.h"
21#include "apr_buckets.h"
22#include "ap_config.h"
23#include "httpd.h"
24#include "http_config.h"
25#include "http_core.h"
26#include "http_protocol.h"
27#include "http_request.h"
28#include "http_log.h"
29#include "util_script.h"
30#include "mod_core.h"
31#include "mod_cgi.h"
32#include "apr_lib.h"
33#include "ap_regkey.h"
34
35/*
36 * CGI Script stuff for Win32...
37 */
44 char **interpreter,
45 char **arguments);
46
47module AP_MODULE_DECLARE_DATA win32_module;
48
49typedef struct {
50 /* Where to find interpreter to run scripts */
53
54static void *create_win32_dir_config(apr_pool_t *p, char *dir)
55{
56 win32_dir_conf *conf;
57 conf = (win32_dir_conf*)apr_palloc(p, sizeof(win32_dir_conf));
58 conf->script_interpreter_source = INTERPRETER_SOURCE_UNSET;
59 return conf;
60}
61
62static void *merge_win32_dir_configs(apr_pool_t *p, void *basev, void *addv)
63{
64 win32_dir_conf *new;
67
68 new = (win32_dir_conf *) apr_pcalloc(p, sizeof(win32_dir_conf));
69 new->script_interpreter_source = (add->script_interpreter_source
71 ? add->script_interpreter_source
73 return new;
74}
75
76static const char *set_interpreter_source(cmd_parms *cmd, void *dv,
77 char *arg)
78{
80 if (!strcasecmp(arg, "registry")) {
81 d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY;
82 }
83 else if (!strcasecmp(arg, "registry-strict")) {
84 d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY_STRICT;
85 }
86 else if (!strcasecmp(arg, "script")) {
87 d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
88 }
89 else {
90 return apr_pstrcat(cmd->temp_pool, "ScriptInterpreterSource \"", arg,
91 "\" must be \"registry\", \"registry-strict\" or "
92 "\"script\"", NULL);
93 }
94 return NULL;
95}
96
97/* XXX: prep_string should translate the string into unicode,
98 * such that it is compatible with whatever codepage the client
99 * will read characters 80-ff. For the moment, use the unicode
100 * values 0080-00ff. This isn't trivial, since the code page
101 * varies between msdos and Windows applications.
102 * For subsystem 2 [GUI] the default is the system Ansi CP.
103 * For subsystem 3 [CLI] the default is the system OEM CP.
104 */
105static void prep_string(const char ** str, apr_pool_t *p)
106{
107 const char *ch = *str;
108 char *ch2;
109 apr_size_t widen = 0;
110
111 if (!ch) {
112 return;
113 }
114 while (*ch) {
115 if (*(ch++) & 0x80) {
116 ++widen;
117 }
118 }
119 if (!widen) {
120 return;
121 }
122 widen += (ch - *str) + 1;
123 ch = *str;
124 *str = ch2 = apr_palloc(p, widen);
125 while (*ch) {
126 if (*ch & 0x80) {
127 /* sign extension won't hurt us here */
128 *(ch2++) = 0xC0 | ((*ch >> 6) & 0x03);
129 *(ch2++) = 0x80 | (*(ch++) & 0x3f);
130 }
131 else {
132 *(ch2++) = *(ch++);
133 }
134 }
135 *(ch2++) = '\0';
136}
137
138/* Somewhat more exciting ... figure out where the registry has stashed the
139 * ExecCGI or Open command - it may be nested one level deep (or more???)
140 */
142 const char* ext,
143 int strict)
144{
145 apr_status_t rv;
149 char execcgi_path[] = "SHELL\\EXECCGI\\COMMAND";
150 char execopen_path[] = "SHELL\\OPEN\\COMMAND";
151 char *type_name;
152 char *buffer;
153
154 if (!ext) {
155 return NULL;
156 }
157 /*
158 * Future optimization:
159 * When the registry is successfully searched, store the strings for
160 * interpreter and arguments in an ext hash to speed up subsequent look-ups
161 */
162
163 /* Open the key associated with the script filetype extension */
165
166 if (rv != APR_SUCCESS) {
167 return NULL;
168 }
169
170 /* Retrieve the name of the script filetype extension */
172
173 if (rv == APR_SUCCESS && type_name[0]) {
174 /* Open the key associated with the script filetype extension */
176 APR_READ, p);
177 }
178
179 /* Open the key for the script command path by:
180 *
181 * 1) the 'named' filetype key for ExecCGI/Command
182 * 2) the extension's type key for ExecCGI/Command
183 *
184 * and if the strict arg is false, then continue trying:
185 *
186 * 3) the 'named' filetype key for Open/Command
187 * 4) the extension's type key for Open/Command
188 */
189
190 if (name_key) {
192 == APR_SUCCESS) {
193 rv = ap_regkey_value_get(&buffer, key, "", p);
195 }
196 }
197
198 if (!name_key || (rv != APR_SUCCESS)) {
200 == APR_SUCCESS) {
201 rv = ap_regkey_value_get(&buffer, key, "", p);
203 }
204 }
205
206 if (!strict && name_key && (rv != APR_SUCCESS)) {
208 == APR_SUCCESS) {
209 rv = ap_regkey_value_get(&buffer, key, "", p);
211 }
212 }
213
214 if (!strict && (rv != APR_SUCCESS)) {
216 == APR_SUCCESS) {
217 rv = ap_regkey_value_get(&buffer, key, "", p);
219 }
220 }
221
222 if (name_key) {
224 }
225
227
228 if (rv != APR_SUCCESS || !buffer[0]) {
229 return NULL;
230 }
231
232 return buffer;
233}
234
235
236static apr_array_header_t *split_argv(apr_pool_t *p, const char *interp,
237 const char *cgiprg, const char *cgiargs)
238{
239 apr_array_header_t *args = apr_array_make(p, 8, sizeof(char*));
240 char *d = apr_palloc(p, strlen(interp)+1);
241 const char *ch = interp;
242 const char **arg;
243 int prgtaken = 0;
244 int argtaken = 0;
245 int inquo;
246 int sl;
247
248 while (*ch) {
249 /* Skip on through Deep Space */
250 if (apr_isspace(*ch)) {
251 ++ch; continue;
252 }
253 /* One Arg */
254 if (((*ch == '$') || (*ch == '%')) && (*(ch + 1) == '*')) {
255 const char *cgiarg = cgiargs;
256 argtaken = 1;
257 for (;;) {
258 char *w = ap_getword_nulls(p, &cgiarg, '+');
259 if (!*w) {
260 break;
261 }
263 prep_string((const char**)&w, p);
264 arg = (const char**)apr_array_push(args);
266 }
267 ch += 2;
268 continue;
269 }
270 if (((*ch == '$') || (*ch == '%')) && (*(ch + 1) == '1')) {
271 /* Todo: Make short name!!! */
272 prgtaken = 1;
273 arg = (const char**)apr_array_push(args);
274 if (*ch == '%') {
275 char *repl = apr_pstrdup(p, cgiprg);
276 *arg = repl;
277 while ((repl = strchr(repl, '/'))) {
278 *repl++ = '\\';
279 }
280 }
281 else {
282 *arg = cgiprg;
283 }
284 ch += 2;
285 continue;
286 }
287 if ((*ch == '\"') && ((*(ch + 1) == '$')
288 || (*(ch + 1) == '%')) && (*(ch + 2) == '1')
289 && (*(ch + 3) == '\"')) {
290 prgtaken = 1;
291 arg = (const char**)apr_array_push(args);
292 if (*(ch + 1) == '%') {
293 char *repl = apr_pstrdup(p, cgiprg);
294 *arg = repl;
295 while ((repl = strchr(repl, '/'))) {
296 *repl++ = '\\';
297 }
298 }
299 else {
300 *arg = cgiprg;
301 }
302 ch += 4;
303 continue;
304 }
305 arg = (const char**)apr_array_push(args);
306 *arg = d;
307 inquo = 0;
308 while (*ch) {
309 if (apr_isspace(*ch) && !inquo) {
310 ++ch; break;
311 }
312 /* Get 'em backslashes */
313 for (sl = 0; *ch == '\\'; ++sl) {
314 *d++ = *ch++;
315 }
316 if (sl & 1) {
317 /* last unmatched '\' + '"' sequence is a '"' */
318 if (*ch == '\"') {
319 *(d - 1) = *ch++;
320 }
321 continue;
322 }
323 if (*ch == '\"') {
324 /* '""' sequence within quotes is a '"' */
325 if (*++ch == '\"' && inquo) {
326 *d++ = *ch++; continue;
327 }
328 /* Flip quote state */
329 inquo = !inquo;
330 if (apr_isspace(*ch) && !inquo) {
331 ++ch; break;
332 }
333 /* All other '"'s are Munched */
334 continue;
335 }
336 /* Anything else is, well, something else */
337 *d++ = *ch++;
338 }
339 /* Term that arg, already pushed on args */
340 *d++ = '\0';
341 }
342
343 if (!prgtaken) {
344 arg = (const char**)apr_array_push(args);
345 *arg = cgiprg;
346 }
347
348 if (!argtaken) {
349 const char *cgiarg = cgiargs;
350 for (;;) {
351 char *w = ap_getword_nulls(p, &cgiarg, '+');
352 if (!*w) {
353 break;
354 }
356 prep_string((const char**)&w, p);
357 arg = (const char**)apr_array_push(args);
359 }
360 }
361
362 arg = (const char**)apr_array_push(args);
363 *arg = NULL;
364
365 return args;
366}
367
368
369static apr_status_t ap_cgi_build_command(const char **cmd, const char ***argv,
372{
374 const apr_table_entry_t *elts = (apr_table_entry_t *) elts_arr->elts;
375 const char *ext = NULL;
376 const char *interpreter = NULL;
378 apr_file_t *fh;
379 const char *args = "";
380 int i;
381
383 &win32_module);
384
385 if (e_info->cmd_type) {
386 /* We have to consider that the client gets any QUERY_ARGS
387 * without any charset interpretation, use prep_string to
388 * create a string of the literal QUERY_ARGS bytes.
389 */
390 *cmd = r->filename;
391 if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) {
392 args = r->args;
393 }
394 }
395 /* Handle the complete file name, we DON'T want to follow suexec, since
396 * an unrooted command is as predictable as shooting craps in Win32.
397 * Notice that unlike most mime extension parsing, we have to use the
398 * win32 parsing here, therefore the final extension is the only one
399 * we will consider.
400 */
402
403 /* If the file has an extension and it is not .com and not .exe and
404 * we've been instructed to search the registry, then do so.
405 * Let apr_proc_create do all of the .bat/.cmd dirty work.
406 */
407 if (ext && (!strcasecmp(ext,".exe") || !strcasecmp(ext,".com")
408 || !strcasecmp(ext,".bat") || !strcasecmp(ext,".cmd"))) {
409 interpreter = "";
410 }
411 if (!interpreter && ext
412 && (d->script_interpreter_source
414 || d->script_interpreter_source
416 /* Check the registry */
417 int strict = (d->script_interpreter_source
420 strict);
421 if (interpreter && e_info->cmd_type != APR_SHELLCMD) {
422 e_info->cmd_type = APR_PROGRAM_PATH;
423 }
424 else {
426 strict ? APLOGNO(03180) "No ExecCGI verb found for files of type '%s'."
427 : APLOGNO(03181) "No ExecCGI or Open verb found for files of type '%s'.",
428 ext);
429 }
430 }
431 if (!interpreter) {
432 apr_status_t rv;
433 char buffer[1024];
434 apr_size_t bytes = sizeof(buffer);
436
437 /* Need to peek into the file figure out what it really is...
438 * ### aught to go back and build a cache for this one of these days.
439 */
440 if ((rv = apr_file_open(&fh, *cmd, APR_READ | APR_BUFFERED,
443 "Failed to open cgi file %s for testing", *cmd);
444 return rv;
445 }
446 if ((rv = apr_file_read(fh, buffer, &bytes)) != APR_SUCCESS) {
448 "Failed to read cgi file %s for testing", *cmd);
449 return rv;
450 }
452
453 /* Some twisted character [no pun intended] at MS decided that a
454 * zero width joiner as the lead wide character would be ideal for
455 * describing Unicode text files. This was further convoluted to
456 * another MSism that the same character mapped into utf-8, EF BB BF
457 * would signify utf-8 text files.
458 *
459 * Since MS configuration files are all protecting utf-8 encoded
460 * Unicode path, file and resource names, we already have the correct
461 * WinNT encoding. But at least eat the stupid three bytes up front.
462 *
463 * ### A more thorough check would also allow UNICODE text in buf, and
464 * convert it to UTF-8 for invoking unicode scripts. Those are few
465 * and far between, so leave that code an enterprising soul with a need.
466 */
467 if ((bytes >= 3) && memcmp(buffer, "\xEF\xBB\xBF", 3) == 0) {
468 memmove(buffer, buffer + 3, bytes -= 3);
469 }
470
471 /* Script or executable, that is the question...
472 * we check here also for '! so that .vbs scripts can work as CGI.
473 */
474 if ((bytes >= 2) && ((buffer[0] == '#') || (buffer[0] == '\''))
475 && (buffer[1] == '!')) {
476 /* Assuming file is a script since it starts with a shebang */
477 for (i = 2; i < bytes; i++) {
478 if ((buffer[i] == '\r') || (buffer[i] == '\n')) {
479 buffer[i] = '\0';
480 break;
481 }
482 }
483 if (i < bytes) {
484 interpreter = buffer + 2;
485 while (apr_isspace(*interpreter)) {
486 ++interpreter;
487 }
488 if (e_info->cmd_type != APR_SHELLCMD) {
489 e_info->cmd_type = APR_PROGRAM_PATH;
490 }
491 }
492 }
493 else if (bytes >= sizeof(IMAGE_DOS_HEADER)) {
494 /* Not a script, is it an executable? */
496 if (hdr->e_magic == IMAGE_DOS_SIGNATURE) {
497 if (hdr->e_lfarlc < 0x40) {
498 /* Ought to invoke this 16 bit exe by a stub, (cmd /c?) */
499 interpreter = "";
500 }
501 else {
502 interpreter = "";
503 }
504 }
505 }
506 }
507 if (!interpreter) {
509 "%s is not executable; ensure interpreted scripts have "
510 "\"#!\" or \"'!\" first line", *cmd);
511 return APR_EBADF;
512 }
513
514 *argv = (const char **)(split_argv(p, interpreter, *cmd,
515 args)->elts);
516 *cmd = (*argv)[0];
517
518 e_info->detached = 1;
519
520 /* XXX: Must fix r->subprocess_env to follow utf-8 conventions from
521 * the client's octets so that win32 apr_proc_create is happy.
522 * The -best- way is to determine if the .exe is unicode aware
523 * (using 0x0080-0x00ff) or is linked as a command or windows
524 * application (following the OEM or Ansi code page in effect.)
525 */
526 for (i = 0; i < elts_arr->nelts; ++i) {
527 if (elts[i].key && *elts[i].key && *elts[i].val
528 && !(strncmp(elts[i].key, "REMOTE_", 7) == 0
529 || strcmp(elts[i].key, "GATEWAY_INTERFACE") == 0
530 || strcmp(elts[i].key, "REQUEST_METHOD") == 0
531 || strcmp(elts[i].key, "SERVER_ADDR") == 0
532 || strcmp(elts[i].key, "SERVER_PORT") == 0
533 || strcmp(elts[i].key, "SERVER_PROTOCOL") == 0)) {
534 prep_string((const char**) &elts[i].val, r->pool);
535 }
536 }
537 return APR_SUCCESS;
538}
539
540static void register_hooks(apr_pool_t *p)
541{
543}
544
545static const command_rec win32_cmds[] = {
546AP_INIT_TAKE1("ScriptInterpreterSource", set_interpreter_source, NULL,
548 "Where to find interpreter to run Win32 scripts "
549 "(Registry or script shebang line)"),
550{ NULL }
551};
552
555 create_win32_dir_config, /* create per-dir config */
556 merge_win32_dir_configs, /* merge per-dir config */
557 NULL, /* server config */
558 NULL, /* merge server config */
559 win32_cmds, /* command apr_table_t */
560 register_hooks /* register hooks */
561};
562
563#endif /* defined WIN32 */
Symbol export macros and hook functions.
#define AP_DECLARE(type)
Definition ap_config.h:67
APR-style Win32 Registry Manipulation.
APR-UTIL Buckets/Bucket Brigades.
APR general purpose library routines.
APR Portability Routines.
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
APR Strings library.
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
#define AP_DECLARE_MODULE(foo)
ap_conf_vector_t * base
request_rec * r
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_INFO
Definition http_log.h:70
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
void const char * arg
Definition http_vhost.h:63
#define APR_EBADF
Definition apr_errno.h:704
#define APR_REGISTER_OPTIONAL_FN(name)
apr_text_header * hdr
Definition apr_xml.h:77
#define OR_FILEINFO
#define STANDARD20_MODULE_STUFF
char * ap_escape_shell_cmd(apr_pool_t *p, const char *s)
Definition util.c:1812
#define ap_strchr_c(s, c)
Definition httpd.h:2353
char * ap_getword_nulls(apr_pool_t *p, const char **line, char stop)
Definition util.c:779
int ap_unescape_url(char *url)
Definition util.c:1939
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
#define apr_isspace(c)
Definition apr_lib.h:225
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
char * buffer
#define APR_READ
Definition apr_file_io.h:93
#define APR_BUFFERED
#define APR_OS_DEFAULT
int strcasecmp(const char *a, const char *b)
#define memmove(a, b, c)
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
apr_dir_t * dir
const void apr_size_t bytes
Definition apr_random.h:91
apr_cmdtype_e cmd
const char const char *const * args
@ APR_SHELLCMD
@ APR_PROGRAM_PATH
Apache Configuration.
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
static void register_hooks(apr_pool_t *p)
CGI Script Execution Extension Module for Apache.
mod_core private header file
const char * argv[3]
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
Definition apr_tables.h:81
A structure that represents the current request.
Definition httpd.h:845
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
apr_table_t * subprocess_env
Definition httpd.h:983
server_rec * server
Definition httpd.h:851
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
char * args
Definition httpd.h:1026
#define str
Apache script tools.