📄 ssl_engine_io.c
字号:
/* Copyright 2001-2005 The Apache Software Foundation or its licensors, as * applicable. * * Licensed 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. *//* _ _ * _ __ ___ ___ __| | ___ ___| | mod_ssl * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL * | | | | | | (_) | (_| | \__ \__ \ | * |_| |_| |_|\___/ \__,_|___|___/___/_| * |_____| * ssl_engine_io.c * I/O Functions */ /* ``MY HACK: This universe. Just one little problem: core keeps dumping.'' -- Unknown */#include "mod_ssl.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 { SSL *pssl; BIO *pbioRead; BIO *pbioWrite; ap_filter_t *pInputFilter; ap_filter_t *pOutputFilter; int nobuffer; /* non-zero to prevent buffering */} ssl_filter_ctx_t;typedef struct { ssl_filter_ctx_t *filter_ctx; conn_rec *c; apr_bucket_brigade *bb; apr_size_t length; char buffer[AP_IOBUFSIZE]; apr_size_t blen; apr_status_t rc;} bio_filter_out_ctx_t;static bio_filter_out_ctx_t *bio_filter_out_ctx_new(ssl_filter_ctx_t *filter_ctx, conn_rec *c){ bio_filter_out_ctx_t *outctx = apr_palloc(c->pool, sizeof(*outctx)); outctx->filter_ctx = filter_ctx; outctx->c = c; outctx->bb = apr_brigade_create(c->pool, c->bucket_alloc); outctx->blen = 0; outctx->length = 0; return outctx;}static int bio_filter_out_flush(BIO *bio){ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr); apr_bucket *e; if (!(outctx->blen || outctx->length)) { outctx->rc = APR_SUCCESS; return 1; } if (outctx->blen) { e = apr_bucket_transient_create(outctx->buffer, outctx->blen, outctx->bb->bucket_alloc); /* we filled this buffer first so add it to the * head of the brigade */ APR_BRIGADE_INSERT_HEAD(outctx->bb, e); outctx->blen = 0; } outctx->length = 0; e = apr_bucket_flush_create(outctx->bb->bucket_alloc); APR_BRIGADE_INSERT_TAIL(outctx->bb, e); 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;}static int bio_filter_create(BIO *bio){ bio->shutdown = 1; bio->init = 1; bio->num = -1; bio->ptr = NULL; return 1;}static int bio_filter_destroy(BIO *bio){ if (bio == NULL) { return 0; } /* nothing to free here. * apache will destroy the bucket brigade for us */ return 1;} static int bio_filter_out_read(BIO *bio, char *out, int outl){ /* this is never called */ return -1;}static int bio_filter_out_write(BIO *bio, const char *in, int inl){ bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr); /* 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) */ BIO_clear_retry_flags(bio); if (!outctx->length && (inl + outctx->blen < sizeof(outctx->buffer)) && !outctx->filter_ctx->nobuffer) { /* 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 */ memcpy(&outctx->buffer[outctx->blen], in, inl); outctx->blen += inl; } 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 */ apr_bucket *bucket = apr_bucket_transient_create(in, inl, outctx->bb->bucket_alloc); outctx->length += inl; APR_BRIGADE_INSERT_TAIL(outctx->bb, bucket); if (bio_filter_out_flush(bio) < 0) { return -1; } } return inl;}static long bio_filter_out_ctrl(BIO *bio, int cmd, long num, void *ptr){ long ret = 1; char **pptr; bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr); switch (cmd) { case BIO_CTRL_RESET: outctx->blen = outctx->length = 0; break; case BIO_CTRL_EOF: ret = (long)((outctx->blen + outctx->length) == 0); break; case BIO_C_SET_BUF_MEM_EOF_RETURN: outctx->blen = outctx->length = (apr_size_t)num; break; case BIO_CTRL_INFO: ret = (long)(outctx->blen + outctx->length); if (ptr) { pptr = (char **)ptr; *pptr = (char *)&(outctx->buffer[0]); } break; case BIO_CTRL_GET_CLOSE: ret = (long)bio->shutdown; break; case BIO_CTRL_SET_CLOSE: bio->shutdown = (int)num; break; case BIO_CTRL_WPENDING: ret = 0L; break; case BIO_CTRL_PENDING: ret = (long)(outctx->blen + outctx->length); 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;}static int bio_filter_out_gets(BIO *bio, char *buf, int size){ /* this is never called */ return -1;}static int bio_filter_out_puts(BIO *bio, const char *str){ /* this is never called */ return -1;}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,#ifdef OPENSSL_VERSION_NUMBER NULL /* sslc does not have the callback_ctrl field */#endif};typedef struct { int length; char *value;} char_buffer_t;typedef struct { SSL *ssl; BIO *bio_out; ap_filter_t *f; apr_status_t rc; ap_input_mode_t mode; apr_read_type_e block; apr_bucket_brigade *bb; char_buffer_t cbuf; apr_pool_t *pool; char buffer[AP_IOBUFSIZE]; ssl_filter_ctx_t *filter_ctx;} bio_filter_in_ctx_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. */static int char_buffer_read(char_buffer_t *buffer, char *in, int inl){ if (!buffer->length) { return 0; } if (buffer->length > inl) { /* we have have enough to fill the caller's buffer */ memcpy(in, buffer->value, inl); buffer->value += inl; buffer->length -= inl; } else { /* swallow remainder of the buffer */ memcpy(in, buffer->value, buffer->length); inl = buffer->length; buffer->value = NULL; buffer->length = 0; } return inl;}static int char_buffer_write(char_buffer_t *buffer, char *in, int inl){ buffer->value = in; buffer->length = 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. */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; while (!APR_BRIGADE_EMPTY(bb)) { apr_bucket *b = APR_BRIGADE_FIRST(bb); const char *str; apr_size_t str_len; apr_size_t consume;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -