summaryrefslogtreecommitdiff
path: root/buckets/ssl_buckets.c
diff options
context:
space:
mode:
Diffstat (limited to 'buckets/ssl_buckets.c')
-rw-r--r--buckets/ssl_buckets.c297
1 files changed, 210 insertions, 87 deletions
diff --git a/buckets/ssl_buckets.c b/buckets/ssl_buckets.c
index 3f43543..c5a0e60 100644
--- a/buckets/ssl_buckets.c
+++ b/buckets/ssl_buckets.c
@@ -43,6 +43,7 @@
#include <apr_atomic.h>
#include "serf.h"
+#include "serf_private.h"
#include "serf_bucket_util.h"
#include <openssl/bio.h>
@@ -64,8 +65,6 @@
#endif
-/*#define SSL_VERBOSE*/
-
/*
* Here's an overview of the SSL bucket's relationship to OpenSSL and serf.
*
@@ -180,6 +179,10 @@ struct serf_ssl_context_t {
EVP_PKEY *cached_cert_pw;
apr_status_t pending_err;
+
+ /* Status of a fatal error, returned on subsequent encrypt or decrypt
+ requests. */
+ apr_status_t fatal_err;
};
typedef struct {
@@ -198,6 +201,47 @@ struct serf_ssl_certificate_t {
int depth;
};
+static void disable_compression(serf_ssl_context_t *ssl_ctx);
+
+#if SSL_VERBOSE
+/* Log all ssl alerts that we receive from the server. */
+static void
+apps_ssl_info_callback(const SSL *s, int where, int ret)
+{
+ const char *str;
+ int w;
+ w = where & ~SSL_ST_MASK;
+
+ if (w & SSL_ST_CONNECT)
+ str = "SSL_connect";
+ else if (w & SSL_ST_ACCEPT)
+ str = "SSL_accept";
+ else
+ str = "undefined";
+
+ if (where & SSL_CB_LOOP) {
+ serf__log(SSL_VERBOSE, __FILE__, "%s:%s\n", str,
+ SSL_state_string_long(s));
+ }
+ else if (where & SSL_CB_ALERT) {
+ str = (where & SSL_CB_READ) ? "read" : "write";
+ serf__log(SSL_VERBOSE, __FILE__, "SSL3 alert %s:%s:%s\n",
+ str,
+ SSL_alert_type_string_long(ret),
+ SSL_alert_desc_string_long(ret));
+ }
+ else if (where & SSL_CB_EXIT) {
+ if (ret == 0)
+ serf__log(SSL_VERBOSE, __FILE__, "%s:failed in %s\n", str,
+ SSL_state_string_long(s));
+ else if (ret < 0) {
+ serf__log(SSL_VERBOSE, __FILE__, "%s:error in %s\n", str,
+ SSL_state_string_long(s));
+ }
+ }
+}
+#endif
+
/* Returns the amount read. */
static int bio_bucket_read(BIO *bio, char *in, int inlen)
{
@@ -206,17 +250,14 @@ static int bio_bucket_read(BIO *bio, char *in, int inlen)
apr_status_t status;
apr_size_t len;
-#ifdef SSL_VERBOSE
- printf("bio_bucket_read called for %d bytes\n", inlen);
-#endif
+ serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_read called for %d bytes\n",
+ inlen);
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",
+ serf__log(SSL_VERBOSE, __FILE__, "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);
@@ -225,9 +266,9 @@ static int bio_bucket_read(BIO *bio, char *in, int inlen)
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
+
+ serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_read received %d bytes (%d)\n",
+ len, status);
if (!SERF_BUCKET_READ_ERROR(status)) {
/* Oh suck. */
@@ -250,16 +291,14 @@ 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
+ serf__log(SSL_VERBOSE, __FILE__, "bio_bucket_write called for %d bytes\n",
+ inl);
+
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",
+ serf__log(SSL_VERBOSE, __FILE__, "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);
@@ -424,6 +463,9 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx)
case X509_V_ERR_INVALID_CA:
failures |= SERF_SSL_CERT_UNKNOWNCA;
break;
+ case X509_V_ERR_CERT_REVOKED:
+ failures |= SERF_SSL_CERT_REVOKED;
+ break;
default:
failures |= SERF_SSL_CERT_UNKNOWN_FAILURE;
break;
@@ -455,9 +497,13 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx)
failures, cert);
if (status == APR_SUCCESS)
cert_valid = 1;
- else
+ else {
+ /* Even if openssl found the certificate valid, the application
+ told us to reject it. */
+ cert_valid = 0;
/* Pass the error back to the caller through the context-run. */
ctx->pending_err = status;
+ }
apr_pool_destroy(subpool);
}
@@ -490,7 +536,7 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx)
certs_len = 1;
} else {
int i;
-
+
certs_len = sk_X509_num(chain);
/* Room for all the certs and a trailing NULL. */
@@ -514,6 +560,9 @@ validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx)
if (status == APR_SUCCESS) {
cert_valid = 1;
} else {
+ /* Even if openssl found the certificate valid, the application
+ told us to reject it. */
+ cert_valid = 0;
/* Pass the error back to the caller through the context-run. */
ctx->pending_err = status;
}
@@ -534,18 +583,18 @@ static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize,
const char *data;
int ssl_len;
-#ifdef SSL_VERBOSE
- printf("ssl_decrypt: begin %d\n", bufsize);
-#endif
+ if (ctx->fatal_err)
+ return ctx->fatal_err;
+
+ serf__log(SSL_VERBOSE, __FILE__, "ssl_decrypt: begin %d\n", bufsize);
/* 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
+ serf__log(SSL_VERBOSE, __FILE__,
+ "ssl_decrypt: %d bytes (%d); status: %d; flags: %d\n",
+ ssl_len, bufsize, ctx->decrypt.status,
+ BIO_get_retry_flags(ctx->bio));
*len = ssl_len;
return APR_SUCCESS;
}
@@ -555,10 +604,9 @@ static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize,
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
+ serf__log(SSL_VERBOSE, __FILE__,
+ "ssl_decrypt: read %d bytes (%d); status: %d\n",
+ priv_len, bufsize, status);
tmp = serf_bucket_simple_copy_create(data, priv_len,
ctx->decrypt.pending->allocator);
@@ -581,29 +629,55 @@ static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize,
break;
case SSL_ERROR_SSL:
*len = 0;
- status = ctx->pending_err ? ctx->pending_err : APR_EGENERAL;
- ctx->pending_err = 0;
+ if (ctx->pending_err) {
+ status = ctx->pending_err;
+ ctx->pending_err = 0;
+ } else {
+ ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
+ }
break;
default:
*len = 0;
- status = APR_EGENERAL;
+ ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
break;
}
- }
- else {
+ } else if (ssl_len == 0) {
+ /* The server shut down the connection. */
+ int ssl_err, shutdown;
+ *len = 0;
+
+ /* Check for SSL_RECEIVED_SHUTDOWN */
+ shutdown = SSL_get_shutdown(ctx->ssl);
+ /* Check for SSL_ERROR_ZERO_RETURN */
+ ssl_err = SSL_get_error(ctx->ssl, ssl_len);
+
+ if (shutdown == SSL_RECEIVED_SHUTDOWN &&
+ ssl_err == SSL_ERROR_ZERO_RETURN) {
+ /* The server closed the SSL session. While this doesn't
+ necessary mean the connection is closed, let's close
+ it here anyway.
+ We can optimize this later. */
+ serf__log(SSL_VERBOSE, __FILE__,
+ "ssl_decrypt: SSL read error: server"
+ " shut down connection!\n");
+ status = APR_EOF;
+ } else {
+ /* A fatal error occurred. */
+ ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
+ }
+ } else {
*len = ssl_len;
-#ifdef SSL_VERBOSE
- printf("---\n%s\n-(%d)-\n", buf, *len);
-#endif
+ serf__log(SSL_MSG_VERBOSE, __FILE__,
+ "---\n%.*s\n-(%d)-\n", *len, buf, *len);
}
}
else {
*len = 0;
}
-#ifdef SSL_VERBOSE
- printf("ssl_decrypt: %d %d %d\n", status, *len,
- BIO_get_retry_flags(ctx->bio));
-#endif
+ serf__log(SSL_VERBOSE, __FILE__,
+ "ssl_decrypt: %d %d %d\n", status, *len,
+ BIO_get_retry_flags(ctx->bio));
+
return status;
}
@@ -616,9 +690,10 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize,
serf_ssl_context_t *ctx = baton;
apr_status_t status;
-#ifdef SSL_VERBOSE
- printf("ssl_encrypt: begin %d\n", bufsize);
-#endif
+ if (ctx->fatal_err)
+ return ctx->fatal_err;
+
+ serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: begin %d\n", bufsize);
/* Try to read already encrypted but unread data first. */
status = serf_bucket_read(ctx->encrypt.pending, bufsize, &data, len);
@@ -632,29 +707,28 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize,
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
+
+ serf__log(SSL_VERBOSE, __FILE__, "ssl_encrypt: %d %d %d (quick read)\n",
+ status, *len, BIO_get_retry_flags(ctx->bio));
+
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
+ serf__log(SSL_VERBOSE, __FILE__,
+ "ssl_encrypt: %d %d %d (should write exit)\n",
+ status, *len, BIO_get_retry_flags(ctx->bio));
+
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
+ serf__log(SSL_VERBOSE, __FILE__, "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));
+
ctx->encrypt.status = APR_SUCCESS;
ctx->encrypt.exhausted_reset = 0;
}
@@ -696,18 +770,20 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize,
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
+ serf__log(SSL_VERBOSE, __FILE__,
+ "ssl_encrypt: bucket read %d bytes; "\
+ "status %d\n", interim_len, status);
+ serf__log(SSL_MSG_VERBOSE, __FILE__, "---\n%.*s\n-(%d)-\n",
+ interim_len, vecs_data, interim_len);
+
/* 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
+
+ serf__log(SSL_VERBOSE, __FILE__,
+ "ssl_encrypt: SSL write: %d\n", ssl_len);
+
/* We're done. */
serf_bucket_mem_free(ctx->allocator, vecs_data);
@@ -720,9 +796,10 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize,
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
+
+ serf__log(SSL_VERBOSE, __FILE__,
+ "ssl_encrypt: SSL write error: %d\n", ssl_err);
+
if (ssl_err == SSL_ERROR_SYSCALL) {
status = ctx->encrypt.status;
if (SERF_BUCKET_READ_ERROR(status)) {
@@ -735,12 +812,13 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize,
status = SERF_ERROR_WAIT_CONN;
}
else {
- status = APR_EGENERAL;
+ ctx->fatal_err = status = SERF_ERROR_SSL_COMM_FAILED;
}
}
-#ifdef SSL_VERBOSE
- printf("ssl_encrypt: SSL write error: %d %d\n", status, *len);
-#endif
+
+ serf__log(SSL_VERBOSE, __FILE__,
+ "ssl_encrypt: SSL write error: %d %d\n",
+ status, *len);
}
}
}
@@ -767,10 +845,9 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize,
*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
+ serf__log(SSL_VERBOSE, __FILE__,
+ "ssl_encrypt read agg: %d %d %d %d\n", status, agg_status,
+ ctx->encrypt.status, *len);
if (!agg_status) {
status = agg_status;
@@ -783,11 +860,11 @@ static apr_status_t ssl_encrypt(void *baton, apr_size_t bufsize,
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
+ serf__log(SSL_VERBOSE, __FILE__,
+ "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));
+
return status;
}
@@ -873,6 +950,20 @@ static void init_ssl_libraries(void)
#if APR_HAS_THREADS
int i, numlocks;
#endif
+
+#ifdef SSL_VERBOSE
+ /* Warn when compile-time and run-time version of OpenSSL differ in
+ major/minor version number. */
+ long libver = SSLeay();
+
+ if ((libver ^ OPENSSL_VERSION_NUMBER) & 0xFFF00000) {
+ serf__log(SSL_VERBOSE, __FILE__,
+ "Warning: OpenSSL library version mismatch, compile-time "
+ "was %lx, runtime is %lx.\n",
+ OPENSSL_VERSION_NUMBER, libver);
+ }
+#endif
+
CRYPTO_malloc_init();
ERR_load_crypto_strings();
SSL_load_error_strings();
@@ -1107,6 +1198,7 @@ static serf_ssl_context_t *ssl_init_context(void)
ssl_ctx->cached_cert = 0;
ssl_ctx->cached_cert_pw = 0;
ssl_ctx->pending_err = APR_SUCCESS;
+ ssl_ctx->fatal_err = APR_SUCCESS;
ssl_ctx->cert_callback = NULL;
ssl_ctx->cert_pw_callback = NULL;
@@ -1116,6 +1208,8 @@ static serf_ssl_context_t *ssl_init_context(void)
SSL_CTX_set_verify(ssl_ctx->ctx, SSL_VERIFY_PEER,
validate_server_certificate);
SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_ALL);
+ /* Disable SSL compression by default. */
+ disable_compression(ssl_ctx);
ssl_ctx->ssl = SSL_new(ssl_ctx->ctx);
ssl_ctx->bio = BIO_new(&bio_bucket_method);
@@ -1127,6 +1221,10 @@ static serf_ssl_context_t *ssl_init_context(void)
SSL_set_app_data(ssl_ctx->ssl, ssl_ctx);
+#if SSL_VERBOSE
+ SSL_CTX_set_info_callback(ssl_ctx->ctx, apps_ssl_info_callback);
+#endif
+
ssl_ctx->encrypt.stream = NULL;
ssl_ctx->encrypt.stream_next = NULL;
ssl_ctx->encrypt.pending = serf_bucket_aggregate_create(allocator);
@@ -1206,7 +1304,7 @@ apr_status_t serf_ssl_use_default_certificates(serf_ssl_context_t *ssl_ctx)
int result = X509_STORE_set_default_paths(store);
- return result ? APR_SUCCESS : APR_EGENERAL;
+ return result ? APR_SUCCESS : SERF_ERROR_SSL_CERT_FAILED;
}
apr_status_t serf_ssl_load_cert_file(
@@ -1228,7 +1326,7 @@ apr_status_t serf_ssl_load_cert_file(
}
}
- return APR_EGENERAL;
+ return SERF_ERROR_SSL_CERT_FAILED;
}
@@ -1240,7 +1338,7 @@ apr_status_t serf_ssl_trust_cert(
int result = X509_STORE_add_cert(store, cert->ssl_cert);
- return result ? APR_SUCCESS : APR_EGENERAL;
+ return result ? APR_SUCCESS : SERF_ERROR_SSL_CERT_FAILED;
}
@@ -1521,6 +1619,31 @@ const char *serf_ssl_cert_export(
return encoded_cert;
}
+/* Disables compression for all SSL sessions. */
+static void disable_compression(serf_ssl_context_t *ssl_ctx)
+{
+#ifdef SSL_OP_NO_COMPRESSION
+ SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_NO_COMPRESSION);
+#endif
+}
+
+apr_status_t serf_ssl_use_compression(serf_ssl_context_t *ssl_ctx, int enabled)
+{
+ if (enabled) {
+#ifdef SSL_OP_NO_COMPRESSION
+ SSL_clear_options(ssl_ctx->ssl, SSL_OP_NO_COMPRESSION);
+ return APR_SUCCESS;
+#endif
+ } else {
+#ifdef SSL_OP_NO_COMPRESSION
+ SSL_set_options(ssl_ctx->ssl, SSL_OP_NO_COMPRESSION);
+ return APR_SUCCESS;
+#endif
+ }
+
+ return APR_EGENERAL;
+}
+
static void serf_ssl_destroy_and_data(serf_bucket_t *bucket)
{
ssl_context_t *ctx = bucket->data;