Hi List,
attached you can find patch that does following:
1) removes ssl_mutex
2) correct SSL_[read|write] operations
3) removes SSL_connect and SSL_accept because these handled by openssl
library trasparent while SSL_[read|write] operations.
I am not a openssl guru and this patch was made with help from examples and
reading of man pages. Also this patch was stress-tested and works as
expected.
Please stress-test this patch on your test boxes...
If no objections there, I will commit this patch next week then.
--
Best regards / Mit besten Gr��en aus D�sseldorf
Dipl.-Ing.
Alexander Malysh
___________________________________________
Centrium GmbH
Vogelsanger Weg 80
40470 D�sseldorf
Fon: +49 (0211) 74 84 51 80
Fax: +49 (0211) 277 49 109
email: [EMAIL PROTECTED]
web: www.centrium.de
msn: [EMAIL PROTECTED]
icq: 98063111
___________________________________________
Please avoid sending me Word, Excel or PowerPoint attachments.
See http://www.fsf.org/philosophy/no-word-attachments.html
Index: gwlib/conn.c
===================================================================
RCS file: /home/cvs/gateway/gwlib/conn.c,v
retrieving revision 1.67
diff -a -u -r1.67 conn.c
--- gwlib/conn.c 15 Nov 2003 13:14:23 -0000 1.67
+++ gwlib/conn.c 26 Nov 2003 17:02:21 -0000
@@ -85,10 +85,8 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
-SSL_CTX *global_ssl_context = NULL;
-SSL_CTX *global_server_ssl_context = NULL;
-X509 *ssl_public_cert;
-RSA *ssl_private_key;
+static SSL_CTX *global_ssl_context = NULL;
+static SSL_CTX *global_server_ssl_context = NULL;
#endif /* HAVE_LIBSSL */
typedef unsigned long (*CRYPTO_CALLBACK_PTR)(void);
@@ -118,8 +116,8 @@
/* fd value is read-only and is not locked */
int fd;
- /* socket state */
- enum {yes,no} connected;
+ /* socket state */
+ enum {yes,no} connected;
/* Protected by outlock */
Octstr *outbuf;
@@ -149,7 +147,6 @@
#ifdef HAVE_LIBSSL
SSL *ssl;
X509 *peer_certificate;
- Mutex *ssl_mutex;
#endif /* HAVE_LIBSSL */
};
@@ -238,33 +235,26 @@
* of bytes written, or -1 in case of error. */
static long unlocked_write(Connection *conn)
{
- long ret;
+ long ret = 0;
#ifdef HAVE_LIBSSL
if (conn->ssl != NULL) {
- mutex_lock(conn->ssl_mutex);
- ret = SSL_write(conn->ssl,
- octstr_get_cstr(conn->outbuf) + conn->outbufpos,
+ if (octstr_len(conn->outbuf) - conn->outbufpos > 0)
+ ret = SSL_write(conn->ssl,
+ octstr_get_cstr(conn->outbuf) + conn->outbufpos,
octstr_len(conn->outbuf) - conn->outbufpos);
if (ret < 0) {
- int SSL_error = SSL_get_error(conn->ssl, ret);
+ int SSL_error = SSL_get_error(conn->ssl, ret);
- if (SSL_error == SSL_ERROR_WANT_READ) {
- SSL_read(conn->ssl, NULL, 0);
- mutex_unlock(conn->ssl_mutex);
- return 0;
- } else if (SSL_error == SSL_ERROR_WANT_WRITE) {
- mutex_unlock(conn->ssl_mutex);
- return 0;
+ if (SSL_error == SSL_ERROR_WANT_READ || SSL_error == SSL_ERROR_WANT_WRITE) {
+ ret = 0; /* no error */
} else {
- error(0, "SSL write failed: OpenSSL error %d: %s",
+ error(errno, "SSL write failed: OpenSSL error %d: %s",
SSL_error, ERR_error_string(SSL_error, NULL));
- mutex_unlock(conn->ssl_mutex);
return -1;
}
}
- mutex_unlock(conn->ssl_mutex);
} else
#endif /* HAVE_LIBSSL */
ret = octstr_write_data(conn->outbuf, conn->fd, conn->outbufpos);
@@ -323,24 +313,7 @@
#ifdef HAVE_LIBSSL
if (conn->ssl != NULL) {
- mutex_lock(conn->ssl_mutex);
len = SSL_read(conn->ssl, buf, sizeof(buf));
-
- if (len < 0) {
- int SSL_error = SSL_get_error(conn->ssl, len);
-
- if (SSL_error == SSL_ERROR_WANT_WRITE) {
- len = SSL_write(conn->ssl, NULL, 0);
- mutex_unlock(conn->ssl_mutex);
- return;
- } else if (SSL_error == SSL_ERROR_WANT_READ) {
- mutex_unlock(conn->ssl_mutex);
- return;
- } else
- error(0, "SSL read failed: OpenSSL error %d: %s",
- SSL_error, ERR_error_string(SSL_error, NULL));
- }
- mutex_unlock(conn->ssl_mutex);
} else
#endif /* HAVE_LIBSSL */
len = read(conn->fd, buf, sizeof(buf));
@@ -348,7 +321,17 @@
if (len < 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
return;
- error(errno, "Error reading from fd %d:", conn->fd);
+#ifdef HAVE_LIBSSL
+ if (conn->ssl) {
+ int SSL_error = SSL_get_error(conn->ssl, len);
+ if (SSL_error == SSL_ERROR_WANT_WRITE || SSL_error == SSL_ERROR_WANT_READ)
+ return; /* no error */
+ error(errno, "SSL read failed: OpenSSL error %d: %s",
+ SSL_error, ERR_error_string(SSL_error, NULL));
+ }
+ else
+#endif /* HAVE_LIBSSL */
+ error(errno, "Error reading from fd %d:", conn->fd);
conn->read_error = 1;
if (conn->registered)
unlocked_register_pollin(conn, 0);
@@ -420,9 +403,6 @@
Octstr *our_host)
{
Connection *ret;
- int SSL_ret = 0;
- int connected = 0;
- time_t timeout;
/* open the TCP connection */
if (!(ret = conn_open_tcp(host, port, our_host))) {
@@ -430,13 +410,18 @@
}
ret->ssl = SSL_new(global_ssl_context);
- ret->ssl_mutex = mutex_create();
-
- SSL_set_connect_state(ret->ssl);
+
+ /*
+ * The current thread's error queue must be empty before
+ * the TLS/SSL I/O operation is attempted, or SSL_get_error()
+ * will not work reliably.
+ */
+ ERR_clear_error();
+
if (certkeyfile != NULL) {
SSL_use_certificate_file(ret->ssl, octstr_get_cstr(certkeyfile),
SSL_FILETYPE_PEM);
- SSL_use_PrivateKey_file(ret->ssl, octstr_get_cstr(certkeyfile),
+ SSL_use_PrivateKey_file(ret->ssl, octstr_get_cstr(certkeyfile),
SSL_FILETYPE_PEM);
if (SSL_check_private_key(ret->ssl) != 1) {
error(0, "conn_open_ssl: private key isn't consistent with the "
@@ -445,15 +430,13 @@
goto error;
}
}
-
- SSL_set_fd(ret->ssl, ret->fd);
- /*
- * The current thread's error queue must be empty before
- * the TLS/SSL I/O operation is attempted, or SSL_get_error()
- * will not work reliably.
- */
- ERR_clear_error();
+ /* SSL_set_fd can fail, so check it */
+ if (SSL_set_fd(ret->ssl, ret->fd) == 0) {
+ /* SSL_set_fd failed, log error */
+ error(errno, "SSL: OpenSSL: %.256s", ERR_error_string(ERR_get_error(), NULL));
+ goto error;
+ }
/*
* make sure the socket is non-blocking while we do SSL_connect
@@ -464,51 +447,10 @@
BIO_set_nbio(SSL_get_rbio(ret->ssl), 1);
BIO_set_nbio(SSL_get_wbio(ret->ssl), 1);
- /* record current time */
- timeout = time(NULL);
-
- while(!connected && (timeout + SSL_CONN_TIMEOUT > time(NULL))) {
- /* Attempt to connect as long as the timeout hasn't run down */
- SSL_ret = SSL_connect(ret->ssl);
- switch(SSL_get_error(ret->ssl,SSL_ret)) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- /* non-blocking socket wants more time to read or write */
- gwthread_sleep(0.01F);
- continue;
- default:
- /* we're connected to the server successfuly */
- connected++;
- }
- }
-
- if (!connected) {
- /* connection timed out - this probably means that something is terrible wrong */
- int SSL_error = SSL_get_error (ret->ssl, SSL_ret);
- error(0,"SSL connection timeout: OpenSSL error %d: %s",
- SSL_error, ERR_error_string(SSL_error, NULL));
- goto error;
- }
-
- /*
- * XXX - restore the non-blocking state
- * we don't need this since we use non-blocking operations
- * anyway before doing SSL_connect(), right?!
- */
- /*
- if (socket_set_blocking(ret->fd, 0) < 0) {
- goto error;
- }
- */
+ SSL_set_connect_state(ret->ssl);
- if (SSL_ret != 1) {
- int SSL_error = SSL_get_error (ret->ssl, SSL_ret);
- error(0, "SSL connect failed: OpenSSL error %d: %s",
- SSL_error, ERR_error_string(SSL_error, NULL));
- goto error;
- }
-
return ret;
+
error:
conn_destroy(ret);
return NULL;
@@ -532,7 +474,7 @@
int sockfd;
int done = -1;
Connection *c;
-
+
sockfd = tcpip_connect_nb_to_server_with_port(octstr_get_cstr(host), port,
our_port, our_host == NULL ?
NULL : octstr_get_cstr(our_host), &done);
@@ -545,13 +487,13 @@
return c;
}
-int conn_is_connected(Connection *conn)
+int conn_is_connected(Connection *conn)
{
if (conn->connected == yes) return 0;
return -1;
}
-int conn_get_connect_result(Connection *conn)
+int conn_get_connect_result(Connection *conn)
{
int err,len;
len = sizeof(len);
@@ -614,16 +556,13 @@
* do all the SSL magic for this connection
*/
if (ssl) {
- int rc;
-
conn->ssl = SSL_new(global_server_ssl_context);
- conn->ssl_mutex = mutex_create();
conn->peer_certificate = NULL;
/* SSL_set_fd can fail, so check it */
if (SSL_set_fd(conn->ssl, conn->fd) == 0) {
/* SSL_set_fd failed, log error and return NULL */
- error(0, "SSL: OpenSSL: %.256s", ERR_error_string(ERR_get_error(), NULL));
+ error(errno, "SSL: OpenSSL: %.256s", ERR_error_string(ERR_get_error(), NULL));
conn_destroy(conn);
return NULL;
}
@@ -633,101 +572,11 @@
BIO_set_nbio(SSL_get_rbio(conn->ssl), 1);
BIO_set_nbio(SSL_get_wbio(conn->ssl), 1);
- /*
- * now enter the SSL handshake phase
- */
-
- /*
- * For non-blocking BIO we may return from SSL_accept(). In this
- * case we check for SSL_get_error() = SSL_ERROR_WANT_[READ|WRITE]
- * and loop the SSL_accept() until we have come through.
- */
- while (((rc = SSL_accept(conn->ssl)) <= 0) &&
- ((SSL_get_error(conn->ssl, rc) == SSL_ERROR_WANT_READ) ||
- (SSL_get_error(conn->ssl, rc) == SSL_ERROR_WANT_WRITE))) {
- /* busy waiting */
- gwthread_sleep(0.02);
- }
-
- /*
- * If SSL_accept() has failed then check which reason it may
- * have been and log the error.
- */
- if (rc <= 0) {
-
- if (SSL_get_error(conn->ssl, rc) == SSL_ERROR_ZERO_RETURN) {
- /*
- * The case where the connection was closed before any data
- * was transferred. That's not a real error and can occur
- * sporadically with some clients.
- */
- error(0, "SSL: handshake stopped: connection was closed");
- error(0, "SSL: OpenSSL: %.256s", ERR_error_string(ERR_get_error(), NULL));
-
- SSL_set_shutdown(conn->ssl, SSL_RECEIVED_SHUTDOWN);
- SSL_smart_shutdown(conn->ssl);
- }
- else if (ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) {
- /*
- * The case where OpenSSL has recognized a HTTP request:
- * This means the client speaks plain HTTP on our HTTPS
- * port. Hmmmm... At least for this error we can be more friendly
- * and try to provide him with a HTML error page. We have only one
- * problem: OpenSSL has already read some bytes from the HTTP
- * request. So we have to skip the request line manually.
- */
- char ca[2];
- int rv;
-
- error(0, "SSL: handshake failed: HTTP spoken on HTTPS port");
- error(0, "SSL: OpenSSL: %.256s", ERR_error_string(ERR_get_error(), NULL));
-
- /* first: skip the remaining bytes of the request line */
- do {
- do {
- rv = read(conn->fd, ca, 1);
- } while (rv == -1 && errno == EINTR);
- } while (rv > 0 && ca[0] != '\012' /*LF*/);
-
- /* second: kick away the SSL stuff */
- SSL_set_shutdown(conn->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
- SSL_smart_shutdown(conn->ssl);
-
- /* tell the user how to access using the HTTPS scheme */
- /* SSL_http_hint(conn, HTTP_BAD_REQUEST); */
- }
- else if (SSL_get_error(conn->ssl, rc) == SSL_ERROR_SYSCALL) {
- if (errno > 0)
- warning(0, "SSL: handshake interrupted by system (stop button pressed in browser?!)");
- else
- warning(0, "SSL: spurious handshake interrupt (one of OpenSSL confusions?!)");
- error(0, "SSL: OpenSSL: %.256s", ERR_error_string(ERR_get_error(), NULL));
-
- SSL_set_shutdown(conn->ssl, SSL_RECEIVED_SHUTDOWN);
- SSL_smart_shutdown(conn->ssl);
- }
- else {
- /*
- * ok, anything else is a fatal error
- */
- error(0, "SSL: handshake failed with fatal error");
- error(0, "SSL: OpenSSL: %.256s", ERR_error_string(ERR_get_error(), NULL));
-
- SSL_set_shutdown(conn->ssl, SSL_RECEIVED_SHUTDOWN);
- SSL_smart_shutdown(conn->ssl);
- }
-
- warning(0, "SSL: disconnecting.");
-
- conn_destroy(conn);
- return NULL;
-
- } /* SSL error */
-
+ /* set accept state , SSL-Handshake will be handled transparent while SSL_[read|write] */
+ SSL_set_accept_state(conn->ssl);
} else {
conn->ssl = NULL;
conn->peer_certificate = NULL;
- conn->ssl_mutex = NULL;
}
#endif /* HAVE_LIBSSL */
@@ -748,23 +597,17 @@
fdset_unregister(conn->registered, conn->fd);
if (conn->fd >= 0) {
- /* Try to flush any remaining data, only in case this is
- * not a SSL connection. Otherwise this crashes the bearerbox
- * at least on the Cygwin platform
- */
+ /* Try to flush any remaining data */
+ unlocked_try_write(conn);
+
#ifdef HAVE_LIBSSL
if (conn->ssl != NULL) {
- mutex_lock(conn->ssl_mutex);
- SSL_shutdown(conn->ssl);
+ SSL_smart_shutdown(conn->ssl);
SSL_free(conn->ssl);
if (conn->peer_certificate != NULL)
X509_free(conn->peer_certificate);
- mutex_unlock(conn->ssl_mutex);
- mutex_destroy(conn->ssl_mutex);
}
- else
#endif /* HAVE_LIBSSL */
- unlocked_write(conn);
ret = close(conn->fd);
if (ret < 0)
@@ -1308,16 +1151,19 @@
#ifdef HAVE_LIBSSL
X509 *conn_get_peer_certificate(Connection *conn)
{
- mutex_lock(conn->ssl_mutex);
- if (conn->peer_certificate == NULL && conn->ssl != NULL)
+ /* Don't know if it needed to be locked , but better safe as crash */
+ lock_in(conn);
+ lock_out(conn);
+ if (conn->peer_certificate == NULL && conn->ssl != NULL)
conn->peer_certificate = SSL_get_peer_certificate(conn->ssl);
- mutex_unlock(conn->ssl_mutex);
- return(conn->peer_certificate);
+ unlock_in(conn);
+ unlock_out(conn);
+ return conn->peer_certificate;
}
-RSA *tmp_rsa_callback(SSL *ssl, int export, int key_len)
+static RSA *tmp_rsa_callback(SSL *ssl, int export, int key_len)
{
- static RSA *rsa = NULL;
+ static RSA *rsa = NULL;
debug("gwlib.http", 0, "SSL: Generating new RSA key (export=%d, keylen=%d)", export, key_len);
if (export) {
rsa = RSA_generate_key(key_len, RSA_F4, NULL, NULL);
@@ -1327,10 +1173,10 @@
return rsa;
}
-Mutex **ssl_static_locks = NULL;
+static Mutex **ssl_static_locks = NULL;
/* the call-back function for the openssl crypto thread locking */
-void openssl_locking_function(int mode, int n, const char *file, int line)
+static void openssl_locking_function(int mode, int n, const char *file, int line)
{
if (mode & CRYPTO_LOCK)
mutex_lock(ssl_static_locks[n-1]);
@@ -1345,7 +1191,7 @@
gw_assert(ssl_static_locks == NULL);
ssl_static_locks = gw_malloc(sizeof(Mutex *) * maxlocks);
- for (c = 0; c < maxlocks; c++)
+ for (c = 0; c < maxlocks; c++)
ssl_static_locks[c] = mutex_create();
/* after the mutexes have been created, apply the call-back to it */
@@ -1371,7 +1217,7 @@
{
SSL_library_init();
SSL_load_error_strings();
- global_ssl_context = SSL_CTX_new(SSLv23_method());
+ global_ssl_context = SSL_CTX_new(SSLv23_client_method());
SSL_CTX_set_mode(global_ssl_context,
SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
}
Index: gwlib/conn.h
===================================================================
RCS file: /home/cvs/gateway/gwlib/conn.h,v
retrieving revision 1.26
diff -a -u -r1.26 conn.h
--- gwlib/conn.h 15 Nov 2003 13:14:23 -0000 1.26
+++ gwlib/conn.h 26 Nov 2003 17:02:21 -0000
@@ -278,11 +278,6 @@
*/
X509 *get_peer_certificate(Connection *conn);
-/* Sets OpenSSL locking for callback within conn.c (client SSL) and
- * http.c (server SSL).
- */
-void openssl_locking_function(int mode, int n, const char *file, int line);
-
/* These are called to initialize and shutdown the OpenSSL mutex locks.
* They should be called before the _init_ssl, _shutdown_ssl functions.
*/
@@ -325,8 +320,6 @@
X509 *conn_get_peer_certificate(Connection *conn);
-RSA *tmp_rsa_callback(SSL *ssl, int export, int key_len);
-void openssl_server_locking_function(int mode, int n, const char *file, int line);
#endif /* HAVE_LIBSSL */
int conn_get_id(Connection *conn);