diff options
author | rcritten <> | 2006-08-09 20:11:45 +0000 |
---|---|---|
committer | rcritten <> | 2006-08-09 20:11:45 +0000 |
commit | a2c56687f17bdefe258c63b17264d80be2cbd44c (patch) | |
tree | 21a4cc83b8f4aeae50778f598faea587d585e91c /nss_engine_io.c | |
parent | ecf3a7e8c544b351ed3923ff01806759e7abbb75 (diff) | |
download | mod_nss-a2c56687f17bdefe258c63b17264d80be2cbd44c.tar.gz mod_nss-a2c56687f17bdefe258c63b17264d80be2cbd44c.tar.xz mod_nss-a2c56687f17bdefe258c63b17264d80be2cbd44c.zip |
Merge in changes from http://svn.apache.org/viewvc?view=rev&revision=290965
Implement a (bounded) buffer of request body data to provide a limited
but safe fix for the mod_nss renegotiation-vs-requests-with-bodies
bug:
* mod_nss.h (nss_io_buffer_fill): Add prototype.
* nss_engine_io.c (nss_io_buffer_fill,
nss_io_filter_buffer): New functions.
* nss_engine_kernel.c (nss_hook_Access): If a renegotiation is needed,
and the request has a non-zero content-length, or a t-e header (and
100-continue was not requested), call nss_io_buffer_fill to set aside
the request body data if possible, then proceed with the negotiation.
PR: 12355
Diffstat (limited to 'nss_engine_io.c')
-rw-r--r-- | nss_engine_io.c | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/nss_engine_io.c b/nss_engine_io.c index 64641d8..8010f89 100644 --- a/nss_engine_io.c +++ b/nss_engine_io.c @@ -602,6 +602,7 @@ static apr_status_t nss_io_filter_error(ap_filter_t *f, } static const char nss_io_filter[] = "NSS SSL/TLS Filter"; +static const char nss_io_buffer[] = "NSS SSL/TLS Buffer"; static apr_status_t nss_filter_io_shutdown(nss_filter_ctx_t *filter_ctx, conn_rec *c, @@ -916,6 +917,180 @@ static void nss_io_output_create(nss_filter_ctx_t *filter_ctx, conn_rec *c) return; } +/* 128K maximum buffer size by default. */ +#ifndef SSL_MAX_IO_BUFFER +#define SSL_MAX_IO_BUFFER (128 * 1024) +#endif + +struct modnss_buffer_ctx { + apr_bucket_brigade *bb; +}; + +int nss_io_buffer_fill(request_rec *r) +{ + conn_rec *c = r->connection; + struct modnss_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. */ + ctx = apr_palloc(r->pool, sizeof *ctx); + ctx->bb = apr_brigade_create(r->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, r->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(nss_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 nss_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 modnss_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); + } + } + 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 nss_io_input_add_filter(nss_filter_ctx_t *filter_ctx, conn_rec *c, PRFileDesc *ssl) { @@ -962,6 +1137,7 @@ void nss_io_filter_register(apr_pool_t *p) { ap_register_input_filter (nss_io_filter, nss_io_filter_input, NULL, AP_FTYPE_CONNECTION + 5); ap_register_output_filter (nss_io_filter, nss_io_filter_output, NULL, AP_FTYPE_CONNECTION + 5); + ap_register_input_filter (nss_io_buffer, nss_io_filter_buffer, NULL, AP_FTYPE_PROTOCOL - 1); return; } |