/* 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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.
*/
/* 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.
*/
typedefstruct {
SSL *pssl;
BIO *pbioRead;
BIO *pbioWrite;
ap_filter_t *pInputFilter;
ap_filter_t *pOutputFilter;
SSLConnRec *config;
} ssl_filter_ctx_t;
typedefstruct {
ssl_filter_ctx_t *filter_ctx;
conn_rec *c;
apr_bucket_brigade *bb; /* Brigade used as a buffer. */
apr_status_t rc;
} bio_filter_out_ctx_t;
/* Pass an output brigade down the filter stack; returns 1 on success
* or -1 on failure. */ staticint bio_filter_out_pass(bio_filter_out_ctx_t *outctx)
{
AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(outctx->bb));
outctx->rc = ap_pass_brigade(outctx->filter_ctx->pOutputFilter->next,
outctx->bb); /* Fail if the connection was reset: */ if (outctx->rc == APR_SUCCESS && outctx->c->aborted) {
outctx->rc = APR_ECONNRESET;
} return (outctx->rc == APR_SUCCESS) ? 1 : -1;
}
/* Send a FLUSH bucket down the output filter stack; returns 1 on
* success, -1 on failure. */ staticint bio_filter_out_flush(BIO *bio)
{
bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
apr_bucket *e;
e = apr_bucket_flush_create(outctx->bb->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(outctx->bb, e);
return bio_filter_out_pass(outctx);
}
staticint bio_filter_create(BIO *bio)
{
BIO_set_shutdown(bio, 1);
BIO_set_init(bio, 1); #if MODSSL_USE_OPENSSL_PRE_1_1_API /* No setter method for OpenSSL 1.1.0 available, * but I can't find any functional use of the * "num" field there either.
*/
bio->num = -1; #endif
BIO_set_data(bio, NULL);
/* nothing to free here. * apache will destroy the bucket brigade for us
*/ return 1;
}
staticint bio_filter_out_read(BIO *bio, char *out, int outl)
{ /* this is never called */
bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, outctx->c, "BUG: %s() should not be called", "bio_filter_out_read");
AP_DEBUG_ASSERT(0); return -1;
}
staticint bio_filter_out_write(BIO *bio, constchar *in, int inl)
{
bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
apr_bucket *e; int need_flush;
BIO_clear_retry_flags(bio);
#ifndef SSL_OP_NO_RENEGOTIATION /* Abort early if the client has initiated a renegotiation. */ if (outctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
outctx->rc = APR_ECONNABORTED; return -1;
} #endif
/* Use a transient bucket for the output data - any downstream
* filter must setaside if necessary. */
e = apr_bucket_transient_create(in, inl, outctx->bb->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(outctx->bb, e);
/* In theory, OpenSSL should flush as necessary, but it is known * not to do so correctly in some cases (< 0.9.8m; see PR 46952), * or on the proxy/client side (after ssl23_client_hello(), e.g. * ssl/proxy.t test suite). * * Historically, this flush call was performed only for an SSLv2 * connection or for a proxy connection. Calling _out_flush can * be expensive in cases where requests/responses are pipelined, * so limit the performance impact to handshake time.
*/ #if OPENSSL_VERSION_NUMBER < 0x0009080df
need_flush = !SSL_is_init_finished(outctx->filter_ctx->pssl); #else
need_flush = SSL_in_connect_init(outctx->filter_ctx->pssl); #endif if (need_flush) {
e = apr_bucket_flush_create(outctx->bb->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(outctx->bb, e);
}
if (bio_filter_out_pass(outctx) < 0) { return -1;
}
return inl;
}
staticlong bio_filter_out_ctrl(BIO *bio, int cmd, long num, void *ptr)
{ long ret = 1;
bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
switch (cmd) { case BIO_CTRL_RESET: case BIO_CTRL_EOF: case BIO_C_SET_BUF_MEM_EOF_RETURN:
ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, outctx->c, "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:
ret = (long)BIO_get_shutdown(bio); break; case BIO_CTRL_SET_CLOSE:
BIO_set_shutdown(bio, (int)num); break; case BIO_CTRL_FLUSH:
ret = bio_filter_out_flush(bio); 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;
}
staticint bio_filter_out_gets(BIO *bio, char *buf, int size)
{ /* this is never called */
bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, outctx->c, "BUG: %s() should not be called", "bio_filter_out_gets");
AP_DEBUG_ASSERT(0); return -1;
}
staticint bio_filter_out_puts(BIO *bio, constchar *str)
{ /* this is never called */
bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)BIO_get_data(bio);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, outctx->c, "BUG: %s() should not be called", "bio_filter_out_puts");
AP_DEBUG_ASSERT(0); return -1;
}
typedefstruct { int length; char *value;
} char_buffer_t;
/* * 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 * that due to the strange way this API is designed/used, the * 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. */ staticint char_buffer_read(char_buffer_t *buffer, char *in, int inl)
{ if (!buffer->length) { return 0;
}
if (buffer->length > inl) { /* we have enough to fill the caller's buffer */
memmove(in, buffer->value, inl);
buffer->value += inl;
buffer->length -= inl;
} else { /* swallow remainder of the buffer */
memmove(in, buffer->value, buffer->length);
inl = buffer->length;
buffer->value = NULL;
buffer->length = 0;
}
/* This function will read from a brigade and discard the read buckets as it * proceeds. It will read at most *len bytes.
*/ static apr_status_t brigade_consume(apr_bucket_brigade *bb,
apr_read_type_e block, char *c, apr_size_t *len)
{
apr_size_t actual = 0;
apr_status_t status = APR_SUCCESS;
/* 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)) {
status = APR_EOF; 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.
*/
status = apr_bucket_read(b, &str, &str_len, block);
if (status != APR_SUCCESS) { if (APR_STATUS_IS_EOF(status)) { /* This stream bucket was consumed */
apr_bucket_delete(b); continue;
} break;
}
if (str_len > 0) { /* Do not block once some data has been consumed */
block = APR_NONBLOCK_READ;
/* Assure we don't overflow. */
consume = (str_len + actual > *len) ? *len - actual : str_len;
memcpy(c, str, consume);
c += consume;
actual += consume;
if (consume >= b->length) { /* This physical bucket was consumed */
apr_bucket_delete(b);
} else { /* Only part of this physical bucket was consumed */
b->start += consume;
b->length -= consume;
}
} elseif (b->length == 0) {
apr_bucket_delete(b);
}
/* This could probably be actual == *len, but be safe from stray
* photons. */ if (actual >= *len) { break;
}
}
*len = actual; return status;
}
/* * this is the function called by SSL_read()
*/ staticint bio_filter_in_read(BIO *bio, char *in, int inlen)
{
apr_size_t inl = inlen;
bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
apr_read_type_e block = inctx->block;
inctx->rc = APR_SUCCESS;
/* OpenSSL catches this case, so should we. */ if (!in) return 0;
BIO_clear_retry_flags(bio);
#ifndef SSL_OP_NO_RENEGOTIATION /* Abort early if the client has initiated a renegotiation. */ if (inctx->filter_ctx->config->reneg_state == RENEG_ABORT) {
inctx->rc = APR_ECONNABORTED; return -1;
} #endif
if (!inctx->bb) {
inctx->rc = APR_EOF; return -1;
}
/* 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. */ if (APR_STATUS_IS_EAGAIN(inctx->rc) || APR_STATUS_IS_EINTR(inctx->rc)
|| (inctx->rc == APR_SUCCESS && APR_BRIGADE_EMPTY(inctx->bb))) {
BIO_set_retry_read(bio); return -1;
}
if (block == APR_BLOCK_READ
&& APR_STATUS_IS_TIMEUP(inctx->rc)
&& APR_BRIGADE_EMPTY(inctx->bb)) { /* don't give up, just return the timeout */ return -1;
} if (inctx->rc != APR_SUCCESS) { /* Unexpected errors discard the brigade */
apr_brigade_cleanup(inctx->bb);
inctx->bb = NULL; return -1;
}
}
inctx->rc = brigade_consume(inctx->bb, block, in, &inl);
if (inctx->rc == APR_SUCCESS) { return (int)inl;
}
if (APR_STATUS_IS_EAGAIN(inctx->rc)
|| APR_STATUS_IS_EINTR(inctx->rc)) {
BIO_set_retry_read(bio); return (int)inl;
}
/* Unexpected errors and APR_EOF clean out the brigade. * Subsequent calls will return APR_EOF.
*/
apr_brigade_cleanup(inctx->bb);
inctx->bb = NULL;
if (APR_STATUS_IS_EOF(inctx->rc) && inl) { /* Provide the results of this read pass, * without resetting the BIO retry_read flag
*/ return (int)inl;
}
return -1;
}
staticint bio_filter_in_write(BIO *bio, constchar *in, int inl)
{
bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c, "BUG: %s() should not be called", "bio_filter_in_write");
AP_DEBUG_ASSERT(0); return -1;
}
staticint bio_filter_in_puts(BIO *bio, constchar *str)
{
bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c, "BUG: %s() should not be called", "bio_filter_in_puts");
AP_DEBUG_ASSERT(0); return -1;
}
staticint bio_filter_in_gets(BIO *bio, char *buf, int size)
{
bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio);
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c, "BUG: %s() should not be called", "bio_filter_in_gets");
AP_DEBUG_ASSERT(0); return -1;
}
staticlong bio_filter_in_ctrl(BIO *bio, int cmd, long num, void *ptr)
{
bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)BIO_get_data(bio); switch (cmd) { #ifdef BIO_CTRL_EOF case BIO_CTRL_EOF: return inctx->rc == APR_EOF; #endif default: break;
}
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, inctx->f->c, "BUG: bio_filter_in_ctrl() should not be called with cmd=%i",
cmd); return 0;
}
#if MODSSL_USE_OPENSSL_PRE_1_1_API
static BIO_METHOD bio_filter_out_method = {
BIO_TYPE_MEM, "APR output filter",
bio_filter_out_write,
bio_filter_out_read, /* read is never called */
bio_filter_out_puts, /* puts is never called */
bio_filter_out_gets, /* gets is never called */
bio_filter_out_ctrl,
bio_filter_create,
bio_filter_destroy,
NULL
};
static BIO_METHOD bio_filter_in_method = {
BIO_TYPE_MEM, "APR input filter",
bio_filter_in_write, /* write is never called */
bio_filter_in_read,
bio_filter_in_puts, /* puts is never called */
bio_filter_in_gets, /* gets is never called */
bio_filter_in_ctrl, /* ctrl is called for EOF check */
bio_filter_create,
bio_filter_destroy,
NULL
};
void init_bio_methods(void)
{
bio_filter_out_method = BIO_meth_new(BIO_TYPE_MEM, "APR output filter");
BIO_meth_set_write(bio_filter_out_method, &bio_filter_out_write);
BIO_meth_set_read(bio_filter_out_method, &bio_filter_out_read); /* read is never called */
BIO_meth_set_puts(bio_filter_out_method, &bio_filter_out_puts); /* puts is never called */
BIO_meth_set_gets(bio_filter_out_method, &bio_filter_out_gets); /* gets is never called */
BIO_meth_set_ctrl(bio_filter_out_method, &bio_filter_out_ctrl);
BIO_meth_set_create(bio_filter_out_method, &bio_filter_create);
BIO_meth_set_destroy(bio_filter_out_method, &bio_filter_destroy);
bio_filter_in_method = BIO_meth_new(BIO_TYPE_MEM, "APR input filter");
BIO_meth_set_write(bio_filter_in_method, &bio_filter_in_write); /* write is never called */
BIO_meth_set_read(bio_filter_in_method, &bio_filter_in_read);
BIO_meth_set_puts(bio_filter_in_method, &bio_filter_in_puts); /* puts is never called */
BIO_meth_set_gets(bio_filter_in_method, &bio_filter_in_gets); /* gets is never called */
BIO_meth_set_ctrl(bio_filter_in_method, &bio_filter_in_ctrl); /* ctrl is never called */
BIO_meth_set_create(bio_filter_in_method, &bio_filter_create);
BIO_meth_set_destroy(bio_filter_in_method, &bio_filter_destroy);
}
/* If we have something leftover from last time, try that first. */ if ((bytes = char_buffer_read(&inctx->cbuf, buf, wanted))) {
*len = bytes; if (inctx->mode == AP_MODE_SPECULATIVE) { /* We want to rollback this read. */ if (inctx->cbuf.length > 0) {
inctx->cbuf.value -= bytes;
inctx->cbuf.length += bytes;
} else {
char_buffer_write(&inctx->cbuf, buf, (int)bytes);
} return APR_SUCCESS;
} /* This could probably be *len == wanted, but be safe from stray * photons.
*/ if (*len >= wanted) { return APR_SUCCESS;
} if (inctx->mode == AP_MODE_GETLINE) { if (memchr(buf, APR_ASCII_LF, *len)) { return APR_SUCCESS;
}
} else { /* Down to a nonblock pattern as we have some data already
*/
inctx->block = APR_NONBLOCK_READ;
}
}
while (1) {
if (!inctx->filter_ctx->pssl) { /* Ensure a non-zero error code is returned */ if (inctx->rc == APR_SUCCESS) {
inctx->rc = APR_EGENERAL;
} break;
}
/* We rely on SSL_get_error() after the read, which requires an empty * error queue before the read in order to work properly.
*/
ERR_clear_error();
/* 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
*/
rc = SSL_read(inctx->filter_ctx->pssl, buf + bytes, wanted - bytes);
if (rc > 0) {
*len += rc; if (inctx->mode == AP_MODE_SPECULATIVE) { /* We want to rollback this read. */
char_buffer_write(&inctx->cbuf, buf, rc);
} return inctx->rc;
} else/* (rc <= 0) */ { int ssl_err;
conn_rec *c; if (rc == 0) { /* If EAGAIN, we will loop given a blocking read, * otherwise consider ourselves at EOF.
*/ if (APR_STATUS_IS_EAGAIN(inctx->rc)
|| APR_STATUS_IS_EINTR(inctx->rc)) { /* 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) {
inctx->rc = APR_SUCCESS; break;
} if (inctx->block == APR_NONBLOCK_READ) { break;
}
} else { if (*len > 0) {
inctx->rc = APR_SUCCESS; break;
}
}
}
ssl_err = SSL_get_error(inctx->filter_ctx->pssl, rc);
c = (conn_rec*)SSL_get_app_data(inctx->filter_ctx->pssl);
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.)
*/
inctx->rc = APR_EAGAIN;
if (*len > 0) {
inctx->rc = APR_SUCCESS; break;
} if (inctx->block == APR_NONBLOCK_READ) { break;
} continue; /* Blocking and nothing yet? Try again. */
} elseif (ssl_err == SSL_ERROR_SYSCALL) { if (APR_STATUS_IS_EAGAIN(inctx->rc)
|| APR_STATUS_IS_EINTR(inctx->rc)) { /* Already read something, return APR_SUCCESS instead. */ if (*len > 0) {
inctx->rc = APR_SUCCESS; break;
} if (inctx->block == APR_NONBLOCK_READ) { break;
} continue; /* Blocking and nothing yet? Try again. */
} elseif (APR_STATUS_IS_TIMEUP(inctx->rc)) { /* just return it, the calling layer might be fine with it,
and we do not want to bloat the log. */
} else {
ap_log_cerror(APLOG_MARK, APLOG_INFO, inctx->rc, c, APLOGNO(01991) "SSL input filter read failed.");
}
} elseif (rc == 0 && ssl_err == SSL_ERROR_ZERO_RETURN) {
inctx->rc = APR_EOF; break;
} else/* if (ssl_err == SSL_ERROR_SSL) */ { /* * Log SSL errors and any unexpected conditions.
*/
ap_log_cerror(APLOG_MARK, APLOG_INFO, inctx->rc, c, APLOGNO(01992) "SSL library error %d reading data", ssl_err);
ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, mySrvFromConn(c));
/* 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. */ static apr_status_t ssl_io_input_getline(bio_filter_in_ctx_t *inctx, char *buf,
apr_size_t *len)
{ constchar *pos = NULL;
apr_status_t status;
apr_size_t tmplen = *len, buflen = *len, offset = 0;
*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) {
status = ssl_io_input_read(inctx, buf + offset, &tmplen);
if (status != APR_SUCCESS) { if (APR_STATUS_IS_EAGAIN(status) && (*len > 0)) { /* Save the part of the line we already got */
char_buffer_write(&inctx->cbuf, buf, *len);
} return status;
}
*len += tmplen;
if ((pos = memchr(buf, APR_ASCII_LF, *len))) { break;
}
offset += tmplen;
tmplen = buflen - offset;
}
if (pos) { char *value; int length;
apr_size_t bytes = pos - buf;
/* We rely on SSL_get_error() after the write, which requires an empty error * queue before the write in order to work properly.
*/
ERR_clear_error();
outctx = (bio_filter_out_ctx_t *)BIO_get_data(filter_ctx->pbioWrite);
res = SSL_write(filter_ctx->pssl, (unsignedchar *)data, len);
if (res < 0) { int ssl_err = SSL_get_error(filter_ctx->pssl, res);
conn_rec *c = (conn_rec*)SSL_get_app_data(outctx->filter_ctx->pssl);
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.)
*/
outctx->rc = APR_EAGAIN;
} elseif (ssl_err == SSL_ERROR_WANT_READ) { /* * If OpenSSL wants to read during write, and we were * nonblocking, set the sense explicitly to read and * report as an EAGAIN. * * (This is usually the case when the client forces an SSL * renegotiation which is handled implicitly by OpenSSL.)
*/
outctx->c->cs->sense = CONN_SENSE_WANT_READ;
outctx->rc = APR_EAGAIN;
ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c, "Want read during nonblocking write");
} elseif (ssl_err == SSL_ERROR_SYSCALL) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01993) "SSL output filter write failed.");
} else/* if (ssl_err == SSL_ERROR_SSL) */ { /* * Log SSL errors
*/
ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01994) "SSL library error %d writing data", ssl_err);
ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, mySrvFromConn(c));
} if (outctx->rc == APR_SUCCESS) {
outctx->rc = APR_EGENERAL;
}
} elseif ((apr_size_t)res != len) {
conn_rec *c = f->c; char *reason = "reason unknown";
/* XXX: probably a better way to determine this */ if (SSL_total_renegotiations(filter_ctx->pssl)) {
reason = "likely due to failed renegotiation";
}
ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01995) "failed to write %" APR_SSIZE_T_FMT " of %" APR_SIZE_T_FMT " bytes (%s)",
len - (apr_size_t)res, len, reason);
outctx->rc = APR_EGENERAL;
} return outctx->rc;
}
/* 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
/* Custom apr_status_t error code, used when a plain HTTP request is
* received 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. */ #define MODSSL_ERROR_BAD_GATEWAY (APR_OS_START_USERERR + 1)
/* * Close the SSL part of the socket connection * (called immediately _before_ the socket is closed) * or called with
*/ staticvoid ssl_filter_io_shutdown(ssl_filter_ctx_t *filter_ctx,
conn_rec *c, int abortive)
{
SSL *ssl = filter_ctx->pssl; constchar *type = "";
SSLConnRec *sslconn = myConnConfig(c); int shutdown_type; int loglevel = APLOG_DEBUG; constchar *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) {
shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN;
type = "abortive";
logno = APLOGNO(01998);
loglevel = APLOG_INFO;
} elseswitch (sslconn->shutdown_type) { case SSL_SHUTDOWN_TYPE_UNCLEAN: /* perform no close notify handshake at all
(violates the SSL/TLS standard!) */
shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN;
type = "unclean";
logno = APLOGNO(01999); break; case SSL_SHUTDOWN_TYPE_ACCURATE: /* send close notify and wait for clients close notify
(standard compliant, but usually causes connection hangs) */
shutdown_type = 0;
type = "accurate";
logno = APLOGNO(02000); 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!) */
shutdown_type = SSL_RECEIVED_SHUTDOWN;
type = "standard";
logno = APLOGNO(02001); break;
}
/* * 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. */ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
{
conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
SSLConnRec *sslconn = myConnConfig(c);
SSLSrvConfigRec *sc;
X509 *cert; int n; int ssl_err; long verify_result;
server_rec *server;
if (SSL_is_init_finished(filter_ctx->pssl)) { return APR_SUCCESS;
}
server = sslconn->server; if (c->outgoing) { #ifdef HAVE_TLSEXT
apr_ipsubnet_t *ip; #ifdef HAVE_TLS_ALPN constchar *alpn_note;
apr_array_header_t *alpn_proposed = NULL; int alpn_empty_ok = 1; #endif #endif constchar *hostname_note = apr_table_get(c->notes, "proxy-request-hostname"); BOOL proxy_ssl_check_peer_ok = TRUE; int post_handshake_rc = OK;
SSLDirConfigRec *dc;
/* Transform the note into a protocol formatted byte array: * (len-byte proto-char+)* * We need the remote server to agree on one of these, unless 'http/1.1' * is also among our proposals. Because pre-ALPN remotes will speak this.
*/
alpn_proposed = apr_array_make(c->pool, 3, sizeof(constchar*));
alpn_empty_ok = 0;
s = protos = apr_pcalloc(c->pool, strlen(alpn_note)+1);
p = apr_pstrdup(c->pool, alpn_note); while ((p = apr_strtok(p, ", ", &last))) {
len = last - p - (*last? 1 : 0); if (len > 255) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(03309) "ALPN proxy protocol identifier too long: %s",
p);
ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, server); return APR_EGENERAL;
}
proto = apr_pstrndup(c->pool, p, len);
APR_ARRAY_PUSH(alpn_proposed, constchar*) = proto; if (!strcmp("http/1.1", proto)) {
alpn_empty_ok = 1;
}
*s++ = (unsignedchar)len; while (len--) {
*s++ = *p++;
}
p = NULL;
}
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "setting alpn protos from '%s', protolen=%d",
alpn_note, (int)(s - protos)); if (protos != s && SSL_set_alpn_protos(filter_ctx->pssl,
(unsignedchar *)protos,
s - protos)) {
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(03310) "error setting alpn protos from '%s'", alpn_note);
ssl_log_ssl_error(SSLLOG_MARK, APLOG_WARNING, server); /* If ALPN was requested and we cannot do it, we must fail */ return MODSSL_ERROR_BAD_GATEWAY;
}
} #endif/* defined HAVE_TLS_ALPN */ /* * 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 && #ifndef OPENSSL_NO_SSL3
dc->proxy->protocol != SSL_PROTOCOL_SSLV3 && #endif
apr_ipsubnet_create(&ip, hostname_note, NULL,
c->pool) != APR_SUCCESS) { if (SSL_set_tlsext_host_name(filter_ctx->pssl, hostname_note)) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c, "SNI extension for SSL Proxy request set to '%s'",
hostname_note);
} else {
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(02002) "Failed to set SNI extension for SSL Proxy " "request to '%s'", hostname_note);
ssl_log_ssl_error(SSLLOG_MARK, APLOG_WARNING, server);
}
} #endif/* defined HAVE_TLSEXT */
if ((n = SSL_connect(filter_ctx->pssl)) <= 0) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02003) "SSL Proxy connect failed");
ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server); /* ensure that the SSL structures etc are freed, etc: */
ssl_filter_io_shutdown(filter_ctx, c, 1);
apr_table_setn(c->notes, "SSL_connect_rv", "err"); return MODSSL_ERROR_BAD_GATEWAY;
}
/* Do string match or simplest wildcard match if that
* fails. */
match = strcasecmp(hostname, hostname_note) == 0; if (!match && strncmp(hostname, "*.", 2) == 0) { constchar *p = ap_strchr_c(hostname_note, '.');
#ifdef HAVE_TLS_ALPN /* If we proposed ALPN protocol(s), we need to check if the server * agreed to one of them. While <https://www.rfc-editor.org/rfc/rfc7301.txt> * chapter 3.2 says the server SHALL error the handshake in such a case, * the reality is that some servers fall back to their default, e.g. http/1.1. * (we also do this right now) * We need to treat this as an error for security reasons.
*/ if (alpn_proposed && alpn_proposed->nelts > 0) { constchar *selected; unsignedint slen;
SSL_get0_alpn_selected(filter_ctx->pssl, (constunsignedchar**)&selected, &slen); if (!selected || !slen) { /* No ALPN selection reported by the remote server. This could mean * it does not support ALPN (old server) or that it does not support
* any of our proposals (Apache itself up to 2.4.48 at least did that). */ if (!alpn_empty_ok) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(10273) "SSL Proxy: Peer did not select any of our ALPN protocols [%s].",
alpn_note);
proxy_ssl_check_peer_ok = FALSE;
}
} else { constchar *proto; int i, found = 0; for (i = 0; !found && i < alpn_proposed->nelts; ++i) {
proto = APR_ARRAY_IDX(alpn_proposed, i, constchar *);
found = !strncmp(selected, proto, slen);
} if (!found) { /* From a conforming peer, this should never happen,
* but life always finds a way... */
proto = apr_pstrndup(c->pool, selected, slen);
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(10274) "SSL Proxy: Peer proposed ALPN protocol %s which is none " "of our proposals [%s].", proto, alpn_note);
proxy_ssl_check_peer_ok = FALSE;
}
}
} #endif
if (proxy_ssl_check_peer_ok == TRUE) { /* another chance to fail */
post_handshake_rc = ssl_run_proxy_post_handshake(c, filter_ctx->pssl);
}
if (cert) {
X509_free(cert);
}
if (proxy_ssl_check_peer_ok != TRUE
|| (post_handshake_rc != OK && post_handshake_rc != DECLINED)) { /* ensure that the SSL structures etc are freed, etc: */
ssl_filter_io_shutdown(filter_ctx, c, 1);
apr_table_setn(c->notes, "SSL_connect_rv", "err"); return MODSSL_ERROR_BAD_GATEWAY;
}
/* We rely on SSL_get_error() after the accept, which requires an empty * error queue before the accept in order to work properly.
*/
ERR_clear_error();
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.
*/
ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c, APLOGNO(02006) "SSL handshake stopped: connection was closed");
} elseif (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.
*/
outctx->rc = APR_EAGAIN; return APR_EAGAIN;
} elseif (ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_SSL &&
ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) { /* * 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;
} elseif (ssl_err == SSL_ERROR_SYSCALL) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rc, c, APLOGNO(02007) "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.
*/
ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c, APLOGNO(02008) "SSL library error %d in handshake " "(server %s)", ssl_err,
ssl_util_vhostid(c->pool, server));
ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
} if (inctx->rc == APR_SUCCESS) {
inctx->rc = APR_EGENERAL;
}
if ((verify_result != X509_V_OK) ||
sslconn->verify_error)
{ if (ssl_verify_error_is_optional(verify_result) &&
(sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
{ /* 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
*/
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02009) "SSL client authentication failed, " "accepting certificate based on " "\"SSLVerifyClient optional_no_ca\" " "configuration");
ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server);
} else { constchar *error = sslconn->verify_error ?
sslconn->verify_error :
X509_verify_cert_error_string(verify_result);
/* * Remember the peer certificate's DN
*/ if ((cert = SSL_get_peer_certificate(filter_ctx->pssl))) { if (sslconn->client_cert) {
X509_free(sslconn->client_cert);
}
sslconn->client_cert = cert;
sslconn->client_dn = NULL;
}
/* * Make really sure that when a peer certificate * is required we really got one... (be paranoid)
*/ if ((sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE) &&
!sslconn->client_cert)
{
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02011) "No acceptable peer certificate available");
static apr_status_t ssl_io_filter_input(ap_filter_t *f,
apr_bucket_brigade *bb,
ap_input_mode_t mode,
apr_read_type_e block,
apr_off_t readbytes)
{
apr_status_t status;
bio_filter_in_ctx_t *inctx = f->ctx; constchar *start = inctx->buffer; /* start of block to return */
apr_size_t len = sizeof(inctx->buffer); /* length of block to return */ int is_init = (mode == AP_MODE_INIT);
apr_bucket *bucket;
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.
*/
bucket = apr_bucket_eos_create(f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, bucket); return APR_ECONNABORTED;
}
/* XXX: we don't currently support anything other than these modes. */ if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE &&
mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) { return APR_ENOTIMPL;
}
inctx->mode = mode;
inctx->block = block;
/* 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 ((status = ssl_io_filter_handshake(inctx->filter_ctx)) != APR_SUCCESS) { return ssl_io_filter_error(inctx, bb, status, is_init);
}
if (is_init) { /* protocol module needs to handshake before sending * data to client (e.g. NNTP or FTP)
*/ return APR_SUCCESS;
}
if (inctx->mode == AP_MODE_READBYTES ||
inctx->mode == AP_MODE_SPECULATIVE) { /* Protected from truncation, readbytes < MAX_SIZE_T
* FIXME: No, it's *not* protected. -- jre */ if (readbytes < len) {
len = (apr_size_t)readbytes;
}
status = ssl_io_input_read(inctx, inctx->buffer, &len);
} elseif (inctx->mode == AP_MODE_GETLINE) { constchar *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. */ if (inctx->cbuf.length
&& (pos = memchr(inctx->cbuf.value, APR_ASCII_LF,
inctx->cbuf.length)) != NULL) {
start = inctx->cbuf.value;
len = 1 + pos - start; /* +1 to include LF */ /* Buffer contents now consumed. */
inctx->cbuf.value += len;
inctx->cbuf.length -= len;
status = APR_SUCCESS;
} else { /* Otherwise fall back to the hard way. */
status = ssl_io_input_getline(inctx, inctx->buffer, &len);
}
} else { /* We have no idea what you are talking about, so return an error. */
status = APR_ENOTIMPL;
}
/* 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. */
inctx->block = APR_BLOCK_READ;
/* Create a transient bucket out of the decrypted data. */ if (len > 0) {
bucket =
apr_bucket_transient_create(start, len, f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, bucket);
}
return APR_SUCCESS;
}
/* ssl_io_filter_output() produces one SSL/TLS record per bucket * passed down the output filter stack. This results in a high * overhead (more network packets & TLS processing) for any output * comprising many small buckets. SSI output passed through the HTTP * chunk filter, for example, may produce many brigades containing * small buckets - [chunk-size CRLF] [chunk-data] [CRLF]. * * Sending HTTP response headers as a separate TLS record to the * response body also reveals information to a network observer (the * size of headers) which can be significant. * * The coalescing filter merges data buckets with the aim of producing * fewer, larger TLS records - without copying/buffering all content * and introducing unnecessary overhead. * * ### This buffering could be probably be done more comprehensively * ### in ssl_io_filter_output itself. * * ### Another possible performance optimisation in particular for the * ### [HEAP] [FILE] HTTP response case is using a brigade rather than * ### a char array to buffer; using apr_brigade_write() to append * ### will use already-allocated memory from the HEAP, reducing # of * ### copies.
*/
#define COALESCE_BYTES (AP_IOBUFSIZE)
struct coalesce_ctx { char buffer[COALESCE_BYTES];
apr_size_t bytes; /* number of bytes of buffer used. */
};
/* The brigade consists of zero-or-more small data buckets which * can be coalesced (referred to as 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 != (apr_size_t)-1
&& e->length <= COALESCE_BYTES
&& (buffered + bytes + e->length) <= COALESCE_BYTES;
e = APR_BUCKET_NEXT(e)) { /* don't count zero-length buckets */ if (e->length) {
bytes += e->length;
count++;
}
}
/* If there is room remaining and the next bucket is a data * bucket, try to include it in the prefix to coalesce. For a * typical [HEAP] [FILE] HTTP response brigade, this handles * merging the headers and the start of the body into a single TLS
* record. */ if (bytes + buffered > 0
&& bytes + buffered < COALESCE_BYTES
&& e != APR_BRIGADE_SENTINEL(bb)
&& !APR_BUCKET_IS_METADATA(e)) {
apr_status_t rv = APR_SUCCESS;
/* For an indeterminate length bucket (PIPE/CGI/...), try a * non-blocking read to have it morph into a HEAP. If the * read fails with EAGAIN, it is harmless to try a split
* anyway, split is ENOTIMPL for most PIPE-like buckets. */ if (e->length == (apr_size_t)-1) { constchar *discard;
apr_size_t ignore;
rv = apr_bucket_read(e, &discard, &ignore, APR_NONBLOCK_READ); if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c, APLOGNO(10232) "coalesce failed to read from %s bucket",
e->type->name); return AP_FILTER_ERROR;
}
}
if (rv == APR_SUCCESS) { /* If the read above made the bucket morph, it may now fit * entirely within the buffer. Otherwise, split it so it does
* fit. */ if (e->length > COALESCE_BYTES
|| e->length + buffered + bytes > COALESCE_BYTES) {
rv = apr_bucket_split(e, COALESCE_BYTES - (buffered + bytes));
}
if (rv == APR_SUCCESS && e->length == 0) { /* As above, don't count in the prefix if the bucket is
* now zero-length. */
} elseif (rv == APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, f->c, "coalesce: adding %" APR_SIZE_T_FMT " bytes " "from split %s bucket, total %" APR_SIZE_T_FMT,
e->length, e->type->name, bytes + buffered);
count++;
bytes += e->length;
e = APR_BUCKET_NEXT(e);
}
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.41 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.