ASF Bugzilla – Attachment 16495 Details for
Bug 12355
POST incompatible w/ renegotiate https: connection
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
patch for 2.0.54
ssl_20reneg2.diff (text/plain), 13.28 KB, created by
Joe Orton
on 2005-09-23 09:24:09 UTC
(
hide
)
Description:
patch for 2.0.54
Filename:
MIME Type:
Creator:
Joe Orton
Created:
2005-09-23 09:24:09 UTC
Size:
13.28 KB
patch
obsolete
>Index: modules/ssl/ssl_engine_io.c >=================================================================== >--- modules/ssl/ssl_engine_io.c (revision 291000) >+++ modules/ssl/ssl_engine_io.c (working copy) >@@ -880,6 +880,7 @@ > } > > static const char ssl_io_filter[] = "SSL/TLS Filter"; >+static const char ssl_io_buffer[] = "SSL/TLS Buffer"; > > /* > * Close the SSL part of the socket connection >@@ -1373,6 +1374,187 @@ > return status; > } > >+/* 128K maximum buffer size by default. */ >+#ifndef SSL_MAX_IO_BUFFER >+#define SSL_MAX_IO_BUFFER (128 * 1024) >+#endif >+ >+struct modssl_buffer_ctx { >+ apr_bucket_brigade *bb; >+ apr_pool_t *pool; >+}; >+ >+int ssl_io_buffer_fill(request_rec *r) >+{ >+ conn_rec *c = r->connection; >+ struct modssl_buffer_ctx *ctx; >+ apr_bucket_brigade *tempb; >+ apr_off_t total = 0; /* total length buffered */ >+ int eos = 0; /* non-zero once EOS is seen */ >+ >+ /* Create the context which will be passed to the input filter; >+ * containing a setaside pool and a brigade which constrain the >+ * lifetime of the buffered data. */ >+ ctx = apr_palloc(r->pool, sizeof *ctx); >+ apr_pool_create(&ctx->pool, r->pool); >+ ctx->bb = apr_brigade_create(ctx->pool, c->bucket_alloc); >+ >+ /* ... and a temporary brigade. */ >+ tempb = apr_brigade_create(r->pool, c->bucket_alloc); >+ >+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "filling buffer"); >+ >+ do { >+ apr_status_t rv; >+ apr_bucket *e, *next; >+ >+ /* The request body is read from the protocol-level input >+ * filters; the buffering filter will reinject it from that >+ * level, allowing content/resource filters to run later, if >+ * necessary. */ >+ >+ rv = ap_get_brigade(r->proto_input_filters, tempb, AP_MODE_READBYTES, >+ APR_BLOCK_READ, 8192); >+ if (rv) { >+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, >+ "could not read request body for SSL buffer"); >+ return HTTP_INTERNAL_SERVER_ERROR; >+ } >+ >+ /* Iterate through the returned brigade: setaside each bucket >+ * into the context's pool and move it into the brigade. */ >+ for (e = APR_BRIGADE_FIRST(tempb); >+ e != APR_BRIGADE_SENTINEL(tempb) && !eos; e = next) { >+ const char *data; >+ apr_size_t len; >+ >+ next = APR_BUCKET_NEXT(e); >+ >+ if (APR_BUCKET_IS_EOS(e)) { >+ eos = 1; >+ } else if (!APR_BUCKET_IS_METADATA(e)) { >+ rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ); >+ if (rv != APR_SUCCESS) { >+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, >+ "could not read bucket for SSL buffer"); >+ return HTTP_INTERNAL_SERVER_ERROR; >+ } >+ total += len; >+ } >+ >+ rv = apr_bucket_setaside(e, ctx->pool); >+ if (rv != APR_SUCCESS) { >+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, >+ "could not setaside bucket for SSL buffer"); >+ return HTTP_INTERNAL_SERVER_ERROR; >+ } >+ >+ APR_BUCKET_REMOVE(e); >+ APR_BRIGADE_INSERT_TAIL(ctx->bb, e); >+ } >+ >+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, >+ "total of %" APR_OFF_T_FMT " bytes in buffer, eos=%d", >+ total, eos); >+ >+ /* Fail if this exceeds the maximum buffer size. */ >+ if (total > SSL_MAX_IO_BUFFER) { >+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, >+ "request body exceeds maximum size for SSL buffer"); >+ return HTTP_REQUEST_ENTITY_TOO_LARGE; >+ } >+ >+ } while (!eos); >+ >+ apr_brigade_destroy(tempb); >+ >+ /* Insert the filter which will supply the buffered data. */ >+ ap_add_input_filter(ssl_io_buffer, ctx, r, c); >+ >+ return 0; >+} >+ >+/* This input filter supplies the buffered request body to the caller >+ * from the brigade stored in f->ctx. */ >+static apr_status_t ssl_io_filter_buffer(ap_filter_t *f, >+ apr_bucket_brigade *bb, >+ ap_input_mode_t mode, >+ apr_read_type_e block, >+ apr_off_t bytes) >+{ >+ struct modssl_buffer_ctx *ctx = f->ctx; >+ apr_status_t rv; >+ >+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, >+ "read from buffered SSL brigade, mode %d, " >+ "%" APR_OFF_T_FMT " bytes", >+ mode, bytes); >+ >+ if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) { >+ return APR_ENOTIMPL; >+ } >+ >+ if (mode == AP_MODE_READBYTES) { >+ apr_bucket *e; >+ >+ /* Partition the buffered brigade. */ >+ rv = apr_brigade_partition(ctx->bb, bytes, &e); >+ if (rv && rv != APR_INCOMPLETE) { >+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, >+ "could not partition buffered SSL brigade"); >+ ap_remove_input_filter(f); >+ return rv; >+ } >+ >+ /* If the buffered brigade contains less then the requested >+ * length, just pass it all back. */ >+ if (rv == APR_INCOMPLETE) { >+ APR_BRIGADE_CONCAT(bb, ctx->bb); >+ } else { >+ apr_bucket *d = APR_BRIGADE_FIRST(ctx->bb); >+ >+ e = APR_BUCKET_PREV(e); >+ >+ /* Unsplice the partitioned segment and move it into the >+ * passed-in brigade; no convenient way to do this with >+ * the APR_BRIGADE_* macros. */ >+ APR_RING_UNSPLICE(d, e, link); >+ APR_RING_SPLICE_HEAD(&bb->list, d, e, apr_bucket, link); >+ >+ APR_BRIGADE_CHECK_CONSISTENCY(bb); >+ APR_BRIGADE_CHECK_CONSISTENCY(ctx->bb); >+ } >+ } >+ else { >+ /* Split a line into the passed-in brigade. */ >+ rv = apr_brigade_split_line(bb, ctx->bb, mode, bytes); >+ >+ if (rv) { >+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, >+ "could not split line from buffered SSL brigade"); >+ ap_remove_input_filter(f); >+ return rv; >+ } >+ } >+ >+ if (APR_BRIGADE_EMPTY(ctx->bb)) { >+ apr_bucket *e = APR_BRIGADE_LAST(bb); >+ >+ /* Ensure that the brigade is terminated by an EOS if the >+ * buffered request body has been entirely consumed. */ >+ if (e == APR_BRIGADE_SENTINEL(bb) || !APR_BUCKET_IS_EOS(e)) { >+ e = apr_bucket_eos_create(f->c->bucket_alloc); >+ APR_BRIGADE_INSERT_TAIL(bb, e); >+ } >+ >+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, >+ "buffered SSL brigade now exhausted; removing filter"); >+ ap_remove_input_filter(f); >+ } >+ >+ return APR_SUCCESS; >+} >+ > static void ssl_io_input_add_filter(ssl_filter_ctx_t *filter_ctx, conn_rec *c, > SSL *ssl) > { >@@ -1429,6 +1611,9 @@ > { > ap_register_input_filter (ssl_io_filter, ssl_io_filter_input, NULL, AP_FTYPE_CONNECTION + 5); > ap_register_output_filter (ssl_io_filter, ssl_io_filter_output, NULL, AP_FTYPE_CONNECTION + 5); >+ >+ ap_register_input_filter (ssl_io_buffer, ssl_io_filter_buffer, NULL, AP_FTYPE_PROTOCOL - 1); >+ > return; > } > >Index: modules/ssl/ssl_engine_kernel.c >=================================================================== >--- modules/ssl/ssl_engine_kernel.c (revision 291000) >+++ modules/ssl/ssl_engine_kernel.c (working copy) >@@ -490,73 +490,35 @@ > } > #endif /* HAVE_SSL_SET_CERT_STORE */ > >- /* >- * SSL renegotiations in conjunction with HTTP >- * requests using the POST method are not supported. >- * >- * Background: >- * >- * 1. When the client sends a HTTP/HTTPS request, Apache's core code >- * reads only the request line ("METHOD /path HTTP/x.y") and the >- * attached MIME headers ("Foo: bar") up to the terminating line ("CR >- * LF"). An attached request body (for instance the data of a POST >- * method) is _NOT_ read. Instead it is read by mod_cgi's content >- * handler and directly passed to the CGI script. >- * >- * 2. mod_ssl supports per-directory re-configuration of SSL parameters. >- * This is implemented by performing an SSL renegotiation of the >- * re-configured parameters after the request is read, but before the >- * response is sent. In more detail: the renegotiation happens after the >- * request line and MIME headers were read, but _before_ the attached >- * request body is read. The reason simply is that in the HTTP protocol >- * usually there is no acknowledgment step between the headers and the >- * body (there is the 100-continue feature and the chunking facility >- * only), so Apache has no API hook for this step. >- * >- * 3. the problem now occurs when the client sends a POST request for >- * URL /foo via HTTPS the server and the server has SSL parameters >- * re-configured on a per-URL basis for /foo. Then mod_ssl has to >- * perform an SSL renegotiation after the request was read and before >- * the response is sent. But the problem is the pending POST body data >- * in the receive buffer of SSL (which Apache still has not read - it's >- * pending until mod_cgi sucks it in). When mod_ssl now tries to perform >- * the renegotiation the pending data leads to an I/O error. >- * >- * Solution Idea: >- * >- * There are only two solutions: Either to simply state that POST >- * requests to URLs with SSL re-configurations are not allowed, or to >- * renegotiate really after the _complete_ request (i.e. including >- * the POST body) was read. Obviously the latter would be preferred, >- * but it cannot be done easily inside Apache, because as already >- * mentioned, there is no API step between the body reading and the body >- * processing. And even when we mod_ssl would hook directly into the >- * loop of mod_cgi, we wouldn't solve the problem for other handlers, of >- * course. So the only general solution is to suck in the pending data >- * of the request body from the OpenSSL BIO into the Apache BUFF. Then >- * the renegotiation can be done and after this step Apache can proceed >- * processing the request as before. >- * >- * Solution Implementation: >- * >- * We cannot simply suck in the data via an SSL_read-based loop because of >- * HTTP chunking. Instead we _have_ to use the Apache API for this step which >- * is aware of HTTP chunking. So the trick is to suck in the pending request >- * data via the Apache API (which uses Apache's BUFF code and in the >- * background mod_ssl's I/O glue code) and re-inject it later into the Apache >- * BUFF code again. This way the data flows twice through the Apache BUFF, of >- * course. But this way the solution doesn't depend on any Apache specifics >- * and is fully transparent to Apache modules. >- * >- * !! BUT ALL THIS IS STILL NOT RE-IMPLEMENTED FOR APACHE 2.0 !! >+ /* If a renegotiation is now required for this location, and the >+ * request includes a message body (and the client has not >+ * requested a "100 Continue" response), then the client will be >+ * streaming the request body over the wire already. In that >+ * case, it is not possible to stop and perform a new SSL >+ * handshake immediately; once the SSL library moves to the >+ * "accept" state, it will reject the SSL packets which the client >+ * is sending for the request body. >+ * >+ * To allow authentication to complete in this auth hook, the >+ * solution used here is to fill a (bounded) buffer with the >+ * request body, and then to reinject that request body later. > */ >- if (renegotiate && !renegotiate_quick && (r->method_number == M_POST)) { >- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >- "SSL Re-negotiation in conjunction " >- "with POST method not supported! " >- "hint: try SSLOptions +OptRenegotiate"); >+ if (renegotiate && !renegotiate_quick >+ && (apr_table_get(r->headers_in, "transfer-encoding") >+ || (apr_table_get(r->headers_in, "content-length") >+ && strcmp(apr_table_get(r->headers_in, "content-length"), "0"))) >+ && !r->expecting_100) { >+ int rv; > >- return HTTP_METHOD_NOT_ALLOWED; >+ /* Fill the I/O buffer with the request body if possible. */ >+ rv = ssl_io_buffer_fill(r); >+ >+ if (rv) { >+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, >+ "could not buffer message body to allow " >+ "SSL renegotiation to proceed"); >+ return rv; >+ } > } > > /* >Index: modules/ssl/mod_ssl.h >=================================================================== >--- modules/ssl/mod_ssl.h (revision 291000) >+++ modules/ssl/mod_ssl.h (working copy) >@@ -683,6 +683,10 @@ > void ssl_io_filter_register(apr_pool_t *); > long ssl_io_data_cb(BIO *, int, MODSSL_BIO_CB_ARG_TYPE *, int, long, long); > >+/* ssl_io_buffer_fill fills the setaside buffering of the HTTP request >+ * to allow an SSL renegotiation to take place. */ >+int ssl_io_buffer_fill(request_rec *r); >+ > /* PRNG */ > int ssl_rand_seed(server_rec *, apr_pool_t *, ssl_rsctx_t, char *); >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 12355
:
9626
|
11681
|
11736
|
11745
|
16491
| 16495