ssl_engine_io.c revision 135402675e89e6df0e17735e48f428a1e1d8eb16
c330021bf3f45cbf187fa644781e67f7e470a58awrowe/* Licensed to the Apache Software Foundation (ASF) under one or more
7d5ac94fda90b837211dadf2585c0fe8c5dc3e5djerenkrantz * contributor license agreements. See the NOTICE file distributed with
c330021bf3f45cbf187fa644781e67f7e470a58awrowe * this work for additional information regarding copyright ownership.
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz * The ASF licenses this file to You under the Apache License, Version 2.0
62f7716b14b71603a8004434ca3536902bfb8899wrowe * (the "License"); you may not use this file except in compliance with
62f7716b14b71603a8004434ca3536902bfb8899wrowe * the License. You may obtain a copy of the License at
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz *
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz * http://www.apache.org/licenses/LICENSE-2.0
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz *
c330021bf3f45cbf187fa644781e67f7e470a58awrowe * Unless required by applicable law or agreed to in writing, software
87d944bf70927764edf8ef69e46d3b4b8fa09131pquerna * distributed under the License is distributed on an "AS IS" BASIS,
87d944bf70927764edf8ef69e46d3b4b8fa09131pquerna * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
39dbd3f60b93f5e0fbf46d9ae237f6742e113442pquerna * See the License for the specific language governing permissions and
7e97354b25d4291fc5d89aabe22c2e1df05059efstriker * limitations under the License.
ac3b8679ebc4b0889ecc0631fdde514a44d0a496jerenkrantz */
0cba3a63e59bfa77f67955cb4e034264ed6c5523jerenkrantz
84cbf7ab5adeca6b94c462a46d74f17388b6ff6fjerenkrantz/* _ _
84cbf7ab5adeca6b94c462a46d74f17388b6ff6fjerenkrantz * _ __ ___ ___ __| | ___ ___| | mod_ssl
2a6c49cfaef5979a5a06098f3ce987cd76769409manoj * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz * | | | | | | (_) | (_| | \__ \__ \ |
af4c982a7cf4515f124935f99a329744035fc699slive * |_| |_| |_|\___/ \__,_|___|___/___/_|
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz * |_____|
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz * ssl_engine_io.c
62f7716b14b71603a8004434ca3536902bfb8899wrowe * I/O Functions
62f7716b14b71603a8004434ca3536902bfb8899wrowe */
62f7716b14b71603a8004434ca3536902bfb8899wrowe /* ``MY HACK: This universe.
62f7716b14b71603a8004434ca3536902bfb8899wrowe Just one little problem:
62f7716b14b71603a8004434ca3536902bfb8899wrowe core keeps dumping.''
af4c982a7cf4515f124935f99a329744035fc699slive -- Unknown */
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe#include "ssl_private.h"
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe
2b8a78eefeba261ced96c3e57527d8ae3fce2f1aerikabele/* _________________________________________________________________
d96ee8cda2799e1f2743c1603adeb4833ed0e15fslive**
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz** I/O Hooks
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz** _________________________________________________________________
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz*/
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz/* This file is designed to be the bridge between OpenSSL and httpd.
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz * However, we really don't expect anyone (let alone ourselves) to
ecf8d72af432e53e4c0661fb99dfda8061507bfajerenkrantz * remember what is in this file. So, first, a quick overview.
d96ee8cda2799e1f2743c1603adeb4833ed0e15fslive *
d96ee8cda2799e1f2743c1603adeb4833ed0e15fslive * In this file, you will find:
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe * - ssl_io_filter_input (Apache input filter)
62f7716b14b71603a8004434ca3536902bfb8899wrowe * - ssl_io_filter_output (Apache output filter)
4b62424416882687387923b3130b96241503cbe0jerenkrantz *
5ca8e11fadb6f7a8d9d0367c1800205c99d4bcd6jerenkrantz * - bio_filter_in_* (OpenSSL input filter)
62f7716b14b71603a8004434ca3536902bfb8899wrowe * - bio_filter_out_* (OpenSSL output filter)
45b0e1c775c1cfed6473c9e5304179ccb9609f53stoddard *
dbec4658981e4f9127e8676457c28d42932be7cdtrawick * The input chain is roughly:
f6a9b598f78b2e745456bfc4bbc4afd2d1572aa9stas *
f6a9b598f78b2e745456bfc4bbc4afd2d1572aa9stas * ssl_io_filter_input->ssl_io_input_read->SSL_read->...
47c81da11264e8870b146dbdf3ac0384d3290ae9jerenkrantz * ...->bio_filter_in_read->ap_get_brigade/next-httpd-filter
0db0abcbe4211435c08e0c0e8f5daa278bed3524wsanchez *
0db0abcbe4211435c08e0c0e8f5daa278bed3524wsanchez * In mortal terminology, we do the following:
0db0abcbe4211435c08e0c0e8f5daa278bed3524wsanchez * - Receive a request for data to the SSL input filter
f6a9b598f78b2e745456bfc4bbc4afd2d1572aa9stas * - Call a helper function once we know we should perform a read
f6a9b598f78b2e745456bfc4bbc4afd2d1572aa9stas * - Call OpenSSL's SSL_read()
f6a9b598f78b2e745456bfc4bbc4afd2d1572aa9stas * - SSL_read() will then call bio_filter_in_read
0db0abcbe4211435c08e0c0e8f5daa278bed3524wsanchez * - bio_filter_in_read will then try to fetch data from the next httpd filter
47c81da11264e8870b146dbdf3ac0384d3290ae9jerenkrantz * - bio_filter_in_read will flatten that data and return it to SSL_read
0db0abcbe4211435c08e0c0e8f5daa278bed3524wsanchez * - SSL_read will then decrypt the data
d64eb0a76ec10c2405d14b892b0eb0f7ce27a613stas * - ssl_io_input_read will then receive decrypted data as a char* and
d64eb0a76ec10c2405d14b892b0eb0f7ce27a613stas * ensure that there were no read errors
d64eb0a76ec10c2405d14b892b0eb0f7ce27a613stas * - The char* is placed in a brigade and returned
f6a9b598f78b2e745456bfc4bbc4afd2d1572aa9stas *
62f7716b14b71603a8004434ca3536902bfb8899wrowe * Since connection-level input filters in httpd need to be able to
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz * handle AP_MODE_GETLINE calls (namely identifying LF-terminated strings),
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz * ssl_io_input_getline which will handle this special case.
f95a0b59eb24c631f15bd83e20c6cf823c432d83trawick *
f95a0b59eb24c631f15bd83e20c6cf823c432d83trawick * Due to AP_MODE_GETLINE and AP_MODE_SPECULATIVE, we may sometimes have
f95a0b59eb24c631f15bd83e20c6cf823c432d83trawick * 'leftover' decoded data which must be setaside for the next read. That
c95a8116bde20ab75f2e54d0e867bdf2e13643c3jim * is currently handled by the char_buffer_{read|write} functions. So,
b08ed1b8a8afa3d8466300f4607b9d1179d2f1eaerikabele * ssl_io_input_read may be able to fulfill reads without invoking
b08ed1b8a8afa3d8466300f4607b9d1179d2f1eaerikabele * SSL_read().
13402b2193f52031b2acfbee2b0965e02f3f29b4wrowe *
f95a0b59eb24c631f15bd83e20c6cf823c432d83trawick * Note that the filter context of ssl_io_filter_input and bio_filter_in_*
f95a0b59eb24c631f15bd83e20c6cf823c432d83trawick * are shared as bio_filter_in_ctx_t.
418f39e60a288f855ec033b566744489c3bbbf39trawick *
cc22a72861c58dda7f3768613aec864e4c4e0353striker * Note that the filter is by choice limited to reading at most
f95a0b59eb24c631f15bd83e20c6cf823c432d83trawick * AP_IOBUFSIZE (8192 bytes) per call.
ddafc111b94558ef4e2d7357ceda623315566ce3slive *
ddafc111b94558ef4e2d7357ceda623315566ce3slive */
b08ed1b8a8afa3d8466300f4607b9d1179d2f1eaerikabele
0db0abcbe4211435c08e0c0e8f5daa278bed3524wsanchez/* this custom BIO allows us to hook SSL_write directly into
ddafc111b94558ef4e2d7357ceda623315566ce3slive * an apr_bucket_brigade and use transient buckets with the SSL
93f189f1198f539d3cfa75a15b23dcde60ee35ffrbb * malloc-ed buffer, rather than copying into a mem BIO.
93f189f1198f539d3cfa75a15b23dcde60ee35ffrbb * also allows us to pass the brigade as data is being written
bca5b27d271b6e1690134a83963424b9825d93bdstriker * rather than buffering up the entire response in the mem BIO.
bca5b27d271b6e1690134a83963424b9825d93bdstriker *
b08ed1b8a8afa3d8466300f4607b9d1179d2f1eaerikabele * when SSL needs to flush (e.g. SSL_accept()), it will call BIO_flush()
13402b2193f52031b2acfbee2b0965e02f3f29b4wrowe * which will trigger a call to bio_filter_out_ctrl() -> bio_filter_out_flush().
13402b2193f52031b2acfbee2b0965e02f3f29b4wrowe * so we only need to flush the output ourselves if we receive an
38d2c5d41cdb5eb28668d0290b59f8c76ae2a4bfjim * EOS or FLUSH bucket. this was not possible with the mem BIO where we
38d2c5d41cdb5eb28668d0290b59f8c76ae2a4bfjim * had to flush all over the place not really knowing when it was required
38d2c5d41cdb5eb28668d0290b59f8c76ae2a4bfjim * to do so.
93f189f1198f539d3cfa75a15b23dcde60ee35ffrbb */
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantztypedef struct {
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz SSL *pssl;
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz BIO *pbioRead;
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz BIO *pbioWrite;
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz ap_filter_t *pInputFilter;
b9b0788ea79de3ee230ccb0abf93a0c2c08f6287lars ap_filter_t *pOutputFilter;
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz int nobuffer; /* non-zero to prevent buffering */
7718f3d5b4da70eb063877f5300ee361435910f4nd} ssl_filter_ctx_t;
38d2c5d41cdb5eb28668d0290b59f8c76ae2a4bfjim
38d2c5d41cdb5eb28668d0290b59f8c76ae2a4bfjimtypedef struct {
38d2c5d41cdb5eb28668d0290b59f8c76ae2a4bfjim ssl_filter_ctx_t *filter_ctx;
7718f3d5b4da70eb063877f5300ee361435910f4nd conn_rec *c;
b9b0788ea79de3ee230ccb0abf93a0c2c08f6287lars apr_bucket_brigade *bb;
b9b0788ea79de3ee230ccb0abf93a0c2c08f6287lars apr_size_t length;
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz char buffer[AP_IOBUFSIZE];
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz apr_size_t blen;
7718f3d5b4da70eb063877f5300ee361435910f4nd apr_status_t rc;
1af5c0e25a649bb298e25ddfa5418fa18bdcb107aaron} bio_filter_out_ctx_t;
38d2c5d41cdb5eb28668d0290b59f8c76ae2a4bfjim
82455c2e3b6991846fbcbf0c9e41f57dbc681217brianpstatic bio_filter_out_ctx_t *bio_filter_out_ctx_new(ssl_filter_ctx_t *filter_ctx,
84cbf7ab5adeca6b94c462a46d74f17388b6ff6fjerenkrantz conn_rec *c)
771a4d2c4ebe8debc609ed7d65938c4b72b9d4cepquerna{
f4cb04eb78da02a38fcdd87489dc7b660107d55fjerenkrantz bio_filter_out_ctx_t *outctx = apr_palloc(c->pool, sizeof(*outctx));
62f7716b14b71603a8004434ca3536902bfb8899wrowe
1b3f48fd6b1ccb8745f908e40156c5a85ca3c347jerenkrantz outctx->filter_ctx = filter_ctx;
b05930e6008f69bd323abe0c10f81f40ffd27983brianp outctx->c = c;
d96ee8cda2799e1f2743c1603adeb4833ed0e15fslive outctx->bb = apr_brigade_create(c->pool, c->bucket_alloc);
380584d22150608f50ca4ae8089efeb30794514ajerenkrantz outctx->blen = 0;
d96ee8cda2799e1f2743c1603adeb4833ed0e15fslive outctx->length = 0;
a07cab794b2b8d92dae6ad9f2642be77a010f5acpquerna
ed7c594e460858633e768386dbc1447a2ccdfe04pquerna return outctx;
0723420d6007137272f4f140ffd17035b17c1563nd}
0723420d6007137272f4f140ffd17035b17c1563nd
0723420d6007137272f4f140ffd17035b17c1563ndstatic int bio_filter_out_flush(BIO *bio)
0723420d6007137272f4f140ffd17035b17c1563nd{
0723420d6007137272f4f140ffd17035b17c1563nd bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr);
0723420d6007137272f4f140ffd17035b17c1563nd apr_bucket *e;
0723420d6007137272f4f140ffd17035b17c1563nd
0723420d6007137272f4f140ffd17035b17c1563nd if (!(outctx->blen || outctx->length)) {
0723420d6007137272f4f140ffd17035b17c1563nd outctx->rc = APR_SUCCESS;
0723420d6007137272f4f140ffd17035b17c1563nd return 1;
0723420d6007137272f4f140ffd17035b17c1563nd }
0723420d6007137272f4f140ffd17035b17c1563nd
016f2545c9375ec7fc5e9cb70aa1ae0cace83c98jerenkrantz if (outctx->blen) {
016f2545c9375ec7fc5e9cb70aa1ae0cace83c98jerenkrantz e = apr_bucket_transient_create(outctx->buffer, outctx->blen,
016f2545c9375ec7fc5e9cb70aa1ae0cace83c98jerenkrantz outctx->bb->bucket_alloc);
016f2545c9375ec7fc5e9cb70aa1ae0cace83c98jerenkrantz /* we filled this buffer first so add it to the
016f2545c9375ec7fc5e9cb70aa1ae0cace83c98jerenkrantz * head of the brigade
016f2545c9375ec7fc5e9cb70aa1ae0cace83c98jerenkrantz */
016f2545c9375ec7fc5e9cb70aa1ae0cace83c98jerenkrantz APR_BRIGADE_INSERT_HEAD(outctx->bb, e);
56afb23a30271d30e87f225cce6c69969d6dd8bbwrowe outctx->blen = 0;
56afb23a30271d30e87f225cce6c69969d6dd8bbwrowe }
016f2545c9375ec7fc5e9cb70aa1ae0cace83c98jerenkrantz
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe outctx->length = 0;
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe e = apr_bucket_flush_create(outctx->bb->bucket_alloc);
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe APR_BRIGADE_INSERT_TAIL(outctx->bb, e);
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe outctx->rc = ap_pass_brigade(outctx->filter_ctx->pOutputFilter->next,
b3b4e853e4958357ee2d50e2fe41effecfde9eedwrowe outctx->bb);
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe /* Fail if the connection was reset: */
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe if (outctx->rc == APR_SUCCESS && outctx->c->aborted) {
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe outctx->rc = APR_ECONNRESET;
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe }
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe return (outctx->rc == APR_SUCCESS) ? 1 : -1;
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe}
6362fd5c48f6b2193f5d356a9747172101b24e91jerenkrantz
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowestatic int bio_filter_create(BIO *bio)
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe{
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe bio->shutdown = 1;
c330021bf3f45cbf187fa644781e67f7e470a58awrowe bio->init = 1;
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe bio->num = -1;
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe bio->ptr = NULL;
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe return 1;
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe}
b05930e6008f69bd323abe0c10f81f40ffd27983brianp
b05930e6008f69bd323abe0c10f81f40ffd27983brianpstatic int bio_filter_destroy(BIO *bio)
b05930e6008f69bd323abe0c10f81f40ffd27983brianp{
b05930e6008f69bd323abe0c10f81f40ffd27983brianp if (bio == NULL) {
b05930e6008f69bd323abe0c10f81f40ffd27983brianp return 0;
4c7dab038d90d7feb67ef8ddbfacc77be8c9dbf0jwoolley }
4c7dab038d90d7feb67ef8ddbfacc77be8c9dbf0jwoolley
4c7dab038d90d7feb67ef8ddbfacc77be8c9dbf0jwoolley /* nothing to free here.
4c7dab038d90d7feb67ef8ddbfacc77be8c9dbf0jwoolley * apache will destroy the bucket brigade for us
bb2a72a6e2327ae4f177c9d26e9d433033cfc27eaaron */
8c8fbb8546af54582539898be704411a60058d85trawick return 1;
8c8fbb8546af54582539898be704411a60058d85trawick}
8c8fbb8546af54582539898be704411a60058d85trawick
f9b8e29cfca92cf0a996e8ab17fa1a1f447cecc7stoddardstatic int bio_filter_out_read(BIO *bio, char *out, int outl)
f9b8e29cfca92cf0a996e8ab17fa1a1f447cecc7stoddard{
f9b8e29cfca92cf0a996e8ab17fa1a1f447cecc7stoddard /* this is never called */
a23b1c5a74208b03884c09a6f9dd5d6c97fa6415trawick return -1;
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe}
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowestatic int bio_filter_out_write(BIO *bio, const char *in, int inl)
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe{
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr);
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe /* when handshaking we'll have a small number of bytes.
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe * max size SSL will pass us here is about 16k.
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe * (16413 bytes to be exact)
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe */
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe BIO_clear_retry_flags(bio);
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe if (!outctx->length && (inl + outctx->blen < sizeof(outctx->buffer)) &&
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe !outctx->filter_ctx->nobuffer) {
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe /* the first two SSL_writes (of 1024 and 261 bytes)
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe * need to be in the same packet (vec[0].iov_base)
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe */
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe /* XXX: could use apr_brigade_write() to make code look cleaner
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe * but this way we avoid the malloc(APR_BUCKET_BUFF_SIZE)
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe * and free() of it later
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe */
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe memcpy(&outctx->buffer[outctx->blen], in, inl);
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe outctx->blen += inl;
db2d668e6233d8949b35ee7f9f42f444758f9ce9rbb }
db2d668e6233d8949b35ee7f9f42f444758f9ce9rbb else {
db2d668e6233d8949b35ee7f9f42f444758f9ce9rbb /* pass along the encrypted data
db2d668e6233d8949b35ee7f9f42f444758f9ce9rbb * need to flush since we're using SSL's malloc-ed buffer
db2d668e6233d8949b35ee7f9f42f444758f9ce9rbb * which will be overwritten once we leave here
db2d668e6233d8949b35ee7f9f42f444758f9ce9rbb */
db2d668e6233d8949b35ee7f9f42f444758f9ce9rbb apr_bucket *bucket = apr_bucket_transient_create(in, inl,
5d12baef135b5d3cb94745e007a1575398469724jerenkrantz outctx->bb->bucket_alloc);
5d12baef135b5d3cb94745e007a1575398469724jerenkrantz
5d12baef135b5d3cb94745e007a1575398469724jerenkrantz outctx->length += inl;
b865daaa4ef731a7066ee6d97e2aae36c7743939jerenkrantz APR_BRIGADE_INSERT_TAIL(outctx->bb, bucket);
b865daaa4ef731a7066ee6d97e2aae36c7743939jerenkrantz
b865daaa4ef731a7066ee6d97e2aae36c7743939jerenkrantz if (bio_filter_out_flush(bio) < 0) {
f881b8be216de36c6b964324c772450bca38a4e6trawick return -1;
f881b8be216de36c6b964324c772450bca38a4e6trawick }
f881b8be216de36c6b964324c772450bca38a4e6trawick }
0bcb1fe39dfaacf9745b6633f5cc9ebc8e2596caaaron
0bcb1fe39dfaacf9745b6633f5cc9ebc8e2596caaaron return inl;
0bcb1fe39dfaacf9745b6633f5cc9ebc8e2596caaaron}
0bcb1fe39dfaacf9745b6633f5cc9ebc8e2596caaaron
0bcb1fe39dfaacf9745b6633f5cc9ebc8e2596caaaronstatic long bio_filter_out_ctrl(BIO *bio, int cmd, long num, void *ptr)
0bcb1fe39dfaacf9745b6633f5cc9ebc8e2596caaaron{
33f5961d34a8b5390cebad0543b3ebe67830e5d7jerenkrantz long ret = 1;
33f5961d34a8b5390cebad0543b3ebe67830e5d7jerenkrantz char **pptr;
33f5961d34a8b5390cebad0543b3ebe67830e5d7jerenkrantz
33f5961d34a8b5390cebad0543b3ebe67830e5d7jerenkrantz bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)(bio->ptr);
33f5961d34a8b5390cebad0543b3ebe67830e5d7jerenkrantz
33f5961d34a8b5390cebad0543b3ebe67830e5d7jerenkrantz switch (cmd) {
ff42f83cbf31893bcde9712332a8e5ee970f6a74trawick case BIO_CTRL_RESET:
ff42f83cbf31893bcde9712332a8e5ee970f6a74trawick outctx->blen = outctx->length = 0;
ff42f83cbf31893bcde9712332a8e5ee970f6a74trawick break;
33f5961d34a8b5390cebad0543b3ebe67830e5d7jerenkrantz case BIO_CTRL_EOF:
54e1babd5a5a56c576eeeace54110150769cc916coar ret = (long)((outctx->blen + outctx->length) == 0);
54e1babd5a5a56c576eeeace54110150769cc916coar break;
54e1babd5a5a56c576eeeace54110150769cc916coar case BIO_C_SET_BUF_MEM_EOF_RETURN:
54e1babd5a5a56c576eeeace54110150769cc916coar outctx->blen = outctx->length = (apr_size_t)num;
54e1babd5a5a56c576eeeace54110150769cc916coar break;
54e1babd5a5a56c576eeeace54110150769cc916coar case BIO_CTRL_INFO:
54e1babd5a5a56c576eeeace54110150769cc916coar ret = (long)(outctx->blen + outctx->length);
54e1babd5a5a56c576eeeace54110150769cc916coar if (ptr) {
54e1babd5a5a56c576eeeace54110150769cc916coar pptr = (char **)ptr;
54e1babd5a5a56c576eeeace54110150769cc916coar *pptr = (char *)&(outctx->buffer[0]);
54e1babd5a5a56c576eeeace54110150769cc916coar }
54e1babd5a5a56c576eeeace54110150769cc916coar break;
54e1babd5a5a56c576eeeace54110150769cc916coar case BIO_CTRL_GET_CLOSE:
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe ret = (long)bio->shutdown;
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe break;
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe case BIO_CTRL_SET_CLOSE:
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe bio->shutdown = (int)num;
949aa7bba7f804faa8e6b08cad42a98fc0255d85jerenkrantz break;
949aa7bba7f804faa8e6b08cad42a98fc0255d85jerenkrantz case BIO_CTRL_WPENDING:
949aa7bba7f804faa8e6b08cad42a98fc0255d85jerenkrantz ret = 0L;
949aa7bba7f804faa8e6b08cad42a98fc0255d85jerenkrantz break;
949aa7bba7f804faa8e6b08cad42a98fc0255d85jerenkrantz case BIO_CTRL_PENDING:
949aa7bba7f804faa8e6b08cad42a98fc0255d85jerenkrantz ret = (long)(outctx->blen + outctx->length);
2b93f4e3bba84578a57e5edcdb510071d2e79ed9pquerna break;
2b93f4e3bba84578a57e5edcdb510071d2e79ed9pquerna case BIO_CTRL_FLUSH:
07021d9f405849228b859d9fb4b877f20e4fbba3jerenkrantz ret = bio_filter_out_flush(bio);
4ca13a5e126946272f02637e268a8e09193c553ecoar break;
4ca13a5e126946272f02637e268a8e09193c553ecoar case BIO_CTRL_DUP:
4ca13a5e126946272f02637e268a8e09193c553ecoar ret = 1;
4ca13a5e126946272f02637e268a8e09193c553ecoar break;
4ca13a5e126946272f02637e268a8e09193c553ecoar /* N/A */
b3b4e853e4958357ee2d50e2fe41effecfde9eedwrowe case BIO_C_SET_BUF_MEM:
b3b4e853e4958357ee2d50e2fe41effecfde9eedwrowe case BIO_C_GET_BUF_MEM_PTR:
48c0c81cd6fabac9d3386406d97633780365b839coar /* we don't care */
48c0c81cd6fabac9d3386406d97633780365b839coar case BIO_CTRL_PUSH:
48c0c81cd6fabac9d3386406d97633780365b839coar case BIO_CTRL_POP:
48c0c81cd6fabac9d3386406d97633780365b839coar default:
48c0c81cd6fabac9d3386406d97633780365b839coar ret = 0;
e0427bf8e52a8fb920cb8b6adb5cdb3b6535b7fecoar break;
4ca13a5e126946272f02637e268a8e09193c553ecoar }
b84f66c93f820824b1d5455181f55598b766319cwrowe
b84f66c93f820824b1d5455181f55598b766319cwrowe return ret;
b84f66c93f820824b1d5455181f55598b766319cwrowe}
b84f66c93f820824b1d5455181f55598b766319cwrowe
7fe18c15b669db9d191859695901dc4fcf3829dawrowestatic int bio_filter_out_gets(BIO *bio, char *buf, int size)
7fe18c15b669db9d191859695901dc4fcf3829dawrowe{
7fe18c15b669db9d191859695901dc4fcf3829dawrowe /* this is never called */
cc22a72861c58dda7f3768613aec864e4c4e0353striker return -1;
cc22a72861c58dda7f3768613aec864e4c4e0353striker}
cc22a72861c58dda7f3768613aec864e4c4e0353striker
cc22a72861c58dda7f3768613aec864e4c4e0353strikerstatic int bio_filter_out_puts(BIO *bio, const char *str)
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowe{
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowe /* this is never called */
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowe return -1;
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe}
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowe
f610c7c704235bc327dbe9b62982f5b3f8e30a77wrowestatic BIO_METHOD bio_filter_out_method = {
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe BIO_TYPE_MEM,
2fb49a1d25f38421a68d31b4cbb5d9293fdeafbewrowe "APR output filter",
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowe bio_filter_out_write,
19cbe4d7b7c931723e7249de6829bf965a1fee72stoddard bio_filter_out_read, /* read is never called */
19cbe4d7b7c931723e7249de6829bf965a1fee72stoddard bio_filter_out_puts, /* puts is never called */
b187d568e1507d75139ebc13ca945b38fc05d55cstoddard bio_filter_out_gets, /* gets is never called */
b187d568e1507d75139ebc13ca945b38fc05d55cstoddard bio_filter_out_ctrl,
b187d568e1507d75139ebc13ca945b38fc05d55cstoddard bio_filter_create,
a5ed555df952c85bc1b179f5981e8a6c54ba16e6stoddard bio_filter_destroy,
d2f8b010487ffa990a9c268df5a25579e7291bcdrbb#ifdef OPENSSL_VERSION_NUMBER
d2f8b010487ffa990a9c268df5a25579e7291bcdrbb NULL /* sslc does not have the callback_ctrl field */
a5ed555df952c85bc1b179f5981e8a6c54ba16e6stoddard#endif
0bff2f28ef945280c17099c142126178a78e1e54manoj};
35330e0d79ceb8027223bbb8330a381b1f989d6etrawick
0bff2f28ef945280c17099c142126178a78e1e54manojtypedef struct {
9c09943bad734ebd5c7cc10bd6d63b75c4c6e056stoddard int length;
ff849e4163ed879288f0df15f78b6c9d278ec804fanf char *value;
ff849e4163ed879288f0df15f78b6c9d278ec804fanf} char_buffer_t;
447c6ce3ff08073c44f6785d5256271fcb877512wrowe
e0427bf8e52a8fb920cb8b6adb5cdb3b6535b7fecoartypedef struct {
e0427bf8e52a8fb920cb8b6adb5cdb3b6535b7fecoar SSL *ssl;
e0427bf8e52a8fb920cb8b6adb5cdb3b6535b7fecoar BIO *bio_out;
e0427bf8e52a8fb920cb8b6adb5cdb3b6535b7fecoar ap_filter_t *f;
6758b07b4b79f898b0f56375016cea7da0bfb495wrowe apr_status_t rc;
6758b07b4b79f898b0f56375016cea7da0bfb495wrowe ap_input_mode_t mode;
6758b07b4b79f898b0f56375016cea7da0bfb495wrowe apr_read_type_e block;
7fe18c15b669db9d191859695901dc4fcf3829dawrowe apr_bucket_brigade *bb;
db3ccce11afac4fc1d4f51a65424412f7480c46cgstein char_buffer_t cbuf;
dd4713dc5b186f4d1be7b88f86608fdb84cbe5d5gstein apr_pool_t *pool;
48c0c81cd6fabac9d3386406d97633780365b839coar char buffer[AP_IOBUFSIZE];
48c0c81cd6fabac9d3386406d97633780365b839coar ssl_filter_ctx_t *filter_ctx;
48c0c81cd6fabac9d3386406d97633780365b839coar} bio_filter_in_ctx_t;
48c0c81cd6fabac9d3386406d97633780365b839coar
db3ccce11afac4fc1d4f51a65424412f7480c46cgstein/*
79d5106a9b65b956d646f5daae4b94bc79e315b8trawick * this char_buffer api might seem silly, but we don't need to copy
79d5106a9b65b956d646f5daae4b94bc79e315b8trawick * any of this data and we need to remember the length.
79d5106a9b65b956d646f5daae4b94bc79e315b8trawick */
cf6bf6c34c936e6a6fe731dbce4a5c3c8bf8e9a3gsteinstatic int char_buffer_read(char_buffer_t *buffer, char *in, int inl)
cf6bf6c34c936e6a6fe731dbce4a5c3c8bf8e9a3gstein{
cf6bf6c34c936e6a6fe731dbce4a5c3c8bf8e9a3gstein if (!buffer->length) {
b3b4e853e4958357ee2d50e2fe41effecfde9eedwrowe return 0;
93c5cba06b623ebe8e4372e886eece12d9a80c3egstein }
14cccaddba3a9263cf0d0ddc311e18f3e3dc9b0fgstein
14cccaddba3a9263cf0d0ddc311e18f3e3dc9b0fgstein if (buffer->length > inl) {
14cccaddba3a9263cf0d0ddc311e18f3e3dc9b0fgstein /* we have have enough to fill the caller's buffer */
14cccaddba3a9263cf0d0ddc311e18f3e3dc9b0fgstein memcpy(in, buffer->value, inl);
14cccaddba3a9263cf0d0ddc311e18f3e3dc9b0fgstein buffer->value += inl;
14cccaddba3a9263cf0d0ddc311e18f3e3dc9b0fgstein buffer->length -= inl;
823c303d33c9e637a83d82208bcbafaf5f532d7bgstein }
823c303d33c9e637a83d82208bcbafaf5f532d7bgstein else {
e636eba7474e0010b5c7198af1c2fe5ad8652dbbmanoj /* swallow remainder of the buffer */
e636eba7474e0010b5c7198af1c2fe5ad8652dbbmanoj memcpy(in, buffer->value, buffer->length);
e636eba7474e0010b5c7198af1c2fe5ad8652dbbmanoj inl = buffer->length;
281da4c02cf40c663298ded7e4e5b913a8f8b814gstein buffer->value = NULL;
281da4c02cf40c663298ded7e4e5b913a8f8b814gstein buffer->length = 0;
2f728b2e8555fee1b7cc11e886488692f2575fbddougm }
2f728b2e8555fee1b7cc11e886488692f2575fbddougm
2f728b2e8555fee1b7cc11e886488692f2575fbddougm return inl;
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowe}
48c0c81cd6fabac9d3386406d97633780365b839coar
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowestatic int char_buffer_write(char_buffer_t *buffer, char *in, int inl)
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowe{
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowe buffer->value = in;
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowe buffer->length = inl;
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowe return inl;
60d567a0c2aae815ee6fc20c0d65032bea52c92cwrowe}
27757f6699a924d4b493a1b6cceb27df27a43287dreid
b3b4e853e4958357ee2d50e2fe41effecfde9eedwrowe/* This function will read from a brigade and discard the read buckets as it
27757f6699a924d4b493a1b6cceb27df27a43287dreid * proceeds. It will read at most *len bytes.
21e01f13f717faeca3e498d7d9c9b4d3af98ae27trawick */
21e01f13f717faeca3e498d7d9c9b4d3af98ae27trawickstatic apr_status_t brigade_consume(apr_bucket_brigade *bb,
21e01f13f717faeca3e498d7d9c9b4d3af98ae27trawick apr_read_type_e block,
21e01f13f717faeca3e498d7d9c9b4d3af98ae27trawick char *c, apr_size_t *len)
21e01f13f717faeca3e498d7d9c9b4d3af98ae27trawick{
21e01f13f717faeca3e498d7d9c9b4d3af98ae27trawick apr_size_t actual = 0;
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard apr_status_t status = APR_SUCCESS;
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay while (!APR_BRIGADE_EMPTY(bb)) {
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay apr_bucket *b = APR_BRIGADE_FIRST(bb);
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay const char *str;
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay apr_size_t str_len;
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay apr_size_t consume;
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay /* Justin points out this is an http-ism that might
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay * not fit if brigade_consume is added to APR. Perhaps
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay * apr_bucket_read(eos_bucket) should return APR_EOF?
9f95877e5e3f99a43eb6f3f632f87f144da3b8e6pquerna * Then this becomes mainline instead of a one-off.
1062c731b19eed1efa24e2229e8c3a34240846c6minfrin */
05cd8b63829a8a9047076ffbce8dd6cd1cb2db92thommay if (APR_BUCKET_IS_EOS(b)) {
62f7716b14b71603a8004434ca3536902bfb8899wrowe status = APR_EOF;
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe break;
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe }
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe /* The reason I'm not offering brigade_consume yet
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe * across to apr-util is that the following call
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe * illustrates how borked that API really is. For
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe * this sort of case (caller provided buffer) it
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe * would be much more trivial for apr_bucket_consume
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe * to do all the work that follows, based on the
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe * particular characteristics of the bucket we are
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe * consuming here.
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe */
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe status = apr_bucket_read(b, &str, &str_len, block);
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe if (status != APR_SUCCESS) {
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe if (APR_STATUS_IS_EOF(status)) {
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe /* This stream bucket was consumed */
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe apr_bucket_delete(b);
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe continue;
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe }
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe break;
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe }
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe if (str_len > 0) {
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe /* Do not block once some data has been consumed */
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe block = APR_NONBLOCK_READ;
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe /* Assure we don't overflow. */
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe consume = (str_len + actual > *len) ? *len - actual : str_len;
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe memcpy(c, str, consume);
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe c += consume;
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe actual += consume;
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe if (consume >= b->length) {
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe /* This physical bucket was consumed */
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe apr_bucket_delete(b);
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe }
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe else {
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe /* Only part of this physical bucket was consumed */
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe b->start += consume;
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe b->length -= consume;
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe }
62f7716b14b71603a8004434ca3536902bfb8899wrowe }
afbd720d176856630fed7c6576cdd3ae25a407edstoddard else if (b->length == 0) {
afbd720d176856630fed7c6576cdd3ae25a407edstoddard apr_bucket_delete(b);
afbd720d176856630fed7c6576cdd3ae25a407edstoddard }
afbd720d176856630fed7c6576cdd3ae25a407edstoddard
afbd720d176856630fed7c6576cdd3ae25a407edstoddard /* This could probably be actual == *len, but be safe from stray
28c4fe67d75f8f26504d75b7aa8dc5d868032888wrowe * photons. */
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz if (actual >= *len) {
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz break;
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz }
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz }
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz *len = actual;
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz return status;
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz}
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz/*
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz * this is the function called by SSL_read()
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz */
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantzstatic int bio_filter_in_read(BIO *bio, char *in, int inlen)
74def8815c725f8128a4e76ab1f5704df80b024ajerenkrantz{
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard apr_size_t inl = inlen;
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)(bio->ptr);
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard apr_read_type_e block = inctx->block;
afbd720d176856630fed7c6576cdd3ae25a407edstoddard SSLConnRec *sslconn = myConnConfig(inctx->f->c);
afbd720d176856630fed7c6576cdd3ae25a407edstoddard
afbd720d176856630fed7c6576cdd3ae25a407edstoddard inctx->rc = APR_SUCCESS;
afbd720d176856630fed7c6576cdd3ae25a407edstoddard
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard /* OpenSSL catches this case, so should we. */
47c81da11264e8870b146dbdf3ac0384d3290ae9jerenkrantz if (!in)
47c81da11264e8870b146dbdf3ac0384d3290ae9jerenkrantz return 0;
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard /* XXX: flush here only required for SSLv2;
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard * OpenSSL calls BIO_flush() at the appropriate times for
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard * the other protocols.
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard */
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard if ((SSL_version(inctx->ssl) == SSL2_VERSION) || sslconn->is_proxy) {
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard if (bio_filter_out_flush(inctx->bio_out) < 0) {
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard bio_filter_out_ctx_t *outctx =
47c81da11264e8870b146dbdf3ac0384d3290ae9jerenkrantz (bio_filter_out_ctx_t *)(inctx->bio_out->ptr);
ddd44b06b04507cae083c52451e28f54f0bdb5afstoddard inctx->rc = outctx->rc;
2a6c49cfaef5979a5a06098f3ce987cd76769409manoj return -1;
d5defd5a0c5cdbaf74b85939484dc2b6c8317d19manoj }
d5defd5a0c5cdbaf74b85939484dc2b6c8317d19manoj }
2a6c49cfaef5979a5a06098f3ce987cd76769409manoj
2a6c49cfaef5979a5a06098f3ce987cd76769409manoj BIO_clear_retry_flags(bio);
2a6c49cfaef5979a5a06098f3ce987cd76769409manoj
2a6c49cfaef5979a5a06098f3ce987cd76769409manoj if (!inctx->bb) {
2a6c49cfaef5979a5a06098f3ce987cd76769409manoj inctx->rc = APR_EOF;
6758b07b4b79f898b0f56375016cea7da0bfb495wrowe return -1;
6758b07b4b79f898b0f56375016cea7da0bfb495wrowe }
6758b07b4b79f898b0f56375016cea7da0bfb495wrowe
2a6c49cfaef5979a5a06098f3ce987cd76769409manoj if (APR_BRIGADE_EMPTY(inctx->bb)) {
ec0315cdf832eac2b78e50ad636af84fe4c9118cgstein
ec0315cdf832eac2b78e50ad636af84fe4c9118cgstein inctx->rc = ap_get_brigade(inctx->f->next, inctx->bb,
ec0315cdf832eac2b78e50ad636af84fe4c9118cgstein AP_MODE_READBYTES, block,
ec0315cdf832eac2b78e50ad636af84fe4c9118cgstein inl);
ec0315cdf832eac2b78e50ad636af84fe4c9118cgstein
ec0315cdf832eac2b78e50ad636af84fe4c9118cgstein /* If the read returns EAGAIN or success with an empty
ec0315cdf832eac2b78e50ad636af84fe4c9118cgstein * brigade, return an error after setting the retry flag;
ec0315cdf832eac2b78e50ad636af84fe4c9118cgstein * SSL_read() will then return -1, and SSL_get_error() will
ec0315cdf832eac2b78e50ad636af84fe4c9118cgstein * indicate SSL_ERROR_WANT_READ. */
62f7716b14b71603a8004434ca3536902bfb8899wrowe if (APR_STATUS_IS_EAGAIN(inctx->rc) || APR_STATUS_IS_EINTR(inctx->rc)
62f7716b14b71603a8004434ca3536902bfb8899wrowe || (inctx->rc == APR_SUCCESS && APR_BRIGADE_EMPTY(inctx->bb))) {
62f7716b14b71603a8004434ca3536902bfb8899wrowe BIO_set_retry_read(bio);
62f7716b14b71603a8004434ca3536902bfb8899wrowe return -1;
62f7716b14b71603a8004434ca3536902bfb8899wrowe }
62f7716b14b71603a8004434ca3536902bfb8899wrowe
62f7716b14b71603a8004434ca3536902bfb8899wrowe if (inctx->rc != APR_SUCCESS) {
62f7716b14b71603a8004434ca3536902bfb8899wrowe /* 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;
}
static BIO_METHOD bio_filter_in_method = {
BIO_TYPE_MEM,
"APR input filter",
NULL, /* write is never called */
bio_filter_in_read,
NULL, /* puts is never called */
NULL, /* gets is never called */
NULL, /* ctrl is never called */
bio_filter_create,
bio_filter_destroy,
#ifdef OPENSSL_VERSION_NUMBER
NULL /* sslc does not have the callback_ctrl field */
#endif
};
static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
char *buf,
apr_size_t *len)
{
apr_size_t wanted = *len;
apr_size_t bytes = 0;
int rc;
*len = 0;
/* 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;
}
/* 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 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;
}
else {
inctx->rc = APR_EOF;
}
break;
}
}
else /* (rc < 0) */ {
int ssl_err = SSL_get_error(inctx->filter_ctx->pssl, rc);
conn_rec *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
* renegotation 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. */
}
else if (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. */
}
else {
ap_log_cerror(APLOG_MARK, APLOG_INFO, inctx->rc, c,
"SSL input filter read failed.");
}
}
else /* if (ssl_err == SSL_ERROR_SSL) */ {
/*
* Log SSL errors and any unexpected conditions.
*/
ap_log_cerror(APLOG_MARK, APLOG_INFO, inctx->rc, c,
"SSL library error %d reading data", ssl_err);
ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server);
}
if (inctx->rc == APR_SUCCESS) {
inctx->rc = APR_EGENERAL;
}
break;
}
}
return inctx->rc;
}
static apr_status_t ssl_io_input_getline(bio_filter_in_ctx_t *inctx,
char *buf,
apr_size_t *len)
{
const char *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) {
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;
bytes += 1;
value = buf + bytes;
length = *len - bytes;
char_buffer_write(&inctx->cbuf, value, length);
*len = bytes;
}
return APR_SUCCESS;
}
static apr_status_t ssl_filter_write(ap_filter_t *f,
const char *data,
apr_size_t len)
{
ssl_filter_ctx_t *filter_ctx = f->ctx;
bio_filter_out_ctx_t *outctx;
int res;
/* write SSL */
if (filter_ctx->pssl == NULL) {
return APR_EGENERAL;
}
outctx = (bio_filter_out_ctx_t *)filter_ctx->pbioWrite->ptr;
res = SSL_write(filter_ctx->pssl, (unsigned char *)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
* renegotation which is handled implicitly by OpenSSL.)
*/
outctx->rc = APR_EAGAIN;
}
else if (ssl_err == SSL_ERROR_SYSCALL) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c,
"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,
"SSL library error %d writing data", ssl_err);
ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server);
}
if (outctx->rc == APR_SUCCESS) {
outctx->rc = APR_EGENERAL;
}
}
else if ((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,
"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.
*
* To avoid calling back for more data from the socket, use an HTTP/0.9
* request, and tack on an EOS bucket.
*/
#define HTTP_ON_HTTPS_PORT \
"GET /" CRLF
#define HTTP_ON_HTTPS_PORT_BUCKET(alloc) \
apr_bucket_immortal_create(HTTP_ON_HTTPS_PORT, \
sizeof(HTTP_ON_HTTPS_PORT) - 1, \
alloc)
static void ssl_io_filter_disable(SSLConnRec *sslconn, ap_filter_t *f)
{
bio_filter_in_ctx_t *inctx = f->ctx;
SSL_free(inctx->ssl);
sslconn->ssl = NULL;
inctx->ssl = NULL;
inctx->filter_ctx->pssl = NULL;
}
static apr_status_t ssl_io_filter_error(ap_filter_t *f,
apr_bucket_brigade *bb,
apr_status_t status)
{
SSLConnRec *sslconn = myConnConfig(f->c);
apr_bucket *bucket;
switch (status) {
case HTTP_BAD_REQUEST:
/* log the situation */
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
"SSL handshake failed: HTTP spoken on HTTPS port; "
"trying to send HTML error page");
ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, f->c->base_server);
sslconn->non_ssl_request = 1;
ssl_io_filter_disable(sslconn, f);
/* fake the request line */
bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc);
break;
default:
return status;
}
APR_BRIGADE_INSERT_TAIL(bb, bucket);
bucket = apr_bucket_eos_create(f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, bucket);
return APR_SUCCESS;
}
static const char ssl_io_filter[] = "SSL/TLS Filter";
static const char ssl_io_buffer[] = "SSL/TLS Buffer";
/*
* Close the SSL part of the socket connection
* (called immediately _before_ the socket is closed)
* or called with
*/
static apr_status_t ssl_filter_io_shutdown(ssl_filter_ctx_t *filter_ctx,
conn_rec *c,
int abortive)
{
SSL *ssl = filter_ctx->pssl;
const char *type = "";
SSLConnRec *sslconn = myConnConfig(c);
int shutdown_type;
if (!ssl) {
return APR_SUCCESS;
}
/*
* 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";
}
else switch (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";
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";
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";
break;
}
SSL_set_shutdown(ssl, shutdown_type);
SSL_smart_shutdown(ssl);
/* and finally log the fact that we've closed the connection */
if (c->base_server->loglevel >= APLOG_INFO) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c,
"Connection closed to child %ld with %s shutdown "
"(server %s)",
c->id, type, ssl_util_vhostid(c->pool, c->base_server));
}
/* deallocate the SSL connection */
if (sslconn->client_cert) {
X509_free(sslconn->client_cert);
sslconn->client_cert = NULL;
}
SSL_free(ssl);
sslconn->ssl = NULL;
filter_ctx->pssl = NULL; /* so filters know we've been shutdown */
if (abortive) {
/* prevent any further I/O */
c->aborted = 1;
}
return APR_SUCCESS;
}
static apr_status_t ssl_io_filter_cleanup(void *data)
{
ssl_filter_ctx_t *filter_ctx = data;
if (filter_ctx->pssl) {
conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
SSLConnRec *sslconn = myConnConfig(c);
SSL_free(filter_ctx->pssl);
sslconn->ssl = filter_ctx->pssl = NULL;
}
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.
*/
static int ssl_io_filter_connect(ssl_filter_ctx_t *filter_ctx)
{
conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl);
SSLConnRec *sslconn = myConnConfig(c);
SSLSrvConfigRec *sc = mySrvConfig(c->base_server);
X509 *cert;
int n;
int ssl_err;
long verify_result;
if (SSL_is_init_finished(filter_ctx->pssl)) {
return APR_SUCCESS;
}
if (sslconn->is_proxy) {
if ((n = SSL_connect(filter_ctx->pssl)) <= 0) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c,
"SSL Proxy connect failed");
ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server);
/* ensure that the SSL structures etc are freed, etc: */
ssl_filter_io_shutdown(filter_ctx, c, 1);
return HTTP_BAD_GATEWAY;
}
return APR_SUCCESS;
}
if ((n = SSL_accept(filter_ctx->pssl)) <= 0) {
bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *)
(filter_ctx->pbioRead->ptr);
bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *)
(filter_ctx->pbioWrite->ptr);
apr_status_t rc = inctx->rc ? inctx->rc : outctx->rc ;
ssl_err = SSL_get_error(filter_ctx->pssl, n);
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,
"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.
*/
outctx->rc = APR_EAGAIN;
return SSL_ERROR_WANT_READ;
}
else if (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 HTTP_BAD_REQUEST;
}
else if (ssl_err == SSL_ERROR_SYSCALL) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c,
"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,
"SSL library error %d in handshake "
"(server %s)", ssl_err,
ssl_util_vhostid(c->pool, c->base_server));
ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server);
}
if (inctx->rc == APR_SUCCESS) {
inctx->rc = APR_EGENERAL;
}
return ssl_filter_io_shutdown(filter_ctx, c, 1);
}
/*
* Check for failed client authentication
*/
verify_result = SSL_get_verify_result(filter_ctx->pssl);
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,
"SSL client authentication failed, "
"accepting certificate based on "
"\"SSLVerifyClient optional_no_ca\" "
"configuration");
ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server);
}
else {
const char *error = sslconn->verify_error ?
sslconn->verify_error :
X509_verify_cert_error_string(verify_result);
ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c,
"SSL client authentication failed: %s",
error ? error : "unknown");
ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server);
return ssl_filter_io_shutdown(filter_ctx, c, 1);
}
}
/*
* 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,
"No acceptable peer certificate available");
return ssl_filter_io_shutdown(filter_ctx, c, 1);
}
return APR_SUCCESS;
}
#define SWITCH_STATUS_LINE "HTTP/1.1 101 Switching Protocols"
#define UPGRADE_HEADER "Upgrade: TLS/1.0, HTTP/1.1"
#define CONNECTION_HEADER "Connection: Upgrade"
static apr_status_t ssl_io_filter_Upgrade(ap_filter_t *f,
apr_bucket_brigade *bb)
{
const char *upgrade;
apr_bucket_brigade *upgradebb;
request_rec *r = f->r;
SSLConnRec *sslconn;
apr_status_t rv;
apr_bucket *b;
SSL *ssl;
/* Just remove the filter, if it doesn't work the first time, it won't
* work at all for this request.
*/
ap_remove_output_filter(f);
/* No need to ensure that this is a server with optional SSL, the filter
* is only inserted if that is true.
*/
upgrade = apr_table_get(r->headers_in, "Upgrade");
if (upgrade == NULL
|| strcmp(ap_getword(r->pool, &upgrade, ','), "TLS/1.0")) {
/* "Upgrade: TLS/1.0, ..." header not found, don't do Upgrade */
return ap_pass_brigade(f->next, bb);
}
apr_table_unset(r->headers_out, "Upgrade");
/* Send the interim 101 response. */
upgradebb = apr_brigade_create(r->pool, f->c->bucket_alloc);
ap_fputstrs(f->next, upgradebb, SWITCH_STATUS_LINE, CRLF,
UPGRADE_HEADER, CRLF, CONNECTION_HEADER, CRLF, CRLF, NULL);
b = apr_bucket_flush_create(f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(upgradebb, b);
rv = ap_pass_brigade(f->next, upgradebb);
if (rv) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
"could not send interim 101 Upgrade response");
return AP_FILTER_ERROR;
}
ssl_init_ssl_connection(f->c);
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
"Awaiting re-negotiation handshake");
sslconn = myConnConfig(f->c);
ssl = sslconn->ssl;
/* XXX: Should replace SSL_set_state with SSL_renegotiate(ssl);
* However, this causes failures in perl-framework currently,
* perhaps pre-test if we have already negotiated?
*/
SSL_set_accept_state(ssl);
SSL_do_handshake(ssl);
if (SSL_get_state(ssl) != SSL_ST_OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"TLS Upgrade handshake failed: "
"Not accepted by client!?");
return AP_FILTER_ERROR;
}
/* Now that we have initialized the ssl connection which added the ssl_io_filter,
pass the brigade off to the connection based output filters so that the
request can complete encrypted */
return ap_pass_brigade(f->c->output_filters, bb);
}
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;
apr_size_t len = sizeof(inctx->buffer);
int is_init = (mode == AP_MODE_INIT);
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.
*/
apr_bucket *bucket = apr_bucket_eos_create(f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, bucket);
return APR_ECONNABORTED;
}
if (!inctx->ssl) {
return ap_get_brigade(f->next, bb, mode, block, readbytes);
}
/* 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_connect 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_connect(inctx->filter_ctx)) != APR_SUCCESS) {
return ssl_io_filter_error(f, bb, status);
}
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);
}
else if (inctx->mode == AP_MODE_GETLINE) {
status = ssl_io_input_getline(inctx, inctx->buffer, &len);
}
else {
/* We have no idea what you are talking about, so return an error. */
return APR_ENOTIMPL;
}
if (status != APR_SUCCESS) {
return ssl_io_filter_error(f, bb, status);
}
/* Create a transient bucket out of the decrypted data. */
if (len > 0) {
apr_bucket *bucket =
apr_bucket_transient_create(inctx->buffer, len, f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, bucket);
}
return APR_SUCCESS;
}
static apr_status_t ssl_io_filter_output(ap_filter_t *f,
apr_bucket_brigade *bb)
{
apr_status_t status = APR_SUCCESS;
ssl_filter_ctx_t *filter_ctx = f->ctx;
bio_filter_in_ctx_t *inctx;
bio_filter_out_ctx_t *outctx;
apr_read_type_e rblock = APR_NONBLOCK_READ;
if (f->c->aborted) {
apr_brigade_cleanup(bb);
return APR_ECONNABORTED;
}
if (!filter_ctx->pssl) {
/* ssl_filter_io_shutdown was called */
return ap_pass_brigade(f->next, bb);
}
inctx = (bio_filter_in_ctx_t *)filter_ctx->pbioRead->ptr;
outctx = (bio_filter_out_ctx_t *)filter_ctx->pbioWrite->ptr;
/* 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.
*/
inctx->mode = AP_MODE_READBYTES;
inctx->block = APR_BLOCK_READ;
if ((status = ssl_io_filter_connect(filter_ctx)) != APR_SUCCESS) {
return ssl_io_filter_error(f, bb, status);
}
while (!APR_BRIGADE_EMPTY(bb)) {
apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
/* If it is a flush or EOS, we need to pass this down.
* These types do not require translation by OpenSSL.
*/
if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) {
if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) {
status = outctx->rc;
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.
*/
if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
return status;
}
break;
}
else {
/* bio_filter_out_flush() already passed down a flush bucket
* if there was any data to be flushed.
*/
apr_bucket_delete(bucket);
}
}
else if (AP_BUCKET_IS_EOC(bucket)) {
/* The special "EOC" bucket means a shutdown is needed;
* - turn off buffering in bio_filter_out_write
* - issue the SSL_shutdown
*/
filter_ctx->nobuffer = 1;
status = ssl_filter_io_shutdown(filter_ctx, f->c, 0);
if (status != APR_SUCCESS) {
ap_log_cerror(APLOG_MARK, APLOG_INFO, status, f->c,
"SSL filter error shutting down I/O");
}
if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
return status;
}
break;
}
else {
/* filter output */
const char *data;
apr_size_t len;
status = apr_bucket_read(bucket, &data, &len, rblock);
if (APR_STATUS_IS_EAGAIN(status)) {
/* No data available: flush... */
if (bio_filter_out_flush(filter_ctx->pbioWrite) < 0) {
status = outctx->rc;
break;
}
rblock = APR_BLOCK_READ;
continue; /* and try again with a blocking read. */
}
rblock = APR_NONBLOCK_READ;
if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
break;
}
status = ssl_filter_write(f, data, len);
apr_bucket_delete(bucket);
if (status != APR_SUCCESS) {
break;
}
}
}
return status;
}
/* 128K maximum buffer size by default. */
#ifndef SSL_MAX_IO_BUFFER
#define SSL_MAX_IO_BUFFER (128 * 1024)
#endif
struct modssl_buffer_ctx {
apr_bucket_brigade *bb;
};
int ssl_io_buffer_fill(request_rec *r)
{
conn_rec *c = r->connection;
struct modssl_buffer_ctx *ctx;
apr_bucket_brigade *tempb;
apr_off_t total = 0; /* total length buffered */
int eos = 0; /* non-zero once EOS is seen */
/* Create the context which will be passed to the input filter;
* containing a setaside pool and a brigade which constrain the
* lifetime of the buffered data. */
ctx = apr_palloc(r->pool, sizeof *ctx);
ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc);
/* ... and a temporary brigade. */
tempb = apr_brigade_create(r->pool, c->bucket_alloc);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "filling buffer");
do {
apr_status_t rv;
apr_bucket *e, *next;
/* The request body is read from the protocol-level input
* filters; the buffering filter will reinject it from that
* level, allowing content/resource filters to run later, if
* necessary. */
rv = ap_get_brigade(r->proto_input_filters, tempb, AP_MODE_READBYTES,
APR_BLOCK_READ, 8192);
if (rv) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
"could not read request body for SSL buffer");
return HTTP_INTERNAL_SERVER_ERROR;
}
/* Iterate through the returned brigade: setaside each bucket
* into the context's pool and move it into the brigade. */
for (e = APR_BRIGADE_FIRST(tempb);
e != APR_BRIGADE_SENTINEL(tempb) && !eos; e = next) {
const char *data;
apr_size_t len;
next = APR_BUCKET_NEXT(e);
if (APR_BUCKET_IS_EOS(e)) {
eos = 1;
} else if (!APR_BUCKET_IS_METADATA(e)) {
rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
"could not read bucket for SSL buffer");
return HTTP_INTERNAL_SERVER_ERROR;
}
total += len;
}
rv = apr_bucket_setaside(e, r->pool);
if (rv != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
"could not setaside bucket for SSL buffer");
return HTTP_INTERNAL_SERVER_ERROR;
}
APR_BUCKET_REMOVE(e);
APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
}
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
"total of %" APR_OFF_T_FMT " bytes in buffer, eos=%d",
total, eos);
/* Fail if this exceeds the maximum buffer size. */
if (total > SSL_MAX_IO_BUFFER) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"request body exceeds maximum size for SSL buffer");
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
} while (!eos);
apr_brigade_destroy(tempb);
/* Insert the filter which will supply the buffered data. */
ap_add_input_filter(ssl_io_buffer, ctx, r, c);
return 0;
}
/* This input filter supplies the buffered request body to the caller
* from the brigade stored in f->ctx. */
static apr_status_t ssl_io_filter_buffer(ap_filter_t *f,
apr_bucket_brigade *bb,
ap_input_mode_t mode,
apr_read_type_e block,
apr_off_t bytes)
{
struct modssl_buffer_ctx *ctx = f->ctx;
apr_status_t rv;
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
"read from buffered SSL brigade, mode %d, "
"%" APR_OFF_T_FMT " bytes",
mode, bytes);
if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
return APR_ENOTIMPL;
}
if (mode == AP_MODE_READBYTES) {
apr_bucket *e;
/* Partition the buffered brigade. */
rv = apr_brigade_partition(ctx->bb, bytes, &e);
if (rv && rv != APR_INCOMPLETE) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c,
"could not partition buffered SSL brigade");
ap_remove_input_filter(f);
return rv;
}
/* If the buffered brigade contains less then the requested
* length, just pass it all back. */
if (rv == APR_INCOMPLETE) {
APR_BRIGADE_CONCAT(bb, ctx->bb);
} else {
apr_bucket *d = APR_BRIGADE_FIRST(ctx->bb);
e = APR_BUCKET_PREV(e);
/* Unsplice the partitioned segment and move it into the
* passed-in brigade; no convenient way to do this with
* the APR_BRIGADE_* macros. */
APR_RING_UNSPLICE(d, e, link);
APR_RING_SPLICE_HEAD(&bb->list, d, e, apr_bucket, link);
APR_BRIGADE_CHECK_CONSISTENCY(bb);
APR_BRIGADE_CHECK_CONSISTENCY(ctx->bb);
}
}
else {
/* Split a line into the passed-in brigade. */
rv = apr_brigade_split_line(bb, ctx->bb, mode, bytes);
if (rv) {
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, f->c,
"could not split line from buffered SSL brigade");
ap_remove_input_filter(f);
return rv;
}
}
if (APR_BRIGADE_EMPTY(ctx->bb)) {
apr_bucket *e = APR_BRIGADE_LAST(bb);
/* Ensure that the brigade is terminated by an EOS if the
* buffered request body has been entirely consumed. */
if (e == APR_BRIGADE_SENTINEL(bb) || !APR_BUCKET_IS_EOS(e)) {
e = apr_bucket_eos_create(f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
}
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
"buffered SSL brigade now exhausted; removing filter");
ap_remove_input_filter(f);
}
return APR_SUCCESS;
}
static void ssl_io_input_add_filter(ssl_filter_ctx_t *filter_ctx, conn_rec *c,
SSL *ssl)
{
bio_filter_in_ctx_t *inctx;
inctx = apr_palloc(c->pool, sizeof(*inctx));
filter_ctx->pInputFilter = ap_add_input_filter(ssl_io_filter, inctx, NULL, c);
filter_ctx->pbioRead = BIO_new(&bio_filter_in_method);
filter_ctx->pbioRead->ptr = (void *)inctx;
inctx->ssl = ssl;
inctx->bio_out = filter_ctx->pbioWrite;
inctx->f = filter_ctx->pInputFilter;
inctx->rc = APR_SUCCESS;
inctx->mode = AP_MODE_READBYTES;
inctx->cbuf.length = 0;
inctx->bb = apr_brigade_create(c->pool, c->bucket_alloc);
inctx->block = APR_BLOCK_READ;
inctx->pool = c->pool;
inctx->filter_ctx = filter_ctx;
}
void ssl_io_filter_init(conn_rec *c, SSL *ssl)
{
ssl_filter_ctx_t *filter_ctx;
server_rec *s = c->base_server;
SSLSrvConfigRec *sc = mySrvConfig(s);
filter_ctx = apr_palloc(c->pool, sizeof(ssl_filter_ctx_t));
filter_ctx->nobuffer = 0;
filter_ctx->pOutputFilter = ap_add_output_filter(ssl_io_filter,
filter_ctx, NULL, c);
filter_ctx->pbioWrite = BIO_new(&bio_filter_out_method);
filter_ctx->pbioWrite->ptr = (void *)bio_filter_out_ctx_new(filter_ctx, c);
/* We insert a clogging input filter. Let the core know. */
c->clogging_input_filters = 1;
ssl_io_input_add_filter(filter_ctx, c, ssl);
SSL_set_bio(ssl, filter_ctx->pbioRead, filter_ctx->pbioWrite);
filter_ctx->pssl = ssl;
apr_pool_cleanup_register(c->pool, (void*)filter_ctx,
ssl_io_filter_cleanup, apr_pool_cleanup_null);
if ((s->loglevel >= APLOG_DEBUG)
&& (sc->ssl_log_level >= SSL_LOG_IO)) {
BIO_set_callback(SSL_get_rbio(ssl), ssl_io_data_cb);
BIO_set_callback_arg(SSL_get_rbio(ssl), (void *)ssl);
}
return;
}
void ssl_io_filter_register(apr_pool_t *p)
{
/* This filter MUST be after the HTTP_HEADER filter, but it also must be
* a resource-level filter so it has the request_rec.
*/
ap_register_output_filter ("UPGRADE_FILTER", ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5);
ap_register_input_filter (ssl_io_filter, ssl_io_filter_input, NULL, AP_FTYPE_CONNECTION + 5);
ap_register_output_filter (ssl_io_filter, ssl_io_filter_output, NULL, AP_FTYPE_CONNECTION + 5);
ap_register_input_filter (ssl_io_buffer, ssl_io_filter_buffer, NULL, AP_FTYPE_PROTOCOL - 1);
return;
}
/* _________________________________________________________________
**
** I/O Data Debugging
** _________________________________________________________________
*/
#define DUMP_WIDTH 16
static void ssl_io_data_dump(server_rec *srvr,
MODSSL_BIO_CB_ARG_TYPE *s,
long len)
{
char buf[256];
char tmp[64];
int i, j, rows, trunc;
unsigned char ch;
trunc = 0;
for(; (len > 0) && ((s[len-1] == ' ') || (s[len-1] == '\0')); len--)
trunc++;
rows = (len / DUMP_WIDTH);
if ((rows * DUMP_WIDTH) < len)
rows++;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, srvr,
"+-------------------------------------------------------------------------+");
for(i = 0 ; i< rows; i++) {
#if APR_CHARSET_EBCDIC
char ebcdic_text[DUMP_WIDTH];
j = DUMP_WIDTH;
if ((i * DUMP_WIDTH + j) > len)
j = len % DUMP_WIDTH;
if (j == 0)
j = DUMP_WIDTH;
memcpy(ebcdic_text,(char *)(s) + i * DUMP_WIDTH, j);
ap_xlate_proto_from_ascii(ebcdic_text, j);
#endif /* APR_CHARSET_EBCDIC */
apr_snprintf(tmp, sizeof(tmp), "| %04x: ", i * DUMP_WIDTH);
apr_cpystrn(buf, tmp, sizeof(buf));
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len)
apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
else {
ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff;
apr_snprintf(tmp, sizeof(tmp), "%02x%c", ch , j==7 ? '-' : ' ');
apr_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
}
}
apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len)
apr_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
else {
ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff;
#if APR_CHARSET_EBCDIC
apr_snprintf(tmp, sizeof(tmp), "%c", (ch >= 0x20 && ch <= 0x7F) ? ebcdic_text[j] : '.');
#else /* APR_CHARSET_EBCDIC */
apr_snprintf(tmp, sizeof(tmp), "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.');
#endif /* APR_CHARSET_EBCDIC */
apr_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
}
}
apr_cpystrn(buf+strlen(buf), " |", sizeof(buf)-strlen(buf));
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, srvr,
"%s", buf);
}
if (trunc > 0)
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, srvr,
"| %04ld - <SPACES/NULS>", len + trunc);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, srvr,
"+-------------------------------------------------------------------------+");
return;
}
long ssl_io_data_cb(BIO *bio, int cmd,
MODSSL_BIO_CB_ARG_TYPE *argp,
int argi, long argl, long rc)
{
SSL *ssl;
conn_rec *c;
server_rec *s;
SSLSrvConfigRec *sc;
if ((ssl = (SSL *)BIO_get_callback_arg(bio)) == NULL)
return rc;
if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL)
return rc;
s = c->base_server;
sc = mySrvConfig(s);
if ( cmd == (BIO_CB_WRITE|BIO_CB_RETURN)
|| cmd == (BIO_CB_READ |BIO_CB_RETURN) ) {
if (rc >= 0) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"%s: %s %ld/%d bytes %s BIO#%pp [mem: %pp] %s",
SSL_LIBRARY_NAME,
(cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"),
bio, argp,
(argp != NULL ? "(BIO dump follows)" : "(Oops, no memory buffer?)"));
if ((argp != NULL) && (sc->ssl_log_level >= SSL_LOG_BYTES))
ssl_io_data_dump(s, argp, rc);
}
else {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
"%s: I/O error, %d bytes expected to %s on BIO#%pp [mem: %pp]",
SSL_LIBRARY_NAME, argi,
(cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
bio, argp);
}
}
return rc;
}