summaryrefslogtreecommitdiff
blob: e7911e0385f4e23d74ef4525c839194fec41344a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
Author: wrowe
Date: Wed Jul 16 20:56:51 2014
New Revision: 1611185

URL: http://svn.apache.org/r1611185
Log:
SECURITY: CVE-2014-0231

  mod_cgid: Fix a denial of service against CGI scripts that do
  not consume stdin that could lead to lingering HTTPD child processes
  filling up the scoreboard and eventually hanging the server.

Submitted by: Rainer Jung, Eric Covener, Yann Ylavic
Backports: r1610509, r1535125
Reviewed by: covener, trawick, ylavic

Modified:
    httpd/httpd/branches/2.2.x/modules/generators/mod_cgid.c

Modified: httpd/httpd/branches/2.2.x/modules/generators/mod_cgid.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/modules/generators/mod_cgid.c?rev=1611185&r1=1611184&r2=1611185&view=diff
==============================================================================
--- httpd/httpd/branches/2.2.x/modules/generators/mod_cgid.c (original)
+++ httpd/httpd/branches/2.2.x/modules/generators/mod_cgid.c Wed Jul 16 20:56:51 2014
@@ -93,6 +93,10 @@ static const char *sockname;
 static pid_t parent_pid;
 static ap_unix_identity_t empty_ugid = { (uid_t)-1, (gid_t)-1, -1 };
 
+typedef struct { 
+    apr_interval_time_t timeout;
+} cgid_dirconf;
+
 /* The APR other-child API doesn't tell us how the daemon exited
  * (SIGSEGV vs. exit(1)).  The other-child maintenance function
  * needs to decide whether to restart the daemon after a failure
@@ -934,7 +938,14 @@ static void *merge_cgid_config(apr_pool_
     return overrides->logname ? overrides : base;
 }
 
+static void *create_cgid_dirconf(apr_pool_t *p, char *dummy)
+{
+    cgid_dirconf *c = (cgid_dirconf *) apr_pcalloc(p, sizeof(cgid_dirconf));
+    return c;
+}
+
 static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg)
+
 {
     server_rec *s = cmd->server;
     cgid_server_conf *conf = ap_get_module_config(s->module_config,
@@ -987,7 +998,16 @@ static const char *set_script_socket(cmd
 
     return NULL;
 }
+static const char *set_script_timeout(cmd_parms *cmd, void *dummy, const char *arg)
+{
+    cgid_dirconf *dc = dummy;
 
+    if (ap_timeout_parameter_parse(arg, &dc->timeout, "s") != APR_SUCCESS) { 
+        return "CGIDScriptTimeout has wrong format";
+    }
+ 
+    return NULL;
+}
 static const command_rec cgid_cmds[] =
 {
     AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF,
@@ -999,6 +1019,10 @@ static const command_rec cgid_cmds[] =
     AP_INIT_TAKE1("ScriptSock", set_script_socket, NULL, RSRC_CONF,
                   "the name of the socket to use for communication with "
                   "the cgi daemon."),
+    AP_INIT_TAKE1("CGIDScriptTimeout", set_script_timeout, NULL, RSRC_CONF | ACCESS_CONF,
+                  "The amount of time to wait between successful reads from "
+                  "the CGI script, in seconds."),
+                  
     {NULL}
 };
 
@@ -1335,11 +1359,15 @@ static int cgid_handler(request_rec *r)
     apr_file_t *tempsock;
     struct cleanup_script_info *info;
     apr_status_t rv;
+    cgid_dirconf *dc;
 
     if (strcmp(r->handler,CGI_MAGIC_TYPE) && strcmp(r->handler,"cgi-script"))
         return DECLINED;
 
     conf = ap_get_module_config(r->server->module_config, &cgid_module);
+    dc = ap_get_module_config(r->per_dir_config, &cgid_module);
+
+    
     is_included = !strcmp(r->protocol, "INCLUDED");
 
     if ((argv0 = strrchr(r->filename, '/')) != NULL)
@@ -1412,6 +1440,12 @@ static int cgid_handler(request_rec *r)
      */
 
     apr_os_pipe_put_ex(&tempsock, &sd, 1, r->pool);
+    if (dc->timeout > 0) { 
+        apr_file_pipe_timeout_set(tempsock, dc->timeout);
+    }
+    else { 
+        apr_file_pipe_timeout_set(tempsock, r->server->timeout);
+    }
     apr_pool_cleanup_kill(r->pool, (void *)((long)sd), close_unix_socket);
 
     if ((argv0 = strrchr(r->filename, '/')) != NULL)
@@ -1487,6 +1521,10 @@ static int cgid_handler(request_rec *r)
             if (rv != APR_SUCCESS) {
                 /* silly script stopped reading, soak up remaining message */
                 child_stopped_reading = 1;
+                ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, 
+                              "Error writing request body to script %s", 
+                              r->filename);
+
             }
         }
         apr_brigade_cleanup(bb);
@@ -1577,7 +1615,13 @@ static int cgid_handler(request_rec *r)
             return HTTP_MOVED_TEMPORARILY;
         }
 
-        ap_pass_brigade(r->output_filters, bb);
+        rv = ap_pass_brigade(r->output_filters, bb);
+        if (rv != APR_SUCCESS) { 
+            /* APLOG_ERR because the core output filter message is at error,
+             * but doesn't know it's passing CGI output 
+             */
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "Failed to flush CGI output to client");
+        }
     }
 
     if (nph) {
@@ -1707,6 +1751,8 @@ static int include_cmd(include_ctx_t *ct
     request_rec *r = f->r;
     cgid_server_conf *conf = ap_get_module_config(r->server->module_config,
                                                   &cgid_module);
+    cgid_dirconf *dc = ap_get_module_config(r->per_dir_config, &cgid_module);
+
     struct cleanup_script_info *info;
 
     add_ssi_vars(r);
@@ -1736,6 +1782,13 @@ static int include_cmd(include_ctx_t *ct
      * get rid of the cleanup we registered when we created the socket.
      */
     apr_os_pipe_put_ex(&tempsock, &sd, 1, r->pool);
+    if (dc->timeout > 0) {
+        apr_file_pipe_timeout_set(tempsock, dc->timeout);
+    }
+    else {
+        apr_file_pipe_timeout_set(tempsock, r->server->timeout);
+    }
+
     apr_pool_cleanup_kill(r->pool, (void *)((long)sd), close_unix_socket);
 
     APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pipe_create(tempsock,
@@ -1841,7 +1894,7 @@ static void register_hook(apr_pool_t *p)
 
 module AP_MODULE_DECLARE_DATA cgid_module = {
     STANDARD20_MODULE_STUFF,
-    NULL, /* dir config creater */
+    create_cgid_dirconf, /* dir config creater */
     NULL, /* dir merger --- default is to override */
     create_cgid_config, /* server config */
     merge_cgid_config, /* merge server config */