ssl_engine_io.c revision 0fdf8c342123fde84405b885fb1720ebc652e10d
/* _ _
** _ __ ___ ___ __| | ___ ___| | mod_ssl
** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
** |_____|
** I/O Functions
*/
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
/* ``MY HACK: This universe.
Just one little problem:
core keeps dumping.''
-- Unknown */
#include "mod_ssl.h"
/* _________________________________________________________________
**
** I/O Hooks
** _________________________________________________________________
*/
/* XXX THIS STUFF NEEDS A MAJOR CLEANUP -RSE XXX */
/* 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_bucket_ctrl() -> BIO_bucket_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 {
conn_rec *c;
char buffer[AP_IOBUFSIZE];
} BIO_bucket_t;
{
b->c = c;
b->blen = 0;
b->length = 0;
return b;
}
{
apr_bucket *e;
return APR_SUCCESS;
}
if (b->blen) {
b->bb->bucket_alloc);
/* we filled this buffer first so add it to the
* head of the brigade
*/
APR_BRIGADE_INSERT_HEAD(b->bb, e);
b->blen = 0;
}
b->length = 0;
APR_BRIGADE_INSERT_TAIL(b->bb, e);
}
{
return 1;
}
{
return 0;
}
/* nothing to free here.
* apache will destroy the bucket brigade for us
*/
return 1;
}
{
/* this is never called */
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)
*/
/* the first two SSL_writes (of 1024 and 261 bytes)
* need to be in the same packet (vec[0].iov_base)
*/
/* XXX: could use apr_brigade_write() to make code look cleaner
* but this way we avoid the malloc(APR_BUCKET_BUFF_SIZE)
* and free() of it later
*/
}
else {
/* pass along the encrypted data
* need to flush since we're using SSL's malloc-ed buffer
* which will be overwritten once we leave here
*/
b->bb->bucket_alloc);
}
return inl;
}
{
long ret = 1;
char **pptr;
switch (cmd) {
case BIO_CTRL_RESET:
break;
case BIO_CTRL_EOF:
break;
break;
case BIO_CTRL_INFO:
if (ptr) {
}
break;
case BIO_CTRL_GET_CLOSE:
break;
case BIO_CTRL_SET_CLOSE:
break;
case BIO_CTRL_WPENDING:
ret = 0L;
break;
case BIO_CTRL_PENDING:
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_bucket_method = {
"APR bucket brigade",
#ifdef OPENSSL_VERSION_NUMBER
NULL /* sslc does not have the callback_ctrl field */
#endif
};
static BIO_METHOD *BIO_s_bucket(void)
{
return &bio_bucket_method;
}
typedef struct {
int length;
char *value;
typedef struct {
ap_filter_t *f;
typedef struct {
char buffer[AP_IOBUFSIZE];
/*
* 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.
*/
{
return 0;
}
/* we have have enough to fill the caller's buffer */
}
else {
/* swallow remainder of the buffer */
}
return inl;
}
{
return inl;
}
/*
* this is the function called by SSL_read()
*/
{
int len = 0;
/* XXX: flush here only required for SSLv2;
* OpenSSL calls BIO_flush() at the appropriate times for
* the other protocols.
*/
}
/* first use data already read from socket if any */
return len;
}
}
while (1) {
const char *buf;
apr_size_t buf_len = 0;
/* all of the data in this bucket has been read,
* so we can delete it now.
*/
}
/* We will always call with READBYTES even if the user wants
* GETLINE.
*/
inl);
{
break;
}
}
return len;
}
if (buf_len) {
/* Protected against len > MAX_INT
*/
/* we have enough to fill the buffer.
* append if we have already written to the buffer.
*/
break;
}
else {
/* not enough data,
* save what we have and try to read more.
*/
}
}
/* only read from the socket once in getline mode.
* since callers buffer size is likely much larger than
* the request headers. caller can always come back for more
* if first read didn't get all the headers.
*/
break;
}
}
return len;
}
static BIO_METHOD bio_bucket_in_method = {
"APR input bucket brigade",
NULL, /* write is never called */
NULL, /* puts is never called */
NULL, /* gets is never called */
NULL, /* ctrl is never called */
#ifdef OPENSSL_VERSION_NUMBER
NULL /* sslc does not have the callback_ctrl field */
#endif
};
static BIO_METHOD *BIO_s_in_bucket(void)
{
return &bio_bucket_in_method;
}
static const char ssl_io_filter[] = "SSL/TLS Filter";
{
int rc;
return -1;
}
if (rc <= 0) {
if (ssl_err == SSL_ERROR_WANT_READ) {
/*
* Simulate an EINTR in case OpenSSL wants to read more.
* (This is usually the case when the client forces an SSL
* renegotation which is handled implicitly by OpenSSL.)
*/
rc = 0; /* non fatal error */
}
else if (ssl_err == SSL_ERROR_SSL) {
/*
* Log SSL errors
*/
"SSL error on reading data");
}
}
return rc;
}
{
int rc;
return -1;
}
if (rc < 0) {
if (ssl_err == SSL_ERROR_WANT_WRITE) {
/*
* Simulate an EINTR in case OpenSSL wants to write more.
*/
}
else if (ssl_err == SSL_ERROR_SSL) {
/*
* Log SSL errors
*/
"SSL error on writing data");
}
/*
* XXX - Just trying to reflect the behaviour in
* openssl_state_machine.c [mod_tls]. TBD
*/
rc = 0;
}
return rc;
}
const char *data,
{
apr_size_t n;
/* write SSL */
if (n != len) {
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 %d of %d bytes (%s)",
return APR_EINVAL;
}
return APR_SUCCESS;
}
{
/* ssl_abort() has been called */
}
return status;
}
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.
*/
return status;
}
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_bucket_flush() already passed down a flush bucket
* if there was any data to be flushed.
*/
}
}
else {
/* read filter */
const char *data;
if (status != APR_SUCCESS) {
break;
}
}
}
return status;
}
/*
* ctx->cbuf is leftover plaintext from ssl_io_input_getline,
* use what we have there first if any,
* then go for more by calling ssl_io_hook_read.
*/
char *buf,
{
apr_size_t bytes = 0;
int rc;
*len = 0;
/* We want to rollback this read. */
return APR_SUCCESS;
}
return APR_SUCCESS;
}
}
if (rc > 0) {
}
}
/* something other than SSL_ERROR_WANT_READ */
return APR_EOF;
}
/*
* bucket read from socket was successful,
* but there was an error within the ssl runtime.
*/
return APR_EGENERAL;
}
}
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) {
return status;
}
break;
}
}
if (pos) {
char *value;
int length;
bytes += 1;
}
return APR_SUCCESS;
}
/* 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.
*
* We should probably use a 0.9 request, but the BIO bucket code is calling
* socket_bucket_read one extra time with all 0.9 requests from the client.
* Until that is resolved, continue to use a 1.0 request, just like we
* always have.
*/
#define HTTP_ON_HTTPS_PORT \
"GET / HTTP/1.0"
#define HTTP_ON_HTTPS_PORT_BUCKET(alloc) \
sizeof(HTTP_ON_HTTPS_PORT) - 1, \
static void ssl_io_filter_disable(ap_filter_t *f)
{
}
{
switch (status) {
case HTTP_BAD_REQUEST:
/* log the situation */
f->c->base_server,
"SSL handshake failed: HTTP spoken on HTTPS port; "
"trying to send HTML error page");
/* fake the request line */
break;
default:
return status;
}
return APR_SUCCESS;
}
{
}
/* XXX: we don't currently support anything other than these modes. */
return APR_ENOTIMPL;
}
/* XXX: we could actually move ssl_hook_process_connection 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 != APR_SUCCESS) {
}
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 */
}
}
}
else {
/* We have no idea what you are talking about, so return an error. */
return APR_ENOTIMPL;
}
if (status != APR_SUCCESS) {
}
if (len > 0) {
apr_bucket *bucket =
}
return APR_SUCCESS;
}
{
}
{
/* already been shutdown */
return APR_SUCCESS;
}
"Error in ssl_hook_CloseConnection");
}
return ret;
}
{
}
return;
}
void ssl_io_filter_register(apr_pool_t *p)
{
return;
}
/* _________________________________________________________________
**
** I/O Data Debugging
** _________________________________________________________________
*/
#define DUMP_WIDTH 16
long len)
{
char buf[256];
char tmp[64];
unsigned char ch;
trunc = 0;
trunc++;
rows++;
"+-------------------------------------------------------------------------+");
for(i = 0 ; i< rows; i++) {
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 {
}
}
"%s", buf);
}
if (trunc > 0)
"+-------------------------------------------------------------------------+");
return;
}
{
conn_rec *c;
server_rec *s;
return rc;
return rc;
s = c->base_server;
if (rc >= 0) {
"%s: %s %ld/%d bytes %s BIO#%p [mem: %p] %s",
}
else {
"%s: I/O error, %d bytes expected to %s on BIO#%p [mem: %p]",
}
}
return rc;
}