diff options
Diffstat (limited to 'buckets/ssl_buckets.c')
-rw-r--r-- | buckets/ssl_buckets.c | 1629 |
1 files changed, 1629 insertions, 0 deletions
diff --git a/buckets/ssl_buckets.c b/buckets/ssl_buckets.c new file mode 100644 index 0000000..3f43543 --- /dev/null +++ b/buckets/ssl_buckets.c @@ -0,0 +1,1629 @@ +/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein + * + * 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. + * + * ---- + * + * For the OpenSSL thread-safety locking code: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally developed by Aaron Bannert and Justin Erenkrantz, eBuilt. + */ + +#include <apr_pools.h> +#include <apr_network_io.h> +#include <apr_portable.h> +#include <apr_strings.h> +#include <apr_base64.h> +#include <apr_version.h> +#include <apr_atomic.h> + +#include "serf.h" +#include "serf_bucket_util.h" + +#include <openssl/bio.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/pkcs12.h> +#include <openssl/x509v3.h> + +#ifndef APR_VERSION_AT_LEAST /* Introduced in APR 1.3.0 */ +#define APR_VERSION_AT_LEAST(major,minor,patch) \ + (((major) < APR_MAJOR_VERSION) \ + || ((major) == APR_MAJOR_VERSION && (minor) < APR_MINOR_VERSION) \ + || ((major) == APR_MAJOR_VERSION && (minor) == APR_MINOR_VERSION && \ + (patch) <= APR_PATCH_VERSION)) +#endif /* APR_VERSION_AT_LEAST */ + +#ifndef APR_ARRAY_PUSH +#define APR_ARRAY_PUSH(ary,type) (*((type *)apr_array_push(ary))) +#endif + + +/*#define SSL_VERBOSE*/ + +/* + * Here's an overview of the SSL bucket's relationship to OpenSSL and serf. + * + * HTTP request: SSLENCRYPT(REQUEST) + * [context.c reads from SSLENCRYPT and writes out to the socket] + * HTTP response: RESPONSE(SSLDECRYPT(SOCKET)) + * [handler function reads from RESPONSE which in turn reads from SSLDECRYPT] + * + * HTTP request read call path: + * + * write_to_connection + * |- serf_bucket_read on SSLENCRYPT + * |- serf_ssl_read + * |- serf_databuf_read + * |- common_databuf_prep + * |- ssl_encrypt + * |- 1. Try to read pending encrypted data; If available, return. + * |- 2. Try to read from ctx->stream [REQUEST bucket] + * |- 3. Call SSL_write with read data + * |- ... + * |- bio_bucket_read can be called + * |- bio_bucket_write with encrypted data + * |- store in sink + * |- 4. If successful, read pending encrypted data and return. + * |- 5. If fails, place read data back in ctx->stream + * + * HTTP response read call path: + * + * read_from_connection + * |- acceptor + * |- handler + * |- ... + * |- serf_bucket_read(SSLDECRYPT) + * |- serf_ssl_read + * |- serf_databuf_read + * |- ssl_decrypt + * |- 1. SSL_read() for pending decrypted data; if any, return. + * |- 2. Try to read from ctx->stream [SOCKET bucket] + * |- 3. Append data to ssl_ctx->source + * |- 4. Call SSL_read() + * |- ... + * |- bio_bucket_write can be called + * |- bio_bucket_read + * |- read data from ssl_ctx->source + * |- If data read, return it. + * |- If an error, set the STATUS value and return. + * + */ + +typedef struct bucket_list { + serf_bucket_t *bucket; + struct bucket_list *next; +} bucket_list_t; + +typedef struct { + /* Helper to read data. Wraps stream. */ + serf_databuf_t databuf; + + /* Our source for more data. */ + serf_bucket_t *stream; + + /* The next set of buckets */ + bucket_list_t *stream_next; + + /* The status of the last thing we read. */ + apr_status_t status; + apr_status_t exhausted; + int exhausted_reset; + + /* Data we've read but not processed. */ + serf_bucket_t *pending; +} serf_ssl_stream_t; + +struct serf_ssl_context_t { + /* How many open buckets refer to this context. */ + int refcount; + + /* The pool that this context uses. */ + apr_pool_t *pool; + + /* The allocator associated with the above pool. */ + serf_bucket_alloc_t *allocator; + + /* Internal OpenSSL parameters */ + SSL_CTX *ctx; + SSL *ssl; + BIO *bio; + + serf_ssl_stream_t encrypt; + serf_ssl_stream_t decrypt; + + /* Client cert callbacks */ + serf_ssl_need_client_cert_t cert_callback; + void *cert_userdata; + apr_pool_t *cert_cache_pool; + const char *cert_file_success; + + /* Client cert PW callbacks */ + serf_ssl_need_cert_password_t cert_pw_callback; + void *cert_pw_userdata; + apr_pool_t *cert_pw_cache_pool; + const char *cert_pw_success; + + /* Server cert callbacks */ + serf_ssl_need_server_cert_t server_cert_callback; + serf_ssl_server_cert_chain_cb_t server_cert_chain_callback; + void *server_cert_userdata; + + const char *cert_path; + + X509 *cached_cert; + EVP_PKEY *cached_cert_pw; + + apr_status_t pending_err; +}; + +typedef struct { + /* The bucket-independent ssl context that this bucket is associated with */ + serf_ssl_context_t *ssl_ctx; + + /* Pointer to the 'right' databuf. */ + serf_databuf_t *databuf; + + /* Pointer to our stream, so we can find it later. */ + serf_bucket_t **our_stream; +} ssl_context_t; + +struct serf_ssl_certificate_t { + X509 *ssl_cert; + int depth; +}; + +/* Returns the amount read. */ +static int bio_bucket_read(BIO *bio, char *in, int inlen) +{ + serf_ssl_context_t *ctx = bio->ptr; + const char *data; + apr_status_t status; + apr_size_t len; + +#ifdef SSL_VERBOSE + printf("bio_bucket_read called for %d bytes\n", inlen); +#endif + + if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN + && BIO_should_read(ctx->bio)) { +#ifdef SSL_VERBOSE + printf("bio_bucket_read waiting: (%d %d %d)\n", + BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), + BIO_get_retry_flags(ctx->bio)); +#endif + /* Falling back... */ + ctx->encrypt.exhausted_reset = 1; + BIO_clear_retry_flags(bio); + } + + status = serf_bucket_read(ctx->decrypt.pending, inlen, &data, &len); + + ctx->decrypt.status = status; +#ifdef SSL_VERBOSE + printf("bio_bucket_read received %d bytes (%d)\n", len, status); +#endif + + if (!SERF_BUCKET_READ_ERROR(status)) { + /* Oh suck. */ + if (len) { + memcpy(in, data, len); + return len; + } + if (APR_STATUS_IS_EOF(status)) { + BIO_set_retry_read(bio); + return -1; + } + } + + return -1; +} + +/* Returns the amount written. */ +static int bio_bucket_write(BIO *bio, const char *in, int inl) +{ + serf_ssl_context_t *ctx = bio->ptr; + serf_bucket_t *tmp; + +#ifdef SSL_VERBOSE + printf("bio_bucket_write called for %d bytes\n", inl); +#endif + if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN + && !BIO_should_read(ctx->bio)) { +#ifdef SSL_VERBOSE + printf("bio_bucket_write waiting: (%d %d %d)\n", + BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), + BIO_get_retry_flags(ctx->bio)); +#endif + /* Falling back... */ + ctx->encrypt.exhausted_reset = 1; + BIO_clear_retry_flags(bio); + } + + tmp = serf_bucket_simple_copy_create(in, inl, + ctx->encrypt.pending->allocator); + + serf_bucket_aggregate_append(ctx->encrypt.pending, tmp); + + return inl; +} + +/* Returns the amount read. */ +static int bio_file_read(BIO *bio, char *in, int inlen) +{ + apr_file_t *file = bio->ptr; + apr_status_t status; + apr_size_t len; + + BIO_clear_retry_flags(bio); + + len = inlen; + status = apr_file_read(file, in, &len); + + if (!SERF_BUCKET_READ_ERROR(status)) { + /* Oh suck. */ + if (APR_STATUS_IS_EOF(status)) { + BIO_set_retry_read(bio); + return -1; + } else { + return len; + } + } + + return -1; +} + +/* Returns the amount written. */ +static int bio_file_write(BIO *bio, const char *in, int inl) +{ + apr_file_t *file = bio->ptr; + apr_size_t nbytes; + + BIO_clear_retry_flags(bio); + + nbytes = inl; + apr_file_write(file, in, &nbytes); + + return nbytes; +} + +static int bio_file_gets(BIO *bio, char *in, int inlen) +{ + return bio_file_read(bio, in, inlen); +} + +static int bio_bucket_create(BIO *bio) +{ + bio->shutdown = 1; + bio->init = 1; + bio->num = -1; + bio->ptr = NULL; + + return 1; +} + +static int bio_bucket_destroy(BIO *bio) +{ + /* Did we already free this? */ + if (bio == NULL) { + return 0; + } + + return 1; +} + +static long bio_bucket_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + long ret = 1; + + switch (cmd) { + default: + /* abort(); */ + break; + case BIO_CTRL_FLUSH: + /* At this point we can't force a flush. */ + break; + case BIO_CTRL_PUSH: + case BIO_CTRL_POP: + ret = 0; + break; + } + return ret; +} + +static BIO_METHOD bio_bucket_method = { + BIO_TYPE_MEM, + "Serf SSL encryption and decryption buckets", + bio_bucket_write, + bio_bucket_read, + NULL, /* Is this called? */ + NULL, /* Is this called? */ + bio_bucket_ctrl, + bio_bucket_create, + bio_bucket_destroy, +#ifdef OPENSSL_VERSION_NUMBER + NULL /* sslc does not have the callback_ctrl field */ +#endif +}; + +static BIO_METHOD bio_file_method = { + BIO_TYPE_FILE, + "Wrapper around APR file structures", + bio_file_write, + bio_file_read, + NULL, /* Is this called? */ + bio_file_gets, /* Is this called? */ + bio_bucket_ctrl, + bio_bucket_create, + bio_bucket_destroy, +#ifdef OPENSSL_VERSION_NUMBER + NULL /* sslc does not have the callback_ctrl field */ +#endif +}; + +static int +validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx) +{ + SSL *ssl; + serf_ssl_context_t *ctx; + X509 *server_cert; + int err, depth; + int failures = 0; + + ssl = X509_STORE_CTX_get_ex_data(store_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + ctx = SSL_get_app_data(ssl); + + server_cert = X509_STORE_CTX_get_current_cert(store_ctx); + depth = X509_STORE_CTX_get_error_depth(store_ctx); + + /* If the certification was found invalid, get the error and convert it to + something our caller will understand. */ + if (! cert_valid) { + err = X509_STORE_CTX_get_error(store_ctx); + + switch(err) { + case X509_V_ERR_CERT_NOT_YET_VALID: + failures |= SERF_SSL_CERT_NOTYETVALID; + break; + case X509_V_ERR_CERT_HAS_EXPIRED: + failures |= SERF_SSL_CERT_EXPIRED; + break; + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + failures |= SERF_SSL_CERT_SELF_SIGNED; + break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_CERT_UNTRUSTED: + case X509_V_ERR_INVALID_CA: + failures |= SERF_SSL_CERT_UNKNOWNCA; + break; + default: + failures |= SERF_SSL_CERT_UNKNOWN_FAILURE; + break; + } + } + + /* Check certificate expiry dates. */ + if (X509_cmp_current_time(X509_get_notBefore(server_cert)) >= 0) { + failures |= SERF_SSL_CERT_NOTYETVALID; + } + else if (X509_cmp_current_time(X509_get_notAfter(server_cert)) <= 0) { + failures |= SERF_SSL_CERT_EXPIRED; + } + + if (ctx->server_cert_callback && + (depth == 0 || failures)) { + apr_status_t status; + serf_ssl_certificate_t *cert; + apr_pool_t *subpool; + + apr_pool_create(&subpool, ctx->pool); + + cert = apr_palloc(subpool, sizeof(serf_ssl_certificate_t)); + cert->ssl_cert = server_cert; + cert->depth = depth; + + /* Callback for further verification. */ + status = ctx->server_cert_callback(ctx->server_cert_userdata, + failures, cert); + if (status == APR_SUCCESS) + cert_valid = 1; + else + /* Pass the error back to the caller through the context-run. */ + ctx->pending_err = status; + apr_pool_destroy(subpool); + } + + if (ctx->server_cert_chain_callback + && (depth == 0 || failures)) { + apr_status_t status; + STACK_OF(X509) *chain; + const serf_ssl_certificate_t **certs; + int certs_len; + apr_pool_t *subpool; + + apr_pool_create(&subpool, ctx->pool); + + /* Borrow the chain to pass to the callback. */ + chain = X509_STORE_CTX_get_chain(store_ctx); + + /* If the chain can't be retrieved, just pass the current + certificate. */ + /* ### can this actually happen with _get_chain() ? */ + if (!chain) { + serf_ssl_certificate_t *cert = apr_palloc(subpool, sizeof(*cert)); + + cert->ssl_cert = server_cert; + cert->depth = depth; + + /* Room for the server_cert and a trailing NULL. */ + certs = apr_palloc(subpool, sizeof(*certs) * 2); + certs[0] = cert; + + certs_len = 1; + } else { + int i; + + certs_len = sk_X509_num(chain); + + /* Room for all the certs and a trailing NULL. */ + certs = apr_palloc(subpool, sizeof(*certs) * (certs_len + 1)); + for (i = 0; i < certs_len; ++i) { + serf_ssl_certificate_t *cert; + + cert = apr_palloc(subpool, sizeof(*cert)); + cert->ssl_cert = sk_X509_value(chain, i); + cert->depth = i; + + certs[i] = cert; + } + } + certs[certs_len] = NULL; + + /* Callback for further verification. */ + status = ctx->server_cert_chain_callback(ctx->server_cert_userdata, + failures, depth, + certs, certs_len); + if (status == APR_SUCCESS) { + cert_valid = 1; + } else { + /* Pass the error back to the caller through the context-run. */ + ctx->pending_err = status; + } + + apr_pool_destroy(subpool); + } + + return cert_valid; +} + +/* This function reads an encrypted stream and returns the decrypted stream. */ +static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize, + char *buf, apr_size_t *len) +{ + serf_ssl_context_t *ctx = baton; + apr_size_t priv_len; + apr_status_t status; + const char *data; + int ssl_len; + +#ifdef SSL_VERBOSE + printf("ssl_decrypt: begin %d\n", bufsize); +#endif + + /* Is there some data waiting to be read? */ + ssl_len = SSL_read(ctx->ssl, buf, bufsize); + if (ssl_len > 0) { +#ifdef SSL_VERBOSE + printf("ssl_decrypt: %d bytes (%d); status: %d; flags: %d\n", + ssl_len, bufsize, ctx->decrypt.status, + BIO_get_retry_flags(ctx->bio)); +#endif + *len = ssl_len; + return APR_SUCCESS; + } + + status = serf_bucket_read(ctx->decrypt.stream, bufsize, &data, &priv_len); + + if (!SERF_BUCKET_READ_ERROR(status) && priv_len) { + serf_bucket_t *tmp; + +#ifdef SSL_VERBOSE + printf("ssl_decrypt: read %d bytes (%d); status: %d\n", priv_len, + bufsize, status); +#endif + + tmp = serf_bucket_simple_copy_create(data, priv_len, + ctx->decrypt.pending->allocator); + + serf_bucket_aggregate_append(ctx->decrypt.pending, tmp); + + ssl_len = SSL_read(ctx->ssl, buf, bufsize); + if (ssl_len < 0) { + int ssl_err; + + ssl_err = SSL_get_error(ctx->ssl, ssl_len); + switch (ssl_err) { + case SSL_ERROR_SYSCALL: + *len = 0; + status = ctx->decrypt.status; + break; + case SSL_ERROR_WANT_READ: + *len = 0; + status = APR_EAGAIN; + break; + case SSL_ERROR_SSL: + *len = 0; + status = ctx->pending_err ? ctx->pending_err : APR_EGENERAL; + ctx->pending_err = 0; + break; + default: + *len = 0; + status = APR_EGENERAL; + break; + } + } + else { + *len = ssl_len; +#ifdef SSL_VERBOSE + printf("---\n%s\n-(%d)-\n", buf, *len); +#endif + } + } + else { + *len = 0; + } +#ifdef SSL_VERBOSE + printf("ssl_decrypt: %d %d %d\n", status, *len, + BIO_get_retry_flags(ctx->bio)); +#endif + return status; +} + +/* This function reads a decrypted stream and returns an encrypted stream. */ +static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize, + char *buf, apr_size_t *len) +{ + const char *data; + apr_size_t interim_bufsize; + serf_ssl_context_t *ctx = baton; + apr_status_t status; + +#ifdef SSL_VERBOSE + printf("ssl_encrypt: begin %d\n", bufsize); +#endif + + /* Try to read already encrypted but unread data first. */ + status = serf_bucket_read(ctx->encrypt.pending, bufsize, &data, len); + if (SERF_BUCKET_READ_ERROR(status)) { + return status; + } + + /* Aha, we read something. Return that now. */ + if (*len) { + memcpy(buf, data, *len); + if (APR_STATUS_IS_EOF(status)) { + status = APR_SUCCESS; + } +#ifdef SSL_VERBOSE + printf("ssl_encrypt: %d %d %d (quick read)\n", status, *len, + BIO_get_retry_flags(ctx->bio)); +#endif + return status; + } + + if (BIO_should_retry(ctx->bio) && BIO_should_write(ctx->bio)) { +#ifdef SSL_VERBOSE + printf("ssl_encrypt: %d %d %d (should write exit)\n", status, *len, + BIO_get_retry_flags(ctx->bio)); +#endif + return APR_EAGAIN; + } + + /* If we were previously blocked, unblock ourselves now. */ + if (BIO_should_read(ctx->bio)) { +#ifdef SSL_VERBOSE + printf("ssl_encrypt: reset %d %d (%d %d %d)\n", status, + ctx->encrypt.status, + BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), + BIO_get_retry_flags(ctx->bio)); +#endif + ctx->encrypt.status = APR_SUCCESS; + ctx->encrypt.exhausted_reset = 0; + } + + /* Oh well, read from our stream now. */ + interim_bufsize = bufsize; + do { + apr_size_t interim_len; + + if (!ctx->encrypt.status) { + struct iovec vecs[64]; + int vecs_read; + + status = serf_bucket_read_iovec(ctx->encrypt.stream, + interim_bufsize, 64, vecs, + &vecs_read); + + if (!SERF_BUCKET_READ_ERROR(status) && vecs_read) { + char *vecs_data; + int i, cur, vecs_data_len; + int ssl_len; + + /* Combine the buffers of the iovec into one buffer, as + that is with SSL_write requires. */ + vecs_data_len = 0; + for (i = 0; i < vecs_read; i++) { + vecs_data_len += vecs[i].iov_len; + } + + vecs_data = serf_bucket_mem_alloc(ctx->allocator, + vecs_data_len); + + cur = 0; + for (i = 0; i < vecs_read; i++) { + memcpy(vecs_data + cur, vecs[i].iov_base, vecs[i].iov_len); + cur += vecs[i].iov_len; + } + + interim_bufsize -= vecs_data_len; + interim_len = vecs_data_len; + +#ifdef SSL_VERBOSE + printf("ssl_encrypt: bucket read %d bytes; status %d\n", + interim_len, status); + printf("---\n%s\n-(%d)-\n", vecs_data, interim_len); +#endif + /* Stash our status away. */ + ctx->encrypt.status = status; + + ssl_len = SSL_write(ctx->ssl, vecs_data, interim_len); +#ifdef SSL_VERBOSE + printf("ssl_encrypt: SSL write: %d\n", ssl_len); +#endif + /* We're done. */ + serf_bucket_mem_free(ctx->allocator, vecs_data); + + /* If we failed to write... */ + if (ssl_len < 0) { + int ssl_err; + + /* Ah, bugger. We need to put that data back. */ + serf_bucket_aggregate_prepend_iovec(ctx->encrypt.stream, + vecs, vecs_read); + + ssl_err = SSL_get_error(ctx->ssl, ssl_len); +#ifdef SSL_VERBOSE + printf("ssl_encrypt: SSL write error: %d\n", ssl_err); +#endif + if (ssl_err == SSL_ERROR_SYSCALL) { + status = ctx->encrypt.status; + if (SERF_BUCKET_READ_ERROR(status)) { + return status; + } + } + else { + /* Oh, no. */ + if (ssl_err == SSL_ERROR_WANT_READ) { + status = SERF_ERROR_WAIT_CONN; + } + else { + status = APR_EGENERAL; + } + } +#ifdef SSL_VERBOSE + printf("ssl_encrypt: SSL write error: %d %d\n", status, *len); +#endif + } + } + } + else { + interim_len = 0; + *len = 0; + status = ctx->encrypt.status; + } + + } while (!status && interim_bufsize); + + /* Okay, we exhausted our underlying stream. */ + if (!SERF_BUCKET_READ_ERROR(status)) { + apr_status_t agg_status; + struct iovec vecs[64]; + int vecs_read, i; + + /* We read something! */ + agg_status = serf_bucket_read_iovec(ctx->encrypt.pending, bufsize, + 64, vecs, &vecs_read); + *len = 0; + for (i = 0; i < vecs_read; i++) { + memcpy(buf + *len, vecs[i].iov_base, vecs[i].iov_len); + *len += vecs[i].iov_len; + } + +#ifdef SSL_VERBOSE + printf("ssl_encrypt read agg: %d %d %d %d\n", status, agg_status, + ctx->encrypt.status, *len); +#endif + + if (!agg_status) { + status = agg_status; + } + } + + if (status == SERF_ERROR_WAIT_CONN + && BIO_should_retry(ctx->bio) && BIO_should_read(ctx->bio)) { + ctx->encrypt.exhausted = ctx->encrypt.status; + ctx->encrypt.status = SERF_ERROR_WAIT_CONN; + } + +#ifdef SSL_VERBOSE + printf("ssl_encrypt finished: %d %d (%d %d %d)\n", status, *len, + BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio), + BIO_get_retry_flags(ctx->bio)); +#endif + return status; +} + +#if APR_HAS_THREADS +static apr_pool_t *ssl_pool; +static apr_thread_mutex_t **ssl_locks; + +typedef struct CRYPTO_dynlock_value { + apr_thread_mutex_t *lock; +} CRYPTO_dynlock_value; + +static CRYPTO_dynlock_value *ssl_dyn_create(const char* file, int line) +{ + CRYPTO_dynlock_value *l; + apr_status_t rv; + + l = apr_palloc(ssl_pool, sizeof(CRYPTO_dynlock_value)); + rv = apr_thread_mutex_create(&l->lock, APR_THREAD_MUTEX_DEFAULT, ssl_pool); + if (rv != APR_SUCCESS) { + /* FIXME: return error here */ + } + return l; +} + +static void ssl_dyn_lock(int mode, CRYPTO_dynlock_value *l, const char *file, + int line) +{ + if (mode & CRYPTO_LOCK) { + apr_thread_mutex_lock(l->lock); + } + else if (mode & CRYPTO_UNLOCK) { + apr_thread_mutex_unlock(l->lock); + } +} + +static void ssl_dyn_destroy(CRYPTO_dynlock_value *l, const char *file, + int line) +{ + apr_thread_mutex_destroy(l->lock); +} + +static void ssl_lock(int mode, int n, const char *file, int line) +{ + if (mode & CRYPTO_LOCK) { + apr_thread_mutex_lock(ssl_locks[n]); + } + else if (mode & CRYPTO_UNLOCK) { + apr_thread_mutex_unlock(ssl_locks[n]); + } +} + +static unsigned long ssl_id(void) +{ + /* FIXME: This is lame and not portable. -aaron */ + return (unsigned long) apr_os_thread_current(); +} + +static apr_status_t cleanup_ssl(void *data) +{ + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + + return APR_SUCCESS; +} + +#endif + +static apr_uint32_t have_init_ssl = 0; + +static void init_ssl_libraries(void) +{ + apr_uint32_t val; +#if APR_VERSION_AT_LEAST(1,0,0) + val = apr_atomic_xchg32(&have_init_ssl, 1); +#else + val = apr_atomic_cas(&have_init_ssl, 1, 0); +#endif + + if (!val) { +#if APR_HAS_THREADS + int i, numlocks; +#endif + CRYPTO_malloc_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + +#if APR_HAS_THREADS + numlocks = CRYPTO_num_locks(); + apr_pool_create(&ssl_pool, NULL); + ssl_locks = apr_palloc(ssl_pool, sizeof(apr_thread_mutex_t*)*numlocks); + for (i = 0; i < numlocks; i++) { + apr_status_t rv; + + /* Intraprocess locks don't /need/ a filename... */ + rv = apr_thread_mutex_create(&ssl_locks[i], + APR_THREAD_MUTEX_DEFAULT, ssl_pool); + if (rv != APR_SUCCESS) { + /* FIXME: error out here */ + } + } + CRYPTO_set_locking_callback(ssl_lock); + CRYPTO_set_id_callback(ssl_id); + CRYPTO_set_dynlock_create_callback(ssl_dyn_create); + CRYPTO_set_dynlock_lock_callback(ssl_dyn_lock); + CRYPTO_set_dynlock_destroy_callback(ssl_dyn_destroy); + + apr_pool_cleanup_register(ssl_pool, NULL, cleanup_ssl, cleanup_ssl); +#endif + } +} + +static int ssl_need_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey) +{ + serf_ssl_context_t *ctx = SSL_get_app_data(ssl); + apr_status_t status; + + if (ctx->cached_cert) { + *cert = ctx->cached_cert; + *pkey = ctx->cached_cert_pw; + return 1; + } + + while (ctx->cert_callback) { + const char *cert_path; + apr_file_t *cert_file; + BIO *bio; + PKCS12 *p12; + int i; + int retrying_success = 0; + + if (ctx->cert_file_success) { + status = APR_SUCCESS; + cert_path = ctx->cert_file_success; + ctx->cert_file_success = NULL; + retrying_success = 1; + } else { + status = ctx->cert_callback(ctx->cert_userdata, &cert_path); + } + + if (status || !cert_path) { + break; + } + + /* Load the x.509 cert file stored in PKCS12 */ + status = apr_file_open(&cert_file, cert_path, APR_READ, APR_OS_DEFAULT, + ctx->pool); + + if (status) { + continue; + } + + bio = BIO_new(&bio_file_method); + bio->ptr = cert_file; + + ctx->cert_path = cert_path; + p12 = d2i_PKCS12_bio(bio, NULL); + apr_file_close(cert_file); + + i = PKCS12_parse(p12, NULL, pkey, cert, NULL); + + if (i == 1) { + PKCS12_free(p12); + ctx->cached_cert = *cert; + ctx->cached_cert_pw = *pkey; + if (!retrying_success && ctx->cert_cache_pool) { + const char *c; + + c = apr_pstrdup(ctx->cert_cache_pool, ctx->cert_path); + + apr_pool_userdata_setn(c, "serf:ssl:cert", + apr_pool_cleanup_null, + ctx->cert_cache_pool); + } + return 1; + } + else { + int err = ERR_get_error(); + ERR_clear_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 && + ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) { + if (ctx->cert_pw_callback) { + const char *password; + + if (ctx->cert_pw_success) { + status = APR_SUCCESS; + password = ctx->cert_pw_success; + ctx->cert_pw_success = NULL; + } else { + status = ctx->cert_pw_callback(ctx->cert_pw_userdata, + ctx->cert_path, + &password); + } + + if (!status && password) { + i = PKCS12_parse(p12, password, pkey, cert, NULL); + if (i == 1) { + PKCS12_free(p12); + ctx->cached_cert = *cert; + ctx->cached_cert_pw = *pkey; + if (!retrying_success && ctx->cert_cache_pool) { + const char *c; + + c = apr_pstrdup(ctx->cert_cache_pool, + ctx->cert_path); + + apr_pool_userdata_setn(c, "serf:ssl:cert", + apr_pool_cleanup_null, + ctx->cert_cache_pool); + } + if (!retrying_success && ctx->cert_pw_cache_pool) { + const char *c; + + c = apr_pstrdup(ctx->cert_pw_cache_pool, + password); + + apr_pool_userdata_setn(c, "serf:ssl:certpw", + apr_pool_cleanup_null, + ctx->cert_pw_cache_pool); + } + return 1; + } + } + } + PKCS12_free(p12); + return 0; + } + else { + printf("OpenSSL cert error: %d %d %d\n", ERR_GET_LIB(err), + ERR_GET_FUNC(err), + ERR_GET_REASON(err)); + PKCS12_free(p12); + } + } + } + + return 0; +} + + +void serf_ssl_client_cert_provider_set( + serf_ssl_context_t *context, + serf_ssl_need_client_cert_t callback, + void *data, + void *cache_pool) +{ + context->cert_callback = callback; + context->cert_userdata = data; + context->cert_cache_pool = cache_pool; + if (context->cert_cache_pool) { + apr_pool_userdata_get((void**)&context->cert_file_success, + "serf:ssl:cert", cache_pool); + } +} + + +void serf_ssl_client_cert_password_set( + serf_ssl_context_t *context, + serf_ssl_need_cert_password_t callback, + void *data, + void *cache_pool) +{ + context->cert_pw_callback = callback; + context->cert_pw_userdata = data; + context->cert_pw_cache_pool = cache_pool; + if (context->cert_pw_cache_pool) { + apr_pool_userdata_get((void**)&context->cert_pw_success, + "serf:ssl:certpw", cache_pool); + } +} + + +void serf_ssl_server_cert_callback_set( + serf_ssl_context_t *context, + serf_ssl_need_server_cert_t callback, + void *data) +{ + context->server_cert_callback = callback; + context->server_cert_userdata = data; +} + +void serf_ssl_server_cert_chain_callback_set( + serf_ssl_context_t *context, + serf_ssl_need_server_cert_t cert_callback, + serf_ssl_server_cert_chain_cb_t cert_chain_callback, + void *data) +{ + context->server_cert_callback = cert_callback; + context->server_cert_chain_callback = cert_chain_callback; + context->server_cert_userdata = data; +} + +static serf_ssl_context_t *ssl_init_context(void) +{ + serf_ssl_context_t *ssl_ctx; + apr_pool_t *pool; + serf_bucket_alloc_t *allocator; + + init_ssl_libraries(); + + apr_pool_create(&pool, NULL); + allocator = serf_bucket_allocator_create(pool, NULL, NULL); + + ssl_ctx = serf_bucket_mem_alloc(allocator, sizeof(*ssl_ctx)); + + ssl_ctx->refcount = 0; + ssl_ctx->pool = pool; + ssl_ctx->allocator = allocator; + + ssl_ctx->ctx = SSL_CTX_new(SSLv23_client_method()); + + SSL_CTX_set_client_cert_cb(ssl_ctx->ctx, ssl_need_client_cert); + ssl_ctx->cached_cert = 0; + ssl_ctx->cached_cert_pw = 0; + ssl_ctx->pending_err = APR_SUCCESS; + + ssl_ctx->cert_callback = NULL; + ssl_ctx->cert_pw_callback = NULL; + ssl_ctx->server_cert_callback = NULL; + ssl_ctx->server_cert_chain_callback = NULL; + + SSL_CTX_set_verify(ssl_ctx->ctx, SSL_VERIFY_PEER, + validate_server_certificate); + SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_ALL); + + ssl_ctx->ssl = SSL_new(ssl_ctx->ctx); + ssl_ctx->bio = BIO_new(&bio_bucket_method); + ssl_ctx->bio->ptr = ssl_ctx; + + SSL_set_bio(ssl_ctx->ssl, ssl_ctx->bio, ssl_ctx->bio); + + SSL_set_connect_state(ssl_ctx->ssl); + + SSL_set_app_data(ssl_ctx->ssl, ssl_ctx); + + ssl_ctx->encrypt.stream = NULL; + ssl_ctx->encrypt.stream_next = NULL; + ssl_ctx->encrypt.pending = serf_bucket_aggregate_create(allocator); + ssl_ctx->encrypt.status = APR_SUCCESS; + serf_databuf_init(&ssl_ctx->encrypt.databuf); + ssl_ctx->encrypt.databuf.read = ssl_encrypt; + ssl_ctx->encrypt.databuf.read_baton = ssl_ctx; + + ssl_ctx->decrypt.stream = NULL; + ssl_ctx->decrypt.pending = serf_bucket_aggregate_create(allocator); + ssl_ctx->decrypt.status = APR_SUCCESS; + serf_databuf_init(&ssl_ctx->decrypt.databuf); + ssl_ctx->decrypt.databuf.read = ssl_decrypt; + ssl_ctx->decrypt.databuf.read_baton = ssl_ctx; + + return ssl_ctx; +} + +static apr_status_t ssl_free_context( + serf_ssl_context_t *ssl_ctx) +{ + apr_pool_t *p; + + /* If never had the pending buckets, don't try to free them. */ + if (ssl_ctx->decrypt.pending != NULL) { + serf_bucket_destroy(ssl_ctx->decrypt.pending); + } + if (ssl_ctx->encrypt.pending != NULL) { + serf_bucket_destroy(ssl_ctx->encrypt.pending); + } + + /* SSL_free implicitly frees the underlying BIO. */ + SSL_free(ssl_ctx->ssl); + SSL_CTX_free(ssl_ctx->ctx); + + p = ssl_ctx->pool; + + serf_bucket_mem_free(ssl_ctx->allocator, ssl_ctx); + apr_pool_destroy(p); + + return APR_SUCCESS; +} + +static serf_bucket_t * serf_bucket_ssl_create( + serf_ssl_context_t *ssl_ctx, + serf_bucket_alloc_t *allocator, + const serf_bucket_type_t *type) +{ + ssl_context_t *ctx; + + ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx)); + if (!ssl_ctx) { + ctx->ssl_ctx = ssl_init_context(); + } + else { + ctx->ssl_ctx = ssl_ctx; + } + ctx->ssl_ctx->refcount++; + + return serf_bucket_create(type, allocator, ctx); +} + +apr_status_t serf_ssl_set_hostname(serf_ssl_context_t *context, + const char * hostname) +{ +#ifdef SSL_set_tlsext_host_name + if (SSL_set_tlsext_host_name(context->ssl, hostname) != 1) { + ERR_clear_error(); + } +#endif + return APR_SUCCESS; +} + +apr_status_t serf_ssl_use_default_certificates(serf_ssl_context_t *ssl_ctx) +{ + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx->ctx); + + int result = X509_STORE_set_default_paths(store); + + return result ? APR_SUCCESS : APR_EGENERAL; +} + +apr_status_t serf_ssl_load_cert_file( + serf_ssl_certificate_t **cert, + const char *file_path, + apr_pool_t *pool) +{ + FILE *fp = fopen(file_path, "r"); + + if (fp) { + X509 *ssl_cert = PEM_read_X509(fp, NULL, NULL, NULL); + fclose(fp); + + if (ssl_cert) { + *cert = apr_palloc(pool, sizeof(serf_ssl_certificate_t)); + (*cert)->ssl_cert = ssl_cert; + + return APR_SUCCESS; + } + } + + return APR_EGENERAL; +} + + +apr_status_t serf_ssl_trust_cert( + serf_ssl_context_t *ssl_ctx, + serf_ssl_certificate_t *cert) +{ + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx->ctx); + + int result = X509_STORE_add_cert(store, cert->ssl_cert); + + return result ? APR_SUCCESS : APR_EGENERAL; +} + + +serf_bucket_t *serf_bucket_ssl_decrypt_create( + serf_bucket_t *stream, + serf_ssl_context_t *ssl_ctx, + serf_bucket_alloc_t *allocator) +{ + serf_bucket_t *bkt; + ssl_context_t *ctx; + + bkt = serf_bucket_ssl_create(ssl_ctx, allocator, + &serf_bucket_type_ssl_decrypt); + + ctx = bkt->data; + + ctx->databuf = &ctx->ssl_ctx->decrypt.databuf; + if (ctx->ssl_ctx->decrypt.stream != NULL) { + return NULL; + } + ctx->ssl_ctx->decrypt.stream = stream; + ctx->our_stream = &ctx->ssl_ctx->decrypt.stream; + + return bkt; +} + + +serf_ssl_context_t *serf_bucket_ssl_decrypt_context_get( + serf_bucket_t *bucket) +{ + ssl_context_t *ctx = bucket->data; + return ctx->ssl_ctx; +} + + +serf_bucket_t *serf_bucket_ssl_encrypt_create( + serf_bucket_t *stream, + serf_ssl_context_t *ssl_ctx, + serf_bucket_alloc_t *allocator) +{ + serf_bucket_t *bkt; + ssl_context_t *ctx; + + bkt = serf_bucket_ssl_create(ssl_ctx, allocator, + &serf_bucket_type_ssl_encrypt); + + ctx = bkt->data; + + ctx->databuf = &ctx->ssl_ctx->encrypt.databuf; + ctx->our_stream = &ctx->ssl_ctx->encrypt.stream; + if (ctx->ssl_ctx->encrypt.stream == NULL) { + serf_bucket_t *tmp = serf_bucket_aggregate_create(stream->allocator); + serf_bucket_aggregate_append(tmp, stream); + ctx->ssl_ctx->encrypt.stream = tmp; + } + else { + bucket_list_t *new_list; + + new_list = serf_bucket_mem_alloc(ctx->ssl_ctx->allocator, + sizeof(*new_list)); + new_list->bucket = stream; + new_list->next = NULL; + if (ctx->ssl_ctx->encrypt.stream_next == NULL) { + ctx->ssl_ctx->encrypt.stream_next = new_list; + } + else { + bucket_list_t *scan = ctx->ssl_ctx->encrypt.stream_next; + + while (scan->next != NULL) + scan = scan->next; + scan->next = new_list; + } + } + + return bkt; +} + + +serf_ssl_context_t *serf_bucket_ssl_encrypt_context_get( + serf_bucket_t *bucket) +{ + ssl_context_t *ctx = bucket->data; + return ctx->ssl_ctx; +} + +/* Functions to read a serf_ssl_certificate structure. */ + +/* Creates a hash_table with keys (E, CN, OU, O, L, ST and C). */ +static apr_hash_t * +convert_X509_NAME_to_table(X509_NAME *org, apr_pool_t *pool) +{ + char buf[1024]; + int ret; + + apr_hash_t *tgt = apr_hash_make(pool); + + ret = X509_NAME_get_text_by_NID(org, + NID_commonName, + buf, 1024); + if (ret != -1) + apr_hash_set(tgt, "CN", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + ret = X509_NAME_get_text_by_NID(org, + NID_pkcs9_emailAddress, + buf, 1024); + if (ret != -1) + apr_hash_set(tgt, "E", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + ret = X509_NAME_get_text_by_NID(org, + NID_organizationalUnitName, + buf, 1024); + if (ret != -1) + apr_hash_set(tgt, "OU", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + ret = X509_NAME_get_text_by_NID(org, + NID_organizationName, + buf, 1024); + if (ret != -1) + apr_hash_set(tgt, "O", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + ret = X509_NAME_get_text_by_NID(org, + NID_localityName, + buf, 1024); + if (ret != -1) + apr_hash_set(tgt, "L", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + ret = X509_NAME_get_text_by_NID(org, + NID_stateOrProvinceName, + buf, 1024); + if (ret != -1) + apr_hash_set(tgt, "ST", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + ret = X509_NAME_get_text_by_NID(org, + NID_countryName, + buf, 1024); + if (ret != -1) + apr_hash_set(tgt, "C", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); + + return tgt; +} + + +int serf_ssl_cert_depth(const serf_ssl_certificate_t *cert) +{ + return cert->depth; +} + + +apr_hash_t *serf_ssl_cert_issuer( + const serf_ssl_certificate_t *cert, + apr_pool_t *pool) +{ + X509_NAME *issuer = X509_get_issuer_name(cert->ssl_cert); + + if (!issuer) + return NULL; + + return convert_X509_NAME_to_table(issuer, pool); +} + + +apr_hash_t *serf_ssl_cert_subject( + const serf_ssl_certificate_t *cert, + apr_pool_t *pool) +{ + X509_NAME *subject = X509_get_subject_name(cert->ssl_cert); + + if (!subject) + return NULL; + + return convert_X509_NAME_to_table(subject, pool); +} + + +apr_hash_t *serf_ssl_cert_certificate( + const serf_ssl_certificate_t *cert, + apr_pool_t *pool) +{ + apr_hash_t *tgt = apr_hash_make(pool); + unsigned int md_size, i; + unsigned char md[EVP_MAX_MD_SIZE]; + BIO *bio; + STACK_OF(GENERAL_NAME) *names; + + /* sha1 fingerprint */ + if (X509_digest(cert->ssl_cert, EVP_sha1(), md, &md_size)) { + const char hex[] = "0123456789ABCDEF"; + char fingerprint[EVP_MAX_MD_SIZE * 3]; + + for (i=0; i<md_size; i++) { + fingerprint[3*i] = hex[(md[i] & 0xf0) >> 4]; + fingerprint[(3*i)+1] = hex[(md[i] & 0x0f)]; + fingerprint[(3*i)+2] = ':'; + } + if (md_size > 0) + fingerprint[(3*(md_size-1))+2] = '\0'; + else + fingerprint[0] = '\0'; + + apr_hash_set(tgt, "sha1", APR_HASH_KEY_STRING, + apr_pstrdup(pool, fingerprint)); + } + + /* set expiry dates */ + bio = BIO_new(BIO_s_mem()); + if (bio) { + ASN1_TIME *notBefore, *notAfter; + char buf[256]; + + memset (buf, 0, sizeof (buf)); + notBefore = X509_get_notBefore(cert->ssl_cert); + if (ASN1_TIME_print(bio, notBefore)) { + BIO_read(bio, buf, 255); + apr_hash_set(tgt, "notBefore", APR_HASH_KEY_STRING, + apr_pstrdup(pool, buf)); + } + memset (buf, 0, sizeof (buf)); + notAfter = X509_get_notAfter(cert->ssl_cert); + if (ASN1_TIME_print(bio, notAfter)) { + BIO_read(bio, buf, 255); + apr_hash_set(tgt, "notAfter", APR_HASH_KEY_STRING, + apr_pstrdup(pool, buf)); + } + } + BIO_free(bio); + + /* Get subjectAltNames */ + names = X509_get_ext_d2i(cert->ssl_cert, NID_subject_alt_name, NULL, NULL); + if (names) { + int names_count = sk_GENERAL_NAME_num(names); + + apr_array_header_t *san_arr = apr_array_make(pool, names_count, + sizeof(char*)); + apr_hash_set(tgt, "subjectAltName", APR_HASH_KEY_STRING, san_arr); + for (i = 0; i < names_count; i++) { + char *p = NULL; + GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, i); + + switch (nm->type) { + case GEN_DNS: + p = apr_pstrmemdup(pool, (const char *)nm->d.ia5->data, + nm->d.ia5->length); + break; + default: + /* Don't know what to do - skip. */ + break; + } + if (p) { + APR_ARRAY_PUSH(san_arr, char*) = p; + } + } + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + } + + return tgt; +} + + +const char *serf_ssl_cert_export( + const serf_ssl_certificate_t *cert, + apr_pool_t *pool) +{ + char *binary_cert; + char *encoded_cert; + int len; + unsigned char *unused; + + /* find the length of the DER encoding. */ + len = i2d_X509(cert->ssl_cert, NULL); + if (len < 0) { + return NULL; + } + + binary_cert = apr_palloc(pool, len); + unused = (unsigned char *)binary_cert; + len = i2d_X509(cert->ssl_cert, &unused); /* unused is incremented */ + if (len < 0) { + return NULL; + } + + encoded_cert = apr_palloc(pool, apr_base64_encode_len(len)); + apr_base64_encode(encoded_cert, binary_cert, len); + + return encoded_cert; +} + +static void serf_ssl_destroy_and_data(serf_bucket_t *bucket) +{ + ssl_context_t *ctx = bucket->data; + + if (!--ctx->ssl_ctx->refcount) { + ssl_free_context(ctx->ssl_ctx); + } + + serf_default_destroy_and_data(bucket); +} + +static void serf_ssl_decrypt_destroy_and_data(serf_bucket_t *bucket) +{ + ssl_context_t *ctx = bucket->data; + + serf_bucket_destroy(*ctx->our_stream); + + serf_ssl_destroy_and_data(bucket); +} + +static void serf_ssl_encrypt_destroy_and_data(serf_bucket_t *bucket) +{ + ssl_context_t *ctx = bucket->data; + serf_ssl_context_t *ssl_ctx = ctx->ssl_ctx; + + if (ssl_ctx->encrypt.stream == *ctx->our_stream) { + serf_bucket_destroy(*ctx->our_stream); + serf_bucket_destroy(ssl_ctx->encrypt.pending); + + /* Reset our encrypted status and databuf. */ + ssl_ctx->encrypt.status = APR_SUCCESS; + ssl_ctx->encrypt.databuf.status = APR_SUCCESS; + + /* Advance to the next stream - if we have one. */ + if (ssl_ctx->encrypt.stream_next == NULL) { + ssl_ctx->encrypt.stream = NULL; + ssl_ctx->encrypt.pending = NULL; + } + else { + bucket_list_t *cur; + + cur = ssl_ctx->encrypt.stream_next; + ssl_ctx->encrypt.stream = cur->bucket; + ssl_ctx->encrypt.pending = + serf_bucket_aggregate_create(cur->bucket->allocator); + ssl_ctx->encrypt.stream_next = cur->next; + serf_bucket_mem_free(ssl_ctx->allocator, cur); + } + } + else { + /* Ah, darn. We haven't sent this one along yet. */ + return; + } + serf_ssl_destroy_and_data(bucket); +} + +static apr_status_t serf_ssl_read(serf_bucket_t *bucket, + apr_size_t requested, + const char **data, apr_size_t *len) +{ + ssl_context_t *ctx = bucket->data; + + return serf_databuf_read(ctx->databuf, requested, data, len); +} + +static apr_status_t serf_ssl_readline(serf_bucket_t *bucket, + int acceptable, int *found, + const char **data, + apr_size_t *len) +{ + ssl_context_t *ctx = bucket->data; + + return serf_databuf_readline(ctx->databuf, acceptable, found, data, len); +} + +static apr_status_t serf_ssl_peek(serf_bucket_t *bucket, + const char **data, + apr_size_t *len) +{ + ssl_context_t *ctx = bucket->data; + + return serf_databuf_peek(ctx->databuf, data, len); +} + + +const serf_bucket_type_t serf_bucket_type_ssl_encrypt = { + "SSLENCRYPT", + serf_ssl_read, + serf_ssl_readline, + serf_default_read_iovec, + serf_default_read_for_sendfile, + serf_default_read_bucket, + serf_ssl_peek, + serf_ssl_encrypt_destroy_and_data, +}; + +const serf_bucket_type_t serf_bucket_type_ssl_decrypt = { + "SSLDECRYPT", + serf_ssl_read, + serf_ssl_readline, + serf_default_read_iovec, + serf_default_read_for_sendfile, + serf_default_read_bucket, + serf_ssl_peek, + serf_ssl_decrypt_destroy_and_data, +}; |