ssl_engine_io.c revision d58a822aff1dfda25384d3d009f88f1883c95436
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* _ _
* _ __ ___ ___ __| | ___ ___| | mod_ssl
* | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
* | | | | | | (_) | (_| | \__ \__ \ |
* |_| |_| |_|\___/ \__,_|___|___/___/_|
* |_____|
* I/O Functions
*/
/* ``MY HACK: This universe.
Just one little problem:
core keeps dumping.''
-- Unknown */
#include "ssl_private.h"
#include "mod_ssl.h"
#include "apr_date.h"
/* _________________________________________________________________
**
** I/O Hooks
** _________________________________________________________________
*/
/* This file is designed to be the bridge between OpenSSL and httpd.
* However, we really don't expect anyone (let alone ourselves) to
* remember what is in this file. So, first, a quick overview.
*
* In this file, you will find:
* - ssl_io_filter_input (Apache input filter)
* - ssl_io_filter_output (Apache output filter)
*
* - bio_filter_in_* (OpenSSL input filter)
* - bio_filter_out_* (OpenSSL output filter)
*
* The input chain is roughly:
*
* ssl_io_filter_input->ssl_io_input_read->SSL_read->...
* ...->bio_filter_in_read->ap_get_brigade/next-httpd-filter
*
* In mortal terminology, we do the following:
* - Receive a request for data to the SSL input filter
* - Call a helper function once we know we should perform a read
* - Call OpenSSL's SSL_read()
* - SSL_read() will then call bio_filter_in_read
* - bio_filter_in_read will then try to fetch data from the next httpd filter
* - bio_filter_in_read will flatten that data and return it to SSL_read
* - SSL_read will then decrypt the data
* - ssl_io_input_read will then receive decrypted data as a char* and
* ensure that there were no read errors
* - The char* is placed in a brigade and returned
*
* Since connection-level input filters in httpd need to be able to
* handle AP_MODE_GETLINE calls (namely identifying LF-terminated strings),
* ssl_io_input_getline which will handle this special case.
*
* Due to AP_MODE_GETLINE and AP_MODE_SPECULATIVE, we may sometimes have
* 'leftover' decoded data which must be setaside for the next read. That
* is currently handled by the char_buffer_{read|write} functions. So,
* ssl_io_input_read may be able to fulfill reads without invoking
* SSL_read().
*
* Note that the filter context of ssl_io_filter_input and bio_filter_in_*
* are shared as bio_filter_in_ctx_t.
*
* Note that the filter is by choice limited to reading at most
* AP_IOBUFSIZE (8192 bytes) per call.
*
*/
/* this custom BIO allows us to hook SSL_write directly into
* an apr_bucket_brigade and use transient buckets with the SSL
* malloc-ed buffer, rather than copying into a mem BIO.
* also allows us to pass the brigade as data is being written
* rather than buffering up the entire response in the mem BIO.
*
* when SSL needs to flush (e.g. SSL_accept()), it will call BIO_flush()
* which will trigger a call to bio_filter_out_ctrl() -> bio_filter_out_flush().
* so we only need to flush the output ourselves if we receive an
* EOS or FLUSH bucket. this was not possible with the mem BIO where we
* had to flush all over the place not really knowing when it was required
* to do so.
*/
typedef struct {
typedef struct {
conn_rec *c;
conn_rec *c)
{
outctx->c = c;
return outctx;
}
/* Pass an output brigade down the filter stack; returns 1 on success
* or -1 on failure. */
{
/* Fail if the connection was reset: */
}
}
/* Send a FLUSH bucket down the output filter stack; returns 1 on
* success, -1 on failure. */
{
apr_bucket *e;
return bio_filter_out_pass(outctx);
}
{
return 1;
}
{
return 0;
}
/* nothing to free here.
* apache will destroy the bucket brigade for us
*/
return 1;
}
{
/* this is never called */
return -1;
}
{
apr_bucket *e;
/* Abort early if the client has initiated a renegotiation. */
return -1;
}
/* when handshaking we'll have a small number of bytes.
* max size SSL will pass us here is about 16k.
* (16413 bytes to be exact)
*/
/* Use a transient bucket for the output data - any downstream
* filter must setaside if necessary. */
if (bio_filter_out_pass(outctx) < 0) {
return -1;
}
return inl;
}
{
long ret = 1;
switch (cmd) {
case BIO_CTRL_RESET:
case BIO_CTRL_EOF:
"output bio: unhandled control %d", cmd);
ret = 0;
break;
case BIO_CTRL_WPENDING:
case BIO_CTRL_PENDING:
case BIO_CTRL_INFO:
ret = 0;
break;
case BIO_CTRL_GET_CLOSE:
break;
case BIO_CTRL_SET_CLOSE:
break;
case BIO_CTRL_FLUSH:
break;
case BIO_CTRL_DUP:
ret = 1;
break;
/* N/A */
case BIO_C_SET_BUF_MEM:
case BIO_C_GET_BUF_MEM_PTR:
/* we don't care */
case BIO_CTRL_PUSH:
case BIO_CTRL_POP:
default:
ret = 0;
break;
}
return ret;
}
{
/* this is never called */
return -1;
}
{
/* this is never called */
return -1;
}
static BIO_METHOD bio_filter_out_method = {
"APR output filter",
bio_filter_out_read, /* read is never called */
bio_filter_out_puts, /* puts is never called */
bio_filter_out_gets, /* gets is never called */
};
typedef struct {
int length;
char *value;
typedef struct {
ap_filter_t *f;
char buffer[AP_IOBUFSIZE];
int npn_finished; /* 1 if NPN has finished, 0 otherwise */
/*
* this char_buffer api might seem silly, but we don't need to copy
* any of this data and we need to remember the length.
*/
/* Copy up to INL bytes from the char_buffer BUFFER into IN. Note
* char_buffer object is used to cache a segment of inctx->buffer, and
* then this function called to copy (part of) that segment to the
* beginning of inctx->buffer. So the segments to copy cannot be
* presumed to be non-overlapping, and memmove must be used. */
{
return 0;
}
/* we have have enough to fill the caller's buffer */
}
else {
/* swallow remainder of the buffer */
}
return inl;
}
{
return inl;
}
/* This function will read from a brigade and discard the read buckets as it
* proceeds. It will read at most *len bytes.
*/
char *c, apr_size_t *len)
{
apr_size_t actual = 0;
while (!APR_BRIGADE_EMPTY(bb)) {
const char *str;
/* Justin points out this is an http-ism that might
* not fit if brigade_consume is added to APR. Perhaps
* apr_bucket_read(eos_bucket) should return APR_EOF?
* Then this becomes mainline instead of a one-off.
*/
if (APR_BUCKET_IS_EOS(b)) {
break;
}
/* The reason I'm not offering brigade_consume yet
* across to apr-util is that the following call
* illustrates how borked that API really is. For
* this sort of case (caller provided buffer) it
* would be much more trivial for apr_bucket_consume
* to do all the work that follows, based on the
* particular characteristics of the bucket we are
* consuming here.
*/
if (status != APR_SUCCESS) {
if (APR_STATUS_IS_EOF(status)) {
/* This stream bucket was consumed */
continue;
}
break;
}
if (str_len > 0) {
/* Do not block once some data has been consumed */
/* Assure we don't overflow. */
c += consume;
/* This physical bucket was consumed */
}
else {
/* Only part of this physical bucket was consumed */
}
}
else if (b->length == 0) {
}
/* This could probably be actual == *len, but be safe from stray
* photons. */
break;
}
}
return status;
}
/*
* this is the function called by SSL_read()
*/
{
/* OpenSSL catches this case, so should we. */
if (!in)
return 0;
/* Abort early if the client has initiated a renegotiation. */
return -1;
}
/* In theory, OpenSSL should flush as necessary, but it is known
* not to do so correctly in some cases; see PR 46952.
*
* Historically, this flush call was performed only for an SSLv2
* connection or for a proxy connection. Calling _out_flush
* should be very cheap in cases where it is unnecessary (and no
* output is buffered) so the performance impact of doing it
* unconditionally should be minimal.
*/
return -1;
}
return -1;
}
inl);
/* If the read returns EAGAIN or success with an empty
* brigade, return an error after setting the retry flag;
* SSL_read() will then return -1, and SSL_get_error() will
* indicate SSL_ERROR_WANT_READ. */
return -1;
}
/* Unexpected errors discard the brigade */
return -1;
}
}
return (int)inl;
}
return (int)inl;
}
/* Unexpected errors and APR_EOF clean out the brigade.
* Subsequent calls will return APR_EOF.
*/
/* Provide the results of this read pass,
* without resetting the BIO retry_read flag
*/
return (int)inl;
}
return -1;
}
static BIO_METHOD bio_filter_in_method = {
"APR input filter",
NULL, /* write is never called */
NULL, /* puts is never called */
NULL, /* gets is never called */
NULL, /* ctrl is never called */
};
char *buf,
{
apr_size_t bytes = 0;
int rc;
*len = 0;
/* If we have something leftover from last time, try that first. */
/* We want to rollback this read. */
} else {
}
return APR_SUCCESS;
}
/* This could probably be *len == wanted, but be safe from stray
* photons.
*/
return APR_SUCCESS;
}
return APR_SUCCESS;
}
}
else {
/* Down to a nonblock pattern as we have some data already
*/
}
}
while (1) {
/* Ensure a non-zero error code is returned */
}
break;
}
/* SSL_read may not read because we haven't taken enough data
* from the stack. This is where we want to consider all of
* the blocking and SPECULATIVE semantics
*/
if (rc > 0) {
/* We want to rollback this read. */
}
}
else if (rc == 0) {
/* If EAGAIN, we will loop given a blocking read,
* otherwise consider ourselves at EOF.
*/
/* Already read something, return APR_SUCCESS instead.
* On win32 in particular, but perhaps on other kernels,
* a blocking call isn't 'always' blocking.
*/
if (*len > 0) {
break;
}
break;
}
}
else {
if (*len > 0) {
}
else {
}
break;
}
}
else /* (rc < 0) */ {
if (ssl_err == SSL_ERROR_WANT_READ) {
/*
* If OpenSSL wants to read more, and we were nonblocking,
* report as an EAGAIN. Otherwise loop, pulling more
* data from network filter.
*
* (This is usually the case when the client forces an SSL
* renegotiation which is handled implicitly by OpenSSL.)
*/
if (*len > 0) {
break;
}
break;
}
continue; /* Blocking and nothing yet? Try again. */
}
else if (ssl_err == SSL_ERROR_SYSCALL) {
/* Already read something, return APR_SUCCESS instead. */
if (*len > 0) {
break;
}
break;
}
continue; /* Blocking and nothing yet? Try again. */
}
else {
"SSL input filter read failed.");
}
}
else /* if (ssl_err == SSL_ERROR_SSL) */ {
/*
* Log SSL errors and any unexpected conditions.
*/
"SSL library error %d reading data", ssl_err);
}
}
break;
}
}
}
/* Read a line of input from the SSL input layer into buffer BUF of
* length *LEN; updating *len to reflect the length of the line
* including the LF character. */
char *buf,
{
*len = 0;
/*
* in most cases we get all the headers on the first SSL_read.
* however, in certain cases SSL_read will only get a partial
* chunk of the headers, so we try to read until LF is seen.
*/
while (tmplen > 0) {
if (status != APR_SUCCESS) {
/* Save the part of the line we already got */
}
return status;
}
break;
}
}
if (pos) {
char *value;
int length;
bytes += 1;
}
return APR_SUCCESS;
}
const char *data,
{
int res;
/* write SSL */
return APR_EGENERAL;
}
if (res < 0) {
if (ssl_err == SSL_ERROR_WANT_WRITE) {
/*
* If OpenSSL wants to write more, and we were nonblocking,
* report as an EAGAIN. Otherwise loop, pushing more
* data at the network filter.
*
* (This is usually the case when the client forces an SSL
* renegotiation which is handled implicitly by OpenSSL.)
*/
}
else if (ssl_err == SSL_ERROR_SYSCALL) {
"SSL output filter write failed.");
}
else /* if (ssl_err == SSL_ERROR_SSL) */ {
/*
* Log SSL errors
*/
"SSL library error %d writing data", ssl_err);
}
}
}
conn_rec *c = f->c;
char *reason = "reason unknown";
/* XXX: probably a better way to determine this */
reason = "likely due to failed renegotiation";
}
"failed to write %" APR_SSIZE_T_FMT
}
}
/* Just use a simple request. Any request will work for this, because
* we use a flag in the conn_rec->conn_vector now. The fake request just
* gets the request back to the Apache core so that a response can be sent.
* Since we use an HTTP/1.x request, we also have to inject the empty line
* that terminates the headers, or the core will read more data from the
* socket.
*/
#define HTTP_ON_HTTPS_PORT \
"GET / HTTP/1.0" CRLF
#define HTTP_ON_HTTPS_PORT_BUCKET(alloc) \
sizeof(HTTP_ON_HTTPS_PORT) - 1, \
/* Custom apr_status_t error code, used when a plain HTTP request is
* recevied on an SSL port. */
#define MODSSL_ERROR_HTTP_ON_HTTPS (APR_OS_START_USERERR + 0)
/* Custom apr_status_t error code, used when the proxy cannot
* establish an outgoing SSL connection. */
{
}
{
int send_eos = 1;
switch (status) {
/* log the situation */
"SSL handshake failed: HTTP spoken on HTTPS port; "
"trying to send HTML error page");
/* fake the request line */
send_eos = 0;
break;
case MODSSL_ERROR_BAD_GATEWAY:
/* Send an error bucket, though the proxy currently has no
* special handling for error buckets and ignores this. */
f->c->pool,
f->c->bucket_alloc);
"SSL handshake failed: sending 502");
break;
default:
return status;
}
if (send_eos) {
}
return APR_SUCCESS;
}
static const char ssl_io_filter[] = "SSL/TLS Filter";
static const char ssl_io_buffer[] = "SSL/TLS Buffer";
static const char ssl_io_coalesce[] = "SSL/TLS Coalescing Filter";
/*
* Close the SSL part of the socket connection
* (called immediately _before_ the socket is closed)
* or called with
*/
{
const char *type = "";
int shutdown_type;
int loglevel = APLOG_DEBUG;
const char *logno;
if (!ssl) {
return;
}
/*
* Now close the SSL layer of the connection. We've to take
* the TLSv1 standard into account here:
*
* | 7.2.1. Closure alerts
* |
* | The client and the server must share knowledge that the connection is
* | ending in order to avoid a truncation attack. Either party may
* | initiate the exchange of closing messages.
* |
* | close_notify
* | This message notifies the recipient that the sender will not send
* | any more messages on this connection. The session becomes
* | unresumable if any connection is terminated without proper
* | close_notify messages with level equal to warning.
* |
* | Either party may initiate a close by sending a close_notify alert.
* | Any data received after a closure alert is ignored.
* |
* | Each party is required to send a close_notify alert before closing
* | the write side of the connection. It is required that the other party
* | respond with a close_notify alert of its own and close down the
* | connection immediately, discarding any pending writes. It is not
* | required for the initiator of the close to wait for the responding
* | close_notify alert before closing the read side of the connection.
*
* This means we've to send a close notify message, but haven't to wait
* for the close notify of the client. Actually we cannot wait for the
* close notify of the client because some clients (including Netscape
* 4.x) don't send one, so we would hang.
*/
/*
* exchange close notify messages, but allow the user
* to force the type of handshake via SetEnvIf directive
*/
if (abortive) {
type = "abortive";
}
else switch (sslconn->shutdown_type) {
/* perform no close notify handshake at all
type = "unclean";
break;
/* send close notify and wait for clients close notify
(standard compliant, but usually causes connection hangs) */
shutdown_type = 0;
type = "accurate";
break;
default:
/*
* case SSL_SHUTDOWN_TYPE_UNSET:
* case SSL_SHUTDOWN_TYPE_STANDARD:
*/
/* send close notify, but don't wait for clients close notify
(standard compliant and safe, so it's the DEFAULT!) */
type = "standard";
break;
}
/* and finally log the fact that we've closed the connection */
"%sConnection closed to child %ld with %s shutdown "
"(server %s)",
}
/* deallocate the SSL connection */
if (sslconn->client_cert) {
}
if (abortive) {
/* prevent any further I/O */
c->aborted = 1;
}
}
{
if (filter_ctx->pssl) {
}
return APR_SUCCESS;
}
/*
* The hook is NOT registered with ap_hook_process_connection. Instead, it is
* called manually from the churn () before it tries to read any data.
* There is some problem if I accept conn_rec *. Still investigating..
* Adv. if conn_rec * can be accepted is we can hook this function using the
* ap_hook_process_connection hook.
*/
/* Perform the SSL handshake (whether in client or server mode), if
* necessary, for the given connection. */
{
int n;
int ssl_err;
long verify_result;
return APR_SUCCESS;
}
#ifndef OPENSSL_NO_TLSEXT
#endif
"proxy-request-hostname");
#ifndef OPENSSL_NO_TLSEXT
/*
* Enable SNI for backend requests. Make sure we don't do it for
* pure SSLv3 connections, and also prevent IP addresses
* from being included in the SNI extension. (OpenSSL would simply
* pass them on, but RFC 6066 is quite clear on this: "Literal
* IPv4 and IPv6 addresses are not permitted".)
*/
if (hostname_note &&
c->pool) != APR_SUCCESS) {
"SNI extension for SSL Proxy request set to '%s'",
} else {
"Failed to set SNI extension for SSL Proxy "
"request to '%s'", hostname_note);
}
}
#endif
"SSL Proxy connect failed");
/* ensure that the SSL structures etc are freed, etc: */
return MODSSL_ERROR_BAD_GATEWAY;
}
if (!cert
X509_get_notBefore(cert)) >= 0)
X509_get_notAfter(cert)) <= 0)) {
"SSL Proxy: Peer certificate is expired");
}
}
if (!cert
"SSL Proxy: Peer certificate does not match "
"for hostname %s", hostname_note);
}
}
const char *hostname;
int match = 0;
"SSL_CLIENT_S_DN_CN");
/* Do string match or simplest wildcard match if that
* fails. */
}
if (!match) {
"SSL Proxy: Peer certificate CN mismatch:"
" Certificate CN: %s Requested hostname: %s",
}
}
if (cert) {
}
if (proxy_ssl_check_peer_ok != TRUE) {
/* ensure that the SSL structures etc are freed, etc: */
return HTTP_BAD_GATEWAY;
}
return APR_SUCCESS;
}
if (ssl_err == SSL_ERROR_ZERO_RETURN) {
/*
* The case where the connection was closed before any data
* was transferred. That's not a real error and can occur
* sporadically with some clients.
*/
"SSL handshake stopped: connection was closed");
}
else if (ssl_err == SSL_ERROR_WANT_READ) {
/*
* This is in addition to what was present earlier. It is
* borrowed from openssl_state_machine.c [mod_tls].
* TBD.
*/
return APR_EAGAIN;
}
/*
* The case where OpenSSL has recognized a HTTP request:
* This means the client speaks plain HTTP on our HTTPS port.
* ssl_io_filter_error will disable the ssl filters when it
* sees this status code.
*/
return MODSSL_ERROR_HTTP_ON_HTTPS;
}
else if (ssl_err == SSL_ERROR_SYSCALL) {
"SSL handshake interrupted by system "
"[Hint: Stop button pressed in browser?!]");
}
else /* if (ssl_err == SSL_ERROR_SSL) */ {
/*
* Log SSL errors and any unexpected conditions.
*/
"SSL library error %d in handshake "
"(server %s)", ssl_err,
}
}
}
/*
* Check for failed client authentication
*/
if ((verify_result != X509_V_OK) ||
{
{
/* leaving this log message as an error for the moment,
* according to the mod_ssl docs:
* "level optional_no_ca is actually against the idea
* of authentication (but can be used to establish
* SSL test pages, etc.)"
* optional_no_ca doesn't appear to work as advertised
* in 1.x
*/
"SSL client authentication failed, "
"accepting certificate based on "
"\"SSLVerifyClient optional_no_ca\" "
"configuration");
}
else {
"SSL client authentication failed: %s",
return APR_ECONNABORTED;
}
}
/*
* Remember the peer certificate's DN
*/
if (sslconn->client_cert) {
}
}
/*
* Make really sure that when a peer certificate
* is required we really got one... (be paranoid)
*/
{
"No acceptable peer certificate available");
return APR_ECONNABORTED;
}
return APR_SUCCESS;
}
{
if (f->c->aborted) {
/* XXX: Ok, if we aborted, we ARE at the EOS. We also have
* aborted. This 'double protection' is probably redundant,
* but also effective against just about anything.
*/
return APR_ECONNABORTED;
}
return APR_SUCCESS;
}
}
/* XXX: we don't currently support anything other than these modes. */
return APR_ENOTIMPL;
}
/* XXX: we could actually move ssl_io_filter_handshake to an
* ap_hook_process_connection but would still need to call it for
* AP_MODE_INIT for protocols that may upgrade the connection
* rather than have SSLEngine On configured.
*/
}
if (is_init) {
/* protocol module needs to handshake before sending
* data to client (e.g. NNTP or FTP)
*/
return APR_SUCCESS;
}
/* Protected from truncation, readbytes < MAX_SIZE_T
* FIXME: No, it's *not* protected. -- jre */
}
}
const char *pos;
/* Satisfy the read directly out of the buffer if possible;
* invoking ssl_io_input_getline will mean the entire buffer
* is copied once (unnecessarily) for each GETLINE call. */
/* Buffer contents now consumed. */
}
else {
/* Otherwise fall back to the hard way. */
}
}
else {
/* We have no idea what you are talking about, so return an error. */
}
/* It is possible for mod_ssl's BIO to be used outside of the
* direct control of mod_ssl's input or output filter -- notably,
* when mod_ssl initiates a renegotiation. Switching the BIO mode
* back to "blocking" here ensures such operations don't fail with
* SSL_ERROR_WANT_READ. */
/* Handle custom errors. */
if (status != APR_SUCCESS) {
}
/* Create a transient bucket out of the decrypted data. */
if (len > 0) {
apr_bucket *bucket =
}
#ifdef HAVE_TLS_NPN
/* By this point, Next Protocol Negotiation (NPN) should be completed (if
* our version of OpenSSL supports it). If we haven't already, find out
* which protocol was decided upon and inform other modules by calling
* npn_proto_negotiated_hook. */
if (!inctx->npn_finished) {
const unsigned char *next_proto = NULL;
unsigned next_proto_len = 0;
next_proto_len, (const char*)next_proto);
f->c, (const char*)next_proto, next_proto_len);
}
#endif
return APR_SUCCESS;
}
* passed down the output filter stack. This results in a high
* overhead (network packets) for any output comprising many small
* buckets. SSI page applied through the HTTP chunk filter, for
* example, may produce many brigades containing small buckets -
* [chunk-size CRLF] [chunk-data] [CRLF].
*
* The coalescing filter merges many small buckets into larger buckets
* where possible, allowing the SSL I/O output filter to handle them
* more efficiently. */
#define COALESCE_BYTES (2048)
struct coalesce_ctx {
char buffer[COALESCE_BYTES];
};
{
apr_size_t bytes = 0;
unsigned count = 0;
/* The brigade consists of zero-or-more small data buckets which
* can be coalesced (the prefix), followed by the remainder of the
* brigade.
*
* Find the last bucket - if any - of that prefix. count gives
* the number of buckets in the prefix. The "prefix" must contain
* only data buckets with known length, and must be of a total
* size which fits into the buffer.
*
* N.B.: The process here could be repeated throughout the brigade
* (coalesce any run of consecutive data buckets) but this would
* add significant complexity, particularly to memory
* management. */
for (e = APR_BRIGADE_FIRST(bb);
e != APR_BRIGADE_SENTINEL(bb)
&& !APR_BUCKET_IS_METADATA(e)
&& e->length < COALESCE_BYTES
e = APR_BUCKET_NEXT(e)) {
last = e;
}
/* Coalesce the prefix, if:
* a) more than one bucket is found to coalesce, or
* b) the brigade contains only a single data bucket, or
* c)
*/
if (bytes > 0
&& (count > 1
/* If coalescing some bytes, ensure a context has been
* created. */
if (!ctx) {
}
/* Iterate through the prefix segment. For non-fatal errors
* in this loop it is safe to break out and fall back to the
* normal path of sending the buffer + remaining buckets in
* brigade. */
e = APR_BRIGADE_FIRST(bb);
while (e != last) {
const char *data;
if (APR_BUCKET_IS_METADATA(e)
"unexpected bucket type during coalesce");
break; /* non-fatal error; break out */
}
if (e->length) {
/* A blocking read should be fine here for a
* known-length data bucket, rather than the usual
if (rv) {
"coalesce failed to read from data bucket");
return AP_FILTER_ERROR;
}
/* Be paranoid. */
"unexpected coalesced bucket data length");
break; /* non-fatal error; break out */
}
}
next = APR_BUCKET_NEXT(e);
e = next;
}
}
if (APR_BRIGADE_EMPTY(bb)) {
/* If the brigade is now empty, our work here is done. */
return APR_SUCCESS;
}
/* If anything remains in the brigade, it must now be passed down
* the filter stack, first prepending anything that has been
* coalesced. */
apr_bucket *e;
}
}
{
if (f->c->aborted) {
return APR_ECONNABORTED;
}
if (!filter_ctx->pssl) {
/* ssl_filter_io_shutdown was called */
}
/* When we are the writer, we must initialize the inctx
* mode so that we block for any required ssl input, because
* output filtering is always nonblocking.
*/
}
while (!APR_BRIGADE_EMPTY(bb)) {
/* If it is a flush or EOS, we need to pass this down.
* These types do not require translation by OpenSSL.
*/
break;
}
if (APR_BUCKET_IS_EOS(bucket)) {
/*
* By definition, nothing can come after EOS.
* which also means we can pass the rest of this brigade
* without creating a new one since it only contains the
* EOS bucket.
*/
return status;
}
break;
}
else {
/* bio_filter_out_flush() already passed down a flush bucket
* if there was any data to be flushed.
*/
}
}
else if (AP_BUCKET_IS_EOC(bucket)) {
/* The EOC bucket indicates connection closure, so SSL
* shutdown must now be performed. */
ssl_filter_io_shutdown(filter_ctx, f->c, 0);
return status;
}
break;
}
else {
/* filter output */
const char *data;
if (APR_STATUS_IS_EAGAIN(status)) {
/* No data available: flush... */
break;
}
continue; /* and try again with a blocking read. */
}
break;
}
if (status != APR_SUCCESS) {
break;
}
}
}
return status;
}
struct modssl_buffer_ctx {
};
{
conn_rec *c = r->connection;
struct modssl_buffer_ctx *ctx;
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. */
/* ... and a temporary brigade. */
do {
apr_bucket *e, *next;
/* The request body is read from the protocol-level input
* filters; the buffering filter will reinject it from that
* necessary. */
APR_BLOCK_READ, 8192);
if (rv) {
"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);
const char *data;
next = APR_BUCKET_NEXT(e);
if (APR_BUCKET_IS_EOS(e)) {
eos = 1;
} else if (!APR_BUCKET_IS_METADATA(e)) {
if (rv != APR_SUCCESS) {
"could not read bucket for SSL buffer");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
if (rv != APR_SUCCESS) {
"could not setaside bucket for SSL buffer");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
/* Fail if this exceeds the maximum buffer size. */
"request body exceeds maximum size (%" APR_SIZE_T_FMT
") for SSL buffer", maxlen);
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
} while (!eos);
/* After consuming all protocol-level input, remove all protocol-level
* filters. It should strictly only be necessary to remove filters
* at exactly ftype == AP_FTYPE_PROTOCOL, since this filter will
* precede all > AP_FTYPE_PROTOCOL anyway. */
}
/* Insert the filter which will supply the buffered content. */
return 0;
}
/* This input filter supplies the buffered request body to the caller
* from the brigade stored in f->ctx. Note that the placement of this
* filter in the filter stack is important; it must be the first
* r->proto_input_filter; lower-typed filters will not be preserved
* across internal redirects (see PR 43738). */
{
"read from buffered SSL brigade, mode %d, "
return APR_ENOTIMPL;
}
/* Suprisingly (and perhaps, wrongly), the request body can be
* pulled from the input filter stack more than once; a
* handler may read it, and ap_discard_request_body() will
* attempt to do so again after *every* request. So input
* filters must be prepared to give up an EOS if invoked after
* initially reading the request. The HTTP_IN filter does this
* with its ->eos_sent flag. */
return APR_SUCCESS;
}
if (mode == AP_MODE_READBYTES) {
apr_bucket *e;
/* Partition the buffered brigade. */
"could not partition buffered SSL brigade");
return rv;
}
/* If the buffered brigade contains less then the requested
* length, just pass it all back. */
if (rv == APR_INCOMPLETE) {
} else {
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);
}
}
else {
/* Split a line into the passed-in brigade. */
if (rv) {
"could not split line from buffered SSL brigade");
return rv;
}
}
/* Ensure that the brigade is terminated by an EOS if the
* buffered request body has been entirely consumed. */
e = apr_bucket_eos_create(f->c->bucket_alloc);
}
"buffered SSL brigade exhausted");
/* Note that the filter must *not* be removed here; it may be
* invoked again, see comment above. */
}
return APR_SUCCESS;
}
/* The request_rec pointer is passed in here only to ensure that the
* filter chain is modified correctly when doing a TLS upgrade. It
* must *not* be used otherwise. */
{
inctx->npn_finished = 0;
}
/* The request_rec pointer is passed in here only to ensure that the
* filter chain is modified correctly when doing a TLS upgrade. It
* must *not* be used otherwise. */
{
filter_ctx, r, c);
/* We insert a clogging input filter. Let the core know. */
c->clogging_input_filters = 1;
}
return;
}
void ssl_io_filter_register(apr_pool_t *p)
{
return;
}
/* _________________________________________________________________
**
** I/O Data Debugging
** _________________________________________________________________
*/
#define DUMP_WIDTH 16
const char *b, long len)
{
char buf[256];
char tmp[64];
unsigned char ch;
trunc = 0;
trunc++;
rows++;
"+-------------------------------------------------------------------------+");
for(i = 0 ; i< rows; i++) {
char ebcdic_text[DUMP_WIDTH];
j = DUMP_WIDTH;
if ((i * DUMP_WIDTH + j) > len)
j = len % DUMP_WIDTH;
if (j == 0)
j = DUMP_WIDTH;
#endif /* APR_CHARSET_EBCDIC */
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len)
else {
}
}
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len)
else {
#else /* APR_CHARSET_EBCDIC */
#endif /* APR_CHARSET_EBCDIC */
}
}
}
if (trunc > 0)
"+-------------------------------------------------------------------------+");
return;
}
const char *argp,
{
conn_rec *c;
server_rec *s;
return rc;
return rc;
s = mySrvFromConn(c);
if (rc >= 0) {
const char *dump = "";
if (APLOG_CS_IS_LEVEL(c, s, APLOG_TRACE7)) {
dump = "(BIO dump follows)";
else
dump = "(Oops, no memory buffer?)";
}
"%s: %s %ld/%d bytes %s BIO#%pp [mem: %pp] %s",
}
else {
"%s: I/O error, %d bytes expected to %s on BIO#%pp [mem: %pp]",
}
}
return rc;
}