[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 parsingpersonal/stbuehler/mod-csrf
parent
4364a4e029
commit
bdbea2aea8
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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() */
|
||||
|
|
|
@ -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
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
|
||||