blob: cd6f6d5a8ef715936894b40fbf40ee2be2be50ac [file] [log] [blame]
/* 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_portable.h>
#include <apr_strings.h>
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "config.h"
#include "flood_profile.h"
#include "flood_net.h"
#include "flood_net_ssl.h"
#if FLOOD_HAS_OPENSSL
#define OPENSSL_THREAD_DEFINES
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
struct ssl_socket_t {
SSL_CTX *ssl_context;
SSL *ssl_connection;
flood_socket_t *socket;
};
apr_pool_t *ssl_pool;
#if APR_HAS_THREADS
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();
}
#endif
/* borrowed from mod_ssl */
static int ssl_rand_choosenum(int l, int h)
{
int i;
char buf[50];
srand((unsigned int)time(NULL));
apr_snprintf(buf, sizeof(buf), "%.0f",
(((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l)));
i = atoi(buf)+1;
if (i < l) i = l;
if (i > h) i = h;
return i;
}
static void load_rand(void)
{
unsigned char stackdata[256];
time_t tt;
pid_t pid;
int l, n;
tt = time(NULL);
l = sizeof(time_t);
RAND_seed((unsigned char *)&tt, l);
pid = (pid_t)getpid();
l = sizeof(pid_t);
RAND_seed((unsigned char *)&pid, l);
n = ssl_rand_choosenum(0, sizeof(stackdata)-128-1);
RAND_seed(stackdata+n, 128);
}
apr_status_t ssl_init_socket(apr_pool_t *pool)
{
#if APR_HAS_THREADS
int i, numlocks;
#endif
ssl_pool = pool;
SSL_library_init();
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();
ERR_load_crypto_strings();
#if ! FLOOD_HAS_DEVRAND
load_rand();
#endif
#if APR_HAS_THREADS
numlocks = CRYPTO_num_locks();
ssl_locks = apr_palloc(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);
#endif
return APR_SUCCESS;
}
void ssl_read_socket_handshake(ssl_socket_t *s);
ssl_socket_t* ssl_open_socket(apr_pool_t *pool, request_t *r,
apr_status_t *status)
{
apr_os_sock_t ossock;
int e, sslError;
ssl_socket_t *ssl_socket = apr_pcalloc(pool, sizeof(ssl_socket_t));
/* Open our TCP-based connection */
ssl_socket->socket = open_socket(pool, r, status);
if (!ssl_socket->socket)
return NULL;
/* Get the native OS socket. */
apr_os_sock_get(&ossock, ssl_socket->socket->socket);
/* Create a local context */
ssl_socket->ssl_context = SSL_CTX_new(SSLv23_client_method());
SSL_CTX_set_options(ssl_socket->ssl_context, SSL_OP_ALL);
#ifdef SSL_MODE_AUTO_RETRY
/* Not all OpenSSL versions support this. */
SSL_CTX_set_options(ssl_socket->ssl_context, SSL_MODE_AUTO_RETRY);
#endif
/*SSL_CTX_set_default_verify_paths(ssl_socket->ssl_context);*/
SSL_CTX_load_verify_locations(ssl_socket->ssl_context, NULL, CAPATH);
/* Initialize the SSL connection */
ssl_socket->ssl_connection = SSL_new(ssl_socket->ssl_context);
SSL_set_connect_state(ssl_socket->ssl_connection);
/* Set the descriptors */
SSL_set_fd(ssl_socket->ssl_connection, ossock);
e = SSL_connect(ssl_socket->ssl_connection);
if (e)
{
sslError = SSL_get_error(ssl_socket->ssl_connection, e);
switch (sslError)
{
case SSL_ERROR_NONE:
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* Treat as okay. */
break;
default:
ERR_print_errors_fp(stderr);
return NULL;
}
}
return ssl_socket;
}
/* close down TCP socket */
void ssl_close_socket(ssl_socket_t *s)
{
SSL_free(s->ssl_connection);
SSL_CTX_free(s->ssl_context);
close_socket(s->socket);
}
apr_status_t ssl_read_socket(ssl_socket_t *s, char *buf, apr_size_t *buflen)
{
apr_status_t e;
int sslError;
apr_int32_t socketsRead;
/* Wait until there is something to read. */
if (SSL_pending(s->ssl_connection) < *buflen) {
e = apr_poll(&s->socket->read_pollset, 1, &socketsRead,
LOCAL_SOCKET_TIMEOUT);
if (socketsRead != 1)
return APR_TIMEUP;
}
e = SSL_read(s->ssl_connection, buf, *buflen);
sslError = SSL_get_error(s->ssl_connection, e);
switch (sslError)
{
case SSL_ERROR_NONE:
*buflen = e;
break;
case SSL_ERROR_WANT_READ:
ssl_read_socket(s, buf, buflen);
break;
case SSL_ERROR_ZERO_RETURN: /* Peer closed connection. */
return APR_EOF;
case SSL_ERROR_SYSCALL: /* Look at errno. */
if (errno == 0)
return APR_EOF;
/* Continue through with the error case. */
case SSL_ERROR_WANT_WRITE: /* Technically, not an error. */
default:
ERR_print_errors_fp(stderr);
return APR_EGENERAL;
}
return APR_SUCCESS;
}
void ssl_read_socket_handshake(ssl_socket_t *s)
{
char buf[1];
int buflen = 1;
/* Wait until there is something to read. */
apr_int32_t socketsRead;
apr_status_t e;
e = apr_poll(&s->socket->read_pollset, 1, &socketsRead,
LOCAL_SOCKET_TIMEOUT);
e = SSL_read(s->ssl_connection, buf, buflen);
}
/* Write to the socket */
apr_status_t ssl_write_socket(ssl_socket_t *s, request_t *r)
{
apr_status_t e;
int sslError;
/* Returns an error. */
e = SSL_write(s->ssl_connection, r->rbuf, r->rbufsize);
sslError = SSL_get_error(s->ssl_connection, e);
switch (sslError)
{
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_READ:
ssl_read_socket_handshake(s);
ssl_write_socket(s, r);
break;
case SSL_ERROR_WANT_WRITE:
break;
default:
ERR_print_errors_fp(stderr);
return APR_EGENERAL;
}
return APR_SUCCESS;
}
#else /* FLOOD_HAS_OPENSSL */
apr_status_t ssl_init_socket(apr_pool_t *pool)
{
return APR_ENOTIMPL;
}
ssl_socket_t* ssl_open_socket(apr_pool_t *pool, request_t *r,
apr_status_t *status)
{
return NULL;
}
void ssl_close_socket(ssl_socket_t *s)
{
}
apr_status_t ssl_write_socket(ssl_socket_t *s, request_t *r)
{
return APR_ENOTIMPL;
}
apr_status_t ssl_read_socket(ssl_socket_t *s, char *buf, apr_size_t *buflen)
{
return APR_ENOTIMPL;
}
#endif /* FLOOD_HAS_OPENSSL */