|
64 | 64 |
|
65 | 65 | #if 0 /* XXX */ |
66 | 66 |
|
67 | | -/* _________________________________________________________________ |
68 | | -** |
69 | | -** I/O Request Body Sucking and Re-Injection |
70 | | -** _________________________________________________________________ |
71 | | -*/ |
72 | | - |
73 | | -#ifndef SSL_CONSERVATIVE |
74 | | - |
75 | | -/* |
76 | | - * Background: |
77 | | - * |
78 | | - * 1. When the client sends a HTTP/HTTPS request, Apache's core code |
79 | | - * reads only the request line ("METHOD /path HTTP/x.y") and the |
80 | | - * attached MIME headers ("Foo: bar") up to the terminating line ("CR |
81 | | - * LF"). An attached request body (for instance the data of a POST |
82 | | - * method) is _NOT_ read. Instead it is read by mod_cgi's content |
83 | | - * handler and directly passed to the CGI script. |
84 | | - * |
85 | | - * 2. mod_ssl supports per-directory re-configuration of SSL parameters. |
86 | | - * This is implemented by performing an SSL renegotiation of the |
87 | | - * re-configured parameters after the request is read, but before the |
88 | | - * response is sent. In more detail: the renegotiation happens after the |
89 | | - * request line and MIME headers were read, but _before_ the attached |
90 | | - * request body is read. The reason simply is that in the HTTP protocol |
91 | | - * usually there is no acknowledgment step between the headers and the |
92 | | - * body (there is the 100-continue feature and the chunking facility |
93 | | - * only), so Apache has no API hook for this step. |
94 | | - * |
95 | | - * 3. the problem now occurs when the client sends a POST request for |
96 | | - * URL /foo via HTTPS the server and the server has SSL parameters |
97 | | - * re-configured on a per-URL basis for /foo. Then mod_ssl has to |
98 | | - * perform an SSL renegotiation after the request was read and before |
99 | | - * the response is sent. But the problem is the pending POST body data |
100 | | - * in the receive buffer of SSL (which Apache still has not read - it's |
101 | | - * pending until mod_cgi sucks it in). When mod_ssl now tries to perform |
102 | | - * the renegotiation the pending data leads to an I/O error. |
103 | | - * |
104 | | - * Solution Idea: |
105 | | - * |
106 | | - * There are only two solutions: Either to simply state that POST |
107 | | - * requests to URLs with SSL re-configurations are not allowed, or to |
108 | | - * renegotiate really after the _complete_ request (i.e. including |
109 | | - * the POST body) was read. Obviously the latter would be preferred, |
110 | | - * but it cannot be done easily inside Apache, because as already |
111 | | - * mentioned, there is no API step between the body reading and the body |
112 | | - * processing. And even when we mod_ssl would hook directly into the |
113 | | - * loop of mod_cgi, we wouldn't solve the problem for other handlers, of |
114 | | - * course. So the only general solution is to suck in the pending data |
115 | | - * of the request body from the OpenSSL BIO into the Apache BUFF. Then |
116 | | - * the renegotiation can be done and after this step Apache can proceed |
117 | | - * processing the request as before. |
118 | | - * |
119 | | - * Solution Implementation: |
120 | | - * |
121 | | - * We cannot simply suck in the data via an SSL_read-based loop because of |
122 | | - * HTTP chunking. Instead we _have_ to use the Apache API for this step which |
123 | | - * is aware of HTTP chunking. So the trick is to suck in the pending request |
124 | | - * data via the Apache API (which uses Apache's BUFF code and in the |
125 | | - * background mod_ssl's I/O glue code) and re-inject it later into the Apache |
126 | | - * BUFF code again. This way the data flows twice through the Apache BUFF, of |
127 | | - * course. But this way the solution doesn't depend on any Apache specifics |
128 | | - * and is fully transparent to Apache modules. |
129 | | - */ |
130 | | - |
131 | | -struct ssl_io_suck_st { |
132 | | - BOOL active; |
133 | | - char *bufptr; |
134 | | - int buflen; |
135 | | - char *pendptr; |
136 | | - int pendlen; |
137 | | -}; |
138 | | - |
139 | | -/* prepare request_rec structure for input sucking */ |
140 | | -static void ssl_io_suck_start(request_rec *r) |
141 | | -{ |
142 | | - struct ssl_io_suck_st *ss; |
143 | | - |
144 | | - ss = ap_ctx_get(r->ctx, "ssl::io::suck"); |
145 | | - if (ss == NULL) { |
146 | | - ss = ap_palloc(r->pool, sizeof(struct ssl_io_suck_st)); |
147 | | - ap_ctx_set(r->ctx, "ssl::io::suck", ss); |
148 | | - ss->buflen = 8192; |
149 | | - ss->bufptr = ap_palloc(r->pool, ss->buflen); |
150 | | - } |
151 | | - ss->pendptr = ss->bufptr; |
152 | | - ss->pendlen = 0; |
153 | | - ss->active = FALSE; |
154 | | - return; |
155 | | -} |
156 | | - |
157 | | -/* record a sucked input chunk */ |
158 | | -static void ssl_io_suck_record(request_rec *r, char *buf, int len) |
159 | | -{ |
160 | | - struct ssl_io_suck_st *ss; |
161 | | - |
162 | | - if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL) |
163 | | - return; |
164 | | - if (((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) < len) { |
165 | | - /* "expand" buffer: actually we cannot really expand the buffer |
166 | | - here, because Apache's pool system doesn't support expanding chunks |
167 | | - of memory. Instead we have to either reuse processed data or |
168 | | - allocate a new chunk of memory in advance if we really need more |
169 | | - memory. */ |
170 | | - int newlen; |
171 | | - char *newptr; |
172 | | - |
173 | | - if (( (ss->pendptr - ss->bufptr) |
174 | | - + ((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) ) >= len) { |
175 | | - /* make memory available by reusing already processed data */ |
176 | | - memmove(ss->bufptr, ss->pendptr, ss->pendlen); |
177 | | - ss->pendptr = ss->bufptr; |
178 | | - } |
179 | | - else { |
180 | | - /* too bad, we have to allocate a new larger buffer */ |
181 | | - newlen = (ss->buflen * 2) + len; |
182 | | - newptr = ap_palloc(r->pool, newlen); |
183 | | - ss->bufptr = newptr; |
184 | | - ss->buflen = newlen; |
185 | | - memcpy(ss->bufptr, ss->pendptr, ss->pendlen); |
186 | | - ss->pendptr = ss->bufptr; |
187 | | - } |
188 | | - } |
189 | | - memcpy(ss->pendptr+ss->pendlen, buf, len); |
190 | | - ss->pendlen += len; |
191 | | - return; |
192 | | -} |
193 | | - |
194 | | -/* finish request_rec after input sucking */ |
195 | | -static void ssl_io_suck_end(request_rec *r) |
196 | | -{ |
197 | | - struct ssl_io_suck_st *ss; |
198 | | - |
199 | | - if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL) |
200 | | - return; |
201 | | - ss->active = TRUE; |
202 | | - r->read_body = REQUEST_NO_BODY; |
203 | | - r->read_length = 0; |
204 | | - r->read_chunked = 0; |
205 | | - r->remaining = 0; |
206 | | - ap_bsetflag(r->connection->client, B_CHUNK, 0); |
207 | | - return; |
208 | | -} |
209 | | - |
210 | | -void ssl_io_suck(request_rec *r, SSL *ssl) |
211 | | -{ |
212 | | - int rc; |
213 | | - int len; |
214 | | - char *buf; |
215 | | - int buflen; |
216 | | - char c; |
217 | | - int sucked; |
218 | | - |
219 | | - if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) == OK) { |
220 | | - if (ap_should_client_block(r)) { |
221 | | - |
222 | | - /* read client request block through Apache API */ |
223 | | - buflen = HUGE_STRING_LEN; |
224 | | - buf = ap_palloc(r->pool, buflen); |
225 | | - ap_hard_timeout("SSL I/O request body pre-sucking", r); |
226 | | - sucked = 0; |
227 | | - ssl_io_suck_start(r); |
228 | | - while ((len = ap_get_client_block(r, buf, buflen)) > 0) { |
229 | | - ssl_io_suck_record(r, buf, len); |
230 | | - sucked += len; |
231 | | - } |
232 | | - ssl_io_suck_end(r); |
233 | | - ap_kill_timeout(r); |
234 | | - |
235 | | - /* suck trailing data (usually CR LF) which |
236 | | - is still in the Apache BUFF layer */ |
237 | | - while (ap_bpeekc(r->connection->client) != EOF) { |
238 | | - c = ap_bgetc(r->connection->client); |
239 | | - ssl_io_suck_record(r, &c, 1); |
240 | | - sucked++; |
241 | | - } |
242 | | - |
243 | | - ssl_log(r->server, SSL_LOG_TRACE, |
244 | | - "I/O: sucked %d bytes of input data from SSL/TLS I/O layer " |
245 | | - "for delayed injection into Apache I/O layer", sucked); |
246 | | - } |
247 | | - } |
248 | | - return; |
249 | | -} |
250 | | - |
251 | | -/* the SSL_read replacement routine which knows about the suck buffer */ |
252 | | -static int ssl_io_suck_read(SSL *ssl, char *buf, int len) |
253 | | -{ |
254 | | - ap_ctx *actx; |
255 | | - struct ssl_io_suck_st *ss; |
256 | | - request_rec *r = NULL; |
257 | | - int rv; |
258 | | - |
259 | | - actx = (ap_ctx *)SSL_get_app_data2(ssl); |
260 | | - if (actx != NULL) |
261 | | - r = (request_rec *)ap_ctx_get(actx, "ssl::request_rec"); |
262 | | - |
263 | | - rv = -1; |
264 | | - if (r != NULL) { |
265 | | - ss = ap_ctx_get(r->ctx, "ssl::io::suck"); |
266 | | - if (ss != NULL) { |
267 | | - if (ss->active && ss->pendlen > 0) { |
268 | | - /* ok, there is pre-sucked data */ |
269 | | - len = (ss->pendlen > len ? len : ss->pendlen); |
270 | | - memcpy(buf, ss->pendptr, len); |
271 | | - ss->pendptr += len; |
272 | | - ss->pendlen -= len; |
273 | | - ssl_log(r->server, SSL_LOG_TRACE, |
274 | | - "I/O: injecting %d bytes of pre-sucked data " |
275 | | - "into Apache I/O layer", len); |
276 | | - rv = len; |
277 | | - } |
278 | | - } |
279 | | - } |
280 | | - if (rv == -1) |
281 | | - rv = SSL_read(ssl, buf, len); |
282 | | - return rv; |
283 | | -} |
284 | | - |
285 | | -/* override SSL_read in the following code... */ |
286 | | -#define SSL_read ssl_io_suck_read |
287 | | - |
288 | | -#endif /* !SSL_CONSERVATIVE */ |
289 | | - |
290 | 67 | /* _________________________________________________________________ |
291 | 68 | ** |
292 | 69 | ** I/O Hooks |
|
0 commit comments