blob: a460bd7ea67cf9ea039493364e9a1ef84bdab398 [file] [log] [blame]
/* Copyright 2013 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.
*/
#include "serf.h"
#include "serf_private.h"
#include "serf_bucket_util.h"
#include "bucket_private.h"
typedef struct serf_ssl_bucket_t {
serf_bucket_t bucket;
/* context (including the type of this bucket) shared between encrypt and
decrypt ssl_bucket */
serf_ssl_context_t *ssl_ctx;
/** the allocator used for this context (needed at destroy time) */
serf_bucket_alloc_t *allocator;
} serf_ssl_bucket_t;
struct serf_ssl_context_t
{
/* How many open buckets refer to this context. */
int refcount;
/* Which SSL implementation is used for this context. */
const serf_ssl_bucket_type_t *type;
/** implementation specific context */
void *impl_ctx;
};
void *serf__ssl_get_impl_context(serf_ssl_context_t *ssl_ctx)
{
return ssl_ctx->impl_ctx;
}
static const serf_ssl_bucket_type_t *decide_ssl_bucket_type(void)
{
apr_uint32_t bucket_impls = serf_config_get_bucket_impls();
/* Prefer SSL implementation integrated in host platform, depending
on what's builtin and what the application allows. */
#ifdef SERF_HAVE_MACOSXSSL
if (bucket_impls & SERF_IMPL_SSL_MACOSXSSL)
return &serf_ssl_bucket_type_macosxssl;
#endif
#ifdef SERF_HAVE_OPENSSL
if (bucket_impls & SERF_IMPL_SSL_OPENSSL)
return &serf_ssl_bucket_type_openssl;
#endif
return NULL;
}
void serf_ssl_client_cert_provider_set(
serf_ssl_context_t *ssl_ctx,
serf_ssl_need_client_cert_t callback,
void *data,
void *cache_pool)
{
return ssl_ctx->type->client_cert_provider_set(ssl_ctx->impl_ctx, callback,
data, cache_pool);
}
void serf_ssl_client_cert_password_set(
serf_ssl_context_t *ssl_ctx,
serf_ssl_need_cert_password_t callback,
void *data,
void *cache_pool)
{
return ssl_ctx->type->client_cert_password_set(ssl_ctx->impl_ctx, callback,
data, cache_pool);
}
void serf_ssl_identity_provider_set(serf_ssl_context_t *ssl_ctx,
serf_ssl_need_identity_t callback,
void *data,
void *cache_pool)
{
return ssl_ctx->type->identity_provider_set(ssl_ctx->impl_ctx, callback,
data, cache_pool);
}
void serf_ssl_identity_password_callback_set(
serf_ssl_context_t *ssl_ctx,
serf_ssl_need_cert_password_t callback,
void *data,
void *cache_pool)
{
return ssl_ctx->type->client_cert_password_set(ssl_ctx->impl_ctx, callback,
data, cache_pool);
}
void serf_ssl_server_cert_callback_set(
serf_ssl_context_t *ssl_ctx,
serf_ssl_need_server_cert_t callback,
void *data)
{
return ssl_ctx->type->server_cert_callback_set(ssl_ctx->impl_ctx,
callback, data);
}
void serf_ssl_server_cert_chain_callback_set(
serf_ssl_context_t *ssl_ctx,
serf_ssl_need_server_cert_t cert_callback,
serf_ssl_server_cert_chain_cb_t cert_chain_callback,
void *data)
{
return ssl_ctx->type->server_cert_chain_callback_set(ssl_ctx->impl_ctx,
cert_callback,
cert_chain_callback,
data);
}
apr_status_t serf_ssl_set_hostname(serf_ssl_context_t *ssl_ctx,
const char * hostname)
{
return ssl_ctx->type->set_hostname(ssl_ctx->impl_ctx, hostname);
}
apr_status_t
serf_ssl_use_compression(serf_ssl_context_t *ssl_ctx, int enabled)
{
return ssl_ctx->type->use_compression(ssl_ctx->impl_ctx, enabled);
}
serf_bucket_t *serf_bucket_ssl_decrypt_create(
serf_bucket_t *stream,
serf_ssl_context_t *ssl_ctx,
serf_bucket_alloc_t *allocator)
{
const serf_ssl_bucket_type_t *type = decide_ssl_bucket_type();
serf_ssl_bucket_t *ssl_bkt;
/* If no SSL implementation is available or allowed, we can't create
a bucket. */
if (!type)
return NULL;
ssl_bkt = serf_bucket_mem_alloc(allocator, sizeof(*ssl_bkt));
ssl_bkt->allocator = allocator;
if (!ssl_ctx) {
ssl_ctx = serf_bucket_mem_alloc(allocator, sizeof(*ssl_ctx));
ssl_ctx->type = type;
ssl_ctx->refcount = 0;
ssl_ctx->impl_ctx = NULL;
}
ssl_ctx->impl_ctx = type->decrypt_create(&ssl_bkt->bucket,
stream,
ssl_ctx->impl_ctx,
allocator);
ssl_ctx->refcount++;
ssl_bkt->ssl_ctx = ssl_ctx;
return (serf_bucket_t*)ssl_bkt;
}
serf_ssl_context_t *serf_bucket_ssl_decrypt_context_get(
serf_bucket_t *bucket)
{
serf_ssl_bucket_t *ssl_bucket = (serf_ssl_bucket_t *)bucket;
return ssl_bucket->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)
{
const serf_ssl_bucket_type_t *type = decide_ssl_bucket_type();
serf_ssl_bucket_t *ssl_bkt;
/* If no SSL implementation is available or allowed, we can't create
a bucket. */
if (!type)
return NULL;
ssl_bkt = serf_bucket_mem_alloc(allocator, sizeof(*ssl_bkt));
ssl_bkt->allocator = allocator;
if (!ssl_ctx) {
ssl_ctx = serf_bucket_mem_alloc(allocator, sizeof(*ssl_ctx));
ssl_ctx->type = type;
ssl_ctx->refcount = 0;
ssl_ctx->impl_ctx = NULL;
}
ssl_ctx->impl_ctx = type->encrypt_create(&ssl_bkt->bucket,
stream,
ssl_ctx->impl_ctx,
allocator);
ssl_ctx->refcount++;
ssl_bkt->ssl_ctx = ssl_ctx;
return (serf_bucket_t*)ssl_bkt;
}
serf_ssl_context_t *
serf_bucket_ssl_encrypt_context_get(serf_bucket_t *bucket)
{
serf_ssl_bucket_t *ssl_bucket = (serf_ssl_bucket_t *)bucket;
return ssl_bucket->ssl_ctx;
}
void
serf_bucket_ssl_destroy_and_data(serf_bucket_t *bucket)
{
serf_ssl_bucket_t *ssl_bucket = (serf_ssl_bucket_t *)bucket;
serf_ssl_context_t *ssl_ctx = ssl_bucket->ssl_ctx;
if (!--ssl_ctx->refcount) {
serf_bucket_mem_free(ssl_bucket->allocator, ssl_ctx);
}
}
apr_status_t serf_ssl_use_default_certificates(serf_ssl_context_t *ssl_ctx)
{
return ssl_ctx->type->use_default_certificates(ssl_ctx->impl_ctx);
}
apr_status_t serf_ssl_trust_cert(serf_ssl_context_t *ssl_ctx,
serf_ssl_certificate_t *cert)
{
return ssl_ctx->type->trust_cert(ssl_ctx->impl_ctx, cert);
}
/* Create a implementation-independent serf_ssl_certificate_t object */
serf_ssl_certificate_t *
serf__create_certificate(serf_bucket_alloc_t *allocator,
const serf_ssl_bucket_type_t *type,
void *impl_cert,
int depth_of_error)
{
serf_ssl_certificate_t *cert;
cert = serf_bucket_mem_alloc(allocator,
sizeof(serf_ssl_certificate_t));
cert->impl_cert = impl_cert;
cert->type = type;
cert->depth_of_error = depth_of_error;
return cert;
}
/* Functions to read a serf_ssl_certificate structure. */
int serf_ssl_cert_depth(const serf_ssl_certificate_t *cert)
{
return cert->depth_of_error;
}
apr_hash_t *serf_ssl_cert_issuer(
const serf_ssl_certificate_t *cert,
apr_pool_t *pool)
{
return cert->type->cert_issuer(cert, pool);
}
apr_hash_t *serf_ssl_cert_subject(
const serf_ssl_certificate_t *cert,
apr_pool_t *pool)
{
return cert->type->cert_subject(cert, pool);
}
apr_hash_t *serf_ssl_cert_certificate(
const serf_ssl_certificate_t *cert,
apr_pool_t *pool)
{
return cert->type->cert_certificate(cert, pool);
}
const char *serf_ssl_cert_export(
const serf_ssl_certificate_t *cert,
apr_pool_t *pool)
{
return cert->type->cert_export(cert, pool);
}
/* Create a implementation-independent serf_ssl_identity_t object */
serf_ssl_identity_t *
serf__create_identity(const serf_ssl_bucket_type_t *type,
void *impl_cert,
void *impl_pkey,
apr_pool_t *pool)
{
serf_ssl_identity_t *identity;
identity = apr_palloc(pool, sizeof(serf_ssl_identity_t));
identity->impl_cert = impl_cert;
identity->impl_pkey = impl_pkey;
identity->type = type;
return identity;
}
apr_status_t serf_ssl_load_identity_from_file(serf_ssl_context_t *ssl_ctx,
const serf_ssl_identity_t **identity,
const char *file_path,
apr_pool_t *pool)
{
return ssl_ctx->type->load_identity_from_file(ssl_ctx->impl_ctx, identity,
file_path, pool);
}
apr_status_t serf_ssl_load_CA_cert_from_file(serf_ssl_context_t *ssl_ctx,
serf_ssl_certificate_t **cert,
const char *file_path,
apr_pool_t *pool)
{
/* The ssl_ctx is not needed to load the certificate, only to determine
which SSL library we're using. */
return ssl_ctx->type->load_CA_cert_from_file(cert, file_path, pool);
}
apr_status_t serf_ssl_load_cert_file(serf_ssl_certificate_t **cert,
const char *file_path,
apr_pool_t *pool)
{
/* ### This is a hack, depends on the SSL implementation type to be
always the same during multiple ssl sessions. While that's currently
the case, it's not guaranteed to stay this way in future versions of
serf. */
const serf_ssl_bucket_type_t *type = decide_ssl_bucket_type();
return type->load_CA_cert_from_file(cert, file_path, pool);
}
/* SSL Session Resumption API's */
void serf_ssl_new_session_callback_set(serf_ssl_context_t *ssl_ctx,
serf_ssl_new_session_t new_session_cb,
void *baton)
{
ssl_ctx->type->new_session_callback_set(ssl_ctx->impl_ctx,
new_session_cb, baton);
}
apr_status_t serf_ssl_resume_session(serf_ssl_context_t *ssl_ctx,
const serf_ssl_session_t *ssl_session,
apr_pool_t *pool)
{
return ssl_ctx->type->resume_session(ssl_ctx->impl_ctx, ssl_session,
pool);
}
apr_status_t serf_ssl_session_export(serf_ssl_context_t *ssl_ctx,
void **data,
apr_size_t *len,
const serf_ssl_session_t *ssl_session,
apr_pool_t *pool)
{
return ssl_ctx->type->session_export(data, len, ssl_session, pool);
}
apr_status_t serf_ssl_session_import(serf_ssl_context_t *ssl_ctx,
const serf_ssl_session_t **ssl_session,
void *data,
apr_size_t len,
apr_pool_t *pool)
{
return ssl_ctx->type->session_import(ssl_session, data, len, pool);
}
/* Define dummy ssl_decrypt and ssl_encrypt buckets. These are needed because
they are exported symbols, previously (not anymore) used in the
SERF_BUCKET_IS_SSL_DECRYPT and SERF_BUCKET_IS_SSL_ENCRYPT macro's. */
const serf_bucket_type_t serf_bucket_type_ssl_decrypt = {
"ABSTRACT DECRYPT", NULL, NULL, NULL, NULL, NULL, NULL, NULL };
const serf_bucket_type_t serf_bucket_type_ssl_encrypt = {
"ABSTRACT ENCRYPT", NULL, NULL, NULL, NULL, NULL, NULL, NULL };