Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1286 lines (1062 sloc) 30.319 kb
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* Cherokee
*
* Authors:
* Alvaro Lopez Ortega <alvaro@alobbs.com>
*
* Copyright (C) 2001-2011 Alvaro Lopez Ortega
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/* IN CASE THIS PLUG-IN IS COMPILED WITH OPENSSL:
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU General Public License in all respects for
* all of the code used other than OpenSSL. If you modify file(s)
* with this exception, you may extend this exception to your version
* of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version.
* If you delete this exception statement from all source files in the
* program, then also delete it here.
*/
#include "common-internal.h"
#include "cryptor_libssl.h"
#include "virtual_server.h"
#include "socket.h"
#include "util.h"
#include "server-protected.h"
#define ENTRIES "crypto,ssl"
static DH *dh_param_512 = NULL;
static DH *dh_param_1024 = NULL;
static DH *dh_param_2048 = NULL;
static DH *dh_param_4096 = NULL;
#include "cryptor_libssl_dh_512.c"
#include "cryptor_libssl_dh_1024.c"
#include "cryptor_libssl_dh_2048.c"
#include "cryptor_libssl_dh_4096.c"
#define CLEAR_LIBSSL_ERRORS \
do { \
unsigned long openssl_error; \
while ((openssl_error = ERR_get_error())) { \
TRACE(ENTRIES, "Ignoring libssl error: %s\n", \
ERR_error_string(openssl_error, NULL)); \
} \
} while(0)
static ret_t
_free (cherokee_cryptor_libssl_t *cryp)
{
/* DH Parameters
*/
if (dh_param_512 != NULL) {
DH_free (dh_param_512);
dh_param_512 = NULL;
}
if (dh_param_1024 != NULL) {
DH_free (dh_param_1024);
dh_param_1024 = NULL;
}
if (dh_param_2048 != NULL) {
DH_free (dh_param_2048);
dh_param_2048 = NULL;
}
if (dh_param_4096 != NULL) {
DH_free (dh_param_4096);
dh_param_4096 = NULL;
}
/* Free loaded error strings
*/
ERR_free_strings();
/* Free all ciphers and digests
*/
EVP_cleanup();
cherokee_cryptor_free_base (CRYPTOR(cryp));
return ret_ok;
}
static ret_t
try_read_dh_param(cherokee_config_node_t *conf,
DH **dh,
int bitsize)
{
ret_t ret;
cherokee_buffer_t *buf;
FILE *paramfile = NULL;
cherokee_buffer_t confentry = CHEROKEE_BUF_INIT;
cherokee_buffer_add_va (&confentry, "dh_param%d", bitsize);
/* Read the configuration parameter
*/
ret = cherokee_config_node_read (conf, confentry.buf, &buf);
if (ret != ret_ok) {
ret = ret_ok;
goto out;
}
/* Read the file
*/
paramfile = fopen (buf->buf, "r");
if (paramfile == NULL) {
TRACE(ENTRIES, "Cannot open file %s\n", buf->buf);
ret = ret_file_not_found;
goto out;
}
/* Process the content
*/
*dh = PEM_read_DHparams (paramfile, NULL, NULL, NULL);
if (*dh == NULL) {
TRACE(ENTRIES, "Failed to load PEM %s\n", buf->buf);
ret = ret_error;
goto out;
}
ret = ret_ok;
out:
/* Clean up
*/
if (paramfile != NULL) {
fclose (paramfile);
}
cherokee_buffer_mrproper (&confentry);
return ret;
}
static ret_t
_configure (cherokee_cryptor_t *cryp,
cherokee_config_node_t *conf,
cherokee_server_t *srv)
{
ret_t ret;
UNUSED(cryp);
UNUSED(srv);
ret = try_read_dh_param (conf, &dh_param_512, 512);
if (ret != ret_ok)
return ret;
ret = try_read_dh_param (conf, &dh_param_1024, 1024);
if (ret != ret_ok)
return ret;
ret = try_read_dh_param (conf, &dh_param_2048, 2048);
if (ret != ret_ok)
return ret;
ret = try_read_dh_param (conf, &dh_param_4096, 4096);
if (ret != ret_ok)
return ret;
return ret_ok;
}
static ret_t
_vserver_free (cherokee_cryptor_vserver_libssl_t *cryp_vsrv)
{
if (cryp_vsrv->context != NULL) {
SSL_CTX_free (cryp_vsrv->context);
cryp_vsrv->context = NULL;
}
free (cryp_vsrv);
return ret_ok;
}
ret_t
cherokee_cryptor_libssl_find_vserver (SSL *ssl,
cherokee_server_t *srv,
cherokee_buffer_t *servername,
cherokee_connection_t *conn)
{
ret_t ret;
cherokee_virtual_server_t *vsrv = NULL;
SSL_CTX *ctx;
/* Try to match the connection to a server
*/
ret = cherokee_server_get_vserver(srv, servername, conn, &vsrv);
if ((ret != ret_ok) || (vsrv == NULL)) {
LOG_ERROR (CHEROKEE_ERROR_SSL_SRV_MATCH, servername->buf);
return ret_error;
}
TRACE (ENTRIES, "Setting new TLS context. Virtual host='%s'\n",
vsrv->name.buf);
/* Check whether the Virtual Server supports TLS
*/
if ((vsrv->cryptor == NULL) ||
(CRYPTOR_VSRV_SSL(vsrv->cryptor)->context == NULL))
{
TRACE (ENTRIES, "Virtual server '%s' does not support SSL\n", servername->buf);
return ret_error;
}
/* Set the new SSL context
*/
ctx = SSL_set_SSL_CTX (ssl, CRYPTOR_VSRV_SSL(vsrv->cryptor)->context);
if (ctx != CRYPTOR_VSRV_SSL(vsrv->cryptor)->context) {
LOG_ERROR (CHEROKEE_ERROR_SSL_CHANGE_CTX, servername->buf);
}
/* SSL_set_SSL_CTX() only change certificates. We need to
* changes more options by hand.
*/
SSL_set_options(ssl, SSL_CTX_get_options(ssl->ctx));
if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
(SSL_num_renegotiations(ssl) == 0)) {
SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ssl->ctx),
SSL_CTX_get_verify_callback(ssl->ctx));
}
return ret_ok;
}
#ifndef OPENSSL_NO_TLSEXT
static int
openssl_sni_servername_cb (SSL *ssl, int *ad, void *arg)
{
ret_t ret;
int re;
const char *servername;
cherokee_connection_t *conn;
cherokee_buffer_t tmp;
cherokee_server_t *srv = SRV(arg);
cherokee_virtual_server_t *vsrv = NULL;
UNUSED(ad);
/* Get the pointer to the socket
*/
conn = SSL_get_app_data (ssl);
if (unlikely (conn == NULL)) {
LOG_ERROR (CHEROKEE_ERROR_SSL_SOCKET, ssl);
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
cherokee_buffer_init(&tmp);
cherokee_buffer_ensure_size(&tmp, 40);
/* Read the SNI server name
*/
servername = SSL_get_servername (ssl, TLSEXT_NAMETYPE_host_name);
if (servername == NULL) {
/* Set the server name to the IP address if we couldn't get the host name via SNI
*/
cherokee_socket_ntop (&conn->socket, tmp.buf, tmp.size);
TRACE (ENTRIES, "No SNI: Did not provide a server name, using IP='%s' as servername.\n", tmp.buf);
} else {
cherokee_buffer_add (&tmp, servername, strlen(servername));
TRACE (ENTRIES, "SNI: Switching to servername='%s'\n", servername);
}
/* Look up and change the vserver
*/
ret = cherokee_cryptor_libssl_find_vserver(ssl, srv, &tmp, conn);
if (ret != ret_ok) {
re = SSL_TLSEXT_ERR_NOACK;
}
else {
re = SSL_TLSEXT_ERR_OK;
}
cherokee_buffer_mrproper (&tmp);
return re;
}
#endif
static DH *
tmp_dh_cb (SSL *ssl, int export, int keylen)
{
UNUSED(ssl);
UNUSED(export);
switch (keylen) {
case 512:
if (dh_param_512 == NULL) {
dh_param_512 = get_dh512();
}
return dh_param_512;
case 1024:
if (dh_param_1024 == NULL) {
dh_param_1024 = get_dh1024();
}
return dh_param_1024;
case 2048:
if (dh_param_2048 == NULL) {
dh_param_2048 = get_dh2048();
}
return dh_param_2048;
case 4096:
if (dh_param_4096 == NULL) {
dh_param_4096 = get_dh4096();
}
return dh_param_4096;
}
return NULL;
}
static ret_t
_vserver_new (cherokee_cryptor_t *cryp,
cherokee_virtual_server_t *vsrv,
cherokee_cryptor_vserver_t **cryp_vsrv)
{
ret_t ret;
int rc;
const char *error;
long options;
int verify_mode = SSL_VERIFY_NONE;
CHEROKEE_NEW_STRUCT (n, cryptor_vserver_libssl);
UNUSED(cryp);
/* Init
*/
ret = cherokee_cryptor_vserver_init_base (CRYPTOR_VSRV(n));
if (ret != ret_ok) {
free (n);
return ret;
}
CRYPTOR_VSRV(n)->free = (cryptor_vsrv_func_free_t) _vserver_free;
/* Init the OpenSSL context
*/
n->context = SSL_CTX_new (SSLv23_server_method());
if (n->context == NULL) {
LOG_ERROR_S(CHEROKEE_ERROR_SSL_ALLOCATE_CTX);
goto error;
}
/* Callback to be used when a DH parameters are required
*/
SSL_CTX_set_tmp_dh_callback (n->context, tmp_dh_cb);
/* Set the SSL context options:
*/
options = SSL_OP_ALL;
options |= SSL_OP_SINGLE_DH_USE;
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
#endif
if (! cryp->allow_SSLv2) {
options |= SSL_OP_NO_SSLv2;
}
#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
if (vsrv->cipher_server_preference) {
options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
}
#endif
#ifndef OPENSSL_NO_COMP
if (! vsrv->ssl_compression) {
#ifdef SSL_OP_NO_COMPRESSION
options |= SSL_OP_NO_COMPRESSION;
#elif OPENSSL_VERSION_NUMBER >= 0x00908000L
sk_SSL_COMP_zero(SSL_COMP_get_compression_methods());
#endif
}
#endif
SSL_CTX_set_options (n->context, options);
/* Set cipher list that vserver will accept.
*/
if (! cherokee_buffer_is_empty (&vsrv->ciphers)) {
rc = SSL_CTX_set_cipher_list (n->context, vsrv->ciphers.buf);
if (rc != 1) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR(CHEROKEE_ERROR_SSL_CIPHER,
vsrv->ciphers.buf, error);
goto error;
}
}
CLEAR_LIBSSL_ERRORS;
/* Certificate
*/
TRACE(ENTRIES, "Vserver '%s'. Reading certificate file '%s'\n",
vsrv->name.buf, vsrv->server_cert.buf);
rc = SSL_CTX_use_certificate_chain_file (n->context, vsrv->server_cert.buf);
if (rc != 1) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR(CHEROKEE_ERROR_SSL_CERTIFICATE,
vsrv->server_cert.buf, error);
goto error;
}
/* Private key
*/
TRACE(ENTRIES, "Vserver '%s'. Reading key file '%s'\n",
vsrv->name.buf, vsrv->server_key.buf);
rc = SSL_CTX_use_PrivateKey_file (n->context, vsrv->server_key.buf, SSL_FILETYPE_PEM);
if (rc != 1) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR(CHEROKEE_ERROR_SSL_KEY, vsrv->server_key.buf, error);
goto error;
}
/* Check private key
*/
rc = SSL_CTX_check_private_key (n->context);
if (rc != 1) {
LOG_ERROR_S(CHEROKEE_ERROR_SSL_KEY_MATCH);
goto error;
}
if (! cherokee_buffer_is_empty (&vsrv->req_client_certs)) {
STACK_OF(X509_NAME) *X509_clients;
verify_mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
if (cherokee_buffer_cmp_str (&vsrv->req_client_certs, "required") == 0) {
verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
}
/* Trusted CA certificates
*/
if (! cherokee_buffer_is_empty (&vsrv->certs_ca)) {
rc = SSL_CTX_load_verify_locations (n->context, vsrv->certs_ca.buf, NULL);
if (rc != 1) {
OPENSSL_LAST_ERROR(error);
LOG_CRITICAL(CHEROKEE_ERROR_SSL_CA_READ,
vsrv->certs_ca.buf, error);
goto error;
}
X509_clients = SSL_load_client_CA_file (vsrv->certs_ca.buf);
if (X509_clients == NULL) {
OPENSSL_LAST_ERROR(error);
LOG_CRITICAL (CHEROKEE_ERROR_SSL_CA_LOAD,
vsrv->certs_ca.buf, error);
goto error;
}
CLEAR_LIBSSL_ERRORS;
SSL_CTX_set_client_CA_list (n->context, X509_clients);
TRACE (ENTRIES, "Setting client CA list: %s on '%s'\n", vsrv->certs_ca.buf, vsrv->name.buf);
} else {
verify_mode = SSL_VERIFY_NONE;
}
}
SSL_CTX_set_verify (n->context, verify_mode, NULL);
SSL_CTX_set_verify_depth (n->context, vsrv->verify_depth);
SSL_CTX_set_read_ahead (n->context, 1);
SSL_CTX_set_mode (n->context,
SSL_CTX_get_mode(n->context) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
/* Set the SSL context cache
*/
rc = SSL_CTX_set_session_id_context (n->context,
(unsigned char *) vsrv->name.buf,
(unsigned int) vsrv->name.len);
if (rc != 1) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR (CHEROKEE_ERROR_SSL_SESSION_ID, vsrv->name.buf, error);
}
SSL_CTX_set_session_cache_mode (n->context, SSL_SESS_CACHE_SERVER);
#ifndef OPENSSL_NO_TLSEXT
/* Enable SNI
*/
rc = SSL_CTX_set_tlsext_servername_callback (n->context, openssl_sni_servername_cb);
if (rc != 1) {
OPENSSL_LAST_ERROR(error);
LOG_WARNING (CHEROKEE_ERROR_SSL_SNI, vsrv->name.buf, error);
} else {
rc = SSL_CTX_set_tlsext_servername_arg (n->context, VSERVER_SRV(vsrv));
if (rc != 1) {
OPENSSL_LAST_ERROR(error);
LOG_WARNING (CHEROKEE_ERROR_SSL_SNI, vsrv->name.buf, error);
}
}
#endif /* OPENSSL_NO_TLSEXT */
*cryp_vsrv = CRYPTOR_VSRV(n);
return ret_ok;
error:
if (n->context != NULL) {
SSL_CTX_free (n->context);
n->context = NULL;
}
free (n);
return ret_error;
}
static ret_t
socket_initialize (cherokee_cryptor_socket_libssl_t *cryp,
cherokee_socket_t *socket,
cherokee_virtual_server_t *vserver,
cherokee_connection_t *conn)
{
int re;
const char *error;
cherokee_cryptor_vserver_libssl_t *vsrv_crytor = CRYPTOR_VSRV_SSL(vserver->cryptor);
#ifdef OPENSSL_NO_TLSEXT
cherokee_buffer_t servername = CHEROKEE_BUF_INIT;
#endif
/* Set the virtual server object reference
*/
CRYPTOR_SOCKET(cryp)->vserver_ref = vserver;
/* Check whether the virtual server supports SSL
*/
if (vserver->cryptor == NULL) {
return ret_not_found;
}
if (vsrv_crytor->context == NULL) {
return ret_not_found;
}
/* New session
*/
cryp->session = SSL_new (vsrv_crytor->context);
if (cryp->session == NULL) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR (CHEROKEE_ERROR_SSL_CONNECTION, error);
return ret_error;
}
/* Sets ssl to work in server mode
*/
SSL_set_accept_state (cryp->session);
/* Set the socket file descriptor
*/
re = SSL_set_fd (cryp->session, socket->socket);
if (re != 1) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR (CHEROKEE_ERROR_SSL_FD,
socket->socket, error);
return ret_error;
}
cryp->is_pending = false;
#ifndef OPENSSL_NO_TLSEXT
SSL_set_app_data (cryp->session, conn);
#else
/* Attempt to determine the vserver without SNI.
*/
cherokee_buffer_ensure_size(&servername, 40);
cherokee_socket_ntop (&conn->socket, servername.buf, servername.size);
cherokee_cryptor_libssl_find_vserver (cryp->session, CONN_SRV(conn), &servername, conn);
cherokee_buffer_mrproper (&servername);
#endif
return ret_ok;
}
static ret_t
_socket_init_tls (cherokee_cryptor_socket_libssl_t *cryp,
cherokee_socket_t *sock,
cherokee_virtual_server_t *vsrv,
cherokee_connection_t *conn,
cherokee_socket_status_t *blocking)
{
int re;
ret_t ret;
/* Initialize
*/
if (CRYPTOR_SOCKET(cryp)->initialized == false) {
ret = socket_initialize (cryp, sock, vsrv, conn);
if (ret != ret_ok) {
return ret_error;
}
CRYPTOR_SOCKET(cryp)->initialized = true;
}
/* TLS Handshake
*/
CLEAR_LIBSSL_ERRORS;
re = SSL_do_handshake (cryp->session);
if (re == 0) {
/* The TLS/SSL handshake was not successful but was
* shut down controlled and by the specifications of
* the TLS/SSL protocol.
*/
return ret_eof;
} else if (re <= 0) {
int err;
const char *error;
int err_sys = errno;
err = SSL_get_error (cryp->session, re);
switch (err) {
case SSL_ERROR_WANT_READ:
*blocking = socket_reading;
return ret_eagain;
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_CONNECT:
case SSL_ERROR_WANT_ACCEPT:
*blocking = socket_writing;
return ret_eagain;
case SSL_ERROR_SYSCALL:
if (err_sys == EAGAIN) {
return ret_eagain;
}
return ret_error;
case SSL_ERROR_SSL:
case SSL_ERROR_ZERO_RETURN:
return ret_error;
default:
OPENSSL_LAST_ERROR(error);
LOG_ERROR (CHEROKEE_ERROR_SSL_INIT, error);
return ret_error;
}
}
/* Report the connection details
*/
#ifdef TRACE_ENABLED
{
CHEROKEE_TEMP (buf,256);
SSL_CIPHER *cipher = SSL_get_current_cipher (cryp->session);
if (cipher) {
SSL_CIPHER_description (cipher, &buf[0], buf_size-1);
TRACE (ENTRIES, "SSL: %s, %sREUSED, Ciphers: %s",
SSL_get_version(cryp->session),
SSL_session_reused(cryp->session)? "" : "Not ", &buf[0]);
}
}
#endif
/* Disable Ciphers renegotiation (CVE-2009-3555)
*/
if (cryp->session->s3) {
cryp->session->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
}
return ret_ok;
}
static ret_t
_socket_shutdown (cherokee_cryptor_socket_libssl_t *cryp)
{
int re;
int ssl_error;
if (unlikely (cryp->session == NULL)) {
return ret_ok;
}
/* Send a 'close_notify' SSL message
*/
errno = 0;
CLEAR_LIBSSL_ERRORS;
re = SSL_shutdown (cryp->session);
if (re == 1) {
/* The shutdown was successfully completed. */
return ret_ok;
} else if (re == 0) {
/* Shutdown in progress */
int ssl_err = SSL_get_error(cryp->session, re);
/* According to OpenSSL bug #611, something bad might
* have happened during the call to SSL_shutdown()..
*/
switch (ssl_err) {
case SSL_ERROR_ZERO_RETURN:
return ret_ok;
case SSL_ERROR_SYSCALL: {
unsigned long e = ERR_get_error();
if (e == 0) {
/* EOF occurred in violation of protocol */
return ret_eof;
} else if (e == -1) {
/* Underlying BIO reported an I/O error */
switch (errno) {
case EINTR:
case EAGAIN:
return ret_eagain;
case EIO:
case EPIPE:
case ECONNRESET:
return ret_eof;
default:
return ret_error;
}
}
return ret_error;
}
default:
return ret_error;
}
} else if (re < 0) {
ssl_error = SSL_get_error (cryp->session, re);
switch (ssl_error) {
case SSL_ERROR_ZERO_RETURN:
return ret_ok;
case SSL_ERROR_WANT_READ:
return ret_eagain;
case SSL_ERROR_WANT_WRITE:
return ret_eagain;
case SSL_ERROR_SYSCALL:
CLEAR_LIBSSL_ERRORS;
switch (errno) {
case 0:
return ret_ok;
case EINTR:
case EAGAIN:
return ret_eagain;
default:
return ret_error;
}
break;
default:
return ret_error;
}
}
SHOULDNT_HAPPEN;
return ret_error;
}
static ret_t
_socket_write (cherokee_cryptor_socket_libssl_t *cryp,
char *buf,
int buf_len,
size_t *pcnt_written)
{
int re;
ssize_t len;
int error;
/* 'Truco del almendruco': This is a method to bypass the
* limitations of the libssl sockets regard to SSL_write.
*
* A buffer being sent cannot be modified. Since Cherokee's
* core does that by default (it removes the chunk of info
* already sent), we have to trick it. This piece of code
* reports EAGAIN errors until the complete buffer is sent.
*/
/* Stage 1: Keep track of the buffer
*/
if (cryp->writing.buf != buf) {
TRACE (ENTRIES, "SSL-Write. Sets new buffer: %p (len %d)\n", buf, buf_len);
cryp->writing.buf = buf;
cryp->writing.buf_len = buf_len;
cryp->writing.written = 0;
}
/* Proceed to write
*/
CLEAR_LIBSSL_ERRORS;
len = SSL_write (cryp->session, buf, buf_len);
if (likely (len > 0) ) {
/* Stage 2: Lie, if required
*/
cryp->writing.written += len;
if (cryp->writing.written >= buf_len) {
TRACE (ENTRIES, "SSL-Write. Buffer sent: %p (total len %d)\n", buf, buf_len);
*pcnt_written = buf_len;
return ret_ok;
}
TRACE (ENTRIES",lie", "SSL-Write lies, (package %d, written %d, total %d): eagain\n", len, cryp->writing.written, buf_len);
return ret_eagain;
}
if (len == 0) {
TRACE (ENTRIES",write", "write got %s\n", "EOF");
return ret_eof;
}
/* len < 0 */
error = errno;
re = SSL_get_error (cryp->session, len);
switch (re) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
TRACE (ENTRIES",write", "write len=%d (written=0), EAGAIN\n", buf_len);
return ret_eagain;
case SSL_ERROR_SYSCALL:
switch (error) {
case EAGAIN:
TRACE (ENTRIES",write", "write len=%d (written=0), EAGAIN\n", buf_len);
return ret_eagain;
#ifdef ENOTCONN
case ENOTCONN:
#endif
case EPIPE:
case ECONNRESET:
TRACE (ENTRIES",write", "write len=%d (written=0), EOF\n", buf_len);
return ret_eof;
default:
LOG_ERRNO_S (error, cherokee_err_error,
CHEROKEE_ERROR_SSL_SW_DEFAULT);
}
TRACE (ENTRIES",write", "write len=%d (written=0), ERROR: %s\n", buf_len, ERR_error_string(re, NULL));
return ret_error;
case SSL_ERROR_SSL:
TRACE (ENTRIES",write", "write len=%d (written=0), ERROR: %s\n", buf_len, ERR_error_string(re, NULL));
return ret_error;
}
LOG_ERROR (CHEROKEE_ERROR_SSL_SW_ERROR,
SSL_get_fd(cryp->session), (int)len, ERR_error_string(re, NULL));
TRACE (ENTRIES",write", "write len=%d (written=0), ERROR: %s\n", buf_len, ERR_error_string(re, NULL));
return ret_error;
}
static ret_t
_socket_read (cherokee_cryptor_socket_libssl_t *cryp,
char *buf,
int buf_size,
size_t *pcnt_read)
{
int re;
int error;
ssize_t len;
CLEAR_LIBSSL_ERRORS;
*pcnt_read = 0;
while (buf_size > 0) {
len = SSL_read (cryp->session, buf, buf_size);
if (len < 1)
break;
*pcnt_read += len;
buf += len;
buf_size -= len;
}
/* We have more data than buffer space. Mark the socket as
* having pending data. */
cryp->is_pending = (buf_size == 0);
if (*pcnt_read > 0) {
return ret_ok;
}
if (len == 0) {
return ret_eof;
}
/* len < 0 */
error = errno;
re = SSL_get_error (cryp->session, len);
switch (re) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
return ret_eagain;
case SSL_ERROR_ZERO_RETURN:
return ret_eof;
case SSL_ERROR_SSL:
return ret_error;
case SSL_ERROR_SYSCALL:
switch (error) {
case EAGAIN:
return ret_eagain;
case EPIPE:
case ECONNRESET:
return ret_eof;
default:
LOG_ERRNO_S (error, cherokee_err_error,
CHEROKEE_ERROR_SSL_SR_DEFAULT);
}
return ret_error;
}
LOG_ERROR (CHEROKEE_ERROR_SSL_SR_ERROR,
SSL_get_fd(cryp->session), (int)len, ERR_error_string(re, NULL));
return ret_error;
}
static int
_socket_pending (cherokee_cryptor_socket_libssl_t *cryp)
{
return cryp->is_pending;
}
static ret_t
_socket_clean (cherokee_cryptor_socket_libssl_t *cryp_socket)
{
cherokee_cryptor_socket_clean_base (CRYPTOR_SOCKET(cryp_socket));
if (cryp_socket->session != NULL) {
SSL_free (cryp_socket->session);
cryp_socket->session = NULL;
}
if (cryp_socket->ssl_ctx != NULL) {
SSL_CTX_free (cryp_socket->ssl_ctx);
cryp_socket->ssl_ctx = NULL;
}
return ret_ok;
}
static ret_t
_socket_free (cherokee_cryptor_socket_libssl_t *cryp_socket)
{
_socket_clean (cryp_socket);
free (cryp_socket);
return ret_ok;
}
static ret_t
_socket_new (cherokee_cryptor_libssl_t *cryp,
cherokee_cryptor_socket_libssl_t **cryp_socket)
{
ret_t ret;
CHEROKEE_NEW_STRUCT (n, cryptor_socket_libssl);
UNUSED(cryp);
ret = cherokee_cryptor_socket_init_base (CRYPTOR_SOCKET(n));
if (unlikely (ret != ret_ok))
return ret;
/* Socket properties */
n->session = NULL;
n->ssl_ctx = NULL;
n->writing.buf = NULL;
n->writing.buf_len = -1;
n->writing.written = -1;
/* Virtual methods */
CRYPTOR_SOCKET(n)->free = (cryptor_socket_func_free_t) _socket_free;
CRYPTOR_SOCKET(n)->clean = (cryptor_socket_func_clean_t) _socket_clean;
CRYPTOR_SOCKET(n)->init_tls = (cryptor_socket_func_init_tls_t) _socket_init_tls;
CRYPTOR_SOCKET(n)->shutdown = (cryptor_socket_func_shutdown_t) _socket_shutdown;
CRYPTOR_SOCKET(n)->read = (cryptor_socket_func_read_t) _socket_read;
CRYPTOR_SOCKET(n)->write = (cryptor_socket_func_write_t) _socket_write;
CRYPTOR_SOCKET(n)->pending = (cryptor_socket_func_pending_t) _socket_pending;
*cryp_socket = n;
return ret_ok;
}
static ret_t
_client_init_tls (cherokee_cryptor_client_libssl_t *cryp,
cherokee_buffer_t *host,
cherokee_socket_t *socket)
{
int re;
const char *error;
/* New context
*/
cryp->ssl_ctx = SSL_CTX_new (SSLv23_client_method());
if (cryp->ssl_ctx == NULL) {
OPENSSL_LAST_ERROR(error);
LOG_CRITICAL (CHEROKEE_ERROR_SSL_CREATE_CTX, error);
return ret_error;
}
/* CA verifications
re = cherokee_buffer_is_empty (&cryp->vserver_ref->certs_ca);
if (! re) {
re = SSL_CTX_load_verify_locations (socket->ssl_ctx,
socket->vserver_ref->certs_ca.buf, NULL);
if (! re) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR (CHEROKEE_ERROR_SSL_CTX_LOAD,
socket->vserver_ref->certs_ca.buf, error);
return ret_error;
}
re = SSL_CTX_set_default_verify_paths (socket->ssl_ctx);
if (! re) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR (CHEROKEE_ERROR_SSL_CTX_SET, error);
return ret_error;
}
}
*/
SSL_CTX_set_verify (cryp->ssl_ctx, SSL_VERIFY_NONE, NULL);
/* New session
*/
cryp->session = SSL_new (cryp->ssl_ctx);
if (cryp->session == NULL) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR (CHEROKEE_ERROR_SSL_CONNECTION, error);
return ret_error;
}
/* Set the socket file descriptor
*/
re = SSL_set_fd (cryp->session, socket->socket);
if (re != 1) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR (CHEROKEE_ERROR_SSL_FD, socket->socket, error);
return ret_error;
}
SSL_set_connect_state (cryp->session);
#ifndef OPENSSL_NO_TLSEXT
if ((host != NULL) &&
(! cherokee_buffer_is_empty (host)))
{
re = SSL_set_tlsext_host_name (cryp->session, host->buf);
if (re <= 0) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR (CHEROKEE_ERROR_SSL_SNI_SRV, error);
return ret_error;
}
}
#endif
re = SSL_connect (cryp->session);
if (re <= 0) {
OPENSSL_LAST_ERROR(error);
LOG_ERROR (CHEROKEE_ERROR_SSL_CONNECT, error);
return ret_error;
}
return ret_ok;
}
static ret_t
_client_free (cherokee_cryptor_client_libssl_t *cryp)
{
if (cryp->session != NULL) {
SSL_free (cryp->session);
cryp->session = NULL;
}
if (cryp->ssl_ctx != NULL) {
SSL_CTX_free (cryp->ssl_ctx);
cryp->ssl_ctx = NULL;
}
free (cryp);
return ret_ok;
}
static ret_t
_client_new (cherokee_cryptor_t *cryp,
cherokee_cryptor_client_t **cryp_client)
{
ret_t ret;
CHEROKEE_NEW_STRUCT (n, cryptor_client_libssl);
UNUSED(cryp);
ret = cherokee_cryptor_socket_init_base (CRYPTOR_SOCKET(n));
if (unlikely (ret != ret_ok))
return ret;
/* Client properties */
n->session = NULL;
n->ssl_ctx = NULL;
/* Socket */
CRYPTOR_SOCKET(n)->shutdown = (cryptor_socket_func_shutdown_t) _socket_shutdown;
CRYPTOR_SOCKET(n)->read = (cryptor_socket_func_read_t) _socket_read;
CRYPTOR_SOCKET(n)->write = (cryptor_socket_func_write_t) _socket_write;
CRYPTOR_SOCKET(n)->pending = (cryptor_socket_func_pending_t) _socket_pending;
CRYPTOR_SOCKET(n)->clean = (cryptor_socket_func_clean_t) _socket_clean;
/* Client */
CRYPTOR_SOCKET(n)->free = (cryptor_socket_func_free_t) _client_free;
CRYPTOR_SOCKET(n)->init_tls = (cryptor_client_func_init_t) _client_init_tls;
*cryp_client = CRYPTOR_CLIENT(n);
return ret_ok;
}
PLUGIN_INFO_INIT (libssl,
cherokee_cryptor,
cherokee_cryptor_libssl_new,
NULL);
ret_t
cherokee_cryptor_libssl_new (cherokee_cryptor_libssl_t **cryp)
{
ret_t ret;
CHEROKEE_NEW_STRUCT (n, cryptor_libssl);
/* Init
*/
ret = cherokee_cryptor_init_base (CRYPTOR(n), PLUGIN_INFO_PTR(libssl));
if (ret != ret_ok)
return ret;
MODULE(n)->free = (module_func_free_t) _free;
CRYPTOR(n)->configure = (cryptor_func_configure_t) _configure;
CRYPTOR(n)->vserver_new = (cryptor_func_vserver_new_t) _vserver_new;
CRYPTOR(n)->socket_new = (cryptor_func_socket_new_t) _socket_new;
CRYPTOR(n)->client_new = (cryptor_func_client_new_t) _client_new;
*cryp = n;
return ret_ok;
}
/* Private low-level functions for OpenSSL
*/
static pthread_mutex_t *locks;
static size_t locks_num;
static unsigned long
__get_thread_id (void)
{
return (unsigned long) pthread_self();
}
static void
__lock_thread (int mode, int n, const char *file, int line)
{
UNUSED (file);
UNUSED (line);
if (mode & CRYPTO_LOCK) {
CHEROKEE_MUTEX_LOCK (&locks[n]);
} else {
CHEROKEE_MUTEX_UNLOCK (&locks[n]);
}
}
/* Plug-in initialization
*/
cherokee_boolean_t PLUGIN_IS_INIT(libssl) = false;
void
PLUGIN_INIT_NAME(libssl) (cherokee_plugin_loader_t *loader)
{
#if HAVE_OPENSSL_ENGINE_H
ENGINE *e;
#endif
UNUSED(loader);
/* Do not initialize the library twice
*/
PLUGIN_INIT_ONCE_CHECK (libssl);
/* Init OpenSSL
*/
OPENSSL_config (NULL);
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
/* Ensure PRNG has been seeded with enough data
*/
if (RAND_status() == 0) {
LOG_WARNING_S (CHEROKEE_ERROR_SSL_NO_ENTROPY);
}
/* Init concurrency related stuff
*/
if ((CRYPTO_get_id_callback() == NULL) &&
(CRYPTO_get_locking_callback() == NULL))
{
cuint_t n;
CRYPTO_set_id_callback (__get_thread_id);
CRYPTO_set_locking_callback (__lock_thread);
locks_num = CRYPTO_num_locks();
locks = malloc (locks_num * sizeof(*locks));
for (n = 0; n < locks_num; n++) {
CHEROKEE_MUTEX_INIT (&locks[n], NULL);
}
}
# if HAVE_OPENSSL_ENGINE_H
# if OPENSSL_VERSION_NUMBER >= 0x00907000L
ENGINE_load_builtin_engines();
OpenSSL_add_all_algorithms();
# endif
e = ENGINE_by_id("pkcs11");
while (e != NULL) {
if(! ENGINE_init(e)) {
ENGINE_free (e);
LOG_CRITICAL_S (CHEROKEE_ERROR_SSL_PKCS11);
break;
}
if(! ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
ENGINE_free (e);
LOG_CRITICAL_S (CHEROKEE_ERROR_SSL_DEFAULTS);
break;
}
ENGINE_finish(e);
ENGINE_free(e);
break;
}
#endif
}
Jump to Line
Something went wrong with that request. Please try again.