Browse Source

[mod_openssl] move openssl code into mod_openssl

large code move, but minimal changes made to code (besides whitespace),
so that code builds

next: need to isolate openssl data structures and config parsing
personal/stbuehler/mod-csrf
Glenn Strauss 5 years ago
parent
commit
bdbea2aea8
  1. 1
      src/CMakeLists.txt
  2. 2
      src/Makefile.am
  3. 2
      src/SConscript
  4. 26
      src/configfile.c
  5. 134
      src/connections-glue.c
  6. 107
      src/connections.c
  7. 1
      src/connections.h
  8. 37
      src/http-header-glue.c
  9. 3
      src/mod_magnet.c
  10. 581
      src/mod_openssl.c
  11. 4
      src/network_backends.h
  12. 188
      src/network_openssl.c
  13. 102
      src/response.c
  14. 3
      src/response.h
  15. 34
      src/server.c

1
src/CMakeLists.txt

@ -560,7 +560,6 @@ add_executable(lighttpd
network_darwin_sendfile.c
network_freebsd_sendfile.c
network_linux_sendfile.c
network_openssl.c
network_solaris_sendfilev.c
network_write.c
network_write_mmap.c

2
src/Makefile.am

@ -80,7 +80,7 @@ src = server.c response.c connections.c network.c \
network_write.c network_linux_sendfile.c \
network_write_mmap.c network_write_no_mmap.c \
network_freebsd_sendfile.c network_writev.c \
network_solaris_sendfilev.c network_openssl.c \
network_solaris_sendfilev.c \
network_darwin_sendfile.c \
configfile.c configparser.c request.c proc_open.c

2
src/SConscript

@ -76,7 +76,7 @@ src = Split("server.c response.c connections.c network.c \
network_write_mmap.c network_write_no_mmap.c \
network_write.c network_linux_sendfile.c \
network_freebsd_sendfile.c \
network_solaris_sendfilev.c network_openssl.c \
network_solaris_sendfilev.c \
network_darwin_sendfile.c \
configfile.c configparser.c request.c proc_open.c")

26
src/configfile.c

@ -350,6 +350,15 @@ static int config_insert(server *srv) {
if (s->stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) {
s->stream_response_body |= FDEVENT_STREAM_RESPONSE;
}
#ifndef USE_OPENSSL
if (s->ssl_enabled) {
log_error_write(srv, __FILE__, __LINE__, "s",
"ssl support is missing, recompile with --with-openssl");
ret = HANDLER_ERROR;
break;
}
#endif
}
{
@ -1594,22 +1603,5 @@ int config_set_defaults(server *srv) {
}
}
if (s->ssl_enabled) {
if (buffer_string_is_empty(s->ssl_pemfile)) {
/* PEM file is require */
log_error_write(srv, __FILE__, __LINE__, "s",
"ssl.pemfile has to be set");
return -1;
}
#ifndef USE_OPENSSL
log_error_write(srv, __FILE__, __LINE__, "s",
"ssl support is missing, recompile with --with-openssl");
return -1;
#endif
}
return 0;
}

134
src/connections-glue.c

@ -6,11 +6,6 @@
#include <errno.h>
#ifdef USE_OPENSSL
# include <openssl/ssl.h>
# include <openssl/err.h>
#endif
const char *connection_get_state(connection_state_t state) {
switch (state) {
case CON_STATE_CONNECT: return "connect";
@ -97,134 +92,6 @@ static void dump_packet(const unsigned char *data, size_t len) {
}
#endif
int connection_read_cq_ssl(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) {
#ifdef USE_OPENSSL
int r, ssl_err, len;
char *mem = NULL;
size_t mem_len = 0;
force_assert(cq == con->read_queue); /*(code transform assumption; minimize diff)*/
UNUSED(max_bytes);
if (!con->srv_socket->is_ssl) return -1;
ERR_clear_error();
do {
chunkqueue_get_memory(con->read_queue, &mem, &mem_len, 0, SSL_pending(con->ssl));
#if 0
/* overwrite everything with 0 */
memset(mem, 0, mem_len);
#endif
len = SSL_read(con->ssl, mem, mem_len);
if (len > 0) {
chunkqueue_use_memory(con->read_queue, len);
con->bytes_read += len;
} else {
chunkqueue_use_memory(con->read_queue, 0);
}
if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) {
log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection");
connection_set_state(srv, con, CON_STATE_ERROR);
return -1;
}
} while (len > 0 && (con->conf.ssl_read_ahead || SSL_pending(con->ssl) > 0));
if (len < 0) {
int oerrno = errno;
switch ((r = SSL_get_error(con->ssl, len))) {
case SSL_ERROR_WANT_WRITE:
con->is_writable = -1;
case SSL_ERROR_WANT_READ:
con->is_readable = 0;
/* the manual says we have to call SSL_read with the same arguments next time.
* we ignore this restriction; no one has complained about it in 1.5 yet, so it probably works anyway.
*/
return 0;
case SSL_ERROR_SYSCALL:
/**
* man SSL_get_error()
*
* SSL_ERROR_SYSCALL
* Some I/O error occurred. The OpenSSL error queue may contain more
* information on the error. If the error queue is empty (i.e.
* ERR_get_error() returns 0), ret can be used to find out more about
* the error: If ret == 0, an EOF was observed that violates the
* protocol. If ret == -1, the underlying BIO reported an I/O error
* (for socket I/O on Unix systems, consult errno for details).
*
*/
while((ssl_err = ERR_get_error())) {
/* get all errors from the error-queue */
log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:",
r, ERR_error_string(ssl_err, NULL));
}
switch(oerrno) {
default:
log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:",
len, r, oerrno,
strerror(oerrno));
break;
}
break;
case SSL_ERROR_ZERO_RETURN:
/* clean shutdown on the remote side */
if (r == 0) {
/* FIXME: later */
}
/* fall thourgh */
default:
while((ssl_err = ERR_get_error())) {
switch (ERR_GET_REASON(ssl_err)) {
case SSL_R_SSL_HANDSHAKE_FAILURE:
#ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA
case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
#endif
#ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN
case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
#endif
#ifdef SSL_R_SSLV3_ALERT_BAD_CERTIFICATE
case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
#endif
if (!con->conf.log_ssl_noise) continue;
break;
default:
break;
}
/* get all errors from the error-queue */
log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:",
r, ERR_error_string(ssl_err, NULL));
}
break;
}
connection_set_state(srv, con, CON_STATE_ERROR);
return -1;
} else if (len == 0) {
con->is_readable = 0;
/* the other end close the connection -> KEEP-ALIVE */
return -2;
} else {
return 0;
}
#else
UNUSED(srv);
UNUSED(con);
UNUSED(cq);
UNUSED(max_bytes);
return -1;
#endif
}
/* 0: everything ok, -1: error, -2: con closed */
int connection_read_cq(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) {
int len;
@ -545,6 +412,7 @@ handler_t connection_handle_read_post_state(server *srv, connection *con) {
switch(con->network_read(srv, con, con->read_queue, MAX_READ_LIMIT)) {
case -1:
connection_set_state(srv, con, CON_STATE_ERROR);
return HANDLER_ERROR;
case -2:
is_closed = 1;

107
src/connections.c

@ -28,11 +28,6 @@
#include <fcntl.h>
#include <assert.h>
#ifdef USE_OPENSSL
# include <openssl/ssl.h>
# include <openssl/err.h>
#endif
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
@ -119,13 +114,6 @@ static int connection_del(server *srv, connection *con) {
}
static int connection_close(server *srv, connection *con) {
#ifdef USE_OPENSSL
server_socket *srv_sock = con->srv_socket;
if (srv_sock->is_ssl) {
if (con->ssl) SSL_free(con->ssl);
con->ssl = NULL;
}
#endif
plugins_call_handle_connection_close(srv, con);
fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd);
@ -187,70 +175,6 @@ static void connection_handle_close_state(server *srv, connection *con) {
}
static void connection_handle_shutdown(server *srv, connection *con) {
#ifdef USE_OPENSSL
server_socket *srv_sock = con->srv_socket;
if (srv_sock->is_ssl && SSL_is_init_finished(con->ssl)) {
int ret, ssl_r;
unsigned long err;
ERR_clear_error();
switch ((ret = SSL_shutdown(con->ssl))) {
case 1:
/* ok */
break;
case 0:
/* wait for fd-event
*
* FIXME: wait for fdevent and call SSL_shutdown again
*
*/
ERR_clear_error();
if (-1 != (ret = SSL_shutdown(con->ssl))) break;
/* fall through */
default:
switch ((ssl_r = SSL_get_error(con->ssl, ret))) {
case SSL_ERROR_ZERO_RETURN:
break;
case SSL_ERROR_WANT_WRITE:
/*con->is_writable = -1;*//*(no effect; shutdown() called below)*/
case SSL_ERROR_WANT_READ:
break;
case SSL_ERROR_SYSCALL:
/* perhaps we have error waiting in our error-queue */
if (0 != (err = ERR_get_error())) {
do {
log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:",
ssl_r, ret,
ERR_error_string(err, NULL));
} while((err = ERR_get_error()));
} else if (errno != 0) { /* ssl bug (see lighttpd ticket #2213): sometimes errno == 0 */
switch(errno) {
case EPIPE:
case ECONNRESET:
break;
default:
log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):",
ssl_r, ret, errno,
strerror(errno));
break;
}
}
break;
default:
while((err = ERR_get_error())) {
log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:",
ssl_r, ret,
ERR_error_string(err, NULL));
}
break;
}
}
ERR_clear_error();
}
#endif
plugins_call_handle_connection_shut_wr(srv, con);
srv->con_closed++;
@ -794,6 +718,7 @@ static int connection_handle_read_state(server *srv, connection *con) {
switch(con->network_read(srv, con, con->read_queue, MAX_READ_LIMIT)) {
case -1:
connection_set_state(srv, con, CON_STATE_ERROR);
return -1;
case -2:
is_closed = 1;
@ -1034,11 +959,6 @@ static int connection_write_cq(server *srv, connection *con, chunkqueue *cq, off
return srv->network_backend_write(srv, con, con->fd, cq, max_bytes);
}
#include "network_backends.h" /* network_write_chunkqueue_openssl() */
static int connection_write_cq_ssl(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) {
return network_write_chunkqueue_openssl(srv, con, con->ssl, cq, max_bytes);
}
connection *connection_accepted(server *srv, server_socket *srv_socket, sock_addr *cnt_addr, int cnt) {
connection *con;
@ -1071,31 +991,6 @@ connection *connection_accepted(server *srv, server_socket *srv_socket, sock_add
connection_close(srv, con);
return NULL;
}
#ifdef USE_OPENSSL
/* connect FD to SSL */
if (srv_socket->is_ssl) {
if (NULL == (con->ssl = SSL_new(srv_socket->ssl_ctx))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
ERR_error_string(ERR_get_error(), NULL));
connection_close(srv, con);
return NULL;
}
con->network_read = connection_read_cq_ssl;
con->network_write = connection_write_cq_ssl;
con->renegotiations = 0;
SSL_set_app_data(con->ssl, con);
SSL_set_accept_state(con->ssl);
if (1 != (SSL_set_fd(con->ssl, cnt))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
ERR_error_string(ERR_get_error(), NULL));
connection_close(srv, con);
return NULL;
}
}
#endif
if (HANDLER_GO_ON != plugins_call_handle_connection_accept(srv, con)) {
connection_close(srv, con);
return NULL;

1
src/connections.h

@ -17,7 +17,6 @@ const char * connection_get_state(connection_state_t state);
const char * connection_get_short_state(connection_state_t state);
int connection_state_machine(server *srv, connection *con);
int connection_read_cq(server *srv, connection *con, chunkqueue *cq, off_t max_bytes);
int connection_read_cq_ssl(server *srv, connection *con, chunkqueue *cq, off_t max_bytes);
handler_t connection_handle_read_post_state(server *srv, connection *con);
handler_t connection_handle_read_post_error(server *srv, connection *con, int http_status);
void connection_response_reset(server *srv, connection *con);

37
src/http-header-glue.c

@ -1007,9 +1007,6 @@ int http_cgi_headers (server *srv, connection *con, http_cgi_opts *opts, http_cg
}
srv->request_env(srv, con);
#ifdef USE_OPENSSL
if (con->ssl) http_cgi_ssl_env(srv, con);
#endif
for (n = 0; n < con->environment->used; n++) {
data_string *ds = (data_string *)con->environment->data[n];
@ -1023,37 +1020,3 @@ int http_cgi_headers (server *srv, connection *con, http_cgi_opts *opts, http_cg
return rc;
}
#ifdef USE_OPENSSL
void http_cgi_ssl_env(server *srv, connection *con) {
const char *s;
const SSL_CIPHER *cipher;
UNUSED(srv);
if (!con->ssl) return;
s = SSL_get_version(con->ssl);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_PROTOCOL"),
s, strlen(s));
if ((cipher = SSL_get_current_cipher(con->ssl))) {
int usekeysize, algkeysize;
char buf[LI_ITOSTRING_LENGTH];
s = SSL_CIPHER_get_name(cipher);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CIPHER"),
s, strlen(s));
usekeysize = SSL_CIPHER_get_bits(cipher, &algkeysize);
li_itostrn(buf, sizeof(buf), usekeysize);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CIPHER_USEKEYSIZE"),
buf, strlen(buf));
li_itostrn(buf, sizeof(buf), algkeysize);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CIPHER_ALGKEYSIZE"),
buf, strlen(buf));
}
}
#endif

3
src/mod_magnet.c

@ -1031,9 +1031,6 @@ static handler_t magnet_attract_array(server *srv, connection *con, plugin_data
if (files->used == 0) return HANDLER_GO_ON;
srv->request_env(srv, con);
#ifdef USE_OPENSSL
if (con->ssl) http_cgi_ssl_env(srv, con);
#endif
/**
* execute all files and jump out on the first !HANDLER_GO_ON

581
src/mod_openssl.c

@ -2,6 +2,7 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
#ifndef USE_OPENSSL_KERBEROS
#ifndef OPENSSL_NO_KRB5
@ -10,6 +11,7 @@
#endif
#include <openssl/ssl.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include "base.h"
@ -64,6 +66,20 @@ FREE_FUNC(mod_openssl_free)
if (!p) return HANDLER_GO_ON;
if (p->config_storage) {
for (size_t i = 0; i < srv->config_context->used; ++i) {
specific_config *s = srv->config_storage[i];
buffer_free(s->ssl_pemfile);
buffer_free(s->ssl_ca_file);
buffer_free(s->ssl_cipher_list);
buffer_free(s->ssl_dh_file);
buffer_free(s->ssl_ec_curve);
buffer_free(s->ssl_verifyclient_username);
SSL_CTX_free(s->ssl_ctx);
EVP_PKEY_free(s->ssl_pemfile_pkey);
X509_free(s->ssl_pemfile_x509);
if (NULL != s->ssl_ca_file_cert_names)
sk_X509_NAME_pop_free(s->ssl_ca_file_cert_names,X509_NAME_free);
}
for (size_t i = 0; i < srv->config_context->used; ++i) {
plugin_config *s = p->config_storage[i];
if (NULL == s) continue;
@ -75,7 +91,23 @@ FREE_FUNC(mod_openssl_free)
free(p);
UNUSED(srv);
if (srv->ssl_is_init) {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L \
&& !defined(LIBRESSL_VERSION_NUMBER)
/*(OpenSSL libraries handle thread init and deinit)
* https://github.com/openssl/openssl/pull/1048 */
#else
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
ERR_remove_thread_state(NULL);
#else
ERR_remove_state(0);
#endif
EVP_cleanup();
#endif
}
return HANDLER_GO_ON;
}
@ -101,16 +133,341 @@ mod_openssl_patch_connection (server *srv, connection *con, handler_ctx *p)
#undef PATCH
static int
load_next_chunk (server *srv, chunkqueue *cq, off_t max_bytes,
const char **data, size_t *data_len)
{
chunk * const c = cq->first;
#define LOCAL_SEND_BUFSIZE (64 * 1024)
/* this is a 64k sendbuffer
*
* it has to stay at the same location all the time to satisfy the needs
* of SSL_write to pass the SAME parameter in case of a _WANT_WRITE
*
* buffer is allocated once, is NOT realloced and is NOT freed at shutdown
* -> we expect a 64k block to 'leak' in valgrind
* */
static char *local_send_buffer = NULL;
force_assert(NULL != c);
switch (c->type) {
case MEM_CHUNK:
{
size_t have;
force_assert(c->offset >= 0
&& c->offset <= (off_t)buffer_string_length(c->mem));
have = buffer_string_length(c->mem) - c->offset;
if ((off_t) have > max_bytes) have = max_bytes;
*data = c->mem->ptr + c->offset;
*data_len = have;
}
return 0;
case FILE_CHUNK:
if (NULL == local_send_buffer) {
local_send_buffer = malloc(LOCAL_SEND_BUFSIZE);
force_assert(NULL != local_send_buffer);
}
if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
{
off_t offset, toSend;
force_assert(c->offset >= 0 && c->offset <= c->file.length);
offset = c->file.start + c->offset;
toSend = c->file.length - c->offset;
if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE;
if (toSend > max_bytes) toSend = max_bytes;
if (-1 == lseek(c->file.fd, offset, SEEK_SET)) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"lseek: ", strerror(errno));
return -1;
}
if (-1 == (toSend = read(c->file.fd, local_send_buffer, toSend))) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"read: ", strerror(errno));
return -1;
}
*data = local_send_buffer;
*data_len = toSend;
}
return 0;
}
return -1;
}
static int
connection_write_cq_ssl (server *srv, connection *con,
chunkqueue *cq, off_t max_bytes)
{
/* the remote side closed the connection before without shutdown request
* - IE
* - wget
* if keep-alive is disabled */
SSL *ssl = con->ssl;
if (con->keep_alive == 0) {
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
}
chunkqueue_remove_finished_chunks(cq);
while (max_bytes > 0 && NULL != cq->first) {
const char *data;
size_t data_len;
int r;
if (0 != load_next_chunk(srv,cq,max_bytes,&data,&data_len)) return -1;
/**
* SSL_write man-page
*
* WARNING
* When an SSL_write() operation has to be repeated because of
* SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be
* repeated with the same arguments.
*/
ERR_clear_error();
r = SSL_write(ssl, data, data_len);
if (con->renegotiations > 1
&& con->conf.ssl_disable_client_renegotiation) {
log_error_write(srv, __FILE__, __LINE__, "s",
"SSL: renegotiation initiated by client, killing connection");
return -1;
}
if (r <= 0) {
int ssl_r;
unsigned long err;
switch ((ssl_r = SSL_get_error(ssl, r))) {
case SSL_ERROR_WANT_READ:
con->is_readable = -1;
return 0; /* try again later */
case SSL_ERROR_WANT_WRITE:
con->is_writable = -1;
return 0; /* try again later */
case SSL_ERROR_SYSCALL:
/* perhaps we have error waiting in our error-queue */
if (0 != (err = ERR_get_error())) {
do {
log_error_write(srv, __FILE__, __LINE__, "sdds",
"SSL:", ssl_r, r,
ERR_error_string(err, NULL));
} while((err = ERR_get_error()));
} else if (r == -1) {
/* no, but we have errno */
switch(errno) {
case EPIPE:
case ECONNRESET:
return -2;
default:
log_error_write(srv, __FILE__, __LINE__, "sddds",
"SSL:", ssl_r, r, errno,
strerror(errno));
break;
}
} else {
/* neither error-queue nor errno ? */
log_error_write(srv, __FILE__, __LINE__, "sddds",
"SSL (error):", ssl_r, r, errno,
strerror(errno));
}
break;
case SSL_ERROR_ZERO_RETURN:
/* clean shutdown on the remote side */
if (r == 0) return -2;
/* fall through */
default:
while((err = ERR_get_error())) {
log_error_write(srv, __FILE__, __LINE__, "sdds",
"SSL:", ssl_r, r,
ERR_error_string(err, NULL));
}
break;
}
return -1;
}
chunkqueue_mark_written(cq, r);
max_bytes -= r;
if ((size_t) r < data_len) break; /* try again later */
}
return 0;
}
static int
connection_read_cq_ssl (server *srv, connection *con,
chunkqueue *cq, off_t max_bytes)
{
int r, ssl_err, len;
char *mem = NULL;
size_t mem_len = 0;
/*(code transform assumption; minimize diff)*/
force_assert(cq == con->read_queue);
UNUSED(max_bytes);
ERR_clear_error();
do {
chunkqueue_get_memory(con->read_queue, &mem, &mem_len, 0,
SSL_pending(con->ssl));
#if 0
/* overwrite everything with 0 */
memset(mem, 0, mem_len);
#endif
len = SSL_read(con->ssl, mem, mem_len);
if (len > 0) {
chunkqueue_use_memory(con->read_queue, len);
con->bytes_read += len;
} else {
chunkqueue_use_memory(con->read_queue, 0);
}
if (con->renegotiations > 1
&& con->conf.ssl_disable_client_renegotiation) {
log_error_write(srv, __FILE__, __LINE__, "s",
"SSL: renegotiation initiated by client, killing connection");
return -1;
}
} while (len > 0 &&(con.conf->ssl_read_ahead || SSL_pending(con->ssl) > 0));
if (len < 0) {
int oerrno = errno;
switch ((r = SSL_get_error(con->ssl, len))) {
case SSL_ERROR_WANT_WRITE:
con->is_writable = -1;
case SSL_ERROR_WANT_READ:
con->is_readable = 0;
/* the manual says we have to call SSL_read with the same arguments
* next time. we ignore this restriction; no one has complained
* about it in 1.5 yet, so it probably works anyway.
*/
return 0;
case SSL_ERROR_SYSCALL:
/**
* man SSL_get_error()
*
* SSL_ERROR_SYSCALL
* Some I/O error occurred. The OpenSSL error queue may contain
* more information on the error. If the error queue is empty
* (i.e. ERR_get_error() returns 0), ret can be used to find out
* more about the error: If ret == 0, an EOF was observed that
* violates the protocol. If ret == -1, the underlying BIO
* reported an I/O error (for socket I/O on Unix systems, consult
* errno for details).
*
*/
while((ssl_err = ERR_get_error())) {
/* get all errors from the error-queue */
log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:",
r, ERR_error_string(ssl_err, NULL));
}
switch(oerrno) {
default:
log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:",
len, r, oerrno,
strerror(oerrno));
break;
}
break;
case SSL_ERROR_ZERO_RETURN:
/* clean shutdown on the remote side */
if (r == 0) {
/* FIXME: later */
}
/* fall thourgh */
default:
while((ssl_err = ERR_get_error())) {
switch (ERR_GET_REASON(ssl_err)) {
case SSL_R_SSL_HANDSHAKE_FAILURE:
#ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA
case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
#endif
#ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN
case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
#endif
#ifdef SSL_R_SSLV3_ALERT_BAD_CERTIFICATE
case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
#endif
if (!con->conf.log_ssl_noise) continue;
break;
default:
break;
}
/* get all errors from the error-queue */
log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:",
r, ERR_error_string(ssl_err, NULL));
}
break;
}
return -1;
} else if (len == 0) {
con->is_readable = 0;
/* the other end close the connection -> KEEP-ALIVE */
return -2;
} else {
return 0;
}
}
CONNECTION_FUNC(mod_openssl_handle_con_accept)
{
plugin_data *p = p_d;
handler_ctx *hctx;
server_socket *srv_sock = con->srv_socket;
if (!srv_sock->is_ssl) return HANDLER_GO_ON;
{
plugin_data *p = p_d;
handler_ctx *hctx = handler_ctx_init();
con->plugin_ctx[p->id] = hctx;
mod_openssl_patch_connection(srv, con, hctx);
hctx = handler_ctx_init();
con->plugin_ctx[p->id] = hctx;
mod_openssl_patch_connection(srv, con, hctx);
/* connect fd to SSL */
con->ssl = SSL_new(srv->config_storage[srv_sock->sidx]->ssl_ctx);
if (NULL == con->ssl) {
log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
ERR_error_string(ERR_get_error(), NULL));
return HANDLER_ERROR;
}
con->network_read = connection_read_cq_ssl;
con->network_write = connection_write_cq_ssl;
con->renegotiations = 0;
SSL_set_app_data(con->ssl, con);
SSL_set_accept_state(con->ssl);
if (1 != (SSL_set_fd(con->ssl, con->fd))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:",
ERR_error_string(ERR_get_error(), NULL));
return HANDLER_ERROR;
}
return HANDLER_GO_ON;
@ -123,7 +480,69 @@ CONNECTION_FUNC(mod_openssl_handle_con_shut_wr)
handler_ctx *hctx = con->plugin_ctx[p->id];
if (NULL == hctx) return HANDLER_GO_ON;
UNUSED(srv);
if (SSL_is_init_finished(con->ssl)) {
int ret, ssl_r;
unsigned long err;
ERR_clear_error();
switch ((ret = SSL_shutdown(con->ssl))) {
case 1:
/* ok */
break;
case 0:
/* wait for fd-event
*
* FIXME: wait for fdevent and call SSL_shutdown again
*
*/
ERR_clear_error();
if (-1 != (ret = SSL_shutdown(con->ssl))) break;
/* fall through */
default:
switch ((ssl_r = SSL_get_error(con->ssl, ret))) {
case SSL_ERROR_ZERO_RETURN:
break;
case SSL_ERROR_WANT_WRITE:
/*con->is_writable=-1;*//*(no effect; shutdown() called below)*/
case SSL_ERROR_WANT_READ:
break;
case SSL_ERROR_SYSCALL:
/* perhaps we have error waiting in our error-queue */
if (0 != (err = ERR_get_error())) {
do {
log_error_write(srv, __FILE__, __LINE__, "sdds",
"SSL:", ssl_r, ret,
ERR_error_string(err, NULL));
} while((err = ERR_get_error()));
} else if (errno != 0) {
/*ssl bug (see lighttpd ticket #2213): sometimes errno==0*/
switch(errno) {
case EPIPE:
case ECONNRESET:
break;
default:
log_error_write(srv, __FILE__, __LINE__, "sddds",
"SSL (error):", ssl_r, ret, errno,
strerror(errno));
break;
}
}
break;
default:
while((err = ERR_get_error())) {
log_error_write(srv, __FILE__, __LINE__, "sdds",
"SSL:", ssl_r, ret,
ERR_error_string(err, NULL));
}
break;
}
}
ERR_clear_error();
}
return HANDLER_GO_ON;
}
@ -134,6 +553,8 @@ CONNECTION_FUNC(mod_openssl_handle_con_close)
handler_ctx *hctx = con->plugin_ctx[p->id];
if (NULL == hctx) return HANDLER_GO_ON;
if (con->ssl) SSL_free(con->ssl);
con->ssl = NULL;
handler_ctx_free(hctx);
UNUSED(srv);
@ -141,6 +562,146 @@ CONNECTION_FUNC(mod_openssl_handle_con_close)
}
static void
https_add_ssl_client_entries (server *srv, connection *con)
{
X509 *xs;
X509_NAME *xn;
int i, nentries;
long vr = SSL_get_verify_result(con->ssl);
if (vr != X509_V_OK) {
char errstr[256];
ERR_error_string_n(vr, errstr, sizeof(errstr));
buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("FAILED:"));
buffer_append_string(srv->tmp_buf, errstr);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CLIENT_VERIFY"),
CONST_BUF_LEN(srv->tmp_buf));
return;
} else if (!(xs = SSL_get_peer_certificate(con->ssl))) {
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CLIENT_VERIFY"),
CONST_STR_LEN("NONE"));
return;
} else {
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CLIENT_VERIFY"),
CONST_STR_LEN("SUCCESS"));
}
buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("SSL_CLIENT_S_DN_"));
xn = X509_get_subject_name(xs);
for (i = 0, nentries = X509_NAME_entry_count(xn); i < nentries; ++i) {
int xobjnid;
const char * xobjsn;
X509_NAME_ENTRY *xe;
if (!(xe = X509_NAME_get_entry(xn, i))) {
continue;
}
xobjnid = OBJ_obj2nid((ASN1_OBJECT*)X509_NAME_ENTRY_get_object(xe));
xobjsn = OBJ_nid2sn(xobjnid);
if (xobjsn) {
buffer_string_set_length(srv->tmp_buf,sizeof("SSL_CLIENT_S_DN_")-1);
buffer_append_string(srv->tmp_buf, xobjsn);
array_set_key_value(con->environment,
CONST_BUF_LEN(srv->tmp_buf),
(const char*)X509_NAME_ENTRY_get_data(xe)->data,
X509_NAME_ENTRY_get_data(xe)->length);
}
}
{
ASN1_INTEGER *xsn = X509_get_serialNumber(xs);
BIGNUM *serialBN = ASN1_INTEGER_to_BN(xsn, NULL);
char *serialHex = BN_bn2hex(serialBN);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CLIENT_M_SERIAL"),
serialHex, strlen(serialHex));
OPENSSL_free(serialHex);
BN_free(serialBN);
}
if (!buffer_string_is_empty(con->conf.ssl_verifyclient_username)) {
/* pick one of the exported values as "REMOTE_USER", for example
* ssl.verifyclient.username = "SSL_CLIENT_S_DN_UID"
* or
* ssl.verifyclient.username = "SSL_CLIENT_S_DN_emailAddress"
*/
data_string *ds = (data_string *)
array_get_element(con->environment,
con->conf.ssl_verifyclient_username->ptr);
if (ds) { /* same as http_auth.c:http_auth_setenv() */
array_set_key_value(con->environment,
CONST_STR_LEN("REMOTE_USER"),
CONST_BUF_LEN(ds->value));
array_set_key_value(con->environment,
CONST_STR_LEN("AUTH_TYPE"),
CONST_STR_LEN("SSL_CLIENT_VERIFY"));
}
}
if (con->conf.ssl_verifyclient_export_cert) {
BIO *bio;
if (NULL != (bio = BIO_new(BIO_s_mem()))) {
data_string *envds;
int n;
PEM_write_bio_X509(bio, xs);
n = BIO_pending(bio);
envds = (data_string *)
array_get_unused_element(con->environment, TYPE_STRING);
if (NULL == envds) {
envds = data_string_init();
}
buffer_copy_string_len(envds->key,CONST_STR_LEN("SSL_CLIENT_CERT"));
buffer_string_prepare_copy(envds->value, n);
BIO_read(bio, envds->value->ptr, n);
BIO_free(bio);
buffer_commit(envds->value, n);
array_replace(con->environment, (data_unset *)envds);
}
}
X509_free(xs);
}
static void
http_cgi_ssl_env (connection *con)
{
const char *s;
const SSL_CIPHER *cipher;
if (!con->ssl) return;
s = SSL_get_version(con->ssl);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_PROTOCOL"),
s, strlen(s));
if ((cipher = SSL_get_current_cipher(con->ssl))) {
int usekeysize, algkeysize;
char buf[LI_ITOSTRING_LENGTH];
s = SSL_CIPHER_get_name(cipher);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CIPHER"),
s, strlen(s));
usekeysize = SSL_CIPHER_get_bits(cipher, &algkeysize);
li_itostrn(buf, sizeof(buf), usekeysize);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CIPHER_USEKEYSIZE"),
buf, strlen(buf));
li_itostrn(buf, sizeof(buf), algkeysize);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CIPHER_ALGKEYSIZE"),
buf, strlen(buf));
}
}
CONNECTION_FUNC(mod_openssl_handle_request_env)
{
plugin_data *p = p_d;
@ -149,7 +710,11 @@ CONNECTION_FUNC(mod_openssl_handle_request_env)
if (hctx->request_env_patched) return HANDLER_GO_ON;
hctx->request_env_patched = 1;
UNUSED(srv);
http_cgi_ssl_env(con);
if (con->conf.ssl_verifyclient) {
https_add_ssl_client_entries(srv, con);
}
return HANDLER_GO_ON;
}

4
src/network_backends.h

@ -75,10 +75,6 @@ int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkq
int network_write_chunkqueue_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes); /* fallback to write */
#endif
#if defined(USE_OPENSSL)
int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes);
#endif
/* write next chunk(s); finished chunks are removed afterwards after successful writes.
* return values: similar as backends (0 succes, -1 error, -2 remote close, -3 try again later (EINTR/EAGAIN)) */
/* next chunk must be MEM_CHUNK. use write()/send() */

188
src/network_openssl.c

@ -1,188 +0,0 @@
#include "first.h"
#include "network_backends.h"
#if defined(USE_OPENSSL)
#include "network.h"
#include "log.h"
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
# include <openssl/ssl.h>
# include <openssl/err.h>
static int load_next_chunk(server *srv, connection *con, chunkqueue *cq, off_t max_bytes, const char **data, size_t *data_len) {
chunk * const c = cq->first;
#define LOCAL_SEND_BUFSIZE (64 * 1024)
/* this is a 64k sendbuffer
*
* it has to stay at the same location all the time to satisfy the needs
* of SSL_write to pass the SAME parameter in case of a _WANT_WRITE
*
* the buffer is allocated once, is NOT realloced and is NOT freed at shutdown
* -> we expect a 64k block to 'leak' in valgrind
* */
static char *local_send_buffer = NULL;
UNUSED(con);
force_assert(NULL != c);
switch (c->type) {
case MEM_CHUNK:
{
size_t have;
force_assert(c->offset >= 0 && c->offset <= (off_t)buffer_string_length(c->mem));
have = buffer_string_length(c->mem) - c->offset;
if ((off_t) have > max_bytes) have = max_bytes;
*data = c->mem->ptr + c->offset;
*data_len = have;
}
return 0;
case FILE_CHUNK:
if (NULL == local_send_buffer) {
local_send_buffer = malloc(LOCAL_SEND_BUFSIZE);
force_assert(NULL != local_send_buffer);
}
if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1;
{
off_t offset, toSend;
force_assert(c->offset >= 0 && c->offset <= c->file.length);
offset = c->file.start + c->offset;
toSend = c->file.length - c->offset;
if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE;
if (toSend > max_bytes) toSend = max_bytes;
if (-1 == lseek(c->file.fd, offset, SEEK_SET)) {
log_error_write(srv, __FILE__, __LINE__, "ss", "lseek: ", strerror(errno));
return -1;
}
if (-1 == (toSend = read(c->file.fd, local_send_buffer, toSend))) {
log_error_write(srv, __FILE__, __LINE__, "ss", "read: ", strerror(errno));
return -1;
}
*data = local_send_buffer;
*data_len = toSend;
}
return 0;
}
return -1;
}
int network_write_chunkqueue_openssl(server *srv, connection *con, SSL *ssl, chunkqueue *cq, off_t max_bytes) {
/* the remote side closed the connection before without shutdown request
* - IE
* - wget
* if keep-alive is disabled */
if (con->keep_alive == 0) {
SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
}
chunkqueue_remove_finished_chunks(cq);
while (max_bytes > 0 && NULL != cq->first) {
const char *data;
size_t data_len;
int r;
if (0 != load_next_chunk(srv, con, cq, max_bytes, &data, &data_len)) return -1;
/**
* SSL_write man-page
*
* WARNING
* When an SSL_write() operation has to be repeated because of
* SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be
* repeated with the same arguments.
*/
ERR_clear_error();
r = SSL_write(ssl, data, data_len);
if (con->renegotiations > 1 && con->conf.ssl_disable_client_renegotiation) {
log_error_write(srv, __FILE__, __LINE__, "s", "SSL: renegotiation initiated by client, killing connection");
return -1;
}
if (r <= 0) {
int ssl_r;
unsigned long err;
switch ((ssl_r = SSL_get_error(ssl, r))) {
case SSL_ERROR_WANT_READ:
con->is_readable = -1;
return 0; /* try again later */
case SSL_ERROR_WANT_WRITE:
con->is_writable = -1;
return 0; /* try again later */
case SSL_ERROR_SYSCALL:
/* perhaps we have error waiting in our error-queue */
if (0 != (err = ERR_get_error())) {
do {
log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:",
ssl_r, r,
ERR_error_string(err, NULL));
} while((err = ERR_get_error()));
} else if (r == -1) {
/* no, but we have errno */
switch(errno) {
case EPIPE:
case ECONNRESET:
return -2;
default:
log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:",
ssl_r, r, errno,
strerror(errno));
break;
}
} else {
/* neither error-queue nor errno ? */
log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL (error):",
ssl_r, r, errno,
strerror(errno));
}
break;
case SSL_ERROR_ZERO_RETURN:
/* clean shutdown on the remote side */
if (r == 0) return -2;
/* fall through */
default:
while((err = ERR_get_error())) {
log_error_write(srv, __FILE__, __LINE__, "sdds", "SSL:",
ssl_r, r,
ERR_error_string(err, NULL));
}
break;
}
return -1;
}
chunkqueue_mark_written(cq, r);
max_bytes -= r;
if ((size_t) r < data_len) break; /* try again later */
}
return 0;
}
#endif /* USE_OPENSSL */

102
src/response.c

@ -128,102 +128,6 @@ int http_response_write_header(server *srv, connection *con) {
return 0;
}
#ifdef USE_OPENSSL
#include <openssl/bn.h>
#include <openssl/err.h>
static void https_add_ssl_client_entries(server *srv, connection *con) {
X509 *xs;
X509_NAME *xn;
int i, nentries;
long vr = SSL_get_verify_result(con->ssl);
if (vr != X509_V_OK) {
char errstr[256];
ERR_error_string_n(vr, errstr, sizeof(errstr));
buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("FAILED:"));
buffer_append_string(srv->tmp_buf, errstr);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CLIENT_VERIFY"),
CONST_BUF_LEN(srv->tmp_buf));
return;
} else if (!(xs = SSL_get_peer_certificate(con->ssl))) {
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CLIENT_VERIFY"),
CONST_STR_LEN("NONE"));
return;
} else {
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CLIENT_VERIFY"),
CONST_STR_LEN("SUCCESS"));
}
buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("SSL_CLIENT_S_DN_"));
xn = X509_get_subject_name(xs);
for (i = 0, nentries = X509_NAME_entry_count(xn); i < nentries; ++i) {
int xobjnid;
const char * xobjsn;
X509_NAME_ENTRY *xe;
if (!(xe = X509_NAME_get_entry(xn, i))) {
continue;
}
xobjnid = OBJ_obj2nid((ASN1_OBJECT*)X509_NAME_ENTRY_get_object(xe));
xobjsn = OBJ_nid2sn(xobjnid);
if (xobjsn) {
buffer_string_set_length(srv->tmp_buf, sizeof("SSL_CLIENT_S_DN_")-1);
buffer_append_string(srv->tmp_buf, xobjsn);
array_set_key_value(con->environment,
CONST_BUF_LEN(srv->tmp_buf),
(const char *)X509_NAME_ENTRY_get_data(xe)->data,
X509_NAME_ENTRY_get_data(xe)->length);
}
}
{
ASN1_INTEGER *xsn = X509_get_serialNumber(xs);
BIGNUM *serialBN = ASN1_INTEGER_to_BN(xsn, NULL);
char *serialHex = BN_bn2hex(serialBN);
array_set_key_value(con->environment,
CONST_STR_LEN("SSL_CLIENT_M_SERIAL"),
serialHex, strlen(serialHex));
OPENSSL_free(serialHex);
BN_free(serialBN);
}
if (!buffer_string_is_empty(con->conf.ssl_verifyclient_username)) {
/* pick one of the exported values as "REMOTE_USER", for example
* ssl.verifyclient.username = "SSL_CLIENT_S_DN_UID" or "SSL_CLIENT_S_DN_emailAddress"
*/
data_string *ds = (data_string *)array_get_element(con->environment, con->conf.ssl_verifyclient_username->ptr);
if (ds) array_set_key_value(con->environment, CONST_STR_LEN("REMOTE_USER"), CONST_BUF_LEN(ds->value));
}
if (con->conf.ssl_verifyclient_export_cert) {
BIO *bio;
if (NULL != (bio = BIO_new(BIO_s_mem()))) {
data_string *envds;
int n;
PEM_write_bio_X509(bio, xs);
n = BIO_pending(bio);
if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
envds = data_string_init();
}
buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_CERT"));
buffer_string_prepare_copy(envds->value, n);
BIO_read(bio, envds->value->ptr, n);
BIO_free(bio);
buffer_commit(envds->value, n);
array_replace(con->environment, (data_unset *)envds);
}
}
X509_free(xs);
}
#endif
handler_t http_response_prepare(server *srv, connection *con) {
handler_t r;
@ -335,12 +239,6 @@ handler_t http_response_prepare(server *srv, connection *con) {
con->conditional_is_valid[COMP_HTTP_QUERY_STRING] = 1; /* HTTPqs */
config_patch_connection(srv, con);
#ifdef USE_OPENSSL
if (con->srv_socket->is_ssl && con->conf.ssl_verifyclient) {
https_add_ssl_client_entries(srv, con);
}
#endif
/* do we have to downgrade to 1.0 ? */
if (!con->conf.allow_http11) {
con->request.http_version = HTTP_VERSION_1_0;

3
src/response.h

@ -22,9 +22,6 @@ typedef struct http_cgi_opts_t {
typedef int (*http_cgi_header_append_cb)(void *vdata, const char *k, size_t klen, const char *v, size_t vlen);
int http_cgi_headers(server *srv, connection *con, http_cgi_opts *opts, http_cgi_header_append_cb cb, void *vdata);
#ifdef USE_OPENSSL
void http_cgi_ssl_env(server *srv, connection *con);
#endif
handler_t http_response_prepare(server *srv, connection *con);
int http_response_redirect_to_directory(server *srv, connection *con);

34
src/server.c

@ -59,10 +59,6 @@
# include <sys/prctl.h>
#endif
#ifdef USE_OPENSSL
# include <openssl/err.h>
#endif
#ifndef __sgi
/* IRIX doesn't like the alarm based time() optimization */
/* #define USE_ALARM */
@ -337,22 +333,10 @@ static void server_free(server *srv) {
buffer_free(s->document_root);
buffer_free(s->server_name);
buffer_free(s->server_tag);
buffer_free(s->ssl_pemfile);
buffer_free(s->ssl_ca_file);
buffer_free(s->ssl_cipher_list);
buffer_free(s->ssl_dh_file);
buffer_free(s->ssl_ec_curve);
buffer_free(s->error_handler);
buffer_free(s->error_handler_404);
buffer_free(s->errorfile_prefix);
array_free(s->mimetypes);
buffer_free(s->ssl_verifyclient_username);
#ifdef USE_OPENSSL
SSL_CTX_free(s->ssl_ctx);
EVP_PKEY_free(s->ssl_pemfile_pkey);
X509_free(s->ssl_pemfile_x509);
if (NULL != s->ssl_ca_file_cert_names) sk_X509_NAME_pop_free(s->ssl_ca_file_cert_names, X509_NAME_free);
#endif
free(s);
}
free(srv->config_storage);