1 /* ------------------------------------------------------------------------- */
2 
3 /*
4  * Copyright 2007-2019 GRAHAM DUMPLETON
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 /* ------------------------------------------------------------------------- */
20 
21 #include "wsgi_apache.h"
22 #include "wsgi_python.h"
23 
24 #ifdef HAVE_SYS_PRCTL_H
25 #include <sys/prctl.h>
26 #endif
27 
28 #ifndef WIN32
29 #include <pwd.h>
30 #endif
31 
32 static PyTypeObject Auth_Type;
33 #if AP_SERVER_MINORVERSION_NUMBER >= 2
34 #define MOD_WSGI_WITH_AUTHN_PROVIDER 1
35 #endif
36 #if AP_MODULE_MAGIC_AT_LEAST(20060110,0)
37 #define MOD_WSGI_WITH_AUTHZ_PROVIDER 1
38 #if AP_MODULE_MAGIC_AT_LEAST(20100919,0)
39 #define MOD_WSGI_WITH_AUTHZ_PROVIDER_PARSED 1
40 #endif
41 #endif
42 
43 #if defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
44 #include "mod_auth.h"
45 #include "ap_provider.h"
46 #ifndef AUTHN_PROVIDER_VERSION
47 #define AUTHN_PROVIDER_VERSION "0"
48 #endif
49 #endif
50 
51 /* Local project header files. */
52 
53 #include "wsgi_version.h"
54 #include "wsgi_convert.h"
55 #include "wsgi_validate.h"
56 #include "wsgi_interp.h"
57 #include "wsgi_server.h"
58 #include "wsgi_logger.h"
59 #include "wsgi_restrict.h"
60 #include "wsgi_stream.h"
61 #include "wsgi_metrics.h"
62 #include "wsgi_daemon.h"
63 #include "wsgi_buckets.h"
64 #include "wsgi_thread.h"
65 
66 /* Module information. */
67 
68 module AP_MODULE_DECLARE_DATA wsgi_module;
69 
70 /* Process information. */
71 
72 static int wsgi_multiprocess = 1;
73 static int wsgi_multithread = 1;
74 
75 /* Daemon information. */
76 
77 static apr_array_header_t *wsgi_daemon_list = NULL;
78 
79 static apr_pool_t *wsgi_parent_pool = NULL;
80 
81 int volatile wsgi_daemon_shutdown = 0;
82 static int volatile wsgi_daemon_graceful = 0;
83 static int wsgi_dump_stack_traces = 0;
84 static char *wsgi_shutdown_reason = "";
85 
86 #if defined(MOD_WSGI_WITH_DAEMONS)
87 static apr_interval_time_t wsgi_startup_timeout = 0;
88 static apr_interval_time_t wsgi_deadlock_timeout = 0;
89 static apr_interval_time_t wsgi_idle_timeout = 0;
90 static apr_interval_time_t wsgi_request_timeout = 0;
91 static apr_interval_time_t wsgi_graceful_timeout = 0;
92 static apr_interval_time_t wsgi_eviction_timeout = 0;
93 static apr_interval_time_t wsgi_restart_interval = 0;
94 static apr_time_t volatile wsgi_startup_shutdown_time = 0;
95 static apr_time_t volatile wsgi_deadlock_shutdown_time = 0;
96 static apr_time_t volatile wsgi_idle_shutdown_time = 0;
97 static apr_time_t volatile wsgi_graceful_shutdown_time = 0;
98 static apr_time_t volatile wsgi_restart_shutdown_time = 0;
99 #endif
100 
101 /* Script information. */
102 
103 static apr_array_header_t *wsgi_import_list = NULL;
104 
wsgi_create_server_config(apr_pool_t * p,server_rec * s)105 static void *wsgi_create_server_config(apr_pool_t *p, server_rec *s)
106 {
107     WSGIServerConfig *config = NULL;
108 
109     config = newWSGIServerConfig(p);
110 
111     return config;
112 }
113 
wsgi_merge_server_config(apr_pool_t * p,void * base_conf,void * new_conf)114 static void *wsgi_merge_server_config(apr_pool_t *p, void *base_conf,
115                                       void *new_conf)
116 {
117     WSGIServerConfig *config = NULL;
118     WSGIServerConfig *parent = NULL;
119     WSGIServerConfig *child = NULL;
120 
121     config = newWSGIServerConfig(p);
122 
123     parent = (WSGIServerConfig *)base_conf;
124     child = (WSGIServerConfig *)new_conf;
125 
126     if (child->alias_list && parent->alias_list) {
127         config->alias_list = apr_array_append(p, child->alias_list,
128                                               parent->alias_list);
129     }
130     else if (child->alias_list) {
131         config->alias_list = apr_array_make(p, 20, sizeof(WSGIAliasEntry));
132         apr_array_cat(config->alias_list, child->alias_list);
133     }
134     else if (parent->alias_list) {
135         config->alias_list = apr_array_make(p, 20, sizeof(WSGIAliasEntry));
136         apr_array_cat(config->alias_list, parent->alias_list);
137     }
138 
139     if (child->restrict_process)
140         config->restrict_process = child->restrict_process;
141     else
142         config->restrict_process = parent->restrict_process;
143 
144     if (child->process_group)
145         config->process_group = child->process_group;
146     else
147         config->process_group = parent->process_group;
148 
149     if (child->application_group)
150         config->application_group = child->application_group;
151     else
152         config->application_group = parent->application_group;
153 
154     if (child->callable_object)
155         config->callable_object = child->callable_object;
156     else
157         config->callable_object = parent->callable_object;
158 
159     if (child->dispatch_script)
160         config->dispatch_script = child->dispatch_script;
161     else
162         config->dispatch_script = parent->dispatch_script;
163 
164     if (child->pass_apache_request != -1)
165         config->pass_apache_request = child->pass_apache_request;
166     else
167         config->pass_apache_request = parent->pass_apache_request;
168 
169     if (child->pass_authorization != -1)
170         config->pass_authorization = child->pass_authorization;
171     else
172         config->pass_authorization = parent->pass_authorization;
173 
174     if (child->script_reloading != -1)
175         config->script_reloading = child->script_reloading;
176     else
177         config->script_reloading = parent->script_reloading;
178 
179     if (child->error_override != -1)
180         config->error_override = child->error_override;
181     else
182         config->error_override = parent->error_override;
183 
184     if (child->chunked_request != -1)
185         config->chunked_request = child->chunked_request;
186     else
187         config->chunked_request = parent->chunked_request;
188 
189     if (child->map_head_to_get != -1)
190         config->map_head_to_get = child->map_head_to_get;
191     else
192         config->map_head_to_get = parent->map_head_to_get;
193 
194     if (child->ignore_activity != -1)
195         config->ignore_activity = child->ignore_activity;
196     else
197         config->ignore_activity = parent->ignore_activity;
198 
199     if (child->trusted_proxy_headers)
200         config->trusted_proxy_headers = child->trusted_proxy_headers;
201     else
202         config->trusted_proxy_headers = parent->trusted_proxy_headers;
203 
204     if (child->trusted_proxies)
205         config->trusted_proxies = child->trusted_proxies;
206     else
207         config->trusted_proxies = parent->trusted_proxies;
208 
209     if (child->enable_sendfile != -1)
210         config->enable_sendfile = child->enable_sendfile;
211     else
212         config->enable_sendfile = parent->enable_sendfile;
213 
214     if (!child->handler_scripts)
215         config->handler_scripts = parent->handler_scripts;
216     else if (!parent->handler_scripts)
217         config->handler_scripts = child->handler_scripts;
218     else {
219         config->handler_scripts = apr_hash_overlay(p, child->handler_scripts,
220                                                    parent->handler_scripts);
221     }
222 
223     return config;
224 }
225 
226 typedef struct {
227     apr_pool_t *pool;
228 
229     apr_table_t *restrict_process;
230 
231     const char *process_group;
232     const char *application_group;
233     const char *callable_object;
234 
235     WSGIScriptFile *dispatch_script;
236 
237     int pass_apache_request;
238     int pass_authorization;
239     int script_reloading;
240     int error_override;
241     int chunked_request;
242     int map_head_to_get;
243     int ignore_activity;
244 
245     apr_array_header_t *trusted_proxy_headers;
246     apr_array_header_t *trusted_proxies;
247 
248     int enable_sendfile;
249 
250     WSGIScriptFile *access_script;
251     WSGIScriptFile *auth_user_script;
252     WSGIScriptFile *auth_group_script;
253     int user_authoritative;
254     int group_authoritative;
255 
256     apr_hash_t *handler_scripts;
257 } WSGIDirectoryConfig;
258 
newWSGIDirectoryConfig(apr_pool_t * p)259 static WSGIDirectoryConfig *newWSGIDirectoryConfig(apr_pool_t *p)
260 {
261     WSGIDirectoryConfig *object = NULL;
262 
263     object = (WSGIDirectoryConfig *)apr_pcalloc(p, sizeof(WSGIDirectoryConfig));
264 
265     object->pool = p;
266 
267     object->process_group = NULL;
268     object->application_group = NULL;
269     object->callable_object = NULL;
270 
271     object->dispatch_script = NULL;
272 
273     object->pass_apache_request = -1;
274     object->pass_authorization = -1;
275     object->script_reloading = -1;
276     object->error_override = -1;
277     object->chunked_request = -1;
278     object->map_head_to_get = -1;
279     object->ignore_activity = -1;
280 
281     object->trusted_proxy_headers = NULL;
282     object->trusted_proxies = NULL;
283 
284     object->enable_sendfile = -1;
285 
286     object->access_script = NULL;
287     object->auth_user_script = NULL;
288     object->auth_group_script = NULL;
289     object->user_authoritative = -1;
290     object->group_authoritative = -1;
291 
292     return object;
293 }
294 
wsgi_create_dir_config(apr_pool_t * p,char * dir)295 static void *wsgi_create_dir_config(apr_pool_t *p, char *dir)
296 {
297     WSGIDirectoryConfig *config = NULL;
298 
299     config = newWSGIDirectoryConfig(p);
300 
301     return config;
302 }
303 
wsgi_merge_dir_config(apr_pool_t * p,void * base_conf,void * new_conf)304 static void *wsgi_merge_dir_config(apr_pool_t *p, void *base_conf,
305                                    void *new_conf)
306 {
307     WSGIDirectoryConfig *config = NULL;
308     WSGIDirectoryConfig *parent = NULL;
309     WSGIDirectoryConfig *child = NULL;
310 
311     config = newWSGIDirectoryConfig(p);
312 
313     parent = (WSGIDirectoryConfig *)base_conf;
314     child = (WSGIDirectoryConfig *)new_conf;
315 
316     if (child->restrict_process)
317         config->restrict_process = child->restrict_process;
318     else
319         config->restrict_process = parent->restrict_process;
320 
321     if (child->process_group)
322         config->process_group = child->process_group;
323     else
324         config->process_group = parent->process_group;
325 
326     if (child->application_group)
327         config->application_group = child->application_group;
328     else
329         config->application_group = parent->application_group;
330 
331     if (child->callable_object)
332         config->callable_object = child->callable_object;
333     else
334         config->callable_object = parent->callable_object;
335 
336     if (child->dispatch_script)
337         config->dispatch_script = child->dispatch_script;
338     else
339         config->dispatch_script = parent->dispatch_script;
340 
341     if (child->pass_apache_request != -1)
342         config->pass_apache_request = child->pass_apache_request;
343     else
344         config->pass_apache_request = parent->pass_apache_request;
345 
346     if (child->pass_authorization != -1)
347         config->pass_authorization = child->pass_authorization;
348     else
349         config->pass_authorization = parent->pass_authorization;
350 
351     if (child->script_reloading != -1)
352         config->script_reloading = child->script_reloading;
353     else
354         config->script_reloading = parent->script_reloading;
355 
356     if (child->error_override != -1)
357         config->error_override = child->error_override;
358     else
359         config->error_override = parent->error_override;
360 
361     if (child->chunked_request != -1)
362         config->chunked_request = child->chunked_request;
363     else
364         config->chunked_request = parent->chunked_request;
365 
366     if (child->map_head_to_get != -1)
367         config->map_head_to_get = child->map_head_to_get;
368     else
369         config->map_head_to_get = parent->map_head_to_get;
370 
371     if (child->ignore_activity != -1)
372         config->ignore_activity = child->ignore_activity;
373     else
374         config->ignore_activity = parent->ignore_activity;
375 
376     if (child->trusted_proxy_headers)
377         config->trusted_proxy_headers = child->trusted_proxy_headers;
378     else
379         config->trusted_proxy_headers = parent->trusted_proxy_headers;
380 
381     if (child->trusted_proxies)
382         config->trusted_proxies = child->trusted_proxies;
383     else
384         config->trusted_proxies = parent->trusted_proxies;
385 
386     if (child->enable_sendfile != -1)
387         config->enable_sendfile = child->enable_sendfile;
388     else
389         config->enable_sendfile = parent->enable_sendfile;
390 
391     if (child->access_script)
392         config->access_script = child->access_script;
393     else
394         config->access_script = parent->access_script;
395 
396     if (child->auth_user_script)
397         config->auth_user_script = child->auth_user_script;
398     else
399         config->auth_user_script = parent->auth_user_script;
400 
401     if (child->auth_group_script)
402         config->auth_group_script = child->auth_group_script;
403     else
404         config->auth_group_script = parent->auth_group_script;
405 
406     if (child->user_authoritative != -1)
407         config->user_authoritative = child->user_authoritative;
408     else
409         config->user_authoritative = parent->user_authoritative;
410 
411     if (child->group_authoritative != -1)
412         config->group_authoritative = child->group_authoritative;
413     else
414         config->group_authoritative = parent->group_authoritative;
415 
416     if (!child->handler_scripts)
417         config->handler_scripts = parent->handler_scripts;
418     else if (!parent->handler_scripts)
419         config->handler_scripts = child->handler_scripts;
420     else {
421         config->handler_scripts = apr_hash_overlay(p, child->handler_scripts,
422                                                    parent->handler_scripts);
423     }
424 
425     return config;
426 }
427 
428 typedef struct {
429     apr_pool_t *pool;
430 
431     apr_table_t *restrict_process;
432 
433     const char *process_group;
434     const char *application_group;
435     const char *callable_object;
436 
437     WSGIScriptFile *dispatch_script;
438 
439     int pass_apache_request;
440     int pass_authorization;
441     int script_reloading;
442     int error_override;
443     int chunked_request;
444     int map_head_to_get;
445     int ignore_activity;
446 
447     apr_array_header_t *trusted_proxy_headers;
448     apr_array_header_t *trusted_proxies;
449 
450     int enable_sendfile;
451 
452     WSGIScriptFile *access_script;
453     WSGIScriptFile *auth_user_script;
454     WSGIScriptFile *auth_group_script;
455     int user_authoritative;
456     int group_authoritative;
457 
458     apr_hash_t *handler_scripts;
459     const char *handler_script;
460 
461     int daemon_connects;
462     int daemon_restarts;
463 
464     apr_time_t request_start;
465     apr_time_t queue_start;
466     apr_time_t daemon_start;
467 } WSGIRequestConfig;
468 
wsgi_find_path_info(const char * uri,const char * path_info)469 static long wsgi_find_path_info(const char *uri, const char *path_info)
470 {
471     long lu = strlen(uri);
472     long lp = strlen(path_info);
473 
474     while (lu-- && lp-- && uri[lu] == path_info[lp]) {
475         if (path_info[lp] == '/') {
476             while (lu && uri[lu-1] == '/') lu--;
477         }
478     }
479 
480     if (lu == -1) {
481         lu = 0;
482     }
483 
484     while (uri[lu] != '\0' && uri[lu] != '/') {
485         lu++;
486     }
487     return lu;
488 }
489 
wsgi_script_name(request_rec * r)490 static const char *wsgi_script_name(request_rec *r)
491 {
492     char *script_name = NULL;
493     long path_info_start = 0;
494 
495     if (!r->path_info || !*r->path_info) {
496         script_name = apr_pstrdup(r->pool, r->uri);
497     }
498     else {
499         path_info_start = wsgi_find_path_info(r->uri, r->path_info);
500 
501         script_name = apr_pstrndup(r->pool, r->uri, path_info_start);
502     }
503 
504     if (*script_name) {
505         while (*script_name && (*(script_name+1) == '/'))
506             script_name++;
507         script_name = apr_pstrdup(r->pool, script_name);
508         ap_no2slash((char*)script_name);
509     }
510 
511     ap_str_tolower(script_name);
512 
513     return script_name;
514 }
515 
wsgi_process_group(request_rec * r,const char * s)516 static const char *wsgi_process_group(request_rec *r, const char *s)
517 {
518     const char *name = NULL;
519     const char *value = NULL;
520 
521     const char *h = NULL;
522     apr_port_t p = 0;
523     const char *n = NULL;
524 
525     if (!s)
526         return "";
527 
528     if (*s != '%')
529         return s;
530 
531     name = s + 1;
532 
533     if (*name) {
534         if (!strcmp(name, "{GLOBAL}"))
535             return "";
536 
537         if (!strcmp(name, "{RESOURCE}")) {
538             h = r->server->server_hostname;
539             p = ap_get_server_port(r);
540             n = wsgi_script_name(r);
541 
542             if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
543                 return apr_psprintf(r->pool, "%s:%u|%s", h, p, n);
544             else
545                 return apr_psprintf(r->pool, "%s|%s", h, n);
546         }
547 
548         if (!strcmp(name, "{SERVER}")) {
549             h = r->server->server_hostname;
550             p = ap_get_server_port(r);
551 
552             if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
553                 return apr_psprintf(r->pool, "%s:%u", h, p);
554             else
555                 return h;
556         }
557 
558         if (!strcmp(name, "{HOST}")) {
559             h = r->hostname;
560             p = ap_get_server_port(r);
561 
562             /*
563              * The Host header could be empty or absent for HTTP/1.0
564              * or older. In that case fallback to ServerName.
565              */
566 
567             if (h == NULL || *h == 0)
568                 h = r->server->server_hostname;
569 
570             if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
571                 return apr_psprintf(r->pool, "%s:%u", h, p);
572             else
573                 return h;
574         }
575 
576         if (strstr(name, "{ENV:") == name) {
577             long len = 0;
578 
579             name = name + 5;
580             len = strlen(name);
581 
582             if (len && name[len-1] == '}') {
583                 name = apr_pstrndup(r->pool, name, len-1);
584 
585                 value = apr_table_get(r->notes, name);
586 
587                 if (!value)
588                     value = apr_table_get(r->subprocess_env, name);
589 
590                 if (!value)
591                     value = getenv(name);
592 
593                 if (value) {
594                     if (*value == '%' && strstr(value, "%{ENV:") != value)
595                         return wsgi_process_group(r, value);
596 
597                     return value;
598                 }
599             }
600         }
601     }
602 
603     return s;
604 }
605 
wsgi_server_group(request_rec * r,const char * s)606 static const char *wsgi_server_group(request_rec *r, const char *s)
607 {
608     const char *name = NULL;
609 
610     const char *h = NULL;
611     apr_port_t p = 0;
612 
613     if (!s)
614         return "";
615 
616     if (*s != '%')
617         return s;
618 
619     name = s + 1;
620 
621     if (*name) {
622         if (!strcmp(name, "{GLOBAL}"))
623             return "";
624 
625         if (!strcmp(name, "{SERVER}")) {
626             h = r->server->server_hostname;
627             p = ap_get_server_port(r);
628 
629             if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
630                 return apr_psprintf(r->pool, "%s:%u", h, p);
631             else
632                 return h;
633         }
634 
635         if (!strcmp(name, "{HOST}")) {
636             h = r->hostname;
637             p = ap_get_server_port(r);
638 
639             /*
640              * The Host header could be empty or absent for HTTP/1.0
641              * or older. In that case fallback to ServerName.
642              */
643 
644             if (h == NULL || *h == 0)
645                 h = r->server->server_hostname;
646 
647             if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
648                 return apr_psprintf(r->pool, "%s:%u", h, p);
649             else
650                 return h;
651         }
652     }
653 
654     return s;
655 }
656 
wsgi_application_group(request_rec * r,const char * s)657 static const char *wsgi_application_group(request_rec *r, const char *s)
658 {
659     const char *name = NULL;
660     const char *value = NULL;
661 
662     const char *h = NULL;
663     apr_port_t p = 0;
664     const char *n = NULL;
665 
666     if (!s) {
667         h = r->server->server_hostname;
668         p = ap_get_server_port(r);
669         n = wsgi_script_name(r);
670 
671         if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
672             return apr_psprintf(r->pool, "%s:%u|%s", h, p, n);
673         else
674             return apr_psprintf(r->pool, "%s|%s", h, n);
675     }
676 
677     if (*s != '%')
678         return s;
679 
680     name = s + 1;
681 
682     if (*name) {
683         if (!strcmp(name, "{GLOBAL}"))
684             return "";
685 
686         if (!strcmp(name, "{RESOURCE}")) {
687             h = r->server->server_hostname;
688             p = ap_get_server_port(r);
689             n = wsgi_script_name(r);
690 
691             if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
692                 return apr_psprintf(r->pool, "%s:%u|%s", h, p, n);
693             else
694                 return apr_psprintf(r->pool, "%s|%s", h, n);
695         }
696 
697         if (!strcmp(name, "{SERVER}")) {
698             h = r->server->server_hostname;
699             p = ap_get_server_port(r);
700 
701             if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
702                 return apr_psprintf(r->pool, "%s:%u", h, p);
703             else
704                 return h;
705         }
706 
707         if (!strcmp(name, "{HOST}")) {
708             h = r->hostname;
709             p = ap_get_server_port(r);
710 
711             /*
712              * The Host header could be empty or absent for HTTP/1.0
713              * or older. In that case fallback to ServerName.
714              */
715 
716             if (h == NULL || *h == 0)
717                 h = r->server->server_hostname;
718 
719             if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
720                 return apr_psprintf(r->pool, "%s:%u", h, p);
721             else
722                 return h;
723         }
724 
725         if (strstr(name, "{ENV:") == name) {
726             long len = 0;
727 
728             name = name + 5;
729             len = strlen(name);
730 
731             if (len && name[len-1] == '}') {
732                 name = apr_pstrndup(r->pool, name, len-1);
733 
734                 value = apr_table_get(r->notes, name);
735 
736                 if (!value)
737                     value = apr_table_get(r->subprocess_env, name);
738 
739                 if (!value)
740                     value = getenv(name);
741 
742                 if (value) {
743                     if (*value == '%' && strstr(value, "%{ENV:") != value)
744                         return wsgi_application_group(r, value);
745 
746                     return value;
747                 }
748             }
749         }
750     }
751 
752     return s;
753 }
754 
wsgi_callable_object(request_rec * r,const char * s)755 static const char *wsgi_callable_object(request_rec *r, const char *s)
756 {
757     const char *name = NULL;
758     const char *value = NULL;
759 
760     if (!s)
761         return "application";
762 
763     if (*s != '%')
764         return s;
765 
766     name = s + 1;
767 
768     if (!*name)
769         return "application";
770 
771     if (strstr(name, "{ENV:") == name) {
772         long len = 0;
773 
774         name = name + 5;
775         len = strlen(name);
776 
777         if (len && name[len-1] == '}') {
778             name = apr_pstrndup(r->pool, name, len-1);
779 
780             value = apr_table_get(r->notes, name);
781 
782             if (!value)
783                 value = apr_table_get(r->subprocess_env, name);
784 
785             if (!value)
786                 value = getenv(name);
787 
788             if (value)
789                 return value;
790         }
791     }
792 
793     return "application";
794 }
795 
wsgi_create_req_config(apr_pool_t * p,request_rec * r)796 static WSGIRequestConfig *wsgi_create_req_config(apr_pool_t *p, request_rec *r)
797 {
798     WSGIRequestConfig *config = NULL;
799     WSGIServerConfig *sconfig = NULL;
800     WSGIDirectoryConfig *dconfig = NULL;
801 
802     config = (WSGIRequestConfig *)apr_pcalloc(p, sizeof(WSGIRequestConfig));
803 
804     dconfig = ap_get_module_config(r->per_dir_config, &wsgi_module);
805     sconfig = ap_get_module_config(r->server->module_config, &wsgi_module);
806 
807     config->pool = p;
808 
809     config->restrict_process = dconfig->restrict_process;
810 
811     if (!config->restrict_process)
812         config->restrict_process = sconfig->restrict_process;
813 
814     config->process_group = dconfig->process_group;
815 
816     if (!config->process_group)
817         config->process_group = sconfig->process_group;
818 
819     config->process_group = wsgi_process_group(r, config->process_group);
820 
821     config->application_group = dconfig->application_group;
822 
823     if (!config->application_group)
824         config->application_group = sconfig->application_group;
825 
826     config->application_group = wsgi_application_group(r,
827                                     config->application_group);
828 
829     config->callable_object = dconfig->callable_object;
830 
831     if (!config->callable_object)
832         config->callable_object = sconfig->callable_object;
833 
834     config->callable_object = wsgi_callable_object(r, config->callable_object);
835 
836     config->dispatch_script = dconfig->dispatch_script;
837 
838     if (!config->dispatch_script)
839         config->dispatch_script = sconfig->dispatch_script;
840 
841     config->pass_apache_request = dconfig->pass_apache_request;
842 
843     if (config->pass_apache_request < 0) {
844         config->pass_apache_request = sconfig->pass_apache_request;
845         if (config->pass_apache_request < 0)
846             config->pass_apache_request = 0;
847     }
848 
849     config->pass_authorization = dconfig->pass_authorization;
850 
851     if (config->pass_authorization < 0) {
852         config->pass_authorization = sconfig->pass_authorization;
853         if (config->pass_authorization < 0)
854             config->pass_authorization = 0;
855     }
856 
857     config->script_reloading = dconfig->script_reloading;
858 
859     if (config->script_reloading < 0) {
860         config->script_reloading = sconfig->script_reloading;
861         if (config->script_reloading < 0)
862             config->script_reloading = 1;
863     }
864 
865     config->error_override = dconfig->error_override;
866 
867     if (config->error_override < 0) {
868         config->error_override = sconfig->error_override;
869         if (config->error_override < 0)
870             config->error_override = 0;
871     }
872 
873     config->chunked_request = dconfig->chunked_request;
874 
875     if (config->chunked_request < 0) {
876         config->chunked_request = sconfig->chunked_request;
877         if (config->chunked_request < 0)
878             config->chunked_request = 0;
879     }
880 
881     config->map_head_to_get = dconfig->map_head_to_get;
882 
883     if (config->map_head_to_get < 0) {
884         config->map_head_to_get = sconfig->map_head_to_get;
885         if (config->map_head_to_get < 0)
886             config->map_head_to_get = 2;
887     }
888 
889     config->ignore_activity = dconfig->ignore_activity;
890 
891     if (config->ignore_activity < 0) {
892         config->ignore_activity = sconfig->ignore_activity;
893         if (config->ignore_activity < 0)
894             config->ignore_activity = 0;
895     }
896 
897     config->trusted_proxy_headers = dconfig->trusted_proxy_headers;
898 
899     if (!config->trusted_proxy_headers)
900         config->trusted_proxy_headers = sconfig->trusted_proxy_headers;
901 
902     config->trusted_proxies = dconfig->trusted_proxies;
903 
904     if (!config->trusted_proxies)
905         config->trusted_proxies = sconfig->trusted_proxies;
906 
907     config->enable_sendfile = dconfig->enable_sendfile;
908 
909     if (config->enable_sendfile < 0) {
910         config->enable_sendfile = sconfig->enable_sendfile;
911         if (config->enable_sendfile < 0)
912             config->enable_sendfile = 0;
913     }
914 
915     config->access_script = dconfig->access_script;
916 
917     config->auth_user_script = dconfig->auth_user_script;
918 
919     config->auth_group_script = dconfig->auth_group_script;
920 
921     config->user_authoritative = dconfig->user_authoritative;
922 
923     if (config->user_authoritative == -1)
924         config->user_authoritative = 1;
925 
926     config->group_authoritative = dconfig->group_authoritative;
927 
928     if (config->group_authoritative == -1)
929         config->group_authoritative = 1;
930 
931     if (!dconfig->handler_scripts)
932         config->handler_scripts = sconfig->handler_scripts;
933     else if (!sconfig->handler_scripts)
934         config->handler_scripts = dconfig->handler_scripts;
935     else {
936         config->handler_scripts = apr_hash_overlay(p, dconfig->handler_scripts,
937                                                    sconfig->handler_scripts);
938     }
939 
940     config->handler_script = "";
941 
942     config->daemon_connects = 0;
943     config->daemon_restarts = 0;
944 
945     config->request_start = 0;
946     config->queue_start = 0;
947     config->daemon_start = 0;
948 
949     return config;
950 }
951 
952 /* Error reporting. */
953 
wsgi_log_script_error(request_rec * r,const char * e,const char * n)954 static void wsgi_log_script_error(request_rec *r, const char *e, const char *n)
955 {
956     char *message = NULL;
957 
958     if (!n)
959         n = r->filename;
960 
961     message = apr_psprintf(r->pool, "%s: %s", e, n);
962 
963     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", message);
964 }
965 
966 /* Class objects used by response handler. */
967 
968 static PyTypeObject Dispatch_Type;
969 
970 typedef struct {
971         PyObject_HEAD
972         request_rec *r;
973         int init;
974         int done;
975         char *buffer;
976         apr_off_t size;
977         apr_off_t offset;
978         apr_off_t length;
979         apr_bucket_brigade *bb;
980         int seen_eos;
981         int seen_error;
982         apr_off_t bytes;
983         apr_off_t reads;
984         apr_time_t time;
985         int ignore_activity;
986 } InputObject;
987 
988 static PyTypeObject Input_Type;
989 
newInputObject(request_rec * r,int ignore_activity)990 static InputObject *newInputObject(request_rec *r, int ignore_activity)
991 {
992     InputObject *self;
993 
994     self = PyObject_New(InputObject, &Input_Type);
995     if (self == NULL)
996         return NULL;
997 
998     self->r = r;
999     self->init = 0;
1000     self->done = 0;
1001 
1002     self->buffer = NULL;
1003     self->size = 0;
1004     self->offset = 0;
1005     self->length = 0;
1006 
1007     self->bb = NULL;
1008 
1009     self->seen_eos = 0;
1010     self->seen_error = 0;
1011 
1012     self->bytes = 0;
1013     self->reads = 0;
1014     self->time = 0;
1015 
1016     self->ignore_activity = ignore_activity;
1017 
1018     return self;
1019 }
1020 
Input_dealloc(InputObject * self)1021 static void Input_dealloc(InputObject *self)
1022 {
1023     if (self->buffer)
1024         free(self->buffer);
1025 
1026     PyObject_Del(self);
1027 }
1028 
Input_finish(InputObject * self)1029 static void Input_finish(InputObject *self)
1030 {
1031     if (self->bb) {
1032         Py_BEGIN_ALLOW_THREADS
1033         apr_brigade_destroy(self->bb);
1034         Py_END_ALLOW_THREADS
1035 
1036         self->bb = NULL;
1037     }
1038 
1039     self->r = NULL;
1040 }
1041 
Input_close(InputObject * self,PyObject * args)1042 static PyObject *Input_close(InputObject *self, PyObject *args)
1043 {
1044     if (!self->r) {
1045         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1046         return NULL;
1047     }
1048 
1049     Py_INCREF(Py_None);
1050     return Py_None;
1051 }
1052 
wsgi_strtoff(apr_off_t * offset,const char * nptr,char ** endptr,int base)1053 static apr_status_t wsgi_strtoff(apr_off_t *offset, const char *nptr,
1054                                  char **endptr, int base)
1055 {
1056    errno = 0;
1057    if (sizeof(apr_off_t) == 4) {
1058        *offset = strtol(nptr, endptr, base);
1059    }
1060    else {
1061        *offset = apr_strtoi64(nptr, endptr, base);
1062    }
1063    return APR_FROM_OS_ERROR(errno);
1064 }
1065 
Input_read_from_input(InputObject * self,char * buffer,apr_size_t bufsiz)1066 static apr_int64_t Input_read_from_input(InputObject *self, char *buffer,
1067                                   apr_size_t bufsiz)
1068 {
1069     request_rec *r = self->r;
1070     apr_bucket_brigade *bb = self->bb;
1071 
1072     apr_status_t rv;
1073 
1074     apr_status_t error_status = 0;
1075     const char *error_message = NULL;
1076 
1077     apr_time_t start = 0;
1078     apr_time_t finish = 0;
1079 
1080     /* If have already seen end of input, return an empty string. */
1081 
1082     if (self->seen_eos)
1083         return 0;
1084 
1085     /* If have already encountered an error, then raise a new error. */
1086 
1087     if (self->seen_error) {
1088         PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi request data read "
1089                 "error: Input is already in error state.");
1090 
1091         return -1;
1092     }
1093 
1094     /*
1095      * When reaading the request content we will be saying that we
1096      * should block if there is no input data available at that
1097      * point but not all data has been exhausted. We therefore need
1098      * to ensure that we do not cause Python as a whole to block by
1099      * releasing the GIL, but also must remember to reacquire the GIL
1100      * when we exit.
1101      */
1102 
1103     Py_BEGIN_ALLOW_THREADS
1104 
1105     start = apr_time_now();
1106 
1107     self->reads += 1;
1108 
1109     /*
1110      * Create the bucket brigade the first time it is required and
1111      * save it against the input object. We need to make sure we
1112      * perform a cleanup, but not destroy, the bucket brigade each
1113      * time we exit this function.
1114      */
1115 
1116     if (!bb) {
1117         bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
1118 
1119         if (bb == NULL) {
1120             r->connection->keepalive = AP_CONN_CLOSE;
1121             error_message = "Unable to create bucket brigade";
1122             goto finally;
1123         }
1124 
1125         self->bb = bb;
1126     }
1127 
1128     /* Force the required amount of input to be read. */
1129 
1130     rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
1131                         APR_BLOCK_READ, bufsiz);
1132 
1133     if (rv != APR_SUCCESS) {
1134         /*
1135          * If we actually fail here, we want to just return and
1136          * stop trying to read data from the client. The HTTP_IN
1137          * input filter is a bit of a pain here as it can return
1138          * EAGAIN in various strange situations where it isn't
1139          * believed that it means to retry, but that it is still
1140          * a permanent failure. This can include timeouts and
1141          * errors in chunked encoding format. To avoid a message
1142          * of 'Resource temporarily unavailable' which could be
1143          * confusing, replace it with a generic message that the
1144          * connection was terminated.
1145          */
1146 
1147         r->connection->keepalive = AP_CONN_CLOSE;
1148 
1149         if (APR_STATUS_IS_EAGAIN(rv))
1150             error_message = "Connection was terminated";
1151         else
1152             error_status = rv;
1153 
1154         goto finally;
1155     }
1156 
1157     /*
1158      * If this fails, it means that a filter is written incorrectly and
1159      * that it needs to learn how to properly handle APR_BLOCK_READ
1160      * requests by returning data when requested.
1161      */
1162 
1163     AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(bb));
1164 
1165     /*
1166      * Check to see if EOS terminates the brigade. If so, we remember
1167      * this to avoid any attempts to read more data in future calls.
1168      */
1169 
1170     if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)))
1171         self->seen_eos = 1;
1172 
1173     /* Now extract the actual data from the bucket brigade. */
1174 
1175     rv = apr_brigade_flatten(bb, buffer, &bufsiz);
1176 
1177     if (rv != APR_SUCCESS) {
1178         error_status = rv;
1179         goto finally;
1180     }
1181 
1182 finally:
1183     /*
1184      * We must always cleanup up, not destroy, the brigade after
1185      * each call.
1186      */
1187 
1188     if (bb)
1189         apr_brigade_cleanup(bb);
1190 
1191     finish = apr_time_now();
1192 
1193     if (finish > start)
1194         self->time += (finish - start);
1195 
1196     /* Make sure we reacquire the GIL when all done. */
1197 
1198     Py_END_ALLOW_THREADS
1199 
1200     /*
1201      * Set any Python exception when an error has occurred and
1202      * remember there was an error so can flag on subsequent
1203      * reads that already in an error state.
1204      */
1205 
1206     if (error_status) {
1207         char status_buffer[512];
1208 
1209         error_message = apr_psprintf(r->pool, "Apache/mod_wsgi request "
1210                 "data read error: %s.", apr_strerror(error_status,
1211                 status_buffer, sizeof(status_buffer)-1));
1212 
1213         PyErr_SetString(PyExc_IOError, error_message);
1214 
1215         self->seen_error = 1;
1216 
1217         return -1;
1218     }
1219     else if (error_message) {
1220         error_message = apr_psprintf(r->pool, "Apache/mod_wsgi request "
1221                 "data read error: %s.", error_message);
1222 
1223         PyErr_SetString(PyExc_IOError, error_message);
1224 
1225         self->seen_error = 1;
1226 
1227         return -1;
1228     }
1229 
1230     /*
1231      * Finally return the amount of data that was read. This will be
1232      * zero if all data has been consumed.
1233      */
1234 
1235     return bufsiz;
1236 }
1237 
Input_read(InputObject * self,PyObject * args)1238 static PyObject *Input_read(InputObject *self, PyObject *args)
1239 {
1240 #if defined(HAVE_LONG_LONG)
1241     PY_LONG_LONG size = -1;
1242 #else
1243     long size = -1;
1244 #endif
1245 
1246     PyObject *result = NULL;
1247     char *buffer = NULL;
1248     apr_off_t length = 0;
1249     int init = 0;
1250 
1251     apr_int64_t n;
1252 
1253     if (!self->r) {
1254         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1255         return NULL;
1256     }
1257 
1258 #if defined(HAVE_LONG_LONG)
1259     if (!PyArg_ParseTuple(args, "|L:read", &size))
1260         return NULL;
1261 #else
1262     if (!PyArg_ParseTuple(args, "|l:read", &size))
1263         return NULL;
1264 #endif
1265 
1266 #if defined(MOD_WSGI_WITH_DAEMONS)
1267     if (wsgi_idle_timeout && !self->ignore_activity) {
1268         apr_thread_mutex_lock(wsgi_monitor_lock);
1269 
1270         if (wsgi_idle_timeout) {
1271             wsgi_idle_shutdown_time = apr_time_now();
1272             wsgi_idle_shutdown_time += wsgi_idle_timeout;
1273         }
1274 
1275         apr_thread_mutex_unlock(wsgi_monitor_lock);
1276     }
1277 #endif
1278 
1279     if (self->seen_error) {
1280         PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi request data read "
1281                 "error: Input is already in error state.");
1282 
1283         return NULL;
1284     }
1285 
1286     init = self->init;
1287 
1288     if (!self->init)
1289         self->init = 1;
1290 
1291     /* No point continuing if no more data to be consumed. */
1292 
1293     if (self->done && self->length == 0)
1294         return PyString_FromString("");
1295 
1296     /*
1297      * If requested size is zero bytes, then still need to pass
1298      * this through to Apache input filters so that any
1299      * 100-continue response is triggered. Only do this if very
1300      * first attempt to read data. Note that this will cause an
1301      * assertion failure in HTTP_IN input filter when Apache
1302      * maintainer mode is enabled. It is arguable that the
1303      * assertion check, which prohibits a zero length read,
1304      * shouldn't exist, as why should a zero length read be not
1305      * allowed if input filter processing still works when it
1306      * does occur.
1307      */
1308 
1309     if (size == 0) {
1310         if (!init) {
1311             char dummy[1];
1312 
1313             n = Input_read_from_input(self, dummy, 0);
1314 
1315             if (n == -1)
1316                 return NULL;
1317         }
1318 
1319         return PyString_FromString("");
1320     }
1321 
1322     /*
1323      * First deal with case where size has been specified. After
1324      * that deal with case where expected that all remaining
1325      * data is to be read in and returned as one string.
1326      */
1327 
1328     if (size > 0) {
1329         /* Allocate string of the exact size required. */
1330 
1331         result = PyString_FromStringAndSize(NULL, size);
1332 
1333         if (!result)
1334             return NULL;
1335 
1336         buffer = PyString_AS_STRING((PyStringObject *)result);
1337 
1338         /* Copy any residual data from use of readline(). */
1339 
1340         if (self->buffer && self->length) {
1341             if (size >= self->length) {
1342                 length = self->length;
1343                 memcpy(buffer, self->buffer + self->offset, length);
1344                 self->offset = 0;
1345                 self->length = 0;
1346             }
1347             else {
1348                 length = size;
1349                 memcpy(buffer, self->buffer + self->offset, length);
1350                 self->offset += length;
1351                 self->length -= length;
1352             }
1353         }
1354 
1355         /* If all data residual buffer consumed then free it. */
1356 
1357         if (!self->length) {
1358             free(self->buffer);
1359             self->buffer = NULL;
1360         }
1361 
1362         /* Read in remaining data required to achieve size. */
1363 
1364         if (length < size) {
1365             while (length != size) {
1366                 n = Input_read_from_input(self, buffer+length, size-length);
1367 
1368                 if (n == -1) {
1369                     Py_DECREF(result);
1370                     return NULL;
1371                 }
1372                 else if (n == 0) {
1373                     /* Have exhausted all the available input data. */
1374 
1375                     self->done = 1;
1376                     break;
1377                 }
1378 
1379                 length += n;
1380             }
1381 
1382             /*
1383              * Resize the final string. If the size reduction is
1384              * by more than 25% of the string size, then Python
1385              * will allocate a new block of memory and copy the
1386              * data into it.
1387              */
1388 
1389             if (length != size) {
1390                 if (_PyString_Resize(&result, length))
1391                     return NULL;
1392             }
1393         }
1394     }
1395     else {
1396         /*
1397          * Here we are going to try and read in all the
1398          * remaining data. First we have to allocate a suitably
1399          * large string, but we can't fully trust the amount
1400          * that the request structure says is remaining based on
1401          * the original content length though, as an input
1402          * filter can insert/remove data from the input stream
1403          * thereby invalidating the original content length.
1404          * What we do is allow for an extra 25% above what we
1405          * have already buffered and what the request structure
1406          * says is remaining. A value of 25% has been chosen so
1407          * as to match best how Python handles resizing of
1408          * strings. Note that even though we do this and allow
1409          * all available content, strictly speaking the WSGI
1410          * specification says we should only read up until content
1411          * length. This though is because the WSGI specification
1412          * is deficient in dealing with the concept of mutating
1413          * input filters. Since read() with no argument is also
1414          * not allowed by WSGI specification implement it in the
1415          * way which is most logical and ensure that input data
1416          * is not truncated.
1417          */
1418 
1419         if (self->buffer) {
1420             size = self->length;
1421             size = size + (size >> 2);
1422 
1423             if (size < HUGE_STRING_LEN)
1424                 size = HUGE_STRING_LEN;
1425         }
1426         else
1427             size = HUGE_STRING_LEN;
1428 
1429         /* Allocate string of the estimated size. */
1430 
1431         result = PyString_FromStringAndSize(NULL, size);
1432 
1433         if (!result)
1434             return NULL;
1435 
1436         buffer = PyString_AS_STRING((PyStringObject *)result);
1437 
1438         /*
1439          * Copy any residual data from use of readline(). The
1440          * residual should always be less in size than the
1441          * string we have allocated to hold it, so can consume
1442          * all of it.
1443          */
1444 
1445         if (self->buffer && self->length) {
1446             length = self->length;
1447             memcpy(buffer, self->buffer + self->offset, length);
1448             self->offset = 0;
1449             self->length = 0;
1450 
1451             free(self->buffer);
1452             self->buffer = NULL;
1453         }
1454 
1455         /* Now make first attempt at reading remaining data. */
1456 
1457         n = Input_read_from_input(self, buffer+length, size-length);
1458 
1459         if (n == -1) {
1460             Py_DECREF(result);
1461             return NULL;
1462         }
1463         else if (n == 0) {
1464             /* Have exhausted all the available input data. */
1465 
1466             self->done = 1;
1467         }
1468 
1469         length += n;
1470 
1471         /*
1472          * Don't just assume that all data has been read if
1473          * amount read was less than that requested. Still must
1474          * perform a read which returns that no more data found.
1475          */
1476 
1477         while (!self->done) {
1478             if (length == size) {
1479                 /* Increase the size of the string by 25%. */
1480 
1481                 size = size + (size >> 2);
1482 
1483                 if (_PyString_Resize(&result, size))
1484                     return NULL;
1485 
1486                 buffer = PyString_AS_STRING((PyStringObject *)result);
1487             }
1488 
1489             /* Now make succesive attempt at reading data. */
1490 
1491             n = Input_read_from_input(self, buffer+length, size-length);
1492 
1493             if (n == -1) {
1494                 Py_DECREF(result);
1495                 return NULL;
1496             }
1497             else if (n == 0) {
1498                 /* Have exhausted all the available input data. */
1499 
1500                 self->done = 1;
1501             }
1502 
1503             length += n;
1504         }
1505 
1506         /*
1507          * Resize the final string. If the size reduction is by
1508          * more than 25% of the string size, then Python will
1509          * allocate a new block of memory and copy the data into
1510          * it.
1511          */
1512 
1513         if (length != size) {
1514             if (_PyString_Resize(&result, length))
1515                 return NULL;
1516         }
1517     }
1518 
1519     self->bytes += length;
1520 
1521     return result;
1522 }
1523 
Input_readline(InputObject * self,PyObject * args)1524 static PyObject *Input_readline(InputObject *self, PyObject *args)
1525 {
1526 #if defined(HAVE_LONG_LONG)
1527     PY_LONG_LONG size = -1;
1528 #else
1529     long size = -1;
1530 #endif
1531 
1532     PyObject *result = NULL;
1533     char *buffer = NULL;
1534     apr_off_t length = 0;
1535 
1536     apr_int64_t n;
1537 
1538     if (!self->r) {
1539         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1540         return NULL;
1541     }
1542 
1543 #if defined(HAVE_LONG_LONG)
1544     if (!PyArg_ParseTuple(args, "|L:readline", &size))
1545         return NULL;
1546 #else
1547     if (!PyArg_ParseTuple(args, "|l:readline", &size))
1548         return NULL;
1549 #endif
1550 
1551     if (self->seen_error) {
1552         PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi request data read "
1553                 "error: Input is already in error state.");
1554 
1555         return NULL;
1556     }
1557 
1558     if (!self->init)
1559         self->init = 1;
1560 
1561     /*
1562      * No point continuing if requested size is zero or if no
1563      * more data to read and no buffered data.
1564      */
1565 
1566     if ((self->done && self->length == 0) || size == 0)
1567         return PyString_FromString("");
1568 
1569     /*
1570      * First deal with case where size has been specified. After
1571      * that deal with case where expected that a complete line
1572      * is returned regardless of the size.
1573      */
1574 
1575     if (size > 0) {
1576         /* Allocate string of the exact size required. */
1577 
1578         result = PyString_FromStringAndSize(NULL, size);
1579 
1580         if (!result)
1581             return NULL;
1582 
1583         buffer = PyString_AS_STRING((PyStringObject *)result);
1584 
1585         /* Copy any residual data from use of readline(). */
1586 
1587         if (self->buffer && self->length) {
1588             char *p = NULL;
1589             const char *q = NULL;
1590 
1591             p = buffer;
1592             q = self->buffer + self->offset;
1593 
1594             while (self->length && length < size) {
1595                 self->offset++;
1596                 self->length--;
1597                 length++;
1598                 if ((*p++ = *q++) == '\n')
1599                     break;
1600             }
1601 
1602             /* If all data in residual buffer consumed then free it. */
1603 
1604             if (!self->length) {
1605                 free(self->buffer);
1606                 self->buffer = NULL;
1607             }
1608         }
1609 
1610         /*
1611          * Read in remaining data required to achieve size. Note
1612          * that can't just return whatever the first read might
1613          * have returned if no EOL encountered as must return
1614          * exactly the required size if no EOL unless that would
1615          * have exhausted all input.
1616          */
1617 
1618         while ((!length || buffer[length-1] != '\n') &&
1619                !self->done && length < size) {
1620 
1621             char *p = NULL;
1622             char *q = NULL;
1623 
1624             n = Input_read_from_input(self, buffer+length, size-length);
1625 
1626             if (n == -1) {
1627                 Py_DECREF(result);
1628                 return NULL;
1629             }
1630             else if (n == 0) {
1631                 /* Have exhausted all the available input data. */
1632 
1633                 self->done = 1;
1634             }
1635             else {
1636                 /*
1637                  * Search for embedded EOL in what was read and if
1638                  * found copy any residual into a buffer for use
1639                  * next time the read functions are called.
1640                  */
1641 
1642                 p = buffer + length;
1643                 q = p + n;
1644 
1645                 while (p != q) {
1646                     length++;
1647                     if (*p++ == '\n')
1648                         break;
1649                 }
1650 
1651                 if (p != q) {
1652                     self->size = q - p;
1653                     self->buffer = (char *)malloc(self->size);
1654                     self->offset = 0;
1655                     self->length = self->size;
1656 
1657                     memcpy(self->buffer, p, self->size);
1658                 }
1659             }
1660         }
1661 
1662         /*
1663          * Resize the final string. If the size reduction is
1664          * by more than 25% of the string size, then Python
1665          * will allocate a new block of memory and copy the
1666          * data into it.
1667          */
1668 
1669         if (length != size) {
1670             if (_PyString_Resize(&result, length))
1671                 return NULL;
1672         }
1673     }
1674     else {
1675         /*
1676          * Here we have to read in a line but where we have no
1677          * idea how long it may be. What we can do first is if
1678          * we have any residual data from a previous read
1679          * operation, see if it contains an EOL. This means we
1680          * have to do a search, but this is likely going to be
1681          * better than having to resize and copy memory later on.
1682          */
1683 
1684         if (self->buffer && self->length) {
1685             const char *p = NULL;
1686             const char *q = NULL;
1687 
1688             p = self->buffer + self->offset;
1689             q = memchr(p, '\n', self->length);
1690 
1691             if (q)
1692                 size = q - p;
1693         }
1694 
1695         /*
1696          * If residual data buffer didn't contain an EOL, all we
1697          * can do is allocate a reasonably sized string and if
1698          * that isn't big enough keep increasing it in size. For
1699          * this we will start out with a buffer 25% greater in
1700          * size than what is stored in the residual data buffer
1701          * or one the same size as Apache string size, whichever
1702          * is greater.
1703          */
1704 
1705         if (self->buffer && size < 0) {
1706             size = self->length;
1707             size = size + (size >> 2);
1708         }
1709 
1710         if (size < HUGE_STRING_LEN)
1711             size = HUGE_STRING_LEN;
1712 
1713         /* Allocate string of the initial size. */
1714 
1715         result = PyString_FromStringAndSize(NULL, size);
1716 
1717         if (!result)
1718             return NULL;
1719 
1720         buffer = PyString_AS_STRING((PyStringObject *)result);
1721 
1722         /* Copy any residual data from use of readline(). */
1723 
1724         if (self->buffer && self->length) {
1725             char *p = NULL;
1726             const char *q = NULL;
1727 
1728             p = buffer;
1729             q = self->buffer + self->offset;
1730 
1731             while (self->length && length < size) {
1732                 self->offset++;
1733                 self->length--;
1734                 length++;
1735                 if ((*p++ = *q++) == '\n')
1736                     break;
1737             }
1738 
1739             /* If all data in residual buffer consumed then free it. */
1740 
1741             if (!self->length) {
1742                 free(self->buffer);
1743                 self->buffer = NULL;
1744             }
1745         }
1746 
1747         /*
1748          * Read in remaining data until find an EOL, or until all
1749          * data has been consumed.
1750          */
1751 
1752         while ((!length || buffer[length-1] != '\n') && !self->done) {
1753 
1754             char *p = NULL;
1755             char *q = NULL;
1756 
1757             n = Input_read_from_input(self, buffer+length, size-length);
1758 
1759             if (n == -1) {
1760                 Py_DECREF(result);
1761                 return NULL;
1762             }
1763             else if (n == 0) {
1764                 /* Have exhausted all the available input data. */
1765 
1766                 self->done = 1;
1767             }
1768             else {
1769                 /*
1770                  * Search for embedded EOL in what was read and if
1771                  * found copy any residual into a buffer for use
1772                  * next time the read functions are called.
1773                  */
1774 
1775                 p = buffer + length;
1776                 q = p + n;
1777 
1778                 while (p != q) {
1779                     length++;
1780                     if (*p++ == '\n')
1781                         break;
1782                 }
1783 
1784                 if (p != q) {
1785                     self->size = q - p;
1786                     self->buffer = (char *)malloc(self->size);
1787                     self->offset = 0;
1788                     self->length = self->size;
1789 
1790                     memcpy(self->buffer, p, self->size);
1791                 }
1792 
1793                 if (buffer[length-1] != '\n' && length == size) {
1794                     /* Increase size of string and keep going. */
1795 
1796                     size = size + (size >> 2);
1797 
1798                     if (_PyString_Resize(&result, size))
1799                         return NULL;
1800 
1801                     buffer = PyString_AS_STRING((PyStringObject *)result);
1802                 }
1803             }
1804         }
1805 
1806         /*
1807          * Resize the final string. If the size reduction is by
1808          * more than 25% of the string size, then Python will
1809          * allocate a new block of memory and copy the data into
1810          * it.
1811          */
1812 
1813         if (length != size) {
1814             if (_PyString_Resize(&result, length))
1815                 return NULL;
1816         }
1817     }
1818 
1819     self->bytes += length;
1820 
1821     return result;
1822 }
1823 
Input_readlines(InputObject * self,PyObject * args)1824 static PyObject *Input_readlines(InputObject *self, PyObject *args)
1825 {
1826     long hint = 0;
1827     long length = 0;
1828 
1829     PyObject *result = NULL;
1830     PyObject *line = NULL;
1831     PyObject *rlargs = NULL;
1832 
1833     if (!self->r) {
1834         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1835         return NULL;
1836     }
1837 
1838     if (!PyArg_ParseTuple(args, "|l:readlines", &hint))
1839         return NULL;
1840 
1841     result = PyList_New(0);
1842     if (!result)
1843         return NULL;
1844 
1845     rlargs = PyTuple_New(0);
1846     if (!rlargs) {
1847         Py_DECREF(result);
1848         return NULL;
1849     }
1850 
1851     while (1) {
1852         long n;
1853 
1854         if (!(line = Input_readline(self, rlargs))) {
1855             Py_DECREF(result);
1856             result = NULL;
1857             break;
1858         }
1859 
1860         if ((n = PyString_Size(line)) == 0) {
1861             Py_DECREF(line);
1862             break;
1863         }
1864 
1865         if (PyList_Append(result, line) == -1) {
1866             Py_DECREF(line);
1867             Py_DECREF(result);
1868             result = NULL;
1869             break;
1870         }
1871 
1872         Py_DECREF(line);
1873 
1874         length += n;
1875         if (hint > 0 && length >= hint)
1876             break;
1877     }
1878 
1879     Py_DECREF(rlargs);
1880 
1881     return result;
1882 }
1883 
1884 static PyMethodDef Input_methods[] = {
1885     { "close",     (PyCFunction)Input_close,     METH_NOARGS, 0 },
1886     { "read",      (PyCFunction)Input_read,      METH_VARARGS, 0 },
1887     { "readline",  (PyCFunction)Input_readline,  METH_VARARGS, 0 },
1888     { "readlines", (PyCFunction)Input_readlines, METH_VARARGS, 0 },
1889     { NULL, NULL}
1890 };
1891 
Input_iter(InputObject * self)1892 static PyObject *Input_iter(InputObject *self)
1893 {
1894     if (!self->r) {
1895         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1896         return NULL;
1897     }
1898 
1899     Py_INCREF(self);
1900     return (PyObject *)self;
1901 }
1902 
Input_iternext(InputObject * self)1903 static PyObject *Input_iternext(InputObject *self)
1904 {
1905     PyObject *line = NULL;
1906     PyObject *rlargs = NULL;
1907 
1908     if (!self->r) {
1909         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1910         return NULL;
1911     }
1912 
1913     rlargs = PyTuple_New(0);
1914 
1915     if (!rlargs)
1916       return NULL;
1917 
1918     line = Input_readline(self, rlargs);
1919 
1920     Py_DECREF(rlargs);
1921 
1922     if (!line)
1923         return NULL;
1924 
1925     if (PyString_GET_SIZE(line) == 0) {
1926         PyErr_SetObject(PyExc_StopIteration, Py_None);
1927         Py_DECREF(line);
1928         return NULL;
1929     }
1930 
1931     return line;
1932 }
1933 
1934 static PyTypeObject Input_Type = {
1935     PyVarObject_HEAD_INIT(NULL, 0)
1936     "mod_wsgi.Input",       /*tp_name*/
1937     sizeof(InputObject),    /*tp_basicsize*/
1938     0,                      /*tp_itemsize*/
1939     /* methods */
1940     (destructor)Input_dealloc, /*tp_dealloc*/
1941     0,                      /*tp_print*/
1942     0,                      /*tp_getattr*/
1943     0,                      /*tp_setattr*/
1944     0,                      /*tp_compare*/
1945     0,                      /*tp_repr*/
1946     0,                      /*tp_as_number*/
1947     0,                      /*tp_as_sequence*/
1948     0,                      /*tp_as_mapping*/
1949     0,                      /*tp_hash*/
1950     0,                      /*tp_call*/
1951     0,                      /*tp_str*/
1952     0,                      /*tp_getattro*/
1953     0,                      /*tp_setattro*/
1954     0,                      /*tp_as_buffer*/
1955 #if defined(Py_TPFLAGS_HAVE_ITER)
1956     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /*tp_flags*/
1957 #else
1958     Py_TPFLAGS_DEFAULT,     /*tp_flags*/
1959 #endif
1960     0,                      /*tp_doc*/
1961     0,                      /*tp_traverse*/
1962     0,                      /*tp_clear*/
1963     0,                      /*tp_richcompare*/
1964     0,                      /*tp_weaklistoffset*/
1965     (getiterfunc)Input_iter, /*tp_iter*/
1966     (iternextfunc)Input_iternext, /*tp_iternext*/
1967     Input_methods,          /*tp_methods*/
1968     0,                      /*tp_members*/
1969     0,                      /*tp_getset*/
1970     0,                      /*tp_base*/
1971     0,                      /*tp_dict*/
1972     0,                      /*tp_descr_get*/
1973     0,                      /*tp_descr_set*/
1974     0,                      /*tp_dictoffset*/
1975     0,                      /*tp_init*/
1976     0,                      /*tp_alloc*/
1977     0,                      /*tp_new*/
1978     0,                      /*tp_free*/
1979     0,                      /*tp_is_gc*/
1980 };
1981 
1982 typedef struct {
1983         PyObject_HEAD
1984         int result;
1985         request_rec *r;
1986         apr_bucket_brigade *bb;
1987         WSGIRequestConfig *config;
1988         InputObject *input;
1989         PyObject *log_buffer;
1990         PyObject *log;
1991         int status;
1992         const char *status_line;
1993         PyObject *headers;
1994         PyObject *sequence;
1995         int content_length_set;
1996         apr_off_t content_length;
1997         apr_off_t output_length;
1998         apr_off_t output_writes;
1999         apr_time_t output_time;
2000         apr_time_t start_time;
2001 } AdapterObject;
2002 
2003 static PyTypeObject Adapter_Type;
2004 
newAdapterObject(request_rec * r)2005 static AdapterObject *newAdapterObject(request_rec *r)
2006 {
2007     AdapterObject *self;
2008 
2009     self = PyObject_New(AdapterObject, &Adapter_Type);
2010     if (self == NULL)
2011         return NULL;
2012 
2013     self->result = HTTP_INTERNAL_SERVER_ERROR;
2014 
2015     self->r = r;
2016 
2017     self->bb = NULL;
2018 
2019     self->config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
2020                                                              &wsgi_module);
2021 
2022     self->status = HTTP_INTERNAL_SERVER_ERROR;
2023     self->status_line = NULL;
2024     self->headers = NULL;
2025     self->sequence = NULL;
2026 
2027     self->content_length_set = 0;
2028     self->content_length = 0;
2029     self->output_length = 0;
2030     self->output_writes = 0;
2031 
2032     self->output_time = 0;
2033 
2034     self->input = newInputObject(r, self->config->ignore_activity);
2035 
2036     self->log_buffer = newLogBufferObject(r, APLOG_ERR, "<wsgi.errors>", 0);
2037     self->log = newLogWrapperObject(self->log_buffer);
2038 
2039     return self;
2040 }
2041 
Adapter_dealloc(AdapterObject * self)2042 static void Adapter_dealloc(AdapterObject *self)
2043 {
2044     Py_XDECREF(self->headers);
2045     Py_XDECREF(self->sequence);
2046 
2047     Py_DECREF(self->input);
2048 
2049     Py_DECREF(self->log_buffer);
2050     Py_DECREF(self->log);
2051 
2052     PyObject_Del(self);
2053 }
2054 
Adapter_start_response(AdapterObject * self,PyObject * args)2055 static PyObject *Adapter_start_response(AdapterObject *self, PyObject *args)
2056 {
2057     PyObject *result = NULL;
2058 
2059     PyObject *status_line = NULL;
2060     PyObject *headers = NULL;
2061     PyObject *exc_info = Py_None;
2062 
2063     PyObject *status_line_as_bytes = NULL;
2064     PyObject *headers_as_bytes = NULL;
2065 
2066     if (!self->r) {
2067         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
2068         return NULL;
2069     }
2070 
2071     if (!PyArg_ParseTuple(args, "OO!|O:start_response",
2072         &status_line, &PyList_Type, &headers, &exc_info)) {
2073         return NULL;
2074     }
2075 
2076     if (exc_info != Py_None && !PyTuple_Check(exc_info)) {
2077         PyErr_SetString(PyExc_RuntimeError, "exception info must be a tuple");
2078         return NULL;
2079     }
2080 
2081     if (exc_info != Py_None) {
2082         if (self->status_line && !self->headers) {
2083             PyObject *type = NULL;
2084             PyObject *value = NULL;
2085             PyObject *traceback = NULL;
2086 
2087             if (!PyArg_ParseTuple(exc_info, "OOO", &type,
2088                                   &value, &traceback)) {
2089                 return NULL;
2090             }
2091 
2092             Py_INCREF(type);
2093             Py_INCREF(value);
2094             Py_INCREF(traceback);
2095 
2096             PyErr_Restore(type, value, traceback);
2097 
2098             return NULL;
2099         }
2100     }
2101     else if (self->status_line && !self->headers) {
2102         PyErr_SetString(PyExc_RuntimeError, "headers have already been sent");
2103         return NULL;
2104     }
2105 
2106     /* Publish event for the start of the response. */
2107 
2108     if (wsgi_event_subscribers()) {
2109         WSGIThreadInfo *thread_info;
2110 
2111         PyObject *event = NULL;
2112         PyObject *value = NULL;
2113 
2114         thread_info = wsgi_thread_info(0, 0);
2115 
2116         event = PyDict_New();
2117 
2118 #if AP_MODULE_MAGIC_AT_LEAST(20100923,2)
2119         if (self->r->log_id) {
2120 #if PY_MAJOR_VERSION >= 3
2121 	    value = PyUnicode_DecodeLatin1(self->r->log_id,
2122                                            strlen(self->r->log_id), NULL);
2123 #else
2124 	    value = PyString_FromString(self->r->log_id);
2125 #endif
2126             PyDict_SetItemString(event, "request_id", value);
2127             Py_DECREF(value);
2128         }
2129 #endif
2130 
2131         PyDict_SetItemString(event, "response_status", status_line);
2132         PyDict_SetItemString(event, "response_headers", headers);
2133         PyDict_SetItemString(event, "exception_info", exc_info);
2134 
2135         PyDict_SetItemString(event, "request_data", thread_info->request_data);
2136 
2137         wsgi_publish_event("response_started", event);
2138 
2139         Py_DECREF(event);
2140     }
2141 
2142     status_line_as_bytes = wsgi_convert_status_line_to_bytes(status_line);
2143 
2144     if (!status_line_as_bytes)
2145         goto finally;
2146 
2147     headers_as_bytes = wsgi_convert_headers_to_bytes(headers);
2148 
2149     if (!headers_as_bytes)
2150         goto finally;
2151 
2152     self->status_line = apr_pstrdup(self->r->pool, PyString_AsString(
2153                                     status_line_as_bytes));
2154     self->status = (int)strtol(self->status_line, NULL, 10);
2155 
2156     Py_XDECREF(self->headers);
2157     self->headers = headers_as_bytes;
2158     Py_INCREF(headers_as_bytes);
2159 
2160     result = PyObject_GetAttrString((PyObject *)self, "write");
2161 
2162 finally:
2163     Py_XDECREF(status_line_as_bytes);
2164     Py_XDECREF(headers_as_bytes);
2165 
2166     return result;
2167 }
2168 
Adapter_output(AdapterObject * self,const char * data,apr_off_t length,PyObject * string_object,int exception_when_aborted)2169 static int Adapter_output(AdapterObject *self, const char *data,
2170                           apr_off_t length, PyObject *string_object,
2171                           int exception_when_aborted)
2172 {
2173     int i = 0;
2174     apr_status_t rv;
2175     request_rec *r;
2176 
2177     apr_time_t output_start = 0;
2178     apr_time_t output_finish = 0;
2179 
2180 #if defined(MOD_WSGI_WITH_DAEMONS)
2181     if (wsgi_idle_timeout && !self->config->ignore_activity) {
2182         apr_thread_mutex_lock(wsgi_monitor_lock);
2183 
2184         if (wsgi_idle_timeout) {
2185             wsgi_idle_shutdown_time = apr_time_now();
2186             wsgi_idle_shutdown_time += wsgi_idle_timeout;
2187         }
2188 
2189         apr_thread_mutex_unlock(wsgi_monitor_lock);
2190     }
2191 #endif
2192 
2193     if (!self->status_line) {
2194         PyErr_SetString(PyExc_RuntimeError, "response has not been started");
2195         return 0;
2196     }
2197 
2198     r = self->r;
2199 
2200     /* Remember we started sending this block of output. */
2201 
2202     output_start = apr_time_now();
2203 
2204     /* Count how many separate blocks have been output. */
2205 
2206     if (string_object)
2207         self->output_writes++;
2208 
2209     /* Have response headers yet been sent. */
2210 
2211     if (self->headers) {
2212         /*
2213          * Apache prior to Apache 2.2.8 has a bug in it
2214          * whereby it doesn't force '100 Continue'
2215          * response before responding with headers if no
2216          * read. So, force a zero length read before
2217          * sending the headers if haven't yet attempted
2218          * to read anything. This will ensure that if no
2219          * request content has been read that any '100
2220          * Continue' response will be flushed and sent
2221          * back to the client if client was expecting
2222          * one. Only want to do this for 2xx and 3xx
2223          * status values. Note that even though Apple
2224          * supplied version of Apache on MacOS X Leopard
2225          * is newer than version 2.2.8, the header file
2226          * has never been patched when they make updates
2227          * and so anything compiled against it thinks it
2228          * is older.
2229          */
2230 
2231 #if (AP_SERVER_MAJORVERSION_NUMBER == 2 && \
2232      AP_SERVER_MINORVERSION_NUMBER < 2) || \
2233     (AP_SERVER_MAJORVERSION_NUMBER == 2 && \
2234      AP_SERVER_MINORVERSION_NUMBER == 2 && \
2235      AP_SERVER_PATCHLEVEL_NUMBER < 8)
2236 
2237         if (!self->input->init) {
2238             if (self->status >= 200 && self->status < 400) {
2239                 PyObject *args = NULL;
2240                 PyObject *result = NULL;
2241                 args = Py_BuildValue("(i)", 0);
2242                 result = Input_read(self->input, args);
2243                 if (PyErr_Occurred())
2244                     PyErr_Clear();
2245                 Py_DECREF(args);
2246                 Py_XDECREF(result);
2247             }
2248         }
2249 
2250 #endif
2251 
2252         /*
2253          * Now setup the response headers in request object. We
2254          * have already converted any native strings in the
2255          * headers to byte strings and validated the format of
2256          * the header names and values so can skip all the error
2257          * checking.
2258          */
2259 
2260         r->status = self->status;
2261         r->status_line = self->status_line;
2262 
2263         for (i = 0; i < PyList_Size(self->headers); i++) {
2264             PyObject *tuple = NULL;
2265 
2266             PyObject *object1 = NULL;
2267             PyObject *object2 = NULL;
2268 
2269             char *name = NULL;
2270             char *value = NULL;
2271 
2272             tuple = PyList_GetItem(self->headers, i);
2273 
2274             object1 = PyTuple_GetItem(tuple, 0);
2275             object2 = PyTuple_GetItem(tuple, 1);
2276 
2277             name = PyBytes_AsString(object1);
2278             value = PyBytes_AsString(object2);
2279 
2280             if (!strcasecmp(name, "Content-Type")) {
2281                 /*
2282                  * In a daemon child process we cannot call the
2283                  * function ap_set_content_type() as want to
2284                  * avoid adding any output filters based on the
2285                  * type of file being served as this will be
2286                  * done in the main Apache child process which
2287                  * proxied the request to the daemon process.
2288                  */
2289 
2290                 if (*self->config->process_group)
2291                     r->content_type = apr_pstrdup(r->pool, value);
2292                 else
2293                     ap_set_content_type(r, apr_pstrdup(r->pool, value));
2294             }
2295             else if (!strcasecmp(name, "Content-Length")) {
2296                 char *endstr;
2297                 apr_off_t length;
2298 
2299                 if (wsgi_strtoff(&length, value, &endstr, 10)
2300                     || *endstr || length < 0) {
2301 
2302                     PyErr_SetString(PyExc_ValueError,
2303                                     "invalid content length");
2304 
2305                     output_finish = apr_time_now();
2306 
2307                     if (output_finish > output_start)
2308                         self->output_time += (output_finish - output_start);
2309 
2310                     return 0;
2311                 }
2312 
2313                 ap_set_content_length(r, length);
2314 
2315                 self->content_length_set = 1;
2316                 self->content_length = length;
2317             }
2318             else if (!strcasecmp(name, "WWW-Authenticate")) {
2319                 apr_table_add(r->err_headers_out, name, value);
2320             }
2321             else {
2322                 apr_table_add(r->headers_out, name, value);
2323             }
2324         }
2325 
2326         /*
2327          * Reset flag indicating whether '100 Continue' response
2328          * expected. If we don't do this then if an attempt to read
2329          * input for the first time is after headers have been
2330          * sent, then Apache is wrongly generate the '100 Continue'
2331          * response into the response content. Not sure if this is
2332          * a bug in Apache, or that it truly believes that input
2333          * will never be read after the response headers have been
2334          * sent.
2335          */
2336 
2337         r->expecting_100 = 0;
2338 
2339         /* No longer need headers now that they have been sent. */
2340 
2341         Py_DECREF(self->headers);
2342         self->headers = NULL;
2343     }
2344 
2345     /*
2346      * If content length was specified, ensure that we don't
2347      * actually output more data than was specified as being
2348      * sent as otherwise technically in violation of HTTP RFC.
2349      */
2350 
2351     if (length) {
2352         apr_off_t output_length = length;
2353 
2354         if (self->content_length_set) {
2355             if (self->output_length < self->content_length) {
2356                 if (self->output_length + length > self->content_length) {
2357                     length = self->content_length - self->output_length;
2358                 }
2359             }
2360             else
2361                 length = 0;
2362         }
2363 
2364         self->output_length += output_length;
2365     }
2366 
2367     /* Now output any data. */
2368 
2369     if (length) {
2370         apr_bucket *b;
2371 
2372         /*
2373          * When using Apache 2.X can use lower level
2374          * bucket brigade APIs. This is preferred as
2375          * ap_rwrite()/ap_rflush() will grow memory in
2376          * the request pool on each call, which will
2377          * result in an increase in memory use over time
2378          * when streaming of data is being performed.
2379          * The memory is still reclaimed, but only at
2380          * the end of the request. Using bucket brigade
2381          * API avoids this, and also avoids any copying
2382          * of response data due to buffering performed
2383          * by ap_rwrite().
2384          */
2385 
2386         if (r->connection->aborted) {
2387             if (!exception_when_aborted) {
2388                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, self->r,
2389                               "mod_wsgi (pid=%d): Client closed connection.",
2390                               getpid());
2391             }
2392             else
2393                 PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi client "
2394                                 "connection closed.");
2395 
2396             output_finish = apr_time_now();
2397 
2398             if (output_finish > output_start)
2399                 self->output_time += (output_finish - output_start);
2400 
2401             return 0;
2402         }
2403 
2404         if (!self->bb) {
2405             self->bb = apr_brigade_create(r->pool,
2406                                           r->connection->bucket_alloc);
2407         }
2408 
2409 #if 0
2410         if (string_object) {
2411             b = wsgi_apr_bucket_python_create(data, length,
2412                     self->config->application_group, string_object,
2413                     r->connection->bucket_alloc);
2414         }
2415         else {
2416 #endif
2417             b = apr_bucket_transient_create(data, (apr_size_t)length,
2418                                             r->connection->bucket_alloc);
2419 #if 0
2420         }
2421 #endif
2422 
2423         APR_BRIGADE_INSERT_TAIL(self->bb, b);
2424 
2425         b = apr_bucket_flush_create(r->connection->bucket_alloc);
2426         APR_BRIGADE_INSERT_TAIL(self->bb, b);
2427 
2428         Py_BEGIN_ALLOW_THREADS
2429         rv = ap_pass_brigade(r->output_filters, self->bb);
2430         Py_END_ALLOW_THREADS
2431 
2432         if (rv != APR_SUCCESS) {
2433             char status_buffer[512];
2434             const char *error_message;
2435 
2436             if (!exception_when_aborted) {
2437                 error_message = apr_psprintf(r->pool, "Failed to write "
2438                         "response data: %s", apr_strerror(rv, status_buffer,
2439                         sizeof(status_buffer)-1));
2440 
2441                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, self->r,
2442                               "mod_wsgi (pid=%d): %s.", getpid(),
2443                               error_message);
2444             }
2445             else {
2446                 error_message = apr_psprintf(r->pool, "Apache/mod_wsgi "
2447                         "failed to write response data: %s",
2448                         apr_strerror(rv, status_buffer,
2449                         sizeof(status_buffer)-1));
2450 
2451                 PyErr_SetString(PyExc_IOError, error_message);
2452             }
2453 
2454             output_finish = apr_time_now();
2455 
2456             if (output_finish > output_start)
2457                 self->output_time += (output_finish - output_start);
2458 
2459             return 0;
2460         }
2461 
2462         Py_BEGIN_ALLOW_THREADS
2463         apr_brigade_cleanup(self->bb);
2464         Py_END_ALLOW_THREADS
2465     }
2466 
2467     /* Add how much time we spent send this block of output. */
2468 
2469     output_finish = apr_time_now();
2470 
2471     if (output_finish > output_start)
2472         self->output_time += (output_finish - output_start);
2473 
2474     /*
2475      * Check whether aborted connection was found when data
2476      * being written, otherwise will not be flagged until next
2477      * time that data is being written. Early detection is
2478      * better as it may have been the last data block being
2479      * written and application may think that data has all
2480      * been written. In a streaming application, we also want
2481      * to avoid any additional data processing to generate any
2482      * successive data.
2483      */
2484 
2485     if (r->connection->aborted) {
2486         if (!exception_when_aborted) {
2487             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, self->r,
2488                           "mod_wsgi (pid=%d): Client closed connection.",
2489                           getpid());
2490         }
2491         else
2492             PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi client "
2493                             "connection closed.");
2494 
2495         return 0;
2496     }
2497 
2498     return 1;
2499 }
2500 
2501 /* Split buckets at 1GB when sending large files. */
2502 
2503 #define MAX_BUCKET_SIZE (0x40000000)
2504 
Adapter_output_file(AdapterObject * self,apr_file_t * tmpfile,apr_off_t offset,apr_off_t len)2505 static int Adapter_output_file(AdapterObject *self, apr_file_t* tmpfile,
2506                                apr_off_t offset, apr_off_t len)
2507 {
2508     request_rec *r;
2509     apr_bucket *b;
2510     apr_status_t rv;
2511     apr_bucket_brigade *bb;
2512 
2513     apr_file_t* dupfile = NULL;
2514 
2515     r = self->r;
2516 
2517     if (r->connection->aborted) {
2518         PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi client "
2519                         "connection closed.");
2520         return 0;
2521     }
2522 
2523     if (len == 0)
2524         return 1;
2525 
2526     bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
2527 
2528     apr_file_dup(&dupfile, tmpfile, r->pool);
2529 
2530     if (sizeof(apr_off_t) == sizeof(apr_size_t) || len < MAX_BUCKET_SIZE) {
2531         /* Can use a single bucket to send file. */
2532 
2533 #if 0
2534         b = apr_bucket_file_create(tmpfile, offset, (apr_size_t)len, r->pool,
2535                                    r->connection->bucket_alloc);
2536 #endif
2537         b = apr_bucket_file_create(dupfile, offset, (apr_size_t)len, r->pool,
2538                                    r->connection->bucket_alloc);
2539     }
2540     else {
2541         /* Need to create multiple buckets to send file. */
2542 
2543 #if 0
2544         b = apr_bucket_file_create(tmpfile, offset, MAX_BUCKET_SIZE, r->pool,
2545                                    r->connection->bucket_alloc);
2546 #endif
2547         b = apr_bucket_file_create(dupfile, offset, MAX_BUCKET_SIZE, r->pool,
2548                                    r->connection->bucket_alloc);
2549 
2550         while (len > MAX_BUCKET_SIZE) {
2551             apr_bucket *cb;
2552             apr_bucket_copy(b, &cb);
2553             APR_BRIGADE_INSERT_TAIL(bb, cb);
2554             b->start += MAX_BUCKET_SIZE;
2555             len -= MAX_BUCKET_SIZE;
2556         }
2557 
2558         /* Resize just the last bucket */
2559 
2560         b->length = (apr_size_t)len;
2561     }
2562 
2563     APR_BRIGADE_INSERT_TAIL(bb, b);
2564 
2565     b = apr_bucket_flush_create(r->connection->bucket_alloc);
2566     APR_BRIGADE_INSERT_TAIL(bb, b);
2567 
2568     b = apr_bucket_eos_create(r->connection->bucket_alloc);
2569     APR_BRIGADE_INSERT_TAIL(bb, b);
2570 
2571     Py_BEGIN_ALLOW_THREADS
2572     rv = ap_pass_brigade(r->output_filters, bb);
2573     Py_END_ALLOW_THREADS
2574 
2575     if (rv != APR_SUCCESS) {
2576         char status_buffer[512];
2577         const char *error_message;
2578 
2579         error_message = apr_psprintf(r->pool, "Apache/mod_wsgi failed "
2580                 "to write response data: %s.", apr_strerror(rv,
2581                 status_buffer, sizeof(status_buffer)-1));
2582 
2583         PyErr_SetString(PyExc_IOError, error_message);
2584         return 0;
2585     }
2586 
2587     Py_BEGIN_ALLOW_THREADS
2588     apr_brigade_destroy(bb);
2589     Py_END_ALLOW_THREADS
2590 
2591     if (r->connection->aborted) {
2592         PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi client connection "
2593                         "closed.");
2594         return 0;
2595     }
2596 
2597     return 1;
2598 }
2599 
2600 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *wsgi_is_https = NULL;
2601 
Adapter_environ(AdapterObject * self)2602 static PyObject *Adapter_environ(AdapterObject *self)
2603 {
2604     request_rec *r = NULL;
2605 
2606     PyObject *vars = NULL;
2607     PyObject *object = NULL;
2608 
2609     const apr_array_header_t *head = NULL;
2610     const apr_table_entry_t *elts = NULL;
2611 
2612     int i = 0;
2613 
2614     const char *scheme = NULL;
2615 
2616     /* Create the WSGI environment dictionary. */
2617 
2618     vars = PyDict_New();
2619 
2620     /* Merge the CGI environment into the WSGI environment. */
2621 
2622     r = self->r;
2623 
2624     head = apr_table_elts(r->subprocess_env);
2625     elts = (apr_table_entry_t *)head->elts;
2626 
2627     for (i = 0; i < head->nelts; ++i) {
2628         if (elts[i].key) {
2629             if (elts[i].val) {
2630 #if PY_MAJOR_VERSION >= 3
2631                 if (!strcmp(elts[i].val, "DOCUMENT_ROOT")) {
2632                     object = PyUnicode_Decode(elts[i].val, strlen(elts[i].val),
2633                                              Py_FileSystemDefaultEncoding,
2634                                              "surrogateescape");
2635                 }
2636                 else if (!strcmp(elts[i].val, "SCRIPT_FILENAME")) {
2637                     object = PyUnicode_Decode(elts[i].val, strlen(elts[i].val),
2638                                              Py_FileSystemDefaultEncoding,
2639                                              "surrogateescape");
2640                 }
2641                 else {
2642                     object = PyUnicode_DecodeLatin1(elts[i].val,
2643                                                     strlen(elts[i].val), NULL);
2644                 }
2645 #else
2646                 object = PyString_FromString(elts[i].val);
2647 #endif
2648                 PyDict_SetItemString(vars, elts[i].key, object);
2649                 Py_DECREF(object);
2650             }
2651             else
2652                 PyDict_SetItemString(vars, elts[i].key, Py_None);
2653         }
2654     }
2655 
2656     PyDict_DelItemString(vars, "PATH");
2657 
2658     /* Now setup all the WSGI specific environment values. */
2659 
2660     object = Py_BuildValue("(ii)", 1, 0);
2661     PyDict_SetItemString(vars, "wsgi.version", object);
2662     Py_DECREF(object);
2663 
2664     object = PyBool_FromLong(wsgi_multithread);
2665     PyDict_SetItemString(vars, "wsgi.multithread", object);
2666     Py_DECREF(object);
2667 
2668     object = PyBool_FromLong(wsgi_multiprocess);
2669     PyDict_SetItemString(vars, "wsgi.multiprocess", object);
2670     Py_DECREF(object);
2671 
2672 #if defined(MOD_WSGI_WITH_DAEMONS)
2673     if (wsgi_daemon_process) {
2674         if (wsgi_daemon_process->group->threads == 1 &&
2675             wsgi_daemon_process->group->maximum_requests == 1) {
2676             PyDict_SetItemString(vars, "wsgi.run_once", Py_True);
2677         }
2678         else
2679             PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
2680     }
2681     else
2682         PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
2683 #else
2684     PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
2685 #endif
2686 
2687     scheme = apr_table_get(r->subprocess_env, "HTTPS");
2688 
2689     if (scheme && (!strcasecmp(scheme, "On") || !strcmp(scheme, "1"))) {
2690 #if PY_MAJOR_VERSION >= 3
2691         object = PyUnicode_FromString("https");
2692 #else
2693         object = PyString_FromString("https");
2694 #endif
2695         PyDict_SetItemString(vars, "wsgi.url_scheme", object);
2696         Py_DECREF(object);
2697     }
2698     else {
2699 #if PY_MAJOR_VERSION >= 3
2700         object = PyUnicode_FromString("http");
2701 #else
2702         object = PyString_FromString("http");
2703 #endif
2704         PyDict_SetItemString(vars, "wsgi.url_scheme", object);
2705         Py_DECREF(object);
2706     }
2707 
2708     /*
2709      * We remove the HTTPS variable because WSGI compliant
2710      * applications shouldn't rely on it. Instead they should
2711      * use wsgi.url_scheme. We do this even if SetEnv was
2712      * used to set HTTPS from Apache configuration. That is
2713      * we convert it into the correct variable and remove the
2714      * original.
2715      */
2716 
2717     if (scheme)
2718         PyDict_DelItemString(vars, "HTTPS");
2719 
2720     /*
2721      * Setup log object for WSGI errors. Don't decrement
2722      * reference to log object as keep reference to it.
2723      */
2724 
2725     object = (PyObject *)self->log;
2726     PyDict_SetItemString(vars, "wsgi.errors", object);
2727 
2728     /* Setup input object for request content. */
2729 
2730     object = (PyObject *)self->input;
2731     PyDict_SetItemString(vars, "wsgi.input", object);
2732 
2733     PyDict_SetItemString(vars, "wsgi.input_terminated", Py_True);
2734 
2735     /* Setup file wrapper object for efficient file responses. */
2736 
2737     PyDict_SetItemString(vars, "wsgi.file_wrapper", (PyObject *)&Stream_Type);
2738 
2739     /* Add Apache and mod_wsgi version information. */
2740 
2741     object = Py_BuildValue("(iii)", AP_SERVER_MAJORVERSION_NUMBER,
2742                            AP_SERVER_MINORVERSION_NUMBER,
2743                            AP_SERVER_PATCHLEVEL_NUMBER);
2744     PyDict_SetItemString(vars, "apache.version", object);
2745     Py_DECREF(object);
2746 
2747     object = Py_BuildValue("(iii)", MOD_WSGI_MAJORVERSION_NUMBER,
2748                            MOD_WSGI_MINORVERSION_NUMBER,
2749                            MOD_WSGI_MICROVERSION_NUMBER);
2750     PyDict_SetItemString(vars, "mod_wsgi.version", object);
2751     Py_DECREF(object);
2752 
2753     /*
2754      * If Apache extensions are enabled and running in embedded
2755      * mode add a CObject reference to the Apache request_rec
2756      * structure instance.
2757      */
2758 
2759     if (!wsgi_daemon_pool && self->config->pass_apache_request) {
2760 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || \
2761     (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 7)
2762         object = PyCapsule_New(self->r, 0, 0);
2763 #else
2764         object = PyCObject_FromVoidPtr(self->r, 0);
2765 #endif
2766         PyDict_SetItemString(vars, "apache.request_rec", object);
2767         Py_DECREF(object);
2768     }
2769 
2770     /*
2771      * Extensions for accessing SSL certificate information from
2772      * mod_ssl when in use.
2773      */
2774 
2775 #if 0
2776     if (!wsgi_daemon_pool) {
2777         object = PyObject_GetAttrString((PyObject *)self, "ssl_is_https");
2778         PyDict_SetItemString(vars, "mod_ssl.is_https", object);
2779         Py_DECREF(object);
2780 
2781         object = PyObject_GetAttrString((PyObject *)self, "ssl_var_lookup");
2782         PyDict_SetItemString(vars, "mod_ssl.var_lookup", object);
2783         Py_DECREF(object);
2784     }
2785 #endif
2786 
2787     return vars;
2788 }
2789 
Adapter_process_file_wrapper(AdapterObject * self)2790 static int Adapter_process_file_wrapper(AdapterObject *self)
2791 {
2792     int done = 0;
2793 
2794 #ifndef WIN32
2795     PyObject *filelike = NULL;
2796     PyObject *method = NULL;
2797     PyObject *object = NULL;
2798 
2799     apr_status_t rv = 0;
2800 
2801     apr_os_file_t fd = -1;
2802     apr_file_t *tmpfile = NULL;
2803     apr_finfo_t finfo;
2804 
2805     apr_off_t fd_offset = 0;
2806     apr_off_t fo_offset = 0;
2807 
2808     apr_off_t length = 0;
2809 
2810     /* Perform file wrapper optimisations where possible. */
2811 
2812     if (!PyObject_IsInstance(self->sequence, (PyObject *)&Stream_Type))
2813         return 0;
2814 
2815     /*
2816      * Only attempt to perform optimisations if the
2817      * write() function returned by start_response()
2818      * function has not been called with non zero length
2819      * data. In other words if no prior response content
2820      * generated. Technically it could be done, but want
2821      * to have a consistent rule about how specifying a
2822      * content length affects how much of a file is
2823      * sent. Don't want to have to take into
2824      * consideration whether write() function has been
2825      * called or not as just complicates things.
2826      */
2827 
2828     if (self->output_length != 0)
2829         return 0;
2830 
2831     /*
2832      * Work out if file wrapper is associated with a
2833      * file like object, where that file object is
2834      * associated with a regular file. If it does then
2835      * we can optimise how the contents of the file are
2836      * sent out. If no such associated file descriptor
2837      * then it needs to be processed like any other
2838      * iterable value.
2839      */
2840 
2841 
2842     filelike = PyObject_GetAttrString((PyObject *)self->sequence, "filelike");
2843 
2844     if (!filelike) {
2845         PyErr_SetString(PyExc_KeyError,
2846                         "file wrapper no filelike attribute");
2847         return 0;
2848     }
2849 
2850     fd = PyObject_AsFileDescriptor(filelike);
2851     if (fd == -1) {
2852         PyErr_Clear();
2853         Py_DECREF(filelike);
2854         return 0;
2855     }
2856 
2857     Py_DECREF(filelike);
2858 
2859     /*
2860      * On some platforms, such as Linux, sendfile() system call
2861      * will not work on UNIX sockets. Thus when using daemon mode
2862      * cannot enable that feature.
2863      */
2864 
2865     if (self->config->enable_sendfile)
2866         apr_os_file_put(&tmpfile, &fd, APR_SENDFILE_ENABLED, self->r->pool);
2867     else
2868         apr_os_file_put(&tmpfile, &fd, 0, self->r->pool);
2869 
2870     rv = apr_file_info_get(&finfo, APR_FINFO_SIZE|APR_FINFO_TYPE, tmpfile);
2871     if (rv != APR_SUCCESS || finfo.filetype != APR_REG)
2872         return 0;
2873 
2874     /*
2875      * Because Python file like objects potentially have
2876      * their own buffering layering, or use an operating
2877      * system FILE object which also has a buffering
2878      * layer on top of a normal file descriptor, need to
2879      * determine from the file like object its position
2880      * within the file and use that as starting position.
2881      * Note that it is assumed that user had flushed any
2882      * modifications to the file as necessary. Also, we
2883      * need to make sure we remember the original file
2884      * descriptor position as will need to restore that
2885      * position so it matches the upper buffering layers
2886      * when done. This is done to avoid any potential
2887      * problems if file like object does anything strange
2888      * in its close() method which relies on file position
2889      * being what it thought it should be.
2890      */
2891 
2892     rv = apr_file_seek(tmpfile, APR_CUR, &fd_offset);
2893     if (rv != APR_SUCCESS)
2894         return 0;
2895 
2896     method = PyObject_GetAttrString(filelike, "tell");
2897     if (!method)
2898         return 0;
2899 
2900     object = PyEval_CallObject(method, NULL);
2901     Py_DECREF(method);
2902 
2903     if (!object) {
2904         PyErr_Clear();
2905         return 0;
2906     }
2907 
2908     if (PyLong_Check(object)) {
2909 #if defined(HAVE_LONG_LONG)
2910         fo_offset = PyLong_AsLongLong(object);
2911 #else
2912         fo_offset = PyLong_AsLong(object);
2913 #endif
2914     }
2915 #if PY_MAJOR_VERSION < 3
2916     else if (PyInt_Check(object)) {
2917         fo_offset = PyInt_AsLong(object);
2918     }
2919 #endif
2920     else {
2921         Py_DECREF(object);
2922         return 0;
2923     }
2924 
2925     if (PyErr_Occurred()){
2926         Py_DECREF(object);
2927         PyErr_Clear();
2928         return 0;
2929     }
2930 
2931     Py_DECREF(object);
2932 
2933     /*
2934      * For a file wrapper object need to always ensure
2935      * that response headers are parsed. This is done so
2936      * that if the content length header has been
2937      * defined we can get its value and use it to limit
2938      * how much of a file is being sent. The WSGI 1.0
2939      * specification says that we are meant to send all
2940      * available bytes from the file, however this is
2941      * questionable as sending more than content length
2942      * would violate HTTP RFC. Note that this doesn't
2943      * actually flush the headers out when using Apache
2944      * 2.X. This is good, as we want to still be able to
2945      * set the content length header if none set and file
2946      * is seekable. If processing response headers fails,
2947      * then need to return as if done, with error being
2948      * logged later.
2949      */
2950 
2951     if (!Adapter_output(self, "", 0, NULL, 0))
2952         return 1;
2953 
2954     /*
2955      * If content length wasn't defined then determine
2956      * the amount of data which is available to send and
2957      * set the content length response header. Either
2958      * way, if can work out length then send data
2959      * otherwise fall through and treat it as normal
2960      * iterable.
2961      */
2962 
2963     if (!self->content_length_set) {
2964         length = finfo.size - fo_offset;
2965         self->output_length += length;
2966 
2967         ap_set_content_length(self->r, length);
2968 
2969         self->content_length_set = 1;
2970         self->content_length = length;
2971 
2972         if (Adapter_output_file(self, tmpfile, fo_offset, length))
2973             self->result = OK;
2974 
2975         done = 1;
2976     }
2977     else {
2978         length = finfo.size - fo_offset;
2979         self->output_length += length;
2980 
2981         /* Use user specified content length instead. */
2982 
2983         length = self->content_length;
2984 
2985         if (Adapter_output_file(self, tmpfile, fo_offset, length))
2986             self->result = OK;
2987 
2988         done = 1;
2989     }
2990 
2991     /*
2992      * Restore position of underlying file descriptor.
2993      * If this fails, then not much we can do about it.
2994      */
2995 
2996     apr_file_seek(tmpfile, APR_SET, &fd_offset);
2997 
2998 #endif
2999 
3000     return done;
3001 }
3002 
Adapter_run(AdapterObject * self,PyObject * object)3003 static int Adapter_run(AdapterObject *self, PyObject *object)
3004 {
3005     PyObject *vars = NULL;
3006     PyObject *start = NULL;
3007     PyObject *args = NULL;
3008     PyObject *iterator = NULL;
3009     PyObject *close = NULL;
3010 
3011     PyObject *nrwrapper = NULL;
3012     PyObject *evwrapper = NULL;
3013 
3014     PyObject *value = NULL;
3015     PyObject *event = NULL;
3016 
3017     const char *msg = NULL;
3018     apr_off_t length = 0;
3019 
3020     WSGIThreadInfo *thread_handle = NULL;
3021 
3022     apr_time_t finish_time;
3023 
3024     WSGIThreadCPUUsage start_usage;
3025     WSGIThreadCPUUsage end_usage;
3026 
3027     int aborted = 0;
3028 
3029 #if defined(MOD_WSGI_WITH_DAEMONS)
3030     if (wsgi_idle_timeout && !self->config->ignore_activity) {
3031         apr_thread_mutex_lock(wsgi_monitor_lock);
3032 
3033         if (wsgi_idle_timeout) {
3034             wsgi_idle_shutdown_time = apr_time_now();
3035             wsgi_idle_shutdown_time += wsgi_idle_timeout;
3036         }
3037 
3038         apr_thread_mutex_unlock(wsgi_monitor_lock);
3039     }
3040 #endif
3041 
3042     if (wsgi_newrelic_config_file) {
3043         PyObject *module = NULL;
3044 
3045         module = PyImport_ImportModule("newrelic.agent");
3046 
3047         if (module) {
3048             PyObject *dict;
3049             PyObject *factory;
3050 
3051             dict = PyModule_GetDict(module);
3052             factory = PyDict_GetItemString(dict, "WSGIApplicationWrapper");
3053 
3054             if (factory) {
3055                 Py_INCREF(factory);
3056 
3057                 nrwrapper = PyObject_CallFunctionObjArgs(
3058                         factory, object, Py_None, NULL);
3059 
3060                 if (!nrwrapper) {
3061                     wsgi_log_python_error(self->r, self->log,
3062                                           self->r->filename, 0);
3063                     PyErr_Clear();
3064                 }
3065 
3066                 Py_DECREF(factory);
3067             }
3068 
3069             Py_DECREF(module);
3070         }
3071     }
3072 
3073     if (nrwrapper)
3074         object = nrwrapper;
3075 
3076     self->start_time = apr_time_now();
3077 
3078     apr_table_setn(self->r->subprocess_env, "mod_wsgi.script_start",
3079                    apr_psprintf(self->r->pool, "%" APR_TIME_T_FMT,
3080                    self->start_time));
3081 
3082     vars = Adapter_environ(self);
3083 
3084     value = wsgi_PyInt_FromLongLong(wsgi_total_requests);
3085     PyDict_SetItemString(vars, "mod_wsgi.total_requests", value);
3086     Py_DECREF(value);
3087 
3088     thread_handle = wsgi_thread_info(1, 1);
3089 
3090     value = wsgi_PyInt_FromLong(thread_handle->thread_id);
3091     PyDict_SetItemString(vars, "mod_wsgi.thread_id", value);
3092     Py_DECREF(value);
3093 
3094     value = wsgi_PyInt_FromLongLong(thread_handle->request_count);
3095     PyDict_SetItemString(vars, "mod_wsgi.thread_requests", value);
3096     Py_DECREF(value);
3097 
3098     /* Publish event for the start of the request. */
3099 
3100     start_usage.user_time = 0.0;
3101     start_usage.system_time = 0.0;
3102 
3103     if (wsgi_event_subscribers()) {
3104         wsgi_thread_cpu_usage(&start_usage);
3105 
3106         event = PyDict_New();
3107 
3108 #if AP_MODULE_MAGIC_AT_LEAST(20100923,2)
3109         if (self->r->log_id) {
3110 #if PY_MAJOR_VERSION >= 3
3111 	    value = PyUnicode_DecodeLatin1(self->r->log_id,
3112                                            strlen(self->r->log_id), NULL);
3113 #else
3114 	    value = PyString_FromString(self->r->log_id);
3115 #endif
3116             PyDict_SetItemString(event, "request_id", value);
3117             Py_DECREF(value);
3118         }
3119 #endif
3120 
3121         value = wsgi_PyInt_FromLong(thread_handle->thread_id);
3122         PyDict_SetItemString(event, "thread_id", value);
3123         Py_DECREF(value);
3124 
3125         value = wsgi_PyInt_FromLong(self->config->daemon_connects);
3126         PyDict_SetItemString(event, "daemon_connects", value);
3127         Py_DECREF(value);
3128 
3129         value = wsgi_PyInt_FromLong(self->config->daemon_restarts);
3130         PyDict_SetItemString(event, "daemon_restarts", value);
3131         Py_DECREF(value);
3132 
3133         value = PyFloat_FromDouble(apr_time_sec(
3134                                    (double)self->config->request_start));
3135         PyDict_SetItemString(event, "request_start", value);
3136         Py_DECREF(value);
3137 
3138         value = PyFloat_FromDouble(apr_time_sec(
3139                                    (double)self->config->queue_start));
3140         PyDict_SetItemString(event, "queue_start", value);
3141         Py_DECREF(value);
3142 
3143         value = PyFloat_FromDouble(apr_time_sec(
3144                                    (double)self->config->daemon_start));
3145         PyDict_SetItemString(event, "daemon_start", value);
3146         Py_DECREF(value);
3147 
3148         PyDict_SetItemString(event, "application_object", object);
3149 
3150         PyDict_SetItemString(event, "request_environ", vars);
3151 
3152         value = PyFloat_FromDouble(apr_time_sec((double)self->start_time));
3153         PyDict_SetItemString(event, "application_start", value);
3154         Py_DECREF(value);
3155 
3156         PyDict_SetItemString(event, "request_data", thread_handle->request_data);
3157 
3158         wsgi_publish_event("request_started", event);
3159 
3160         evwrapper = PyDict_GetItemString(event, "application_object");
3161 
3162         if (evwrapper) {
3163             if (evwrapper != object) {
3164                 Py_INCREF(evwrapper);
3165                 object = evwrapper;
3166             }
3167             else
3168                 evwrapper = NULL;
3169         }
3170 
3171         Py_DECREF(event);
3172     }
3173 
3174     /* Pass the request through to the WSGI application. */
3175 
3176     thread_handle->request_count++;
3177 
3178     start = PyObject_GetAttrString((PyObject *)self, "start_response");
3179 
3180     args = Py_BuildValue("(OO)", vars, start);
3181 
3182     self->sequence = PyEval_CallObject(object, args);
3183 
3184     if (self->sequence != NULL) {
3185         if (!Adapter_process_file_wrapper(self)) {
3186             iterator = PyObject_GetIter(self->sequence);
3187 
3188             if (iterator != NULL) {
3189                 PyObject *item = NULL;
3190 
3191                 while ((item = PyIter_Next(iterator))) {
3192                     if (!PyString_Check(item)) {
3193                         PyErr_Format(PyExc_TypeError, "sequence of byte "
3194                                      "string values expected, value of "
3195                                      "type %.200s found",
3196                                      item->ob_type->tp_name);
3197                         Py_DECREF(item);
3198                         break;
3199                     }
3200 
3201                     msg = PyString_AsString(item);
3202                     length = PyString_Size(item);
3203 
3204                     if (!msg) {
3205                         Py_DECREF(item);
3206                         break;
3207                     }
3208 
3209                     if (length && !Adapter_output(self, msg, length,
3210                                 item, 0)) {
3211                         if (!PyErr_Occurred())
3212                             aborted = 1;
3213                         Py_DECREF(item);
3214                         break;
3215                     }
3216 
3217                     Py_DECREF(item);
3218                 }
3219             }
3220 
3221             if (!PyErr_Occurred()) {
3222                 if (!aborted) {
3223                     /*
3224                      * In the case where the response was empty we
3225                      * need to ensure we explicitly flush out the
3226                      * headers. This is done by calling the output
3227                      * routine but with an empty string as content.
3228                      * This could be gated on whether any content
3229                      * had already been sent, but easier to just call
3230                      * it all the time.
3231                      */
3232 
3233                     if (Adapter_output(self, "", 0, NULL, 0))
3234                         self->result = OK;
3235                 }
3236                 else {
3237                     /*
3238                      * If the client connection was already marked
3239                      * as aborted, then it indicates the client has
3240                      * closed the connection. In this case mark the
3241                      * final result as okay rather than an error so
3242                      * that the access log still records the original
3243                      * HTTP response code for the request rather than
3244                      * overriding it. If don't do this then access
3245                      * log will show 500 when the WSGI application
3246                      * itself had run fine.
3247                      */
3248 
3249                     self->result = OK;
3250                 }
3251             }
3252 
3253             Py_XDECREF(iterator);
3254         }
3255 
3256         /*
3257          * Log warning if more response content generated than was
3258          * indicated, or less, if there was no errors generated by
3259          * the application and connection wasn't aborted.
3260          */
3261 
3262         if (self->content_length_set && ((!PyErr_Occurred() && !aborted &&
3263             self->output_length != self->content_length) ||
3264             (self->output_length > self->content_length))) {
3265             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, self->r,
3266                           "mod_wsgi (pid=%d): Content length mismatch, "
3267                           "expected %s, response generated %s: %s", getpid(),
3268                           apr_off_t_toa(self->r->pool, self->content_length),
3269                           apr_off_t_toa(self->r->pool, self->output_length),
3270                           self->r->filename);
3271         }
3272 
3273         if (PyErr_Occurred()) {
3274             /*
3275              * Response content has already been sent, so cannot
3276              * return an internal server error as Apache will
3277              * append its own error page. Thus need to return OK
3278              * and just truncate the response.
3279              */
3280 
3281             if (self->status_line && !self->headers)
3282                 self->result = OK;
3283 
3284             wsgi_log_python_error(self->r, self->log, self->r->filename, 1);
3285 
3286             /*
3287              * If response content is being chunked and an error
3288              * occurred, we need to prevent the sending of the EOS
3289              * bucket so a client is able to detect that the the
3290              * response was incomplete.
3291              */
3292 
3293             if (self->r->chunked)
3294                 self->r->eos_sent = 1;
3295         }
3296 
3297         if (PyObject_HasAttrString(self->sequence, "close")) {
3298             PyObject *args = NULL;
3299             PyObject *data = NULL;
3300 
3301             close = PyObject_GetAttrString(self->sequence, "close");
3302 
3303             args = Py_BuildValue("()");
3304             data = PyEval_CallObject(close, args);
3305 
3306             Py_DECREF(args);
3307             Py_XDECREF(data);
3308             Py_DECREF(close);
3309         }
3310 
3311         if (PyErr_Occurred())
3312             wsgi_log_python_error(self->r, self->log, self->r->filename, 1);
3313     }
3314     else
3315         wsgi_log_python_error(self->r, self->log, self->r->filename, 1);
3316 
3317     /* Publish event for the end of the request. */
3318 
3319     if (wsgi_event_subscribers()) {
3320         double application_time = 0.0;
3321         double output_time = 0.0;
3322 
3323         event = PyDict_New();
3324 
3325 #if AP_MODULE_MAGIC_AT_LEAST(20100923,2)
3326         if (self->r->log_id) {
3327 #if PY_MAJOR_VERSION >= 3
3328 	    value = PyUnicode_DecodeLatin1(self->r->log_id,
3329                                            strlen(self->r->log_id), NULL);
3330 #else
3331 	    value = PyString_FromString(self->r->log_id);
3332 #endif
3333             PyDict_SetItemString(event, "request_id", value);
3334             Py_DECREF(value);
3335         }
3336 #endif
3337 
3338         value = wsgi_PyInt_FromLongLong(self->input->reads);
3339         PyDict_SetItemString(event, "input_reads", value);
3340         Py_DECREF(value);
3341 
3342         value = wsgi_PyInt_FromLongLong(self->input->bytes);
3343         PyDict_SetItemString(event, "input_length", value);
3344         Py_DECREF(value);
3345 
3346         value = PyFloat_FromDouble(apr_time_sec((double)self->input->time));
3347         PyDict_SetItemString(event, "input_time", value);
3348         Py_DECREF(value);
3349 
3350         value = wsgi_PyInt_FromLongLong(self->output_length);
3351         PyDict_SetItemString(event, "output_length", value);
3352         Py_DECREF(value);
3353 
3354         value = wsgi_PyInt_FromLongLong(self->output_writes);
3355         PyDict_SetItemString(event, "output_writes", value);
3356         Py_DECREF(value);
3357 
3358         output_time = apr_time_sec((double)self->output_time);
3359 
3360         if (output_time < 0.0)
3361             output_time = 0.0;
3362 
3363         finish_time = apr_time_now();
3364 
3365         application_time = apr_time_sec((double)finish_time-self->start_time);
3366 
3367         if (application_time < 0.0)
3368             application_time = 0.0;
3369 
3370         if (start_usage.user_time != 0.0) {
3371             if (wsgi_thread_cpu_usage(&end_usage)) {
3372                 double user_seconds;
3373                 double system_seconds;
3374                 double total_seconds;
3375 
3376                 user_seconds = end_usage.user_time;
3377                 user_seconds -= start_usage.user_time;
3378 
3379                 if (user_seconds < 0.0)
3380                     user_seconds = 0.0;
3381 
3382                 system_seconds = end_usage.system_time;
3383                 system_seconds -= start_usage.system_time;
3384 
3385                 if (system_seconds < 0.0)
3386                     system_seconds = 0.0;
3387 
3388                 total_seconds = user_seconds + system_seconds;
3389 
3390                 if (total_seconds && total_seconds > application_time) {
3391                     user_seconds = (user_seconds/total_seconds)*application_time;
3392                     system_seconds = application_time - user_seconds;
3393                 }
3394 
3395                 value = PyFloat_FromDouble(user_seconds);
3396                 PyDict_SetItemString(event, "cpu_user_time", value);
3397                 Py_DECREF(value);
3398 
3399                 value = PyFloat_FromDouble(system_seconds);
3400                 PyDict_SetItemString(event, "cpu_system_time", value);
3401                 Py_DECREF(value);
3402             }
3403         }
3404 
3405         value = PyFloat_FromDouble(output_time);
3406         PyDict_SetItemString(event, "output_time", value);
3407         Py_DECREF(value);
3408 
3409         value = PyFloat_FromDouble(apr_time_sec((double)finish_time));
3410         PyDict_SetItemString(event, "application_finish", value);
3411         Py_DECREF(value);
3412 
3413         value = PyFloat_FromDouble(application_time);
3414         PyDict_SetItemString(event, "application_time", value);
3415         Py_DECREF(value);
3416 
3417         PyDict_SetItemString(event, "request_data", thread_handle->request_data);
3418 
3419         wsgi_publish_event("request_finished", event);
3420 
3421         Py_DECREF(event);
3422     }
3423 
3424     /*
3425      * If result indicates an internal server error, then
3426      * replace the status line in the request object else
3427      * that provided by the application will be what is used
3428      * in any error page automatically generated by Apache.
3429      */
3430 
3431     if (self->result == HTTP_INTERNAL_SERVER_ERROR)
3432         self->r->status_line = "500 Internal Server Error";
3433 
3434     Py_DECREF(args);
3435     Py_DECREF(start);
3436     Py_DECREF(vars);
3437 
3438     Py_XDECREF(nrwrapper);
3439     Py_XDECREF(evwrapper);
3440 
3441     Py_XDECREF(self->sequence);
3442     self->sequence = NULL;
3443 
3444     return self->result;
3445 }
3446 
Adapter_write(AdapterObject * self,PyObject * args)3447 static PyObject *Adapter_write(AdapterObject *self, PyObject *args)
3448 {
3449     PyObject *item = NULL;
3450     const char *data = NULL;
3451     long length = 0;
3452 
3453     if (!self->r) {
3454         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
3455         return NULL;
3456     }
3457 
3458     if (!PyArg_ParseTuple(args, "O:write", &item))
3459         return NULL;
3460 
3461     if (!PyString_Check(item)) {
3462         PyErr_Format(PyExc_TypeError, "byte string value expected, value "
3463                      "of type %.200s found", item->ob_type->tp_name);
3464         return NULL;
3465     }
3466 
3467     data = PyString_AsString(item);
3468     length = PyString_Size(item);
3469 
3470     if (!Adapter_output(self, data, length, item, 1)) {
3471         return NULL;
3472     }
3473 
3474     Py_INCREF(Py_None);
3475     return Py_None;
3476 }
3477 
Adapter_ssl_is_https(AdapterObject * self,PyObject * args)3478 static PyObject *Adapter_ssl_is_https(AdapterObject *self, PyObject *args)
3479 {
3480     APR_OPTIONAL_FN_TYPE(ssl_is_https) *ssl_is_https = 0;
3481 
3482     if (!self->r) {
3483         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
3484         return NULL;
3485     }
3486 
3487     if (!PyArg_ParseTuple(args, ":ssl_is_https"))
3488         return NULL;
3489 
3490     ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
3491 
3492     if (ssl_is_https == 0)
3493       return Py_BuildValue("i", 0);
3494 
3495     return Py_BuildValue("i", ssl_is_https(self->r->connection));
3496 }
3497 
Adapter_ssl_var_lookup(AdapterObject * self,PyObject * args)3498 static PyObject *Adapter_ssl_var_lookup(AdapterObject *self, PyObject *args)
3499 {
3500     APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *ssl_var_lookup = 0;
3501 
3502     PyObject *item = NULL;
3503     PyObject *latin_item = NULL;
3504 
3505     char *name = 0;
3506     char *value = 0;
3507 
3508     if (!self->r) {
3509         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
3510         return NULL;
3511     }
3512 
3513     if (!PyArg_ParseTuple(args, "O:ssl_var_lookup", &item))
3514         return NULL;
3515 
3516 #if PY_MAJOR_VERSION >= 3
3517     if (PyUnicode_Check(item)) {
3518         latin_item = PyUnicode_AsLatin1String(item);
3519         if (!latin_item) {
3520             PyErr_Format(PyExc_TypeError, "byte string value expected, "
3521                          "value containing non 'latin-1' characters found");
3522 
3523             return NULL;
3524         }
3525 
3526         item = latin_item;
3527     }
3528 #endif
3529 
3530     if (!PyString_Check(item)) {
3531         PyErr_Format(PyExc_TypeError, "byte string value expected, value "
3532                      "of type %.200s found", item->ob_type->tp_name);
3533 
3534         Py_XDECREF(latin_item);
3535 
3536         return NULL;
3537     }
3538 
3539     name = PyString_AsString(item);
3540 
3541     ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
3542 
3543     if (ssl_var_lookup == 0)
3544     {
3545         Py_XDECREF(latin_item);
3546 
3547         Py_INCREF(Py_None);
3548 
3549         return Py_None;
3550     }
3551 
3552     value = ssl_var_lookup(self->r->pool, self->r->server,
3553                            self->r->connection, self->r, name);
3554 
3555     Py_XDECREF(latin_item);
3556 
3557     if (!value) {
3558         Py_INCREF(Py_None);
3559 
3560         return Py_None;
3561     }
3562 
3563 #if PY_MAJOR_VERSION >= 3
3564     return PyUnicode_DecodeLatin1(value, strlen(value), NULL);
3565 #else
3566     return PyString_FromString(value);
3567 #endif
3568 }
3569 
3570 static PyMethodDef Adapter_methods[] = {
3571     { "start_response", (PyCFunction)Adapter_start_response, METH_VARARGS, 0 },
3572     { "write",          (PyCFunction)Adapter_write, METH_VARARGS, 0 },
3573     { "ssl_is_https",   (PyCFunction)Adapter_ssl_is_https, METH_VARARGS, 0 },
3574     { "ssl_var_lookup", (PyCFunction)Adapter_ssl_var_lookup, METH_VARARGS, 0 },
3575     { NULL, NULL}
3576 };
3577 
3578 static PyTypeObject Adapter_Type = {
3579     PyVarObject_HEAD_INIT(NULL, 0)
3580     "mod_wsgi.Adapter",     /*tp_name*/
3581     sizeof(AdapterObject),  /*tp_basicsize*/
3582     0,                      /*tp_itemsize*/
3583     /* methods */
3584     (destructor)Adapter_dealloc, /*tp_dealloc*/
3585     0,                      /*tp_print*/
3586     0,                      /*tp_getattr*/
3587     0,                      /*tp_setattr*/
3588     0,                      /*tp_compare*/
3589     0,                      /*tp_repr*/
3590     0,                      /*tp_as_number*/
3591     0,                      /*tp_as_sequence*/
3592     0,                      /*tp_as_mapping*/
3593     0,                      /*tp_hash*/
3594     0,                      /*tp_call*/
3595     0,                      /*tp_str*/
3596     0,                      /*tp_getattro*/
3597     0,                      /*tp_setattro*/
3598     0,                      /*tp_as_buffer*/
3599     Py_TPFLAGS_DEFAULT,     /*tp_flags*/
3600     0,                      /*tp_doc*/
3601     0,                      /*tp_traverse*/
3602     0,                      /*tp_clear*/
3603     0,                      /*tp_richcompare*/
3604     0,                      /*tp_weaklistoffset*/
3605     0,                      /*tp_iter*/
3606     0,                      /*tp_iternext*/
3607     Adapter_methods,        /*tp_methods*/
3608     0,                      /*tp_members*/
3609     0,                      /*tp_getset*/
3610     0,                      /*tp_base*/
3611     0,                      /*tp_dict*/
3612     0,                      /*tp_descr_get*/
3613     0,                      /*tp_descr_set*/
3614     0,                      /*tp_dictoffset*/
3615     0,                      /*tp_init*/
3616     0,                      /*tp_alloc*/
3617     0,                      /*tp_new*/
3618     0,                      /*tp_free*/
3619     0,                      /*tp_is_gc*/
3620 };
3621 
3622 /*
3623  * Code for importing a module from source by absolute path.
3624  */
3625 
wsgi_load_source(apr_pool_t * pool,request_rec * r,const char * name,int exists,const char * filename,const char * process_group,const char * application_group,int ignore_system_exit)3626 static PyObject *wsgi_load_source(apr_pool_t *pool, request_rec *r,
3627                                   const char *name, int exists,
3628                                   const char* filename,
3629                                   const char *process_group,
3630                                   const char *application_group,
3631                                   int ignore_system_exit)
3632 {
3633     FILE *fp = NULL;
3634     PyObject *m = NULL;
3635     PyObject *co = NULL;
3636     struct _node *n = NULL;
3637 
3638 #if defined(WIN32) && defined(APR_HAS_UNICODE_FS)
3639     apr_wchar_t wfilename[APR_PATH_MAX];
3640 #endif
3641 
3642     if (exists) {
3643         Py_BEGIN_ALLOW_THREADS
3644         if (r) {
3645             ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
3646                           "mod_wsgi (pid=%d, process='%s', application='%s'): "
3647                           "Reloading WSGI script '%s'.", getpid(),
3648                           process_group, application_group, filename);
3649         }
3650         else {
3651             ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
3652                          "mod_wsgi (pid=%d, process='%s', application='%s'): "
3653                          "Reloading WSGI script '%s'.", getpid(),
3654                          process_group, application_group, filename);
3655         }
3656         Py_END_ALLOW_THREADS
3657     }
3658     else {
3659         Py_BEGIN_ALLOW_THREADS
3660         if (r) {
3661             ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
3662                           "mod_wsgi (pid=%d, process='%s', application='%s'): "
3663                           "Loading Python script file '%s'.", getpid(),
3664                           process_group, application_group, filename);
3665         }
3666         else {
3667             ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
3668                          "mod_wsgi (pid=%d, process='%s', application='%s'): "
3669                          "Loading Python script file '%s'.", getpid(),
3670                          process_group, application_group, filename);
3671         }
3672         Py_END_ALLOW_THREADS
3673     }
3674 
3675 #if defined(WIN32) && defined(APR_HAS_UNICODE_FS)
3676     if (wsgi_utf8_to_unicode_path(wfilename, sizeof(wfilename) /
3677                                   sizeof(apr_wchar_t), filename)) {
3678 
3679         Py_BEGIN_ALLOW_THREADS
3680         if (r) {
3681             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
3682                           "mod_wsgi (pid=%d, process='%s', "
3683                           "application='%s'): Failed to convert '%s' "
3684                           "to UCS2 filename.", getpid(),
3685                           process_group, application_group, filename);
3686         }
3687         else {
3688             ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
3689                          "mod_wsgi (pid=%d, process='%s', "
3690                          "application='%s'): Failed to convert '%s' "
3691                          "to UCS2 filename.", getpid(),
3692                          process_group, application_group, filename);
3693         }
3694         Py_END_ALLOW_THREADS
3695         return NULL;
3696     }
3697 
3698     fp = _wfopen(wfilename, L"r");
3699 #else
3700     fp = fopen(filename, "r");
3701 #endif
3702 
3703     if (!fp) {
3704         Py_BEGIN_ALLOW_THREADS
3705         if (r) {
3706             ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
3707                           "mod_wsgi (pid=%d, process='%s', application='%s'): "
3708                           "Call to fopen() failed for '%s'.", getpid(),
3709                           process_group, application_group, filename);
3710         }
3711         else {
3712             ap_log_error(APLOG_MARK, APLOG_ERR, errno, wsgi_server,
3713                          "mod_wsgi (pid=%d, process='%s', application='%s'): "
3714                          "Call to fopen() failed for '%s'.", getpid(),
3715                          process_group, application_group, filename);
3716         }
3717         Py_END_ALLOW_THREADS
3718         return NULL;
3719     }
3720 
3721     n = PyParser_SimpleParseFile(fp, filename, Py_file_input);
3722 
3723     fclose(fp);
3724 
3725     if (!n) {
3726         Py_BEGIN_ALLOW_THREADS
3727         if (r) {
3728             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
3729                           "mod_wsgi (pid=%d, process='%s', application='%s'): "
3730                           "Failed to parse Python script file '%s'.", getpid(),
3731                           process_group, application_group, filename);
3732         }
3733         else {
3734             ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
3735                          "mod_wsgi (pid=%d, process='%s', application='%s'): "
3736                          "Failed to parse Python script file '%s'.", getpid(),
3737                          process_group, application_group, filename);
3738         }
3739         Py_END_ALLOW_THREADS
3740 
3741         wsgi_log_python_error(r, NULL, filename, 0);
3742 
3743         return NULL;
3744     }
3745 
3746     co = (PyObject *)PyNode_Compile(n, filename);
3747     PyNode_Free(n);
3748 
3749     if (co)
3750         m = PyImport_ExecCodeModuleEx((char *)name, co, (char *)filename);
3751 
3752     Py_XDECREF(co);
3753 
3754     if (m) {
3755         PyObject *object = NULL;
3756 
3757         if (!r || strcmp(r->filename, filename)) {
3758             apr_finfo_t finfo;
3759             if (apr_stat(&finfo, filename, APR_FINFO_NORM,
3760                          pool) != APR_SUCCESS) {
3761                 object = PyLong_FromLongLong(0);
3762             }
3763             else {
3764                 object = PyLong_FromLongLong(finfo.mtime);
3765             }
3766         }
3767         else {
3768             object = PyLong_FromLongLong(r->finfo.mtime);
3769         }
3770         PyModule_AddObject(m, "__mtime__", object);
3771     }
3772     else {
3773         if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
3774             if (!ignore_system_exit) {
3775                 Py_BEGIN_ALLOW_THREADS
3776                 if (r) {
3777                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
3778                                   "mod_wsgi (pid=%d): SystemExit exception "
3779                                   "raised when doing exec of Python script "
3780                                   "file '%s'.", getpid(), filename);
3781                 }
3782                 else {
3783                     ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
3784                                  "mod_wsgi (pid=%d): SystemExit exception "
3785                                  "raised when doing exec of Python script "
3786                                  "file '%s'.", getpid(), filename);
3787                 }
3788                 Py_END_ALLOW_THREADS
3789             }
3790         }
3791         else {
3792             Py_BEGIN_ALLOW_THREADS
3793             if (r) {
3794                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
3795                               "mod_wsgi (pid=%d): Failed to exec Python script "
3796                               "file '%s'.", getpid(), filename);
3797             }
3798             else {
3799                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
3800                              "mod_wsgi (pid=%d): Failed to exec Python script "
3801                              "file '%s'.", getpid(), filename);
3802             }
3803             Py_END_ALLOW_THREADS
3804 
3805             wsgi_log_python_error(r, NULL, filename, 0);
3806         }
3807     }
3808 
3809     return m;
3810 }
3811 
wsgi_reload_required(apr_pool_t * pool,request_rec * r,const char * filename,PyObject * module,const char * resource)3812 static int wsgi_reload_required(apr_pool_t *pool, request_rec *r,
3813                                 const char *filename, PyObject *module,
3814                                 const char *resource)
3815 {
3816     PyObject *dict = NULL;
3817     PyObject *object = NULL;
3818     apr_time_t mtime = 0;
3819 
3820     dict = PyModule_GetDict(module);
3821     object = PyDict_GetItemString(dict, "__mtime__");
3822 
3823     if (object) {
3824         mtime = PyLong_AsLongLong(object);
3825 
3826         if (!r || strcmp(r->filename, filename)) {
3827             apr_finfo_t finfo;
3828             if (apr_stat(&finfo, filename, APR_FINFO_NORM,
3829                          pool) != APR_SUCCESS) {
3830                 return 1;
3831             }
3832             else if (mtime != finfo.mtime) {
3833                 return 1;
3834             }
3835         }
3836         else {
3837             if (mtime != r->finfo.mtime)
3838                 return 1;
3839         }
3840     }
3841     else
3842         return 1;
3843 
3844     if (resource) {
3845         PyObject *dict = NULL;
3846         PyObject *object = NULL;
3847 
3848         dict = PyModule_GetDict(module);
3849         object = PyDict_GetItemString(dict, "reload_required");
3850 
3851         if (object) {
3852             PyObject *args = NULL;
3853             PyObject *result = NULL;
3854 
3855             Py_INCREF(object);
3856             args = Py_BuildValue("(s)", resource);
3857             result = PyEval_CallObject(object, args);
3858             Py_DECREF(args);
3859             Py_DECREF(object);
3860 
3861             if (result && PyObject_IsTrue(result)) {
3862                 Py_DECREF(result);
3863 
3864                 return 1;
3865             }
3866 
3867             if (PyErr_Occurred())
3868                 wsgi_log_python_error(r, NULL, filename, 0);
3869 
3870             Py_XDECREF(result);
3871         }
3872     }
3873 
3874     return 0;
3875 }
3876 
wsgi_module_name(apr_pool_t * pool,const char * filename)3877 static char *wsgi_module_name(apr_pool_t *pool, const char *filename)
3878 {
3879     char *hash = NULL;
3880     char *file = NULL;
3881 
3882     /*
3883      * Calculate a name for the module using the MD5 of its full
3884      * pathname. This is so that different code files with the
3885      * same basename are still considered unique. Note that where
3886      * we believe a case insensitive file system is being used,
3887      * we always change the file name to lower case so that use
3888      * of different case in name doesn't result in duplicate
3889      * modules being loaded for the same file.
3890      */
3891 
3892     file = (char *)filename;
3893 
3894     if (wsgi_server_config->case_sensitivity) {
3895         file = apr_pstrdup(pool, file);
3896         ap_str_tolower(file);
3897     }
3898 
3899     hash = ap_md5(pool, (const unsigned char *)file);
3900     return apr_pstrcat(pool, "_mod_wsgi_", hash, NULL);
3901 }
3902 
3903 #if APR_HAS_THREADS
3904 static apr_thread_mutex_t* wsgi_module_lock = NULL;
3905 #endif
3906 
wsgi_execute_script(request_rec * r)3907 static int wsgi_execute_script(request_rec *r)
3908 {
3909     WSGIRequestConfig *config = NULL;
3910 
3911     InterpreterObject *interp = NULL;
3912     PyObject *modules = NULL;
3913     PyObject *module = NULL;
3914     const char *script = NULL;
3915     const char *name = NULL;
3916     int exists = 0;
3917 
3918     int status;
3919 
3920     WSGIThreadInfo *thread_info = NULL;
3921 
3922     /* Grab request configuration. */
3923 
3924     config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
3925                                                        &wsgi_module);
3926 
3927     /*
3928      * Acquire the desired python interpreter. Once this is done
3929      * it is safe to start manipulating python objects.
3930      */
3931 
3932     interp = wsgi_acquire_interpreter(config->application_group);
3933 
3934     if (!interp) {
3935         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
3936                       "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
3937                       getpid(), config->application_group);
3938 
3939         return HTTP_INTERNAL_SERVER_ERROR;
3940     }
3941 
3942     /* Setup startup timeout if first request and specified. */
3943 
3944 #if defined(MOD_WSGI_WITH_DAEMONS)
3945     if (wsgi_daemon_process) {
3946         if (wsgi_startup_shutdown_time == 0) {
3947             if (wsgi_startup_timeout > 0) {
3948                 apr_thread_mutex_lock(wsgi_monitor_lock);
3949                 wsgi_startup_shutdown_time = apr_time_now();
3950                 wsgi_startup_shutdown_time += wsgi_startup_timeout;
3951                 apr_thread_mutex_unlock(wsgi_monitor_lock);
3952             }
3953         }
3954     }
3955 #endif
3956 
3957     /*
3958      * Use a lock around the check to see if the module is
3959      * already loaded and the import of the module to prevent
3960      * two request handlers trying to import the module at the
3961      * same time.
3962      */
3963 
3964 #if APR_HAS_THREADS
3965     Py_BEGIN_ALLOW_THREADS
3966     apr_thread_mutex_lock(wsgi_module_lock);
3967     Py_END_ALLOW_THREADS
3968 #endif
3969 
3970     /* Calculate the Python module name to be used for script. */
3971 
3972     if (config->handler_script && *config->handler_script) {
3973         script = config->handler_script;
3974 
3975 #if 0
3976         /*
3977          * Check for whether a module reference is provided
3978          * as opposed to a filesystem path.
3979          */
3980 
3981         if (strlen(script) > 2 && script[0] == '(' &&
3982             script[strlen(script)-1] == ')') {
3983             name = apr_pstrndup(r->pool, script+1, strlen(script)-2);
3984 
3985             module = PyImport_ImportModule(name);
3986 
3987             if (!module) {
3988                 Py_BEGIN_ALLOW_THREADS
3989                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
3990                              "mod_wsgi (pid=%d): Failed to import handler "
3991                              "via Python module reference %s.", getpid(),
3992                              script);
3993                 Py_END_ALLOW_THREADS
3994 
3995                 wsgi_log_python_error(r, NULL, r->filename, 0);
3996             }
3997         }
3998 #endif
3999     }
4000     else
4001         script = r->filename;
4002 
4003     if (!module) {
4004         name = wsgi_module_name(r->pool, script);
4005 
4006         modules = PyImport_GetModuleDict();
4007         module = PyDict_GetItemString(modules, name);
4008 
4009         Py_XINCREF(module);
4010 
4011         if (module)
4012             exists = 1;
4013 
4014         /*
4015          * If script reloading is enabled and the module for it has
4016          * previously been loaded, see if it has been modified since
4017          * the last time it was accessed. For a handler script will
4018          * also see if it contains a custom function for determining
4019          * if a reload should be performed.
4020          */
4021 
4022         if (module && config->script_reloading) {
4023             if (wsgi_reload_required(r->pool, r, script, module, r->filename)) {
4024                 /*
4025                  * Script file has changed. Discard reference to
4026                  * loaded module and work out what action we are
4027                  * supposed to take. Choices are process reloading
4028                  * and module reloading. Process reloading cannot be
4029                  * performed unless a daemon process is being used.
4030                  */
4031 
4032                 Py_DECREF(module);
4033                 module = NULL;
4034 
4035 #if defined(MOD_WSGI_WITH_DAEMONS)
4036                 if (*config->process_group) {
4037                     /*
4038                      * Need to restart the daemon process. We bail
4039                      * out on the request process here, sending back
4040                      * a special response header indicating that
4041                      * process is being restarted and that remote
4042                      * end should abandon connection and attempt to
4043                      * reconnect again. We also need to signal this
4044                      * process so it will actually shutdown. The
4045                      * process supervisor code will ensure that it
4046                      * is restarted.
4047                      */
4048 
4049                     Py_BEGIN_ALLOW_THREADS
4050                     ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
4051                                  "mod_wsgi (pid=%d): Force restart of "
4052                                  "process '%s'.", getpid(),
4053                                  config->process_group);
4054                     Py_END_ALLOW_THREADS
4055 
4056 #if APR_HAS_THREADS
4057                     apr_thread_mutex_unlock(wsgi_module_lock);
4058 #endif
4059 
4060                     wsgi_release_interpreter(interp);
4061 
4062                     r->status = HTTP_INTERNAL_SERVER_ERROR;
4063                     r->status_line = "200 Rejected";
4064 
4065                     wsgi_daemon_shutdown++;
4066                     kill(getpid(), SIGINT);
4067 
4068                     return OK;
4069                 }
4070                 else {
4071                     /*
4072                      * Need to reload just the script module. Remove
4073                      * the module from the modules dictionary before
4074                      * reloading it again. If code is executing
4075                      * within the module at the time, the callers
4076                      * reference count on the module should ensure
4077                      * it isn't actually destroyed until it is
4078                      * finished.
4079                      */
4080 
4081                     PyDict_DelItemString(modules, name);
4082                 }
4083 #else
4084                 /*
4085                  * Need to reload just the script module. Remove
4086                  * the module from the modules dictionary before
4087                  * reloading it again. If code is executing
4088                  * within the module at the time, the callers
4089                  * reference count on the module should ensure
4090                  * it isn't actually destroyed until it is
4091                  * finished.
4092                  */
4093 
4094                 PyDict_DelItemString(modules, name);
4095 #endif
4096             }
4097         }
4098     }
4099 
4100     /*
4101      * When process reloading is in use need to indicate
4102      * that request content should now be sent through.
4103      * This is done by writing a special response header
4104      * directly out onto the appropriate network output
4105      * filter. The special response is picked up by
4106      * remote end and data will then be sent.
4107      */
4108 
4109 #if defined(MOD_WSGI_WITH_DAEMONS)
4110     if (*config->process_group) {
4111         ap_filter_t *filters;
4112         apr_bucket_brigade *bb;
4113         apr_bucket *b;
4114 
4115         const char *data = "Status: 200 Continue\r\n\r\n";
4116         long length = strlen(data);
4117 
4118         filters = r->output_filters;
4119         while (filters && filters->frec->ftype != AP_FTYPE_NETWORK) {
4120             filters = filters->next;
4121         }
4122 
4123         bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
4124 
4125         b = apr_bucket_transient_create(data, length,
4126                                         r->connection->bucket_alloc);
4127         APR_BRIGADE_INSERT_TAIL(bb, b);
4128 
4129         b = apr_bucket_flush_create(r->connection->bucket_alloc);
4130         APR_BRIGADE_INSERT_TAIL(bb, b);
4131 
4132         /*
4133          * This should always work, so ignore any errors
4134          * from passing the brigade to the network
4135          * output filter. If there are are problems they
4136          * will be picked up further down in processing
4137          * anyway.
4138          */
4139 
4140         ap_pass_brigade(filters, bb);
4141     }
4142 #endif
4143 
4144     /* Setup metrics for start of request. */
4145 
4146     thread_info = wsgi_start_request(r);
4147 
4148     /* Load module if not already loaded. */
4149 
4150     if (!module) {
4151         module = wsgi_load_source(r->pool, r, name, exists, script,
4152                                   config->process_group,
4153                                   config->application_group, 0);
4154     }
4155 
4156     /* Safe now to release the module lock. */
4157 
4158 #if APR_HAS_THREADS
4159     apr_thread_mutex_unlock(wsgi_module_lock);
4160 #endif
4161 
4162     /*
4163      * Clear startup timeout and prevent from running again if the
4164      * module was successfully loaded.
4165      */
4166 
4167 #if defined(MOD_WSGI_WITH_DAEMONS)
4168     if (module && wsgi_startup_shutdown_time > 0) {
4169         wsgi_startup_shutdown_time = -1;
4170     }
4171 #endif
4172 
4173     /* Assume an internal server error unless everything okay. */
4174 
4175     status = HTTP_INTERNAL_SERVER_ERROR;
4176 
4177     /* Determine if script exists and execute it. */
4178 
4179     if (module) {
4180         PyObject *module_dict = NULL;
4181         PyObject *object = NULL;
4182 
4183         module_dict = PyModule_GetDict(module);
4184         object = PyDict_GetItemString(module_dict, config->callable_object);
4185 
4186         if (object) {
4187             AdapterObject *adapter = NULL;
4188             adapter = newAdapterObject(r);
4189 
4190             if (adapter) {
4191                 PyObject *method = NULL;
4192                 PyObject *args = NULL;
4193 
4194                 Py_INCREF(adapter->log_buffer);
4195                 thread_info->log_buffer = adapter->log_buffer;
4196 
4197                 Py_INCREF(object);
4198                 status = Adapter_run(adapter, object);
4199                 Py_DECREF(object);
4200 
4201                 /*
4202                  * Wipe out references to Apache request object
4203                  * held by Python objects, so can detect when an
4204                  * application holds on to the transient Python
4205                  * objects beyond the life of the request and
4206                  * thus raise an exception if they are used.
4207                  */
4208 
4209                 adapter->r = NULL;
4210 
4211                 Input_finish(adapter->input);
4212 
4213                 /* Close the log object so data is flushed. */
4214 
4215                 method = PyObject_GetAttrString(adapter->log, "close");
4216 
4217                 if (!method) {
4218                     PyErr_Format(PyExc_AttributeError,
4219                                  "'%s' object has no attribute 'close'",
4220                                  adapter->log->ob_type->tp_name);
4221                 }
4222                 else {
4223                     args = PyTuple_New(0);
4224                     object = PyEval_CallObject(method, args);
4225                     Py_DECREF(args);
4226                 }
4227 
4228                 Py_XDECREF(object);
4229                 Py_XDECREF(method);
4230 
4231                 Py_CLEAR(thread_info->log_buffer);
4232 
4233                 adapter->bb = NULL;
4234             }
4235 
4236             Py_XDECREF((PyObject *)adapter);
4237         }
4238         else {
4239             Py_BEGIN_ALLOW_THREADS
4240             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
4241                           "mod_wsgi (pid=%d): Target WSGI script '%s' does "
4242                           "not contain WSGI application '%s'.",
4243                           getpid(), script, config->callable_object);
4244             Py_END_ALLOW_THREADS
4245 
4246             status = HTTP_NOT_FOUND;
4247         }
4248     }
4249 
4250     /* Log any details of exceptions if execution failed. */
4251 
4252     if (PyErr_Occurred())
4253         wsgi_log_python_error(r, NULL, r->filename, 0);
4254 
4255     Py_XDECREF(module);
4256 
4257     /* Finalise any metrics at end of the request. */
4258 
4259     wsgi_end_request();
4260 
4261     /* Cleanup and release interpreter, */
4262 
4263     wsgi_release_interpreter(interp);
4264 
4265     return status;
4266 }
4267 
4268 /*
4269  * Apache child process initialisation and cleanup. Initialise
4270  * global table containing Python interpreter instances and
4271  * cache reference to main interpreter. Also register cleanup
4272  * function to delete interpreter on process shutdown.
4273  */
4274 
wsgi_python_child_cleanup(void * data)4275 static apr_status_t wsgi_python_child_cleanup(void *data)
4276 {
4277     PyObject *interp = NULL;
4278 
4279     /*
4280      * If not a daemon process need to publish that process
4281      * is shutting down here. For daemon we did it earlier
4282      * before trying to wait on request threads.
4283      */
4284 
4285 #if defined(MOD_WSGI_WITH_DAEMONS)
4286     if (!wsgi_daemon_process)
4287         wsgi_publish_process_stopping(wsgi_shutdown_reason);
4288 #else
4289     wsgi_publish_process_stopping(wsgi_shutdown_reason);
4290 #endif
4291 
4292     /* In a multithreaded MPM must protect table. */
4293 
4294 #if APR_HAS_THREADS
4295     apr_thread_mutex_lock(wsgi_interp_lock);
4296 #endif
4297 
4298     /*
4299      * We should be executing in the main thread again at this
4300      * point but without the GIL, so simply restore the original
4301      * thread state for that thread that we remembered when we
4302      * initialised the interpreter.
4303      */
4304 
4305     PyEval_AcquireThread(wsgi_main_tstate);
4306 
4307     /*
4308      * Extract a handle to the main Python interpreter from
4309      * interpreters dictionary as want to process that one last.
4310      */
4311 
4312     interp = PyDict_GetItemString(wsgi_interpreters, "");
4313     Py_INCREF(interp);
4314 
4315     /*
4316      * Remove all items from interpreters dictionary. This will
4317      * have side affect of calling any exit functions and
4318      * destroying interpreters we own.
4319      */
4320 
4321     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
4322                  "mod_wsgi (pid=%d): Destroying interpreters.", getpid());
4323 
4324     PyDict_Clear(wsgi_interpreters);
4325 
4326 #if APR_HAS_THREADS
4327     apr_thread_mutex_unlock(wsgi_interp_lock);
4328 #endif
4329 
4330     /*
4331      * Now we decrement reference on handle for main Python
4332      * interpreter. This only causes exit functions to be called
4333      * and doesn't result in interpreter being destroyed as we
4334      * we didn't previously mark ourselves as the owner of the
4335      * interpreter. Note that when Python as a whole is later
4336      * being destroyed it will also call exit functions, but by
4337      * then the exit function registrations have been removed
4338      * and so they will not actually be run a second time.
4339      */
4340 
4341     Py_DECREF(interp);
4342 
4343     /*
4344      * The code which performs actual shutdown of the main
4345      * interpreter expects to be called without the GIL, so
4346      * we release it here again.
4347      */
4348 
4349     PyEval_ReleaseThread(wsgi_main_tstate);
4350 
4351     /*
4352      * Destroy Python itself including the main interpreter.
4353      * If mod_python is being loaded it is left to mod_python to
4354      * destroy Python, although it currently doesn't do so.
4355      */
4356 
4357     if (wsgi_python_initialized)
4358         wsgi_python_term();
4359 
4360     return APR_SUCCESS;
4361 }
4362 
wsgi_python_child_init(apr_pool_t * p)4363 static void wsgi_python_child_init(apr_pool_t *p)
4364 {
4365     PyGILState_STATE state;
4366     PyObject *object = NULL;
4367 
4368     int ignore_system_exit = 0;
4369 
4370     /* Working with Python, so must acquire GIL. */
4371 
4372     state = PyGILState_Ensure();
4373 
4374     /*
4375      * Trigger any special Python stuff required after a fork.
4376      * Only do this though if we were responsible for the
4377      * initialisation of the Python interpreter in the first
4378      * place to avoid it being done multiple times. Also only
4379      * do it if Python was initialised in parent process.
4380      */
4381 
4382 #ifdef HAVE_FORK
4383     if (wsgi_python_initialized && !wsgi_python_after_fork) {
4384 #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 7)
4385         PyOS_AfterFork_Child();
4386 #else
4387         PyOS_AfterFork();
4388 #endif
4389     }
4390 #endif
4391 
4392     /* Finalise any Python objects required by child process. */
4393 
4394     PyType_Ready(&Log_Type);
4395     PyType_Ready(&Stream_Type);
4396     PyType_Ready(&Input_Type);
4397     PyType_Ready(&Adapter_Type);
4398     PyType_Ready(&Restricted_Type);
4399     PyType_Ready(&Interpreter_Type);
4400     PyType_Ready(&Dispatch_Type);
4401     PyType_Ready(&Auth_Type);
4402 
4403     PyType_Ready(&SignalIntercept_Type);
4404 
4405 #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 4)
4406     PyType_Ready(&ShutdownInterpreter_Type);
4407 #endif
4408 
4409     /* Initialise Python interpreter instance table and lock. */
4410 
4411     wsgi_interpreters = PyDict_New();
4412 
4413 #if APR_HAS_THREADS
4414     apr_thread_mutex_create(&wsgi_interp_lock, APR_THREAD_MUTEX_UNNESTED, p);
4415     apr_thread_mutex_create(&wsgi_module_lock, APR_THREAD_MUTEX_UNNESTED, p);
4416     apr_thread_mutex_create(&wsgi_shutdown_lock, APR_THREAD_MUTEX_UNNESTED, p);
4417 #endif
4418 
4419     /*
4420      * Create an interpreters index using Apache data structure so
4421      * can iterate over interpreter names without needing Python GIL.
4422      */
4423 
4424     wsgi_interpreters_index = apr_hash_make(p);
4425 
4426     /*
4427      * Initialise the key for data related to a thread and force
4428      * creation of thread info.
4429      */
4430 
4431     apr_threadkey_private_create(&wsgi_thread_key, NULL, p);
4432 
4433     wsgi_thread_info(1, 0);
4434 
4435     /*
4436      * Cache a reference to the first Python interpreter
4437      * instance. This interpreter is special as some third party
4438      * Python modules will only work when used from within this
4439      * interpreter. This is generally when they use the Python
4440      * simplified GIL API or otherwise don't use threading API
4441      * properly. An empty string for name is used to identify
4442      * the first Python interpreter instance.
4443      */
4444 
4445     object = (PyObject *)newInterpreterObject(NULL);
4446     PyDict_SetItemString(wsgi_interpreters, "", object);
4447     Py_DECREF(object);
4448 
4449     apr_hash_set(wsgi_interpreters_index, "", APR_HASH_KEY_STRING, "");
4450 
4451     /* Restore the prior thread state and release the GIL. */
4452 
4453     PyGILState_Release(state);
4454 
4455     /* Register cleanups to performed on process shutdown. */
4456 
4457     apr_pool_cleanup_register(p, NULL, wsgi_python_child_cleanup,
4458                               apr_pool_cleanup_null);
4459 
4460     /* Loop through import scripts for this process and load them. */
4461 
4462 #if defined(MOD_WSGI_WITH_DAEMONS)
4463     if (wsgi_daemon_process && wsgi_daemon_process->group->threads == 0)
4464         ignore_system_exit = 1;
4465 #endif
4466 
4467     if (wsgi_import_list) {
4468         apr_array_header_t *scripts = NULL;
4469 
4470         WSGIScriptFile *entries;
4471         WSGIScriptFile *entry;
4472 
4473         int i;
4474 
4475         scripts = wsgi_import_list;
4476         entries = (WSGIScriptFile *)scripts->elts;
4477 
4478         for (i = 0; i < scripts->nelts; ++i) {
4479             entry = &entries[i];
4480 
4481             /*
4482              * Stop loading scripts if this is a daemon process and
4483              * we have already been flagged to be shutdown.
4484              */
4485 
4486             if (wsgi_daemon_shutdown)
4487                 break;
4488 
4489             if (!strcmp(wsgi_daemon_group, entry->process_group)) {
4490                 InterpreterObject *interp = NULL;
4491                 PyObject *modules = NULL;
4492                 PyObject *module = NULL;
4493                 char *name = NULL;
4494                 int exists = 0;
4495 
4496                 interp = wsgi_acquire_interpreter(entry->application_group);
4497 
4498                 if (!interp) {
4499                     ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
4500                                   "mod_wsgi (pid=%d): Cannot acquire "
4501                                   "interpreter '%s'.", getpid(),
4502                                   entry->application_group);
4503                 }
4504 
4505                 /* Calculate the Python module name to be used for script. */
4506 
4507                 name = wsgi_module_name(p, entry->handler_script);
4508 
4509                 /*
4510                  * Use a lock around the check to see if the module is
4511                  * already loaded and the import of the module. Strictly
4512                  * speaking shouldn't be required at this point.
4513                  */
4514 
4515 #if APR_HAS_THREADS
4516                 Py_BEGIN_ALLOW_THREADS
4517                 apr_thread_mutex_lock(wsgi_module_lock);
4518                 Py_END_ALLOW_THREADS
4519 #endif
4520 
4521                 modules = PyImport_GetModuleDict();
4522                 module = PyDict_GetItemString(modules, name);
4523 
4524                 Py_XINCREF(module);
4525 
4526                 if (module)
4527                     exists = 1;
4528 
4529                 /*
4530                  * If script reloading is enabled and the module for it has
4531                  * previously been loaded, see if it has been modified since
4532                  * the last time it was accessed.
4533                  */
4534 
4535                 if (module && wsgi_server_config->script_reloading) {
4536                     if (wsgi_reload_required(p, NULL, entry->handler_script,
4537                                              module, NULL)) {
4538                         /*
4539                          * Script file has changed. Only support module
4540                          * reloading for dispatch scripts. Remove the
4541                          * module from the modules dictionary before
4542                          * reloading it again. If code is executing within
4543                          * the module at the time, the callers reference
4544                          * count on the module should ensure it isn't
4545                          * actually destroyed until it is finished.
4546                          */
4547 
4548                         Py_DECREF(module);
4549                         module = NULL;
4550 
4551                         PyDict_DelItemString(modules, name);
4552                     }
4553                 }
4554 
4555                 if (!module) {
4556                     module = wsgi_load_source(p, NULL, name, exists,
4557                                               entry->handler_script,
4558                                               entry->process_group,
4559                                               entry->application_group,
4560                                               ignore_system_exit);
4561 
4562                     if (PyErr_Occurred())
4563                         PyErr_Clear();
4564                 }
4565 
4566                 /* Safe now to release the module lock. */
4567 
4568 #if APR_HAS_THREADS
4569                 apr_thread_mutex_unlock(wsgi_module_lock);
4570 #endif
4571 
4572                 /* Cleanup and release interpreter, */
4573 
4574                 Py_XDECREF(module);
4575 
4576                 wsgi_release_interpreter(interp);
4577             }
4578         }
4579     }
4580 }
4581 
4582 /* The processors for directives. */
4583 
wsgi_parse_option(apr_pool_t * p,const char ** line,const char ** name,const char ** value)4584 static int wsgi_parse_option(apr_pool_t *p, const char **line,
4585                              const char **name, const char **value)
4586 {
4587     const char *str = *line, *strend;
4588 
4589     while (*str && apr_isspace(*str))
4590         ++str;
4591 
4592     if (!*str || *str == '=') {
4593         *line = str;
4594         return !APR_SUCCESS;
4595     }
4596 
4597     /* Option must be of form name=value. Extract the name. */
4598 
4599     strend = str;
4600     while (*strend && *strend != '=' && !apr_isspace(*strend))
4601         ++strend;
4602 
4603     if (*strend != '=') {
4604         *line = str;
4605         return !APR_SUCCESS;
4606     }
4607 
4608     *name = apr_pstrndup(p, str, strend-str);
4609 
4610     *line = strend+1;
4611 
4612     /* Now extract the value. Note that value can be quoted. */
4613 
4614     *value = ap_getword_conf(p, line);
4615 
4616     return APR_SUCCESS;
4617 }
4618 
wsgi_add_script_alias(cmd_parms * cmd,void * mconfig,const char * args)4619 static const char *wsgi_add_script_alias(cmd_parms *cmd, void *mconfig,
4620                                          const char *args)
4621 {
4622     const char *l = NULL;
4623     const char *a = NULL;
4624 
4625     WSGIServerConfig *sconfig = NULL;
4626     WSGIAliasEntry *entry = NULL;
4627 
4628     const char *option = NULL;
4629     const char *value = NULL;
4630 
4631 #if defined(MOD_WSGI_WITH_DAEMONS)
4632     const char *process_group = NULL;
4633 #else
4634     const char *process_group = "";
4635 #endif
4636 
4637     const char *application_group = NULL;
4638     const char *callable_object = NULL;
4639 
4640     int pass_authorization = -1;
4641 
4642     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4643 
4644     if (!sconfig->alias_list) {
4645         sconfig->alias_list = apr_array_make(sconfig->pool, 20,
4646                                             sizeof(WSGIAliasEntry));
4647     }
4648 
4649     l = ap_getword_conf(cmd->pool, &args);
4650 
4651     if (*l == '\0' || *args == 0) {
4652         return apr_pstrcat(cmd->pool, cmd->cmd->name,
4653                            " requires at least two arguments",
4654                            cmd->cmd->errmsg ? ", " : NULL,
4655                            cmd->cmd->errmsg, NULL);
4656     }
4657 
4658     a = ap_getword_conf(cmd->pool, &args);
4659 
4660     if (*a == '\0') {
4661         return apr_pstrcat(cmd->pool, cmd->cmd->name,
4662                            " requires at least two arguments",
4663                            cmd->cmd->errmsg ? ", " : NULL,
4664                            cmd->cmd->errmsg, NULL);
4665     }
4666 
4667     while (*args) {
4668         if (wsgi_parse_option(cmd->pool, &args, &option,
4669                               &value) != APR_SUCCESS) {
4670             return "Invalid option to WSGI script alias definition.";
4671         }
4672 
4673         if (!strcmp(option, "application-group")) {
4674             if (!*value)
4675                 return "Invalid name for WSGI application group.";
4676 
4677             if (!strcmp(value, "%{GLOBAL}"))
4678                 value = "";
4679 
4680             application_group = value;
4681         }
4682 #if defined(MOD_WSGI_WITH_DAEMONS)
4683         else if (!strcmp(option, "process-group")) {
4684             if (!*value)
4685                 return "Invalid name for WSGI process group.";
4686 
4687             if (!strcmp(value, "%{GLOBAL}"))
4688                 value = "";
4689 
4690             process_group = value;
4691         }
4692 #endif
4693         else if (!strcmp(option, "callable-object")) {
4694             if (!*value)
4695                 return "Invalid name for WSGI callable object.";
4696 
4697             callable_object = value;
4698         }
4699         else if (!strcmp(option, "pass-authorization")) {
4700             if (!*value)
4701                 return "Invalid value for authorization flag.";
4702 
4703             if (strcasecmp(value, "Off") == 0)
4704                 pass_authorization = 0;
4705             else if (strcasecmp(value, "On") == 0)
4706                 pass_authorization = 1;
4707             else
4708                 return "Invalid value for authorization flag.";
4709         }
4710         else
4711             return "Invalid option to WSGI script alias definition.";
4712     }
4713 
4714     entry = (WSGIAliasEntry *)apr_array_push(sconfig->alias_list);
4715 
4716     if (cmd->info) {
4717         entry->regexp = ap_pregcomp(cmd->pool, l, AP_REG_EXTENDED);
4718         if (!entry->regexp)
4719             return "Regular expression could not be compiled.";
4720     }
4721 
4722     entry->location = l;
4723     entry->application = a;
4724 
4725     entry->process_group = process_group;
4726     entry->application_group = application_group;
4727     entry->callable_object = callable_object;
4728     entry->pass_authorization = pass_authorization;
4729 
4730     /*
4731      * Only add to import list if both process group and application
4732      * group are specified, that they don't include substitution values,
4733      * and in the case of WSGIScriptAliasMatch, that the WSGI script
4734      * target path doesn't include substitutions from URL pattern.
4735      */
4736 
4737     if (process_group && application_group &&
4738         !strstr(process_group, "%{") &&
4739         !strstr(application_group, "%{") &&
4740         (!cmd->info || !strstr(a, "$"))) {
4741 
4742         WSGIScriptFile *object = NULL;
4743 
4744         if (!wsgi_import_list) {
4745             wsgi_import_list = apr_array_make(sconfig->pool, 20,
4746                                               sizeof(WSGIScriptFile));
4747         }
4748 
4749         object = (WSGIScriptFile *)apr_array_push(wsgi_import_list);
4750 
4751         object->handler_script = a;
4752         object->process_group = process_group;
4753         object->application_group = application_group;
4754 
4755 #if defined(MOD_WSGI_WITH_DAEMONS)
4756         if (*object->process_group &&
4757             strcmp(object->process_group, "%{RESOURCE}") != 0 &&
4758             strcmp(object->process_group, "%{SERVER}") != 0 &&
4759             strcmp(object->process_group, "%{HOST}") != 0) {
4760 
4761             WSGIProcessGroup *group = NULL;
4762             WSGIProcessGroup *entries = NULL;
4763             WSGIProcessGroup *entry = NULL;
4764             int i;
4765 
4766             if (!wsgi_daemon_list)
4767                 return "WSGI process group not yet configured.";
4768 
4769             entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
4770 
4771             for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
4772                 entry = &entries[i];
4773 
4774                 if (!strcmp(entry->name, object->process_group)) {
4775                     group = entry;
4776                     break;
4777                 }
4778             }
4779 
4780             if (!group)
4781                 return "WSGI process group not yet configured.";
4782 
4783             if (cmd->server->server_hostname &&
4784                 group->server->server_hostname &&
4785                 strcmp(cmd->server->server_hostname,
4786                 group->server->server_hostname) &&
4787                 group->server->is_virtual) {
4788 
4789                 return "WSGI process group not accessible.";
4790             }
4791 
4792             if (!cmd->server->server_hostname &&
4793                 group->server->server_hostname &&
4794                 group->server->is_virtual) {
4795 
4796                 return "WSGI process group not matchable.";
4797             }
4798 
4799             if (cmd->server->server_hostname &&
4800                 !group->server->server_hostname &&
4801                 group->server->is_virtual) {
4802 
4803                 return "WSGI process group not matchable.";
4804             }
4805         }
4806 #endif
4807     }
4808 
4809     return NULL;
4810 }
4811 
wsgi_set_verbose_debugging(cmd_parms * cmd,void * mconfig,const char * f)4812 static const char *wsgi_set_verbose_debugging(cmd_parms *cmd, void *mconfig,
4813                                               const char *f)
4814 {
4815     const char *error = NULL;
4816     WSGIServerConfig *sconfig = NULL;
4817 
4818     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4819     if (error != NULL)
4820         return error;
4821 
4822     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4823 
4824     if (strcasecmp(f, "Off") == 0)
4825         sconfig->verbose_debugging = 0;
4826     else if (strcasecmp(f, "On") == 0)
4827         sconfig->verbose_debugging = 1;
4828     else
4829         return "WSGIVerboseDebugging must be one of: Off | On";
4830 
4831     return NULL;
4832 }
4833 
wsgi_set_lazy_initialization(cmd_parms * cmd,void * mconfig,const char * f)4834 static const char *wsgi_set_lazy_initialization(cmd_parms *cmd, void *mconfig,
4835                                                 const char *f)
4836 {
4837     const char *error = NULL;
4838 
4839     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4840     if (error != NULL)
4841         return error;
4842 
4843     if (strcasecmp(f, "Off") == 0)
4844         wsgi_python_after_fork = 0;
4845     else if (strcasecmp(f, "On") == 0)
4846         wsgi_python_after_fork = 1;
4847     else
4848         return "WSGILazyInitialization must be one of: Off | On";
4849 
4850     return NULL;
4851 }
4852 
wsgi_add_python_warnings(cmd_parms * cmd,void * mconfig,const char * f)4853 static const char *wsgi_add_python_warnings(cmd_parms *cmd, void *mconfig,
4854                                             const char *f)
4855 {
4856     const char *error = NULL;
4857     WSGIServerConfig *sconfig = NULL;
4858 
4859     char **entry = NULL;
4860 
4861     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4862     if (error != NULL)
4863         return error;
4864 
4865     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4866 
4867     if (!sconfig->python_warnings) {
4868         sconfig->python_warnings = apr_array_make(sconfig->pool, 5,
4869                                                   sizeof(char*));
4870     }
4871 
4872     entry = (char **)apr_array_push(sconfig->python_warnings);
4873     *entry = apr_pstrdup(sconfig->pool, f);
4874 
4875     return NULL;
4876 }
4877 
4878 #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6
wsgi_set_py3k_warning_flag(cmd_parms * cmd,void * mconfig,const char * f)4879 static const char *wsgi_set_py3k_warning_flag(cmd_parms *cmd, void *mconfig,
4880                                               const char *f)
4881 {
4882     const char *error = NULL;
4883     WSGIServerConfig *sconfig = NULL;
4884 
4885     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4886     if (error != NULL)
4887         return error;
4888 
4889     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4890 
4891     if (strcasecmp(f, "Off") == 0)
4892         sconfig->py3k_warning_flag = 0;
4893     else if (strcasecmp(f, "On") == 0)
4894         sconfig->py3k_warning_flag = 1;
4895     else
4896         return "WSGIPy3kWarningFlag must be one of: Off | On";
4897 
4898     return NULL;
4899 }
4900 #endif
4901 
4902 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 3) || \
4903     (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6)
wsgi_set_dont_write_bytecode(cmd_parms * cmd,void * mconfig,const char * f)4904 static const char *wsgi_set_dont_write_bytecode(cmd_parms *cmd, void *mconfig,
4905                                                 const char *f)
4906 {
4907     const char *error = NULL;
4908     WSGIServerConfig *sconfig = NULL;
4909 
4910     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4911     if (error != NULL)
4912         return error;
4913 
4914     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4915 
4916     if (strcasecmp(f, "Off") == 0)
4917         sconfig->dont_write_bytecode = 0;
4918     else if (strcasecmp(f, "On") == 0)
4919         sconfig->dont_write_bytecode = 1;
4920     else
4921         return "WSGIDontWriteBytecode must be one of: Off | On";
4922 
4923     return NULL;
4924 }
4925 #endif
4926 
wsgi_set_python_optimize(cmd_parms * cmd,void * mconfig,const char * f)4927 static const char *wsgi_set_python_optimize(cmd_parms *cmd, void *mconfig,
4928                                             const char *f)
4929 {
4930     const char *error = NULL;
4931     WSGIServerConfig *sconfig = NULL;
4932 
4933     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4934     if (error != NULL)
4935         return error;
4936 
4937     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4938     sconfig->python_optimize = atoi(f);
4939 
4940     return NULL;
4941 }
4942 
wsgi_set_python_home(cmd_parms * cmd,void * mconfig,const char * f)4943 static const char *wsgi_set_python_home(cmd_parms *cmd, void *mconfig,
4944                                         const char *f)
4945 {
4946     const char *error = NULL;
4947     WSGIServerConfig *sconfig = NULL;
4948 
4949     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4950     if (error != NULL)
4951         return error;
4952 
4953     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4954     sconfig->python_home = f;
4955 
4956     return NULL;
4957 }
4958 
wsgi_set_python_path(cmd_parms * cmd,void * mconfig,const char * f)4959 static const char *wsgi_set_python_path(cmd_parms *cmd, void *mconfig,
4960                                         const char *f)
4961 {
4962     const char *error = NULL;
4963     WSGIServerConfig *sconfig = NULL;
4964 
4965     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4966     if (error != NULL)
4967         return error;
4968 
4969     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4970     sconfig->python_path = f;
4971 
4972     return NULL;
4973 }
4974 
wsgi_set_python_eggs(cmd_parms * cmd,void * mconfig,const char * f)4975 static const char *wsgi_set_python_eggs(cmd_parms *cmd, void *mconfig,
4976                                         const char *f)
4977 {
4978     const char *error = NULL;
4979     WSGIServerConfig *sconfig = NULL;
4980 
4981     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4982     if (error != NULL)
4983         return error;
4984 
4985     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4986     sconfig->python_eggs = f;
4987 
4988     return NULL;
4989 }
4990 
wsgi_set_python_hash_seed(cmd_parms * cmd,void * mconfig,const char * f)4991 static const char *wsgi_set_python_hash_seed(cmd_parms *cmd, void *mconfig,
4992                                              const char *f)
4993 {
4994     const char *error = NULL;
4995     WSGIServerConfig *sconfig = NULL;
4996 
4997     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4998     if (error != NULL)
4999         return error;
5000 
5001     /*
5002      * Must check this here because if we don't and is wrong, then
5003      * Python interpreter will check later and may kill the process.
5004      */
5005 
5006     if (f && *f != '\0' && strcmp(f, "random") != 0) {
5007         const char *endptr = f;
5008         unsigned long seed;
5009 
5010         seed = PyOS_strtoul((char *)f, (char **)&endptr, 10);
5011 
5012         if (*endptr != '\0' || seed > 4294967295UL
5013                 || (errno == ERANGE && seed == ULONG_MAX))
5014         {
5015             return "WSGIPythonHashSeed must be \"random\" or an integer "
5016                               "in range [0; 4294967295]";
5017         }
5018     }
5019 
5020     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5021     sconfig->python_hash_seed = f;
5022 
5023     return NULL;
5024 }
5025 
wsgi_set_restrict_embedded(cmd_parms * cmd,void * mconfig,const char * f)5026 static const char *wsgi_set_restrict_embedded(cmd_parms *cmd, void *mconfig,
5027                                               const char *f)
5028 {
5029     const char *error = NULL;
5030     WSGIServerConfig *sconfig = NULL;
5031 
5032     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5033     if (error != NULL)
5034         return error;
5035 
5036     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5037 
5038     if (strcasecmp(f, "Off") == 0)
5039         sconfig->restrict_embedded = 0;
5040     else if (strcasecmp(f, "On") == 0)
5041         sconfig->restrict_embedded = 1;
5042     else
5043         return "WSGIRestrictEmbedded must be one of: Off | On";
5044 
5045     if (sconfig->restrict_embedded) {
5046         if (wsgi_python_required == -1)
5047             wsgi_python_required = 0;
5048     }
5049 
5050     return NULL;
5051 }
5052 
wsgi_set_restrict_stdin(cmd_parms * cmd,void * mconfig,const char * f)5053 static const char *wsgi_set_restrict_stdin(cmd_parms *cmd, void *mconfig,
5054                                            const char *f)
5055 {
5056     const char *error = NULL;
5057     WSGIServerConfig *sconfig = NULL;
5058 
5059     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5060     if (error != NULL)
5061         return error;
5062 
5063     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5064 
5065     if (strcasecmp(f, "Off") == 0)
5066         sconfig->restrict_stdin = 0;
5067     else if (strcasecmp(f, "On") == 0)
5068         sconfig->restrict_stdin = 1;
5069     else
5070         return "WSGIRestrictStdin must be one of: Off | On";
5071 
5072     return NULL;
5073 }
5074 
wsgi_set_restrict_stdout(cmd_parms * cmd,void * mconfig,const char * f)5075 static const char *wsgi_set_restrict_stdout(cmd_parms *cmd, void *mconfig,
5076                                             const char *f)
5077 {
5078     const char *error = NULL;
5079     WSGIServerConfig *sconfig = NULL;
5080 
5081     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5082     if (error != NULL)
5083         return error;
5084 
5085     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5086 
5087     if (strcasecmp(f, "Off") == 0)
5088         sconfig->restrict_stdout = 0;
5089     else if (strcasecmp(f, "On") == 0)
5090         sconfig->restrict_stdout = 1;
5091     else
5092         return "WSGIRestrictStdout must be one of: Off | On";
5093 
5094     return NULL;
5095 }
5096 
wsgi_set_restrict_signal(cmd_parms * cmd,void * mconfig,const char * f)5097 static const char *wsgi_set_restrict_signal(cmd_parms *cmd, void *mconfig,
5098                                             const char *f)
5099 {
5100     const char *error = NULL;
5101     WSGIServerConfig *sconfig = NULL;
5102 
5103     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5104     if (error != NULL)
5105         return error;
5106 
5107     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5108 
5109     if (strcasecmp(f, "Off") == 0)
5110         sconfig->restrict_signal = 0;
5111     else if (strcasecmp(f, "On") == 0)
5112         sconfig->restrict_signal = 1;
5113     else
5114         return "WSGIRestrictSignal must be one of: Off | On";
5115 
5116     return NULL;
5117 }
5118 
wsgi_set_case_sensitivity(cmd_parms * cmd,void * mconfig,const char * f)5119 static const char *wsgi_set_case_sensitivity(cmd_parms *cmd, void *mconfig,
5120                                            const char *f)
5121 {
5122     const char *error = NULL;
5123     WSGIServerConfig *sconfig = NULL;
5124 
5125     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5126     if (error != NULL)
5127         return error;
5128 
5129     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5130 
5131     if (strcasecmp(f, "Off") == 0)
5132         sconfig->case_sensitivity = 0;
5133     else if (strcasecmp(f, "On") == 0)
5134         sconfig->case_sensitivity = 1;
5135     else
5136         return "WSGICaseSensitivity must be one of: Off | On";
5137 
5138     return NULL;
5139 }
5140 
wsgi_set_restrict_process(cmd_parms * cmd,void * mconfig,const char * args)5141 static const char *wsgi_set_restrict_process(cmd_parms *cmd, void *mconfig,
5142                                              const char *args)
5143 {
5144     apr_table_t *index = apr_table_make(cmd->pool, 5);
5145 
5146     if (cmd->path) {
5147         WSGIDirectoryConfig *dconfig = NULL;
5148         dconfig = (WSGIDirectoryConfig *)mconfig;
5149 
5150         dconfig->restrict_process = index;
5151     }
5152     else {
5153         WSGIServerConfig *sconfig = NULL;
5154         sconfig = ap_get_module_config(cmd->server->module_config,
5155                                        &wsgi_module);
5156 
5157         sconfig->restrict_process = index;
5158     }
5159 
5160     while (*args) {
5161         const char *option;
5162 
5163         option = ap_getword_conf(cmd->pool, &args);
5164 
5165         if (!strcmp(option, "%{GLOBAL}"))
5166             option = "";
5167 
5168         apr_table_setn(index, option, option);
5169     }
5170 
5171     return NULL;
5172 }
5173 
wsgi_set_process_group(cmd_parms * cmd,void * mconfig,const char * n)5174 static const char *wsgi_set_process_group(cmd_parms *cmd, void *mconfig,
5175                                           const char *n)
5176 {
5177     if (cmd->path) {
5178         WSGIDirectoryConfig *dconfig = NULL;
5179         dconfig = (WSGIDirectoryConfig *)mconfig;
5180         dconfig->process_group = n;
5181     }
5182     else {
5183         WSGIServerConfig *sconfig = NULL;
5184         sconfig = ap_get_module_config(cmd->server->module_config,
5185                                        &wsgi_module);
5186         sconfig->process_group = n;
5187     }
5188 
5189     return NULL;
5190 }
5191 
wsgi_set_application_group(cmd_parms * cmd,void * mconfig,const char * n)5192 static const char *wsgi_set_application_group(cmd_parms *cmd, void *mconfig,
5193                                               const char *n)
5194 {
5195     if (cmd->path) {
5196         WSGIDirectoryConfig *dconfig = NULL;
5197         dconfig = (WSGIDirectoryConfig *)mconfig;
5198         dconfig->application_group = n;
5199     }
5200     else {
5201         WSGIServerConfig *sconfig = NULL;
5202         sconfig = ap_get_module_config(cmd->server->module_config,
5203                                        &wsgi_module);
5204         sconfig->application_group = n;
5205     }
5206 
5207     return NULL;
5208 }
5209 
wsgi_set_callable_object(cmd_parms * cmd,void * mconfig,const char * n)5210 static const char *wsgi_set_callable_object(cmd_parms *cmd, void *mconfig,
5211                                             const char *n)
5212 {
5213     if (cmd->path) {
5214         WSGIDirectoryConfig *dconfig = NULL;
5215         dconfig = (WSGIDirectoryConfig *)mconfig;
5216         dconfig->callable_object = n;
5217     }
5218     else {
5219         WSGIServerConfig *sconfig = NULL;
5220         sconfig = ap_get_module_config(cmd->server->module_config,
5221                                        &wsgi_module);
5222         sconfig->callable_object = n;
5223     }
5224 
5225     return NULL;
5226 }
5227 
wsgi_add_import_script(cmd_parms * cmd,void * mconfig,const char * args)5228 static const char *wsgi_add_import_script(cmd_parms *cmd, void *mconfig,
5229                                           const char *args)
5230 {
5231     WSGIScriptFile *object = NULL;
5232 
5233     const char *option = NULL;
5234     const char *value = NULL;
5235 
5236     if (!wsgi_import_list) {
5237         wsgi_import_list = apr_array_make(cmd->pool, 20,
5238                                           sizeof(WSGIScriptFile));
5239     }
5240 
5241     object = (WSGIScriptFile *)apr_array_push(wsgi_import_list);
5242 
5243     object->handler_script = ap_getword_conf(cmd->pool, &args);
5244     object->process_group = NULL;
5245     object->application_group = NULL;
5246 
5247     if (!object->handler_script || !*object->handler_script)
5248         return "Location of import script not supplied.";
5249 
5250     while (*args) {
5251         if (wsgi_parse_option(cmd->pool, &args, &option,
5252                               &value) != APR_SUCCESS) {
5253             return "Invalid option to WSGI import script definition.";
5254         }
5255 
5256         if (!strcmp(option, "application-group")) {
5257             if (!*value)
5258                 return "Invalid name for WSGI application group.";
5259 
5260             object->application_group = value;
5261         }
5262 #if defined(MOD_WSGI_WITH_DAEMONS)
5263         else if (!strcmp(option, "process-group")) {
5264             if (!*value)
5265                 return "Invalid name for WSGI process group.";
5266 
5267             object->process_group = value;
5268         }
5269 #endif
5270         else
5271             return "Invalid option to WSGI import script definition.";
5272     }
5273 
5274     if (!object->application_group)
5275         return "Name of WSGI application group required.";
5276 
5277     if (!strcmp(object->application_group, "%{GLOBAL}"))
5278         object->application_group = "";
5279 
5280 #if defined(MOD_WSGI_WITH_DAEMONS)
5281     if (!object->process_group)
5282         return "Name of WSGI process group required.";
5283 
5284     if (!strcmp(object->process_group, "%{GLOBAL}"))
5285         object->process_group = "";
5286 
5287     if (*object->process_group) {
5288         WSGIProcessGroup *group = NULL;
5289         WSGIProcessGroup *entries = NULL;
5290         WSGIProcessGroup *entry = NULL;
5291         int i;
5292 
5293         if (!wsgi_daemon_list)
5294             return "WSGI process group not yet configured.";
5295 
5296         entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
5297 
5298         for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
5299             entry = &entries[i];
5300 
5301             if (!strcmp(entry->name, object->process_group)) {
5302                 group = entry;
5303                 break;
5304             }
5305         }
5306 
5307         if (!group)
5308             return "WSGI process group not yet configured.";
5309 
5310         if (cmd->server->server_hostname &&
5311             group->server->server_hostname &&
5312             strcmp(cmd->server->server_hostname,
5313             group->server->server_hostname) &&
5314             group->server->is_virtual) {
5315 
5316             return "WSGI process group not accessible.";
5317         }
5318 
5319         if (!cmd->server->server_hostname &&
5320             group->server->server_hostname &&
5321             group->server->is_virtual) {
5322 
5323             return "WSGI process group not matchable.";
5324         }
5325 
5326         if (cmd->server->server_hostname &&
5327             !group->server->server_hostname &&
5328             group->server->is_virtual) {
5329 
5330             return "WSGI process group not matchable.";
5331         }
5332     }
5333 #else
5334     object->process_group = "";
5335 #endif
5336 
5337     if (!*object->process_group)
5338         wsgi_python_required = 1;
5339 
5340     return NULL;
5341 }
5342 
wsgi_set_dispatch_script(cmd_parms * cmd,void * mconfig,const char * args)5343 static const char *wsgi_set_dispatch_script(cmd_parms *cmd, void *mconfig,
5344                                             const char *args)
5345 {
5346     WSGIScriptFile *object = NULL;
5347 
5348     const char *option = NULL;
5349     const char *value = NULL;
5350 
5351     object = newWSGIScriptFile(cmd->pool);
5352 
5353     object->handler_script = ap_getword_conf(cmd->pool, &args);
5354 
5355     if (!object->handler_script || !*object->handler_script)
5356         return "Location of dispatch script not supplied.";
5357 
5358     while (*args) {
5359         if (wsgi_parse_option(cmd->pool, &args, &option,
5360                               &value) != APR_SUCCESS) {
5361             return "Invalid option to WSGI dispatch script definition.";
5362         }
5363 
5364         if (!strcmp(option, "application-group")) {
5365             if (!*value)
5366                 return "Invalid name for WSGI application group.";
5367 
5368             object->application_group = value;
5369         }
5370         else
5371             return "Invalid option to WSGI dispatch script definition.";
5372     }
5373 
5374     if (cmd->path) {
5375         WSGIDirectoryConfig *dconfig = NULL;
5376         dconfig = (WSGIDirectoryConfig *)mconfig;
5377         dconfig->dispatch_script = object;
5378     }
5379     else {
5380         WSGIServerConfig *sconfig = NULL;
5381         sconfig = ap_get_module_config(cmd->server->module_config,
5382                                        &wsgi_module);
5383         sconfig->dispatch_script = object;
5384     }
5385 
5386     wsgi_python_required = 1;
5387 
5388     return NULL;
5389 }
5390 
wsgi_set_pass_apache_request(cmd_parms * cmd,void * mconfig,const char * f)5391 static const char *wsgi_set_pass_apache_request(cmd_parms *cmd, void *mconfig,
5392                                                 const char *f)
5393 {
5394     if (cmd->path) {
5395         WSGIDirectoryConfig *dconfig = NULL;
5396         dconfig = (WSGIDirectoryConfig *)mconfig;
5397 
5398         if (strcasecmp(f, "Off") == 0)
5399             dconfig->pass_apache_request = 0;
5400         else if (strcasecmp(f, "On") == 0)
5401             dconfig->pass_apache_request = 1;
5402         else
5403             return "WSGIPassApacheRequest must be one of: Off | On";
5404     }
5405     else {
5406         WSGIServerConfig *sconfig = NULL;
5407         sconfig = ap_get_module_config(cmd->server->module_config,
5408                                        &wsgi_module);
5409 
5410         if (strcasecmp(f, "Off") == 0)
5411             sconfig->pass_apache_request = 0;
5412         else if (strcasecmp(f, "On") == 0)
5413             sconfig->pass_apache_request = 1;
5414         else
5415             return "WSGIPassApacheRequest must be one of: Off | On";
5416     }
5417 
5418     return NULL;
5419 }
5420 
wsgi_set_pass_authorization(cmd_parms * cmd,void * mconfig,const char * f)5421 static const char *wsgi_set_pass_authorization(cmd_parms *cmd, void *mconfig,
5422                                                const char *f)
5423 {
5424     if (cmd->path) {
5425         WSGIDirectoryConfig *dconfig = NULL;
5426         dconfig = (WSGIDirectoryConfig *)mconfig;
5427 
5428         if (strcasecmp(f, "Off") == 0)
5429             dconfig->pass_authorization = 0;
5430         else if (strcasecmp(f, "On") == 0)
5431             dconfig->pass_authorization = 1;
5432         else
5433             return "WSGIPassAuthorization must be one of: Off | On";
5434     }
5435     else {
5436         WSGIServerConfig *sconfig = NULL;
5437         sconfig = ap_get_module_config(cmd->server->module_config,
5438                                        &wsgi_module);
5439 
5440         if (strcasecmp(f, "Off") == 0)
5441             sconfig->pass_authorization = 0;
5442         else if (strcasecmp(f, "On") == 0)
5443             sconfig->pass_authorization = 1;
5444         else
5445             return "WSGIPassAuthorization must be one of: Off | On";
5446     }
5447 
5448     return NULL;
5449 }
5450 
wsgi_set_script_reloading(cmd_parms * cmd,void * mconfig,const char * f)5451 static const char *wsgi_set_script_reloading(cmd_parms *cmd, void *mconfig,
5452                                              const char *f)
5453 {
5454     if (cmd->path) {
5455         WSGIDirectoryConfig *dconfig = NULL;
5456         dconfig = (WSGIDirectoryConfig *)mconfig;
5457 
5458         if (strcasecmp(f, "Off") == 0)
5459             dconfig->script_reloading = 0;
5460         else if (strcasecmp(f, "On") == 0)
5461             dconfig->script_reloading = 1;
5462         else
5463             return "WSGIScriptReloading must be one of: Off | On";
5464     }
5465     else {
5466         WSGIServerConfig *sconfig = NULL;
5467         sconfig = ap_get_module_config(cmd->server->module_config,
5468                                        &wsgi_module);
5469 
5470         if (strcasecmp(f, "Off") == 0)
5471             sconfig->script_reloading = 0;
5472         else if (strcasecmp(f, "On") == 0)
5473             sconfig->script_reloading = 1;
5474         else
5475             return "WSGIScriptReloading must be one of: Off | On";
5476     }
5477 
5478     return NULL;
5479 }
5480 
wsgi_set_error_override(cmd_parms * cmd,void * mconfig,const char * f)5481 static const char *wsgi_set_error_override(cmd_parms *cmd, void *mconfig,
5482                                            const char *f)
5483 {
5484     if (cmd->path) {
5485         WSGIDirectoryConfig *dconfig = NULL;
5486         dconfig = (WSGIDirectoryConfig *)mconfig;
5487 
5488         if (strcasecmp(f, "Off") == 0)
5489             dconfig->error_override = 0;
5490         else if (strcasecmp(f, "On") == 0)
5491             dconfig->error_override = 1;
5492         else
5493             return "WSGIErrorOverride must be one of: Off | On";
5494     }
5495     else {
5496         WSGIServerConfig *sconfig = NULL;
5497         sconfig = ap_get_module_config(cmd->server->module_config,
5498                                        &wsgi_module);
5499 
5500         if (strcasecmp(f, "Off") == 0)
5501             sconfig->error_override = 0;
5502         else if (strcasecmp(f, "On") == 0)
5503             sconfig->error_override = 1;
5504         else
5505             return "WSGIErrorOverride must be one of: Off | On";
5506     }
5507 
5508     return NULL;
5509 }
5510 
wsgi_set_chunked_request(cmd_parms * cmd,void * mconfig,const char * f)5511 static const char *wsgi_set_chunked_request(cmd_parms *cmd, void *mconfig,
5512                                             const char *f)
5513 {
5514     if (cmd->path) {
5515         WSGIDirectoryConfig *dconfig = NULL;
5516         dconfig = (WSGIDirectoryConfig *)mconfig;
5517 
5518         if (strcasecmp(f, "Off") == 0)
5519             dconfig->chunked_request = 0;
5520         else if (strcasecmp(f, "On") == 0)
5521             dconfig->chunked_request = 1;
5522         else
5523             return "WSGIChunkedRequest must be one of: Off | On";
5524     }
5525     else {
5526         WSGIServerConfig *sconfig = NULL;
5527         sconfig = ap_get_module_config(cmd->server->module_config,
5528                                        &wsgi_module);
5529 
5530         if (strcasecmp(f, "Off") == 0)
5531             sconfig->chunked_request = 0;
5532         else if (strcasecmp(f, "On") == 0)
5533             sconfig->chunked_request = 1;
5534         else
5535             return "WSGIChunkedRequest must be one of: Off | On";
5536     }
5537 
5538     return NULL;
5539 }
5540 
wsgi_set_map_head_to_get(cmd_parms * cmd,void * mconfig,const char * f)5541 static const char *wsgi_set_map_head_to_get(cmd_parms *cmd, void *mconfig,
5542                                             const char *f)
5543 {
5544     if (cmd->path) {
5545         WSGIDirectoryConfig *dconfig = NULL;
5546         dconfig = (WSGIDirectoryConfig *)mconfig;
5547 
5548         if (strcasecmp(f, "Off") == 0)
5549             dconfig->map_head_to_get = 0;
5550         else if (strcasecmp(f, "On") == 0)
5551             dconfig->map_head_to_get = 1;
5552         else if (strcasecmp(f, "Auto") == 0)
5553             dconfig->map_head_to_get = 2;
5554         else
5555             return "WSGIMapHEADToGET must be one of: Off | On | Auto";
5556     }
5557     else {
5558         WSGIServerConfig *sconfig = NULL;
5559         sconfig = ap_get_module_config(cmd->server->module_config,
5560                                        &wsgi_module);
5561 
5562         if (strcasecmp(f, "Off") == 0)
5563             sconfig->map_head_to_get = 0;
5564         else if (strcasecmp(f, "On") == 0)
5565             sconfig->map_head_to_get = 1;
5566         else if (strcasecmp(f, "Auto") == 0)
5567             sconfig->map_head_to_get = 2;
5568         else
5569             return "WSGIMapHEADToGET must be one of: Off | On | Auto";
5570     }
5571 
5572     return NULL;
5573 }
5574 
wsgi_set_ignore_activity(cmd_parms * cmd,void * mconfig,const char * f)5575 static const char *wsgi_set_ignore_activity(cmd_parms *cmd, void *mconfig,
5576                                             const char *f)
5577 {
5578     if (cmd->path) {
5579         WSGIDirectoryConfig *dconfig = NULL;
5580         dconfig = (WSGIDirectoryConfig *)mconfig;
5581 
5582         if (strcasecmp(f, "Off") == 0)
5583             dconfig->ignore_activity = 0;
5584         else if (strcasecmp(f, "On") == 0)
5585             dconfig->ignore_activity = 1;
5586         else
5587             return "WSGIIgnoreActivity must be one of: Off | On";
5588     }
5589     else {
5590         WSGIServerConfig *sconfig = NULL;
5591         sconfig = ap_get_module_config(cmd->server->module_config,
5592                                        &wsgi_module);
5593 
5594         if (strcasecmp(f, "Off") == 0)
5595             sconfig->ignore_activity = 0;
5596         else if (strcasecmp(f, "On") == 0)
5597             sconfig->ignore_activity = 1;
5598         else
5599             return "WSGIIgnoreActivity must be one of: Off | On";
5600     }
5601 
5602     return NULL;
5603 }
5604 
5605 static char *wsgi_http2env(apr_pool_t *a, const char *w);
5606 
wsgi_set_trusted_proxy_headers(cmd_parms * cmd,void * mconfig,const char * args)5607 static const char *wsgi_set_trusted_proxy_headers(cmd_parms *cmd,
5608                                                   void *mconfig,
5609                                                   const char *args)
5610 {
5611     apr_array_header_t *headers = NULL;
5612 
5613     if (cmd->path) {
5614         WSGIDirectoryConfig *dconfig = NULL;
5615         dconfig = (WSGIDirectoryConfig *)mconfig;
5616 
5617         if (!dconfig->trusted_proxy_headers) {
5618             headers = apr_array_make(cmd->pool, 3, sizeof(char*));
5619             dconfig->trusted_proxy_headers = headers;
5620         }
5621         else
5622             headers = dconfig->trusted_proxy_headers;
5623     }
5624     else {
5625         WSGIServerConfig *sconfig = NULL;
5626         sconfig = ap_get_module_config(cmd->server->module_config,
5627                                        &wsgi_module);
5628 
5629         if (!sconfig->trusted_proxy_headers) {
5630             headers = apr_array_make(cmd->pool, 3, sizeof(char*));
5631             sconfig->trusted_proxy_headers = headers;
5632         }
5633         else
5634             headers = sconfig->trusted_proxy_headers;
5635     }
5636 
5637     while (*args) {
5638         const char **entry = NULL;
5639 
5640         entry = (const char **)apr_array_push(headers);
5641         *entry = wsgi_http2env(cmd->pool, ap_getword_conf(cmd->pool, &args));
5642     }
5643 
5644     return NULL;
5645 }
5646 
wsgi_looks_like_ip(const char * ip)5647 static int wsgi_looks_like_ip(const char *ip) {
5648     static const char ipv4_set[] = "0123456789./";
5649     static const char ipv6_set[] = "0123456789abcdef:/";
5650 
5651     const char *ptr;
5652 
5653     /* Zero length value is not valid. */
5654 
5655     if (!*ip)
5656       return 0;
5657 
5658     /* Determine if this could be a IPv6 or IPv4 address. */
5659 
5660     ptr = ip;
5661 
5662     if (strchr(ip, ':')) {
5663         while(*ptr && strchr(ipv6_set, *ptr) != NULL)
5664             ++ptr;
5665     }
5666     else {
5667         while(*ptr && strchr(ipv4_set, *ptr) != NULL)
5668             ++ptr;
5669     }
5670 
5671     return (*ptr == '\0');
5672 }
5673 
wsgi_set_trusted_proxies(cmd_parms * cmd,void * mconfig,const char * args)5674 static const char *wsgi_set_trusted_proxies(cmd_parms *cmd,
5675                                               void *mconfig, const char *args)
5676 {
5677     apr_array_header_t *proxy_ips = NULL;
5678 
5679     if (cmd->path) {
5680         WSGIDirectoryConfig *dconfig = NULL;
5681         dconfig = (WSGIDirectoryConfig *)mconfig;
5682 
5683         if (!dconfig->trusted_proxies) {
5684             proxy_ips = apr_array_make(cmd->pool, 3, sizeof(char*));
5685             dconfig->trusted_proxies = proxy_ips;
5686         }
5687         else
5688             proxy_ips = dconfig->trusted_proxies;
5689     }
5690     else {
5691         WSGIServerConfig *sconfig = NULL;
5692         sconfig = ap_get_module_config(cmd->server->module_config,
5693                                        &wsgi_module);
5694 
5695         if (!sconfig->trusted_proxies) {
5696             proxy_ips = apr_array_make(cmd->pool, 3, sizeof(char*));
5697             sconfig->trusted_proxies = proxy_ips;
5698         }
5699         else
5700             proxy_ips = sconfig->trusted_proxies;
5701     }
5702 
5703     while (*args) {
5704         const char *proxy_ip;
5705 
5706         proxy_ip = ap_getword_conf(cmd->pool, &args);
5707 
5708         if (wsgi_looks_like_ip(proxy_ip)) {
5709             char *ip;
5710             char *mask;
5711             apr_ipsubnet_t **sub;
5712             apr_status_t rv;
5713 
5714             ip = apr_pstrdup(cmd->temp_pool, proxy_ip);
5715 
5716             if ((mask = ap_strchr(ip, '/')))
5717                 *mask++ = '\0';
5718 
5719             sub = (apr_ipsubnet_t **)apr_array_push(proxy_ips);
5720 
5721             rv = apr_ipsubnet_create(sub, ip, mask, cmd->pool);
5722 
5723             if (rv != APR_SUCCESS) {
5724                 char msgbuf[128];
5725                 apr_strerror(rv, msgbuf, sizeof(msgbuf));
5726 
5727                 return apr_pstrcat(cmd->pool, "Unable to parse trusted "
5728                                    "proxy IP address/subnet of \"", proxy_ip,
5729                                    "\". ", msgbuf, NULL);
5730             }
5731         }
5732         else {
5733             return apr_pstrcat(cmd->pool, "Unable to parse trusted proxy "
5734                                "IP address/subnet of \"", proxy_ip, "\".",
5735                                NULL);
5736         }
5737     }
5738 
5739     return NULL;
5740 }
5741 
wsgi_set_enable_sendfile(cmd_parms * cmd,void * mconfig,const char * f)5742 static const char *wsgi_set_enable_sendfile(cmd_parms *cmd, void *mconfig,
5743                                             const char *f)
5744 {
5745     if (cmd->path) {
5746         WSGIDirectoryConfig *dconfig = NULL;
5747         dconfig = (WSGIDirectoryConfig *)mconfig;
5748 
5749         if (strcasecmp(f, "Off") == 0)
5750             dconfig->enable_sendfile = 0;
5751         else if (strcasecmp(f, "On") == 0)
5752             dconfig->enable_sendfile = 1;
5753         else
5754             return "WSGIEnableSendfile must be one of: Off | On";
5755     }
5756     else {
5757         WSGIServerConfig *sconfig = NULL;
5758         sconfig = ap_get_module_config(cmd->server->module_config,
5759                                        &wsgi_module);
5760 
5761         if (strcasecmp(f, "Off") == 0)
5762             sconfig->enable_sendfile = 0;
5763         else if (strcasecmp(f, "On") == 0)
5764             sconfig->enable_sendfile = 1;
5765         else
5766             return "WSGIEnableSendfile must be one of: Off | On";
5767     }
5768 
5769     return NULL;
5770 }
5771 
wsgi_set_access_script(cmd_parms * cmd,void * mconfig,const char * args)5772 static const char *wsgi_set_access_script(cmd_parms *cmd, void *mconfig,
5773                                           const char *args)
5774 {
5775     WSGIDirectoryConfig *dconfig = NULL;
5776     WSGIScriptFile *object = NULL;
5777 
5778     const char *option = NULL;
5779     const char *value = NULL;
5780 
5781     object = newWSGIScriptFile(cmd->pool);
5782 
5783     object->handler_script = ap_getword_conf(cmd->pool, &args);
5784 
5785     if (!object->handler_script || !*object->handler_script)
5786         return "Location of access script not supplied.";
5787 
5788     while (*args) {
5789         if (wsgi_parse_option(cmd->pool, &args, &option,
5790                               &value) != APR_SUCCESS) {
5791             return "Invalid option to WSGI access script definition.";
5792         }
5793 
5794         if (!strcmp(option, "application-group")) {
5795             if (!*value)
5796                 return "Invalid name for WSGI application group.";
5797 
5798             object->application_group = value;
5799         }
5800         else
5801             return "Invalid option to WSGI access script definition.";
5802     }
5803 
5804     dconfig = (WSGIDirectoryConfig *)mconfig;
5805     dconfig->access_script = object;
5806 
5807     wsgi_python_required = 1;
5808 
5809     return NULL;
5810 }
5811 
wsgi_set_auth_user_script(cmd_parms * cmd,void * mconfig,const char * args)5812 static const char *wsgi_set_auth_user_script(cmd_parms *cmd, void *mconfig,
5813                                              const char *args)
5814 {
5815     WSGIDirectoryConfig *dconfig = NULL;
5816     WSGIScriptFile *object = NULL;
5817 
5818     const char *option = NULL;
5819     const char *value = NULL;
5820 
5821     object = newWSGIScriptFile(cmd->pool);
5822 
5823     object->handler_script = ap_getword_conf(cmd->pool, &args);
5824 
5825     if (!object->handler_script || !*object->handler_script)
5826         return "Location of auth user script not supplied.";
5827 
5828     while (*args) {
5829         if (wsgi_parse_option(cmd->pool, &args, &option,
5830                               &value) != APR_SUCCESS) {
5831             return "Invalid option to WSGI auth user script definition.";
5832         }
5833 
5834         if (!strcmp(option, "application-group")) {
5835             if (!*value)
5836                 return "Invalid name for WSGI application group.";
5837 
5838             object->application_group = value;
5839         }
5840         else
5841             return "Invalid option to WSGI auth user script definition.";
5842     }
5843 
5844     dconfig = (WSGIDirectoryConfig *)mconfig;
5845     dconfig->auth_user_script = object;
5846 
5847     wsgi_python_required = 1;
5848 
5849     return NULL;
5850 }
5851 
wsgi_set_auth_group_script(cmd_parms * cmd,void * mconfig,const char * args)5852 static const char *wsgi_set_auth_group_script(cmd_parms *cmd, void *mconfig,
5853                                                const char *args)
5854 {
5855     WSGIDirectoryConfig *dconfig = NULL;
5856     WSGIScriptFile *object = NULL;
5857 
5858     const char *option = NULL;
5859     const char *value = NULL;
5860 
5861     object = newWSGIScriptFile(cmd->pool);
5862 
5863     object->handler_script = ap_getword_conf(cmd->pool, &args);
5864 
5865     if (!object->handler_script || !*object->handler_script)
5866         return "Location of auth group script not supplied.";
5867 
5868     while (*args) {
5869         if (wsgi_parse_option(cmd->pool, &args, &option,
5870                               &value) != APR_SUCCESS) {
5871             return "Invalid option to WSGI auth group script definition.";
5872         }
5873 
5874         if (!strcmp(option, "application-group")) {
5875             if (!*value)
5876                 return "Invalid name for WSGI application group.";
5877 
5878             object->application_group = value;
5879         }
5880         else
5881             return "Invalid option to WSGI auth group script definition.";
5882     }
5883 
5884     dconfig = (WSGIDirectoryConfig *)mconfig;
5885     dconfig->auth_group_script = object;
5886 
5887     wsgi_python_required = 1;
5888 
5889     return NULL;
5890 }
5891 
5892 #if !defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
wsgi_set_user_authoritative(cmd_parms * cmd,void * mconfig,const char * f)5893 static const char *wsgi_set_user_authoritative(cmd_parms *cmd, void *mconfig,
5894                                                const char *f)
5895 {
5896     WSGIDirectoryConfig *dconfig = NULL;
5897     dconfig = (WSGIDirectoryConfig *)mconfig;
5898 
5899     if (strcasecmp(f, "Off") == 0)
5900         dconfig->user_authoritative = 0;
5901     else if (strcasecmp(f, "On") == 0)
5902         dconfig->user_authoritative = 1;
5903     else
5904         return "WSGIUserAuthoritative must be one of: Off | On";
5905 
5906     return NULL;
5907 }
5908 #endif
5909 
wsgi_set_group_authoritative(cmd_parms * cmd,void * mconfig,const char * f)5910 static const char *wsgi_set_group_authoritative(cmd_parms *cmd, void *mconfig,
5911                                                 const char *f)
5912 {
5913     WSGIDirectoryConfig *dconfig = NULL;
5914     dconfig = (WSGIDirectoryConfig *)mconfig;
5915 
5916     if (strcasecmp(f, "Off") == 0)
5917         dconfig->group_authoritative = 0;
5918     else if (strcasecmp(f, "On") == 0)
5919         dconfig->group_authoritative = 1;
5920     else
5921         return "WSGIGroupAuthoritative must be one of: Off | On";
5922 
5923     return NULL;
5924 }
5925 
wsgi_add_handler_script(cmd_parms * cmd,void * mconfig,const char * args)5926 static const char *wsgi_add_handler_script(cmd_parms *cmd, void *mconfig,
5927                                            const char *args)
5928 {
5929     WSGIScriptFile *object = NULL;
5930 
5931     const char *name = NULL;
5932     const char *option = NULL;
5933     const char *value = NULL;
5934 
5935     name = ap_getword_conf(cmd->pool, &args);
5936 
5937     if (!name || !*name)
5938         return "Name for handler script not supplied.";
5939 
5940     object = newWSGIScriptFile(cmd->pool);
5941 
5942     object->handler_script = ap_getword_conf(cmd->pool, &args);
5943 
5944     if (!object->handler_script || !*object->handler_script)
5945         return "Location of handler script not supplied.";
5946 
5947     while (*args) {
5948         if (wsgi_parse_option(cmd->pool, &args, &option,
5949                               &value) != APR_SUCCESS) {
5950             return "Invalid option to WSGI handler script definition.";
5951         }
5952 
5953         if (!strcmp(option, "process-group")) {
5954             if (!*value)
5955                 return "Invalid name for WSGI process group.";
5956 
5957             object->process_group = value;
5958         }
5959         else if (!strcmp(option, "application-group")) {
5960             if (!*value)
5961                 return "Invalid name for WSGI application group.";
5962 
5963             object->application_group = value;
5964         }
5965         else if (!strcmp(option, "pass-authorization")) {
5966             if (!*value)
5967                 return "Invalid value for authorization flag.";
5968 
5969             if (strcasecmp(value, "Off") == 0)
5970                 object->pass_authorization = "0";
5971             else if (strcasecmp(value, "On") == 0)
5972                 object->pass_authorization = "1";
5973             else
5974                 return "Invalid value for authorization flag.";
5975         }
5976         else
5977             return "Invalid option to WSGI handler script definition.";
5978     }
5979 
5980     if (cmd->path) {
5981         WSGIDirectoryConfig *dconfig = NULL;
5982         dconfig = (WSGIDirectoryConfig *)mconfig;
5983 
5984         if (!dconfig->handler_scripts)
5985             dconfig->handler_scripts = apr_hash_make(cmd->pool);
5986 
5987         apr_hash_set(dconfig->handler_scripts, name, APR_HASH_KEY_STRING,
5988                      object);
5989     }
5990     else {
5991         WSGIServerConfig *sconfig = NULL;
5992         sconfig = ap_get_module_config(cmd->server->module_config,
5993                                        &wsgi_module);
5994 
5995         if (!sconfig->handler_scripts)
5996             sconfig->handler_scripts = apr_hash_make(cmd->pool);
5997 
5998         apr_hash_set(sconfig->handler_scripts, name, APR_HASH_KEY_STRING,
5999                      object);
6000     }
6001 
6002     return NULL;
6003 }
6004 
wsgi_set_server_metrics(cmd_parms * cmd,void * mconfig,const char * f)6005 static const char *wsgi_set_server_metrics(cmd_parms *cmd, void *mconfig,
6006                                            const char *f)
6007 {
6008     const char *error = NULL;
6009     WSGIServerConfig *sconfig = NULL;
6010 
6011     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
6012     if (error != NULL)
6013         return error;
6014 
6015     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
6016 
6017     if (strcasecmp(f, "Off") == 0)
6018         sconfig->server_metrics = 0;
6019     else if (strcasecmp(f, "On") == 0)
6020         sconfig->server_metrics = 1;
6021     else
6022         return "WSGIServerMetrics must be one of: Off | On";
6023 
6024     return NULL;
6025 }
6026 
wsgi_set_newrelic_config_file(cmd_parms * cmd,void * mconfig,const char * f)6027 static const char *wsgi_set_newrelic_config_file(
6028         cmd_parms *cmd, void *mconfig, const char *f)
6029 {
6030     const char *error = NULL;
6031     WSGIServerConfig *sconfig = NULL;
6032 
6033     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
6034     if (error != NULL)
6035         return error;
6036 
6037     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
6038     sconfig->newrelic_config_file = f;
6039 
6040     return NULL;
6041 }
6042 
wsgi_set_newrelic_environment(cmd_parms * cmd,void * mconfig,const char * f)6043 static const char *wsgi_set_newrelic_environment(
6044         cmd_parms *cmd, void *mconfig, const char *f)
6045 {
6046     const char *error = NULL;
6047     WSGIServerConfig *sconfig = NULL;
6048 
6049     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
6050     if (error != NULL)
6051         return error;
6052 
6053     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
6054     sconfig->newrelic_environment = f;
6055 
6056     return NULL;
6057 }
6058 
6059 /* Handler for the translate name phase. */
6060 
wsgi_alias_matches(const char * uri,const char * alias_fakename)6061 static long wsgi_alias_matches(const char *uri, const char *alias_fakename)
6062 {
6063     /* Code for this function from Apache mod_alias module. */
6064 
6065     const char *aliasp = alias_fakename, *urip = uri;
6066 
6067     while (*aliasp) {
6068         if (*aliasp == '/') {
6069             /* any number of '/' in the alias matches any number in
6070              * the supplied URI, but there must be at least one...
6071              */
6072             if (*urip != '/')
6073                 return 0;
6074 
6075             do {
6076                 ++aliasp;
6077             } while (*aliasp == '/');
6078             do {
6079                 ++urip;
6080             } while (*urip == '/');
6081         }
6082         else {
6083             /* Other characters are compared literally */
6084             if (*urip++ != *aliasp++)
6085                 return 0;
6086         }
6087     }
6088 
6089     /* Check last alias path component matched all the way */
6090 
6091     if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
6092         return 0;
6093 
6094     /* Return number of characters from URI which matched (may be
6095      * greater than length of alias, since we may have matched
6096      * doubled slashes)
6097      */
6098 
6099     return urip - uri;
6100 }
6101 
wsgi_hook_intercept(request_rec * r)6102 static int wsgi_hook_intercept(request_rec *r)
6103 {
6104     WSGIServerConfig *config = NULL;
6105 
6106     apr_array_header_t *aliases = NULL;
6107 
6108     WSGIAliasEntry *entries = NULL;
6109     WSGIAliasEntry *entry = NULL;
6110 
6111     ap_regmatch_t matches[AP_MAX_REG_MATCH];
6112 
6113     const char *location = NULL;
6114     const char *application = NULL;
6115 
6116     int i = 0;
6117 
6118     config = ap_get_module_config(r->server->module_config, &wsgi_module);
6119 
6120     if (!config->alias_list)
6121         return DECLINED;
6122 
6123     if (r->uri[0] != '/' && r->uri[0])
6124         return DECLINED;
6125 
6126     aliases = config->alias_list;
6127     entries = (WSGIAliasEntry *)aliases->elts;
6128 
6129     for (i = 0; i < aliases->nelts; ++i) {
6130         long l = 0;
6131 
6132         entry = &entries[i];
6133 
6134         if (entry->regexp) {
6135             if (!ap_regexec(entry->regexp, r->uri, AP_MAX_REG_MATCH,
6136                 matches, 0)) {
6137                 if (entry->application) {
6138                     l = matches[0].rm_eo;
6139 
6140                     location = apr_pstrndup(r->pool, r->uri, l);
6141                     application = ap_pregsub(r->pool, entry->application,
6142                                              r->uri, AP_MAX_REG_MATCH,
6143                                              matches);
6144                 }
6145             }
6146         }
6147         else if (entry->location) {
6148             l = wsgi_alias_matches(r->uri, entry->location);
6149 
6150             location = entry->location;
6151             application = entry->application;
6152         }
6153 
6154         if (l > 0) {
6155             if (!strcmp(location, "/")) {
6156                 r->filename = apr_pstrcat(r->pool, application,
6157                                           r->uri, NULL);
6158             }
6159             else {
6160                 r->filename = apr_pstrcat(r->pool, application,
6161                                           r->uri + l, NULL);
6162             }
6163 
6164             r->handler = "wsgi-script";
6165             apr_table_setn(r->notes, "alias-forced-type", r->handler);
6166 
6167             if (entry->process_group) {
6168                 apr_table_setn(r->notes, "mod_wsgi.process_group",
6169                                entry->process_group);
6170             }
6171             if (entry->application_group) {
6172                 apr_table_setn(r->notes, "mod_wsgi.application_group",
6173                                entry->application_group);
6174             }
6175             if (entry->callable_object) {
6176                 apr_table_setn(r->notes, "mod_wsgi.callable_object",
6177                                entry->callable_object);
6178             }
6179 
6180             if (entry->pass_authorization == 0)
6181                 apr_table_setn(r->notes, "mod_wsgi.pass_authorization", "0");
6182             else if (entry->pass_authorization == 1)
6183                 apr_table_setn(r->notes, "mod_wsgi.pass_authorization", "1");
6184 
6185             return OK;
6186         }
6187     }
6188 
6189     return DECLINED;
6190 }
6191 
6192 /* Handler for the response handler phase. */
6193 
6194 static void wsgi_drop_invalid_headers(request_rec *r);
6195 static void wsgi_process_proxy_headers(request_rec *r);
6196 
wsgi_build_environment(request_rec * r)6197 static void wsgi_build_environment(request_rec *r)
6198 {
6199     WSGIRequestConfig *config = NULL;
6200 
6201     const char *value = NULL;
6202     const char *script_name = NULL;
6203     const char *path_info = NULL;
6204 
6205     conn_rec *c = r->connection;
6206 
6207     /* Grab request configuration. */
6208 
6209     config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
6210                                                        &wsgi_module);
6211 
6212     /*
6213      * Remove any invalid headers which use invalid characters.
6214      * This is necessary to ensure that someone doesn't try and
6215      * take advantage of header spoofing. This can come about
6216      * where characters other than alphanumerics or '-' are used
6217      * as the conversion of non alphanumerics to '_' means one
6218      * can get collisions. This is technically only an issue
6219      * with Apache 2.2 as Apache 2.4 addresses the problem and
6220      * drops them anyway. Still go through and drop them even
6221      * for Apache 2.4 as not sure which version of Apache 2.4
6222      * introduces the change.
6223      */
6224 
6225     wsgi_drop_invalid_headers(r);
6226 
6227     /* Populate environment with standard CGI variables. */
6228 
6229     ap_add_cgi_vars(r);
6230     ap_add_common_vars(r);
6231 
6232     /*
6233      * Mutate a HEAD request into a GET request. This is
6234      * required because WSGI specification doesn't lay out
6235      * clearly how WSGI applications should treat a HEAD
6236      * request. Generally authors of WSGI applications or
6237      * frameworks take it that they do not need to return any
6238      * content, but this screws up any Apache output filters
6239      * which need to see all the response content in order to
6240      * correctly set up response headers for a HEAD request such
6241      * that they are the same as a GET request. Thus change a
6242      * HEAD request into a GET request to ensure that request
6243      * content is generated. If using Apache 2.X we can skip
6244      * doing this if we know there is no output filter that
6245      * might change the content and/or headers.
6246      *
6247      * The default behaviour here of changing it if an output
6248      * filter is detected can be overridden using the directive
6249      * WSGIMapHEADToGet. The default value is 'Auto'. If set to
6250      * 'On' then it remapped regardless of whether an output
6251      * filter is present. If 'Off' then it will be left alone
6252      * and the original value used.
6253      */
6254 
6255     if (config->map_head_to_get == 2) {
6256         if (r->method_number == M_GET && r->header_only &&
6257             r->output_filters->frec->ftype < AP_FTYPE_PROTOCOL)
6258             apr_table_setn(r->subprocess_env, "REQUEST_METHOD", "GET");
6259     }
6260     else if (config->map_head_to_get == 1) {
6261         if (r->method_number == M_GET)
6262             apr_table_setn(r->subprocess_env, "REQUEST_METHOD", "GET");
6263     }
6264 
6265     /*
6266      * If enabled, pass along authorisation headers which Apache
6267      * leaves out of CGI environment. WSGI still needs to see
6268      * these if it needs to implement any of the standard
6269      * authentication schemes such as Basic and Digest. We do
6270      * not pass these through by default though as it can result
6271      * in passwords being leaked though to a WSGI application
6272      * when it shouldn't. This would be a problem where there is
6273      * some sort of site wide authorisation scheme in place
6274      * which has got nothing to do with specific applications.
6275      */
6276 
6277     if (config->pass_authorization) {
6278         value = apr_table_get(r->headers_in, "Authorization");
6279         if (value)
6280             apr_table_setn(r->subprocess_env, "HTTP_AUTHORIZATION", value);
6281     }
6282 
6283     /* If PATH_INFO not set, set it to an empty string. */
6284 
6285     value = apr_table_get(r->subprocess_env, "PATH_INFO");
6286     if (!value)
6287         apr_table_setn(r->subprocess_env, "PATH_INFO", "");
6288 
6289     /*
6290      * Multiple slashes are not always collapsed into a single
6291      * slash in SCRIPT_NAME and PATH_INFO with Apache 1.3 and
6292      * Apache 2.X behaving a bit differently. Because some WSGI
6293      * applications don't deal with multiple slashes properly we
6294      * collapse any duplicate slashes to a single slash so
6295      * Apache behaviour is consistent across all versions. We
6296      * don't care that PATH_TRANSLATED can on Apache 1.3 still
6297      * contain multiple slashes as that should not be getting
6298      * used from a WSGI application anyway.
6299      */
6300 
6301     script_name = apr_table_get(r->subprocess_env, "SCRIPT_NAME");
6302 
6303     if (*script_name == '/') {
6304         while (*script_name && (*(script_name+1) == '/'))
6305             script_name++;
6306         script_name = apr_pstrdup(r->pool, script_name);
6307         ap_no2slash((char*)script_name);
6308         apr_table_setn(r->subprocess_env, "SCRIPT_NAME", script_name);
6309     }
6310 
6311     path_info = apr_table_get(r->subprocess_env, "PATH_INFO");
6312 
6313     if (*path_info == '/') {
6314         while (*path_info && (*(path_info+1) == '/'))
6315             path_info++;
6316         path_info = apr_pstrdup(r->pool, path_info);
6317         ap_no2slash((char*)path_info);
6318         apr_table_setn(r->subprocess_env, "PATH_INFO", path_info);
6319     }
6320 
6321     /*
6322      * Save away the SCRIPT_NAME and PATH_INFO values at this point
6323      * so we have a way of determining if they are rewritten somehow.
6324      * This can be important when dealing with rewrite rules and
6325      * a trusted header was being handled for SCRIPT_NAME.
6326      */
6327 
6328     apr_table_setn(r->subprocess_env, "mod_wsgi.script_name", script_name);
6329     apr_table_setn(r->subprocess_env, "mod_wsgi.path_info", path_info);
6330 
6331     /*
6332      * Perform fixups on environment based on trusted proxy headers
6333      * sent through from a front end proxy.
6334      */
6335 
6336     wsgi_process_proxy_headers(r);
6337 
6338     /*
6339      * Determine whether connection uses HTTPS protocol. This has
6340      * to be done after and fixups due to trusted proxy headers.
6341      */
6342 
6343     if (!wsgi_is_https)
6344         wsgi_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
6345 
6346     if (wsgi_is_https && wsgi_is_https(r->connection))
6347         apr_table_set(r->subprocess_env, "HTTPS", "1");
6348 
6349     /*
6350      * Set values specific to mod_wsgi configuration. These control
6351      * aspects of how a request is managed but don't strictly need
6352      * to be passed through to the application itself. It is though
6353      * easier to set them here as then they are carried across to
6354      * the daemon process as part of the environment where they can
6355      * be extracted and used.
6356      */
6357 
6358     apr_table_setn(r->subprocess_env, "mod_wsgi.process_group",
6359                    config->process_group);
6360     apr_table_setn(r->subprocess_env, "mod_wsgi.application_group",
6361                    config->application_group);
6362     apr_table_setn(r->subprocess_env, "mod_wsgi.callable_object",
6363                    config->callable_object);
6364 
6365     apr_table_setn(r->subprocess_env, "mod_wsgi.request_handler", r->handler);
6366     apr_table_setn(r->subprocess_env, "mod_wsgi.handler_script",
6367                    config->handler_script);
6368 
6369     apr_table_setn(r->subprocess_env, "mod_wsgi.script_reloading",
6370                    apr_psprintf(r->pool, "%d", config->script_reloading));
6371 
6372 #if defined(MOD_WSGI_WITH_DAEMONS)
6373     apr_table_setn(r->subprocess_env, "mod_wsgi.listener_host",
6374                    c->local_addr->hostname ? c->local_addr->hostname : "");
6375     apr_table_setn(r->subprocess_env, "mod_wsgi.listener_port",
6376                    apr_psprintf(r->pool, "%d", c->local_addr->port));
6377 #endif
6378 
6379     apr_table_setn(r->subprocess_env, "mod_wsgi.enable_sendfile",
6380                    apr_psprintf(r->pool, "%d", config->enable_sendfile));
6381     apr_table_setn(r->subprocess_env, "mod_wsgi.ignore_activity",
6382                    apr_psprintf(r->pool, "%d", config->ignore_activity));
6383 
6384     apr_table_setn(r->subprocess_env, "mod_wsgi.request_start",
6385                    apr_psprintf(r->pool, "%" APR_TIME_T_FMT, r->request_time));
6386 
6387 #if AP_MODULE_MAGIC_AT_LEAST(20100923,2)
6388     if (!r->log_id) {
6389         const char **id;
6390 
6391         /* Need to cast const away. */
6392 
6393         id = &((request_rec *)r)->log_id;
6394 
6395         ap_run_generate_log_id(c, r, id);
6396     }
6397 
6398     if (r->log_id)
6399         apr_table_setn(r->subprocess_env, "mod_wsgi.request_id", r->log_id);
6400     if (r->connection->log_id)
6401         apr_table_setn(r->subprocess_env, "mod_wsgi.connection_id",
6402                        r->connection->log_id);
6403 #endif
6404 }
6405 
6406 typedef struct {
6407         PyObject_HEAD
6408         request_rec *r;
6409         WSGIRequestConfig *config;
6410         PyObject *log;
6411 } DispatchObject;
6412 
newDispatchObject(request_rec * r,WSGIRequestConfig * config)6413 static DispatchObject *newDispatchObject(request_rec *r,
6414                                          WSGIRequestConfig *config)
6415 {
6416     DispatchObject *self;
6417 
6418     self = PyObject_New(DispatchObject, &Dispatch_Type);
6419     if (self == NULL)
6420         return NULL;
6421 
6422     self->config = config;
6423 
6424     self->r = r;
6425 
6426     self->log = newLogObject(r, APLOG_ERR, NULL, 0);
6427 
6428     return self;
6429 }
6430 
Dispatch_dealloc(DispatchObject * self)6431 static void Dispatch_dealloc(DispatchObject *self)
6432 {
6433     Py_DECREF(self->log);
6434 
6435     PyObject_Del(self);
6436 }
6437 
Dispatch_environ(DispatchObject * self,const char * group)6438 static PyObject *Dispatch_environ(DispatchObject *self, const char *group)
6439 {
6440     request_rec *r = NULL;
6441 
6442     PyObject *vars = NULL;
6443     PyObject *object = NULL;
6444 
6445     const apr_array_header_t *head = NULL;
6446     const apr_table_entry_t *elts = NULL;
6447 
6448     int i = 0;
6449 
6450     /* Create the WSGI environment dictionary. */
6451 
6452     vars = PyDict_New();
6453 
6454     /* Merge the CGI environment into the WSGI environment. */
6455 
6456     r = self->r;
6457 
6458     head = apr_table_elts(r->subprocess_env);
6459     elts = (apr_table_entry_t *)head->elts;
6460 
6461     for (i = 0; i < head->nelts; ++i) {
6462         if (elts[i].key) {
6463             if (elts[i].val) {
6464 #if PY_MAJOR_VERSION >= 3
6465                 object = PyUnicode_DecodeLatin1(elts[i].val,
6466                                                 strlen(elts[i].val), NULL);
6467 #else
6468                 object = PyString_FromString(elts[i].val);
6469 #endif
6470                 PyDict_SetItemString(vars, elts[i].key, object);
6471                 Py_DECREF(object);
6472             }
6473             else
6474                 PyDict_SetItemString(vars, elts[i].key, Py_None);
6475         }
6476     }
6477 
6478     /*
6479      * Need to override process and application group as they
6480      * are set to the default target, where as we need to set
6481      * them to context dispatch script is run in. Also need
6482      * to remove callable object reference.
6483      */
6484 
6485 #if PY_MAJOR_VERSION >= 3
6486     object = PyUnicode_FromString("");
6487 #else
6488     object = PyString_FromString("");
6489 #endif
6490     PyDict_SetItemString(vars, "mod_wsgi.process_group", object);
6491     Py_DECREF(object);
6492 
6493 #if PY_MAJOR_VERSION >= 3
6494     object = PyUnicode_DecodeLatin1(group, strlen(group), NULL);
6495 #else
6496     object = PyString_FromString(group);
6497 #endif
6498     PyDict_SetItemString(vars, "mod_wsgi.application_group", object);
6499     Py_DECREF(object);
6500 
6501     PyDict_DelItemString(vars, "mod_wsgi.callable_object");
6502 
6503     /*
6504      * Setup log object for WSGI errors. Don't decrement
6505      * reference to log object as keep reference to it.
6506      */
6507 
6508     object = (PyObject *)self->log;
6509     PyDict_SetItemString(vars, "wsgi.errors", object);
6510 
6511     /*
6512      * If Apache extensions are enabled add a CObject reference
6513      * to the Apache request_rec structure instance.
6514      */
6515 
6516     if (!wsgi_daemon_pool && self->config->pass_apache_request) {
6517 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || \
6518     (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 7)
6519         object = PyCapsule_New(self->r, 0, 0);
6520 #else
6521         object = PyCObject_FromVoidPtr(self->r, 0);
6522 #endif
6523         PyDict_SetItemString(vars, "apache.request_rec", object);
6524         Py_DECREF(object);
6525     }
6526 
6527     /*
6528      * Extensions for accessing SSL certificate information from
6529      * mod_ssl when in use.
6530      */
6531 
6532 #if 0
6533     object = PyObject_GetAttrString((PyObject *)self, "ssl_is_https");
6534     PyDict_SetItemString(vars, "mod_ssl.is_https", object);
6535     Py_DECREF(object);
6536 
6537     object = PyObject_GetAttrString((PyObject *)self, "ssl_var_lookup");
6538     PyDict_SetItemString(vars, "mod_ssl.var_lookup", object);
6539     Py_DECREF(object);
6540 #endif
6541 
6542     return vars;
6543 }
6544 
Dispatch_ssl_is_https(DispatchObject * self,PyObject * args)6545 static PyObject *Dispatch_ssl_is_https(DispatchObject *self, PyObject *args)
6546 {
6547     APR_OPTIONAL_FN_TYPE(ssl_is_https) *ssl_is_https = 0;
6548 
6549     if (!self->r) {
6550         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
6551         return NULL;
6552     }
6553 
6554     if (!PyArg_ParseTuple(args, ":ssl_is_https"))
6555         return NULL;
6556 
6557     ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
6558 
6559     if (ssl_is_https == 0)
6560       return Py_BuildValue("i", 0);
6561 
6562     return Py_BuildValue("i", ssl_is_https(self->r->connection));
6563 }
6564 
Dispatch_ssl_var_lookup(DispatchObject * self,PyObject * args)6565 static PyObject *Dispatch_ssl_var_lookup(DispatchObject *self, PyObject *args)
6566 {
6567     APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *ssl_var_lookup = 0;
6568 
6569     PyObject *item = NULL;
6570 
6571     char *name = 0;
6572     char *value = 0;
6573 
6574     if (!self->r) {
6575         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
6576         return NULL;
6577     }
6578 
6579     if (!PyArg_ParseTuple(args, "O:ssl_var_lookup", &item))
6580         return NULL;
6581 
6582 #if PY_MAJOR_VERSION >= 3
6583     if (PyUnicode_Check(item)) {
6584         PyObject *latin_item;
6585         latin_item = PyUnicode_AsLatin1String(item);
6586         if (!latin_item) {
6587             PyErr_Format(PyExc_TypeError, "byte string value expected, "
6588                          "value containing non 'latin-1' characters found");
6589             Py_DECREF(item);
6590             return NULL;
6591         }
6592 
6593         Py_DECREF(item);
6594         item = latin_item;
6595     }
6596 #endif
6597 
6598     if (!PyString_Check(item)) {
6599         PyErr_Format(PyExc_TypeError, "byte string value expected, value "
6600                      "of type %.200s found", item->ob_type->tp_name);
6601         Py_DECREF(item);
6602         return NULL;
6603     }
6604 
6605     name = PyString_AsString(item);
6606 
6607     ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
6608 
6609     if (ssl_var_lookup == 0)
6610     {
6611         Py_INCREF(Py_None);
6612 
6613         return Py_None;
6614     }
6615 
6616     value = ssl_var_lookup(self->r->pool, self->r->server,
6617                            self->r->connection, self->r, name);
6618 
6619     if (!value) {
6620         Py_INCREF(Py_None);
6621 
6622         return Py_None;
6623     }
6624 
6625 #if PY_MAJOR_VERSION >= 3
6626     return PyUnicode_DecodeLatin1(value, strlen(value), NULL);
6627 #else
6628     return PyString_FromString(value);
6629 #endif
6630 }
6631 
6632 static PyMethodDef Dispatch_methods[] = {
6633     { "ssl_is_https",   (PyCFunction)Dispatch_ssl_is_https, METH_VARARGS, 0 },
6634     { "ssl_var_lookup", (PyCFunction)Dispatch_ssl_var_lookup, METH_VARARGS, 0 },
6635     { NULL, NULL}
6636 };
6637 
6638 static PyTypeObject Dispatch_Type = {
6639     PyVarObject_HEAD_INIT(NULL, 0)
6640     "mod_wsgi.Dispatch",    /*tp_name*/
6641     sizeof(DispatchObject), /*tp_basicsize*/
6642     0,                      /*tp_itemsize*/
6643     /* methods */
6644     (destructor)Dispatch_dealloc, /*tp_dealloc*/
6645     0,                      /*tp_print*/
6646     0,                      /*tp_getattr*/
6647     0,                      /*tp_setattr*/
6648     0,                      /*tp_compare*/
6649     0,                      /*tp_repr*/
6650     0,                      /*tp_as_number*/
6651     0,                      /*tp_as_sequence*/
6652     0,                      /*tp_as_mapping*/
6653     0,                      /*tp_hash*/
6654     0,                      /*tp_call*/
6655     0,                      /*tp_str*/
6656     0,                      /*tp_getattro*/
6657     0,                      /*tp_setattro*/
6658     0,                      /*tp_as_buffer*/
6659     Py_TPFLAGS_DEFAULT,     /*tp_flags*/
6660     0,                      /*tp_doc*/
6661     0,                      /*tp_traverse*/
6662     0,                      /*tp_clear*/
6663     0,                      /*tp_richcompare*/
6664     0,                      /*tp_weaklistoffset*/
6665     0,                      /*tp_iter*/
6666     0,                      /*tp_iternext*/
6667     Dispatch_methods,       /*tp_methods*/
6668     0,                      /*tp_members*/
6669     0,                      /*tp_getset*/
6670     0,                      /*tp_base*/
6671     0,                      /*tp_dict*/
6672     0,                      /*tp_descr_get*/
6673     0,                      /*tp_descr_set*/
6674     0,                      /*tp_dictoffset*/
6675     0,                      /*tp_init*/
6676     0,                      /*tp_alloc*/
6677     0,                      /*tp_new*/
6678     0,                      /*tp_free*/
6679     0,                      /*tp_is_gc*/
6680 };
6681 
wsgi_execute_dispatch(request_rec * r)6682 static int wsgi_execute_dispatch(request_rec *r)
6683 {
6684     WSGIRequestConfig *config;
6685 
6686     InterpreterObject *interp = NULL;
6687     PyObject *modules = NULL;
6688     PyObject *module = NULL;
6689     char *name = NULL;
6690     int exists = 0;
6691 
6692     const char *script = NULL;
6693     const char *group = NULL;
6694 
6695     int status;
6696 
6697     /* Grab request configuration. */
6698 
6699     config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
6700                                                        &wsgi_module);
6701 
6702     if (!config->dispatch_script) {
6703         ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
6704                      "mod_wsgi (pid=%d): Location of WSGI dispatch "
6705                      "script not provided.", getpid());
6706 
6707         return HTTP_INTERNAL_SERVER_ERROR;
6708     }
6709 
6710     /*
6711      * Acquire the desired python interpreter. Once this is done
6712      * it is safe to start manipulating python objects.
6713      */
6714 
6715     script = config->dispatch_script->handler_script;
6716     group = wsgi_server_group(r, config->dispatch_script->application_group);
6717 
6718     interp = wsgi_acquire_interpreter(group);
6719 
6720     if (!interp) {
6721         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
6722                       "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
6723                       getpid(), group);
6724 
6725         return HTTP_INTERNAL_SERVER_ERROR;
6726     }
6727 
6728     /* Calculate the Python module name to be used for script. */
6729 
6730     name = wsgi_module_name(r->pool, script);
6731 
6732     /*
6733      * Use a lock around the check to see if the module is
6734      * already loaded and the import of the module to prevent
6735      * two request handlers trying to import the module at the
6736      * same time.
6737      */
6738 
6739 #if APR_HAS_THREADS
6740     Py_BEGIN_ALLOW_THREADS
6741     apr_thread_mutex_lock(wsgi_module_lock);
6742     Py_END_ALLOW_THREADS
6743 #endif
6744 
6745     modules = PyImport_GetModuleDict();
6746     module = PyDict_GetItemString(modules, name);
6747 
6748     Py_XINCREF(module);
6749 
6750     if (module)
6751         exists = 1;
6752 
6753     /*
6754      * If script reloading is enabled and the module for it has
6755      * previously been loaded, see if it has been modified since
6756      * the last time it was accessed.
6757      */
6758 
6759     if (module && config->script_reloading) {
6760         if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
6761             /*
6762              * Script file has changed. Only support module
6763              * reloading for dispatch scripts. Remove the
6764              * module from the modules dictionary before
6765              * reloading it again. If code is executing within
6766              * the module at the time, the callers reference
6767              * count on the module should ensure it isn't
6768              * actually destroyed until it is finished.
6769              */
6770 
6771             Py_DECREF(module);
6772             module = NULL;
6773 
6774             PyDict_DelItemString(modules, name);
6775         }
6776     }
6777 
6778     if (!module) {
6779         module = wsgi_load_source(r->pool, r, name, exists, script,
6780                                   "", group, 0);
6781     }
6782 
6783     /* Safe now to release the module lock. */
6784 
6785 #if APR_HAS_THREADS
6786     apr_thread_mutex_unlock(wsgi_module_lock);
6787 #endif
6788 
6789     /* Log any details of exceptions if import failed. */
6790 
6791     if (PyErr_Occurred())
6792         wsgi_log_python_error(r, NULL, script, 0);
6793 
6794     /* Assume everything will be okay for now. */
6795 
6796     status = OK;
6797 
6798     /* Determine if script exists and execute it. */
6799 
6800     if (module) {
6801         PyObject *module_dict = NULL;
6802         PyObject *object = NULL;
6803         DispatchObject *adapter = NULL;
6804 
6805         module_dict = PyModule_GetDict(module);
6806 
6807         adapter = newDispatchObject(r, config);
6808 
6809         if (adapter) {
6810             PyObject *vars = NULL;
6811             PyObject *args = NULL;
6812             PyObject *method = NULL;
6813 
6814             vars = Dispatch_environ(adapter, group);
6815 
6816             /* First check process_group(). */
6817 
6818 #if defined(MOD_WSGI_WITH_DAEMONS)
6819             object = PyDict_GetItemString(module_dict, "process_group");
6820 
6821             if (object) {
6822                 PyObject *result = NULL;
6823 
6824                 if (adapter) {
6825                     Py_INCREF(object);
6826                     args = Py_BuildValue("(O)", vars);
6827                     result = PyEval_CallObject(object, args);
6828                     Py_DECREF(args);
6829                     Py_DECREF(object);
6830 
6831                     if (result) {
6832                         if (result != Py_None) {
6833                             if (PyString_Check(result)) {
6834                                 const char *s;
6835 
6836                                 s = PyString_AsString(result);
6837                                 s = apr_pstrdup(r->pool, s);
6838                                 s = wsgi_process_group(r, s);
6839                                 config->process_group = s;
6840 
6841                                 apr_table_setn(r->subprocess_env,
6842                                                "mod_wsgi.process_group",
6843                                                config->process_group);
6844                             }
6845 #if PY_MAJOR_VERSION >= 3
6846                             else if (PyUnicode_Check(result)) {
6847                                 PyObject *latin_item;
6848                                 latin_item = PyUnicode_AsLatin1String(result);
6849                                 if (!latin_item) {
6850                                     PyErr_SetString(PyExc_TypeError,
6851                                                     "Process group must be "
6852                                                     "a byte string, value "
6853                                                     "containing non 'latin-1' "
6854                                                     "characters found");
6855 
6856                                     status = HTTP_INTERNAL_SERVER_ERROR;
6857                                 }
6858                                 else {
6859                                     const char *s;
6860 
6861                                     Py_DECREF(result);
6862                                     result = latin_item;
6863 
6864                                     s = PyString_AsString(result);
6865                                     s = apr_pstrdup(r->pool, s);
6866                                     s = wsgi_process_group(r, s);
6867                                     config->process_group = s;
6868 
6869                                     apr_table_setn(r->subprocess_env,
6870                                                    "mod_wsgi.process_group",
6871                                                    config->process_group);
6872                                 }
6873                             }
6874 #endif
6875                             else {
6876                                 PyErr_SetString(PyExc_TypeError, "Process "
6877                                                 "group must be a byte string");
6878 
6879                                 status = HTTP_INTERNAL_SERVER_ERROR;
6880                             }
6881                         }
6882 
6883                         Py_DECREF(result);
6884                     }
6885                     else
6886                         status = HTTP_INTERNAL_SERVER_ERROR;
6887 
6888                     /* Log any details of exceptions if execution failed. */
6889 
6890                     if (PyErr_Occurred())
6891                         wsgi_log_python_error(r, NULL, script, 0);
6892                 }
6893 
6894                 object = NULL;
6895             }
6896 #endif
6897 
6898             /* Now check application_group(). */
6899 
6900             if (status == OK)
6901                 object = PyDict_GetItemString(module_dict, "application_group");
6902 
6903             if (object) {
6904                 PyObject *result = NULL;
6905 
6906                 if (adapter) {
6907                     Py_INCREF(object);
6908                     args = Py_BuildValue("(O)", vars);
6909                     result = PyEval_CallObject(object, args);
6910                     Py_DECREF(args);
6911                     Py_DECREF(object);
6912 
6913                     if (result) {
6914                         if (result != Py_None) {
6915                             if (PyString_Check(result)) {
6916                                 const char *s;
6917 
6918                                 s = PyString_AsString(result);
6919                                 s = apr_pstrdup(r->pool, s);
6920                                 s = wsgi_application_group(r, s);
6921                                 config->application_group = s;
6922 
6923                                 apr_table_setn(r->subprocess_env,
6924                                                "mod_wsgi.application_group",
6925                                                config->application_group);
6926                             }
6927 #if PY_MAJOR_VERSION >= 3
6928                             else if (PyUnicode_Check(result)) {
6929                                 PyObject *latin_item;
6930                                 latin_item = PyUnicode_AsLatin1String(result);
6931                                 if (!latin_item) {
6932                                     PyErr_SetString(PyExc_TypeError,
6933                                                     "Application group must "
6934                                                     "be a byte string, value "
6935                                                     "containing non 'latin-1' "
6936                                                     "characters found");
6937 
6938                                     status = HTTP_INTERNAL_SERVER_ERROR;
6939                                 }
6940                                 else {
6941                                     const char *s;
6942 
6943                                     Py_DECREF(result);
6944                                     result = latin_item;
6945 
6946                                     s = PyString_AsString(result);
6947                                     s = apr_pstrdup(r->pool, s);
6948                                     s = wsgi_application_group(r, s);
6949                                     config->application_group = s;
6950 
6951                                     apr_table_setn(r->subprocess_env,
6952                                                    "mod_wsgi.application_group",
6953                                                    config->application_group);
6954                                 }
6955                             }
6956 #endif
6957                             else {
6958                                 PyErr_SetString(PyExc_TypeError, "Application "
6959                                                 "group must be a string "
6960                                                 "object");
6961 
6962                                 status = HTTP_INTERNAL_SERVER_ERROR;
6963                             }
6964                         }
6965 
6966                         Py_DECREF(result);
6967                     }
6968                     else
6969                         status = HTTP_INTERNAL_SERVER_ERROR;
6970 
6971                     /* Log any details of exceptions if execution failed. */
6972 
6973                     if (PyErr_Occurred())
6974                         wsgi_log_python_error(r, NULL, script, 0);
6975                 }
6976 
6977                 object = NULL;
6978             }
6979 
6980             /* Now check callable_object(). */
6981 
6982             if (status == OK)
6983                 object = PyDict_GetItemString(module_dict, "callable_object");
6984 
6985             if (object) {
6986                 PyObject *result = NULL;
6987 
6988                 if (adapter) {
6989                     Py_INCREF(object);
6990                     args = Py_BuildValue("(O)", vars);
6991                     result = PyEval_CallObject(object, args);
6992                     Py_DECREF(args);
6993                     Py_DECREF(object);
6994 
6995                     if (result) {
6996                         if (result != Py_None) {
6997                             if (PyString_Check(result)) {
6998                                 const char *s;
6999 
7000                                 s = PyString_AsString(result);
7001                                 s = apr_pstrdup(r->pool, s);
7002                                 s = wsgi_callable_object(r, s);
7003                                 config->callable_object = s;
7004 
7005                                 apr_table_setn(r->subprocess_env,
7006                                                "mod_wsgi.callable_object",
7007                                                config->callable_object);
7008                             }
7009 #if PY_MAJOR_VERSION >= 3
7010                             else if (PyUnicode_Check(result)) {
7011                                 PyObject *latin_item;
7012                                 latin_item = PyUnicode_AsLatin1String(result);
7013                                 if (!latin_item) {
7014                                     PyErr_SetString(PyExc_TypeError,
7015                                                     "Callable object must "
7016                                                     "be a byte string, value "
7017                                                     "containing non 'latin-1' "
7018                                                     "characters found");
7019 
7020                                     status = HTTP_INTERNAL_SERVER_ERROR;
7021                                 }
7022                                 else {
7023                                     const char *s;
7024 
7025                                     Py_DECREF(result);
7026                                     result = latin_item;
7027 
7028                                     s = PyString_AsString(result);
7029                                     s = apr_pstrdup(r->pool, s);
7030                                     s = wsgi_callable_object(r, s);
7031                                     config->callable_object = s;
7032 
7033                                     apr_table_setn(r->subprocess_env,
7034                                                    "mod_wsgi.callable_object",
7035                                                    config->callable_object);
7036                                 }
7037                             }
7038 #endif
7039                             else {
7040                                 PyErr_SetString(PyExc_TypeError, "Callable "
7041                                                 "object must be a string "
7042                                                 "object");
7043 
7044                                 status = HTTP_INTERNAL_SERVER_ERROR;
7045                             }
7046                         }
7047 
7048                         Py_DECREF(result);
7049                     }
7050                     else
7051                         status = HTTP_INTERNAL_SERVER_ERROR;
7052 
7053                     /* Log any details of exceptions if execution failed. */
7054 
7055                     if (PyErr_Occurred())
7056                         wsgi_log_python_error(r, NULL, script, 0);
7057                 }
7058 
7059                 object = NULL;
7060             }
7061 
7062             /*
7063              * Wipe out references to Apache request object
7064              * held by Python objects, so can detect when an
7065              * application holds on to the transient Python
7066              * objects beyond the life of the request and
7067              * thus raise an exception if they are used.
7068              */
7069 
7070             adapter->r = NULL;
7071 
7072             /* Close the log object so data is flushed. */
7073 
7074             method = PyObject_GetAttrString(adapter->log, "close");
7075 
7076             if (!method) {
7077                 PyErr_Format(PyExc_AttributeError,
7078                              "'%s' object has no attribute 'close'",
7079                              adapter->log->ob_type->tp_name);
7080             }
7081             else {
7082                 args = PyTuple_New(0);
7083                 object = PyEval_CallObject(method, args);
7084                 Py_DECREF(args);
7085             }
7086 
7087             Py_XDECREF(object);
7088             Py_XDECREF(method);
7089 
7090             /* No longer need adapter object. */
7091 
7092             Py_DECREF((PyObject *)adapter);
7093 
7094             /* Log any details of exceptions if execution failed. */
7095 
7096             if (PyErr_Occurred())
7097                 wsgi_log_python_error(r, NULL, script, 0);
7098 
7099             Py_DECREF(vars);
7100         }
7101     }
7102 
7103     /* Cleanup and release interpreter, */
7104 
7105     Py_XDECREF(module);
7106 
7107     wsgi_release_interpreter(interp);
7108 
7109     return status;
7110 }
7111 
wsgi_is_script_aliased(request_rec * r)7112 static int wsgi_is_script_aliased(request_rec *r)
7113 {
7114     const char *t = NULL;
7115 
7116     t = apr_table_get(r->notes, "alias-forced-type");
7117     return t && (!strcasecmp(t, "wsgi-script"));
7118 }
7119 
7120 #if defined(MOD_WSGI_WITH_DAEMONS)
7121 static int wsgi_execute_remote(request_rec *r);
7122 #endif
7123 
wsgi_hook_handler(request_rec * r)7124 static int wsgi_hook_handler(request_rec *r)
7125 {
7126     int status;
7127     apr_off_t limit = 0;
7128 
7129     WSGIRequestConfig *config = NULL;
7130 
7131     const char *value = NULL;
7132 
7133     const char *tenc = NULL;
7134     const char *lenp = NULL;
7135 
7136     /* Filter out the obvious case of no handler defined. */
7137 
7138     if (!r->handler)
7139         return DECLINED;
7140 
7141     /*
7142      * Construct request configuration and cache it in the
7143      * request object against this module so can access it later
7144      * from handler code.
7145      */
7146 
7147     config = wsgi_create_req_config(r->pool, r);
7148 
7149     ap_set_module_config(r->request_config, &wsgi_module, config);
7150 
7151     /*
7152      * Only process requests for this module. First check for
7153      * where target is the actual WSGI script. Then need to
7154      * check for the case where handler name mapped to a handler
7155      * script definition.
7156      */
7157 
7158     if (!strcmp(r->handler, "wsgi-script") ||
7159         !strcmp(r->handler, "application/x-httpd-wsgi")) {
7160 
7161         /*
7162          * Ensure that have adequate privileges to run the WSGI
7163          * script. Require ExecCGI to be specified in Options for
7164          * this. In doing this, using the wider interpretation that
7165          * ExecCGI refers to any executable like script even though
7166          * not a separate process execution.
7167          */
7168 
7169         if (!(ap_allow_options(r) & OPT_EXECCGI) &&
7170             !wsgi_is_script_aliased(r)) {
7171             wsgi_log_script_error(r, "Options ExecCGI is off in this "
7172                                   "directory", r->filename);
7173             return HTTP_FORBIDDEN;
7174         }
7175 
7176         /* Ensure target script exists and is a file. */
7177 
7178         if (r->finfo.filetype == 0) {
7179             wsgi_log_script_error(r, "Target WSGI script not found or unable "
7180                                   "to stat", r->filename);
7181             return HTTP_NOT_FOUND;
7182         }
7183 
7184         if (r->finfo.filetype == APR_DIR) {
7185             wsgi_log_script_error(r, "Attempt to invoke directory as WSGI "
7186                                   "application", r->filename);
7187             return HTTP_FORBIDDEN;
7188         }
7189 
7190         if (wsgi_is_script_aliased(r)) {
7191             /*
7192              * Allow any configuration supplied through request notes to
7193              * override respective values. Request notes are used when
7194              * configuration supplied with WSGIScriptAlias directives.
7195              */
7196 
7197             if ((value = apr_table_get(r->notes, "mod_wsgi.process_group")))
7198                 config->process_group = wsgi_process_group(r, value);
7199             if ((value = apr_table_get(r->notes, "mod_wsgi.application_group")))
7200                 config->application_group = wsgi_application_group(r, value);
7201             if ((value = apr_table_get(r->notes, "mod_wsgi.callable_object")))
7202                 config->callable_object = value;
7203 
7204             if ((value = apr_table_get(r->notes,
7205                                       "mod_wsgi.pass_authorization"))) {
7206                 if (!strcmp(value, "1"))
7207                     config->pass_authorization = 1;
7208                 else
7209                     config->pass_authorization = 0;
7210             }
7211         }
7212     }
7213 #if 0
7214     else if (strstr(r->handler, "wsgi-handler=") == r->handler) {
7215         config->handler_script = apr_pstrcat(r->pool, r->handler+13, NULL);
7216         config->callable_object = "handle_request";
7217     }
7218 #endif
7219     else if (config->handler_scripts) {
7220         WSGIScriptFile *entry;
7221 
7222         entry = (WSGIScriptFile *)apr_hash_get(config->handler_scripts,
7223                                                r->handler,
7224                                                APR_HASH_KEY_STRING);
7225 
7226         if (entry) {
7227             config->handler_script = entry->handler_script;
7228             config->callable_object = "handle_request";
7229 
7230             if ((value = entry->process_group))
7231                 config->process_group = wsgi_process_group(r, value);
7232             if ((value = entry->application_group))
7233                 config->application_group = wsgi_application_group(r, value);
7234 
7235             if ((value = entry->pass_authorization)) {
7236                 if (!strcmp(value, "1"))
7237                     config->pass_authorization = 1;
7238                 else
7239                     config->pass_authorization = 0;
7240             }
7241         }
7242         else
7243             return DECLINED;
7244     }
7245     else
7246         return DECLINED;
7247 
7248     /*
7249      * Honour AcceptPathInfo directive. Default behaviour is
7250      * accept additional path information.
7251      */
7252 
7253 #if AP_MODULE_MAGIC_AT_LEAST(20011212,0)
7254     if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) &&
7255         r->path_info && *r->path_info) {
7256         wsgi_log_script_error(r, "AcceptPathInfo off disallows user's path",
7257                               r->filename);
7258         return HTTP_NOT_FOUND;
7259     }
7260 #endif
7261 
7262     /*
7263      * Setup policy to apply if request contains a body. Note that the
7264      * WSGI specification doesn't strictly allow for chunked request
7265      * content as CONTENT_LENGTH is required when reading input and
7266      * an application isn't meant to read more than what is defined by
7267      * CONTENT_LENGTH. We still optionally allow chunked request content.
7268      * For an application to use the content, it has to ignore the WSGI
7269      * specification and use read() with no arguments to read all
7270      * available input, or call read() with specific block size until
7271      * read() returns an empty string.
7272      */
7273 
7274     tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
7275 
7276     if (tenc) {
7277         /* Only chunked transfer encoding is supported. */
7278 
7279         if (strcasecmp(tenc, "chunked")) {
7280             wsgi_log_script_error(r, apr_psprintf(r->pool,
7281                     "Unexpected value for Transfer-Encoding of '%s' "
7282                     "supplied. Only 'chunked' supported.", tenc),
7283                     r->filename);
7284             return HTTP_NOT_IMPLEMENTED;
7285         }
7286 
7287         /* Only allow chunked requests when explicitly enabled. */
7288 
7289         if (!config->chunked_request) {
7290             wsgi_log_script_error(r, "Received request requiring chunked "
7291                     "transfer encoding, but optional support for chunked "
7292                     "transfer encoding has not been enabled.", r->filename);
7293             return HTTP_LENGTH_REQUIRED;
7294         }
7295 
7296         /*
7297          * When chunked transfer encoding is specified, there should
7298          * not be any content length specified.
7299          */
7300 
7301         if (lenp) {
7302             wsgi_log_script_error(r, "Unexpected Content-Length header "
7303                     "supplied where Transfer-Encoding was specified "
7304                     "as 'chunked'.", r->filename);
7305             return HTTP_BAD_REQUEST;
7306         }
7307     }
7308 
7309     /*
7310      * Check to see if the request content is too large if the
7311      * Content-Length header is defined then end the request here. We do
7312      * this as otherwise it will not be done until first time input data
7313      * is read in by the application. Problem is that underlying HTTP
7314      * output filter will also generate a 413 response and the error
7315      * raised from the application will be appended to that. The call to
7316      * ap_discard_request_body() is hopefully enough to trigger sending
7317      * of the 413 response by the HTTP filter.
7318      */
7319 
7320     lenp = apr_table_get(r->headers_in, "Content-Length");
7321 
7322     if (lenp) {
7323         char *endstr;
7324         apr_off_t length;
7325 
7326         if (wsgi_strtoff(&length, lenp, &endstr, 10)
7327             || *endstr || length < 0) {
7328 
7329             wsgi_log_script_error(r, apr_psprintf(r->pool,
7330                     "Invalid Content-Length header value of '%s' was "
7331                     "supplied.", lenp), r->filename);
7332 
7333             return HTTP_BAD_REQUEST;
7334         }
7335 
7336         limit = ap_get_limit_req_body(r);
7337 
7338         if (limit && limit < length) {
7339             ap_discard_request_body(r);
7340             return OK;
7341         }
7342     }
7343 
7344     /* Build the sub process environment. */
7345 
7346     config->request_start = r->request_time;
7347 
7348     wsgi_build_environment(r);
7349 
7350     /*
7351      * If a dispatch script has been provided, as appropriate
7352      * allow it to override any of the configuration related
7353      * to what context the script will be executed in and what
7354      * the target callable object for the application is.
7355      */
7356 
7357     if (config->dispatch_script) {
7358         status = wsgi_execute_dispatch(r);
7359 
7360         if (status != OK)
7361             return status;
7362     }
7363 
7364     /*
7365      * Execute the target WSGI application script or proxy
7366      * request to one of the daemon processes as appropriate.
7367      */
7368 
7369 #if defined(MOD_WSGI_WITH_DAEMONS)
7370     status = wsgi_execute_remote(r);
7371 
7372     if (status != DECLINED)
7373         return status;
7374 #endif
7375 
7376 #if defined(MOD_WSGI_DISABLE_EMBEDDED)
7377     wsgi_log_script_error(r, "Embedded mode of mod_wsgi disabled at compile "
7378                           "time", r->filename);
7379     return HTTP_INTERNAL_SERVER_ERROR;
7380 #endif
7381 
7382     if (wsgi_server_config->restrict_embedded == 1) {
7383         wsgi_log_script_error(r, "Embedded mode of mod_wsgi disabled by "
7384                               "runtime configuration", r->filename);
7385         return HTTP_INTERNAL_SERVER_ERROR;
7386     }
7387 
7388     return wsgi_execute_script(r);
7389 }
7390 
7391 /*
7392  * Apache 2.X and UNIX specific code for creation and management
7393  * of distinct daemon processes.
7394  */
7395 
7396 #if defined(MOD_WSGI_WITH_DAEMONS)
7397 
wsgi_add_daemon_process(cmd_parms * cmd,void * mconfig,const char * args)7398 static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig,
7399                                            const char *args)
7400 {
7401     const char *name = NULL;
7402     const char *user = NULL;
7403     const char *group = NULL;
7404 
7405     int processes = 1;
7406     int multiprocess = 0;
7407     int threads = 15;
7408     long umask = -1;
7409 
7410     const char *root = NULL;
7411     const char *home = NULL;
7412 
7413     const char *lang = NULL;
7414     const char *locale = NULL;
7415 
7416     const char *python_home = NULL;
7417     const char *python_path = NULL;
7418     const char *python_eggs = NULL;
7419 
7420     int stack_size = 0;
7421     int maximum_requests = 0;
7422     int startup_timeout = 0;
7423     int shutdown_timeout = 5;
7424     int deadlock_timeout = 300;
7425     int inactivity_timeout = 0;
7426     int request_timeout = 0;
7427     int graceful_timeout = 0;
7428     int eviction_timeout = 0;
7429     int restart_interval = 0;
7430     int connect_timeout = 15;
7431     int socket_timeout = 0;
7432     int queue_timeout = 0;
7433 
7434     const char *socket_user = NULL;
7435 
7436     int listen_backlog = WSGI_LISTEN_BACKLOG;
7437 
7438     const char *display_name = NULL;
7439 
7440     int send_buffer_size = 0;
7441     int recv_buffer_size = 0;
7442     int header_buffer_size = 0;
7443     int response_buffer_size = 0;
7444 
7445     int response_socket_timeout = 0;
7446 
7447     const char *script_user = NULL;
7448     const char *script_group = NULL;
7449 
7450     int cpu_time_limit = 0;
7451     int cpu_priority = 0;
7452 
7453     apr_int64_t memory_limit = 0;
7454     apr_int64_t virtual_memory_limit = 0;
7455 
7456     uid_t uid;
7457     uid_t gid;
7458 
7459     const char *groups_list = NULL;
7460     int groups_count = 0;
7461     gid_t *groups = NULL;
7462 
7463     int server_metrics = 0;
7464 
7465     const char *newrelic_config_file = NULL;
7466     const char *newrelic_environment = NULL;
7467 
7468     const char *option = NULL;
7469     const char *value = NULL;
7470 
7471     WSGIProcessGroup *entries = NULL;
7472     WSGIProcessGroup *entry = NULL;
7473 
7474     int i;
7475 
7476     /*
7477      * Set the defaults for user/group from values
7478      * defined for the User/Group directives in main
7479      * Apache configuration.
7480      */
7481 
7482     uid = ap_unixd_config.user_id;
7483     user = ap_unixd_config.user_name;
7484 
7485     gid = ap_unixd_config.group_id;
7486 
7487     /* Now parse options for directive. */
7488 
7489     name = ap_getword_conf(cmd->pool, &args);
7490 
7491     if (!name || !*name)
7492         return "Name of WSGI daemon process not supplied.";
7493 
7494     while (*args) {
7495         if (wsgi_parse_option(cmd->pool, &args, &option,
7496                               &value) != APR_SUCCESS) {
7497             return "Invalid option to WSGI daemon process definition.";
7498         }
7499 
7500         if (!strcmp(option, "user")) {
7501             if (!*value)
7502                 return "Invalid user for WSGI daemon process.";
7503 
7504             user = value;
7505             uid = ap_uname2id(user);
7506             if (uid == 0)
7507                 return "WSGI process blocked from running as root.";
7508 
7509             if (*user == '#') {
7510                 struct passwd *entry = NULL;
7511 
7512                 if ((entry = getpwuid(uid)) == NULL)
7513                     return "Couldn't determine user name from uid.";
7514 
7515                 user = entry->pw_name;
7516             }
7517         }
7518         else if (!strcmp(option, "group")) {
7519             if (!*value)
7520                 return "Invalid group for WSGI daemon process.";
7521 
7522             group = value;
7523             gid = ap_gname2id(group);
7524         }
7525         else if (!strcmp(option, "supplementary-groups")) {
7526             groups_list = value;
7527         }
7528         else if (!strcmp(option, "processes")) {
7529             if (!*value)
7530                 return "Invalid process count for WSGI daemon process.";
7531 
7532             processes = atoi(value);
7533             if (processes < 1)
7534                 return "Invalid process count for WSGI daemon process.";
7535 
7536             multiprocess = 1;
7537         }
7538         else if (!strcmp(option, "threads")) {
7539             if (!*value)
7540                 return "Invalid thread count for WSGI daemon process.";
7541 
7542             threads = atoi(value);
7543             if (threads < 0 || threads >= WSGI_STACK_LAST-1)
7544                 return "Invalid thread count for WSGI daemon process.";
7545         }
7546         else if (!strcmp(option, "umask")) {
7547             if (!*value)
7548                 return "Invalid umask for WSGI daemon process.";
7549 
7550             errno = 0;
7551             umask = strtol(value, (char **)&value, 8);
7552 
7553             if (*value || errno == ERANGE || umask < 0)
7554                 return "Invalid umask for WSGI daemon process.";
7555         }
7556         else if (!strcmp(option, "chroot")) {
7557             if (geteuid())
7558                 return "Cannot chroot WSGI daemon process when not root.";
7559 
7560             if (*value != '/')
7561                 return "Invalid chroot directory for WSGI daemon process.";
7562 
7563             root = value;
7564         }
7565         else if (!strcmp(option, "home")) {
7566             if (*value != '/')
7567                 return "Invalid home directory for WSGI daemon process.";
7568 
7569             home = value;
7570         }
7571         else if (!strcmp(option, "lang")) {
7572             lang = value;
7573         }
7574         else if (!strcmp(option, "locale")) {
7575             locale = value;
7576         }
7577         else if (!strcmp(option, "python-home")) {
7578             python_home = value;
7579         }
7580         else if (!strcmp(option, "python-path")) {
7581             python_path = value;
7582         }
7583         else if (!strcmp(option, "python-eggs")) {
7584             python_eggs = value;
7585         }
7586 #if (APR_MAJOR_VERSION >= 1)
7587         else if (!strcmp(option, "stack-size")) {
7588             if (!*value)
7589                 return "Invalid stack size for WSGI daemon process.";
7590 
7591             stack_size = atoi(value);
7592             if (stack_size <= 0)
7593                 return "Invalid stack size for WSGI daemon process.";
7594         }
7595 #endif
7596         else if (!strcmp(option, "maximum-requests")) {
7597             if (!*value)
7598                 return "Invalid request count for WSGI daemon process.";
7599 
7600             maximum_requests = atoi(value);
7601             if (maximum_requests < 0)
7602                 return "Invalid request count for WSGI daemon process.";
7603         }
7604         else if (!strcmp(option, "startup-timeout")) {
7605             if (!*value)
7606                 return "Invalid startup timeout for WSGI daemon process.";
7607 
7608             startup_timeout = atoi(value);
7609             if (startup_timeout < 0)
7610                 return "Invalid startup timeout for WSGI daemon process.";
7611         }
7612         else if (!strcmp(option, "shutdown-timeout")) {
7613             if (!*value)
7614                 return "Invalid shutdown timeout for WSGI daemon process.";
7615 
7616             shutdown_timeout = atoi(value);
7617             if (shutdown_timeout < 0)
7618                 return "Invalid shutdown timeout for WSGI daemon process.";
7619         }
7620         else if (!strcmp(option, "deadlock-timeout")) {
7621             if (!*value)
7622                 return "Invalid deadlock timeout for WSGI daemon process.";
7623 
7624             deadlock_timeout = atoi(value);
7625             if (deadlock_timeout < 0)
7626                 return "Invalid deadlock timeout for WSGI daemon process.";
7627         }
7628         else if (!strcmp(option, "inactivity-timeout")) {
7629             if (!*value)
7630                 return "Invalid inactivity timeout for WSGI daemon process.";
7631 
7632             inactivity_timeout = atoi(value);
7633             if (inactivity_timeout < 0)
7634                 return "Invalid inactivity timeout for WSGI daemon process.";
7635         }
7636         else if (!strcmp(option, "request-timeout")) {
7637             if (!*value)
7638                 return "Invalid request timeout for WSGI daemon process.";
7639 
7640             request_timeout = atoi(value);
7641             if (request_timeout < 0)
7642                 return "Invalid request timeout for WSGI daemon process.";
7643         }
7644         else if (!strcmp(option, "graceful-timeout")) {
7645             if (!*value)
7646                 return "Invalid graceful timeout for WSGI daemon process.";
7647 
7648             graceful_timeout = atoi(value);
7649             if (graceful_timeout < 0)
7650                 return "Invalid graceful timeout for WSGI daemon process.";
7651         }
7652         else if (!strcmp(option, "eviction-timeout")) {
7653             if (!*value)
7654                 return "Invalid eviction timeout for WSGI daemon process.";
7655 
7656             eviction_timeout = atoi(value);
7657             if (eviction_timeout < 0)
7658                 return "Invalid eviction timeout for WSGI daemon process.";
7659         }
7660         else if (!strcmp(option, "restart-interval")) {
7661             if (!*value)
7662                 return "Invalid restart interval for WSGI daemon process.";
7663 
7664             restart_interval = atoi(value);
7665             if (restart_interval < 0)
7666                 return "Invalid restart interval for WSGI daemon process.";
7667         }
7668         else if (!strcmp(option, "connect-timeout")) {
7669             if (!*value)
7670                 return "Invalid connect timeout for WSGI daemon process.";
7671 
7672             connect_timeout = atoi(value);
7673             if (connect_timeout < 0)
7674                 return "Invalid connect timeout for WSGI daemon process.";
7675         }
7676         else if (!strcmp(option, "socket-timeout")) {
7677             if (!*value)
7678                 return "Invalid socket timeout for WSGI daemon process.";
7679 
7680             socket_timeout = atoi(value);
7681             if (socket_timeout < 0)
7682                 return "Invalid socket timeout for WSGI daemon process.";
7683         }
7684         else if (!strcmp(option, "queue-timeout")) {
7685             if (!*value)
7686                 return "Invalid queue timeout for WSGI daemon process.";
7687 
7688             queue_timeout = atoi(value);
7689             if (queue_timeout < 0)
7690                 return "Invalid queue timeout for WSGI daemon process.";
7691         }
7692         else if (!strcmp(option, "listen-backlog")) {
7693             if (!*value)
7694                 return "Invalid listen backlog for WSGI daemon process.";
7695 
7696             listen_backlog = atoi(value);
7697             if (listen_backlog < 0)
7698                 return "Invalid listen backlog for WSGI daemon process.";
7699         }
7700         else if (!strcmp(option, "display-name")) {
7701             display_name = value;
7702         }
7703         else if (!strcmp(option, "send-buffer-size")) {
7704             if (!*value)
7705                 return "Invalid send buffer size for WSGI daemon process.";
7706 
7707             send_buffer_size = atoi(value);
7708             if (send_buffer_size < 512 && send_buffer_size != 0) {
7709                 return "Send buffer size must be >= 512 bytes, "
7710                        "or 0 for system default.";
7711             }
7712         }
7713         else if (!strcmp(option, "receive-buffer-size")) {
7714             if (!*value)
7715                 return "Invalid receive buffer size for WSGI daemon process.";
7716 
7717             recv_buffer_size = atoi(value);
7718             if (recv_buffer_size < 512 && recv_buffer_size != 0) {
7719                 return "Receive buffer size must be >= 512 bytes, "
7720                        "or 0 for system default.";
7721             }
7722         }
7723         else if (!strcmp(option, "header-buffer-size")) {
7724             if (!*value)
7725                 return "Invalid header buffer size for WSGI daemon process.";
7726 
7727             header_buffer_size = atoi(value);
7728             if (header_buffer_size < 8192 && header_buffer_size != 0) {
7729                 return "Header buffer size must be >= 8192 bytes, "
7730                        "or 0 for default.";
7731             }
7732         }
7733         else if (!strcmp(option, "response-buffer-size")) {
7734             if (!*value)
7735                 return "Invalid response buffer size for WSGI daemon process.";
7736 
7737             response_buffer_size = atoi(value);
7738             if (response_buffer_size < 65536 && response_buffer_size != 0) {
7739                 return "Response buffer size must be >= 65536 bytes, "
7740                        "or 0 for default.";
7741             }
7742         }
7743         else if (!strcmp(option, "response-socket-timeout")) {
7744             if (!*value)
7745                 return "Invalid response socket timeout for WSGI daemon process.";
7746 
7747             response_socket_timeout = atoi(value);
7748             if (response_socket_timeout < 0)
7749                 return "Invalid response socket timeout for WSGI daemon process.";
7750         }
7751         else if (!strcmp(option, "socket-user")) {
7752             uid_t socket_uid;
7753 
7754             if (!*value)
7755                 return "Invalid socket user for WSGI daemon process.";
7756 
7757             socket_uid = ap_uname2id(value);
7758 
7759             if (*value == '#') {
7760                 struct passwd *entry = NULL;
7761 
7762                 if ((entry = getpwuid(socket_uid)) == NULL)
7763                     return "Couldn't determine user name from socket user.";
7764 
7765                 value = entry->pw_name;
7766             }
7767 
7768             socket_user = value;
7769         }
7770         else if (!strcmp(option, "script-user")) {
7771             uid_t script_uid;
7772 
7773             if (!*value)
7774                 return "Invalid script user for WSGI daemon process.";
7775 
7776             script_uid = ap_uname2id(value);
7777 
7778             if (*value == '#') {
7779                 struct passwd *entry = NULL;
7780 
7781                 if ((entry = getpwuid(script_uid)) == NULL)
7782                     return "Couldn't determine uid from script user.";
7783 
7784                 value = entry->pw_name;
7785             }
7786 
7787             script_user = value;
7788         }
7789         else if (!strcmp(option, "script-group")) {
7790             gid_t script_gid;
7791 
7792             if (!*value)
7793                 return "Invalid script group for WSGI daemon process.";
7794 
7795             script_gid = ap_gname2id(value);
7796 
7797             if (*value == '#') {
7798                 struct group *entry = NULL;
7799 
7800                 if ((entry = getgrgid(script_gid)) == NULL)
7801                     return "Couldn't determine gid from script group.";
7802 
7803                 value = entry->gr_name;
7804             }
7805 
7806             script_group = value;
7807         }
7808         else if (!strcmp(option, "cpu-time-limit")) {
7809             if (!*value)
7810                 return "Invalid CPU time limit for WSGI daemon process.";
7811 
7812             cpu_time_limit = atoi(value);
7813             if (cpu_time_limit < 0)
7814                 return "Invalid CPU time limit for WSGI daemon process.";
7815         }
7816         else if (!strcmp(option, "cpu-priority")) {
7817             if (!*value)
7818                 return "Invalid CPU priority for WSGI daemon process.";
7819 
7820             cpu_priority = atoi(value);
7821         }
7822         else if (!strcmp(option, "memory-limit")) {
7823             if (!*value)
7824                 return "Invalid memory limit for WSGI daemon process.";
7825 
7826             memory_limit = apr_atoi64(value);
7827             if (memory_limit < 0)
7828                 return "Invalid memory limit for WSGI daemon process.";
7829         }
7830         else if (!strcmp(option, "virtual-memory-limit")) {
7831             if (!*value)
7832                 return "Invalid virtual memory limit for WSGI daemon process.";
7833 
7834             virtual_memory_limit = apr_atoi64(value);
7835             if (virtual_memory_limit < 0)
7836                 return "Invalid virtual memory limit for WSGI daemon process.";
7837         }
7838         else if (!strcmp(option, "server-metrics")) {
7839             if (!*value)
7840                 return "Invalid server metrics flag for WSGI daemon process.";
7841 
7842             if (strcasecmp(value, "Off") == 0)
7843                 server_metrics = 0;
7844             else if (strcasecmp(value, "On") == 0)
7845                 server_metrics = 1;
7846             else
7847                 return "Invalid server metrics flag for WSGI daemon process.";
7848         }
7849         else if (!strcmp(option, "newrelic-config-file")) {
7850             newrelic_config_file = value;
7851         }
7852         else if (!strcmp(option, "newrelic-environment")) {
7853             newrelic_environment = value;
7854         }
7855         else
7856             return "Invalid option to WSGI daemon process definition.";
7857     }
7858 
7859     if (script_user && script_group)
7860         return "Only one of script-user and script-group allowed.";
7861 
7862     if (groups_list) {
7863         const char *group_name = NULL;
7864         long groups_maximum = NGROUPS_MAX;
7865         const char *items = NULL;
7866 
7867 #ifdef _SC_NGROUPS_MAX
7868         groups_maximum = sysconf(_SC_NGROUPS_MAX);
7869         if (groups_maximum < 0)
7870             groups_maximum = NGROUPS_MAX;
7871 #endif
7872         groups = (gid_t *)apr_pcalloc(cmd->pool,
7873                                       groups_maximum*sizeof(groups[0]));
7874 
7875         groups[groups_count++] = gid;
7876 
7877         items = groups_list;
7878         group_name = ap_getword(cmd->pool, &items, ',');
7879 
7880         while (group_name && *group_name) {
7881             if (groups_count >= groups_maximum)
7882                 return "Too many supplementary groups WSGI daemon process";
7883 
7884             groups[groups_count++] = ap_gname2id(group_name);
7885             group_name = ap_getword(cmd->pool, &items, ',');
7886         }
7887     }
7888 
7889     if (!wsgi_daemon_list) {
7890         wsgi_daemon_list = apr_array_make(cmd->pool, 20,
7891                                           sizeof(WSGIProcessGroup));
7892     }
7893 
7894     entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
7895 
7896     for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
7897         entry = &entries[i];
7898 
7899         if (!strcmp(entry->name, name))
7900             return "Name duplicates previous WSGI daemon definition.";
7901     }
7902 
7903     wsgi_daemon_count++;
7904 
7905     entry = (WSGIProcessGroup *)apr_array_push(wsgi_daemon_list);
7906 
7907     entry->server = cmd->server;
7908 
7909     entry->random = random();
7910     entry->id = wsgi_daemon_count;
7911 
7912     entry->name = apr_pstrdup(cmd->pool, name);
7913     entry->user = apr_pstrdup(cmd->pool, user);
7914     entry->group = apr_pstrdup(cmd->pool, group);
7915 
7916     entry->uid = uid;
7917     entry->gid = gid;
7918 
7919     entry->groups_list = groups_list;
7920     entry->groups_count = groups_count;
7921     entry->groups = groups;
7922 
7923     entry->processes = processes;
7924     entry->multiprocess = multiprocess;
7925     entry->threads = threads;
7926 
7927     entry->umask = umask;
7928     entry->root = root;
7929     entry->home = home;
7930 
7931     entry->lang = lang;
7932     entry->locale = locale;
7933 
7934     entry->python_home = python_home;
7935     entry->python_path = python_path;
7936     entry->python_eggs = python_eggs;
7937 
7938     entry->stack_size = stack_size;
7939     entry->maximum_requests = maximum_requests;
7940     entry->shutdown_timeout = shutdown_timeout;
7941     entry->startup_timeout = apr_time_from_sec(startup_timeout);
7942     entry->deadlock_timeout = apr_time_from_sec(deadlock_timeout);
7943     entry->inactivity_timeout = apr_time_from_sec(inactivity_timeout);
7944     entry->request_timeout = apr_time_from_sec(request_timeout);
7945     entry->graceful_timeout = apr_time_from_sec(graceful_timeout);
7946     entry->eviction_timeout = apr_time_from_sec(eviction_timeout);
7947     entry->restart_interval = apr_time_from_sec(restart_interval);
7948     entry->connect_timeout = apr_time_from_sec(connect_timeout);
7949     entry->socket_timeout = apr_time_from_sec(socket_timeout);
7950     entry->queue_timeout = apr_time_from_sec(queue_timeout);
7951 
7952     entry->socket_user = apr_pstrdup(cmd->pool, socket_user);
7953 
7954     entry->listen_backlog = listen_backlog;
7955 
7956     entry->display_name = display_name;
7957 
7958     entry->send_buffer_size = send_buffer_size;
7959     entry->recv_buffer_size = recv_buffer_size;
7960     entry->header_buffer_size = header_buffer_size;
7961     entry->response_buffer_size = response_buffer_size;
7962 
7963     if (response_socket_timeout == 0)
7964         response_socket_timeout = socket_timeout;
7965 
7966     entry->response_socket_timeout = apr_time_from_sec(response_socket_timeout);
7967 
7968     entry->script_user = script_user;
7969     entry->script_group = script_group;
7970 
7971     entry->cpu_time_limit = cpu_time_limit;
7972     entry->cpu_priority = cpu_priority;
7973 
7974     entry->memory_limit = memory_limit;
7975     entry->virtual_memory_limit = virtual_memory_limit;
7976 
7977     entry->server_metrics = server_metrics;
7978 
7979     entry->newrelic_config_file = newrelic_config_file;
7980     entry->newrelic_environment = newrelic_environment;
7981 
7982     entry->listener_fd = -1;
7983 
7984     return NULL;
7985 }
7986 
wsgi_set_socket_prefix(cmd_parms * cmd,void * mconfig,const char * arg)7987 static const char *wsgi_set_socket_prefix(cmd_parms *cmd, void *mconfig,
7988                                          const char *arg)
7989 {
7990     const char *error = NULL;
7991     WSGIServerConfig *sconfig = NULL;
7992 
7993     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
7994     if (error != NULL)
7995         return error;
7996 
7997     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
7998 
7999     sconfig->socket_prefix = ap_server_root_relative(cmd->pool, arg);
8000 
8001     if (!sconfig->socket_prefix) {
8002         return apr_pstrcat(cmd->pool, "Invalid WSGISocketPrefix '",
8003                            arg, "'.", NULL);
8004     }
8005 
8006     return NULL;
8007 }
8008 
wsgi_set_socket_rotation(cmd_parms * cmd,void * mconfig,const char * f)8009 static const char *wsgi_set_socket_rotation(cmd_parms *cmd, void *mconfig,
8010                                             const char *f)
8011 {
8012     const char *error = NULL;
8013     WSGIServerConfig *sconfig = NULL;
8014 
8015     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
8016     if (error != NULL)
8017         return error;
8018 
8019     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
8020 
8021     if (strcasecmp(f, "Off") == 0)
8022         sconfig->socket_rotation = 0;
8023     else if (strcasecmp(f, "On") == 0)
8024         sconfig->socket_rotation = 1;
8025     else
8026         return "WSGISocketRotation must be one of: Off | On";
8027 
8028     return NULL;
8029 }
8030 
8031 static const char wsgi_valid_accept_mutex_string[] =
8032     "Valid accept mutex mechanisms for this platform are: default"
8033 #if APR_HAS_FLOCK_SERIALIZE
8034     ", flock"
8035 #endif
8036 #if APR_HAS_FCNTL_SERIALIZE
8037     ", fcntl"
8038 #endif
8039 #if APR_HAS_SYSVSEM_SERIALIZE
8040     ", sysvsem"
8041 #endif
8042 #if APR_HAS_POSIXSEM_SERIALIZE
8043     ", posixsem"
8044 #endif
8045 #if APR_HAS_PROC_PTHREAD_SERIALIZE
8046     ", pthread"
8047 #endif
8048     ".";
8049 
wsgi_set_accept_mutex(cmd_parms * cmd,void * mconfig,const char * arg)8050 static const char *wsgi_set_accept_mutex(cmd_parms *cmd, void *mconfig,
8051                                          const char *arg)
8052 {
8053     const char *error = NULL;
8054     WSGIServerConfig *sconfig = NULL;
8055 
8056     error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
8057     if (error != NULL)
8058         return error;
8059 
8060     sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
8061 
8062 #if !defined(AP_ACCEPT_MUTEX_TYPE)
8063     sconfig->lock_mechanism = ap_accept_lock_mech;
8064 #else
8065     sconfig->lock_mechanism = APR_LOCK_DEFAULT;
8066 #endif
8067 
8068     if (!strcasecmp(arg, "default")) {
8069         sconfig->lock_mechanism = APR_LOCK_DEFAULT;
8070     }
8071 #if APR_HAS_FLOCK_SERIALIZE
8072     else if (!strcasecmp(arg, "flock")) {
8073         sconfig->lock_mechanism = APR_LOCK_FLOCK;
8074     }
8075 #endif
8076 #if APR_HAS_FCNTL_SERIALIZE
8077     else if (!strcasecmp(arg, "fcntl")) {
8078         sconfig->lock_mechanism = APR_LOCK_FCNTL;
8079     }
8080 #endif
8081 #if APR_HAS_SYSVSEM_SERIALIZE
8082     else if (!strcasecmp(arg, "sysvsem")) {
8083         sconfig->lock_mechanism = APR_LOCK_SYSVSEM;
8084     }
8085 #endif
8086 #if APR_HAS_POSIXSEM_SERIALIZE
8087     else if (!strcasecmp(arg, "posixsem")) {
8088         sconfig->lock_mechanism = APR_LOCK_POSIXSEM;
8089     }
8090 #endif
8091 #if APR_HAS_PROC_PTHREAD_SERIALIZE
8092     else if (!strcasecmp(arg, "pthread")) {
8093         sconfig->lock_mechanism = APR_LOCK_PROC_PTHREAD;
8094     }
8095 #endif
8096     else {
8097         return apr_pstrcat(cmd->pool, "Accept mutex lock mechanism '", arg,
8098                            "' is invalid. ", wsgi_valid_accept_mutex_string,
8099                            NULL);
8100     }
8101 
8102     return NULL;
8103 }
8104 
8105 static apr_file_t *wsgi_signal_pipe_in = NULL;
8106 static apr_file_t *wsgi_signal_pipe_out = NULL;
8107 
wsgi_signal_handler(int signum)8108 static void wsgi_signal_handler(int signum)
8109 {
8110     apr_size_t nbytes = 1;
8111 
8112     if (wsgi_daemon_pid != 0 && wsgi_daemon_pid != getpid())
8113         exit(-1);
8114 
8115     if (signum == AP_SIG_GRACEFUL) {
8116         apr_file_write(wsgi_signal_pipe_out, "G", &nbytes);
8117         apr_file_flush(wsgi_signal_pipe_out);
8118     }
8119     else if (signum == SIGXCPU) {
8120         if (!wsgi_graceful_timeout)
8121             wsgi_daemon_shutdown++;
8122 
8123         apr_file_write(wsgi_signal_pipe_out, "C", &nbytes);
8124         apr_file_flush(wsgi_signal_pipe_out);
8125     }
8126     else {
8127         wsgi_daemon_shutdown++;
8128 
8129         apr_file_write(wsgi_signal_pipe_out, "S", &nbytes);
8130         apr_file_flush(wsgi_signal_pipe_out);
8131     }
8132 }
8133 
wsgi_exit_daemon_process(int status)8134 static void wsgi_exit_daemon_process(int status)
8135 {
8136     if (wsgi_server && wsgi_daemon_group) {
8137         ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
8138                      "mod_wsgi (pid=%d): Exiting process '%s'.", getpid(),
8139                      wsgi_daemon_group);
8140     }
8141 
8142     exit(status);
8143 }
8144 
8145 static int wsgi_start_process(apr_pool_t *p, WSGIDaemonProcess *daemon);
8146 
wsgi_manage_process(int reason,void * data,apr_wait_t status)8147 static void wsgi_manage_process(int reason, void *data, apr_wait_t status)
8148 {
8149     WSGIDaemonProcess *daemon = data;
8150 
8151     switch (reason) {
8152 
8153         /* Child daemon process has died. */
8154 
8155         case APR_OC_REASON_DEATH: {
8156             int mpm_state;
8157             int stopping;
8158 
8159             /*
8160              * Determine if Apache is being shutdown or not and
8161              * if it is not being shutdown, we will need to
8162              * restart the child daemon process that has died.
8163              * If MPM doesn't support query assume that child
8164              * daemon process shouldn't be restarted. Both
8165              * prefork and worker MPMs support this query so
8166              * should always be okay.
8167              */
8168 
8169             stopping = 1;
8170 
8171             if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state) == APR_SUCCESS
8172                 && mpm_state != AP_MPMQ_STOPPING) {
8173                 stopping = 0;
8174             }
8175 
8176             if (!stopping) {
8177                 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8178                              wsgi_server, "mod_wsgi (pid=%d): "
8179                              "Process '%s' has died, deregister and "
8180                              "restart it.", daemon->process.pid,
8181                              daemon->group->name);
8182 
8183                 if (WIFEXITED(status)) {
8184                     ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8185                              wsgi_server, "mod_wsgi (pid=%d): "
8186                              "Process '%s' terminated normally, exit code %d",
8187                              daemon->process.pid, daemon->group->name,
8188                              WEXITSTATUS(status));
8189                 }
8190                 else if (WIFSIGNALED(status)) {
8191                     ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8192                              wsgi_server, "mod_wsgi (pid=%d): "
8193                              "Process '%s' terminated by signal %d",
8194                              daemon->process.pid, daemon->group->name,
8195                              WTERMSIG(status));
8196                 }
8197             }
8198             else {
8199                 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8200                              wsgi_server, "mod_wsgi (pid=%d): "
8201                              "Process '%s' has died but server is "
8202                              "being stopped, deregister it.",
8203                              daemon->process.pid, daemon->group->name);
8204             }
8205 
8206             /* Deregister existing process so we stop watching it. */
8207 
8208             apr_proc_other_child_unregister(daemon);
8209 
8210             /* Now restart process if not shutting down. */
8211 
8212             if (!stopping)
8213                 wsgi_start_process(wsgi_parent_pool, daemon);
8214 
8215             break;
8216         }
8217 
8218         /* Apache is being restarted or shutdown. */
8219 
8220         case APR_OC_REASON_RESTART: {
8221 
8222             ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8223                          wsgi_server, "mod_wsgi (pid=%d): "
8224                          "Process '%s' to be deregistered, as server is "
8225                          "restarting or being shutdown.",
8226                          daemon->process.pid, daemon->group->name);
8227 
8228             /* Deregister existing process so we stop watching it. */
8229 
8230             apr_proc_other_child_unregister(daemon);
8231 
8232             break;
8233         }
8234 
8235         /* Child daemon process vanished. */
8236 
8237         case APR_OC_REASON_LOST: {
8238 
8239             ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8240                          wsgi_server, "mod_wsgi (pid=%d): "
8241                          "Process '%s' appears to have been lost, "
8242                          "deregister and restart it.",
8243                          daemon->process.pid, daemon->group->name);
8244 
8245             /* Deregister existing process so we stop watching it. */
8246 
8247             apr_proc_other_child_unregister(daemon);
8248 
8249             /* Restart the child daemon process that has died. */
8250 
8251             wsgi_start_process(wsgi_parent_pool, daemon);
8252 
8253             break;
8254         }
8255 
8256         /* Call to unregister the process. */
8257 
8258         case APR_OC_REASON_UNREGISTER: {
8259 
8260             /* Nothing to do at present. */
8261 
8262             ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8263                          wsgi_server, "mod_wsgi (pid=%d): "
8264                          "Process '%s' has been deregistered and will "
8265                          "no longer be monitored.", daemon->process.pid,
8266                          daemon->group->name);
8267 
8268             break;
8269         }
8270 
8271         default: {
8272             ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8273                          wsgi_server, "mod_wsgi (pid=%d): "
8274                          "Process '%s' targeted by unexpected event %d.",
8275                          daemon->process.pid, daemon->group->name, reason);
8276         }
8277     }
8278 }
8279 
wsgi_setup_daemon_name(WSGIDaemonProcess * daemon,apr_pool_t * p)8280 static void wsgi_setup_daemon_name(WSGIDaemonProcess *daemon, apr_pool_t *p)
8281 {
8282     const char *display_name = NULL;
8283 
8284 #if !(defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__))
8285     long slen = 0;
8286     long dlen = 0;
8287 
8288     char *argv0 = NULL;
8289 #endif
8290 
8291     display_name = daemon->group->display_name;
8292 
8293     if (!display_name)
8294         return;
8295 
8296     if (!strcmp(display_name, "%{GROUP}")) {
8297         display_name = apr_pstrcat(p, "(wsgi:", daemon->group->name,
8298                                    ")", NULL);
8299     }
8300 
8301     /*
8302      * Only argv[0] is guaranteed to be the real things as MPM
8303      * modules may make modifications to subsequent arguments.
8304      * Thus can only replace the argv[0] value. Because length
8305      * is restricted, need to truncate display name if too long.
8306      */
8307 
8308 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
8309     setproctitle("%s", display_name);
8310 #else
8311     argv0 = (char*)wsgi_server->process->argv[0];
8312 
8313     dlen = strlen(argv0);
8314     slen = strlen(display_name);
8315 
8316     memset(argv0, ' ', dlen);
8317 
8318     if (slen < dlen)
8319         memcpy(argv0, display_name, slen);
8320     else
8321         memcpy(argv0, display_name, dlen);
8322 #endif
8323 }
8324 
wsgi_setup_access(WSGIDaemonProcess * daemon)8325 static int wsgi_setup_access(WSGIDaemonProcess *daemon)
8326 {
8327     /* Change to chroot environment. */
8328 
8329     if (daemon->group->root) {
8330         if (chroot(daemon->group->root) == -1) {
8331             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8332                          "mod_wsgi (pid=%d): Unable to change root "
8333                          "directory to '%s'.", getpid(), daemon->group->root);
8334 
8335             return -1;
8336         }
8337     }
8338 
8339     /* We don't need to switch user/group if not root. */
8340 
8341     if (geteuid() == 0) {
8342         /* Setup the daemon process real and effective group. */
8343 
8344         if (setgid(daemon->group->gid) == -1) {
8345             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8346                          "mod_wsgi (pid=%d): Unable to set group id "
8347                          "to gid=%u.", getpid(),
8348                          (unsigned)daemon->group->gid);
8349 
8350             return -1;
8351         }
8352         else {
8353             if (daemon->group->groups) {
8354                 if (setgroups(daemon->group->groups_count,
8355                               daemon->group->groups) == -1) {
8356                     ap_log_error(APLOG_MARK, APLOG_ALERT, errno,
8357                                  wsgi_server, "mod_wsgi (pid=%d): Unable "
8358                                  "to set supplementary groups for uname=%s "
8359                                  "of '%s'.", getpid(), daemon->group->user,
8360                                  daemon->group->groups_list);
8361 
8362                     return -1;
8363                 }
8364             }
8365             else if (initgroups(daemon->group->user,
8366                      daemon->group->gid) == -1) {
8367                 ap_log_error(APLOG_MARK, APLOG_ALERT, errno,
8368                              wsgi_server, "mod_wsgi (pid=%d): Unable "
8369                              "to set groups for uname=%s and gid=%u.",
8370                              getpid(), daemon->group->user,
8371                              (unsigned)daemon->group->gid);
8372 
8373                 return -1;
8374             }
8375         }
8376 
8377         /* Setup the daemon process real and effective user. */
8378 
8379         if (setuid(daemon->group->uid) == -1) {
8380             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8381                          "mod_wsgi (pid=%d): Unable to change to uid=%ld.",
8382                          getpid(), (long)daemon->group->uid);
8383 
8384             /*
8385              * On true UNIX systems this should always succeed at
8386              * this point. With certain Linux kernel versions though
8387              * we can get back EAGAIN where the target user had
8388              * reached their process limit. In that case will be left
8389              * running as wrong user. Just exit on all failures to be
8390              * safe. Don't die immediately to avoid a fork bomb.
8391              *
8392              * We could just return -1 here and let the caller do the
8393              * sleep() and exit() but this failure is critical enough
8394              * that we still do it here so it is obvious that the issue
8395              * is being addressed.
8396              */
8397 
8398             ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server,
8399                          "mod_wsgi (pid=%d): Failure to configure the "
8400                          "daemon process correctly and process left in "
8401                          "unspecified state. Restarting daemon process "
8402                          "after delay.", getpid());
8403 
8404             sleep(20);
8405 
8406             wsgi_exit_daemon_process(-1);
8407 
8408             return -1;
8409         }
8410     }
8411 
8412     /*
8413      * Setup the working directory for the process. It is either set to
8414      * what the 'home' option explicitly provides, or the home home
8415      * directory of the user, where it has been set to be different to
8416      * the user that Apache's own processes run as.
8417      */
8418 
8419     if (daemon->group->home) {
8420         if (chdir(daemon->group->home) == -1) {
8421             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8422                          "mod_wsgi (pid=%d): Unable to change working "
8423                          "directory to '%s'.", getpid(), daemon->group->home);
8424 
8425             return -1;
8426         }
8427     }
8428     else if (geteuid() != ap_unixd_config.user_id) {
8429         struct passwd *pwent;
8430 
8431         pwent = getpwuid(geteuid());
8432 
8433         if (pwent) {
8434             if (chdir(pwent->pw_dir) == -1) {
8435                 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8436                              "mod_wsgi (pid=%d): Unable to change working "
8437                              "directory to home directory '%s' for uid=%ld.",
8438                              getpid(), pwent->pw_dir, (long)geteuid());
8439 
8440             return -1;
8441             }
8442         }
8443         else {
8444             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8445                          "mod_wsgi (pid=%d): Unable to determine home "
8446                          "directory for uid=%ld.", getpid(), (long)geteuid());
8447 
8448             return -1;
8449         }
8450     }
8451 
8452     /* Setup the umask for the effective user. */
8453 
8454     if (daemon->group->umask != -1)
8455         umask(daemon->group->umask);
8456 
8457     /*
8458      * Linux prevents generation of core dumps after setuid()
8459      * has been used. Attempt to reenable ability to dump core
8460      * so that the CoreDumpDirectory directive still works.
8461      */
8462 
8463 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
8464     /* This applies to Linux 2.4 and later. */
8465 
8466     if (ap_coredumpdir_configured) {
8467         if (prctl(PR_SET_DUMPABLE, 1)) {
8468             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8469                     "mod_wsgi (pid=%d): Set dumpable failed. This child "
8470                     "will not coredump after software errors.", getpid());
8471         }
8472     }
8473 #endif
8474 
8475     return 0;
8476 }
8477 
wsgi_setup_socket(WSGIProcessGroup * process)8478 static int wsgi_setup_socket(WSGIProcessGroup *process)
8479 {
8480     int sockfd = -1;
8481     struct sockaddr_un addr;
8482     mode_t omask;
8483     int rc;
8484 
8485     int sendsz = process->send_buffer_size;
8486     int recvsz = process->recv_buffer_size;
8487 
8488     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
8489                  "mod_wsgi (pid=%d): Socket for '%s' is '%s'.",
8490                  getpid(), process->name, process->socket_path);
8491 
8492     if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
8493         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8494                      "mod_wsgi (pid=%d): Couldn't create unix domain "
8495                      "socket.", getpid());
8496         return -1;
8497     }
8498 
8499 #ifdef SO_SNDBUF
8500     if (sendsz) {
8501         if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
8502                        (void *)&sendsz, sizeof(sendsz)) == -1) {
8503             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, wsgi_server,
8504                          "mod_wsgi (pid=%d): Failed to set send buffer "
8505                          "size on daemon process socket.", getpid());
8506         }
8507     }
8508 #endif
8509 #ifdef SO_RCVBUF
8510     if (recvsz) {
8511         if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
8512                        (void *)&recvsz, sizeof(recvsz)) == -1) {
8513             ap_log_error(APLOG_MARK, APLOG_WARNING, errno, wsgi_server,
8514                          "mod_wsgi (pid=%d): Failed to set receive buffer "
8515                          "size on daemon process socket.", getpid());
8516         }
8517     }
8518 #endif
8519 
8520     if (strlen(process->socket_path) > sizeof(addr.sun_path)) {
8521         ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server,
8522                      "mod_wsgi (pid=%d): Length of path for daemon process "
8523                      "socket exceeds maxmimum allowed value and will be "
8524                      "truncated, resulting in likely failure to bind the "
8525                      "socket, or other later related failure.", getpid());
8526     }
8527 
8528     memset(&addr, 0, sizeof(addr));
8529     addr.sun_family = AF_UNIX;
8530     apr_cpystrn(addr.sun_path, process->socket_path, sizeof(addr.sun_path));
8531 
8532     omask = umask(0077);
8533     rc = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
8534 
8535     if (rc < 0 && errno == EADDRINUSE) {
8536         ap_log_error(APLOG_MARK, APLOG_WARNING, errno, wsgi_server,
8537                      "mod_wsgi (pid=%d): Removing stale unix domain "
8538                      "socket '%s'.", getpid(), process->socket_path);
8539 
8540         unlink(process->socket_path);
8541 
8542         rc = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
8543     }
8544 
8545     umask(omask);
8546 
8547     if (rc < 0) {
8548         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8549                      "mod_wsgi (pid=%d): Couldn't bind unix domain "
8550                      "socket '%s'.", getpid(), process->socket_path);
8551 
8552         close(sockfd);
8553 
8554         return -1;
8555     }
8556 
8557     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
8558                  "mod_wsgi (pid=%d): Listen backlog for socket '%s' is '%d'.",
8559                  getpid(), process->socket_path, process->listen_backlog);
8560 
8561     if (listen(sockfd, process->listen_backlog) < 0) {
8562         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8563                      "mod_wsgi (pid=%d): Couldn't listen on unix domain "
8564                      "socket.", getpid());
8565 
8566         close(sockfd);
8567 
8568         return -1;
8569     }
8570 
8571     /*
8572      * Set the ownership of the UNIX listener socket. This would
8573      * normally be the Apache user that the Apache server child
8574      * processes run as, as they are the only processes that
8575      * would connect to the sockets. In the case of ITK MPM,
8576      * having them owned by Apache user is useless as at the
8577      * time the request is to be proxied, the Apache server
8578      * child process will have uid corresponding to the user
8579      * whose request they are handling. For ITK, thus set the
8580      * ownership to be the same as the daemon processes. This is
8581      * still restrictive, in that can only connect to daemon
8582      * process group running under same user, but most of the
8583      * time that is what you would want anyway when using ITK
8584      * MPM.
8585      */
8586 
8587     if (!geteuid()) {
8588 #if defined(MPM_ITK) || defined(ITK_MPM)
8589         uid_t socket_uid = process->uid;
8590 #else
8591         uid_t socket_uid = ap_unixd_config.user_id;
8592 #endif
8593 
8594         if (process->socket_user)
8595             socket_uid = ap_uname2id(process->socket_user);
8596 
8597         if (chown(process->socket_path, socket_uid, -1) < 0) {
8598             ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8599                          "mod_wsgi (pid=%d): Couldn't change owner of unix "
8600                          "domain socket '%s' to uid=%ld.", getpid(),
8601                          process->socket_path, (long)socket_uid);
8602 
8603             close(sockfd);
8604 
8605             return -1;
8606         }
8607     }
8608 
8609     return sockfd;
8610 }
8611 
8612 static int wsgi_hook_daemon_handler(conn_rec *c);
8613 
wsgi_process_socket(apr_pool_t * p,apr_socket_t * sock,apr_bucket_alloc_t * bucket_alloc,WSGIDaemonProcess * daemon)8614 static void wsgi_process_socket(apr_pool_t *p, apr_socket_t *sock,
8615                                 apr_bucket_alloc_t *bucket_alloc,
8616                                 WSGIDaemonProcess *daemon)
8617 {
8618     apr_status_t rv;
8619 
8620     conn_rec *c;
8621     ap_sb_handle_t *sbh;
8622     core_net_rec *net;
8623 
8624     /*
8625      * This duplicates Apache connection setup. This is done
8626      * here rather than letting Apache do it so that avoid the
8627      * possibility that any Apache modules, such as mod_ssl
8628      * will add their own input/output filters to the chain.
8629      */
8630 
8631 #if AP_MODULE_MAGIC_AT_LEAST(20110619,0)
8632     /* For 2.4 a NULL sbh pointer should work. */
8633     sbh = NULL;
8634 #else
8635     /* For 2.2 a dummy sbh pointer is needed. */
8636     ap_create_sb_handle(&sbh, p, -1, 0);
8637 #endif
8638 
8639     c = (conn_rec *)apr_pcalloc(p, sizeof(conn_rec));
8640 
8641     c->sbh = sbh;
8642 
8643     c->conn_config = ap_create_conn_config(p);
8644     c->notes = apr_table_make(p, 5);
8645     c->pool = p;
8646 
8647     if ((rv = apr_socket_addr_get(&c->local_addr, APR_LOCAL, sock))
8648         != APR_SUCCESS) {
8649         ap_log_error(APLOG_MARK, APLOG_INFO, rv, wsgi_server,
8650                      "mod_wsgi (pid=%d): Failed call "
8651                      "apr_socket_addr_get(APR_LOCAL).", getpid());
8652         apr_socket_close(sock);
8653         return;
8654     }
8655     apr_sockaddr_ip_get(&c->local_ip, c->local_addr);
8656 
8657 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
8658     if ((rv = apr_socket_addr_get(&c->client_addr, APR_REMOTE, sock))
8659         != APR_SUCCESS) {
8660         ap_log_error(APLOG_MARK, APLOG_INFO, rv, wsgi_server,
8661                      "mod_wsgi (pid=%d): Failed call "
8662                      "apr_socket_addr_get(APR_REMOTE).", getpid());
8663         apr_socket_close(sock);
8664         return;
8665     }
8666     c->client_ip = "unknown";
8667 #else
8668     if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, sock))
8669         != APR_SUCCESS) {
8670         ap_log_error(APLOG_MARK, APLOG_INFO, rv, wsgi_server,
8671                      "mod_wsgi (pid=%d): Failed call "
8672                      "apr_socket_addr_get(APR_REMOTE).", getpid());
8673         apr_socket_close(sock);
8674         return;
8675     }
8676     c->remote_ip = "unknown";
8677 #endif
8678 
8679     c->base_server = daemon->group->server;
8680 
8681     c->bucket_alloc = bucket_alloc;
8682     c->id = 1;
8683 
8684     net = apr_palloc(c->pool, sizeof(core_net_rec));
8685 
8686     if (daemon->group->socket_timeout)
8687         rv = apr_socket_timeout_set(sock, daemon->group->socket_timeout);
8688     else
8689         rv = apr_socket_timeout_set(sock, c->base_server->timeout);
8690 
8691     if (rv != APR_SUCCESS) {
8692         ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, wsgi_server,
8693                       "mod_wsgi (pid=%d): Failed call "
8694                       "apr_socket_timeout_set().", getpid());
8695     }
8696 
8697     net->c = c;
8698     net->in_ctx = NULL;
8699     net->out_ctx = NULL;
8700     net->client_socket = sock;
8701 
8702     ap_set_module_config(net->c->conn_config, &core_module, sock);
8703     ap_add_input_filter_handle(ap_core_input_filter_handle,
8704                                net, NULL, net->c);
8705     ap_add_output_filter_handle(ap_core_output_filter_handle,
8706                                 net, NULL, net->c);
8707 
8708     wsgi_hook_daemon_handler(c);
8709 
8710     ap_lingering_close(c);
8711 }
8712 
wsgi_worker_acquire(int id)8713 static apr_status_t wsgi_worker_acquire(int id)
8714 {
8715     WSGIThreadStack *stack = wsgi_worker_stack;
8716     WSGIDaemonThread *thread = &wsgi_worker_threads[id];
8717 
8718     while (1) {
8719         apr_uint32_t state = stack->state;
8720         if (state & (WSGI_STACK_TERMINATED | WSGI_STACK_NO_LISTENER)) {
8721             if (state & WSGI_STACK_TERMINATED) {
8722                 return APR_EINVAL;
8723             }
8724             if (apr_atomic_cas32(&(stack->state), WSGI_STACK_LAST, state) !=
8725                 state) {
8726                 continue;
8727             }
8728             else {
8729                 return APR_SUCCESS;
8730             }
8731         }
8732         thread->next = state;
8733         if (apr_atomic_cas32(&(stack->state), (unsigned)id, state) != state) {
8734             continue;
8735         }
8736         else {
8737             apr_status_t rv;
8738 
8739             if (thread->wakeup) {
8740                 thread->wakeup = 0;
8741 
8742                 return APR_SUCCESS;
8743             }
8744 
8745             rv = apr_thread_cond_wait(thread->condition, thread->mutex);
8746 
8747             while (rv == APR_SUCCESS && !thread->wakeup)
8748                 rv = apr_thread_cond_wait(thread->condition, thread->mutex);
8749 
8750             if (rv != APR_SUCCESS) {
8751                 ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
8752                              wsgi_server, "mod_wsgi (pid=%d): "
8753                              "Wait on thread %d wakeup condition variable "
8754                              "failed.", getpid(), id);
8755             }
8756 
8757             thread->wakeup = 0;
8758 
8759             return rv;
8760         }
8761     }
8762 }
8763 
wsgi_worker_release(void)8764 static apr_status_t wsgi_worker_release(void)
8765 {
8766     WSGIThreadStack *stack = wsgi_worker_stack;
8767 
8768     while (1) {
8769         apr_uint32_t state = stack->state;
8770         unsigned int first = state & WSGI_STACK_HEAD;
8771         if (first == WSGI_STACK_LAST) {
8772             if (apr_atomic_cas32(&(stack->state),
8773                                  state | WSGI_STACK_NO_LISTENER,
8774                                  state) != state) {
8775                 continue;
8776             }
8777             else {
8778                 return APR_SUCCESS;
8779             }
8780         }
8781         else {
8782             WSGIDaemonThread *thread = &wsgi_worker_threads[first];
8783             if (apr_atomic_cas32(&(stack->state),
8784                                  (state ^ first) | thread->next,
8785                                  state) != state) {
8786                 continue;
8787             }
8788             else {
8789                 /*
8790                  * Flag that thread should be woken up and then
8791                  * signal it via the condition variable.
8792                  */
8793 
8794                 apr_status_t rv;
8795                 if ((rv = apr_thread_mutex_lock(thread->mutex)) !=
8796                     APR_SUCCESS) {
8797                     return rv;
8798                 }
8799 
8800                 thread->wakeup = 1;
8801 
8802                 if ((rv = apr_thread_mutex_unlock(thread->mutex)) !=
8803                     APR_SUCCESS) {
8804                     return rv;
8805                 }
8806 
8807                 return apr_thread_cond_signal(thread->condition);
8808             }
8809         }
8810     }
8811 }
8812 
wsgi_worker_shutdown(void)8813 static apr_status_t wsgi_worker_shutdown(void)
8814 {
8815     int i;
8816     apr_status_t rv;
8817     WSGIThreadStack *stack = wsgi_worker_stack;
8818 
8819     while (1) {
8820         apr_uint32_t state = stack->state;
8821         if (apr_atomic_cas32(&(stack->state), state | WSGI_STACK_TERMINATED,
8822                            state) == state) {
8823             break;
8824         }
8825     }
8826     for (i = 0; i < wsgi_daemon_process->group->threads; i++) {
8827         if ((rv = wsgi_worker_release()) != APR_SUCCESS) {
8828             return rv;
8829         }
8830     }
8831     return APR_SUCCESS;
8832 }
8833 
wsgi_daemon_worker(apr_pool_t * p,WSGIDaemonThread * thread)8834 static void wsgi_daemon_worker(apr_pool_t *p, WSGIDaemonThread *thread)
8835 {
8836     apr_status_t status;
8837     apr_socket_t *socket;
8838 
8839     apr_pool_t *ptrans;
8840 
8841     apr_pollset_t *pollset;
8842     apr_pollfd_t pfd = { 0 };
8843     apr_int32_t numdesc;
8844     const apr_pollfd_t *pdesc;
8845 
8846     apr_bucket_alloc_t *bucket_alloc;
8847 
8848     WSGIDaemonProcess *daemon = thread->process;
8849     WSGIProcessGroup *group = daemon->group;
8850 
8851     /* Loop until signal received to shutdown daemon process. */
8852 
8853     while (!wsgi_daemon_shutdown) {
8854         apr_status_t rv;
8855 
8856         /*
8857          * Only allow one thread in this process to attempt to
8858          * acquire the global process lock as the global process
8859          * lock will actually allow all threads in this process
8860          * through once one in this process acquires lock. Only
8861          * allowing one means better chance of another process
8862          * subsequently getting it thereby distributing requests
8863          * across processes better and reducing chance of Python
8864          * GIL contention.
8865          */
8866 
8867         wsgi_worker_acquire(thread->id);
8868 
8869         if (wsgi_daemon_shutdown)
8870             break;
8871 
8872         if (group->mutex) {
8873             /*
8874              * Grab the accept mutex across all daemon processes
8875              * in this process group.
8876              */
8877 
8878             rv = apr_proc_mutex_lock(group->mutex);
8879 
8880             if (rv != APR_SUCCESS) {
8881 #if 0
8882 #if defined(EIDRM)
8883                 /*
8884                  * When using multiple threads locking the
8885                  * process accept mutex fails with an EIDRM when
8886                  * process being shutdown but signal check
8887                  * hasn't triggered quick enough to set shutdown
8888                  * flag. This causes lots of error messages to
8889                  * be logged which make it look like something
8890                  * nasty has happened even when it hasn't. For
8891                  * now assume that if multiple threads and EIDRM
8892                  * occurs that it is okay and the process is
8893                  * being shutdown. The condition should by
8894                  * rights only occur when the Apache parent
8895                  * process is being shutdown or has died for
8896                  * some reason so daemon process would logically
8897                  * therefore also be in process of being
8898                  * shutdown or killed.
8899                  */
8900                 if (!strcmp(apr_proc_mutex_name(group->mutex), "sysvsem")) {
8901                     if (errno == EIDRM && group->threads > 1)
8902                         wsgi_daemon_shutdown = 1;
8903                 }
8904 #endif
8905 #endif
8906 
8907                 if (!wsgi_daemon_shutdown) {
8908                     ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
8909                                  wsgi_server, "mod_wsgi (pid=%d): "
8910                                  "Couldn't acquire accept mutex '%s'. "
8911                                  "Shutting down daemon process.",
8912                                  getpid(), group->socket_path);
8913 
8914                     wsgi_daemon_shutdown++;
8915                     kill(getpid(), SIGTERM);
8916                     sleep(5);
8917                 }
8918 
8919                 break;
8920             }
8921 
8922             /*
8923              * Daemon process being shutdown so don't accept the
8924              * connection after all.
8925              */
8926 
8927             if (wsgi_daemon_shutdown) {
8928                 apr_proc_mutex_unlock(group->mutex);
8929 
8930                 wsgi_worker_release();
8931 
8932                 break;
8933             }
8934         }
8935 
8936         apr_pool_create(&ptrans, p);
8937 
8938         /*
8939          * Accept socket connection from the child process. We
8940          * test the socket for whether it is ready before actually
8941          * performing the accept() so that can know for sure that
8942          * we will be processing a request and flag thread as
8943          * running. Only bother to do join with thread which is
8944          * actually running when process is being shutdown.
8945          */
8946 
8947         apr_pollset_create(&pollset, 1, ptrans, 0);
8948 
8949         memset(&pfd, '\0', sizeof(pfd));
8950         pfd.desc_type = APR_POLL_SOCKET;
8951         pfd.desc.s = daemon->listener;
8952         pfd.reqevents = APR_POLLIN;
8953         pfd.client_data = daemon;
8954 
8955         apr_pollset_add(pollset, &pfd);
8956 
8957         rv = apr_pollset_poll(pollset, -1, &numdesc, &pdesc);
8958 
8959         if (rv != APR_SUCCESS && !APR_STATUS_IS_EINTR(rv)) {
8960             ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
8961                          wsgi_server, "mod_wsgi (pid=%d): "
8962                          "Unable to poll daemon socket for '%s'. "
8963                          "Shutting down daemon process.",
8964                          getpid(), group->socket_path);
8965 
8966             wsgi_daemon_shutdown++;
8967             kill(getpid(), SIGTERM);
8968             sleep(5);
8969 
8970             break;
8971         }
8972 
8973         if (wsgi_daemon_shutdown) {
8974             if (group->mutex)
8975                 apr_proc_mutex_unlock(group->mutex);
8976 
8977             wsgi_worker_release();
8978 
8979             apr_pool_destroy(ptrans);
8980 
8981             break;
8982         }
8983 
8984         if (rv != APR_SUCCESS && APR_STATUS_IS_EINTR(rv)) {
8985             if (group->mutex)
8986                 apr_proc_mutex_unlock(group->mutex);
8987 
8988             wsgi_worker_release();
8989 
8990             apr_pool_destroy(ptrans);
8991 
8992             continue;
8993         }
8994 
8995         thread->running = 1;
8996 
8997         status = apr_socket_accept(&socket, daemon->listener, ptrans);
8998 
8999         if (group->mutex) {
9000             apr_status_t rv;
9001             rv = apr_proc_mutex_unlock(group->mutex);
9002 
9003             if (rv != APR_SUCCESS) {
9004                 if (!wsgi_daemon_shutdown) {
9005                     wsgi_worker_release();
9006 
9007                     ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
9008                                  wsgi_server, "mod_wsgi (pid=%d): "
9009                                  "Couldn't release accept mutex '%s'.",
9010                                  getpid(), group->socket_path);
9011 
9012                     apr_pool_destroy(ptrans);
9013                     thread->running = 0;
9014 
9015                     break;
9016                 }
9017             }
9018         }
9019 
9020         wsgi_worker_release();
9021 
9022         if (status != APR_SUCCESS && APR_STATUS_IS_EINTR(status)) {
9023             apr_pool_destroy(ptrans);
9024             thread->running = 0;
9025 
9026             continue;
9027         }
9028 
9029         /* Process the request proxied from the child process. */
9030 
9031         apr_thread_mutex_lock(wsgi_monitor_lock);
9032         thread->request = apr_time_now();
9033         apr_thread_mutex_unlock(wsgi_monitor_lock);
9034 
9035         bucket_alloc = apr_bucket_alloc_create(ptrans);
9036         wsgi_process_socket(ptrans, socket, bucket_alloc, daemon);
9037 
9038         apr_thread_mutex_lock(wsgi_monitor_lock);
9039         thread->request = 0;
9040         apr_thread_mutex_unlock(wsgi_monitor_lock);
9041 
9042         /* Cleanup ready for next request. */
9043 
9044         apr_pool_destroy(ptrans);
9045 
9046         thread->running = 0;
9047 
9048         /* Check to see if maximum number of requests reached. */
9049 
9050         if (daemon->group->maximum_requests) {
9051             if (--wsgi_request_count <= 0) {
9052                 if (wsgi_graceful_timeout && wsgi_active_requests) {
9053                     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9054                                  "mod_wsgi (pid=%d): Maximum requests "
9055                                  "reached, attempt a graceful shutdown "
9056                                  "'%s'.", getpid(), daemon->group->name);
9057 
9058                     apr_thread_mutex_lock(wsgi_monitor_lock);
9059                     wsgi_graceful_shutdown_time = apr_time_now();
9060                     wsgi_graceful_shutdown_time += wsgi_graceful_timeout;
9061                     apr_thread_mutex_unlock(wsgi_monitor_lock);
9062                 }
9063                 else {
9064                     if (!wsgi_daemon_shutdown) {
9065                         ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9066                                      "mod_wsgi (pid=%d): Maximum requests "
9067                                      "reached, triggering immediate shutdown "
9068                                      "'%s'.", getpid(), daemon->group->name);
9069                     }
9070 
9071                     wsgi_daemon_shutdown++;
9072                     kill(getpid(), SIGINT);
9073                 }
9074             }
9075         }
9076         else if (wsgi_daemon_graceful && !wsgi_daemon_shutdown) {
9077             if (wsgi_active_requests == 0) {
9078                 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9079                              "mod_wsgi (pid=%d): Requests have completed, "
9080                              "triggering immediate shutdown '%s'.",
9081                              getpid(), daemon->group->name);
9082 
9083                 wsgi_daemon_shutdown++;
9084                 kill(getpid(), SIGINT);
9085             }
9086         }
9087     }
9088 
9089     if (wsgi_server_config->verbose_debugging) {
9090         ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9091                      "mod_wsgi (pid=%d): Exiting thread %d in daemon "
9092                      "process '%s'.", getpid(), thread->id,
9093                      thread->process->group->name);
9094     }
9095     else {
9096         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9097                      "mod_wsgi (pid=%d): Exiting thread %d in daemon "
9098                      "process '%s'.", getpid(), thread->id,
9099                      thread->process->group->name);
9100     }
9101 }
9102 
wsgi_daemon_thread(apr_thread_t * thd,void * data)9103 static void *wsgi_daemon_thread(apr_thread_t *thd, void *data)
9104 {
9105     WSGIDaemonThread *thread = data;
9106     apr_pool_t *p = apr_thread_pool_get(thd);
9107 
9108     if (wsgi_server_config->verbose_debugging) {
9109       ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9110                    "mod_wsgi (pid=%d): Started thread %d in daemon "
9111                    "process '%s'.", getpid(), thread->id,
9112                    thread->process->group->name);
9113     }
9114     else {
9115       ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9116                    "mod_wsgi (pid=%d): Started thread %d in daemon "
9117                    "process '%s'.", getpid(), thread->id,
9118                    thread->process->group->name);
9119     }
9120 
9121     apr_thread_mutex_lock(thread->mutex);
9122 
9123     wsgi_daemon_worker(p, thread);
9124 
9125     apr_thread_exit(thd, APR_SUCCESS);
9126 
9127     return NULL;
9128 }
9129 
wsgi_reaper_thread(apr_thread_t * thd,void * data)9130 static void *wsgi_reaper_thread(apr_thread_t *thd, void *data)
9131 {
9132     WSGIDaemonProcess *daemon = data;
9133 
9134     sleep(daemon->group->shutdown_timeout);
9135 
9136     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9137                  "mod_wsgi (pid=%d): Aborting process '%s'.",
9138                  getpid(), daemon->group->name);
9139 
9140     wsgi_exit_daemon_process(-1);
9141 
9142     return NULL;
9143 }
9144 
wsgi_deadlock_thread(apr_thread_t * thd,void * data)9145 static void *wsgi_deadlock_thread(apr_thread_t *thd, void *data)
9146 {
9147     WSGIDaemonProcess *daemon = data;
9148 
9149     PyGILState_STATE gilstate;
9150 
9151     if (wsgi_server_config->verbose_debugging) {
9152         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9153                      "mod_wsgi (pid=%d): Enable deadlock thread in "
9154                      "process '%s'.", getpid(), daemon->group->name);
9155     }
9156 
9157     apr_thread_mutex_lock(wsgi_monitor_lock);
9158     wsgi_deadlock_shutdown_time = apr_time_now();
9159     wsgi_deadlock_shutdown_time += wsgi_deadlock_timeout;
9160     apr_thread_mutex_unlock(wsgi_monitor_lock);
9161 
9162     while (1) {
9163         apr_sleep(apr_time_from_sec(1));
9164 
9165         apr_thread_mutex_lock(wsgi_shutdown_lock);
9166 
9167         if (!wsgi_daemon_shutdown) {
9168             gilstate = PyGILState_Ensure();
9169             PyGILState_Release(gilstate);
9170         }
9171 
9172         apr_thread_mutex_unlock(wsgi_shutdown_lock);
9173 
9174         apr_thread_mutex_lock(wsgi_monitor_lock);
9175         wsgi_deadlock_shutdown_time = apr_time_now();
9176         wsgi_deadlock_shutdown_time += wsgi_deadlock_timeout;
9177         apr_thread_mutex_unlock(wsgi_monitor_lock);
9178     }
9179 
9180     return NULL;
9181 }
9182 
wsgi_monitor_thread(apr_thread_t * thd,void * data)9183 static void *wsgi_monitor_thread(apr_thread_t *thd, void *data)
9184 {
9185     WSGIDaemonProcess *daemon = data;
9186     WSGIProcessGroup *group = daemon->group;
9187 
9188     int restart = 0;
9189 
9190     if (wsgi_server_config->verbose_debugging) {
9191         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9192                      "mod_wsgi (pid=%d): Enable monitor thread in "
9193                      "process '%s'.", getpid(), group->name);
9194 
9195         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9196                      "mod_wsgi (pid=%d): Startup timeout is %d.",
9197                      getpid(), (int)(apr_time_sec(wsgi_startup_timeout)));
9198         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9199                      "mod_wsgi (pid=%d): Deadlock timeout is %d.",
9200                      getpid(), (int)(apr_time_sec(wsgi_deadlock_timeout)));
9201         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9202                      "mod_wsgi (pid=%d): Idle inactivity timeout is %d.",
9203                      getpid(), (int)(apr_time_sec(wsgi_idle_timeout)));
9204         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9205                      "mod_wsgi (pid=%d): Request time limit is %d.",
9206                      getpid(), (int)(apr_time_sec(wsgi_request_timeout)));
9207         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9208                      "mod_wsgi (pid=%d): Graceful timeout is %d.",
9209                      getpid(), (int)(apr_time_sec(wsgi_graceful_timeout)));
9210         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9211                      "mod_wsgi (pid=%d): Eviction timeout is %d.",
9212                      getpid(), (int)(apr_time_sec(wsgi_eviction_timeout)));
9213         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9214                      "mod_wsgi (pid=%d): Restart interval is %d.",
9215                      getpid(), (int)(apr_time_sec(wsgi_restart_interval)));
9216     }
9217 
9218     /*
9219      * If a restart interval was specified then set up the time for
9220      * when the restart should occur.
9221      */
9222 
9223     if (wsgi_restart_interval) {
9224         wsgi_restart_shutdown_time = apr_time_now();
9225         wsgi_restart_shutdown_time += wsgi_restart_interval;
9226     }
9227 
9228     while (1) {
9229         apr_time_t now;
9230 
9231         apr_time_t startup_time;
9232         apr_time_t deadlock_time;
9233         apr_time_t idle_time;
9234         apr_time_t graceful_time;
9235         apr_time_t restart_time;
9236 
9237         apr_time_t request_time = 0;
9238 
9239         apr_interval_time_t period = 0;
9240 
9241         int i = 0;
9242 
9243         now = apr_time_now();
9244 
9245         apr_thread_mutex_lock(wsgi_monitor_lock);
9246 
9247         startup_time = wsgi_startup_shutdown_time;
9248         deadlock_time = wsgi_deadlock_shutdown_time;
9249         idle_time = wsgi_idle_shutdown_time;
9250         graceful_time = wsgi_graceful_shutdown_time;
9251         restart_time = wsgi_restart_shutdown_time;
9252 
9253         if (wsgi_request_timeout && wsgi_worker_threads) {
9254             for (i = 0; i<wsgi_daemon_process->group->threads; i++) {
9255                 if (wsgi_worker_threads[i].request)
9256                     request_time += (now - wsgi_worker_threads[i].request);
9257             }
9258         }
9259 
9260         request_time /= wsgi_daemon_process->group->threads;
9261 
9262         apr_thread_mutex_unlock(wsgi_monitor_lock);
9263 
9264         if (!restart && wsgi_request_timeout) {
9265             if (request_time > wsgi_request_timeout) {
9266                 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9267                              "mod_wsgi (pid=%d): Daemon process request "
9268                              "time limit exceeded, stopping process "
9269                              "'%s'.", getpid(), group->name);
9270 
9271                 wsgi_shutdown_reason = "request_timeout";
9272 
9273                 wsgi_dump_stack_traces = 1;
9274 
9275                 restart = 1;
9276             }
9277         }
9278 
9279         if (!restart && wsgi_startup_timeout) {
9280             if (startup_time > 0) {
9281                 if (startup_time <= now) {
9282                     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9283                                  "mod_wsgi (pid=%d): Application startup "
9284                                  "timer expired, stopping process '%s'.",
9285                                  getpid(), group->name);
9286 
9287                     wsgi_shutdown_reason = "startup_timeout";
9288 
9289                     restart = 1;
9290                 }
9291                 else {
9292                     period = startup_time - now;
9293                 }
9294             }
9295         }
9296 
9297         if (!restart && wsgi_restart_interval) {
9298             if (restart_time > 0) {
9299                 if (restart_time <= now) {
9300                     if (!wsgi_daemon_graceful) {
9301                         if (wsgi_active_requests) {
9302                             wsgi_daemon_graceful++;
9303 
9304                             apr_thread_mutex_lock(wsgi_monitor_lock);
9305                             wsgi_graceful_shutdown_time = apr_time_now();
9306                             wsgi_graceful_shutdown_time += wsgi_graceful_timeout;
9307                             apr_thread_mutex_unlock(wsgi_monitor_lock);
9308 
9309                             ap_log_error(APLOG_MARK, APLOG_INFO, 0,
9310                                          wsgi_server, "mod_wsgi (pid=%d): "
9311                                          "Application restart timer expired, "
9312                                          "waiting for requests to complete "
9313                                          "'%s'.", getpid(),
9314                                          daemon->group->name);
9315                         }
9316                         else {
9317                             ap_log_error(APLOG_MARK, APLOG_INFO, 0,
9318                                          wsgi_server, "mod_wsgi (pid=%d): "
9319                                          "Application restart timer expired, "
9320                                          "stopping process '%s'.", getpid(),
9321                                          daemon->group->name);
9322 
9323                             wsgi_shutdown_reason = "restart_interval";
9324 
9325                             restart = 1;
9326                         }
9327                     }
9328                 }
9329                 else {
9330                     period = restart_time - now;
9331                 }
9332             }
9333         }
9334 
9335         if (!restart && wsgi_deadlock_timeout) {
9336             if (deadlock_time) {
9337                 if (deadlock_time <= now) {
9338                     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9339                                  "mod_wsgi (pid=%d): Daemon process deadlock "
9340                                  "timer expired, stopping process '%s'.",
9341                                  getpid(), group->name);
9342 
9343                     restart = 1;
9344                 }
9345                 else {
9346                     if (!period || ((deadlock_time - now) < period))
9347                         period = deadlock_time - now;
9348                 }
9349             }
9350             else {
9351                 if (!period || (wsgi_deadlock_timeout < period))
9352                     period = wsgi_deadlock_timeout;
9353             }
9354         }
9355 
9356         if (!restart && wsgi_idle_timeout) {
9357             if (idle_time) {
9358                 if (idle_time <= now) {
9359                     if (wsgi_active_requests == 0) {
9360                         ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9361                                      "mod_wsgi (pid=%d): Daemon process "
9362                                      "idle inactivity timer expired, "
9363                                      "stopping process '%s'.", getpid(),
9364                                      group->name);
9365 
9366                         wsgi_shutdown_reason = "inactivity_timeout";
9367 
9368                         restart = 1;
9369                     }
9370                     else {
9371                         /* Ignore for now as still have requests. */
9372 
9373                         if (!period || (wsgi_idle_timeout < period))
9374                             period = wsgi_idle_timeout;
9375                     }
9376                 }
9377                 else {
9378                     if (!period || ((idle_time - now) < period))
9379                         period = idle_time - now;
9380                 }
9381             }
9382             else {
9383                 if (!period || (wsgi_idle_timeout < period))
9384                     period = wsgi_idle_timeout;
9385             }
9386         }
9387 
9388         if (!restart && wsgi_graceful_timeout) {
9389             if (graceful_time) {
9390                 if (graceful_time <= now) {
9391                     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9392                                  "mod_wsgi (pid=%d): Daemon process "
9393                                  "graceful timer expired '%s'.", getpid(),
9394                                  group->name);
9395 
9396                     restart = 1;
9397                 }
9398                 else {
9399                     if (!period || ((graceful_time - now) < period))
9400                         period = graceful_time - now;
9401                     else if (wsgi_graceful_timeout < period)
9402                         period = wsgi_graceful_timeout;
9403                 }
9404             }
9405             else {
9406                 if (!period || (wsgi_graceful_timeout < period))
9407                     period = wsgi_graceful_timeout;
9408             }
9409         }
9410 
9411         if (!restart && wsgi_eviction_timeout) {
9412             if (graceful_time) {
9413                 if (graceful_time <= now) {
9414                     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9415                                  "mod_wsgi (pid=%d): Daemon process "
9416                                  "graceful timer expired '%s'.", getpid(),
9417                                  group->name);
9418 
9419                     restart = 1;
9420                 }
9421                 else {
9422                     if (!period || ((graceful_time - now) < period))
9423                         period = graceful_time - now;
9424                     else if (wsgi_eviction_timeout < period)
9425                         period = wsgi_eviction_timeout;
9426                 }
9427             }
9428             else {
9429                 if (!period || (wsgi_eviction_timeout < period))
9430                     period = wsgi_eviction_timeout;
9431             }
9432         }
9433 
9434         if (restart) {
9435             wsgi_daemon_shutdown++;
9436             kill(getpid(), SIGINT);
9437         }
9438 
9439         if (restart || wsgi_request_timeout || period <= 0)
9440             period = apr_time_from_sec(1);
9441 
9442         apr_sleep(period);
9443     }
9444 
9445     return NULL;
9446 }
9447 
9448 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 5)
wsgi_log_stack_traces(void)9449 static void wsgi_log_stack_traces(void)
9450 {
9451     PyGILState_STATE state;
9452 
9453     PyObject *threads = NULL;
9454 
9455     /*
9456      * This should only be called on shutdown so don't try and log
9457      * any errors, just dump them straight out.
9458      */
9459 
9460     state = PyGILState_Ensure();
9461 
9462     threads = _PyThread_CurrentFrames();
9463 
9464     if (threads && PyDict_Size(threads) != 0) {
9465         PyObject *seq = NULL;
9466 
9467         seq = PyObject_GetIter(threads);
9468 
9469         if (seq) {
9470             PyObject *id = NULL;
9471             PyObject *frame = NULL;
9472 
9473             Py_ssize_t i = 0;
9474 
9475             ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9476                           "mod_wsgi (pid=%d): Dumping stack trace for "
9477                           "active Python threads.", getpid());
9478 
9479             while (PyDict_Next(threads, &i, &id, &frame)) {
9480                 apr_int64_t thread_id = 0;
9481 
9482                 PyFrameObject *current = NULL;
9483 
9484                 thread_id = PyLong_AsLong(id);
9485 
9486                 current = (PyFrameObject *)frame;
9487 
9488                 while (current) {
9489                     int lineno;
9490 
9491                     const char *filename = NULL;
9492                     const char *name = NULL;
9493 
9494                     if (current->f_trace) {
9495                         lineno = current->f_lineno;
9496                     }
9497                     else {
9498                         lineno = PyCode_Addr2Line(current->f_code,
9499                                                   current->f_lasti);
9500                     }
9501 
9502 #if PY_MAJOR_VERSION >= 3
9503                     filename = PyUnicode_AsUTF8(current->f_code->co_filename);
9504                     name = PyUnicode_AsUTF8(current->f_code->co_name);
9505 #else
9506                     filename = PyString_AsString(current->f_code->co_filename);
9507                     name = PyString_AsString(current->f_code->co_name);
9508 #endif
9509 
9510                     if (current == (PyFrameObject *)frame) {
9511                         ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9512                                 "mod_wsgi (pid=%d): Thread %" APR_INT64_T_FMT
9513                                 " executing file \"%s\", line %d, in %s",
9514                                 getpid(), thread_id, filename, lineno, name);
9515                     }
9516                     else {
9517                         if (current->f_back) {
9518                             ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9519                                     "mod_wsgi (pid=%d): called from file "
9520                                     "\"%s\", line %d, in %s,", getpid(),
9521                                     filename, lineno, name);
9522                         }
9523                         else {
9524                             ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9525                                     "mod_wsgi (pid=%d): called from file "
9526                                     "\"%s\", line %d, in %s.", getpid(),
9527                                     filename, lineno, name);
9528                         }
9529                     }
9530 
9531                     current = current->f_back;
9532                 }
9533             }
9534         }
9535         else {
9536             ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
9537                           "mod_wsgi (pid=%d): Failed to iterate over "
9538                           "current frames for active threads.", getpid());
9539 
9540             PyErr_Print();
9541             PyErr_Clear();
9542         }
9543     }
9544     else {
9545         ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
9546                       "mod_wsgi (pid=%d): Failed to get current frames "
9547                       "for active threads.", getpid());
9548 
9549         PyErr_Print();
9550         PyErr_Clear();
9551     }
9552 
9553     Py_XDECREF(threads);
9554 
9555     PyGILState_Release(state);
9556 }
9557 #endif
9558 
wsgi_daemon_main(apr_pool_t * p,WSGIDaemonProcess * daemon)9559 static void wsgi_daemon_main(apr_pool_t *p, WSGIDaemonProcess *daemon)
9560 {
9561     apr_threadattr_t *thread_attr;
9562     apr_thread_t *reaper = NULL;
9563 
9564     int i;
9565     apr_status_t rv;
9566     apr_status_t thread_rv;
9567 
9568     apr_pollfd_t poll_fd;
9569     apr_int32_t poll_count = 0;
9570 
9571     /*
9572      * Setup poll object for listening for shutdown notice from
9573      * signal handler.
9574      */
9575 
9576     poll_fd.desc_type = APR_POLL_FILE;
9577     poll_fd.reqevents = APR_POLLIN;
9578     poll_fd.desc.f = wsgi_signal_pipe_in;
9579 
9580     /* Initialise maximum request count for daemon. */
9581 
9582     if (daemon->group->maximum_requests)
9583         wsgi_request_count = daemon->group->maximum_requests;
9584 
9585     /* Ensure that threads are joinable. */
9586 
9587     apr_threadattr_create(&thread_attr, p);
9588     apr_threadattr_detach_set(thread_attr, 0);
9589 
9590 #if (APR_MAJOR_VERSION >= 1)
9591     if (daemon->group->stack_size) {
9592         apr_threadattr_stacksize_set(thread_attr, daemon->group->stack_size);
9593     }
9594 #endif
9595 
9596     /* Start monitoring thread if required. */
9597 
9598     wsgi_startup_timeout = daemon->group->startup_timeout;
9599     wsgi_deadlock_timeout = daemon->group->deadlock_timeout;
9600     wsgi_idle_timeout = daemon->group->inactivity_timeout;
9601     wsgi_request_timeout = daemon->group->request_timeout;
9602     wsgi_graceful_timeout = daemon->group->graceful_timeout;
9603     wsgi_eviction_timeout = daemon->group->eviction_timeout;
9604     wsgi_restart_interval = daemon->group->restart_interval;
9605 
9606     if (wsgi_deadlock_timeout || wsgi_idle_timeout) {
9607         rv = apr_thread_create(&reaper, thread_attr, wsgi_monitor_thread,
9608                                daemon, p);
9609 
9610         if (rv != APR_SUCCESS) {
9611             ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9612                          "mod_wsgi (pid=%d): Couldn't create monitor "
9613                          "thread in daemon process '%s'.", getpid(),
9614                          daemon->group->name);
9615         }
9616     }
9617 
9618     if (wsgi_deadlock_timeout) {
9619         if (rv != APR_SUCCESS) {
9620             ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9621                          "mod_wsgi (pid=%d): Couldn't create deadlock "
9622                          "thread in daemon process '%s'.", getpid(),
9623                          daemon->group->name);
9624         }
9625 
9626         rv = apr_thread_create(&reaper, thread_attr, wsgi_deadlock_thread,
9627                                daemon, p);
9628     }
9629 
9630     /* Initialise worker stack. */
9631 
9632     wsgi_worker_stack = (WSGIThreadStack *)apr_palloc(p,
9633             sizeof(WSGIThreadStack));
9634     wsgi_worker_stack->state = WSGI_STACK_NO_LISTENER | WSGI_STACK_LAST;
9635 
9636     /* Start the required number of threads. */
9637 
9638     wsgi_worker_threads = (WSGIDaemonThread *)apr_pcalloc(p,
9639                            daemon->group->threads * sizeof(WSGIDaemonThread));
9640 
9641     if (wsgi_server_config->verbose_debugging) {
9642         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9643                      "mod_wsgi (pid=%d): Starting %d threads in daemon "
9644                      "process '%s'.", getpid(), daemon->group->threads,
9645                      daemon->group->name);
9646     }
9647 
9648     for (i=0; i<daemon->group->threads; i++) {
9649         WSGIDaemonThread *thread = &wsgi_worker_threads[i];
9650 
9651         if (wsgi_server_config->verbose_debugging) {
9652             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9653                          "mod_wsgi (pid=%d): Starting thread %d in daemon "
9654                          "process '%s'.", getpid(), i+1, daemon->group->name);
9655         }
9656 
9657         /* Create the mutex and condition variable for this thread. */
9658 
9659         rv = apr_thread_cond_create(&thread->condition, p);
9660 
9661         if (rv != APR_SUCCESS) {
9662             ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9663                          "mod_wsgi (pid=%d): Couldn't create worker "
9664                          "thread %d state condition variable in daemon "
9665                          "process '%s'.", getpid(), i, daemon->group->name);
9666 
9667             /*
9668              * Try to force an exit of the process if fail
9669              * to create the worker threads.
9670              */
9671 
9672             kill(getpid(), SIGTERM);
9673             sleep(5);
9674         }
9675 
9676         rv = apr_thread_mutex_create(&thread->mutex,
9677                                      APR_THREAD_MUTEX_DEFAULT, p);
9678 
9679         if (rv != APR_SUCCESS) {
9680             ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9681                          "mod_wsgi (pid=%d): Couldn't create worker "
9682                          "thread %d state mutex variable in daemon "
9683                          "process '%s'.", getpid(), i, daemon->group->name);
9684 
9685             /*
9686              * Try to force an exit of the process if fail
9687              * to create the worker threads.
9688              */
9689 
9690             kill(getpid(), SIGTERM);
9691             sleep(5);
9692         }
9693 
9694         /* Now create the actual thread. */
9695 
9696         thread->id = i;
9697         thread->process = daemon;
9698         thread->running = 0;
9699         thread->request = 0;
9700 
9701         rv = apr_thread_create(&thread->thread, thread_attr,
9702                                wsgi_daemon_thread, thread, p);
9703 
9704         if (rv != APR_SUCCESS) {
9705             ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9706                          "mod_wsgi (pid=%d): Couldn't create worker "
9707                          "thread %d in daemon process '%s'.", getpid(),
9708                          i, daemon->group->name);
9709 
9710             /*
9711              * Try to force an exit of the process if fail
9712              * to create the worker threads.
9713              */
9714 
9715             kill(getpid(), SIGTERM);
9716             sleep(5);
9717         }
9718     }
9719 
9720     /* Block until we get a process shutdown signal. */
9721 
9722     while (1) {
9723         char buf[1];
9724         apr_size_t nbytes = 1;
9725 
9726         rv = apr_poll(&poll_fd, 1, &poll_count, -1);
9727         if (APR_STATUS_IS_EINTR(rv))
9728             continue;
9729 
9730         rv = apr_file_read(wsgi_signal_pipe_in, buf, &nbytes);
9731 
9732         if (rv != APR_SUCCESS || nbytes != 1) {
9733             ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server,
9734                          "mod_wsgi (pid=%d): Failed read on signal pipe '%s'.",
9735                          getpid(), daemon->group->name);
9736 
9737             break;
9738         }
9739 
9740         if (buf[0] == 'C') {
9741             if (!wsgi_daemon_graceful) {
9742                 wsgi_shutdown_reason = "cpu_time_limit";
9743 
9744                 if (wsgi_active_requests) {
9745                     wsgi_daemon_graceful++;
9746 
9747                     apr_thread_mutex_lock(wsgi_monitor_lock);
9748                     wsgi_graceful_shutdown_time = apr_time_now();
9749                     wsgi_graceful_shutdown_time += wsgi_graceful_timeout;
9750                     apr_thread_mutex_unlock(wsgi_monitor_lock);
9751 
9752                     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9753                                  "mod_wsgi (pid=%d): Exceeded CPU time "
9754                                  "limit, waiting for requests to complete "
9755                                  "'%s'.", getpid(), daemon->group->name);
9756                 }
9757                 else {
9758                     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9759                                  "mod_wsgi (pid=%d): Exceeded CPU time "
9760                                  "limit, triggering immediate shutdown "
9761                                  "'%s'.", getpid(), daemon->group->name);
9762 
9763                     wsgi_daemon_shutdown++;
9764                     kill(getpid(), SIGINT);
9765                 }
9766             }
9767         }
9768         else if (buf[0] == 'G') {
9769             if (!wsgi_daemon_graceful) {
9770                 wsgi_shutdown_reason = "graceful_signal";
9771 
9772                 if (wsgi_active_requests) {
9773                     wsgi_daemon_graceful++;
9774 
9775                     apr_thread_mutex_lock(wsgi_monitor_lock);
9776                     wsgi_graceful_shutdown_time = apr_time_now();
9777                     if (wsgi_eviction_timeout)
9778                         wsgi_graceful_shutdown_time += wsgi_eviction_timeout;
9779                     else
9780                         wsgi_graceful_shutdown_time += wsgi_graceful_timeout;
9781                     apr_thread_mutex_unlock(wsgi_monitor_lock);
9782 
9783                     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9784                                  "mod_wsgi (pid=%d): Process eviction "
9785                                  "requested, waiting for requests to complete "
9786                                  "'%s'.", getpid(), daemon->group->name);
9787                 }
9788                 else {
9789                     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9790                                  "mod_wsgi (pid=%d): Process eviction "
9791                                  "requested, triggering immediate shutdown "
9792                                  "'%s'.", getpid(), daemon->group->name);
9793 
9794                     wsgi_daemon_shutdown++;
9795                     kill(getpid(), SIGINT);
9796                 }
9797             }
9798         }
9799         else
9800             break;
9801     }
9802 
9803     ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9804                  "mod_wsgi (pid=%d): Shutdown requested '%s'.",
9805                  getpid(), daemon->group->name);
9806 
9807     /*
9808      * Create a reaper thread to abort process if graceful
9809      * shutdown takes too long. Not recommended to disable
9810      * this unless external process is controlling shutdown.
9811      */
9812 
9813     if (daemon->group->shutdown_timeout) {
9814         rv = apr_thread_create(&reaper, thread_attr, wsgi_reaper_thread,
9815                                daemon, p);
9816 
9817         if (rv != APR_SUCCESS) {
9818             ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9819                          "mod_wsgi (pid=%d): Couldn't create reaper "
9820                          "thread in daemon process '%s'.", getpid(),
9821                          daemon->group->name);
9822         }
9823     }
9824 
9825     /*
9826      * If shutting down process due to reaching request time
9827      * limit, then try and dump out stack traces of any threads
9828      * which are running as a debugging aid.
9829      */
9830 
9831     wsgi_publish_process_stopping(wsgi_shutdown_reason);
9832 
9833 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 5)
9834     if (wsgi_dump_stack_traces)
9835         wsgi_log_stack_traces();
9836 #endif
9837 
9838     /*
9839      * Attempt a graceful shutdown by waiting for any
9840      * threads which were processing a request at the time
9841      * of shutdown. In some respects this is a bit pointless
9842      * as even though we allow the requests to be completed,
9843      * the Apache child process which proxied the request
9844      * through to this daemon process could get killed off
9845      * before the daemon process and so the response gets
9846      * cut off or lost.
9847      */
9848 
9849     wsgi_worker_shutdown();
9850 
9851     for (i=0; i<daemon->group->threads; i++) {
9852         if (wsgi_worker_threads[i].thread && wsgi_worker_threads[i].running) {
9853             rv = apr_thread_join(&thread_rv, wsgi_worker_threads[i].thread);
9854             if (rv != APR_SUCCESS) {
9855                 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, wsgi_server,
9856                              "mod_wsgi (pid=%d): Couldn't join with "
9857                              "worker thread %d in daemon process '%s'.",
9858                              getpid(), i, daemon->group->name);
9859             }
9860         }
9861     }
9862 }
9863 
wsgi_cleanup_process(void * data)9864 static apr_status_t wsgi_cleanup_process(void *data)
9865 {
9866     WSGIProcessGroup *group = (WSGIProcessGroup *)data;
9867 
9868     /* Only do cleanup if in Apache parent process. */
9869 
9870     if (wsgi_parent_pid != getpid())
9871         return APR_SUCCESS;
9872 
9873     if (group->listener_fd != -1) {
9874         if (close(group->listener_fd) < 0) {
9875             ap_log_error(APLOG_MARK, APLOG_ERR, errno,
9876                          wsgi_server, "mod_wsgi (pid=%d): "
9877                          "Couldn't close unix domain socket '%s'.",
9878                          getpid(), group->socket_path);
9879         }
9880 
9881         if (unlink(group->socket_path) < 0 && errno != ENOENT) {
9882             ap_log_error(APLOG_MARK, APLOG_ERR, errno,
9883                          wsgi_server, "mod_wsgi (pid=%d): "
9884                          "Couldn't unlink unix domain socket '%s'.",
9885                          getpid(), group->socket_path);
9886         }
9887     }
9888 
9889     return APR_SUCCESS;
9890 }
9891 
wsgi_start_process(apr_pool_t * p,WSGIDaemonProcess * daemon)9892 static int wsgi_start_process(apr_pool_t *p, WSGIDaemonProcess *daemon)
9893 {
9894     apr_status_t status;
9895 
9896     ap_listen_rec *lr;
9897 
9898     WSGIProcessGroup *entries = NULL;
9899     WSGIProcessGroup *entry = NULL;
9900     int i = 0;
9901 
9902     if ((status = apr_proc_fork(&daemon->process, p)) < 0) {
9903         ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
9904                      "mod_wsgi: Couldn't spawn process '%s'.",
9905                      daemon->group->name);
9906         return DECLINED;
9907     }
9908     else if (status == APR_INCHILD) {
9909         if (!geteuid()) {
9910             ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9911                          "mod_wsgi (pid=%d): Starting process '%s' with "
9912                          "uid=%ld, gid=%u and threads=%d.", getpid(),
9913                          daemon->group->name, (long)daemon->group->uid,
9914                          (unsigned)daemon->group->gid, daemon->group->threads);
9915         }
9916         else {
9917             ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9918                          "mod_wsgi (pid=%d): Starting process '%s' with "
9919                          "threads=%d.", getpid(), daemon->group->name,
9920                          daemon->group->threads);
9921         }
9922 
9923 #ifdef HAVE_BINDPROCESSOR
9924         /*
9925          * By default, AIX binds to a single processor.  This
9926          * bit unbinds children which will then bind to another
9927          * CPU.
9928          */
9929 
9930         status = bindprocessor(BINDPROCESS, (int)getpid(),
9931                                PROCESSOR_CLASS_ANY);
9932         if (status != OK) {
9933             ap_log_error(APLOG_MARK, APLOG_ERR, errno, wsgi_server,
9934                          "mod_wsgi (pid=%d): Failed to unbind processor.",
9935                          getpid());
9936         }
9937 #endif
9938 
9939         /* Setup daemon process name displayed by 'ps'. */
9940 
9941         wsgi_setup_daemon_name(daemon, p);
9942 
9943         /* Adjust CPU priority if overridden. */
9944 
9945         if (daemon->group->cpu_priority != 0) {
9946             if (setpriority(PRIO_PROCESS, 0,
9947                             daemon->group->cpu_priority) == -1) {
9948                 ap_log_error(APLOG_MARK, APLOG_ERR, errno, wsgi_server,
9949                              "mod_wsgi (pid=%d): Couldn't set CPU priority "
9950                              "in daemon process '%d'.", getpid(),
9951                              daemon->group->cpu_priority);
9952             }
9953         }
9954 
9955         /* Setup daemon process user/group/umask etc. */
9956 
9957         if (wsgi_setup_access(daemon) == -1) {
9958             /*
9959              * If we get any failure from setting up the appropriate
9960              * permissions or working directory for the daemon process
9961              * then we exit the process. Don't die immediately to avoid
9962              * a fork bomb.
9963              */
9964 
9965             ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server,
9966                          "mod_wsgi (pid=%d): Failure to configure the "
9967                          "daemon process correctly and process left in "
9968                          "unspecified state. Restarting daemon process "
9969                          "after delay.", getpid());
9970 
9971             sleep(20);
9972 
9973             wsgi_exit_daemon_process(-1);
9974         }
9975 
9976         /* Reinitialise accept mutex in daemon process. */
9977 
9978         if (daemon->group->mutex) {
9979             status = apr_proc_mutex_child_init(&daemon->group->mutex,
9980                                                daemon->group->mutex_path, p);
9981 
9982             if (status != APR_SUCCESS) {
9983                 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
9984                              "mod_wsgi (pid=%d): Couldn't intialise accept "
9985                              "mutex in daemon process '%s'.",
9986                              getpid(), daemon->group->mutex_path);
9987 
9988                 /* Don't die immediately to avoid a fork bomb. */
9989 
9990                 sleep(20);
9991 
9992                 wsgi_exit_daemon_process(-1);
9993             }
9994         }
9995 
9996         /*
9997          * Create a lookup table of listener socket address
9998          * details so can use it later in daemon when trying
9999          * to map request to correct virtual host server.
10000          */
10001 
10002         wsgi_daemon_listeners = apr_hash_make(p);
10003 
10004         for (lr = ap_listeners; lr; lr = lr->next) {
10005             char *key;
10006             char *host;
10007             apr_port_t port;
10008 
10009             host = lr->bind_addr->hostname;
10010             port = lr->bind_addr->port;
10011 
10012             if (!host)
10013                 host = "";
10014 
10015             key = apr_psprintf(p, "%s|%d", host, port);
10016 
10017             apr_hash_set(wsgi_daemon_listeners, key, APR_HASH_KEY_STRING,
10018                          lr->bind_addr);
10019         }
10020 
10021         /*
10022          * Close child copy of the listening sockets for the
10023          * Apache parent process so we don't interfere with
10024          * the parent process.
10025          */
10026 
10027         ap_close_listeners();
10028 
10029         /*
10030          * Cleanup the Apache scoreboard to ensure that any
10031          * shared memory segments or memory mapped files not
10032          * available to code in daemon processes.
10033          */
10034 
10035         /*
10036          * XXX If this is closed, under Apache 2.4 then daemon
10037          * mode processes will crash. Not much choice but to
10038          * leave it open. Daemon mode really needs to be
10039          * rewritten not to use normal Apache request object and
10040          * output bucket chain to avoid potential for problems.
10041          */
10042 
10043 #if 0
10044         ap_cleanup_scoreboard(0);
10045 #endif
10046 
10047         /*
10048          * Wipe out random value used in magic token so that not
10049          * possible for user code running in daemon process to
10050          * discover this value for other daemon process groups.
10051          * In other words, wipe out all but our own.
10052          */
10053 
10054         entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
10055 
10056         for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
10057             entry = &entries[i];
10058 
10059             if (entry != daemon->group)
10060                 entry->random = 0;
10061         }
10062 
10063         /*
10064          * Close listener socket for daemon processes for other
10065          * daemon process groups. In other words, close all but
10066          * our own.
10067          */
10068 
10069         entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
10070 
10071         for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
10072             entry = &entries[i];
10073 
10074             if (entry != daemon->group && entry->listener_fd != -1) {
10075                 close(entry->listener_fd);
10076                 entry->listener_fd = -1;
10077             }
10078         }
10079 
10080         /*
10081          * Register signal handler to receive shutdown signal
10082          * from Apache parent process. We need to first create
10083          * pipe by which signal handler can notify the main
10084          * thread that signal has arrived indicating that
10085          * process needs to shutdown.
10086          */
10087 
10088         status = apr_file_pipe_create(&wsgi_signal_pipe_in,
10089                                       &wsgi_signal_pipe_out, p);
10090 
10091         if (status != APR_SUCCESS) {
10092             ap_log_error(APLOG_MARK, APLOG_EMERG, status, wsgi_server,
10093                          "mod_wsgi (pid=%d): Couldn't initialise signal "
10094                          "pipe in daemon process '%s'.", getpid(),
10095                          daemon->group->name);
10096 
10097             /* Don't die immediately to avoid a fork bomb. */
10098 
10099             sleep(20);
10100 
10101             wsgi_exit_daemon_process(-1);
10102         }
10103 
10104         wsgi_daemon_shutdown = 0;
10105 
10106         wsgi_daemon_pid = getpid();
10107 
10108         apr_signal(SIGINT, wsgi_signal_handler);
10109         apr_signal(SIGTERM, wsgi_signal_handler);
10110 
10111         apr_signal(AP_SIG_GRACEFUL, wsgi_signal_handler);
10112 
10113 #ifdef SIGXCPU
10114         apr_signal(SIGXCPU, wsgi_signal_handler);
10115 #endif
10116 
10117         /* Set limits on amount of CPU time that can be used. */
10118 
10119         if (daemon->group->cpu_time_limit > 0) {
10120             struct rlimit limit;
10121             int result = -1;
10122 
10123             limit.rlim_cur = daemon->group->cpu_time_limit;
10124 
10125             limit.rlim_max = daemon->group->cpu_time_limit + 1;
10126             limit.rlim_max += daemon->group->shutdown_timeout;
10127 
10128 #if defined(RLIMIT_CPU)
10129             result = setrlimit(RLIMIT_CPU, &limit);
10130 #endif
10131 
10132             if (result == -1) {
10133                 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
10134                              "mod_wsgi (pid=%d): Couldn't set CPU time "
10135                              "limit of %d seconds for process '%s'.", getpid(),
10136                              daemon->group->cpu_time_limit,
10137                              daemon->group->name);
10138             }
10139         }
10140 
10141         /*
10142          * Set limits on amount of date segment memory that can
10143          * be used. Although this is done, some platforms
10144          * doesn't actually support it.
10145          */
10146 
10147         if (daemon->group->memory_limit > 0) {
10148             struct rlimit limit;
10149             int result = -1;
10150 
10151             limit.rlim_cur = daemon->group->memory_limit;
10152 
10153             limit.rlim_max = daemon->group->memory_limit;
10154 
10155 #if defined(RLIMIT_DATA)
10156             result = setrlimit(RLIMIT_DATA, &limit);
10157 #endif
10158 
10159             if (result == -1) {
10160                 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
10161                              "mod_wsgi (pid=%d): Couldn't set memory "
10162                              "limit of %ld for process '%s'.", getpid(),
10163                              (long)daemon->group->memory_limit,
10164                              daemon->group->name);
10165             }
10166         }
10167 
10168         /*
10169          * Set limits on amount of virtual memory that can be used.
10170          * Although this is done, some platforms doesn't actually
10171          * support it.
10172          */
10173 
10174         if (daemon->group->virtual_memory_limit > 0) {
10175             struct rlimit limit;
10176             int result = -1;
10177 
10178             limit.rlim_cur = daemon->group->virtual_memory_limit;
10179 
10180             limit.rlim_max = daemon->group->virtual_memory_limit;
10181 
10182 #if defined(RLIMIT_AS)
10183             result = setrlimit(RLIMIT_AS, &limit);
10184 #elif defined(RLIMIT_VMEM)
10185             result = setrlimit(RLIMIT_VMEM, &limit);
10186 #endif
10187 
10188             if (result == -1) {
10189                 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
10190                              "mod_wsgi (pid=%d): Couldn't set virtual memory "
10191                              "limit of %ld for process '%s'.", getpid(),
10192                              (long)daemon->group->virtual_memory_limit,
10193                              daemon->group->name);
10194             }
10195         }
10196 
10197         /*
10198          * Flag whether multiple daemon processes or denoted
10199          * that requests could be spread across multiple daemon
10200          * process groups.
10201          */
10202 
10203         wsgi_multiprocess = daemon->group->multiprocess;
10204         wsgi_multithread = daemon->group->threads != 1;
10205 
10206         /*
10207          * Create a pool for the child daemon process so
10208          * we can trigger various events off it at shutdown.
10209          */
10210 
10211         apr_pool_create(&wsgi_daemon_pool, p);
10212 
10213         /*
10214          * Retain a reference to daemon process details. Do
10215          * this here as when doing lazy initialisation of
10216          * the interpreter we want to know if in a daemon
10217          * process so can pick any daemon process specific
10218          * home directory for Python installation.
10219          */
10220 
10221         wsgi_daemon_group = daemon->group->name;
10222         wsgi_daemon_process = daemon;
10223 
10224         /* Set lang/locale if specified for daemon process. */
10225 
10226         if (daemon->group->lang) {
10227             char *envvar;
10228 
10229             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10230                          "mod_wsgi (pid=%d): Setting lang to %s for "
10231                          "daemon process group %s.", getpid(),
10232                          daemon->group->lang, daemon->group->name);
10233 
10234             envvar = apr_pstrcat(p, "LANG=", daemon->group->lang, NULL);
10235             putenv(envvar);
10236         }
10237 
10238         if (daemon->group->locale) {
10239             char *envvar;
10240             char *result;
10241 
10242             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10243                          "mod_wsgi (pid=%d): Setting locale to %s for "
10244                          "daemon process group %s.", getpid(),
10245                          daemon->group->locale, daemon->group->name);
10246 
10247             envvar = apr_pstrcat(p, "LC_ALL=", daemon->group->locale, NULL);
10248             putenv(envvar);
10249 
10250             result = setlocale(LC_ALL, daemon->group->locale);
10251 
10252             if (!result) {
10253                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
10254                              "mod_wsgi (pid=%d): Unsupported locale setting "
10255                              "%s specified for daemon process group %s. "
10256                              "Consider using 'C.UTF-8' as fallback setting.",
10257                              getpid(), daemon->group->locale,
10258                              daemon->group->name);
10259             }
10260         }
10261 
10262         /* Create lock for request monitoring. */
10263 
10264         apr_thread_mutex_create(&wsgi_monitor_lock,
10265                                 APR_THREAD_MUTEX_UNNESTED, p);
10266 
10267         /*
10268          * Initialise Python if required to be done in the child
10269          * process. Note that it will not be initialised if
10270          * mod_python loaded and it has already been done.
10271          */
10272 
10273         if (wsgi_python_after_fork)
10274             wsgi_python_init(p);
10275 
10276 #if PY_MAJOR_VERSION < 3
10277         /*
10278          * If mod_python is also being loaded and thus it was
10279          * responsible for initialising Python it can leave in
10280          * place an active thread state. Under normal conditions
10281          * this would be eliminated in Apache child process by
10282          * the time that mod_wsgi got to do its own child
10283          * initialisation but in daemon process we skip the
10284          * mod_python child initialisation so the active thread
10285          * state still exists. Thus need to do a bit of a fiddle
10286          * to ensure there is no active thread state. Don't need
10287          * to worry about this with Python 3.X as mod_python
10288          * only supports Python 2.X.
10289          */
10290 
10291         if (!wsgi_python_initialized) {
10292             PyGILState_STATE state;
10293 
10294             PyEval_AcquireLock();
10295 
10296             state = PyGILState_Ensure();
10297             PyGILState_Release(state);
10298 
10299             if (state == PyGILState_LOCKED)
10300                 PyThreadState_Swap(NULL);
10301 
10302             PyEval_ReleaseLock();
10303         }
10304 #endif
10305 
10306         /*
10307          * If the daemon is associated with a virtual host then
10308          * we can close all other error logs so long as they
10309          * aren't the same one as being used for the virtual
10310          * host. If the virtual host error log is different to
10311          * the main server error log, then also tie stderr to
10312          * that log file instead. This way any debugging sent
10313          * direct to stderr from C code also goes to the virtual
10314          * host error log. We close the error logs that aren't
10315          * required as that eliminates possibility that user
10316          * code executing in daemon process could maliciously
10317          * dump messages into error log for a different virtual
10318          * host, as well as stop them being reopened with mode
10319          * that would allow seeking back to start of file and
10320          * read any information in them.
10321          */
10322 
10323         if (daemon->group->server->is_virtual) {
10324             server_rec *server = NULL;
10325             apr_file_t *errfile = NULL;
10326 
10327             /*
10328              * Iterate over all servers and close any error
10329              * logs different to that for virtual host. Note that
10330              * if errors are being redirected to syslog, then
10331              * the server error log reference will actually be
10332              * a null pointer, so need to ensure that check for
10333              * that and don't attempt to close it in that case.
10334              */
10335 
10336             server = wsgi_server;
10337 
10338             while (server != NULL) {
10339                 if (server->error_log &&
10340                     server->error_log != daemon->group->server->error_log) {
10341                     apr_file_close(server->error_log);
10342                 }
10343 
10344                 server = server->next;
10345             }
10346 
10347             /*
10348              * Reassociate stderr output with error log from the
10349              * virtual host the daemon is associated with. Close
10350              * the virtual host error log and point it at stderr
10351              * log instead. Do the latter so don't get two
10352              * references to same open file. Just in case
10353              * anything still accesses error log of main server,
10354              * map main server error log to that of the virtual
10355              * host. Note that cant do this if errors are being
10356              * redirected to syslog, as indicated by virtual
10357              * host error log being a null pointer. In that case
10358              * just leave everything as it was. Also can't remap
10359              * the error log for main server if it was being
10360              * redirected to syslog but virtual host wasn't.
10361              */
10362 
10363             if (daemon->group->server->error_log  &&
10364                 daemon->group->server->error_log != wsgi_server->error_log) {
10365 
10366                 apr_file_t *oldfile = NULL;
10367 
10368                 apr_file_open_stderr(&errfile, wsgi_server->process->pool);
10369                 apr_file_dup2(errfile, daemon->group->server->error_log,
10370                               wsgi_server->process->pool);
10371 
10372                 oldfile = daemon->group->server->error_log;
10373 
10374                 server = wsgi_server;
10375 
10376                 while (server != NULL) {
10377                     if (server->error_log == oldfile)
10378                         server->error_log = errfile;
10379                     server = server->next;
10380                 }
10381 
10382                 apr_file_close(oldfile);
10383 
10384                 if (wsgi_server->error_log)
10385                     wsgi_server->error_log = errfile;
10386             }
10387         }
10388 
10389         /*
10390          * Update reference to server object in case daemon
10391          * process is actually associated with a virtual host.
10392          * This way all logging actually goes into the virtual
10393          * hosts log file.
10394          */
10395 
10396         if (daemon->group->server) {
10397             if (wsgi_server_config->verbose_debugging) {
10398                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10399                              "mod_wsgi (pid=%d): Process '%s' logging to "
10400                              "'%s'.", getpid(), daemon->group->name,
10401                              daemon->group->server->server_hostname);
10402             }
10403 
10404             wsgi_server = daemon->group->server;
10405         }
10406         else {
10407             if (wsgi_server_config->verbose_debugging) {
10408                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10409                              "mod_wsgi (pid=%d): Process '%s' forced to log "
10410                              "to '%s'.", getpid(), daemon->group->name,
10411                              wsgi_server->server_hostname);
10412             }
10413         }
10414 
10415         /* Time daemon process started waiting for requests. */
10416 
10417         wsgi_restart_time = apr_time_now();
10418 
10419         /*
10420          * Setup Python in the child daemon process. Note that
10421          * we ensure that we are now marked as the original
10422          * initialiser of the Python interpreter even though
10423          * mod_python might have done it, as we will be the one
10424          * to cleanup the child daemon process and not
10425          * mod_python. We also need to perform the special
10426          * Python setup which has to be done after a fork.
10427          */
10428 
10429         wsgi_python_initialized = 1;
10430 
10431         wsgi_python_path = daemon->group->python_path;
10432         wsgi_python_eggs = daemon->group->python_eggs;
10433 
10434         wsgi_newrelic_config_file = daemon->group->newrelic_config_file;
10435         wsgi_newrelic_environment = daemon->group->newrelic_environment;
10436 
10437         wsgi_python_child_init(wsgi_daemon_pool);
10438 
10439         /*
10440          * Create socket wrapper for listener file descriptor
10441          * and mutex for controlling which thread gets to
10442          * perform the accept() when a connection is ready.
10443          */
10444 
10445         apr_os_sock_put(&daemon->listener, &daemon->group->listener_fd, p);
10446 
10447         /*
10448          * Run the main routine for the daemon process if there
10449          * is a non zero number of threads. When number of threads
10450          * is zero we actually go on and shutdown immediately.
10451          */
10452 
10453         if (daemon->group->threads != 0)
10454             wsgi_daemon_main(p, daemon);
10455 
10456         /*
10457          * Destroy the pool for the daemon process. This will
10458          * have the side affect of also destroying Python.
10459          */
10460 
10461         ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
10462                      "mod_wsgi (pid=%d): Stopping process '%s'.", getpid(),
10463                      daemon->group->name);
10464 
10465         apr_pool_destroy(wsgi_daemon_pool);
10466 
10467         /* Exit the daemon process when being shutdown. */
10468 
10469         wsgi_exit_daemon_process(0);
10470     }
10471 
10472 #ifdef HAVE_FORK
10473     if (wsgi_python_initialized) {
10474 #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 7)
10475         PyOS_AfterFork_Parent();
10476 #endif
10477     }
10478 #endif
10479 
10480     apr_pool_note_subprocess(p, &daemon->process, APR_KILL_AFTER_TIMEOUT);
10481     apr_proc_other_child_register(&daemon->process, wsgi_manage_process,
10482                                   daemon, NULL, p);
10483 
10484     return OK;
10485 }
10486 
wsgi_start_daemons(apr_pool_t * p)10487 static int wsgi_start_daemons(apr_pool_t *p)
10488 {
10489     WSGIProcessGroup *entries = NULL;
10490     WSGIProcessGroup *entry = NULL;
10491     WSGIDaemonProcess *process = NULL;
10492 
10493     int mpm_generation = 0;
10494 
10495     int i, j;
10496 
10497     /* Do we need to create any daemon processes. */
10498 
10499     if (!wsgi_daemon_list)
10500         return OK;
10501 
10502     /* What server generation is this. */
10503 
10504 #if defined(AP_MPMQ_GENERATION)
10505     ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
10506 #else
10507     mpm_generation = ap_my_generation;
10508 #endif
10509 
10510     /*
10511      * Cache references to root server and pool as will need
10512      * to access these when restarting daemon process when
10513      * they die.
10514      */
10515 
10516     wsgi_parent_pool = p;
10517 
10518     /*
10519      * Startup in turn the required number of daemon processes
10520      * for each of the named process groups.
10521      */
10522 
10523     wsgi_daemon_index = apr_hash_make(p);
10524 
10525     entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
10526 
10527     for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
10528         int status;
10529 
10530         entry = &entries[i];
10531 
10532         /*
10533          * Check for whether the daemon process user and
10534          * group are the default Apache values. If they are
10535          * then reset them to the current values configured for
10536          * Apache. This is to work around where the User/Group
10537          * directives had not been set before the WSGIDaemonProcess
10538          * directive was used in configuration file. In this case,
10539          * where no 'user' and 'group' options were provided,
10540          * the default values would have been used, but these
10541          * were later overridden thus why we need to update it.
10542          */
10543 
10544         if (entry->uid == ap_uname2id(DEFAULT_USER)) {
10545             entry->uid = ap_unixd_config.user_id;
10546             entry->user = ap_unixd_config.user_name;
10547 
10548             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10549                          "mod_wsgi (pid=%d): Reset default user for "
10550                          "daemon process group '%s' to uid=%ld.",
10551                          getpid(), entry->name, (long)entry->uid);
10552         }
10553 
10554         if (entry->gid == ap_gname2id(DEFAULT_GROUP)) {
10555             entry->gid = ap_unixd_config.group_id;
10556 
10557             ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10558                          "mod_wsgi (pid=%d): Reset default group for "
10559                          "daemon process group '%s' to gid=%ld.",
10560                          getpid(), entry->name, (long)entry->gid);
10561         }
10562 
10563         /*
10564          * Calculate path for socket to accept requests on and
10565          * create the socket.
10566          */
10567 
10568         entry->socket_rotation = wsgi_server_config->socket_rotation;
10569 
10570         if (entry->socket_rotation) {
10571             entry->socket_path = apr_psprintf(p, "%s.%d.%d.%d.sock",
10572                                          wsgi_server_config->socket_prefix,
10573                                          getpid(), mpm_generation, entry->id);
10574         }
10575         else {
10576             entry->socket_path = apr_psprintf(p, "%s.%d.u%d.%d.sock",
10577                                          wsgi_server_config->socket_prefix,
10578                                          getpid(), entry->uid, entry->id);
10579         }
10580 
10581         apr_hash_set(wsgi_daemon_index, entry->name, APR_HASH_KEY_STRING,
10582                      entry);
10583 
10584         entry->listener_fd = wsgi_setup_socket(entry);
10585 
10586         if (entry->listener_fd == -1)
10587             return DECLINED;
10588 
10589         /*
10590          * Register cleanup so that listener socket is cleaned
10591          * up properly on a restart and on shutdown.
10592          */
10593 
10594         apr_pool_cleanup_register(p, entry, wsgi_cleanup_process,
10595                                   apr_pool_cleanup_null);
10596 
10597         /*
10598          * If there is more than one daemon process in the group
10599          * then need to create an accept mutex for the daemon
10600          * processes to use so they don't interfere with each
10601          * other.
10602          */
10603 
10604         if (entry->processes > 1) {
10605             entry->mutex_path = apr_psprintf(p, "%s.%d.%d.%d.lock",
10606                                              wsgi_server_config->socket_prefix,
10607                                              getpid(), mpm_generation,
10608                                              entry->id);
10609 
10610             status = apr_proc_mutex_create(&entry->mutex, entry->mutex_path,
10611                                            wsgi_server_config->lock_mechanism,
10612                                            p);
10613 
10614             if (status != APR_SUCCESS) {
10615                 ap_log_error(APLOG_MARK, APLOG_CRIT, errno, wsgi_server,
10616                              "mod_wsgi (pid=%d): Couldn't create accept "
10617                              "lock '%s' (%d).", getpid(), entry->mutex_path,
10618                              wsgi_server_config->lock_mechanism);
10619                 return DECLINED;
10620             }
10621 
10622             /*
10623              * Depending on the locking mechanism being used
10624              * need to change the permissions of the lock. Can't
10625              * use unixd_set_proc_mutex_perms() as it uses the
10626              * default Apache child process uid/gid where the
10627              * daemon process uid/gid can be different.
10628              */
10629 
10630             if (!geteuid()) {
10631 #if APR_HAS_SYSVSEM_SERIALIZE
10632                 if (!strcmp(apr_proc_mutex_name(entry->mutex), "sysvsem")) {
10633                     apr_os_proc_mutex_t ospmutex;
10634 #if !APR_HAVE_UNION_SEMUN
10635                     union semun {
10636                         long val;
10637                         struct semid_ds *buf;
10638                         unsigned short *array;
10639                     };
10640 #endif
10641                     union semun ick;
10642                     struct semid_ds buf;
10643 
10644                     apr_os_proc_mutex_get(&ospmutex, entry->mutex);
10645                     buf.sem_perm.uid = entry->uid;
10646                     buf.sem_perm.gid = entry->gid;
10647                     buf.sem_perm.mode = 0600;
10648                     ick.buf = &buf;
10649                     if (semctl(ospmutex.crossproc, 0, IPC_SET, ick) < 0) {
10650                         ap_log_error(APLOG_MARK, APLOG_CRIT, errno,
10651                                      wsgi_server, "mod_wsgi (pid=%d): "
10652                                      "Couldn't set permissions on accept "
10653                                      "mutex '%s' (sysvsem).", getpid(),
10654                                      entry->mutex_path);
10655                         return DECLINED;
10656                     }
10657                 }
10658 #endif
10659 #if APR_HAS_FLOCK_SERIALIZE
10660                 if (!strcmp(apr_proc_mutex_name(entry->mutex), "flock")) {
10661                     if (chown(entry->mutex_path, entry->uid, -1) < 0) {
10662                         ap_log_error(APLOG_MARK, APLOG_CRIT, errno,
10663                                      wsgi_server, "mod_wsgi (pid=%d): "
10664                                      "Couldn't set permissions on accept "
10665                                      "mutex '%s' (flock).", getpid(),
10666                                      entry->mutex_path);
10667                         return DECLINED;
10668                     }
10669                 }
10670 #endif
10671             }
10672         }
10673 
10674         /* Create the actual required daemon processes. */
10675 
10676         for (j = 1; j <= entry->processes; j++) {
10677             process = (WSGIDaemonProcess *)apr_pcalloc(p, sizeof(
10678                                                        WSGIDaemonProcess));
10679 
10680             process->group = entry;
10681             process->instance = j;
10682 
10683             status = wsgi_start_process(p, process);
10684 
10685             if (status != OK)
10686                 return status;
10687         }
10688     }
10689 
10690     return OK;
10691 }
10692 
10693 static apr_pool_t *wsgi_pconf_pool = NULL;
10694 
wsgi_deferred_start_daemons(apr_pool_t * p,ap_scoreboard_e sb_type)10695 static int wsgi_deferred_start_daemons(apr_pool_t *p, ap_scoreboard_e sb_type)
10696 {
10697     return wsgi_start_daemons(wsgi_pconf_pool);
10698 }
10699 
wsgi_socket_connect_un(apr_socket_t * sock,struct sockaddr_un * sa)10700 static apr_status_t wsgi_socket_connect_un(apr_socket_t *sock,
10701                                            struct sockaddr_un *sa)
10702 {
10703     apr_status_t rv;
10704     apr_os_sock_t rawsock;
10705     apr_interval_time_t t;
10706 
10707     rv = apr_os_sock_get(&rawsock, sock);
10708     if (rv != APR_SUCCESS) {
10709         return rv;
10710     }
10711 
10712     rv = apr_socket_timeout_get(sock, &t);
10713     if (rv != APR_SUCCESS) {
10714         return rv;
10715     }
10716 
10717     do {
10718         rv = connect(rawsock, (struct sockaddr*)sa,
10719                      APR_OFFSETOF(struct sockaddr_un, sun_path)
10720                      + strlen(sa->sun_path) + 1);
10721     } while (rv == -1 && errno == EINTR);
10722 
10723     if ((rv == -1) && (errno == EINPROGRESS || errno == EALREADY)
10724         && (t > 0)) {
10725 #if APR_MAJOR_VERSION < 2
10726         rv = apr_wait_for_io_or_timeout(NULL, sock, 0);
10727 #else
10728         rv = apr_socket_wait(sock, APR_WAIT_WRITE);
10729 #endif
10730 
10731         if (rv != APR_SUCCESS) {
10732             return rv;
10733         }
10734     }
10735 
10736     if (rv == -1 && errno != EISCONN) {
10737         return errno;
10738     }
10739 
10740     return APR_SUCCESS;
10741 }
10742 
wsgi_connect_daemon(request_rec * r,WSGIDaemonSocket * daemon)10743 static int wsgi_connect_daemon(request_rec *r, WSGIDaemonSocket *daemon)
10744 {
10745     WSGIRequestConfig *config = NULL;
10746 
10747     apr_status_t rv;
10748 
10749     struct sockaddr_un addr;
10750 
10751     int retries = 0;
10752     apr_interval_time_t timer = 0;
10753     apr_interval_time_t total_time = 0;
10754 
10755     apr_time_t start_time = 0;
10756 
10757     /* Grab request configuration. */
10758 
10759     config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
10760                                                        &wsgi_module);
10761 
10762     memset(&addr, 0, sizeof(addr));
10763     addr.sun_family = AF_UNIX;
10764     apr_cpystrn(addr.sun_path, daemon->socket_path, sizeof(addr.sun_path));
10765 
10766     start_time = apr_time_now();
10767 
10768     while (1) {
10769         retries++;
10770 
10771         config->daemon_connects++;
10772 
10773         rv = apr_socket_create(&daemon->socket, AF_UNIX, SOCK_STREAM,
10774                                0, r->pool);
10775 
10776         if (rv != APR_SUCCESS) {
10777             ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r,
10778                          "mod_wsgi (pid=%d): Unable to create socket to "
10779                          "connect to WSGI daemon process.", getpid());
10780 
10781             return HTTP_INTERNAL_SERVER_ERROR;
10782         }
10783 
10784         /*
10785          * Apply timeout before issuing the socket connection in
10786          * case this hangs for some reason. Would have to be an extreme
10787          * event for a UNIX socket connect to hang, but have had some
10788          * unexplained situations which look exactly like that.
10789          */
10790 
10791         if (daemon->socket_timeout)
10792             apr_socket_timeout_set(daemon->socket, daemon->socket_timeout);
10793         else
10794             apr_socket_timeout_set(daemon->socket, r->server->timeout);
10795 
10796         rv = wsgi_socket_connect_un(daemon->socket, &addr);
10797 
10798         if (rv != APR_SUCCESS) {
10799             /*
10800              * We need to check for both connection refused and
10801              * connection unavailable as Linux systems when
10802              * connecting to a UNIX listener socket in non
10803              * blocking mode, where the listener backlog is full
10804              * will return the error EAGAIN rather than returning
10805              * ECONNREFUSED as is supposedly dictated by POSIX.
10806              */
10807 
10808             if (APR_STATUS_IS_ECONNREFUSED(rv) || APR_STATUS_IS_EAGAIN(rv)) {
10809                 if ((apr_time_now()-start_time) < daemon->connect_timeout) {
10810                     if (wsgi_server_config->verbose_debugging) {
10811                         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r,
10812                                      "mod_wsgi (pid=%d): Connection attempt "
10813                                      "#%d to WSGI daemon process '%s' on "
10814                                      "'%s' failed, sleeping before retrying "
10815                                      "again.", getpid(), retries,
10816                                      daemon->name, daemon->socket_path);
10817                     }
10818 
10819                     apr_socket_close(daemon->socket);
10820 
10821                     /*
10822                      * Progressively increase time we wait between
10823                      * connection attempts. Start at 0.125 second, but
10824                      * back off to 1 second interval after 2 seconds.
10825                      */
10826 
10827                     if (total_time < apr_time_make(2, 0))
10828                         timer = apr_time_make(0, 125000);
10829                     else
10830                         timer = apr_time_make(1, 0);
10831 
10832                     apr_sleep(timer);
10833 
10834                     total_time += timer;
10835                 }
10836                 else {
10837                     ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
10838                                  "mod_wsgi (pid=%d): Unable to connect to "
10839                                  "WSGI daemon process '%s' on '%s' after "
10840                                  "multiple attempts as listener backlog "
10841                                  "limit was exceeded or the socket does "
10842                                  "not exist.", getpid(), daemon->name,
10843                                  daemon->socket_path);
10844 
10845                     apr_socket_close(daemon->socket);
10846 
10847                     return HTTP_SERVICE_UNAVAILABLE;
10848                 }
10849             }
10850             else {
10851                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
10852                              "mod_wsgi (pid=%d): Unable to connect to "
10853                              "WSGI daemon process '%s' on '%s' as user "
10854                              "with uid=%ld.", getpid(), daemon->name,
10855                              daemon->socket_path, (long)geteuid());
10856 
10857                 apr_socket_close(daemon->socket);
10858 
10859                 return HTTP_SERVICE_UNAVAILABLE;
10860             }
10861         }
10862         else
10863             break;
10864     }
10865 
10866     return OK;
10867 }
10868 
wsgi_socket_send(apr_socket_t * sock,const char * buf,size_t buf_size)10869 static apr_status_t wsgi_socket_send(apr_socket_t *sock, const char *buf,
10870                                      size_t buf_size)
10871 {
10872     apr_status_t rv;
10873     apr_size_t len;
10874 
10875     while (buf_size > 0)
10876     {
10877         len = buf_size;
10878 
10879         rv = apr_socket_send(sock, buf, &len);
10880 
10881         if (rv != APR_SUCCESS)
10882             return rv;
10883 
10884         buf += len;
10885         buf_size -= len;
10886     }
10887 
10888     return APR_SUCCESS;
10889 }
10890 
wsgi_socket_sendv_limit(apr_socket_t * sock,struct iovec * vec,size_t nvec)10891 static apr_status_t wsgi_socket_sendv_limit(apr_socket_t *sock,
10892         struct iovec *vec, size_t nvec)
10893 {
10894     apr_status_t rv;
10895     apr_size_t written = 0;
10896     apr_size_t to_write = 0;
10897     size_t i, offset;
10898 
10899     /* Calculate how much has to be sent. */
10900 
10901     for (i = 0; i < nvec; i++) {
10902         to_write += vec[i].iov_len;
10903     }
10904 
10905     /* Loop until all data has been sent. */
10906 
10907     offset = 0;
10908 
10909     while (to_write) {
10910         apr_size_t n = 0;
10911 
10912         rv = apr_socket_sendv(sock, vec+offset, nvec-offset, &n);
10913 
10914         if (rv != APR_SUCCESS)
10915             return rv;
10916 
10917         if (n > 0) {
10918             /* Bail out of all data has been sent. */
10919 
10920             written += n;
10921 
10922             if (written >= to_write)
10923                 break;
10924 
10925             /*
10926              * Not all data was sent, so ween need to try
10927              * again with the remainder of the data. We
10928              * first need to work out where to start from.
10929              */
10930 
10931             for (i = offset; i < nvec; ) {
10932                 if (n >= vec[i].iov_len) {
10933                     offset++;
10934                     n -= vec[i++].iov_len;
10935                 } else {
10936                     vec[i].iov_len -= n;
10937                     vec[i].iov_base = (char *) vec[i].iov_base + n;
10938                     break;
10939                 }
10940             }
10941         }
10942     }
10943 
10944     return APR_SUCCESS;
10945 }
10946 
wsgi_socket_sendv(apr_socket_t * sock,struct iovec * vec,size_t nvec)10947 static apr_status_t wsgi_socket_sendv(apr_socket_t *sock, struct iovec *vec,
10948                                       size_t nvec)
10949 {
10950 #if defined(_SC_IOV_MAX)
10951     static size_t iov_max = 0;
10952 
10953     if (iov_max == 0)
10954         iov_max = sysconf(_SC_IOV_MAX);
10955 #else
10956     static size_t iov_max = APR_MAX_IOVEC_SIZE;
10957 #endif
10958 
10959     if (nvec > iov_max) {
10960         int offset = 0;
10961 
10962         while (nvec != 0) {
10963             apr_status_t rv;
10964 
10965             rv = wsgi_socket_sendv_limit(sock, &vec[offset],
10966                     (nvec < iov_max ? nvec : (int)iov_max));
10967 
10968             if (rv != APR_SUCCESS)
10969                 return rv;
10970 
10971             if (nvec > iov_max) {
10972                 nvec -= iov_max;
10973                 offset += iov_max;
10974             } else {
10975                 nvec = 0;
10976             }
10977         }
10978 
10979         return APR_SUCCESS;
10980     }
10981     else
10982         return wsgi_socket_sendv_limit(sock, vec, nvec);
10983 }
10984 
wsgi_send_request(request_rec * r,WSGIRequestConfig * config,WSGIDaemonSocket * daemon)10985 static apr_status_t wsgi_send_request(request_rec *r,
10986                                       WSGIRequestConfig *config,
10987                                       WSGIDaemonSocket *daemon)
10988 {
10989     int rv;
10990 
10991     const apr_array_header_t *env_arr;
10992     const apr_table_entry_t *elts;
10993     int i;
10994 
10995     struct iovec *vec;
10996     struct iovec *vec_start;
10997     struct iovec *vec_next;
10998 
10999     apr_size_t total = 0;
11000     apr_size_t count = 0;
11001 
11002     apr_table_setn(r->subprocess_env, "mod_wsgi.daemon_connects",
11003                    apr_psprintf(r->pool, "%d", config->daemon_connects));
11004     apr_table_setn(r->subprocess_env, "mod_wsgi.daemon_restarts",
11005                    apr_psprintf(r->pool, "%d", config->daemon_restarts));
11006 
11007     /* Send subprocess environment from request object. */
11008 
11009     env_arr = apr_table_elts(r->subprocess_env);
11010     elts = (const apr_table_entry_t *)env_arr->elts;
11011 
11012     /*
11013      * Sending total amount of data, followed by count of separate
11014      * strings and then each null terminated string. The total is
11015      * inclusive of the bytes used for the count of the strings.
11016      */
11017 
11018     vec = (struct iovec *)apr_palloc(r->pool, (2+(2*env_arr->nelts))*
11019                                      sizeof(struct iovec));
11020 
11021     vec_start = &vec[2];
11022     vec_next = vec_start;
11023 
11024     for (i=0; i<env_arr->nelts; ++i) {
11025         if (!elts[i].key)
11026             continue;
11027 
11028         vec_next->iov_base = (void*)elts[i].key;
11029         vec_next->iov_len = strlen(elts[i].key) + 1;
11030 
11031         total += vec_next->iov_len;
11032 
11033         vec_next++;
11034 
11035         if (elts[i].val) {
11036             vec_next->iov_base = (void*)elts[i].val;
11037             vec_next->iov_len = strlen(elts[i].val) + 1;
11038         }
11039         else
11040         {
11041             vec_next->iov_base = (void*)"";
11042             vec_next->iov_len = 1;
11043         }
11044 
11045         total += vec_next->iov_len;
11046 
11047         vec_next++;
11048     }
11049 
11050     count = vec_next - vec_start;
11051 
11052     vec[1].iov_base = (void*)&count;
11053     vec[1].iov_len = sizeof(count);
11054 
11055     total += vec[1].iov_len;
11056 
11057     vec[0].iov_base = (void*)&total;
11058     vec[0].iov_len = sizeof(total);
11059 
11060     rv = wsgi_socket_sendv(daemon->socket, vec, (int)(vec_next-vec));
11061 
11062     if (rv != APR_SUCCESS)
11063         return rv;
11064 
11065     return APR_SUCCESS;
11066 }
11067 
wsgi_discard_output(apr_bucket_brigade * bb)11068 static void wsgi_discard_output(apr_bucket_brigade *bb)
11069 {
11070     apr_bucket *e;
11071     const char *buf;
11072     apr_size_t len;
11073     apr_status_t rv;
11074 
11075     for (e = APR_BRIGADE_FIRST(bb);
11076          e != APR_BRIGADE_SENTINEL(bb);
11077          e = APR_BUCKET_NEXT(e))
11078     {
11079         if (APR_BUCKET_IS_EOS(e)) {
11080             break;
11081         }
11082         rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
11083         if (rv != APR_SUCCESS) {
11084             break;
11085         }
11086     }
11087 }
11088 
wsgi_copy_header(void * v,const char * key,const char * val)11089 static int wsgi_copy_header(void *v, const char *key, const char *val)
11090 {
11091     apr_table_addn(v, key, val);
11092     return 1;
11093 }
11094 
11095 #define HTTP_UNSET (-HTTP_OK)
11096 
wsgi_scan_headers(request_rec * r,char * buffer,int buflen,int (* getsfunc)(char *,int,void *),void * getsfunc_data)11097 static int wsgi_scan_headers(request_rec *r, char *buffer, int buflen,
11098                              int (*getsfunc) (char *, int, void *),
11099                              void *getsfunc_data)
11100 {
11101     char x[32768];
11102     char *w, *l;
11103     size_t p;
11104 
11105     int cgi_status = HTTP_UNSET;
11106 
11107     apr_table_t *merge;
11108     apr_table_t *cookie_table;
11109     apr_table_t *authen_table;
11110 
11111     WSGIRequestConfig *config = NULL;
11112 
11113     config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
11114                                                        &wsgi_module);
11115 
11116     /*
11117      * Default to internal fixed size buffer for reading headers if one
11118      * is not supplied explicitly with the call.
11119      */
11120 
11121     if (buffer)
11122         *buffer = '\0';
11123 
11124     w = buffer ? buffer : x;
11125     buflen = buffer ? buflen : sizeof(x);
11126 
11127     /* Temporary place to hold headers as we read them. */
11128 
11129     merge = apr_table_make(r->pool, 10);
11130 
11131     /*
11132      * The HTTP specification says that it is legal to merge duplicate
11133      * headers into one. Some browsers don't like certain headers being
11134      * merged however. These headers are Set-Cookie and WWW-Authenticate.
11135      * We will therefore keep these separate and merge them back in
11136      * independently at the end. Before we start though, we need to make
11137      * sure we save away any instances of these headers which may already
11138      * be listed in the request structure for some reason.
11139      */
11140 
11141     cookie_table = apr_table_make(r->pool, 2);
11142     apr_table_do(wsgi_copy_header, cookie_table, r->headers_out,
11143                  "Set-Cookie", NULL);
11144 
11145     authen_table = apr_table_make(r->pool, 2);
11146     apr_table_do(wsgi_copy_header, authen_table, r->err_headers_out,
11147                  "WWW-Authenticate", NULL);
11148 
11149     while (1) {
11150         int rv = (*getsfunc) (w, buflen - 1, getsfunc_data);
11151 
11152         if (rv == 0) {
11153             wsgi_log_script_error(r, apr_psprintf(r->pool, "Truncated or "
11154                                   "oversized response headers received from "
11155                                   "daemon process '%s'",
11156                                   config->process_group), r->filename);
11157 
11158             r->status_line = NULL;
11159 
11160             return HTTP_INTERNAL_SERVER_ERROR;
11161         }
11162         else if (rv == -1) {
11163             wsgi_log_script_error(r, apr_psprintf(r->pool, "Timeout when "
11164                                   "reading response headers from daemon "
11165                                   "process '%s'", config->process_group),
11166                                   r->filename);
11167 
11168             r->status_line = NULL;
11169 
11170             return HTTP_GATEWAY_TIME_OUT;
11171         }
11172 
11173         /*
11174          * Delete any trailing (CR?)LF. Indeed, the host's '\n':
11175          * '\012' for UNIX; '\015' for MacOS; '\025' for OS/390.
11176          */
11177 
11178         p = strlen(w);
11179 
11180         if (p > 0 && w[p - 1] == '\n') {
11181             if (p > 1 && w[p - 2] == CR) {
11182                 w[p - 2] = '\0';
11183             }
11184             else {
11185                 w[p - 1] = '\0';
11186             }
11187         }
11188 
11189         /*
11190          * If we've finished reading the headers, check to make sure
11191          * any HTTP/1.1 conditions are met. If so, we're done; normal
11192          * processing will handle the script's output. If not, just
11193          * return the error.
11194          */
11195 
11196         if (w[0] == '\0') {
11197             int cond_status = OK;
11198 
11199            /*
11200             * This fails because it gets confused when a CGI Status
11201             * header overrides ap_meets_conditions.
11202             *
11203             * We can fix that by dropping ap_meets_conditions when
11204             * Status has been set.  Since this is the only place
11205             * cgi_status gets used, let's test it explicitly.
11206             *
11207             * The alternative would be to ignore CGI Status when
11208             * ap_meets_conditions returns anything interesting. That
11209             * would be safer wrt HTTP, but would break CGI.
11210             */
11211 
11212             if ((cgi_status == HTTP_UNSET) && (r->method_number == M_GET)) {
11213                 cond_status = ap_meets_conditions(r);
11214             }
11215 
11216             /*
11217              * Merge the headers received back into the request
11218              * structure. There should only be one per header with
11219              * values combined for these.
11220              */
11221 
11222             apr_table_overlap(r->headers_out, merge,
11223                               APR_OVERLAP_TABLES_MERGE);
11224 
11225             /*
11226              * Now add in the special headers which we can't merge
11227              * because it gives certain browsers problems.
11228              */
11229 
11230             if (!apr_is_empty_table(cookie_table)) {
11231                 apr_table_unset(r->headers_out, "Set-Cookie");
11232                 r->headers_out = apr_table_overlay(r->pool,
11233                     r->headers_out, cookie_table);
11234             }
11235 
11236             if (!apr_is_empty_table(authen_table)) {
11237                 apr_table_unset(r->err_headers_out, "WWW-Authenticate");
11238                 r->err_headers_out = apr_table_overlay(r->pool,
11239                     r->err_headers_out, authen_table);
11240             }
11241 
11242             return cond_status;
11243         }
11244 
11245         /* If we see a bogus header don't ignore it. Shout and scream. */
11246 
11247         if (!(l = strchr(w, ':'))) {
11248             char malformed[32];
11249 
11250             strncpy(malformed, w, sizeof(malformed)-1);
11251             malformed[sizeof(malformed)-1] = '\0';
11252 
11253             if (!buffer) {
11254                 /* Soak up all the script output. */
11255 
11256                 while ((*getsfunc)(w, buflen - 1, getsfunc_data) > 0) {
11257                     continue;
11258                 }
11259             }
11260 
11261             wsgi_log_script_error(r, apr_psprintf(r->pool, "Malformed "
11262                                   "header '%s' found when reading script "
11263                                   "headers from daemon process '%s'",
11264                                   malformed, config->process_group),
11265                                   r->filename);
11266 
11267             r->status_line = NULL;
11268 
11269             return HTTP_INTERNAL_SERVER_ERROR;
11270         }
11271 
11272         /* Strip leading white space from header value. */
11273 
11274         *l++ = '\0';
11275         while (*l && apr_isspace(*l)) {
11276             ++l;
11277         }
11278 
11279         if (!strcasecmp(w, "Content-type")) {
11280             char *tmp;
11281 
11282             /* Nuke trailing whitespace. */
11283 
11284             char *endp = l + strlen(l) - 1;
11285             while (endp > l && apr_isspace(*endp)) {
11286                 *endp-- = '\0';
11287             }
11288 
11289             tmp = apr_pstrdup(r->pool, l);
11290             ap_content_type_tolower(tmp);
11291             ap_set_content_type(r, tmp);
11292         }
11293         else if (!strcasecmp(w, "Status")) {
11294             /*
11295              * If the script returned a specific status, that's what
11296              * we'll use, otherwise we assume 200 OK.
11297              */
11298 
11299             r->status = cgi_status = atoi(l);
11300             r->status_line = apr_pstrdup(r->pool, l);
11301         }
11302         else if (!strcasecmp(w, "Location")) {
11303             apr_table_set(r->headers_out, w, l);
11304         }
11305         else if (!strcasecmp(w, "Content-Length")) {
11306             apr_table_set(r->headers_out, w, l);
11307         }
11308         else if (!strcasecmp(w, "Content-Range")) {
11309             apr_table_set(r->headers_out, w, l);
11310         }
11311         else if (!strcasecmp(w, "Transfer-Encoding")) {
11312             apr_table_set(r->headers_out, w, l);
11313         }
11314         else if (!strcasecmp(w, "Last-Modified")) {
11315             /*
11316              * If the script gave us a Last-Modified header, we can't just
11317              * pass it on blindly because of restrictions on future values.
11318              */
11319 
11320             ap_update_mtime(r, apr_date_parse_http(l));
11321             ap_set_last_modified(r);
11322         }
11323         else if (!strcasecmp(w, "Set-Cookie")) {
11324             apr_table_add(cookie_table, w, l);
11325         }
11326         else if (!strcasecmp(w, "WWW-Authenticate")) {
11327             apr_table_add(authen_table, w, l);
11328         }
11329         else {
11330             apr_table_add(merge, w, l);
11331         }
11332     }
11333 
11334     return OK;
11335 }
11336 
wsgi_getsfunc_brigade(char * buf,int len,void * arg)11337 static int wsgi_getsfunc_brigade(char *buf, int len, void *arg)
11338 {
11339     apr_bucket_brigade *bb = (apr_bucket_brigade *)arg;
11340     const char *dst_end = buf + len - 1;
11341     char *dst = buf;
11342     apr_bucket *e = APR_BRIGADE_FIRST(bb);
11343     apr_status_t rv;
11344     int done = 0;
11345 
11346     while ((dst < dst_end) && !done && e != APR_BRIGADE_SENTINEL(bb)
11347            && !APR_BUCKET_IS_EOS(e)) {
11348         const char *bucket_data;
11349         apr_size_t bucket_data_len;
11350         const char *src;
11351         const char *src_end;
11352         apr_bucket * next;
11353 
11354         rv = apr_bucket_read(e, &bucket_data, &bucket_data_len,
11355                              APR_BLOCK_READ);
11356         if (rv != APR_SUCCESS || (bucket_data_len == 0)) {
11357             *dst = '\0';
11358             return APR_STATUS_IS_TIMEUP(rv) ? -1 : 0;
11359         }
11360         src = bucket_data;
11361         src_end = bucket_data + bucket_data_len;
11362         while ((src < src_end) && (dst < dst_end) && !done) {
11363             if (*src == '\n') {
11364                 done = 1;
11365             }
11366             else if (*src != '\r') {
11367                 *dst++ = *src;
11368             }
11369             src++;
11370         }
11371 
11372         if (src < src_end) {
11373             apr_bucket_split(e, src - bucket_data);
11374         }
11375         next = APR_BUCKET_NEXT(e);
11376         APR_BUCKET_REMOVE(e);
11377         apr_bucket_destroy(e);
11378         e = next;
11379     }
11380     *dst = '\0';
11381     return done;
11382 }
11383 
wsgi_scan_headers_brigade(request_rec * r,apr_bucket_brigade * bb,char * buffer,int buflen)11384 static int wsgi_scan_headers_brigade(request_rec *r,
11385                                      apr_bucket_brigade *bb,
11386                                      char *buffer, int buflen)
11387 {
11388     return wsgi_scan_headers(r, buffer, buflen, wsgi_getsfunc_brigade, bb);
11389 }
11390 
wsgi_transfer_response(request_rec * r,apr_bucket_brigade * bb,apr_size_t buffer_size,apr_time_t timeout)11391 static int wsgi_transfer_response(request_rec *r, apr_bucket_brigade *bb,
11392                                   apr_size_t buffer_size, apr_time_t timeout)
11393 {
11394     apr_bucket *e;
11395     apr_read_type_e mode = APR_NONBLOCK_READ;
11396 
11397     apr_bucket_brigade *tmpbb;
11398 
11399     const char *data = NULL;
11400     apr_size_t length = 0;
11401 
11402     apr_size_t bytes_transfered = 0;
11403 
11404     int bucket_count = 0;
11405 
11406     apr_status_t rv;
11407 
11408 #if AP_MODULE_MAGIC_AT_LEAST(20110605, 2)
11409     apr_socket_t *sock;
11410     apr_interval_time_t existing_timeout = 0;
11411 #endif
11412 
11413     if (buffer_size == 0)
11414         buffer_size = 65536;
11415 
11416     /*
11417      * Override the socket timeout for writing back data to the
11418      * client. If that wasn't defined this will be the same as
11419      * the timeout for the socket used in communicating with the
11420      * daemon, or left as the overall server timeout if that
11421      * isn't specified. Just to be safe we remember the existing
11422      * timeout and restore it at the end of a successful request
11423      * in case the same connection if kept alive and used for a
11424      * subsequent request with a different handler.
11425      */
11426 
11427 #if AP_MODULE_MAGIC_AT_LEAST(20110605, 2)
11428     sock = ap_get_conn_socket(r->connection);
11429 
11430     rv = apr_socket_timeout_get(sock, &existing_timeout);
11431 
11432     if (rv != APR_SUCCESS) {
11433         existing_timeout = 0;
11434     }
11435     else {
11436         if (timeout)
11437             apr_socket_timeout_set(sock, timeout);
11438     }
11439 #endif
11440 
11441     /*
11442      * Transfer any response content. We want to avoid the
11443      * problem where the core output filter has no flow control
11444      * to deal with slow HTTP clients and can actually buffer up
11445      * excessive amounts of response content in memory. A fix
11446      * for this was only introduced in Apache 2.3.3, with
11447      * possible further tweaks in Apache 2.4.1. To avoid issue of
11448      * what version it was implemented in, just employ a
11449      * strategy of forcing a flush every time we pass through
11450      * more than a certain amount of data.
11451      */
11452 
11453     tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
11454 
11455     while ((e = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) {
11456         /* If we have reached end of stream, we need to pass it on */
11457 
11458         if (APR_BUCKET_IS_EOS(e)) {
11459             /*
11460              * Probably do not need to force a flush as EOS should
11461              * do that, but do it just in case when we potentially
11462              * have pending data to be written out.
11463              */
11464 
11465             if (bytes_transfered != 0) {
11466                 APR_BRIGADE_INSERT_TAIL(tmpbb, apr_bucket_flush_create(
11467                                         r->connection->bucket_alloc));
11468             }
11469 
11470             APR_BRIGADE_INSERT_TAIL(tmpbb, apr_bucket_eos_create(
11471                                     r->connection->bucket_alloc));
11472 
11473             rv = ap_pass_brigade(r->output_filters, tmpbb);
11474 
11475             apr_brigade_cleanup(tmpbb);
11476 
11477             if (rv != APR_SUCCESS) {
11478                 apr_brigade_destroy(bb);
11479 
11480                 /*
11481                  * Don't flag error if client connection was aborted
11482                  * so that access log still records the original HTTP
11483                  * response code returned by the WSGI application.
11484                  */
11485 
11486                 if (r->connection->aborted)
11487                     return OK;
11488 
11489                 return HTTP_INTERNAL_SERVER_ERROR;
11490             }
11491 
11492             break;
11493         }
11494 
11495         /*
11496          * Force the reading in of next block of data to be
11497          * transfered if necessary. If the bucket is a heap
11498          * bucket, then it will be whatever data is in it. If it
11499          * is a socket bucket, this will result in the bucket
11500          * being converted to a heap bucket with some amount of
11501          * data and the socket bucket added back in after it. Any
11502          * non data buckets should be skipped and discarded. The
11503          * result should always be that the first bucket is a
11504          * heap bucket.
11505          */
11506 
11507         rv = apr_bucket_read(e, &data, &length, mode);
11508 
11509         /*
11510          * If we would have blocked if not in non blocking mode
11511          * we send a flush bucket to ensure that all buffered
11512          * data is sent out before we block waiting for more.
11513          */
11514 
11515         if (rv == APR_EAGAIN && mode == APR_NONBLOCK_READ) {
11516             APR_BRIGADE_INSERT_TAIL(tmpbb, apr_bucket_flush_create(
11517                                     r->connection->bucket_alloc));
11518 
11519             rv = ap_pass_brigade(r->output_filters, tmpbb);
11520 
11521             apr_brigade_cleanup(tmpbb);
11522 
11523             if (rv == APR_TIMEUP) {
11524                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
11525                              "mod_wsgi (pid=%d): Failed to proxy response "
11526                              "to client.", getpid());
11527             }
11528 
11529             if (rv != APR_SUCCESS) {
11530                 apr_brigade_destroy(bb);
11531 
11532                 /*
11533                  * Don't flag error if client connection was aborted
11534                  * so that access log still records the original HTTP
11535                  * response code returned by the WSGI application.
11536                  */
11537 
11538                 if (r->connection->aborted)
11539                     return OK;
11540 
11541                 return HTTP_INTERNAL_SERVER_ERROR;
11542             }
11543 
11544             bytes_transfered = 0;
11545 
11546             bucket_count = 0;
11547 
11548             /*
11549              * Retry read from daemon using a blocking read. We do
11550              * not delete the bucket as we want to operate on the
11551              * same one as we would have blocked.
11552              */
11553 
11554             mode = APR_BLOCK_READ;
11555 
11556             continue;
11557 
11558         } else if (rv != APR_SUCCESS) {
11559             apr_brigade_destroy(bb);
11560 
11561             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
11562                          "mod_wsgi (pid=%d): Failed to proxy response "
11563                          "from daemon.", getpid());
11564 
11565             /*
11566              * Don't flag error if couldn't read from daemon
11567              * so that access log still records the original HTTP
11568              * response code returned by the WSGI application.
11569              */
11570 
11571             return OK;
11572         }
11573 
11574         /*
11575          * We had some data to transfer. Next time round we need to
11576          * always be try a non-blocking read first.
11577          */
11578 
11579         mode = APR_NONBLOCK_READ;
11580 
11581         /*
11582          * Now we don't actually work with the data which was
11583          * read direct and instead simply remove what should be a
11584          * heap bucket from the start of the bucket brigade and
11585          * then place in a new bucket brigade to be pushed out to
11586          * the client. By passing down the bucket, it avoids the
11587          * need to create a transient bucket holding a reference
11588          * to the data from the first bucket.
11589          */
11590 
11591         APR_BUCKET_REMOVE(e);
11592         APR_BRIGADE_INSERT_TAIL(tmpbb, e);
11593 
11594         /*
11595          * If we have reached the buffer size threshold, we want
11596          * to flush the data so that we aren't buffering too much
11597          * in memory and blowing out memory size. We also have a
11598          * check on the number of buckets we have accumulated as
11599          * a large number of buckets with very small amounts of
11600          * data will also accumulate a lot of memory. Apache's
11601          * own flow control doesn't cope with such a situation.
11602          * Right now hard wire the max number of buckets at 16
11603          * which equates to worst case number of separate data
11604          * blocks can be written by a writev() call on systems
11605          * such as Solaris.
11606          */
11607 
11608         bytes_transfered += length;
11609 
11610         bucket_count += 1;
11611 
11612         if (bytes_transfered > buffer_size || bucket_count >= 16) {
11613             APR_BRIGADE_INSERT_TAIL(tmpbb, apr_bucket_flush_create(
11614                                     r->connection->bucket_alloc));
11615 
11616             bytes_transfered = 0;
11617 
11618             bucket_count = 0;
11619 
11620             /*
11621              * Since we flushed the data out to the client, it is
11622              * okay to go back and do a blocking read the next time.
11623              */
11624 
11625             mode = APR_BLOCK_READ;
11626         }
11627 
11628         /* Pass the heap bucket and any flush bucket on. */
11629 
11630         rv = ap_pass_brigade(r->output_filters, tmpbb);
11631 
11632         apr_brigade_cleanup(tmpbb);
11633 
11634         if (rv == APR_TIMEUP) {
11635             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
11636                          "mod_wsgi (pid=%d): Failed to proxy response "
11637                          "to client.", getpid());
11638         }
11639 
11640         if (rv != APR_SUCCESS) {
11641             apr_brigade_destroy(bb);
11642 
11643             /*
11644              * Don't flag error if client connection was aborted
11645              * so that access log still records the original HTTP
11646              * response code returned by the WSGI application.
11647              */
11648 
11649             if (r->connection->aborted)
11650                 return OK;
11651 
11652             return HTTP_INTERNAL_SERVER_ERROR;
11653         }
11654     }
11655 
11656 #if AP_MODULE_MAGIC_AT_LEAST(20110605, 2)
11657     if (existing_timeout)
11658         apr_socket_timeout_set(sock, existing_timeout);
11659 #endif
11660 
11661     apr_brigade_destroy(bb);
11662 
11663     return OK;
11664 }
11665 
11666 #define ASCII_CRLF  "\015\012"
11667 #define ASCII_ZERO  "\060"
11668 
wsgi_execute_remote(request_rec * r)11669 static int wsgi_execute_remote(request_rec *r)
11670 {
11671     WSGIRequestConfig *config = NULL;
11672     WSGIDaemonSocket *daemon = NULL;
11673     WSGIProcessGroup *group = NULL;
11674 
11675     char *key = NULL;
11676     const char *hash = NULL;
11677 
11678     int status;
11679     apr_status_t rv;
11680 
11681     int seen_eos;
11682     int child_stopped_reading;
11683     apr_bucket_brigade *bbout;
11684     apr_bucket_brigade *bbin;
11685     apr_bucket *b;
11686 
11687     const char *location = NULL;
11688 
11689     char *header_buffer = NULL;
11690     int header_buflen = 0;
11691 
11692     /* Grab request configuration. */
11693 
11694     config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
11695                                                        &wsgi_module);
11696 
11697     /*
11698      * Only allow the process group to match against a restricted
11699      * set of processes if such a restricted set has been defined.
11700      */
11701 
11702     if (config->restrict_process) {
11703         if (!apr_table_get(config->restrict_process,
11704                            config->process_group)) {
11705             wsgi_log_script_error(r, apr_psprintf(r->pool, "Daemon "
11706                                   "process called '%s' cannot be "
11707                                   "accessed by this WSGI application "
11708                                   "as not a member of allowed groups",
11709                                   config->process_group), r->filename);
11710 
11711             return HTTP_INTERNAL_SERVER_ERROR;
11712         }
11713     }
11714 
11715     /*
11716      * Do not process request as remote if actually targeted at
11717      * the main Apache processes.
11718      */
11719 
11720     if (!*config->process_group)
11721         return DECLINED;
11722 
11723     /* Grab details of matching process group. */
11724 
11725     if (!wsgi_daemon_index) {
11726         wsgi_log_script_error(r, apr_psprintf(r->pool, "No WSGI daemon "
11727                               "process called '%s' has been configured",
11728                               config->process_group), r->filename);
11729 
11730         return HTTP_INTERNAL_SERVER_ERROR;
11731     }
11732 
11733     group = (WSGIProcessGroup *)apr_hash_get(wsgi_daemon_index,
11734                                              config->process_group,
11735                                              APR_HASH_KEY_STRING);
11736 
11737     if (!group) {
11738         wsgi_log_script_error(r, apr_psprintf(r->pool, "No WSGI daemon "
11739                               "process called '%s' has been configured",
11740                               config->process_group), r->filename);
11741 
11742         return HTTP_INTERNAL_SERVER_ERROR;
11743     }
11744 
11745     /*
11746      * Only allow the process group to match against a daemon
11747      * process defined within a virtual host with the same
11748      * server name or a daemon process defined at global server
11749      * scope.
11750      */
11751 
11752     if (group->server != r->server && group->server != wsgi_server) {
11753         if (strcmp(group->server->server_hostname,
11754                    r->server->server_hostname) != 0) {
11755             wsgi_log_script_error(r, apr_psprintf(r->pool, "Daemon "
11756                                   "process called '%s' cannot be "
11757                                   "accessed by this WSGI application",
11758                                   config->process_group), r->filename);
11759 
11760             return HTTP_INTERNAL_SERVER_ERROR;
11761         }
11762     }
11763 
11764     /*
11765      * Check restrictions related to the group of the WSGI
11766      * script file and who has write access to the directory it
11767      * is contained in. If not satisfied forbid access.
11768      */
11769 
11770     if (group->script_group) {
11771         apr_uid_t gid;
11772         struct group *grent = NULL;
11773         const char *grname = NULL;
11774         apr_finfo_t finfo;
11775         const char *path = NULL;
11776 
11777         if (!(r->finfo.valid & APR_FINFO_GROUP)) {
11778             wsgi_log_script_error(r, apr_psprintf(r->pool, "Group "
11779                                   "information not available for WSGI "
11780                                   "script file"), r->filename);
11781             return HTTP_FORBIDDEN;
11782         }
11783 
11784         gid = r->finfo.group;
11785 
11786         if ((grent = getgrgid(gid)) == NULL) {
11787             wsgi_log_script_error(r, apr_psprintf(r->pool, "Couldn't "
11788                                   "determine group of WSGI script file, "
11789                                   "gid=%ld", (long)gid), r->filename);
11790             return HTTP_FORBIDDEN;
11791         }
11792 
11793         grname = grent->gr_name;
11794 
11795         if (strcmp(group->script_group, grname)) {
11796             wsgi_log_script_error(r, apr_psprintf(r->pool, "Group of WSGI "
11797                                   "script file does not match required group "
11798                                   "for daemon process, group=%s", grname),
11799                                   r->filename);
11800             return HTTP_FORBIDDEN;
11801         }
11802 
11803         if (!(r->finfo.valid & APR_FINFO_WPROT)) {
11804             wsgi_log_script_error(r, apr_psprintf(r->pool, "World "
11805                                   "permissions not available for WSGI "
11806                                   "script file"), r->filename);
11807             return HTTP_FORBIDDEN;
11808         }
11809 
11810         if (r->finfo.protection & APR_FPROT_WWRITE) {
11811             wsgi_log_script_error(r, apr_psprintf(r->pool, "WSGI script "
11812                                   "file is writable to world"), r->filename);
11813             return HTTP_FORBIDDEN;
11814         }
11815 
11816         path = ap_make_dirstr_parent(r->pool, r->filename);
11817 
11818         if (apr_stat(&finfo, path, APR_FINFO_NORM, r->pool) != APR_SUCCESS) {
11819             wsgi_log_script_error(r, apr_psprintf(r->pool, "Unable to stat "
11820                                   "parent directory of WSGI script"), path);
11821             return HTTP_FORBIDDEN;
11822         }
11823 
11824         gid = finfo.group;
11825 
11826         if ((grent = getgrgid(gid)) == NULL) {
11827             wsgi_log_script_error(r, apr_psprintf(r->pool, "Couldn't "
11828                                   "determine group of parent directory of "
11829                                   "WSGI script file, gid=%ld", (long)gid),
11830                                   r->filename);
11831             return HTTP_FORBIDDEN;
11832         }
11833 
11834         grname = grent->gr_name;
11835 
11836         if (strcmp(group->script_group, grname)) {
11837             wsgi_log_script_error(r, apr_psprintf(r->pool, "Group of parent "
11838                                   "directory of WSGI script file does not "
11839                                   "match required group for daemon process, "
11840                                   "group=%s", grname), r->filename);
11841             return HTTP_FORBIDDEN;
11842         }
11843 
11844         if (finfo.protection & APR_FPROT_WWRITE) {
11845             wsgi_log_script_error(r, apr_psprintf(r->pool, "Parent directory "
11846                                   "of WSGI script file is writable to world"),
11847                                   r->filename);
11848             return HTTP_FORBIDDEN;
11849         }
11850     }
11851 
11852     /*
11853      * Check restrictions related to who can be the owner of
11854      * the WSGI script file and who has write access to the
11855      * directory it is contained in. If not satisfied forbid
11856      * access.
11857      */
11858 
11859     if (group->script_user) {
11860         apr_uid_t uid;
11861         struct passwd *pwent = NULL;
11862         const char *pwname = NULL;
11863         apr_finfo_t finfo;
11864         const char *path = NULL;
11865 
11866         if (!(r->finfo.valid & APR_FINFO_USER)) {
11867             wsgi_log_script_error(r, apr_psprintf(r->pool, "User "
11868                                   "information not available for WSGI "
11869                                   "script file"), r->filename);
11870             return HTTP_FORBIDDEN;
11871         }
11872 
11873         uid = r->finfo.user;
11874 
11875         if ((pwent = getpwuid(uid)) == NULL) {
11876             wsgi_log_script_error(r, apr_psprintf(r->pool, "Couldn't "
11877                                   "determine owner of WSGI script file, "
11878                                   "uid=%ld", (long)uid), r->filename);
11879             return HTTP_FORBIDDEN;
11880         }
11881 
11882         pwname = pwent->pw_name;
11883 
11884         if (strcmp(group->script_user, pwname)) {
11885             wsgi_log_script_error(r, apr_psprintf(r->pool, "Owner of WSGI "
11886                                   "script file does not match required user "
11887                                   "for daemon process, user=%s", pwname),
11888                                   r->filename);
11889             return HTTP_FORBIDDEN;
11890         }
11891 
11892         if (!(r->finfo.valid & APR_FINFO_GPROT)) {
11893             wsgi_log_script_error(r, apr_psprintf(r->pool, "Group "
11894                                   "permissions not available for WSGI "
11895                                   "script file"), r->filename);
11896             return HTTP_FORBIDDEN;
11897         }
11898 
11899         if (r->finfo.protection & APR_FPROT_GWRITE) {
11900             wsgi_log_script_error(r, apr_psprintf(r->pool, "WSGI script "
11901                                   "file is writable to group"), r->filename);
11902             return HTTP_FORBIDDEN;
11903         }
11904 
11905         if (!(r->finfo.valid & APR_FINFO_WPROT)) {
11906             wsgi_log_script_error(r, apr_psprintf(r->pool, "World "
11907                                   "permissions not available for WSGI "
11908                                   "script file"), r->filename);
11909             return HTTP_FORBIDDEN;
11910         }
11911 
11912         if (r->finfo.protection & APR_FPROT_WWRITE) {
11913             wsgi_log_script_error(r, apr_psprintf(r->pool, "WSGI script "
11914                                   "file is writable to world"), r->filename);
11915             return HTTP_FORBIDDEN;
11916         }
11917 
11918         path = ap_make_dirstr_parent(r->pool, r->filename);
11919 
11920         if (apr_stat(&finfo, path, APR_FINFO_NORM, r->pool) != APR_SUCCESS) {
11921             wsgi_log_script_error(r, apr_psprintf(r->pool, "Unable to stat "
11922                                   "parent directory of WSGI script"), path);
11923             return HTTP_FORBIDDEN;
11924         }
11925 
11926         uid = finfo.user;
11927 
11928         if ((pwent = getpwuid(uid)) == NULL) {
11929             wsgi_log_script_error(r, apr_psprintf(r->pool, "Couldn't "
11930                                   "determine owner of parent directory of "
11931                                   "WSGI script file, uid=%ld", (long)uid),
11932                                   r->filename);
11933             return HTTP_FORBIDDEN;
11934         }
11935 
11936         pwname = pwent->pw_name;
11937 
11938         if (strcmp(group->script_user, pwname)) {
11939             wsgi_log_script_error(r, apr_psprintf(r->pool, "Owner of parent "
11940                                   "directory of WSGI script file does not "
11941                                   "match required user for daemon process, "
11942                                   "user=%s", pwname), r->filename);
11943             return HTTP_FORBIDDEN;
11944         }
11945 
11946         if (finfo.protection & APR_FPROT_WWRITE) {
11947             wsgi_log_script_error(r, apr_psprintf(r->pool, "Parent directory "
11948                                   "of WSGI script file is writable to world"),
11949                                   r->filename);
11950             return HTTP_FORBIDDEN;
11951         }
11952 
11953         if (finfo.protection & APR_FPROT_GWRITE) {
11954             wsgi_log_script_error(r, apr_psprintf(r->pool, "Parent directory "
11955                                   "of WSGI script file is writable to group"),
11956                                   r->filename);
11957             return HTTP_FORBIDDEN;
11958         }
11959     }
11960 
11961     /*
11962      * Add magic marker into request environment so that daemon
11963      * process can verify that request is from a sender that can
11964      * be trusted. Wipe out original key to make it a bit harder
11965      * for rogue code in Apache child processes to trawl through
11966      * memory looking for unhashed string.
11967      */
11968 
11969     key = apr_psprintf(r->pool, "%ld|%s|%s|%s", group->random,
11970                        group->socket_path, r->filename,
11971                        config->handler_script);
11972     hash = ap_md5(r->pool, (const unsigned char *)key);
11973     memset(key, '\0', strlen(key));
11974 
11975     apr_table_setn(r->subprocess_env, "mod_wsgi.magic", hash);
11976 
11977     /* Create connection to the daemon process. */
11978 
11979     apr_table_setn(r->subprocess_env, "mod_wsgi.queue_start",
11980                    apr_psprintf(r->pool, "%" APR_TIME_T_FMT, apr_time_now()));
11981 
11982     daemon = (WSGIDaemonSocket *)apr_pcalloc(r->pool,
11983                                              sizeof(WSGIDaemonSocket));
11984 
11985     daemon->name = config->process_group;
11986     daemon->socket_path = group->socket_path;
11987     daemon->connect_timeout = group->connect_timeout;
11988     daemon->socket_timeout = group->socket_timeout;
11989 
11990     if ((status = wsgi_connect_daemon(r, daemon)) != OK)
11991         return status;
11992 
11993     /* Send request details and subprocess environment. */
11994 
11995     if (wsgi_server_config->verbose_debugging) {
11996         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
11997                      "mod_wsgi (pid=%d): Request server was "
11998                      "'%s|%d'.", getpid(), r->server->server_hostname,
11999                      r->server->port);
12000     }
12001 
12002     if ((rv = wsgi_send_request(r, config, daemon)) != APR_SUCCESS) {
12003         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
12004                      "mod_wsgi (pid=%d): Unable to send request details "
12005                      "to WSGI daemon process '%s' on '%s'.", getpid(),
12006                      daemon->name, daemon->socket_path);
12007 
12008         return HTTP_INTERNAL_SERVER_ERROR;
12009     }
12010 
12011     /* Setup bucket brigade for reading response from daemon. */
12012 
12013     bbin = apr_brigade_create(r->pool, r->connection->bucket_alloc);
12014     b = apr_bucket_socket_create(daemon->socket, r->connection->bucket_alloc);
12015     APR_BRIGADE_INSERT_TAIL(bbin, b);
12016     b = apr_bucket_eos_create(r->connection->bucket_alloc);
12017     APR_BRIGADE_INSERT_TAIL(bbin, b);
12018 
12019     /* Create alternate buffer for reading in response header values. */
12020 
12021     if (group->header_buffer_size != 0) {
12022         header_buflen = group->header_buffer_size;
12023         header_buffer = apr_pcalloc(r->pool, header_buflen);
12024     }
12025 
12026     /*
12027      * If process reload mechanism enabled, then we need to look
12028      * for marker indicating it is okay to transfer content, or
12029      * whether process is being restarted and that we should
12030      * therefore create a connection to daemon process again.
12031      */
12032 
12033     if (*config->process_group) {
12034         int retries = 0;
12035         int maximum = (2*group->processes)+1;
12036 
12037         /*
12038          * While special header indicates a restart is being
12039          * done, then keep trying to reconnect. Cap the number
12040          * of retries to at most about 2 times the number of
12041          * daemon processes in the process group. If still being
12042          * told things are being restarted, then we will error
12043          * indicating service is unavailable.
12044          */
12045 
12046         while (retries < maximum) {
12047             /* Scan the CGI script like headers from daemon. */
12048 
12049             status = wsgi_scan_headers_brigade(r, bbin, header_buffer,
12050                                                header_buflen);
12051 
12052             if (status != OK)
12053                 return status;
12054 
12055             /*
12056              * Status must be 200 for our special headers. Ideally
12057              * we would use 0 as did in the past but Apache 2.4
12058              * complains if use 0 as not a valid status value.
12059              */
12060 
12061             if (r->status != 200) {
12062                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12063                              "mod_wsgi (pid=%d): Unexpected status from "
12064                              "WSGI daemon process '%d'.", getpid(),
12065                              r->status);
12066 
12067                 r->status_line = NULL;
12068 
12069                 return HTTP_INTERNAL_SERVER_ERROR;
12070             }
12071 
12072             if (!strcmp(r->status_line, "200 Continue")) {
12073                 r->status_line = NULL;
12074 
12075                 break;
12076             }
12077 
12078             if (!strcmp(r->status_line, "200 Timeout")) {
12079                 r->status_line = NULL;
12080 
12081                 return HTTP_GATEWAY_TIME_OUT;
12082             }
12083 
12084             if (strcmp(r->status_line, "200 Rejected")) {
12085                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12086                              "mod_wsgi (pid=%d): Unexpected status from "
12087                              "WSGI daemon process '%d'.", getpid(), r->status);
12088 
12089                 r->status_line = NULL;
12090 
12091                 return HTTP_INTERNAL_SERVER_ERROR;
12092             }
12093 
12094             r->status_line = NULL;
12095 
12096             /* Need to close previous socket connection first. */
12097 
12098             apr_socket_close(daemon->socket);
12099 
12100             /* Has maximum number of attempts been reached. */
12101 
12102             if (retries == maximum) {
12103                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
12104                              "mod_wsgi (pid=%d): Maximum number of WSGI "
12105                              "daemon process restart connects reached '%d'.",
12106                              getpid(), maximum);
12107                 return HTTP_SERVICE_UNAVAILABLE;
12108             }
12109 
12110             retries++;
12111 
12112             config->daemon_restarts++;
12113 
12114             ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
12115                          "mod_wsgi (pid=%d): Connect after WSGI daemon "
12116                          "process restart, attempt #%d.", getpid(),
12117                          retries);
12118 
12119             /* Connect and setup connection just like before. */
12120 
12121             if ((status = wsgi_connect_daemon(r, daemon)) != OK)
12122                 return status;
12123 
12124             if ((rv = wsgi_send_request(r, config, daemon)) != APR_SUCCESS) {
12125                 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
12126                              "mod_wsgi (pid=%d): Unable to send request "
12127                              "details to WSGI daemon process '%s' on '%s'.",
12128                              getpid(), daemon->name, daemon->socket_path);
12129 
12130                 return HTTP_INTERNAL_SERVER_ERROR;
12131             }
12132 
12133             apr_brigade_destroy(bbin);
12134 
12135             bbin = apr_brigade_create(r->pool, r->connection->bucket_alloc);
12136             b = apr_bucket_socket_create(daemon->socket,
12137                                        r->connection->bucket_alloc);
12138             APR_BRIGADE_INSERT_TAIL(bbin, b);
12139             b = apr_bucket_eos_create(r->connection->bucket_alloc);
12140             APR_BRIGADE_INSERT_TAIL(bbin, b);
12141         }
12142     }
12143 
12144 
12145     /*
12146      * Need to reset request status value to HTTP_OK else it
12147      * screws up HTTP input filter when processing a POST
12148      * request with 100-continue requirement.
12149      */
12150 
12151     r->status = HTTP_OK;
12152 
12153     /*
12154      * Transfer any request content which was provided. Note that we
12155      * actually frame each data block sent with same format as is used
12156      * for chunked transfer encoding. This will be decoded in the
12157      * daemon process. This is done so that the EOS can be properly
12158      * identified by the daemon process in the absence of a value for
12159      * CONTENT_LENGTH that can be relied on. The CONTENT_LENGTH is
12160      * dodgy when have mutating input filters and none will be present
12161      * at all if chunked request content was used.
12162      */
12163 
12164     seen_eos = 0;
12165     child_stopped_reading = 0;
12166 
12167     bbout = apr_brigade_create(r->pool, r->connection->bucket_alloc);
12168 
12169     do {
12170         apr_bucket *bucket;
12171 
12172         rv = ap_get_brigade(r->input_filters, bbout, AP_MODE_READBYTES,
12173                             APR_BLOCK_READ, HUGE_STRING_LEN);
12174 
12175         if (rv != APR_SUCCESS) {
12176             char status_buffer[512];
12177             const char *error_message;
12178 
12179             error_message = apr_psprintf(r->pool, "Request data read "
12180                     "error when proxying data to daemon process: %s",
12181                     apr_strerror(rv, status_buffer, sizeof(
12182                     status_buffer)-1));
12183 
12184             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12185                          "mod_wsgi (pid=%d): %s.", getpid(), error_message);
12186 
12187             if (APR_STATUS_IS_TIMEUP(rv))
12188                 return HTTP_REQUEST_TIME_OUT;
12189 
12190             return HTTP_INTERNAL_SERVER_ERROR;
12191         }
12192 
12193         for (bucket = APR_BRIGADE_FIRST(bbout);
12194              bucket != APR_BRIGADE_SENTINEL(bbout);
12195              bucket = APR_BUCKET_NEXT(bucket))
12196         {
12197             const char *data;
12198             apr_size_t len;
12199 
12200             char chunk_hdr[20];
12201             apr_size_t hdr_len;
12202 
12203             struct iovec vec[3];
12204 
12205             if (APR_BUCKET_IS_EOS(bucket)) {
12206                 /* Send closing frame for chunked content. */
12207 
12208                 rv = wsgi_socket_send(daemon->socket,
12209                         ASCII_ZERO ASCII_CRLF ASCII_CRLF, 5);
12210 
12211                 if (rv != APR_SUCCESS) {
12212                     char status_buffer[512];
12213                     const char *error_message;
12214 
12215                     error_message = apr_psprintf(r->pool, "Request data write "
12216                             "error when proxying data to daemon process: %s",
12217                             apr_strerror(rv, status_buffer, sizeof(
12218                             status_buffer)-1));
12219 
12220                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12221                                  "mod_wsgi (pid=%d): %s.", getpid(),
12222                                  error_message);
12223                 }
12224 
12225                 seen_eos = 1;
12226                 break;
12227             }
12228 
12229             /* We can't do much with this. */
12230 
12231             if (APR_BUCKET_IS_FLUSH(bucket)) {
12232                 continue;
12233             }
12234 
12235             /* If the child stopped, we still must read to EOS. */
12236 
12237             if (child_stopped_reading) {
12238                 continue;
12239             }
12240 
12241             /* Read block. */
12242 
12243             rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
12244 
12245             if (rv != APR_SUCCESS) {
12246                 char status_buffer[512];
12247                 const char *error_message;
12248 
12249                 error_message = apr_psprintf(r->pool, "Request data read "
12250                         "error when proxying data to daemon process: %s",
12251                         apr_strerror(rv, status_buffer, sizeof(
12252                         status_buffer)-1));
12253 
12254                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12255                              "mod_wsgi (pid=%d): %s.", getpid(),
12256                              error_message);
12257 
12258                 break;
12259             }
12260 
12261             /*
12262              * Keep writing data to the child until done or too
12263              * much time elapses with no progress or an error
12264              * occurs. Frame the data being sent with format used
12265              * for chunked transfer encoding.
12266              */
12267 
12268             hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
12269                     "%" APR_UINT64_T_HEX_FMT ASCII_CRLF, (apr_uint64_t)len);
12270 
12271             vec[0].iov_base = (void *)chunk_hdr;
12272             vec[0].iov_len = hdr_len;
12273             vec[1].iov_base = (void *)data;
12274             vec[1].iov_len = len;
12275             vec[2].iov_base = (void *)ASCII_CRLF;
12276             vec[2].iov_len = 2;
12277 
12278             rv = wsgi_socket_sendv(daemon->socket, vec, 3);
12279 
12280             if (rv != APR_SUCCESS) {
12281                 char status_buffer[512];
12282                 const char *error_message;
12283 
12284                 error_message = apr_psprintf(r->pool, "Request data write "
12285                         "error when proxying data to daemon process: %s",
12286                         apr_strerror(rv, status_buffer, sizeof(
12287                         status_buffer)-1));
12288 
12289                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12290                              "mod_wsgi (pid=%d): %s.", getpid(),
12291                              error_message);
12292 
12293                 /* Daemon stopped reading, discard remainder. */
12294 
12295                 child_stopped_reading = 1;
12296             }
12297         }
12298         apr_brigade_cleanup(bbout);
12299     }
12300     while (!seen_eos);
12301 
12302     /*
12303      * Close socket for writing so that daemon detects end of
12304      * request content.
12305      */
12306 
12307     apr_socket_shutdown(daemon->socket, APR_SHUTDOWN_WRITE);
12308 
12309     /* Scan the CGI script like headers from daemon. */
12310 
12311     status = wsgi_scan_headers_brigade(r, bbin, header_buffer,
12312                                        header_buflen);
12313 
12314     if (status != OK)
12315         return status;
12316 
12317     /*
12318      * Look for the special case of status being 200 but the
12319      * status line indicating an error and translate it into a
12320      * 500 error so that error document processing will occur
12321      * for those cases where WSGI application wouldn't have
12322      * supplied their own error document. We used to use 0
12323      * here for status but Apache 2.4 prohibits it now.
12324      */
12325 
12326     if (r->status == 200 && !strcmp(r->status_line, "200 Error")) {
12327         r->status_line = NULL;
12328 
12329         return HTTP_INTERNAL_SERVER_ERROR;
12330     }
12331 
12332     /*
12333      * Look for 'Location' header and if an internal
12334      * redirect, execute the redirect. This behaviour is
12335      * consistent with how mod_cgi and mod_cgid work and
12336      * what is permitted by the CGI specification.
12337      */
12338 
12339     location = apr_table_get(r->headers_out, "Location");
12340 
12341     if (location && location[0] == '/' && r->status == 200) {
12342         /*
12343          * Discard all response content returned from
12344          * the daemon process.
12345          */
12346 
12347         wsgi_discard_output(bbin);
12348         apr_brigade_destroy(bbin);
12349 
12350         /*
12351          * The internal redirect needs to be a GET no
12352          * matter what the original method was.
12353          */
12354 
12355         r->method = apr_pstrdup(r->pool, "GET");
12356         r->method_number = M_GET;
12357 
12358         /*
12359          * We already read the message body (if any), so
12360          * don't allow the redirected request to think
12361          * it has one. Not sure if we need to worry
12362          * about removing 'Transfer-Encoding' header.
12363          */
12364 
12365         apr_table_unset(r->headers_in, "Content-Length");
12366 
12367         ap_internal_redirect_handler(location, r);
12368 
12369         return OK;
12370     }
12371 
12372     /*
12373      * Allow the web server to override any error
12374      * page produced by the WSGI application.
12375      */
12376 
12377     if (config->error_override && ap_is_HTTP_ERROR(r->status)) {
12378         status = r->status;
12379 
12380         r->status = HTTP_OK;
12381         r->status_line = NULL;
12382 
12383         /*
12384          * Discard all response content returned from
12385          * the daemon process if any expected.
12386          */
12387 
12388         if (!r->header_only && /* not HEAD request */
12389             (status != HTTP_NO_CONTENT) && /* not 204 */
12390             (status != HTTP_NOT_MODIFIED)) { /* not 304 */
12391             wsgi_discard_output(bbin);
12392             apr_brigade_destroy(bbin);
12393         }
12394 
12395         return status;
12396     }
12397 
12398     /* Transfer any response content. */
12399 
12400     return wsgi_transfer_response(r, bbin, group->response_buffer_size,
12401                                   group->response_socket_timeout);
12402 }
12403 
wsgi_socket_read(apr_socket_t * sock,void * vbuf,apr_size_t size)12404 static apr_status_t wsgi_socket_read(apr_socket_t *sock, void *vbuf,
12405                                      apr_size_t size)
12406 {
12407     char *buf = vbuf;
12408     apr_status_t rv;
12409     apr_size_t count = 0;
12410     apr_size_t len = 0;
12411 
12412     do {
12413         len = size - count;
12414         if ((rv = apr_socket_recv(sock, buf + count, &len)) != APR_SUCCESS)
12415              return rv;
12416         count += len;
12417     } while (count < size);
12418 
12419     return APR_SUCCESS;
12420 }
12421 
wsgi_read_strings(apr_socket_t * sock,char *** s,apr_pool_t * p)12422 static apr_status_t wsgi_read_strings(apr_socket_t *sock, char ***s,
12423                                       apr_pool_t *p)
12424 {
12425     apr_status_t rv;
12426 
12427     apr_size_t total;
12428 
12429     apr_size_t n;
12430     apr_size_t i;
12431     apr_size_t l;
12432 
12433     char *buffer;
12434     char *offset;
12435 
12436     if ((rv = wsgi_socket_read(sock, &total, sizeof(total))) != APR_SUCCESS)
12437         return rv;
12438 
12439     buffer = apr_palloc(p, total);
12440     offset = buffer;
12441 
12442     if ((rv = wsgi_socket_read(sock, buffer, total)) != APR_SUCCESS)
12443         return rv;
12444 
12445     memcpy(&n, offset, sizeof(n));
12446     offset += sizeof(n);
12447 
12448     *s = apr_pcalloc(p, (n+1)*sizeof(**s));
12449 
12450     for (i = 0; i < n; i++) {
12451         l = strlen(offset) + 1;
12452         (*s)[i] = offset;
12453         offset += l;
12454     }
12455 
12456     return APR_SUCCESS;
12457 }
12458 
wsgi_read_request(apr_socket_t * sock,request_rec * r)12459 static apr_status_t wsgi_read_request(apr_socket_t *sock, request_rec *r)
12460 {
12461     int rv;
12462 
12463     char **vars;
12464 
12465     /* Read subprocess environment from request object. */
12466 
12467     rv = wsgi_read_strings(sock, &vars, r->pool);
12468 
12469     if (rv != APR_SUCCESS)
12470         return rv;
12471 
12472     while (*vars) {
12473         char *key = *vars++;
12474 
12475         apr_table_setn(r->subprocess_env, key, *vars++);
12476     }
12477 
12478     return APR_SUCCESS;
12479 }
12480 
12481 static ap_filter_rec_t *wsgi_header_filter_handle;
12482 
wsgi_header_filter(ap_filter_t * f,apr_bucket_brigade * b)12483 static apr_status_t wsgi_header_filter(ap_filter_t *f, apr_bucket_brigade *b)
12484 {
12485     request_rec *r = f->r;
12486 
12487     struct iovec vec1[4];
12488     apr_bucket_brigade *b2;
12489     char crlf[] = CRLF;
12490     apr_size_t buflen;
12491 
12492     const apr_array_header_t *elts;
12493     const apr_table_entry_t *t_elt;
12494     const apr_table_entry_t *t_end;
12495     struct iovec *vec2;
12496     struct iovec *vec2_next;
12497 
12498     /* Output status line. */
12499 
12500     vec1[0].iov_base = (void *)"Status:";
12501     vec1[0].iov_len  = strlen("Status:");
12502     vec1[1].iov_base = (void *)" ";
12503     vec1[1].iov_len  = sizeof(" ") - 1;
12504     vec1[2].iov_base = (void *)(r->status_line);
12505     vec1[2].iov_len  = strlen(r->status_line);
12506     vec1[3].iov_base = (void *)CRLF;
12507     vec1[3].iov_len  = sizeof(CRLF) - 1;
12508 
12509     b2 = apr_brigade_create(r->pool, r->connection->bucket_alloc);
12510     apr_brigade_writev(b2, NULL, NULL, vec1, 4);
12511 
12512     /* Merge response header tables together. */
12513 
12514     if (!apr_is_empty_table(r->err_headers_out)) {
12515         r->headers_out = apr_table_overlay(r->pool, r->err_headers_out,
12516                                            r->headers_out);
12517     }
12518 
12519     /* Override the content type for response. */
12520 
12521     if (r->content_type)
12522         apr_table_setn(r->headers_out, "Content-Type", r->content_type);
12523 
12524     /* Formt the response headers for output. */
12525 
12526     elts = apr_table_elts(r->headers_out);
12527     if (elts->nelts != 0) {
12528         t_elt = (const apr_table_entry_t *)(elts->elts);
12529         t_end = t_elt + elts->nelts;
12530         vec2 = (struct iovec *)apr_palloc(r->pool, 4 * elts->nelts *
12531                                           sizeof(struct iovec));
12532         vec2_next = vec2;
12533 
12534         do {
12535             vec2_next->iov_base = (void*)(t_elt->key);
12536             vec2_next->iov_len = strlen(t_elt->key);
12537             vec2_next++;
12538             vec2_next->iov_base = ": ";
12539             vec2_next->iov_len = sizeof(": ") - 1;
12540             vec2_next++;
12541             vec2_next->iov_base = (void*)(t_elt->val);
12542             vec2_next->iov_len = strlen(t_elt->val);
12543             vec2_next++;
12544             vec2_next->iov_base = CRLF;
12545             vec2_next->iov_len = sizeof(CRLF) - 1;
12546             vec2_next++;
12547             t_elt++;
12548         } while (t_elt < t_end);
12549 
12550         apr_brigade_writev(b2, NULL, NULL, vec2, vec2_next - vec2);
12551     }
12552 
12553     /* Format terminating blank line for response headers. */
12554 
12555     buflen = strlen(crlf);
12556     apr_brigade_write(b2, NULL, NULL, crlf, buflen);
12557 
12558     /* Output the response headers. */
12559 
12560     ap_pass_brigade(f->next, b2);
12561 
12562     /* Remove ourselves from filter chain so we aren't called again. */
12563 
12564     ap_remove_output_filter(f);
12565 
12566     /* Output the partial response content. */
12567 
12568     return ap_pass_brigade(f->next, b);
12569 }
12570 
12571 typedef struct cve_2013_5704_fields cve_2013_5704_fields;
12572 typedef struct cve_2013_5704_apache22 cve_2013_5704_apache22;
12573 typedef struct cve_2013_5704_apache24 cve_2013_5704_apache24;
12574 
12575 struct cve_2013_5704_fields {
12576     apr_table_t *trailers_in;
12577     apr_table_t *trailers_out;
12578 };
12579 
12580 struct cve_2013_5704_apache22 {
12581     struct ap_filter_t *proto_input_filters;
12582     int eos_sent;
12583     cve_2013_5704_fields fields;
12584 };
12585 
12586 struct cve_2013_5704_apache24 {
12587     apr_sockaddr_t *useragent_addr;
12588     char *useragent_ip;
12589     cve_2013_5704_fields fields;
12590 };
12591 
wsgi_hook_daemon_handler(conn_rec * c)12592 static int wsgi_hook_daemon_handler(conn_rec *c)
12593 {
12594     apr_socket_t *csd;
12595     request_rec *r;
12596     apr_pool_t *p;
12597     apr_status_t rv;
12598 
12599     char *key;
12600     apr_sockaddr_t *addr;
12601 
12602     const char *filename;
12603     const char *script;
12604     const char *magic;
12605     const char *hash;
12606 
12607     WSGIRequestConfig *config;
12608 
12609     apr_bucket *e;
12610     apr_bucket_brigade *bb;
12611 
12612     core_request_config *req_cfg;
12613 
12614     ap_filter_t *current = NULL;
12615     ap_filter_t *next = NULL;
12616 
12617     const char *item;
12618 
12619     int queue_timeout_occurred = 0;
12620 
12621 #if ! (AP_MODULE_MAGIC_AT_LEAST(20120211, 37) || \
12622     (AP_SERVER_MAJORVERSION_NUMBER == 2 && \
12623      AP_SERVER_MINORVERSION_NUMBER <= 2 && \
12624      AP_MODULE_MAGIC_AT_LEAST(20051115, 36)))
12625     apr_size_t size = 0;
12626 #endif
12627 
12628     /* Don't do anything if not in daemon process. */
12629 
12630     if (!wsgi_daemon_pool)
12631         return DECLINED;
12632 
12633     /*
12634      * Remove all input/output filters except the core filters.
12635      * This will ensure that any SSL filters we don't want are
12636      * removed. This is a bit of a hack. Only other option is to
12637      * duplicate the code for core input/output filters so can
12638      * avoid full Apache connection processing, which is what is
12639      * installed the SSL filters and possibly other filters for
12640      * logging etc.
12641      */
12642 
12643     current = c->input_filters;
12644     next = current->next;
12645 
12646     while (current) {
12647         if (current->frec == ap_core_input_filter_handle) {
12648             current = next;
12649             if (!current)
12650                 break;
12651             next = current->next;
12652             continue;
12653         }
12654 
12655         ap_remove_input_filter(current);
12656 
12657         current = next;
12658         if (current)
12659             next = current->next;
12660     }
12661 
12662     current = c->output_filters;
12663     next = current->next;
12664 
12665     while (current) {
12666         if (current->frec == ap_core_output_filter_handle) {
12667             current = next;
12668             if (!current)
12669                 break;
12670             next = current->next;
12671             continue;
12672         }
12673 
12674         ap_remove_output_filter(current);
12675 
12676         current = next;
12677         if (current)
12678             next = current->next;
12679     }
12680 
12681     /*
12682      * Create and populate our own request object. We allocate more
12683      * memory than we require here for the request_rec in order to
12684      * implement an opimistic hack for the case where mod_wsgi is built
12685      * against an Apache version prior to CVE-2013-6704 being applied to
12686      * it. If that Apache is upgraded but mod_wsgi not recompiled then
12687      * it will crash in daemon mode. We therefore use the extra space to
12688      * set the structure members which are added by CVE-2013-6704 to try
12689      * and avoid that situation. Note that this is distinct from the
12690      * hack down below to deal with where mod_wsgi was compiled against
12691      * an Apache version which had CVE-2013-6704 backported.
12692      */
12693 
12694     apr_pool_create(&p, c->pool);
12695 
12696     r = apr_pcalloc(p, sizeof(request_rec)+sizeof(cve_2013_5704_fields));
12697 
12698     r->pool = p;
12699     r->connection = c;
12700     r->server = c->base_server;
12701 
12702     r->user = NULL;
12703     r->ap_auth_type = NULL;
12704 
12705     r->allowed_methods = ap_make_method_list(p, 2);
12706 
12707     r->headers_in = apr_table_make(r->pool, 25);
12708     r->subprocess_env = apr_table_make(r->pool, 25);
12709     r->headers_out = apr_table_make(r->pool, 12);
12710     r->err_headers_out = apr_table_make(r->pool, 5);
12711     r->notes = apr_table_make(r->pool, 5);
12712 
12713     r->request_config  = ap_create_request_config(r->pool);
12714 
12715     r->proto_output_filters = c->output_filters;
12716     r->output_filters = r->proto_output_filters;
12717     r->proto_input_filters = c->input_filters;
12718     r->input_filters = r->proto_input_filters;
12719 
12720 #if AP_MODULE_MAGIC_AT_LEAST(20120211, 37) || \
12721     (AP_SERVER_MAJORVERSION_NUMBER == 2 && \
12722      AP_SERVER_MINORVERSION_NUMBER <= 2 && \
12723      AP_MODULE_MAGIC_AT_LEAST(20051115, 36))
12724 
12725     /*
12726      * New request_rec fields were added to Apache because of changes
12727      * related to CVE-2013-5704. The change means that mod_wsgi version
12728      * 4.4.0-4.4.5 will crash if run on the Apache versions with the
12729      * addition fields if mod_wsgi daemon mode is used. If we are using
12730      * Apache 2.2.29 or 2.4.11, we set the fields direct against the
12731      * new structure members.
12732      */
12733 
12734     r->trailers_in = apr_table_make(r->pool, 5);
12735     r->trailers_out = apr_table_make(r->pool, 5);
12736 #else
12737     /*
12738      * We use a huge hack here to try and identify when CVE-2013-5704
12739      * has been back ported to older Apache version. This is necessary
12740      * as when backported the Apache module magic number will not be
12741      * updated and it isn't possible to determine from that at compile
12742      * time if the new structure members exist and so that they should
12743      * be set. We therefore try and work out whether the extra structure
12744      * members exist through looking at the size of request_rec and
12745      * whether memory has been allocated above what is known to be the
12746      * last member in the structure before the new members were added.
12747      */
12748 
12749 #if AP_SERVER_MINORVERSION_NUMBER <= 2
12750     size = offsetof(request_rec, eos_sent);
12751     size += sizeof(r->eos_sent);
12752 #else
12753     size = offsetof(request_rec, useragent_ip);
12754     size += sizeof(r->useragent_ip);
12755 #endif
12756 
12757     /*
12758      * Check whether request_rec is at least as large as minimal size
12759      * plus the size of the extra fields. If it is, then we need to
12760      * set the additional fields.
12761      */
12762 
12763     if (sizeof(request_rec) >= size + sizeof(cve_2013_5704_fields)) {
12764 #if AP_SERVER_MINORVERSION_NUMBER <= 2
12765         cve_2013_5704_apache22 *rext;
12766         rext = (cve_2013_5704_apache22 *)&r->proto_input_filters;
12767 #else
12768         cve_2013_5704_apache24 *rext;
12769         rext = (cve_2013_5704_apache24 *)&r->useragent_addr;
12770 #endif
12771 
12772         rext->fields.trailers_in = apr_table_make(r->pool, 5);
12773         rext->fields.trailers_out = apr_table_make(r->pool, 5);
12774     }
12775     else {
12776         /*
12777          * Finally, to allow forward portability of a compiled mod_wsgi
12778          * binary from an Apache version without the CVE-2013-5704
12779          * change to one where it is, without needing to recompile
12780          * mod_wsgi, we set fields in the extra memory we added before
12781          * the actual request_rec.
12782          */
12783 
12784         cve_2013_5704_fields *rext;
12785         rext = (cve_2013_5704_fields *)(r+1);
12786 
12787         rext->trailers_in = apr_table_make(r->pool, 5);
12788         rext->trailers_out = apr_table_make(r->pool, 5);
12789     }
12790 #endif
12791 
12792     r->per_dir_config  = r->server->lookup_defaults;
12793 
12794     r->sent_bodyct = 0;
12795 
12796     r->read_length = 0;
12797     r->read_body = REQUEST_NO_BODY;
12798 
12799     r->status = HTTP_OK;
12800     r->status_line = NULL;
12801     r->the_request = NULL;
12802 
12803     r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
12804 
12805     /*
12806      * Install our own output filter for writing back headers in
12807      * CGI script style.
12808      */
12809 
12810     ap_add_output_filter_handle(wsgi_header_filter_handle,
12811                                 NULL, r, r->connection);
12812 
12813     /* Create and install the WSGI request config. */
12814 
12815     config = (WSGIRequestConfig *)apr_pcalloc(r->pool,
12816                                               sizeof(WSGIRequestConfig));
12817     ap_set_module_config(r->request_config, &wsgi_module, (void *)config);
12818 
12819     /* Grab the socket from the connection core config. */
12820 
12821     csd = ap_get_module_config(c->conn_config, &core_module);
12822 
12823     /*
12824      * Fake up parts of the internal per request core
12825      * configuration. If we don't do this then when Apache is
12826      * compiled with the symbol AP_DEBUG, internal checks made
12827      * by Apache will result in process crashing.
12828      */
12829 
12830     req_cfg = apr_pcalloc(r->pool, sizeof(core_request_config));
12831 
12832     req_cfg->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
12833 
12834     ap_set_module_config(r->request_config, &core_module, req_cfg);
12835 
12836     /* Read in the request details and setup request object. */
12837 
12838     if ((rv = wsgi_read_request(csd, r)) != APR_SUCCESS) {
12839         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, wsgi_server,
12840                      "mod_wsgi (pid=%d): Unable to read WSGI request.",
12841                      getpid());
12842 
12843         apr_pool_destroy(p);
12844 
12845         return HTTP_INTERNAL_SERVER_ERROR;
12846     }
12847 
12848     /* Check magic marker used to validate origin of request. */
12849 
12850     filename = apr_table_get(r->subprocess_env, "SCRIPT_FILENAME");
12851     script = apr_table_get(r->subprocess_env, "mod_wsgi.handler_script");
12852 
12853     magic = apr_table_get(r->subprocess_env, "mod_wsgi.magic");
12854 
12855     if (!magic) {
12856         ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
12857                      "mod_wsgi (pid=%d): Request origin could not be "
12858                      "validated.", getpid());
12859 
12860         apr_pool_destroy(p);
12861 
12862         return HTTP_INTERNAL_SERVER_ERROR;
12863     }
12864 
12865     key = apr_psprintf(r->pool, "%ld|%s|%s|%s",
12866                        wsgi_daemon_process->group->random,
12867                        wsgi_daemon_process->group->socket_path,
12868                        filename, script);
12869     hash = ap_md5(r->pool, (const unsigned char *)key);
12870     memset(key, '\0', strlen(key));
12871 
12872     if (strcmp(magic, hash) != 0) {
12873         ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
12874                      "mod_wsgi (pid=%d): Request origin could not be "
12875                      "validated.", getpid());
12876 
12877         apr_pool_destroy(p);
12878 
12879         return HTTP_INTERNAL_SERVER_ERROR;
12880     }
12881 
12882     apr_table_unset(r->subprocess_env, "mod_wsgi.magic");
12883 
12884     /*
12885      * If we are executing in a chroot environment, we need to
12886      * adjust SCRIPT_FILENAME to remove leading portion of path
12887      * that corresponds to the location of the chroot directory.
12888      * Also need to adjust DOCUMENT_ROOT as well, although in
12889      * that case if it doesn't actually fall within the choot
12890      * directory, we just delete it outright as would be incorrect
12891      * if that directory lay outside of the chroot directory.
12892      */
12893 
12894     if (wsgi_daemon_process->group->root) {
12895         const char *root;
12896         const char *path;
12897 
12898         root = wsgi_daemon_process->group->root;
12899 
12900         path = filename;
12901 
12902         if (strstr(path, root) == path && path[strlen(root)] == '/') {
12903             path += strlen(root);
12904 
12905             apr_table_set(r->subprocess_env, "SCRIPT_FILENAME", path);
12906 
12907             filename = path;
12908         }
12909         else {
12910             ap_log_error(APLOG_MARK, APLOG_CRIT, rv, wsgi_server,
12911                          "mod_wsgi (pid=%d): WSGI script '%s' not located "
12912                          "within chroot directory '%s'.", getpid(), path, root);
12913 
12914             return HTTP_INTERNAL_SERVER_ERROR;
12915         }
12916 
12917         path = (char *)apr_table_get(r->subprocess_env, "DOCUMENT_ROOT");
12918 
12919         if (strstr(path, root) == path) {
12920             path += strlen(root);
12921 
12922             apr_table_set(r->subprocess_env, "DOCUMENT_ROOT", path);
12923         }
12924         else {
12925             apr_table_unset(r->subprocess_env, "DOCUMENT_ROOT");
12926         }
12927     }
12928 
12929     r->filename = (char *)filename;
12930 
12931     /* Recalculate WSGI script or handler script modification time. */
12932 
12933     if (script && *script) {
12934         if ((rv = apr_stat(&r->finfo, script, APR_FINFO_NORM,
12935                            r->pool)) != APR_SUCCESS) {
12936             /*
12937              * Don't fail at this point. Allow the lack of file to
12938              * be detected later when trying to load the script file.
12939              */
12940 
12941             ap_log_error(APLOG_MARK, APLOG_WARNING, rv, wsgi_server,
12942                          "mod_wsgi (pid=%d): Unable to stat target handler "
12943                          "script '%s'.", getpid(), script);
12944 
12945             r->finfo.mtime = 0;
12946         }
12947     }
12948     else {
12949         if ((rv = apr_stat(&r->finfo, filename, APR_FINFO_NORM,
12950                            r->pool)) != APR_SUCCESS) {
12951             /*
12952              * Don't fail at this point. Allow the lack of file to
12953              * be detected later when trying to load the script file.
12954              */
12955 
12956             ap_log_error(APLOG_MARK, APLOG_WARNING, rv, wsgi_server,
12957                          "mod_wsgi (pid=%d): Unable to stat target WSGI "
12958                          "script '%s'.", getpid(), filename);
12959 
12960             r->finfo.mtime = 0;
12961         }
12962     }
12963 
12964     /*
12965      * Trigger mapping of host information to server configuration
12966      * so that when logging errors they go to the correct error log
12967      * file for the host.
12968      */
12969 
12970 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
12971     r->connection->client_ip = (char *)apr_table_get(r->subprocess_env,
12972                                                      "REMOTE_ADDR");
12973     r->connection->client_addr->port = atoi(apr_table_get(r->subprocess_env,
12974                                                           "REMOTE_PORT"));
12975 #else
12976     r->connection->remote_ip = (char *)apr_table_get(r->subprocess_env,
12977                                                      "REMOTE_ADDR");
12978     r->connection->remote_addr->port = atoi(apr_table_get(r->subprocess_env,
12979                                                           "REMOTE_PORT"));
12980 #endif
12981 
12982 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
12983     r->useragent_addr = c->client_addr;
12984     r->useragent_ip = c->client_ip;
12985 #endif
12986 
12987     key = apr_psprintf(p, "%s|%s",
12988                        apr_table_get(r->subprocess_env,
12989                                      "mod_wsgi.listener_host"),
12990                        apr_table_get(r->subprocess_env,
12991                                      "mod_wsgi.listener_port"));
12992 
12993     if (wsgi_server_config->verbose_debugging) {
12994         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
12995                      "mod_wsgi (pid=%d): Server listener address '%s'.",
12996                      getpid(), key);
12997     }
12998 
12999     addr = (apr_sockaddr_t *)apr_hash_get(wsgi_daemon_listeners,
13000                                           key, APR_HASH_KEY_STRING);
13001 
13002     if (wsgi_server_config->verbose_debugging) {
13003         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
13004                      "mod_wsgi (pid=%d): Server listener address '%s' was"
13005                      "%s found.", getpid(), key, addr ? "" : " not");
13006     }
13007 
13008     if (addr) {
13009         c->local_addr = addr;
13010     }
13011 
13012     ap_update_vhost_given_ip(r->connection);
13013 
13014     if (wsgi_server_config->verbose_debugging) {
13015         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
13016                      "mod_wsgi (pid=%d): Connection server matched was "
13017                      "'%s|%d'.", getpid(), c->base_server->server_hostname,
13018                      c->base_server->port);
13019     }
13020 
13021     r->server = c->base_server;
13022 
13023     if (apr_table_get(r->subprocess_env, "HTTP_HOST")) {
13024         apr_table_setn(r->headers_in, "Host",
13025                        apr_table_get(r->subprocess_env, "HTTP_HOST"));
13026     }
13027 
13028     ap_update_vhost_from_headers(r);
13029 
13030     if (wsgi_server_config->verbose_debugging) {
13031         ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
13032                      "mod_wsgi (pid=%d): Request server matched was '%s|%d'.",
13033                      getpid(), r->server->server_hostname, r->server->port);
13034     }
13035 
13036     /*
13037      * Set content length of any request content and add the
13038      * standard HTTP input filter so that standard input routines
13039      * for request content will work.
13040      */
13041 
13042     item = apr_table_get(r->subprocess_env, "CONTENT_LENGTH");
13043 
13044     if (item)
13045         apr_table_setn(r->headers_in, "Content-Length", item);
13046 
13047     /* Set details of WSGI specific request config. */
13048 
13049     config->process_group = apr_table_get(r->subprocess_env,
13050                                           "mod_wsgi.process_group");
13051     config->application_group = apr_table_get(r->subprocess_env,
13052                                               "mod_wsgi.application_group");
13053     config->callable_object = apr_table_get(r->subprocess_env,
13054                                             "mod_wsgi.callable_object");
13055 
13056     config->handler_script = apr_table_get(r->subprocess_env,
13057                                            "mod_wsgi.handler_script");
13058 
13059     config->script_reloading = atoi(apr_table_get(r->subprocess_env,
13060                                                   "mod_wsgi.script_reloading"));
13061 
13062     item = apr_table_get(r->subprocess_env, "mod_wsgi.enable_sendfile");
13063 
13064     if (item && !strcasecmp(item, "1"))
13065         config->enable_sendfile = 1;
13066     else
13067         config->enable_sendfile = 0;
13068 
13069     item = apr_table_get(r->subprocess_env, "mod_wsgi.ignore_activity");
13070 
13071     if (item && !strcasecmp(item, "1"))
13072         config->ignore_activity = 1;
13073     else
13074         config->ignore_activity = 0;
13075 
13076     config->daemon_connects = atoi(apr_table_get(r->subprocess_env,
13077                                                  "mod_wsgi.daemon_connects"));
13078     config->daemon_restarts = atoi(apr_table_get(r->subprocess_env,
13079                                                  "mod_wsgi.daemon_restarts"));
13080 
13081     item = apr_table_get(r->subprocess_env, "mod_wsgi.request_start");
13082 
13083     if (item) {
13084         errno = 0;
13085         config->request_start = apr_strtoi64(item, (char **)&item, 10);
13086 
13087         if (!*item && errno != ERANGE)
13088             r->request_time = config->request_start;
13089         else
13090             config->request_start = 0.0;
13091     }
13092 
13093     item = apr_table_get(r->subprocess_env, "mod_wsgi.queue_start");
13094 
13095     if (item) {
13096         errno = 0;
13097         config->queue_start = apr_strtoi64(item, (char **)&item, 10);
13098 
13099         if (!(!*item && errno != ERANGE))
13100             config->queue_start = 0.0;
13101     }
13102 
13103     config->daemon_start = apr_time_now();
13104 
13105     apr_table_setn(r->subprocess_env, "mod_wsgi.daemon_start",
13106                    apr_psprintf(r->pool, "%" APR_TIME_T_FMT,
13107                    config->daemon_start));
13108 
13109 #if AP_MODULE_MAGIC_AT_LEAST(20100923,2)
13110     item = apr_table_get(r->subprocess_env, "mod_wsgi.request_id");
13111 
13112     if (item)
13113         r->log_id = item;
13114 
13115     item = apr_table_get(r->subprocess_env, "mod_wsgi.connection_id");
13116 
13117     if (item)
13118         r->connection->log_id = item;
13119 #endif
13120 
13121     /*
13122      * Install the standard HTTP input filter and set header for
13123      * chunked transfer encoding to force it to dechunk the input.
13124      * This is necessary as we chunk the data that is proxied to
13125      * the daemon processes so that we can determining whether we
13126      * actually receive all input or it was truncated.
13127      *
13128      * Note that the subprocess_env table that gets passed to the
13129      * WSGI environ dictionary has already been populated, so the
13130      * Transfer-Encoding header will not be passed in the WSGI
13131      * environ dictionary as a result of this.
13132      */
13133 
13134     apr_table_setn(r->headers_in, "Transfer-Encoding", "chunked");
13135 
13136     ap_add_input_filter("HTTP_IN", NULL, r, r->connection);
13137 
13138     /* Check for queue timeout. */
13139 
13140     r->status = HTTP_OK;
13141 
13142     if (wsgi_daemon_process->group->queue_timeout) {
13143         if (config->request_start) {
13144             apr_time_t queue_time = 0;
13145 
13146             queue_time = config->daemon_start - config->request_start;
13147 
13148             if (queue_time > wsgi_daemon_process->group->queue_timeout) {
13149                 queue_timeout_occurred = 1;
13150 
13151                 r->status = HTTP_INTERNAL_SERVER_ERROR;
13152                 r->status_line = "200 Timeout";
13153 
13154                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
13155                              "mod_wsgi (pid=%d): Queue timeout expired "
13156                              "for WSGI daemon process '%s'.", getpid(),
13157                              wsgi_daemon_process->group->name);
13158             }
13159         }
13160     }
13161 
13162     /*
13163      * Execute the actual target WSGI application. In
13164      * normal cases OK should always be returned. If
13165      * however an error occurs in importing or executing
13166      * the script or the Python code raises an exception
13167      * which is not caught and handled, then an internal
13168      * server error can be returned. As we don't want to
13169      * be triggering any error document handlers in the
13170      * daemon process we use a fake status line with 0
13171      * as the status value. This will be picked up in
13172      * the Apache child process which will translate it
13173      * back to a 500 error so that normal error document
13174      * processing occurs.
13175      */
13176 
13177     if (!queue_timeout_occurred) {
13178         if (wsgi_execute_script(r) != OK) {
13179             r->status = HTTP_INTERNAL_SERVER_ERROR;
13180             r->status_line = "200 Error";
13181         }
13182     }
13183 
13184     /*
13185      * Ensure that request is finalised and any response
13186      * is flushed out. This will as a side effect read
13187      * any input data which wasn't consumed, thus
13188      * ensuring that the Apache child process isn't hung
13189      * waiting to send the request content and can
13190      * therefore process the response correctly.
13191      */
13192 
13193     ap_finalize_request_protocol(r);
13194 
13195     bb = apr_brigade_create(r->pool, c->bucket_alloc);
13196     e = apr_bucket_flush_create(c->bucket_alloc);
13197     APR_BRIGADE_INSERT_HEAD(bb, e);
13198     ap_pass_brigade(r->connection->output_filters, bb);
13199 
13200     apr_pool_destroy(p);
13201 
13202     return OK;
13203 }
13204 
13205 #endif
13206 
13207 /*
13208  * Apache 2.X module initialisation functions.
13209  */
13210 
wsgi_hook_init(apr_pool_t * pconf,apr_pool_t * ptemp,apr_pool_t * plog,server_rec * s)13211 static int wsgi_hook_init(apr_pool_t *pconf, apr_pool_t *ptemp,
13212                           apr_pool_t *plog, server_rec *s)
13213 {
13214     void *data = NULL;
13215     const char *userdata_key;
13216     char package[128];
13217     char interpreter[256];
13218 
13219     int status = OK;
13220 
13221     /*
13222      * No longer support using mod_python at the same time as
13223      * mod_wsgi as becoming too painful to hack around
13224      * mod_python's broken usage of threading APIs when align
13225      * code to the stricter API requirements of Python 3.2.
13226      */
13227 
13228     userdata_key = "python_init";
13229 
13230     apr_pool_userdata_get(&data, userdata_key, s->process->pool);
13231     if (data) {
13232         ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
13233                      "mod_wsgi (pid=%d): The mod_python module can "
13234                      "not be used in conjunction with mod_wsgi 4.0+. "
13235                      "Remove the mod_python module from the Apache "
13236                      "configuration.", getpid());
13237 
13238         return HTTP_INTERNAL_SERVER_ERROR;
13239     }
13240 
13241     /*
13242      * Init function gets called twice during startup, we only
13243      * need to actually do anything on the second time it is
13244      * called. This avoids unecessarily initialising and then
13245      * destroying Python for no reason. We also though have to
13246      * deal with a special case when a graceful restart is done.
13247      * For that we are only called once, which is generally okay
13248      * as the 'wsgi_init' key will be set from initial start up
13249      * of the server. The exception to this is where the module
13250      * is only loaded into Apache when the server is already
13251      * running. In this case we have to detect that it is not
13252      * the initial startup, but a subsequent restart. We can do
13253      * this by looking at whether the scoreboard has been
13254      * initialised yet. That is probably enough, but to be safe,
13255      * also check what generation it is.
13256      */
13257 
13258     userdata_key = "wsgi_init";
13259 
13260     apr_pool_userdata_get(&data, userdata_key, s->process->pool);
13261 
13262     if (!data) {
13263         apr_pool_userdata_set((const void *)1, userdata_key,
13264                               apr_pool_cleanup_null, s->process->pool);
13265 
13266         /*
13267          * Check for the special case of a graceful restart and
13268          * the module being loaded for the first time. In this
13269          * case we still go onto perform initialisation as the
13270          * initialisation routine for the module will not be
13271          * called a second time.
13272          */
13273 
13274         if (!ap_scoreboard_image ||
13275             ap_get_scoreboard_global()->running_generation == 0) {
13276 
13277             return OK;
13278         }
13279     }
13280 
13281     /* Setup module version information. */
13282 
13283     sprintf(package, "mod_wsgi/%s", MOD_WSGI_VERSION_STRING);
13284 
13285     ap_add_version_component(pconf, package);
13286 
13287     /* Record Python version string with Apache. */
13288 
13289     sprintf(interpreter, "Python/%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION);
13290     ap_add_version_component(pconf, interpreter);
13291 
13292     /* Retain reference to base server. */
13293 
13294     wsgi_server = s;
13295 
13296     /* Retain record of parent process ID. */
13297 
13298     wsgi_parent_pid = getpid();
13299 
13300     /* Determine whether multiprocess and/or multithread. */
13301 
13302     ap_mpm_query(AP_MPMQ_IS_THREADED, &wsgi_multithread);
13303     wsgi_multithread = (wsgi_multithread != AP_MPMQ_NOT_SUPPORTED);
13304 
13305     ap_mpm_query(AP_MPMQ_IS_FORKED, &wsgi_multiprocess);
13306     if (wsgi_multiprocess != AP_MPMQ_NOT_SUPPORTED) {
13307         ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &wsgi_multiprocess);
13308         wsgi_multiprocess = (wsgi_multiprocess != 1);
13309     }
13310 
13311     /* Retain reference to main server config. */
13312 
13313     wsgi_server_config = ap_get_module_config(s->module_config, &wsgi_module);
13314 
13315     /*
13316      * Check that the version of Python found at
13317      * runtime is what was used at compilation.
13318      *
13319      * XXX Can't do this as will cause Anaconda
13320      * Python to fail as not safe to call the
13321      * Py_GetVersion() function before one calls
13322      * the Py_Initialize() function when using
13323      * Anaconda Python.
13324      */
13325 
13326 #if 0
13327     wsgi_python_version();
13328 #endif
13329 
13330     /*
13331      * Initialise Python if required to be done in
13332      * the parent process. Note that it will not be
13333      * initialised if mod_python loaded and it has
13334      * already been done.
13335      */
13336 
13337     if (wsgi_python_required == -1)
13338         wsgi_python_required = 1;
13339 
13340     if (!wsgi_python_after_fork)
13341         wsgi_python_init(pconf);
13342 
13343     /*
13344      * Startup separate named daemon processes. This is
13345      * a bit tricky as we only want to do this after the
13346      * scoreboard has been created. On the initial server
13347      * startup though, this hook function is called prior
13348      * to the MPM being run, which means the scoreboard
13349      * hasn't been created yet. In that case we need to
13350      * defer process creation until after that, which we
13351      * can only do by hooking into the pre_mpm hook after
13352      * scoreboard creation has been done. On a server
13353      * restart, the scoreboard will be preserved, so we
13354      * can do it here, which is just as well as the pre_mpm
13355      * hook isn't run on a restart.
13356      */
13357 
13358 #if defined(MOD_WSGI_WITH_DAEMONS)
13359     if (!ap_scoreboard_image) {
13360         /*
13361          * Need to remember the pool we were given here as
13362          * the pre_mpm hook functions get given a different
13363          * pool which isn't the one we want and if we use
13364          * that then Apache will crash when it is being
13365          * shutdown. So our pre_mpm hook will use the pool
13366          * we have remembered here.
13367          */
13368 
13369         wsgi_pconf_pool = pconf;
13370 
13371         ap_hook_pre_mpm(wsgi_deferred_start_daemons, NULL, NULL,
13372                         APR_HOOK_REALLY_LAST);
13373     }
13374     else
13375         status = wsgi_start_daemons(pconf);
13376 #endif
13377 
13378     return status;
13379 }
13380 
wsgi_hook_child_init(apr_pool_t * p,server_rec * s)13381 static void wsgi_hook_child_init(apr_pool_t *p, server_rec *s)
13382 {
13383 #if defined(MOD_WSGI_WITH_DAEMONS)
13384     WSGIProcessGroup *entries = NULL;
13385     WSGIProcessGroup *entry = NULL;
13386 
13387     int i;
13388 
13389     /* Close listener sockets for daemon processes. */
13390 
13391     if (wsgi_daemon_list) {
13392         entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
13393 
13394         for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
13395             entry = &entries[i];
13396 
13397             if (entry->listener_fd != -1) {
13398                 close(entry->listener_fd);
13399                 entry->listener_fd = -1;
13400             }
13401         }
13402     }
13403 #endif
13404 
13405     /* Remember worker process ID. */
13406 
13407     wsgi_worker_pid = getpid();
13408 
13409     /* Time child process started waiting for requests. */
13410 
13411     wsgi_restart_time = apr_time_now();
13412 
13413     /* Create lock for request monitoring. */
13414 
13415     apr_thread_mutex_create(&wsgi_monitor_lock,
13416                             APR_THREAD_MUTEX_UNNESTED, p);
13417 
13418     if (wsgi_python_required) {
13419         /*
13420          * Initialise Python if required to be done in
13421          * the child process. Note that it will not be
13422          * initialised if mod_python loaded and it has
13423          * already been done.
13424          */
13425 
13426         if (wsgi_python_after_fork)
13427             wsgi_python_init(p);
13428 
13429         /*
13430          * Now perform additional initialisation steps
13431          * always done in child process.
13432          */
13433 
13434         wsgi_python_child_init(p);
13435     }
13436 }
13437 
13438 #include "apr_lib.h"
13439 
wsgi_original_uri(request_rec * r)13440 static char *wsgi_original_uri(request_rec *r)
13441 {
13442     char *first, *last;
13443 
13444     if (r->the_request == NULL) {
13445         return (char *) apr_pcalloc(r->pool, 1);
13446     }
13447 
13448     first = r->the_request;     /* use the request-line */
13449 
13450     while (*first && !apr_isspace(*first)) {
13451         ++first;                /* skip over the method */
13452     }
13453     while (apr_isspace(*first)) {
13454         ++first;                /*   and the space(s)   */
13455     }
13456 
13457     last = first;
13458     while (*last && !apr_isspace(*last)) {
13459         ++last;                 /* end at next whitespace */
13460     }
13461 
13462     return apr_pstrmemdup(r->pool, first, last - first);
13463 }
13464 
wsgi_http_invalid_header(const char * w)13465 static int wsgi_http_invalid_header(const char *w)
13466 {
13467     char c;
13468 
13469     while ((c = *w++) != 0) {
13470         if (!apr_isalnum(c) && c != '-')
13471             return 1;
13472     }
13473 
13474     return 0;
13475 }
13476 
wsgi_drop_invalid_headers(request_rec * r)13477 static void wsgi_drop_invalid_headers(request_rec *r)
13478 {
13479     /*
13480      * Apache 2.2 when converting headers for CGI variables, doesn't
13481      * ignore headers with invalid names. That is, any which use any
13482      * characters besides alphanumerics and the '-' character. This
13483      * opens us up to header spoofing whereby something can inject
13484      * multiple headers which differ by using non alphanumeric
13485      * characters in the same position, which would then encode to same
13486      * value. Since not easy to cleanup after the fact, as a workaround,
13487      * is easier to simply remove the invalid headers. This will make
13488      * things end up being the same as Apache 2.4. Doing this could
13489      * annoy some users of Apache 2.2 who were using invalid headers,
13490      * but things will break for them under Apache 2.4 anyway.
13491      */
13492 
13493     apr_array_header_t *to_delete = NULL;
13494 
13495     const apr_array_header_t *hdrs_arr;
13496     const apr_table_entry_t *hdrs;
13497 
13498     int i;
13499 
13500     hdrs_arr = apr_table_elts(r->headers_in);
13501     hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
13502 
13503     for (i = 0; i < hdrs_arr->nelts; ++i) {
13504         if (!hdrs[i].key) {
13505             continue;
13506         }
13507 
13508         if (wsgi_http_invalid_header(hdrs[i].key)) {
13509             char **new;
13510 
13511             if (!to_delete)
13512                 to_delete = apr_array_make(r->pool, 1, sizeof(char *));
13513 
13514             new = (char **)apr_array_push(to_delete);
13515             *new = hdrs[i].key;
13516         }
13517     }
13518 
13519     if (to_delete) {
13520         char *key;
13521 
13522         for (i = 0; i < to_delete->nelts; i++) {
13523             key = ((char **)to_delete->elts)[i];
13524 
13525             apr_table_unset(r->headers_in, key);
13526         }
13527     }
13528 }
13529 
13530 static const char *wsgi_proxy_client_headers[] = {
13531     "HTTP_X_FORWARDED_FOR",
13532     "HTTP_X_CLIENT_IP",
13533     "HTTP_X_REAL_IP",
13534     NULL,
13535 };
13536 
13537 static const char *wsgi_proxy_scheme_headers[] = {
13538     "HTTP_X_FORWARDED_HTTPS",
13539     "HTTP_X_FORWARDED_PROTO",
13540     "HTTP_X_FORWARDED_SCHEME",
13541     "HTTP_X_FORWARDED_SSL",
13542     "HTTP_X_HTTPS",
13543     "HTTP_X_SCHEME",
13544     NULL,
13545 };
13546 
13547 static const char *wsgi_proxy_host_headers[] = {
13548     "HTTP_X_FORWARDED_HOST",
13549     "HTTP_X_HOST",
13550     NULL,
13551 };
13552 
13553 static const char *wsgi_proxy_script_name_headers[] = {
13554     "HTTP_X_SCRIPT_NAME",
13555     "HTTP_X_FORWARDED_SCRIPT_NAME",
13556     NULL,
13557 };
13558 
wsgi_ip_is_in_array(apr_sockaddr_t * client_ip,apr_array_header_t * proxy_ips)13559 static int wsgi_ip_is_in_array(apr_sockaddr_t *client_ip,
13560                                apr_array_header_t *proxy_ips) {
13561     int i;
13562     apr_ipsubnet_t **subs = (apr_ipsubnet_t **)proxy_ips->elts;
13563 
13564     for (i = 0; i < proxy_ips->nelts; i++) {
13565         if (apr_ipsubnet_test(subs[i], client_ip)) {
13566             return 1;
13567         }
13568     }
13569 
13570     return 0;
13571 }
13572 
wsgi_process_forwarded_for(request_rec * r,WSGIRequestConfig * config,const char * value)13573 static void wsgi_process_forwarded_for(request_rec *r,
13574                                        WSGIRequestConfig *config,
13575                                        const char *value
13576 )
13577 {
13578     if (config->trusted_proxies) {
13579         /*
13580          * A potentially comma separated list where client we are
13581          * interested in will be that immediately before the last
13582          * trusted proxy working from the end forwards. If there
13583          * are no trusted proxies then we use the last.
13584          */
13585 
13586         apr_array_header_t *arr;
13587 
13588         arr = apr_array_make(r->pool, 3, sizeof(char *));
13589 
13590         while (*value != '\0') {
13591             /* Skip leading whitespace for item. */
13592 
13593             while (*value != '\0' && apr_isspace(*value))
13594                 value++;
13595 
13596             if (*value != '\0') {
13597                 const char *end = NULL;
13598                 const char *next = NULL;
13599 
13600                 char **entry = NULL;
13601 
13602                 end = value;
13603 
13604                 while (*end != '\0' && *end != ',')
13605                     end++;
13606 
13607                 if (*end == '\0')
13608                     next = end;
13609                 else if (*end == ',')
13610                     next = end+1;
13611 
13612                 /* Need deal with trailing whitespace. */
13613 
13614                 while (end != value) {
13615                     if (!apr_isspace(*(end-1)))
13616                         break;
13617 
13618                     end--;
13619                 }
13620 
13621                 entry = (char **)apr_array_push(arr);
13622                 *entry = apr_pstrndup(r->pool, value, (end-value));
13623 
13624                 value = next;
13625             }
13626         }
13627 
13628         if (arr->nelts != 0) {
13629             /* HTTP_X_FORDWARDED_FOR wasn't just an empty string. */
13630 
13631             char **items;
13632             int first = -1;
13633             int i;
13634 
13635             items = (char **)arr->elts;
13636 
13637             /*
13638              * Work out the position of the IP closest to the start
13639              * that we actually trusted.
13640              */
13641 
13642             for (i=arr->nelts; i>0; ) {
13643                 apr_sockaddr_t *sa;
13644                 apr_status_t rv;
13645 
13646                 i--;
13647 
13648                 rv = apr_sockaddr_info_get(&sa, items[i], APR_UNSPEC,
13649                                            0, 0, r->pool);
13650 
13651                 if (rv == APR_SUCCESS) {
13652                     if (!wsgi_ip_is_in_array(sa, config->trusted_proxies))
13653                         break;
13654 
13655                     first = i;
13656                 }
13657                 else {
13658                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, r,
13659                               "mod_wsgi (pid=%d): Forwarded IP of \"%s\" is "
13660                               "not a valid IP address.", getpid(), items[i]);
13661                     break;
13662                 }
13663             }
13664 
13665             if (first >= 0) {
13666                 /*
13667                  * We found at least one trusted IP. We use the
13668                  * IP that may have appeared before that as
13669                  * REMOTE_ADDR. We rewrite HTTP_X_FORWARDED_FOR
13670                  * to record only from REMOTE_ADDR onwards.
13671                  */
13672 
13673                 char *list;
13674 
13675                 i = first-1;
13676                 if (i<0)
13677                     i = 0;
13678 
13679                 apr_table_setn(r->subprocess_env, "REMOTE_ADDR", items[i]);
13680 
13681                 list = items[i];
13682 
13683                 i++;
13684 
13685                 while (arr->nelts != i) {
13686                     list = apr_pstrcat(r->pool, list, ", ", items[i], NULL);
13687                     i++;
13688                 }
13689 
13690                 apr_table_setn(r->subprocess_env, "HTTP_X_FORWARDED_FOR",
13691                                list);
13692             }
13693             else {
13694                 /*
13695                  * No trusted IP. Use the last for REMOTE_ADDR.
13696                  * We rewrite HTTP_X_FORWARDED_FOR to record only
13697                  * the last.
13698                  */
13699 
13700                 apr_table_setn(r->subprocess_env, "REMOTE_ADDR",
13701                         items[arr->nelts-1]);
13702                 apr_table_setn(r->subprocess_env, "HTTP_X_FORWARDED_FOR",
13703                         items[arr->nelts-1]);
13704             }
13705         }
13706     }
13707     else {
13708         /*
13709          * We do not need to validate the proxies. We will have a
13710          * potentially comma separated list where the client we
13711          * are interested in will be listed first.
13712          */
13713 
13714         const char *end = NULL;
13715 
13716         /* Skip leading whitespace for item. */
13717 
13718         while (*value != '\0' && apr_isspace(*value))
13719             value++;
13720 
13721         if (*value != '\0') {
13722             end = value;
13723 
13724             while (*end != '\0' && *end != ',')
13725                 end++;
13726 
13727             /* Need deal with trailing whitespace. */
13728 
13729             while (end != value) {
13730                 if (!apr_isspace(*(end-1)))
13731                     break;
13732 
13733                 end--;
13734             }
13735 
13736             /* Override REMOTE_ADDR. Leave HTTP_X_FORWARDED_FOR. */
13737 
13738             apr_table_setn(r->subprocess_env, "REMOTE_ADDR",
13739                     apr_pstrndup(r->pool, value, (end-value)));
13740         }
13741     }
13742 }
13743 
wsgi_process_proxy_headers(request_rec * r)13744 static void wsgi_process_proxy_headers(request_rec *r)
13745 {
13746     WSGIRequestConfig *config = NULL;
13747 
13748     apr_array_header_t *trusted_proxy_headers = NULL;
13749 
13750     int match_client_header = 0;
13751     int match_host_header = 0;
13752     int match_script_name_header = 0;
13753     int match_scheme_header = 0;
13754 
13755     const char *trusted_client_header = NULL;
13756     const char *trusted_host_header = NULL;
13757     const char *trusted_script_name_header = NULL;
13758     const char *trusted_scheme_header = NULL;
13759 
13760     int i = 0;
13761 
13762     int trusted_proxy = 1;
13763 
13764     const char *client_ip = NULL;
13765 
13766     apr_status_t rv;
13767 
13768     config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
13769                                                        &wsgi_module);
13770 
13771     trusted_proxy_headers = config->trusted_proxy_headers;
13772 
13773     /* Nothing to do if no trusted headers have been specified. */
13774 
13775     if (!trusted_proxy_headers)
13776         return;
13777 
13778     /*
13779      * Check for any special processing required for each trusted
13780      * header which has been specified. We should only do this if
13781      * there was no list of trusted proxies, or if the client IP
13782      * was that of a trusted proxy.
13783      */
13784 
13785     if (config->trusted_proxies) {
13786         client_ip = apr_table_get(r->subprocess_env, "REMOTE_ADDR");
13787 
13788         if (client_ip) {
13789             apr_sockaddr_t *sa;
13790 
13791             rv = apr_sockaddr_info_get(&sa, client_ip, APR_UNSPEC,
13792                                        0, 0, r->pool);
13793 
13794             if (rv == APR_SUCCESS) {
13795                 if (!wsgi_ip_is_in_array(sa, config->trusted_proxies))
13796                     trusted_proxy = 0;
13797             }
13798             else {
13799                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
13800                               "mod_wsgi (pid=%d): REMOTE_ADDR of \"%s\" is "
13801                               "not a valid IP address.", getpid(), client_ip);
13802 
13803                 trusted_proxy = 0;
13804             }
13805         }
13806         else
13807             trusted_proxy = 0;
13808     }
13809 
13810     if (trusted_proxy) {
13811         for (i=0; i<trusted_proxy_headers->nelts; i++) {
13812             const char *name;
13813             const char *value;
13814 
13815             name = ((const char**)trusted_proxy_headers->elts)[i];
13816             value = apr_table_get(r->subprocess_env, name);
13817 
13818             if (!strcmp(name, "HTTP_X_FORWARDED_FOR")) {
13819                 match_client_header = 1;
13820 
13821                 if (value) {
13822                     wsgi_process_forwarded_for(r, config, value);
13823 
13824                     trusted_client_header = name;
13825                 }
13826             }
13827             else if (!strcmp(name, "HTTP_X_CLIENT_IP") ||
13828                     !strcmp(name, "HTTP_X_REAL_IP")) {
13829 
13830                 match_client_header = 1;
13831 
13832                 if (value) {
13833                     /* Use the value as is. */
13834 
13835                     apr_table_setn(r->subprocess_env, "REMOTE_ADDR", value);
13836 
13837                     trusted_client_header = name;
13838                 }
13839             }
13840             else if (!strcmp(name, "HTTP_X_FORWARDED_HOST") ||
13841                      !strcmp(name, "HTTP_X_HOST")) {
13842 
13843                 match_host_header = 1;
13844 
13845                 if (value) {
13846                     /* Use the value as is. May include a port. */
13847 
13848                     trusted_host_header = name;
13849 
13850                     apr_table_setn(r->subprocess_env, "HTTP_HOST", value);
13851                 }
13852             }
13853             else if (!strcmp(name, "HTTP_X_FORWARDED_SERVER")) {
13854                 if (value) {
13855                     /* Use the value as is. */
13856 
13857                     apr_table_setn(r->subprocess_env, "SERVER_NAME", value);
13858                 }
13859             }
13860             else if (!strcmp(name, "HTTP_X_FORWARDED_PORT")) {
13861                 if (value) {
13862                     /* Use the value as is. */
13863 
13864                     apr_table_setn(r->subprocess_env, "SERVER_PORT", value);
13865                 }
13866             }
13867             else if (!strcmp(name, "HTTP_X_SCRIPT_NAME") ||
13868                      !strcmp(name, "HTTP_X_FORWARDED_SCRIPT_NAME")) {
13869 
13870                 match_script_name_header = 1;
13871 
13872                 if (value) {
13873                     /*
13874                      * Use the value as is. We want to remember what the
13875                      * original value for SCRIPT_NAME was though.
13876                      */
13877 
13878                     apr_table_setn(r->subprocess_env, "mod_wsgi.mount_point",
13879                                    value);
13880 
13881                     trusted_script_name_header = name;
13882 
13883                     apr_table_setn(r->subprocess_env, "SCRIPT_NAME", value);
13884                 }
13885             }
13886             else if (!strcmp(name, "HTTP_X_FORWARDED_PROTO") ||
13887                 !strcmp(name, "HTTP_X_FORWARDED_SCHEME") ||
13888                 !strcmp(name, "HTTP_X_SCHEME")) {
13889 
13890                 match_scheme_header = 1;
13891 
13892                 if (value) {
13893                     trusted_scheme_header = name;
13894 
13895                     /* Value can be either 'http' or 'https'. */
13896 
13897                     if (!strcasecmp(value, "https"))
13898                         apr_table_setn(r->subprocess_env, "HTTPS", "1");
13899                     else if (!strcasecmp(value, "http"))
13900                         apr_table_unset(r->subprocess_env, "HTTPS");
13901                 }
13902             }
13903             else if (!strcmp(name, "HTTP_X_FORWARDED_HTTPS") ||
13904                      !strcmp(name, "HTTP_X_FORWARDED_SSL") ||
13905                      !strcmp(name, "HTTP_X_HTTPS")) {
13906 
13907                 match_scheme_header = 1;
13908 
13909                 if (value) {
13910                     trusted_scheme_header = name;
13911 
13912                     /*
13913                      * Value can be a boolean like flag such as 'On',
13914                      * 'Off', 'true', 'false', '1' or '0'.
13915                      */
13916 
13917                     if (!strcasecmp(value, "On") ||
13918                         !strcasecmp(value, "true") ||
13919                         !strcasecmp(value, "1")) {
13920 
13921                         apr_table_setn(r->subprocess_env, "HTTPS", "1");
13922                     }
13923                     else if (!strcasecmp(value, "Off") ||
13924                         !strcasecmp(value, "false") ||
13925                         !strcasecmp(value, "0")) {
13926 
13927                         apr_table_unset(r->subprocess_env, "HTTPS");
13928                     }
13929                 }
13930             }
13931         }
13932     }
13933     else {
13934         /*
13935          * If it isn't a trusted proxy, we still need to knock
13936          * out any headers for categories we were interested in.
13937          */
13938 
13939         for (i=0; i<trusted_proxy_headers->nelts; i++) {
13940             const char *name;
13941 
13942             name = ((const char**)trusted_proxy_headers->elts)[i];
13943 
13944             if (!strcmp(name, "HTTP_X_FORWARDED_FOR") ||
13945                      !strcmp(name, "HTTP_X_REAL_IP")) {
13946 
13947                 match_client_header = 1;
13948             }
13949             else if (!strcmp(name, "HTTP_X_FORWARDED_HOST") ||
13950                      !strcmp(name, "HTTP_X_HOST")) {
13951 
13952                 match_host_header = 1;
13953             }
13954             else if (!strcmp(name, "HTTP_X_SCRIPT_NAME") ||
13955                      !strcmp(name, "HTTP_X_FORWARDED_SCRIPT_NAME")) {
13956 
13957                 match_script_name_header = 1;
13958             }
13959             else if (!strcmp(name, "HTTP_X_FORWARDED_PROTO") ||
13960                 !strcmp(name, "HTTP_X_FORWARDED_SCHEME") ||
13961                 !strcmp(name, "HTTP_X_SCHEME") ||
13962                 !strcmp(name, "HTTP_X_FORWARDED_HTTPS") ||
13963                 !strcmp(name, "HTTP_X_FORWARDED_SSL") ||
13964                 !strcmp(name, "HTTP_X_HTTPS")) {
13965 
13966                 match_scheme_header = 1;
13967             }
13968         }
13969     }
13970 
13971     /*
13972      * Remove all client IP headers from request environment which
13973      * weren't matched as being trusted.
13974      */
13975 
13976     if (match_client_header) {
13977         const char *name = NULL;
13978 
13979         for (i=0; (name=wsgi_proxy_client_headers[i]); i++) {
13980             if (!trusted_client_header || strcmp(name, trusted_client_header)) {
13981                 apr_table_unset(r->subprocess_env, name);
13982             }
13983         }
13984     }
13985 
13986     /*
13987      * Remove all proxy scheme headers from request environment
13988      * which weren't matched as being trusted.
13989      */
13990 
13991     if (match_scheme_header) {
13992         const char *name = NULL;
13993 
13994         for (i=0; (name=wsgi_proxy_scheme_headers[i]); i++) {
13995             if (!trusted_scheme_header || strcmp(name, trusted_scheme_header)) {
13996                 apr_table_unset(r->subprocess_env, name);
13997             }
13998         }
13999     }
14000 
14001     /*
14002      * Remove all proxy host from request environment which weren't
14003      * matched as being trusted.
14004      */
14005 
14006     if (match_host_header) {
14007         const char *name = NULL;
14008 
14009         for (i=0; (name=wsgi_proxy_host_headers[i]); i++) {
14010             if (!trusted_host_header || strcmp(name, trusted_host_header))
14011                 apr_table_unset(r->subprocess_env, name);
14012         }
14013     }
14014 
14015     /*
14016      * Remove all proxy script name headers from request environment
14017      * which weren't matched as being trusted.
14018      */
14019 
14020     if (match_script_name_header) {
14021         const char *name = NULL;
14022 
14023         for (i=0; (name=wsgi_proxy_script_name_headers[i]); i++) {
14024             if (!trusted_script_name_header ||
14025                 strcmp(name, trusted_script_name_header)) {
14026                 apr_table_unset(r->subprocess_env, name);
14027             }
14028         }
14029     }
14030 }
14031 
wsgi_http2env(apr_pool_t * a,const char * w)14032 static char *wsgi_http2env(apr_pool_t *a, const char *w)
14033 {
14034     char *res = (char *)apr_palloc(a, sizeof("HTTP_") + strlen(w));
14035     char *cp = res;
14036     char c;
14037 
14038     *cp++ = 'H';
14039     *cp++ = 'T';
14040     *cp++ = 'T';
14041     *cp++ = 'P';
14042     *cp++ = '_';
14043 
14044     while ((c = *w++) != 0) {
14045         if (apr_isalnum(c)) {
14046             *cp++ = apr_toupper(c);
14047         }
14048         else if (c == '-') {
14049             *cp++ = '_';
14050         }
14051         else
14052             return NULL;
14053     }
14054     *cp = 0;
14055 
14056     return res;
14057 }
14058 
14059 typedef struct {
14060         PyObject_HEAD
14061         request_rec *r;
14062         WSGIRequestConfig *config;
14063         PyObject *log;
14064 } AuthObject;
14065 
newAuthObject(request_rec * r,WSGIRequestConfig * config)14066 static AuthObject *newAuthObject(request_rec *r, WSGIRequestConfig *config)
14067 {
14068     AuthObject *self;
14069 
14070     self = PyObject_New(AuthObject, &Auth_Type);
14071     if (self == NULL)
14072         return NULL;
14073 
14074     self->config = config;
14075 
14076     self->r = r;
14077 
14078     self->log = newLogObject(r, APLOG_ERR, NULL, 0);
14079 
14080     return self;
14081 }
14082 
Auth_dealloc(AuthObject * self)14083 static void Auth_dealloc(AuthObject *self)
14084 {
14085     Py_DECREF(self->log);
14086 
14087     PyObject_Del(self);
14088 }
14089 
Auth_environ(AuthObject * self,const char * group)14090 static PyObject *Auth_environ(AuthObject *self, const char *group)
14091 {
14092     PyObject *vars;
14093     PyObject *object;
14094 
14095     request_rec *r = self->r;
14096     server_rec *s = r->server;
14097     conn_rec *c = r->connection;
14098     apr_port_t rport;
14099 
14100     const apr_array_header_t *hdrs_arr;
14101     const apr_table_entry_t *hdrs;
14102 
14103     const char *value = NULL;
14104 
14105     int i;
14106 
14107     vars = PyDict_New();
14108 
14109     hdrs_arr = apr_table_elts(r->headers_in);
14110     hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
14111 
14112     for (i = 0; i < hdrs_arr->nelts; ++i) {
14113         if (!hdrs[i].key) {
14114             continue;
14115         }
14116 
14117         if (!strcasecmp(hdrs[i].key, "Content-type")) {
14118 #if PY_MAJOR_VERSION >= 3
14119             object = PyUnicode_DecodeLatin1(hdrs[i].val,
14120                                             strlen(hdrs[i].val), NULL);
14121 #else
14122             object = PyString_FromString(hdrs[i].val);
14123 #endif
14124             PyDict_SetItemString(vars, "CONTENT_TYPE", object);
14125             Py_DECREF(object);
14126         }
14127         else if (!strcasecmp(hdrs[i].key, "Content-length")) {
14128 #if PY_MAJOR_VERSION >= 3
14129             object = PyUnicode_DecodeLatin1(hdrs[i].val,
14130                                             strlen(hdrs[i].val), NULL);
14131 #else
14132             object = PyString_FromString(hdrs[i].val);
14133 #endif
14134             PyDict_SetItemString(vars, "CONTENT_LENGTH", object);
14135             Py_DECREF(object);
14136         }
14137         else if (!strcasecmp(hdrs[i].key, "Authorization")
14138                  || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) {
14139             continue;
14140         }
14141         else {
14142             if (hdrs[i].val) {
14143                 char *header = wsgi_http2env(r->pool, hdrs[i].key);
14144 
14145                 if (header) {
14146 #if PY_MAJOR_VERSION >= 3
14147                     object = PyUnicode_DecodeLatin1(hdrs[i].val,
14148                                                     strlen(hdrs[i].val), NULL);
14149 #else
14150                     object = PyString_FromString(hdrs[i].val);
14151 #endif
14152 
14153                     PyDict_SetItemString(vars, header, object);
14154 
14155                     Py_DECREF(object);
14156                 }
14157             }
14158         }
14159     }
14160 
14161     value = ap_psignature("", r);
14162 #if PY_MAJOR_VERSION >= 3
14163     object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14164 #else
14165     object = PyString_FromString(value);
14166 #endif
14167     PyDict_SetItemString(vars, "SERVER_SIGNATURE", object);
14168     Py_DECREF(object);
14169 
14170 #if AP_MODULE_MAGIC_AT_LEAST(20060905,0)
14171     value = ap_get_server_banner();
14172 #else
14173     value = ap_get_server_version();
14174 #endif
14175 #if PY_MAJOR_VERSION >= 3
14176     object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14177 #else
14178     object = PyString_FromString(value);
14179 #endif
14180     PyDict_SetItemString(vars, "SERVER_SOFTWARE", object);
14181     Py_DECREF(object);
14182 
14183     value = ap_escape_html(r->pool, ap_get_server_name(r));
14184 #if PY_MAJOR_VERSION >= 3
14185     object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14186 #else
14187     object = PyString_FromString(value);
14188 #endif
14189     PyDict_SetItemString(vars, "SERVER_NAME", object);
14190     Py_DECREF(object);
14191 
14192     if (r->connection->local_ip) {
14193         value = r->connection->local_ip;
14194 #if PY_MAJOR_VERSION >= 3
14195         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14196 #else
14197         object = PyString_FromString(value);
14198 #endif
14199         PyDict_SetItemString(vars, "SERVER_ADDR", object);
14200         Py_DECREF(object);
14201     }
14202 
14203     value = apr_psprintf(r->pool, "%u", ap_get_server_port(r));
14204 #if PY_MAJOR_VERSION >= 3
14205     object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14206 #else
14207     object = PyString_FromString(value);
14208 #endif
14209     PyDict_SetItemString(vars, "SERVER_PORT", object);
14210     Py_DECREF(object);
14211 
14212     value = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST, NULL);
14213     if (value) {
14214 #if PY_MAJOR_VERSION >= 3
14215         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14216 #else
14217         object = PyString_FromString(value);
14218 #endif
14219         PyDict_SetItemString(vars, "REMOTE_HOST", object);
14220         Py_DECREF(object);
14221     }
14222 
14223 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
14224     if (r->useragent_ip) {
14225         value = r->useragent_ip;
14226 #if PY_MAJOR_VERSION >= 3
14227         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14228 #else
14229         object = PyString_FromString(value);
14230 #endif
14231         PyDict_SetItemString(vars, "REMOTE_ADDR", object);
14232         Py_DECREF(object);
14233     }
14234 #else
14235     if (c->remote_ip) {
14236         value = c->remote_ip;
14237 #if PY_MAJOR_VERSION >= 3
14238         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14239 #else
14240         object = PyString_FromString(value);
14241 #endif
14242         PyDict_SetItemString(vars, "REMOTE_ADDR", object);
14243         Py_DECREF(object);
14244     }
14245 #endif
14246 
14247 #if PY_MAJOR_VERSION >= 3
14248     value = ap_document_root(r);
14249     object = PyUnicode_Decode(value, strlen(value),
14250                              Py_FileSystemDefaultEncoding,
14251                              "surrogateescape");
14252 #else
14253     object = PyString_FromString(ap_document_root(r));
14254 #endif
14255     PyDict_SetItemString(vars, "DOCUMENT_ROOT", object);
14256     Py_DECREF(object);
14257 
14258     if (s->server_admin) {
14259         value = s->server_admin;
14260 #if PY_MAJOR_VERSION >= 3
14261         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14262 #else
14263         object = PyString_FromString(value);
14264 #endif
14265         PyDict_SetItemString(vars, "SERVER_ADMIN", object);
14266         Py_DECREF(object);
14267     }
14268 
14269 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
14270     rport = c->client_addr->port;
14271     value = apr_itoa(r->pool, rport);
14272 #if PY_MAJOR_VERSION >= 3
14273     object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14274 #else
14275     object = PyString_FromString(value);
14276 #endif
14277     PyDict_SetItemString(vars, "REMOTE_PORT", object);
14278     Py_DECREF(object);
14279 #else
14280     rport = c->remote_addr->port;
14281     value = apr_itoa(r->pool, rport);
14282 #if PY_MAJOR_VERSION >= 3
14283     object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14284 #else
14285     object = PyString_FromString(value);
14286 #endif
14287     PyDict_SetItemString(vars, "REMOTE_PORT", object);
14288     Py_DECREF(object);
14289 #endif
14290 
14291     value = r->protocol;
14292 #if PY_MAJOR_VERSION >= 3
14293     object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14294 #else
14295     object = PyString_FromString(value);
14296 #endif
14297     PyDict_SetItemString(vars, "SERVER_PROTOCOL", object);
14298     Py_DECREF(object);
14299 
14300     value = r->method;
14301 #if PY_MAJOR_VERSION >= 3
14302     object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14303 #else
14304     object = PyString_FromString(value);
14305 #endif
14306     PyDict_SetItemString(vars, "REQUEST_METHOD", object);
14307     Py_DECREF(object);
14308 
14309     value = r->args ? r->args : "";
14310 #if PY_MAJOR_VERSION >= 3
14311     object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14312 #else
14313     object = PyString_FromString(value);
14314 #endif
14315     PyDict_SetItemString(vars, "QUERY_STRING", object);
14316     Py_DECREF(object);
14317 
14318     value = wsgi_original_uri(r);
14319 #if PY_MAJOR_VERSION >= 3
14320     object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14321 #else
14322     object = PyString_FromString(value);
14323 #endif
14324     PyDict_SetItemString(vars, "REQUEST_URI", object);
14325     Py_DECREF(object);
14326 
14327     /*
14328      * XXX Apparently webdav does actually do modifications to
14329      * the uri and path_info attributes of request and they
14330      * could be used as part of authorisation.
14331      */
14332 
14333     if (!strcmp(r->protocol, "INCLUDED")) {
14334         value = r->uri;
14335 #if PY_MAJOR_VERSION >= 3
14336         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14337 #else
14338         object = PyString_FromString(value);
14339 #endif
14340         PyDict_SetItemString(vars, "SCRIPT_NAME", object);
14341         Py_DECREF(object);
14342 
14343         value = r->path_info ? r->path_info : "";
14344 #if PY_MAJOR_VERSION >= 3
14345         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14346 #else
14347         object = PyString_FromString(value);
14348 #endif
14349         PyDict_SetItemString(vars, "PATH_INFO", object);
14350         Py_DECREF(object);
14351     }
14352     else if (!r->path_info || !*r->path_info) {
14353         value = r->uri;
14354 #if PY_MAJOR_VERSION >= 3
14355         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14356 #else
14357         object = PyString_FromString(value);
14358 #endif
14359         PyDict_SetItemString(vars, "SCRIPT_NAME", object);
14360         Py_DECREF(object);
14361 
14362         value = "";
14363 #if PY_MAJOR_VERSION >= 3
14364         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14365 #else
14366         object = PyString_FromString(value);
14367 #endif
14368         PyDict_SetItemString(vars, "PATH_INFO", object);
14369         Py_DECREF(object);
14370     }
14371     else {
14372         int path_info_start = ap_find_path_info(r->uri, r->path_info);
14373         value = apr_pstrndup(r->pool, r->uri, path_info_start);
14374 #if PY_MAJOR_VERSION >= 3
14375         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14376 #else
14377         object = PyString_FromString(value);
14378 #endif
14379         PyDict_SetItemString(vars, "SCRIPT_NAME", object);
14380         Py_DECREF(object);
14381 
14382         value = r->path_info ? r->path_info : "";
14383 #if PY_MAJOR_VERSION >= 3
14384         object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14385 #else
14386         object = PyString_FromString(value);
14387 #endif
14388         PyDict_SetItemString(vars, "PATH_INFO", object);
14389         Py_DECREF(object);
14390     }
14391 
14392     object = Py_BuildValue("(iii)", AP_SERVER_MAJORVERSION_NUMBER,
14393                            AP_SERVER_MINORVERSION_NUMBER,
14394                            AP_SERVER_PATCHLEVEL_NUMBER);
14395     PyDict_SetItemString(vars, "apache.version", object);
14396     Py_DECREF(object);
14397 
14398     object = Py_BuildValue("(iii)", MOD_WSGI_MAJORVERSION_NUMBER,
14399                            MOD_WSGI_MINORVERSION_NUMBER,
14400                            MOD_WSGI_MICROVERSION_NUMBER);
14401     PyDict_SetItemString(vars, "mod_wsgi.version", object);
14402     Py_DECREF(object);
14403 
14404 #if PY_MAJOR_VERSION >= 3
14405     object = PyUnicode_FromString("");
14406 #else
14407     object = PyString_FromString("");
14408 #endif
14409     PyDict_SetItemString(vars, "mod_wsgi.process_group", object);
14410     Py_DECREF(object);
14411 
14412 #if PY_MAJOR_VERSION >= 3
14413     object = PyUnicode_DecodeLatin1(group, strlen(group), NULL);
14414 #else
14415     object = PyString_FromString(group);
14416 #endif
14417     PyDict_SetItemString(vars, "mod_wsgi.application_group", object);
14418     Py_DECREF(object);
14419 
14420     object = PyLong_FromLong(self->config->script_reloading);
14421     PyDict_SetItemString(vars, "mod_wsgi.script_reloading", object);
14422     Py_DECREF(object);
14423 
14424     /*
14425      * Setup log object for WSGI errors. Don't decrement
14426      * reference to log object as keep reference to it.
14427      */
14428 
14429     object = (PyObject *)self->log;
14430     PyDict_SetItemString(vars, "wsgi.errors", object);
14431 
14432     /*
14433      * If Apache extensions are enabled add a CObject reference
14434      * to the Apache request_rec structure instance.
14435      */
14436 
14437     if (!wsgi_daemon_pool && self->config->pass_apache_request) {
14438 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || \
14439     (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 7)
14440         object = PyCapsule_New(self->r, 0, 0);
14441 #else
14442         object = PyCObject_FromVoidPtr(self->r, 0);
14443 #endif
14444         PyDict_SetItemString(vars, "apache.request_rec", object);
14445         Py_DECREF(object);
14446     }
14447 
14448     /*
14449      * Extensions for accessing SSL certificate information from
14450      * mod_ssl when in use.
14451      */
14452 
14453     object = PyObject_GetAttrString((PyObject *)self, "ssl_is_https");
14454     PyDict_SetItemString(vars, "mod_ssl.is_https", object);
14455     Py_DECREF(object);
14456 
14457     object = PyObject_GetAttrString((PyObject *)self, "ssl_var_lookup");
14458     PyDict_SetItemString(vars, "mod_ssl.var_lookup", object);
14459     Py_DECREF(object);
14460 
14461     return vars;
14462 }
14463 
Auth_ssl_is_https(AuthObject * self,PyObject * args)14464 static PyObject *Auth_ssl_is_https(AuthObject *self, PyObject *args)
14465 {
14466     APR_OPTIONAL_FN_TYPE(ssl_is_https) *ssl_is_https = 0;
14467 
14468     if (!self->r) {
14469         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
14470         return NULL;
14471     }
14472 
14473     if (!PyArg_ParseTuple(args, ":ssl_is_https"))
14474         return NULL;
14475 
14476     ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
14477 
14478     if (ssl_is_https == 0)
14479       return Py_BuildValue("i", 0);
14480 
14481     return Py_BuildValue("i", ssl_is_https(self->r->connection));
14482 }
14483 
Auth_ssl_var_lookup(AuthObject * self,PyObject * args)14484 static PyObject *Auth_ssl_var_lookup(AuthObject *self, PyObject *args)
14485 {
14486     APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *ssl_var_lookup = 0;
14487 
14488     PyObject *item = NULL;
14489     PyObject *latin_item = NULL;
14490 
14491     char *name = 0;
14492     char *value = 0;
14493 
14494     if (!self->r) {
14495         PyErr_SetString(PyExc_RuntimeError, "request object has expired");
14496         return NULL;
14497     }
14498 
14499     if (!PyArg_ParseTuple(args, "O:ssl_var_lookup", &item))
14500         return NULL;
14501 
14502 #if PY_MAJOR_VERSION >= 3
14503     if (PyUnicode_Check(item)) {
14504         latin_item = PyUnicode_AsLatin1String(item);
14505         if (!latin_item) {
14506             PyErr_Format(PyExc_TypeError, "byte string value expected, "
14507                          "value containing non 'latin-1' characters found");
14508 
14509             return NULL;
14510         }
14511 
14512         item = latin_item;
14513     }
14514 #endif
14515 
14516     if (!PyString_Check(item)) {
14517         PyErr_Format(PyExc_TypeError, "byte string value expected, value "
14518                      "of type %.200s found", item->ob_type->tp_name);
14519 
14520         Py_XDECREF(latin_item);
14521 
14522         return NULL;
14523     }
14524 
14525     name = PyString_AsString(item);
14526 
14527     ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
14528 
14529     if (ssl_var_lookup == 0)
14530     {
14531         Py_XDECREF(latin_item);
14532 
14533         Py_INCREF(Py_None);
14534 
14535         return Py_None;
14536     }
14537 
14538     value = ssl_var_lookup(self->r->pool, self->r->server,
14539                            self->r->connection, self->r, name);
14540 
14541     Py_XDECREF(latin_item);
14542 
14543     if (!value) {
14544         Py_INCREF(Py_None);
14545 
14546         return Py_None;
14547     }
14548 
14549 #if PY_MAJOR_VERSION >= 3
14550     return PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14551 #else
14552     return PyString_FromString(value);
14553 #endif
14554 }
14555 
14556 static PyMethodDef Auth_methods[] = {
14557     { "ssl_is_https",   (PyCFunction)Auth_ssl_is_https, METH_VARARGS, 0 },
14558     { "ssl_var_lookup", (PyCFunction)Auth_ssl_var_lookup, METH_VARARGS, 0 },
14559     { NULL, NULL}
14560 };
14561 
14562 static PyTypeObject Auth_Type = {
14563     PyVarObject_HEAD_INIT(NULL, 0)
14564     "mod_wsgi.Auth",        /*tp_name*/
14565     sizeof(AuthObject),     /*tp_basicsize*/
14566     0,                      /*tp_itemsize*/
14567     /* methods */
14568     (destructor)Auth_dealloc, /*tp_dealloc*/
14569     0,                      /*tp_print*/
14570     0,                      /*tp_getattr*/
14571     0,                      /*tp_setattr*/
14572     0,                      /*tp_compare*/
14573     0,                      /*tp_repr*/
14574     0,                      /*tp_as_number*/
14575     0,                      /*tp_as_sequence*/
14576     0,                      /*tp_as_mapping*/
14577     0,                      /*tp_hash*/
14578     0,                      /*tp_call*/
14579     0,                      /*tp_str*/
14580     0,                      /*tp_getattro*/
14581     0,                      /*tp_setattro*/
14582     0,                      /*tp_as_buffer*/
14583     Py_TPFLAGS_DEFAULT,     /*tp_flags*/
14584     0,                      /*tp_doc*/
14585     0,                      /*tp_traverse*/
14586     0,                      /*tp_clear*/
14587     0,                      /*tp_richcompare*/
14588     0,                      /*tp_weaklistoffset*/
14589     0,                      /*tp_iter*/
14590     0,                      /*tp_iternext*/
14591     Auth_methods,           /*tp_methods*/
14592     0,                      /*tp_members*/
14593     0,                      /*tp_getset*/
14594     0,                      /*tp_base*/
14595     0,                      /*tp_dict*/
14596     0,                      /*tp_descr_get*/
14597     0,                      /*tp_descr_set*/
14598     0,                      /*tp_dictoffset*/
14599     0,                      /*tp_init*/
14600     0,                      /*tp_alloc*/
14601     0,                      /*tp_new*/
14602     0,                      /*tp_free*/
14603     0,                      /*tp_is_gc*/
14604 };
14605 
14606 #if defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
wsgi_check_password(request_rec * r,const char * user,const char * password)14607 static authn_status wsgi_check_password(request_rec *r, const char *user,
14608                                         const char *password)
14609 {
14610     WSGIRequestConfig *config;
14611 
14612     InterpreterObject *interp = NULL;
14613     PyObject *modules = NULL;
14614     PyObject *module = NULL;
14615     char *name = NULL;
14616     int exists = 0;
14617 
14618     const char *script;
14619     const char *group;
14620 
14621     authn_status status;
14622 
14623     config = wsgi_create_req_config(r->pool, r);
14624 
14625     if (!config->auth_user_script) {
14626         ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
14627                      "mod_wsgi (pid=%d): Location of WSGI user "
14628                      "authentication script not provided.", getpid());
14629 
14630         return AUTH_GENERAL_ERROR;
14631     }
14632 
14633     /*
14634      * Acquire the desired python interpreter. Once this is done
14635      * it is safe to start manipulating python objects.
14636      */
14637 
14638     script = config->auth_user_script->handler_script;
14639     group = wsgi_server_group(r, config->auth_user_script->application_group);
14640 
14641     interp = wsgi_acquire_interpreter(group);
14642 
14643     if (!interp) {
14644         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
14645                       "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
14646                       getpid(), group);
14647 
14648         return AUTH_GENERAL_ERROR;
14649     }
14650 
14651     /* Calculate the Python module name to be used for script. */
14652 
14653     name = wsgi_module_name(r->pool, script);
14654 
14655     /*
14656      * Use a lock around the check to see if the module is
14657      * already loaded and the import of the module to prevent
14658      * two request handlers trying to import the module at the
14659      * same time.
14660      */
14661 
14662 #if APR_HAS_THREADS
14663     Py_BEGIN_ALLOW_THREADS
14664     apr_thread_mutex_lock(wsgi_module_lock);
14665     Py_END_ALLOW_THREADS
14666 #endif
14667 
14668     modules = PyImport_GetModuleDict();
14669     module = PyDict_GetItemString(modules, name);
14670 
14671     Py_XINCREF(module);
14672 
14673     if (module)
14674         exists = 1;
14675 
14676     /*
14677      * If script reloading is enabled and the module for it has
14678      * previously been loaded, see if it has been modified since
14679      * the last time it was accessed.
14680      */
14681 
14682     if (module && config->script_reloading) {
14683         if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
14684             /*
14685              * Script file has changed. Only support module
14686              * reloading for authentication scripts. Remove the
14687              * module from the modules dictionary before
14688              * reloading it again. If code is executing within
14689              * the module at the time, the callers reference
14690              * count on the module should ensure it isn't
14691              * actually destroyed until it is finished.
14692              */
14693 
14694             Py_DECREF(module);
14695             module = NULL;
14696 
14697             PyDict_DelItemString(modules, name);
14698         }
14699     }
14700 
14701     if (!module) {
14702         module = wsgi_load_source(r->pool, r, name, exists, script,
14703                                   "", group, 0);
14704     }
14705 
14706     /* Safe now to release the module lock. */
14707 
14708 #if APR_HAS_THREADS
14709     apr_thread_mutex_unlock(wsgi_module_lock);
14710 #endif
14711 
14712     /* Log any details of exceptions if import failed. */
14713 
14714     if (PyErr_Occurred())
14715         wsgi_log_python_error(r, NULL, script, 0);
14716 
14717     /* Assume an internal server error unless everything okay. */
14718 
14719     status = AUTH_GENERAL_ERROR;
14720 
14721     /* Determine if script exists and execute it. */
14722 
14723     if (module) {
14724         PyObject *module_dict = NULL;
14725         PyObject *object = NULL;
14726 
14727         module_dict = PyModule_GetDict(module);
14728         object = PyDict_GetItemString(module_dict, "check_password");
14729 
14730         if (object) {
14731             PyObject *vars = NULL;
14732             PyObject *args = NULL;
14733             PyObject *result = NULL;
14734             PyObject *method = NULL;
14735 
14736             AuthObject *adapter = NULL;
14737 
14738             adapter = newAuthObject(r, config);
14739 
14740             if (adapter) {
14741                 vars = Auth_environ(adapter, group);
14742 
14743                 Py_INCREF(object);
14744                 args = Py_BuildValue("(Oss)", vars, user, password);
14745                 result = PyEval_CallObject(object, args);
14746                 Py_DECREF(args);
14747                 Py_DECREF(object);
14748                 Py_DECREF(vars);
14749 
14750                 if (result) {
14751                     if (result == Py_None) {
14752                         status = AUTH_USER_NOT_FOUND;
14753                     }
14754                     else if (result == Py_True) {
14755                         status = AUTH_GRANTED;
14756                     }
14757                     else if (result == Py_False) {
14758                         status = AUTH_DENIED;
14759                     }
14760 #if PY_MAJOR_VERSION >= 3
14761                     else if (PyUnicode_Check(result)) {
14762                         PyObject *str = NULL;
14763 
14764                         str = PyUnicode_AsUTF8String(result);
14765 
14766                         if (str) {
14767                             adapter->r->user = apr_pstrdup(adapter->r->pool,
14768                                     PyString_AsString(str));
14769 
14770                             status = AUTH_GRANTED;
14771                         }
14772                     }
14773 #else
14774                     else if (PyString_Check(result)) {
14775                         adapter->r->user = apr_pstrdup(adapter->r->pool,
14776                                 PyString_AsString(result));
14777 
14778                         status = AUTH_GRANTED;
14779                     }
14780 #endif
14781                     else {
14782                         PyErr_SetString(PyExc_TypeError, "Basic auth "
14783                                         "provider must return True, False "
14784                                         "None or user name as string");
14785                     }
14786 
14787                     Py_DECREF(result);
14788                 }
14789 
14790                 /*
14791                  * Wipe out references to Apache request object
14792                  * held by Python objects, so can detect when an
14793                  * application holds on to the transient Python
14794                  * objects beyond the life of the request and
14795                  * thus raise an exception if they are used.
14796                  */
14797 
14798                 adapter->r = NULL;
14799 
14800                 /* Log any details of exceptions if execution failed. */
14801 
14802                 if (PyErr_Occurred())
14803                     wsgi_log_python_error(r, NULL, script, 0);
14804 
14805                 /* Close the log object so data is flushed. */
14806 
14807                 method = PyObject_GetAttrString(adapter->log, "close");
14808 
14809                 if (!method) {
14810                     PyErr_Format(PyExc_AttributeError,
14811                                  "'%s' object has no attribute 'close'",
14812                                  adapter->log->ob_type->tp_name);
14813                 }
14814                 else {
14815                     args = PyTuple_New(0);
14816                     result = PyEval_CallObject(method, args);
14817                     Py_XDECREF(result);
14818                     Py_DECREF(args);
14819                 }
14820 
14821                 /* Log any details of exceptions if execution failed. */
14822 
14823                 if (PyErr_Occurred())
14824                     wsgi_log_python_error(r, NULL, script, 0);
14825 
14826                 Py_XDECREF(method);
14827 
14828                 /* No longer need adapter object. */
14829 
14830                 Py_DECREF((PyObject *)adapter);
14831             }
14832         }
14833         else {
14834             Py_BEGIN_ALLOW_THREADS
14835             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
14836                           "mod_wsgi (pid=%d): Target WSGI user "
14837                           "authentication script '%s' does not provide "
14838                           "'Basic' auth provider.", getpid(), script);
14839             Py_END_ALLOW_THREADS
14840         }
14841     }
14842 
14843     /* Cleanup and release interpreter, */
14844 
14845     Py_XDECREF(module);
14846 
14847     wsgi_release_interpreter(interp);
14848 
14849     return status;
14850 }
14851 
wsgi_get_realm_hash(request_rec * r,const char * user,const char * realm,char ** rethash)14852 static authn_status wsgi_get_realm_hash(request_rec *r, const char *user,
14853                                         const char *realm, char **rethash)
14854 {
14855     WSGIRequestConfig *config;
14856 
14857     InterpreterObject *interp = NULL;
14858     PyObject *modules = NULL;
14859     PyObject *module = NULL;
14860     char *name = NULL;
14861     int exists = 0;
14862 
14863     const char *script;
14864     const char *group;
14865 
14866     authn_status status;
14867 
14868     config = wsgi_create_req_config(r->pool, r);
14869 
14870     if (!config->auth_user_script) {
14871         ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
14872                      "mod_wsgi (pid=%d): Location of WSGI user "
14873                      "authentication script not provided.", getpid());
14874 
14875         return AUTH_GENERAL_ERROR;
14876     }
14877 
14878     /*
14879      * Acquire the desired python interpreter. Once this is done
14880      * it is safe to start manipulating python objects.
14881      */
14882 
14883     script = config->auth_user_script->handler_script;
14884     group = wsgi_server_group(r, config->auth_user_script->application_group);
14885 
14886     interp = wsgi_acquire_interpreter(group);
14887 
14888     if (!interp) {
14889         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
14890                       "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
14891                       getpid(), group);
14892 
14893         return AUTH_GENERAL_ERROR;
14894     }
14895 
14896     /* Calculate the Python module name to be used for script. */
14897 
14898     name = wsgi_module_name(r->pool, script);
14899 
14900     /*
14901      * Use a lock around the check to see if the module is
14902      * already loaded and the import of the module to prevent
14903      * two request handlers trying to import the module at the
14904      * same time.
14905      */
14906 
14907 #if APR_HAS_THREADS
14908     Py_BEGIN_ALLOW_THREADS
14909     apr_thread_mutex_lock(wsgi_module_lock);
14910     Py_END_ALLOW_THREADS
14911 #endif
14912 
14913     modules = PyImport_GetModuleDict();
14914     module = PyDict_GetItemString(modules, name);
14915 
14916     Py_XINCREF(module);
14917 
14918     if (module)
14919         exists = 1;
14920 
14921     /*
14922      * If script reloading is enabled and the module for it has
14923      * previously been loaded, see if it has been modified since
14924      * the last time it was accessed.
14925      */
14926 
14927     if (module && config->script_reloading) {
14928         if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
14929             /*
14930              * Script file has changed. Only support module
14931              * reloading for authentication scripts. Remove the
14932              * module from the modules dictionary before
14933              * reloading it again. If code is executing within
14934              * the module at the time, the callers reference
14935              * count on the module should ensure it isn't
14936              * actually destroyed until it is finished.
14937              */
14938 
14939             Py_DECREF(module);
14940             module = NULL;
14941 
14942             PyDict_DelItemString(modules, name);
14943         }
14944     }
14945 
14946     if (!module) {
14947         module = wsgi_load_source(r->pool, r, name, exists, script,
14948                                   "", group, 0);
14949     }
14950 
14951     /* Safe now to release the module lock. */
14952 
14953 #if APR_HAS_THREADS
14954     apr_thread_mutex_unlock(wsgi_module_lock);
14955 #endif
14956 
14957     /* Log any details of exceptions if import failed. */
14958 
14959     if (PyErr_Occurred())
14960         wsgi_log_python_error(r, NULL, script, 0);
14961 
14962     /* Assume an internal server error unless everything okay. */
14963 
14964     status = AUTH_GENERAL_ERROR;
14965 
14966     /* Determine if script exists and execute it. */
14967 
14968     if (module) {
14969         PyObject *module_dict = NULL;
14970         PyObject *object = NULL;
14971 
14972         module_dict = PyModule_GetDict(module);
14973         object = PyDict_GetItemString(module_dict, "get_realm_hash");
14974 
14975         if (object) {
14976             PyObject *vars = NULL;
14977             PyObject *args = NULL;
14978             PyObject *result = NULL;
14979             PyObject *method = NULL;
14980 
14981             AuthObject *adapter = NULL;
14982 
14983             adapter = newAuthObject(r, config);
14984 
14985             if (adapter) {
14986                 vars = Auth_environ(adapter, group);
14987 
14988                 Py_INCREF(object);
14989                 args = Py_BuildValue("(Oss)", vars, user, realm);
14990                 result = PyEval_CallObject(object, args);
14991                 Py_DECREF(args);
14992                 Py_DECREF(object);
14993                 Py_DECREF(vars);
14994 
14995                 if (result) {
14996                     if (result == Py_None) {
14997                         status = AUTH_USER_NOT_FOUND;
14998                     }
14999                     else if (PyString_Check(result)) {
15000                         *rethash = PyString_AsString(result);
15001                         *rethash = apr_pstrdup(r->pool, *rethash);
15002 
15003                         status = AUTH_USER_FOUND;
15004                     }
15005 #if PY_MAJOR_VERSION >= 3
15006                     else if (PyUnicode_Check(result)) {
15007                         PyObject *latin_item;
15008                         latin_item = PyUnicode_AsLatin1String(result);
15009                         if (!latin_item) {
15010                             PyErr_SetString(PyExc_TypeError, "Digest auth "
15011                                             "provider must return None "
15012                                             "or string object, value "
15013                                             "containing non 'latin-1' "
15014                                             "characters found");
15015                         }
15016                         else {
15017                             Py_DECREF(result);
15018                             result = latin_item;
15019 
15020                             *rethash = PyString_AsString(result);
15021                             *rethash = apr_pstrdup(r->pool, *rethash);
15022 
15023                             status = AUTH_USER_FOUND;
15024                         }
15025                     }
15026 #endif
15027                     else {
15028                         PyErr_SetString(PyExc_TypeError, "Digest auth "
15029                                         "provider must return None "
15030                                         "or string object");
15031                     }
15032 
15033                     Py_DECREF(result);
15034                 }
15035 
15036                 /*
15037                  * Wipe out references to Apache request object
15038                  * held by Python objects, so can detect when an
15039                  * application holds on to the transient Python
15040                  * objects beyond the life of the request and
15041                  * thus raise an exception if they are used.
15042                  */
15043 
15044                 adapter->r = NULL;
15045 
15046                 /* Log any details of exceptions if execution failed. */
15047 
15048                 if (PyErr_Occurred())
15049                     wsgi_log_python_error(r, NULL, script, 0);
15050 
15051                 /* Close the log object so data is flushed. */
15052 
15053                 method = PyObject_GetAttrString(adapter->log, "close");
15054 
15055                 if (!method) {
15056                     PyErr_Format(PyExc_AttributeError,
15057                                  "'%s' object has no attribute 'close'",
15058                                  adapter->log->ob_type->tp_name);
15059                 }
15060                 else {
15061                     args = PyTuple_New(0);
15062                     result = PyEval_CallObject(method, args);
15063                     Py_XDECREF(result);
15064                     Py_DECREF(args);
15065                 }
15066 
15067                 /* Log any details of exceptions if execution failed. */
15068 
15069                 if (PyErr_Occurred())
15070                     wsgi_log_python_error(r, NULL, script, 0);
15071 
15072                 Py_XDECREF(method);
15073 
15074                 /* No longer need adapter object. */
15075 
15076                 Py_DECREF((PyObject *)adapter);
15077             }
15078         }
15079         else {
15080             Py_BEGIN_ALLOW_THREADS
15081             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15082                           "mod_wsgi (pid=%d): Target WSGI user "
15083                           "authentication script '%s' does not provide "
15084                           "'Digest' auth provider.", getpid(), script);
15085             Py_END_ALLOW_THREADS
15086         }
15087     }
15088 
15089     /* Cleanup and release interpreter, */
15090 
15091     Py_XDECREF(module);
15092 
15093     wsgi_release_interpreter(interp);
15094 
15095     return status;
15096 }
15097 
15098 static const authn_provider wsgi_authn_provider =
15099 {
15100     &wsgi_check_password,
15101     &wsgi_get_realm_hash
15102 };
15103 #endif
15104 
wsgi_groups_for_user(request_rec * r,WSGIRequestConfig * config,apr_table_t ** grpstatus)15105 static int wsgi_groups_for_user(request_rec *r, WSGIRequestConfig *config,
15106                                 apr_table_t **grpstatus)
15107 {
15108     apr_table_t *grps = apr_table_make(r->pool, 15);
15109 
15110     InterpreterObject *interp = NULL;
15111     PyObject *modules = NULL;
15112     PyObject *module = NULL;
15113     char *name = NULL;
15114     int exists = 0;
15115 
15116     const char *script;
15117     const char *group;
15118 
15119     int status = HTTP_INTERNAL_SERVER_ERROR;
15120 
15121     if (!config->auth_group_script) {
15122         ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
15123                      "mod_wsgi (pid=%d): Location of WSGI group "
15124                      "authentication script not provided.", getpid());
15125 
15126         return HTTP_INTERNAL_SERVER_ERROR;
15127     }
15128 
15129     /*
15130      * Acquire the desired python interpreter. Once this is done
15131      * it is safe to start manipulating python objects.
15132      */
15133 
15134     script = config->auth_group_script->handler_script;
15135     group = wsgi_server_group(r, config->auth_group_script->application_group);
15136 
15137     interp = wsgi_acquire_interpreter(group);
15138 
15139     if (!interp) {
15140         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
15141                       "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
15142                       getpid(), group);
15143 
15144         return HTTP_INTERNAL_SERVER_ERROR;
15145     }
15146 
15147     /* Calculate the Python module name to be used for script. */
15148 
15149     name = wsgi_module_name(r->pool, script);
15150 
15151     /*
15152      * Use a lock around the check to see if the module is
15153      * already loaded and the import of the module to prevent
15154      * two request handlers trying to import the module at the
15155      * same time.
15156      */
15157 
15158 #if APR_HAS_THREADS
15159     Py_BEGIN_ALLOW_THREADS
15160     apr_thread_mutex_lock(wsgi_module_lock);
15161     Py_END_ALLOW_THREADS
15162 #endif
15163 
15164     modules = PyImport_GetModuleDict();
15165     module = PyDict_GetItemString(modules, name);
15166 
15167     Py_XINCREF(module);
15168 
15169     if (module)
15170         exists = 1;
15171 
15172     /*
15173      * If script reloading is enabled and the module for it has
15174      * previously been loaded, see if it has been modified since
15175      * the last time it was accessed.
15176      */
15177 
15178     if (module && config->script_reloading) {
15179         if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
15180             /*
15181              * Script file has changed. Only support module
15182              * reloading for authentication scripts. Remove the
15183              * module from the modules dictionary before
15184              * reloading it again. If code is executing within
15185              * the module at the time, the callers reference
15186              * count on the module should ensure it isn't
15187              * actually destroyed until it is finished.
15188              */
15189 
15190             Py_DECREF(module);
15191             module = NULL;
15192 
15193             PyDict_DelItemString(modules, name);
15194         }
15195     }
15196 
15197     if (!module) {
15198         module = wsgi_load_source(r->pool, r, name, exists, script,
15199                                   "", group, 0);
15200     }
15201 
15202     /* Safe now to release the module lock. */
15203 
15204 #if APR_HAS_THREADS
15205     apr_thread_mutex_unlock(wsgi_module_lock);
15206 #endif
15207 
15208     /* Log any details of exceptions if import failed. */
15209 
15210     if (PyErr_Occurred())
15211         wsgi_log_python_error(r, NULL, script, 0);
15212 
15213     /* Assume an internal server error unless everything okay. */
15214 
15215     status = HTTP_INTERNAL_SERVER_ERROR;
15216 
15217     /* Determine if script exists and execute it. */
15218 
15219     if (module) {
15220         PyObject *module_dict = NULL;
15221         PyObject *object = NULL;
15222 
15223         module_dict = PyModule_GetDict(module);
15224         object = PyDict_GetItemString(module_dict, "groups_for_user");
15225 
15226         if (object) {
15227             PyObject *vars = NULL;
15228             PyObject *args = NULL;
15229             PyObject *result = NULL;
15230             PyObject *method = NULL;
15231 
15232             AuthObject *adapter = NULL;
15233 
15234             adapter = newAuthObject(r, config);
15235 
15236             if (adapter) {
15237                 vars = Auth_environ(adapter, group);
15238 
15239                 Py_INCREF(object);
15240                 args = Py_BuildValue("(Os)", vars, r->user);
15241                 result = PyEval_CallObject(object, args);
15242                 Py_DECREF(args);
15243                 Py_DECREF(object);
15244                 Py_DECREF(vars);
15245 
15246                 if (result) {
15247                     PyObject *iterator;
15248 
15249                     iterator = PyObject_GetIter(result);
15250 
15251                     if (iterator) {
15252                         PyObject *item;
15253                         const char *name;
15254 
15255                         status = OK;
15256 
15257                         while ((item = PyIter_Next(iterator))) {
15258 #if PY_MAJOR_VERSION >= 3
15259                             if (PyUnicode_Check(item)) {
15260                                 PyObject *latin_item;
15261                                 latin_item = PyUnicode_AsLatin1String(item);
15262                                 if (!latin_item) {
15263                                     Py_BEGIN_ALLOW_THREADS
15264                                     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0,
15265                                                   r, "mod_wsgi (pid=%d): "
15266                                                   "Groups for user returned "
15267                                                   "from '%s' must be an "
15268                                                   "iterable sequence of "
15269                                                   "byte strings, value "
15270                                                   "containing non 'latin-1' "
15271                                                   "characters found",
15272                                                   getpid(), script);
15273                                     Py_END_ALLOW_THREADS
15274 
15275                                     Py_DECREF(item);
15276 
15277                                     status = HTTP_INTERNAL_SERVER_ERROR;
15278 
15279                                     break;
15280                                 }
15281                                 else {
15282                                     Py_DECREF(item);
15283                                     item = latin_item;
15284                                 }
15285                             }
15286 #endif
15287 
15288                             if (!PyString_Check(item)) {
15289                                 Py_BEGIN_ALLOW_THREADS
15290                                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15291                                               "mod_wsgi (pid=%d): Groups for "
15292                                               "user returned from '%s' must "
15293                                               "be an iterable sequence of "
15294                                               "byte strings.", getpid(),
15295                                               script);
15296                                 Py_END_ALLOW_THREADS
15297 
15298                                 Py_DECREF(item);
15299 
15300                                 status = HTTP_INTERNAL_SERVER_ERROR;
15301 
15302                                 break;
15303                             }
15304 
15305                             name = PyString_AsString(item);
15306 
15307                             apr_table_setn(grps, apr_pstrdup(r->pool, name),
15308                                            "1");
15309 
15310                             Py_DECREF(item);
15311                         }
15312 
15313                         Py_DECREF(iterator);
15314                     }
15315                     else {
15316                         Py_BEGIN_ALLOW_THREADS
15317                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15318                                       "mod_wsgi (pid=%d): Groups for user "
15319                                       "returned from '%s' must be an iterable "
15320                                       "sequence of byte strings.", getpid(),
15321                                       script);
15322                         Py_END_ALLOW_THREADS
15323                     }
15324 
15325                     Py_DECREF(result);
15326                 }
15327 
15328                 /*
15329                  * Wipe out references to Apache request object
15330                  * held by Python objects, so can detect when an
15331                  * application holds on to the transient Python
15332                  * objects beyond the life of the request and
15333                  * thus raise an exception if they are used.
15334                  */
15335 
15336                 adapter->r = NULL;
15337 
15338                 /* Log any details of exceptions if execution failed. */
15339 
15340                 if (PyErr_Occurred())
15341                     wsgi_log_python_error(r, NULL, script, 0);
15342 
15343                 /* Close the log object so data is flushed. */
15344 
15345                 method = PyObject_GetAttrString(adapter->log, "close");
15346 
15347                 if (!method) {
15348                     PyErr_Format(PyExc_AttributeError,
15349                                  "'%s' object has no attribute 'close'",
15350                                  adapter->log->ob_type->tp_name);
15351                 }
15352                 else {
15353                     args = PyTuple_New(0);
15354                     result = PyEval_CallObject(method, args);
15355                     Py_XDECREF(result);
15356                     Py_DECREF(args);
15357                 }
15358 
15359                 /* Log any details of exceptions if execution failed. */
15360 
15361                 if (PyErr_Occurred())
15362                     wsgi_log_python_error(r, NULL, script, 0);
15363 
15364                 Py_XDECREF(method);
15365 
15366                 /* No longer need adapter object. */
15367 
15368                 Py_DECREF((PyObject *)adapter);
15369             }
15370         }
15371         else {
15372             Py_BEGIN_ALLOW_THREADS
15373             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15374                           "mod_wsgi (pid=%d): Target WSGI group "
15375                           "authentication script '%s' does not provide "
15376                           "group provider.", getpid(), script);
15377             Py_END_ALLOW_THREADS
15378         }
15379     }
15380 
15381     /* Cleanup and release interpreter, */
15382 
15383     Py_XDECREF(module);
15384 
15385     wsgi_release_interpreter(interp);
15386 
15387     if (status == OK)
15388         *grpstatus = grps;
15389 
15390     return status;
15391 }
15392 
wsgi_allow_access(request_rec * r,WSGIRequestConfig * config,const char * host)15393 static int wsgi_allow_access(request_rec *r, WSGIRequestConfig *config,
15394                              const char *host)
15395 {
15396     InterpreterObject *interp = NULL;
15397     PyObject *modules = NULL;
15398     PyObject *module = NULL;
15399     char *name = NULL;
15400     int exists = 0;
15401 
15402     const char *script;
15403     const char *group;
15404 
15405     int allow = 0;
15406 
15407     if (!config->access_script) {
15408         ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
15409                      "mod_wsgi (pid=%d): Location of WSGI host "
15410                      "access script not provided.", getpid());
15411 
15412         return 0;
15413     }
15414 
15415     /*
15416      * Acquire the desired python interpreter. Once this is done
15417      * it is safe to start manipulating python objects.
15418      */
15419 
15420     script = config->access_script->handler_script;
15421     group = wsgi_server_group(r, config->access_script->application_group);
15422 
15423     interp = wsgi_acquire_interpreter(group);
15424 
15425     if (!interp) {
15426         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
15427                       "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
15428                       getpid(), group);
15429 
15430         return 0;
15431     }
15432 
15433     /* Calculate the Python module name to be used for script. */
15434 
15435     name = wsgi_module_name(r->pool, script);
15436 
15437     /*
15438      * Use a lock around the check to see if the module is
15439      * already loaded and the import of the module to prevent
15440      * two request handlers trying to import the module at the
15441      * same time.
15442      */
15443 
15444 #if APR_HAS_THREADS
15445     Py_BEGIN_ALLOW_THREADS
15446     apr_thread_mutex_lock(wsgi_module_lock);
15447     Py_END_ALLOW_THREADS
15448 #endif
15449 
15450     modules = PyImport_GetModuleDict();
15451     module = PyDict_GetItemString(modules, name);
15452 
15453     Py_XINCREF(module);
15454 
15455     if (module)
15456         exists = 1;
15457 
15458     /*
15459      * If script reloading is enabled and the module for it has
15460      * previously been loaded, see if it has been modified since
15461      * the last time it was accessed.
15462      */
15463 
15464     if (module && config->script_reloading) {
15465         if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
15466             /*
15467              * Script file has changed. Only support module
15468              * reloading for authentication scripts. Remove the
15469              * module from the modules dictionary before
15470              * reloading it again. If code is executing within
15471              * the module at the time, the callers reference
15472              * count on the module should ensure it isn't
15473              * actually destroyed until it is finished.
15474              */
15475 
15476             Py_DECREF(module);
15477             module = NULL;
15478 
15479             PyDict_DelItemString(modules, name);
15480         }
15481     }
15482 
15483     if (!module) {
15484         module = wsgi_load_source(r->pool, r, name, exists, script,
15485                                   "", group, 0);
15486     }
15487 
15488     /* Safe now to release the module lock. */
15489 
15490 #if APR_HAS_THREADS
15491     apr_thread_mutex_unlock(wsgi_module_lock);
15492 #endif
15493 
15494     /* Log any details of exceptions if import failed. */
15495 
15496     if (PyErr_Occurred())
15497         wsgi_log_python_error(r, NULL, script, 0);
15498 
15499     /* Assume not allowed unless everything okay. */
15500 
15501     allow = 0;
15502 
15503     /* Determine if script exists and execute it. */
15504 
15505     if (module) {
15506         PyObject *module_dict = NULL;
15507         PyObject *object = NULL;
15508 
15509         module_dict = PyModule_GetDict(module);
15510         object = PyDict_GetItemString(module_dict, "allow_access");
15511 
15512         if (object) {
15513             PyObject *vars = NULL;
15514             PyObject *args = NULL;
15515             PyObject *result = NULL;
15516             PyObject *method = NULL;
15517 
15518             AuthObject *adapter = NULL;
15519 
15520             adapter = newAuthObject(r, config);
15521 
15522             if (adapter) {
15523                 vars = Auth_environ(adapter, group);
15524 
15525                 Py_INCREF(object);
15526                 args = Py_BuildValue("(Oz)", vars, host);
15527                 result = PyEval_CallObject(object, args);
15528                 Py_DECREF(args);
15529                 Py_DECREF(object);
15530                 Py_DECREF(vars);
15531 
15532                 if (result) {
15533                     if (result == Py_None) {
15534                         allow = -1;
15535                     }
15536                     else if (PyBool_Check(result)) {
15537                         if (result == Py_True)
15538                             allow = 1;
15539                     }
15540                     else {
15541                         Py_BEGIN_ALLOW_THREADS
15542                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15543                                       "mod_wsgi (pid=%d): Indicator of "
15544                                       "host accessibility returned from '%s' "
15545                                       "must a boolean or None.", getpid(),
15546                                       script);
15547                         Py_END_ALLOW_THREADS
15548                     }
15549 
15550                     Py_DECREF(result);
15551                 }
15552 
15553                 /*
15554                  * Wipe out references to Apache request object
15555                  * held by Python objects, so can detect when an
15556                  * application holds on to the transient Python
15557                  * objects beyond the life of the request and
15558                  * thus raise an exception if they are used.
15559                  */
15560 
15561                 adapter->r = NULL;
15562 
15563                 /* Log any details of exceptions if execution failed. */
15564 
15565                 if (PyErr_Occurred())
15566                     wsgi_log_python_error(r, NULL, script, 0);
15567 
15568                 /* Close the log object so data is flushed. */
15569 
15570                 method = PyObject_GetAttrString(adapter->log, "close");
15571 
15572                 if (!method) {
15573                     PyErr_Format(PyExc_AttributeError,
15574                                  "'%s' object has no attribute 'close'",
15575                                  adapter->log->ob_type->tp_name);
15576                 }
15577                 else {
15578                     args = PyTuple_New(0);
15579                     result = PyEval_CallObject(method, args);
15580                     Py_XDECREF(result);
15581                     Py_DECREF(args);
15582                 }
15583 
15584                 /* Log any details of exceptions if execution failed. */
15585 
15586                 if (PyErr_Occurred())
15587                     wsgi_log_python_error(r, NULL, script, 0);
15588 
15589                 Py_XDECREF(method);
15590 
15591                 /* No longer need adapter object. */
15592 
15593                 Py_DECREF((PyObject *)adapter);
15594             }
15595         }
15596         else {
15597             Py_BEGIN_ALLOW_THREADS
15598             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15599                           "mod_wsgi (pid=%d): Target WSGI host "
15600                           "access script '%s' does not provide "
15601                           "host validator.", getpid(), script);
15602             Py_END_ALLOW_THREADS
15603         }
15604     }
15605 
15606     /* Cleanup and release interpreter, */
15607 
15608     Py_XDECREF(module);
15609 
15610     wsgi_release_interpreter(interp);
15611 
15612     return allow;
15613 }
15614 
wsgi_hook_access_checker(request_rec * r)15615 static int wsgi_hook_access_checker(request_rec *r)
15616 {
15617     WSGIRequestConfig *config;
15618 
15619     int allow = 0;
15620     const char *host = NULL;
15621 
15622     config = wsgi_create_req_config(r->pool, r);
15623 
15624     if (!config->access_script)
15625         return DECLINED;
15626 
15627     host = ap_get_remote_host(r->connection, r->per_dir_config,
15628                               REMOTE_HOST, NULL);
15629 
15630 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
15631     if (!host)
15632         host = r->useragent_ip;
15633 #else
15634     if (!host)
15635         host = r->connection->remote_ip;
15636 #endif
15637 
15638     allow = wsgi_allow_access(r, config, host);
15639 
15640     if (allow < 0)
15641         return DECLINED;
15642     else if (allow)
15643         return OK;
15644 
15645     if (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r)) {
15646         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_wsgi (pid=%d): "
15647                       "Client denied by server configuration: '%s'.",
15648                       getpid(), r->filename);
15649     }
15650 
15651     return HTTP_FORBIDDEN;
15652 }
15653 
15654 #if !defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
wsgi_hook_check_user_id(request_rec * r)15655 static int wsgi_hook_check_user_id(request_rec *r)
15656 {
15657     WSGIRequestConfig *config;
15658 
15659     int status = -1;
15660 
15661     const char *password;
15662 
15663     InterpreterObject *interp = NULL;
15664     PyObject *modules = NULL;
15665     PyObject *module = NULL;
15666     char *name = NULL;
15667     int exists = 0;
15668 
15669     const char *script;
15670     const char *group;
15671 
15672     if ((status = ap_get_basic_auth_pw(r, &password)))
15673         return status;
15674 
15675     config = wsgi_create_req_config(r->pool, r);
15676 
15677     if (!config->auth_user_script)
15678         return DECLINED;
15679 
15680     /*
15681      * Acquire the desired python interpreter. Once this is done
15682      * it is safe to start manipulating python objects.
15683      */
15684 
15685     script = config->auth_user_script->handler_script;
15686     group = wsgi_server_group(r, config->auth_user_script->application_group);
15687 
15688     interp = wsgi_acquire_interpreter(group);
15689 
15690     if (!interp) {
15691         ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
15692                       "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
15693                       getpid(), group);
15694 
15695         return HTTP_INTERNAL_SERVER_ERROR;
15696     }
15697 
15698     /* Calculate the Python module name to be used for script. */
15699 
15700     name = wsgi_module_name(r->pool, script);
15701 
15702     /*
15703      * Use a lock around the check to see if the module is
15704      * already loaded and the import of the module to prevent
15705      * two request handlers trying to import the module at the
15706      * same time.
15707      */
15708 
15709 #if APR_HAS_THREADS
15710     Py_BEGIN_ALLOW_THREADS
15711     apr_thread_mutex_lock(wsgi_module_lock);
15712     Py_END_ALLOW_THREADS
15713 #endif
15714 
15715     modules = PyImport_GetModuleDict();
15716     module = PyDict_GetItemString(modules, name);
15717 
15718     Py_XINCREF(module);
15719 
15720     if (module)
15721         exists = 1;
15722 
15723     /*
15724      * If script reloading is enabled and the module for it has
15725      * previously been loaded, see if it has been modified since
15726      * the last time it was accessed.
15727      */
15728 
15729     if (module && config->script_reloading) {
15730         if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
15731             /*
15732              * Script file has changed. Only support module
15733              * reloading for authentication scripts. Remove the
15734              * module from the modules dictionary before
15735              * reloading it again. If code is executing within
15736              * the module at the time, the callers reference
15737              * count on the module should ensure it isn't
15738              * actually destroyed until it is finished.
15739              */
15740 
15741             Py_DECREF(module);
15742             module = NULL;
15743 
15744             PyDict_DelItemString(modules, name);
15745         }
15746     }
15747 
15748     if (!module) {
15749         module = wsgi_load_source(r->pool, r, name, exists, script,
15750                                   "", group, 0);
15751     }
15752 
15753     /* Safe now to release the module lock. */
15754 
15755 #if APR_HAS_THREADS
15756     apr_thread_mutex_unlock(wsgi_module_lock);
15757 #endif
15758 
15759     /* Log any details of exceptions if import failed. */
15760 
15761     if (PyErr_Occurred())
15762         wsgi_log_python_error(r, NULL, script, 0);
15763 
15764     /* Assume an internal server error unless everything okay. */
15765 
15766     status = HTTP_INTERNAL_SERVER_ERROR;
15767 
15768     /* Determine if script exists and execute it. */
15769 
15770     if (module) {
15771         PyObject *module_dict = NULL;
15772         PyObject *object = NULL;
15773 
15774         module_dict = PyModule_GetDict(module);
15775         object = PyDict_GetItemString(module_dict, "check_password");
15776 
15777         if (object) {
15778             PyObject *vars = NULL;
15779             PyObject *args = NULL;
15780             PyObject *result = NULL;
15781             PyObject *method = NULL;
15782 
15783             AuthObject *adapter = NULL;
15784 
15785             adapter = newAuthObject(r, config);
15786 
15787             if (adapter) {
15788                 vars = Auth_environ(adapter, group);
15789 
15790                 Py_INCREF(object);
15791                 args = Py_BuildValue("(Oss)", vars, r->user, password);
15792                 result = PyEval_CallObject(object, args);
15793                 Py_DECREF(args);
15794                 Py_DECREF(object);
15795                 Py_DECREF(vars);
15796 
15797                 if (result) {
15798                     if (result == Py_None) {
15799                         if (config->user_authoritative) {
15800                             ap_note_basic_auth_failure(r);
15801                             status = HTTP_UNAUTHORIZED;
15802 
15803                             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15804                                           "mod_wsgi (pid=%d): User '%s' not "
15805                                           "found in executing authentication "
15806                                           "script '%s', for uri '%s'.",
15807                                           getpid(), r->user, script, r->uri);
15808                         }
15809                         else
15810                             status = DECLINED;
15811                     }
15812                     else if (result == Py_True) {
15813                         status = OK;
15814                     }
15815                     else if (result == Py_False) {
15816                         ap_note_basic_auth_failure(r);
15817                         status = HTTP_UNAUTHORIZED;
15818 
15819                         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15820                                       "mod_wsgi (pid=%d): Password mismatch "
15821                                       "for user '%s' in executing "
15822                                       "authentication script '%s', for uri "
15823                                       "'%s'.", getpid(), r->user, script,
15824                                       r->uri);
15825                     }
15826                     else {
15827                         PyErr_SetString(PyExc_TypeError, "Basic auth "
15828                                         "provider must return True, False "
15829                                         "or None");
15830                     }
15831 
15832                     Py_DECREF(result);
15833                 }
15834 
15835                 /*
15836                  * Wipe out references to Apache request object
15837                  * held by Python objects, so can detect when an
15838                  * application holds on to the transient Python
15839                  * objects beyond the life of the request and
15840                  * thus raise an exception if they are used.
15841                  */
15842 
15843                 adapter->r = NULL;
15844 
15845                 /* Log any details of exceptions if execution failed. */
15846 
15847                 if (PyErr_Occurred())
15848                     wsgi_log_python_error(r, NULL, script, 0);
15849 
15850                 /* Close the log object so data is flushed. */
15851 
15852                 method = PyObject_GetAttrString(adapter->log, "close");
15853 
15854                 if (!method) {
15855                     PyErr_Format(PyExc_AttributeError,
15856                                  "'%s' object has no attribute 'close'",
15857                                  adapter->log->ob_type->tp_name);
15858                 }
15859                 else {
15860                     args = PyTuple_New(0);
15861                     result = PyEval_CallObject(method, args);
15862                     Py_XDECREF(result);
15863                     Py_DECREF(args);
15864                 }
15865 
15866                 /* Log any details of exceptions if execution failed. */
15867 
15868                 if (PyErr_Occurred())
15869                     wsgi_log_python_error(r, NULL, script, 0);
15870 
15871                 Py_XDECREF(method);
15872 
15873                 /* No longer need adapter object. */
15874 
15875                 Py_DECREF((PyObject *)adapter);
15876             }
15877         }
15878         else {
15879             Py_BEGIN_ALLOW_THREADS
15880             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15881                           "mod_wsgi (pid=%d): Target WSGI user "
15882                           "authentication script '%s' does not provide "
15883                           "'Basic' auth provider.", getpid(), script);
15884             Py_END_ALLOW_THREADS
15885         }
15886     }
15887 
15888     /* Cleanup and release interpreter, */
15889 
15890     Py_XDECREF(module);
15891 
15892     wsgi_release_interpreter(interp);
15893 
15894     return status;
15895 }
15896 #endif
15897 
15898 #if defined(MOD_WSGI_WITH_AUTHZ_PROVIDER)
15899 
15900 #if MOD_WSGI_WITH_AUTHZ_PROVIDER_PARSED
wsgi_check_authorization(request_rec * r,const char * require_args,const void * parsed_require_line)15901 static authz_status wsgi_check_authorization(request_rec *r,
15902                                              const char *require_args,
15903                                              const void *parsed_require_line)
15904 #else
15905 static authz_status wsgi_check_authorization(request_rec *r,
15906                                              const char *require_args)
15907 #endif
15908 {
15909     WSGIRequestConfig *config;
15910 
15911     apr_table_t *grpstatus = NULL;
15912     const char *t, *w;
15913     int status;
15914 
15915 #if AP_MODULE_MAGIC_AT_LEAST(20100714,0)
15916     if (!r->user)
15917         return AUTHZ_DENIED_NO_USER;
15918 #endif
15919 
15920     config = wsgi_create_req_config(r->pool, r);
15921 
15922     if (!config->auth_group_script) {
15923         ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
15924                      "mod_wsgi (pid=%d): Location of WSGI group "
15925                      "authorization script not provided.", getpid());
15926 
15927         return AUTHZ_DENIED;
15928     }
15929 
15930     status = wsgi_groups_for_user(r, config, &grpstatus);
15931 
15932     if (status != OK)
15933         return AUTHZ_DENIED;
15934 
15935     if (apr_table_elts(grpstatus)->nelts == 0) {
15936         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_wsgi (pid=%d): "
15937                       "Authorization of user '%s' to access '%s' failed. "
15938                       "User is not a member of any groups.", getpid(),
15939                       r->user, r->uri);
15940         return AUTHZ_DENIED;
15941     }
15942 
15943     t = require_args;
15944     while ((w = ap_getword_conf(r->pool, &t)) && w[0]) {
15945         if (apr_table_get(grpstatus, w)) {
15946             return AUTHZ_GRANTED;
15947         }
15948     }
15949 
15950     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_wsgi (pid=%d): "
15951                   "Authorization of user '%s' to access '%s' failed. "
15952                   "User is not a member of designated groups.", getpid(),
15953                   r->user, r->uri);
15954 
15955     return AUTHZ_DENIED;
15956 }
15957 
15958 static const authz_provider wsgi_authz_provider =
15959 {
15960     &wsgi_check_authorization,
15961 #if MOD_WSGI_WITH_AUTHZ_PROVIDER_PARSED
15962     NULL,
15963 #endif
15964 };
15965 
15966 #else
15967 
wsgi_hook_auth_checker(request_rec * r)15968 static int wsgi_hook_auth_checker(request_rec *r)
15969 {
15970     WSGIRequestConfig *config;
15971 
15972     int m = r->method_number;
15973     const apr_array_header_t *reqs_arr;
15974     require_line *reqs;
15975     int required_group = 0;
15976     register int x;
15977     const char *t, *w;
15978     apr_table_t *grpstatus = NULL;
15979     char *reason = NULL;
15980 
15981     config = wsgi_create_req_config(r->pool, r);
15982 
15983     if (!config->auth_group_script)
15984         return DECLINED;
15985 
15986     reqs_arr = ap_requires(r);
15987 
15988     if (!reqs_arr)
15989         return DECLINED;
15990 
15991     reqs = (require_line *)reqs_arr->elts;
15992 
15993     for (x = 0; x < reqs_arr->nelts; x++) {
15994 
15995         if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) {
15996             continue;
15997         }
15998 
15999         t = reqs[x].requirement;
16000         w = ap_getword_white(r->pool, &t);
16001 
16002 #if AP_MODULE_MAGIC_AT_LEAST(20100714,0)
16003         if (!strcasecmp(w, "wsgi-group")) {
16004 #else
16005         if (!strcasecmp(w, "group") || !strcasecmp(w, "wsgi-group")) {
16006 #endif
16007             required_group = 1;
16008 
16009             if (!grpstatus) {
16010                 int status;
16011 
16012                 status = wsgi_groups_for_user(r, config, &grpstatus);
16013 
16014                 if (status != OK)
16015                     return status;
16016 
16017                 if (apr_table_elts(grpstatus)->nelts == 0) {
16018                     reason = "User is not a member of any groups";
16019                     break;
16020                 }
16021             }
16022 
16023             while (t[0]) {
16024                 w = ap_getword_conf(r->pool, &t);
16025                 if (apr_table_get(grpstatus, w)) {
16026                     return OK;
16027                 }
16028             }
16029         }
16030     }
16031 
16032     if (!required_group || !config->group_authoritative)
16033         return DECLINED;
16034 
16035     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_wsgi (pid=%d): "
16036                   "Authorization of user '%s' to access '%s' failed. %s.",
16037                   getpid(), r->user, r->uri, reason ? reason : "User is not "
16038                   "a member of designated groups");
16039 
16040     ap_note_auth_failure(r);
16041 
16042     return HTTP_UNAUTHORIZED;
16043 }
16044 
16045 #endif
16046 
16047 APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *wsgi_logio_add_bytes_out;
16048 
16049 static void ap_logio_add_bytes_out(conn_rec *c, apr_off_t bytes)
16050 {
16051     if (!wsgi_daemon_pool && wsgi_logio_add_bytes_out)
16052         wsgi_logio_add_bytes_out(c, bytes);
16053 }
16054 
16055 static int wsgi_hook_logio(apr_pool_t *pconf, apr_pool_t *ptemp,
16056                            apr_pool_t *plog, server_rec *s)
16057 {
16058     /*
16059      * This horrible fiddle is to insert a proxy function before
16060      * the normal ap_logio_add_bytes_out() function so that the
16061      * call to it can be disabled when mod_wsgi running in daemon
16062      * mode. If this is not done, then daemon process will crash
16063      * when mod_logio has been loaded.
16064      */
16065 
16066     wsgi_logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out);
16067 
16068     APR_REGISTER_OPTIONAL_FN(ap_logio_add_bytes_out);
16069 
16070     return OK;
16071 }
16072 
16073 static void wsgi_register_hooks(apr_pool_t *p)
16074 {
16075     static const char * const p1[] = { "mod_alias.c", NULL };
16076     static const char * const n1[]= { "mod_userdir.c",
16077                                       "mod_vhost_alias.c", NULL };
16078 
16079     static const char * const n2[] = { "core.c", NULL };
16080 
16081 #if !defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
16082     static const char * const p3[] = { "mod_auth.c", NULL };
16083 #endif
16084 #if !defined(MOD_WSGI_WITH_AUTHZ_PROVIDER)
16085     static const char * const n4[] = { "mod_authz_user.c", NULL };
16086 #endif
16087     static const char * const n5[] = { "mod_authz_host.c", NULL };
16088 
16089     static const char * const p6[] = { "mod_python.c", NULL };
16090 
16091     static const char * const p7[] = { "mod_ssl.c", NULL };
16092 
16093     ap_hook_post_config(wsgi_hook_init, p6, NULL, APR_HOOK_MIDDLE);
16094     ap_hook_child_init(wsgi_hook_child_init, p6, NULL, APR_HOOK_MIDDLE);
16095 
16096     ap_hook_translate_name(wsgi_hook_intercept, p1, n1, APR_HOOK_MIDDLE);
16097     ap_hook_handler(wsgi_hook_handler, NULL, NULL, APR_HOOK_MIDDLE);
16098 
16099 #if defined(MOD_WSGI_WITH_DAEMONS)
16100     ap_hook_post_config(wsgi_hook_logio, NULL, n2, APR_HOOK_REALLY_FIRST);
16101 
16102     wsgi_header_filter_handle =
16103         ap_register_output_filter("WSGI_HEADER", wsgi_header_filter,
16104                                   NULL, AP_FTYPE_PROTOCOL);
16105 #endif
16106 
16107 #if !defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
16108     ap_hook_check_user_id(wsgi_hook_check_user_id, p3, NULL, APR_HOOK_MIDDLE);
16109 #else
16110     ap_register_provider(p, AUTHN_PROVIDER_GROUP, "wsgi",
16111                          AUTHN_PROVIDER_VERSION, &wsgi_authn_provider);
16112 #endif
16113 #if !defined(MOD_WSGI_WITH_AUTHZ_PROVIDER)
16114     ap_hook_auth_checker(wsgi_hook_auth_checker, NULL, n4, APR_HOOK_MIDDLE);
16115 #else
16116     ap_register_provider(p, AUTHZ_PROVIDER_GROUP, "wsgi-group",
16117                          AUTHZ_PROVIDER_VERSION, &wsgi_authz_provider);
16118 #endif
16119     ap_hook_access_checker(wsgi_hook_access_checker, p7, n5, APR_HOOK_MIDDLE);
16120 }
16121 
16122 static const command_rec wsgi_commands[] =
16123 {
16124     AP_INIT_RAW_ARGS("WSGIScriptAlias", wsgi_add_script_alias,
16125         NULL, RSRC_CONF, "Map location to target WSGI script file."),
16126     AP_INIT_RAW_ARGS("WSGIScriptAliasMatch", wsgi_add_script_alias,
16127         "*", RSRC_CONF, "Map location pattern to target WSGI script file."),
16128 
16129 #if defined(MOD_WSGI_WITH_DAEMONS)
16130     AP_INIT_RAW_ARGS("WSGIDaemonProcess", wsgi_add_daemon_process,
16131         NULL, RSRC_CONF, "Specify details of daemon processes to start."),
16132     AP_INIT_TAKE1("WSGISocketPrefix", wsgi_set_socket_prefix,
16133         NULL, RSRC_CONF, "Path prefix for the daemon process sockets."),
16134     AP_INIT_TAKE1("WSGISocketRotation", wsgi_set_socket_rotation,
16135         NULL, RSRC_CONF, "Enable/Disable rotation of daemon process sockets."),
16136     AP_INIT_TAKE1("WSGIAcceptMutex", wsgi_set_accept_mutex,
16137         NULL, RSRC_CONF, "Set accept mutex type for daemon processes."),
16138 
16139     AP_INIT_TAKE1("WSGILazyInitialization", wsgi_set_lazy_initialization,
16140         NULL, RSRC_CONF, "Enable/Disable lazy Python initialization."),
16141 #endif
16142 
16143     AP_INIT_TAKE1("WSGIVerboseDebugging", wsgi_set_verbose_debugging,
16144         NULL, RSRC_CONF, "Enable/Disable verbose debugging messages."),
16145 
16146 #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6
16147     AP_INIT_TAKE1("WSGIPy3kWarningFlag", wsgi_set_py3k_warning_flag,
16148         NULL, RSRC_CONF, "Enable/Disable Python 3.0 warnings."),
16149 #endif
16150 
16151 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 3) || \
16152     (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6)
16153     AP_INIT_TAKE1("WSGIDontWriteBytecode", wsgi_set_dont_write_bytecode,
16154         NULL, RSRC_CONF, "Enable/Disable writing of byte code."),
16155 #endif
16156 
16157     AP_INIT_TAKE1("WSGIPythonWarnings", wsgi_add_python_warnings,
16158         NULL, RSRC_CONF, "Control Python warning messages."),
16159     AP_INIT_TAKE1("WSGIPythonOptimize", wsgi_set_python_optimize,
16160         NULL, RSRC_CONF, "Set level of Python compiler optimisations."),
16161     AP_INIT_TAKE1("WSGIPythonHome", wsgi_set_python_home,
16162         NULL, RSRC_CONF, "Python prefix/exec_prefix absolute path names."),
16163     AP_INIT_TAKE1("WSGIPythonPath", wsgi_set_python_path,
16164         NULL, RSRC_CONF, "Python module search path."),
16165     AP_INIT_TAKE1("WSGIPythonEggs", wsgi_set_python_eggs,
16166         NULL, RSRC_CONF, "Python eggs cache directory."),
16167     AP_INIT_TAKE1("WSGIPythonHashSeed", wsgi_set_python_hash_seed,
16168         NULL, RSRC_CONF, "Python hash seed."),
16169 
16170 #if defined(MOD_WSGI_WITH_DAEMONS)
16171     AP_INIT_TAKE1("WSGIRestrictEmbedded", wsgi_set_restrict_embedded,
16172         NULL, RSRC_CONF, "Enable/Disable use of embedded mode."),
16173 #endif
16174     AP_INIT_TAKE1("WSGIRestrictStdin", wsgi_set_restrict_stdin,
16175         NULL, RSRC_CONF, "Enable/Disable restrictions on use of STDIN."),
16176     AP_INIT_TAKE1("WSGIRestrictStdout", wsgi_set_restrict_stdout,
16177         NULL, RSRC_CONF, "Enable/Disable restrictions on use of STDOUT."),
16178     AP_INIT_TAKE1("WSGIRestrictSignal", wsgi_set_restrict_signal,
16179         NULL, RSRC_CONF, "Enable/Disable restrictions on use of signal()."),
16180 
16181     AP_INIT_TAKE1("WSGICaseSensitivity", wsgi_set_case_sensitivity,
16182         NULL, RSRC_CONF, "Define whether file system is case sensitive."),
16183 
16184 #if defined(MOD_WSGI_WITH_DAEMONS)
16185     AP_INIT_RAW_ARGS("WSGIRestrictProcess", wsgi_set_restrict_process,
16186         NULL, ACCESS_CONF|RSRC_CONF, "Limit selectable WSGI process groups."),
16187     AP_INIT_TAKE1("WSGIProcessGroup", wsgi_set_process_group,
16188         NULL, ACCESS_CONF|RSRC_CONF, "Name of the WSGI process group."),
16189 #endif
16190 
16191     AP_INIT_TAKE1("WSGIApplicationGroup", wsgi_set_application_group,
16192         NULL, ACCESS_CONF|RSRC_CONF, "Application interpreter group."),
16193     AP_INIT_TAKE1("WSGICallableObject", wsgi_set_callable_object,
16194         NULL, OR_FILEINFO, "Name of entry point in WSGI script file."),
16195 
16196     AP_INIT_RAW_ARGS("WSGIImportScript", wsgi_add_import_script,
16197         NULL, RSRC_CONF, "Location of WSGI import script."),
16198     AP_INIT_RAW_ARGS("WSGIDispatchScript", wsgi_set_dispatch_script,
16199         NULL, ACCESS_CONF|RSRC_CONF, "Location of WSGI dispatch script."),
16200 
16201     AP_INIT_TAKE1("WSGIPassApacheRequest", wsgi_set_pass_apache_request,
16202         NULL, ACCESS_CONF|RSRC_CONF, "Enable/Disable Apache request object."),
16203     AP_INIT_TAKE1("WSGIPassAuthorization", wsgi_set_pass_authorization,
16204         NULL, OR_FILEINFO, "Enable/Disable WSGI authorization."),
16205     AP_INIT_TAKE1("WSGIScriptReloading", wsgi_set_script_reloading,
16206         NULL, OR_FILEINFO, "Enable/Disable script reloading mechanism."),
16207     AP_INIT_TAKE1("WSGIErrorOverride", wsgi_set_error_override,
16208         NULL, OR_FILEINFO, "Enable/Disable overriding of error pages."),
16209     AP_INIT_TAKE1("WSGIChunkedRequest", wsgi_set_chunked_request,
16210         NULL, OR_FILEINFO, "Enable/Disable support for chunked requests."),
16211     AP_INIT_TAKE1("WSGIMapHEADToGET", wsgi_set_map_head_to_get,
16212         NULL, OR_FILEINFO, "Enable/Disable mapping of HEAD to GET."),
16213     AP_INIT_TAKE1("WSGIIgnoreActivity", wsgi_set_ignore_activity,
16214         NULL, OR_FILEINFO, "Enable/Disable reset of inactvity timeout."),
16215 
16216     AP_INIT_RAW_ARGS("WSGITrustedProxyHeaders", wsgi_set_trusted_proxy_headers,
16217         NULL, OR_FILEINFO, "Specify a list of trusted proxy headers."),
16218     AP_INIT_RAW_ARGS("WSGITrustedProxies", wsgi_set_trusted_proxies,
16219         NULL, OR_FILEINFO, "Specify a list of trusted proxies."),
16220 
16221 #ifndef WIN32
16222     AP_INIT_TAKE1("WSGIEnableSendfile", wsgi_set_enable_sendfile,
16223         NULL, OR_FILEINFO, "Enable/Disable support for kernel sendfile."),
16224 #endif
16225 
16226     AP_INIT_RAW_ARGS("WSGIAccessScript", wsgi_set_access_script,
16227         NULL, OR_AUTHCFG, "Location of WSGI host access script file."),
16228     AP_INIT_RAW_ARGS("WSGIAuthUserScript", wsgi_set_auth_user_script,
16229         NULL, OR_AUTHCFG, "Location of WSGI user auth script file."),
16230     AP_INIT_RAW_ARGS("WSGIAuthGroupScript", wsgi_set_auth_group_script,
16231         NULL, OR_AUTHCFG, "Location of WSGI group auth script file."),
16232 #if !defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
16233     AP_INIT_TAKE1("WSGIUserAuthoritative", wsgi_set_user_authoritative,
16234         NULL, OR_AUTHCFG, "Enable/Disable as being authoritative on users."),
16235 #endif
16236     AP_INIT_TAKE1("WSGIGroupAuthoritative", wsgi_set_group_authoritative,
16237         NULL, OR_AUTHCFG, "Enable/Disable as being authoritative on groups."),
16238 
16239     AP_INIT_RAW_ARGS("WSGIHandlerScript", wsgi_add_handler_script,
16240         NULL, ACCESS_CONF|RSRC_CONF, "Location of WSGI handler script file."),
16241 
16242     AP_INIT_TAKE1("WSGIServerMetrics", wsgi_set_server_metrics,
16243         NULL, RSRC_CONF, "Enabled/Disable access to server metrics."),
16244 
16245     AP_INIT_TAKE1("WSGINewRelicConfigFile", wsgi_set_newrelic_config_file,
16246         NULL, RSRC_CONF, "New Relic monitoring agent configuration file."),
16247     AP_INIT_TAKE1("WSGINewRelicEnvironment", wsgi_set_newrelic_environment,
16248         NULL, RSRC_CONF, "New Relic monitoring agent environment."),
16249 
16250     { NULL }
16251 };
16252 
16253 /* Dispatch list for API hooks */
16254 
16255 module AP_MODULE_DECLARE_DATA wsgi_module = {
16256     STANDARD20_MODULE_STUFF,
16257     wsgi_create_dir_config,    /* create per-dir    config structures */
16258     wsgi_merge_dir_config,     /* merge  per-dir    config structures */
16259     wsgi_create_server_config, /* create per-server config structures */
16260     wsgi_merge_server_config,  /* merge  per-server config structures */
16261     wsgi_commands,             /* table of config file commands       */
16262     wsgi_register_hooks        /* register hooks                      */
16263 };
16264 
16265 /* ------------------------------------------------------------------------- */
16266 
16267 #if defined(_WIN32)
16268 #if PY_MAJOR_VERSION < 3
16269 PyMODINIT_FUNC initmod_wsgi(void)
16270 {
16271 }
16272 #else
16273 PyMODINIT_FUNC PyInit_mod_wsgi(void)
16274 {
16275     return NULL;
16276 }
16277 #endif
16278 #endif
16279 
16280 /* ------------------------------------------------------------------------- */
16281 
16282 /* vi: set sw=4 expandtab : */
16283