+/*
+ Bacula® - The Network Backup Solution
+
+ Copyright (C) 2005-2010 Free Software Foundation Europe e.V.
+
+ The main author of Bacula is Kern Sibbald, with contributions from
+ many others, a complete list can be found in the file AUTHORS.
+ This program is Free Software; you can redistribute it and/or
+ modify it under the terms of version three of the GNU Affero General Public
+ License as published by the Free Software Foundation and included
+ in the file LICENSE.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+ Bacula® is a registered trademark of Kern Sibbald.
+ The licensor of Bacula is the Free Software Foundation Europe
+ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+ Switzerland, email:ftf@fsfeurope.org.
+*/
/*
* tls.c TLS support functions
*
* Author: Landon Fuller <landonf@threerings.net>
*
- * Version $Id$
- *
- * Copyright (C) 2005 Kern Sibbald
- *
* This file was contributed to the Bacula project by Landon Fuller
* and Three Rings Design, Inc.
*
* under an alternate open source license please contact
* Landon Fuller <landonf@threerings.net>.
*/
-/*
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307, USA.
- */
#include "bacula.h"
#include <assert.h>
-extern time_t watchdog_time;
#ifdef HAVE_TLS /* Is TLS enabled? */
/* No anonymous ciphers, no <128 bit ciphers, no export ciphers, no MD5 ciphers */
#define TLS_DEFAULT_CIPHERS "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"
-/* Array of mutexes for use with OpenSSL static locking */
-static pthread_mutex_t *mutexes;
-
-/* OpenSSL dynamic locking structure */
-struct CRYPTO_dynlock_value {
- pthread_mutex_t mutex;
-};
-
-/* Are we initialized? */
-static int tls_initialized = false;
-
/* TLS Context Structure */
struct TLS_Context {
SSL_CTX *openssl;
- TLS_PEM_PASSWD_CB *pem_callback;
+ CRYPTO_PEM_PASSWD_CB *pem_callback;
const void *pem_userdata;
+ bool tls_enable;
+ bool tls_require;
};
struct TLS_Connection {
SSL *openssl;
};
-/* post all per-thread openssl errors */
-static void openssl_post_errors(int code, const char *errstring)
-{
- char buf[512];
- unsigned long sslerr;
-
- /* Pop errors off of the per-thread queue */
- while((sslerr = ERR_get_error()) != 0) {
- /* Acquire the human readable string */
- ERR_error_string_n(sslerr, (char *) &buf, sizeof(buf));
- Emsg2(M_ERROR, 0, "%s: ERR=%s\n", errstring, buf);
- }
-}
-
/*
* OpenSSL certificate verification callback.
* OpenSSL has already performed internal certificate verification.
*/
static int openssl_verify_peer(int ok, X509_STORE_CTX *store)
{
-
if (!ok) {
X509 *cert = X509_STORE_CTX_get_current_cert(store);
int depth = X509_STORE_CTX_get_error_depth(store);
X509_NAME_oneline(X509_get_issuer_name(cert), issuer, 256);
X509_NAME_oneline(X509_get_subject_name(cert), subject, 256);
- Emsg5(M_ERROR, 0, _("Error with certificate at depth: %d, issuer = %s,"
- " subject = %s, ERR=%d:%s\n"), depth, issuer,
- subject, err, X509_verify_cert_error_string(err));
+ Jmsg5(NULL, M_ERROR, 0, _("Error with certificate at depth: %d, issuer = %s,"
+ " subject = %s, ERR=%d:%s\n"), depth, issuer,
+ subject, err, X509_verify_cert_error_string(err));
}
return ok;
}
-/*
- * Default PEM encryption passphrase callback.
- * Returns an empty password.
- */
-static int tls_default_pem_callback(char *buf, int size, const void *userdata)
-{
- bstrncpy(buf, "", size);
- return (strlen(buf));
-}
-
/* Dispatch user PEM encryption callbacks */
-static int openssl_pem_callback_dispatch (char *buf, int size, int rwflag, void *userdata)
+static int tls_pem_callback_dispatch (char *buf, int size, int rwflag, void *userdata)
{
- TLS_CONTEXT *ctx = (TLS_CONTEXT *) userdata;
+ TLS_CONTEXT *ctx = (TLS_CONTEXT *)userdata;
return (ctx->pem_callback(buf, size, ctx->pem_userdata));
}
*/
TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir,
const char *certfile, const char *keyfile,
- TLS_PEM_PASSWD_CB *pem_callback,
+ CRYPTO_PEM_PASSWD_CB *pem_callback,
const void *pem_userdata, const char *dhfile,
bool verify_peer)
{
BIO *bio;
DH *dh;
- ctx = (TLS_CONTEXT *) malloc(sizeof(TLS_CONTEXT));
+ ctx = (TLS_CONTEXT *)malloc(sizeof(TLS_CONTEXT));
/* Allocate our OpenSSL TLSv1 Context */
ctx->openssl = SSL_CTX_new(TLSv1_method());
if (!ctx->openssl) {
- openssl_post_errors(M_ERROR, _("Error initializing SSL context"));
+ openssl_post_errors(M_FATAL, _("Error initializing SSL context"));
goto err;
}
ctx->pem_callback = pem_callback;
ctx->pem_userdata = pem_userdata;
} else {
- ctx->pem_callback = tls_default_pem_callback;
+ ctx->pem_callback = crypto_default_pem_callback;
ctx->pem_userdata = NULL;
}
- SSL_CTX_set_default_passwd_cb(ctx->openssl, openssl_pem_callback_dispatch);
+ SSL_CTX_set_default_passwd_cb(ctx->openssl, tls_pem_callback_dispatch);
SSL_CTX_set_default_passwd_cb_userdata(ctx->openssl, (void *) ctx);
/*
*/
if (ca_certfile || ca_certdir) {
if (!SSL_CTX_load_verify_locations(ctx->openssl, ca_certfile, ca_certdir)) {
- openssl_post_errors(M_ERROR, _("Error loading certificate verification stores"));
+ openssl_post_errors(M_FATAL, _("Error loading certificate verification stores"));
goto err;
}
} else if (verify_peer) {
/* At least one CA is required for peer verification */
- Emsg0(M_ERROR, 0, _("Either a certificate file or a directory must be"
+ Jmsg0(NULL, M_ERROR, 0, _("Either a certificate file or a directory must be"
" specified as a verification store\n"));
goto err;
}
*/
if (certfile) {
if (!SSL_CTX_use_certificate_chain_file(ctx->openssl, certfile)) {
- openssl_post_errors(M_ERROR, _("Error loading certificate file"));
+ openssl_post_errors(M_FATAL, _("Error loading certificate file"));
goto err;
}
}
/* Load our private key. */
if (keyfile) {
if (!SSL_CTX_use_PrivateKey_file(ctx->openssl, keyfile, SSL_FILETYPE_PEM)) {
- openssl_post_errors(M_ERROR, _("Error loading private key"));
+ openssl_post_errors(M_FATAL, _("Error loading private key"));
goto err;
}
}
/* Load Diffie-Hellman Parameters. */
if (dhfile) {
if (!(bio = BIO_new_file(dhfile, "r"))) {
- openssl_post_errors(M_ERROR, _("Unable to open DH parameters file"));
+ openssl_post_errors(M_FATAL, _("Unable to open DH parameters file"));
goto err;
}
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
if (!dh) {
- openssl_post_errors(M_ERROR, _("Unable to load DH parameters from specified file"));
+ openssl_post_errors(M_FATAL, _("Unable to load DH parameters from specified file"));
goto err;
}
if (!SSL_CTX_set_tmp_dh(ctx->openssl, dh)) {
- openssl_post_errors(M_ERROR, _("Failed to set TLS Diffie-Hellman parameters"));
+ openssl_post_errors(M_FATAL, _("Failed to set TLS Diffie-Hellman parameters"));
DH_free(dh);
goto err;
}
}
if (SSL_CTX_set_cipher_list(ctx->openssl, TLS_DEFAULT_CIPHERS) != 1) {
- Emsg0(M_ERROR, 0, _("Error setting cipher list, no valid ciphers available\n"));
+ Jmsg0(NULL, M_ERROR, 0,
+ _("Error setting cipher list, no valid ciphers available\n"));
goto err;
}
/* Verify Peer Certificate */
if (verify_peer) {
- /* SSL_VERIFY_FAIL_IF_NO_PEER_CERT has no effect in client mode */
- SSL_CTX_set_verify(ctx->openssl,
- SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
- openssl_verify_peer);
+ /* SSL_VERIFY_FAIL_IF_NO_PEER_CERT has no effect in client mode */
+ SSL_CTX_set_verify(ctx->openssl,
+ SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ openssl_verify_peer);
}
return ctx;
free(ctx);
}
+bool get_tls_require(TLS_CONTEXT *ctx)
+{
+ return ctx->tls_require;
+}
+
+bool get_tls_enable(TLS_CONTEXT *ctx)
+{
+ return ctx->tls_enable;
+}
+
+
/*
* Verifies a list of common names against the certificate
* commonName attribute.
* Returns: true on success
* false on failure
*/
-bool tls_postconnect_verify_cn(TLS_CONNECTION *tls, alist *verify_list)
+bool tls_postconnect_verify_cn(JCR *jcr, TLS_CONNECTION *tls, alist *verify_list)
{
SSL *ssl = tls->openssl;
X509 *cert;
X509_NAME *subject;
- int auth_success = false;
+ bool auth_success = false;
char data[256];
/* Check if peer provided a certificate */
if (!(cert = SSL_get_peer_certificate(ssl))) {
- Emsg0(M_ERROR, 0, _("Peer failed to present a TLS certificate\n"));
+ Qmsg0(jcr, M_ERROR, 0, _("Peer failed to present a TLS certificate\n"));
return false;
}
* Returns: true on success
* false on failure
*/
-bool tls_postconnect_verify_host(TLS_CONNECTION *tls, const char *host)
+bool tls_postconnect_verify_host(JCR *jcr, TLS_CONNECTION *tls, const char *host)
{
SSL *ssl = tls->openssl;
X509 *cert;
X509_NAME *subject;
- int auth_success = false;
+ bool auth_success = false;
int extensions;
- char data[256];
int i, j;
+ int cnLastPos = -1;
+ X509_NAME_ENTRY *neCN;
+ ASN1_STRING *asn1CN;
/* Check if peer provided a certificate */
if (!(cert = SSL_get_peer_certificate(ssl))) {
- Emsg1(M_ERROR, 0, _("Peer %s failed to present a TLS certificate\n"), host);
+ Qmsg1(jcr, M_ERROR, 0,
+ _("Peer %s failed to present a TLS certificate\n"), host);
return false;
}
extname = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
if (strcmp(extname, "subjectAltName") == 0) {
+#ifdef HAVE_OPENSSLv1
+ const X509V3_EXT_METHOD *method;
+#else
X509V3_EXT_METHOD *method;
+#endif
STACK_OF(CONF_VALUE) *val;
CONF_VALUE *nval;
- unsigned char *data;
void *extstr = NULL;
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800FL)
+ const unsigned char *ext_value_data;
+#else
+ unsigned char *ext_value_data;
+#endif
/* Get x509 extension method structure */
if (!(method = X509V3_EXT_get(ext))) {
break;
}
- data = ext->value->data;
+ ext_value_data = ext->value->data;
#if (OPENSSL_VERSION_NUMBER > 0x00907000L)
if (method->it) {
/* New style ASN1 */
/* Decode ASN1 item in data */
- extstr = ASN1_item_d2i(NULL, &data, ext->value->length,
+ extstr = ASN1_item_d2i(NULL, &ext_value_data, ext->value->length,
ASN1_ITEM_ptr(method->it));
} else {
/* Old style ASN1 */
/* Decode ASN1 item in data */
- extstr = method->d2i(NULL, &data, ext->value->length);
+ extstr = method->d2i(NULL, &ext_value_data, ext->value->length);
}
#else
- extstr = method->d2i(NULL, &data, ext->value->length);
+ extstr = method->d2i(NULL, &ext_value_data, ext->value->length);
#endif
/* Iterate through to find the dNSName field(s) */
for (j = 0; j < sk_CONF_VALUE_num(val); j++) {
nval = sk_CONF_VALUE_value(val, j);
if (strcmp(nval->name, "DNS") == 0) {
- if (strcasecmp(nval->name, host) == 0) {
+ if (strcasecmp(nval->value, host) == 0) {
auth_success = true;
goto success;
}
/* Try verifying against the subject name */
if (!auth_success) {
if ((subject = X509_get_subject_name(cert)) != NULL) {
- if (X509_NAME_get_text_by_NID(subject, NID_commonName, data, sizeof(data)) > 0) {
- /* NULL terminate data */
- data[255] = 0;
- if (strcasecmp(data, host) == 0) {
+ /* Loop through all CNs */
+ for (;;) {
+ cnLastPos = X509_NAME_get_index_by_NID(subject, NID_commonName, cnLastPos);
+ if (cnLastPos == -1) {
+ break;
+ }
+ neCN = X509_NAME_get_entry(subject, cnLastPos);
+ asn1CN = X509_NAME_ENTRY_get_data(neCN);
+ if (strcasecmp((const char*)asn1CN->data, host) == 0) {
auth_success = true;
+ break;
}
}
}
}
-
success:
X509_free(cert);
* Returns: Pointer to TLS_CONNECTION instance on success
* NULL on failure;
*/
-TLS_CONNECTION *new_tls_connection (TLS_CONTEXT *ctx, int fd)
+TLS_CONNECTION *new_tls_connection(TLS_CONTEXT *ctx, int fd)
{
BIO *bio;
bio = BIO_new(BIO_s_socket());
if (!bio) {
/* Not likely, but never say never */
- openssl_post_errors(M_ERROR, "Error creating file descriptor-based BIO");
+ openssl_post_errors(M_FATAL, _("Error creating file descriptor-based BIO"));
return NULL; /* Nothing allocated, nothing to clean up */
}
BIO_set_fd(bio, fd, BIO_NOCLOSE);
/* Allocate our new tls connection */
- TLS_CONNECTION *tls = (TLS_CONNECTION *) malloc(sizeof(TLS_CONNECTION));
+ TLS_CONNECTION *tls = (TLS_CONNECTION *)malloc(sizeof(TLS_CONNECTION));
/* Create the SSL object and attach the socket BIO */
if ((tls->openssl = SSL_new(ctx->openssl)) == NULL) {
/* Not likely, but never say never */
- openssl_post_errors(M_ERROR, "Error creating new SSL object");
+ openssl_post_errors(M_FATAL, _("Error creating new SSL object"));
goto err;
}
/* Non-blocking partial writes */
SSL_set_mode(tls->openssl, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- return (tls);
+ return tls;
err:
/* Clean up */
BIO_free(bio);
SSL_free(tls->openssl);
free(tls);
-
return NULL;
}
/*
* Free TLS_CONNECTION instance
*/
-void free_tls_connection (TLS_CONNECTION *tls)
+void free_tls_connection(TLS_CONNECTION *tls)
{
SSL_free(tls->openssl);
free(tls);
/* Zero the fdset, we'll set our fd prior to each invocation of select() */
FD_ZERO(&fdset);
- tv.tv_sec = 10;
- tv.tv_usec = 0;
- fdmax = bsock->fd + 1;
+ fdmax = bsock->m_fd + 1;
/* Ensure that socket is non-blocking */
- flags = bnet_set_nonblocking(bsock);
+ flags = bsock->set_nonblocking();
/* start timer */
bsock->timer_start = watchdog_time;
- bsock->timed_out = 0;
+ bsock->clear_timed_out();
+ bsock->set_killable(false);
for (;;) {
if (server) {
/* Handle errors */
switch (SSL_get_error(tls->openssl, err)) {
- case SSL_ERROR_NONE:
- stat = true;
- goto cleanup;
- case SSL_ERROR_ZERO_RETURN:
- /* TLS connection was cleanly shut down */
- openssl_post_errors(M_ERROR, "Connect failure");
- stat = false;
- goto cleanup;
- case SSL_ERROR_WANT_READ:
- /* If we timeout of a select, this will be unset */
- FD_SET((unsigned) bsock->fd, &fdset);
- /* Block until we can read */
- select(fdmax, &fdset, NULL, &fdset, &tv);
- break;
- case SSL_ERROR_WANT_WRITE:
- /* If we timeout of a select, this will be unset */
- FD_SET((unsigned) bsock->fd, &fdset);
- /* Block until we can write */
- select(fdmax, NULL, &fdset, &fdset, &tv);
- break;
- default:
- /* Socket Error Occured */
- openssl_post_errors(M_ERROR, "Connect failure");
- stat = false;
- goto cleanup;
+ case SSL_ERROR_NONE:
+ stat = true;
+ goto cleanup;
+ case SSL_ERROR_ZERO_RETURN:
+ /* TLS connection was cleanly shut down */
+ openssl_post_errors(bsock->get_jcr(), M_FATAL, _("Connect failure"));
+ stat = false;
+ goto cleanup;
+ case SSL_ERROR_WANT_READ:
+ /* If we timeout of a select, this will be unset */
+ FD_SET((unsigned) bsock->m_fd, &fdset);
+ /* Set our timeout */
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ /* Block until we can read */
+ select(fdmax, &fdset, NULL, NULL, &tv);
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ /* If we timeout of a select, this will be unset */
+ FD_SET((unsigned) bsock->m_fd, &fdset);
+ /* Set our timeout */
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ /* Block until we can write */
+ select(fdmax, NULL, &fdset, NULL, &tv);
+ break;
+ default:
+ /* Socket Error Occurred */
+ openssl_post_errors(bsock->get_jcr(), M_FATAL, _("Connect failure"));
+ stat = false;
+ goto cleanup;
}
- if (bsock->timed_out) {
+ if (bsock->is_timed_out()) {
goto cleanup;
}
}
cleanup:
/* Restore saved flags */
- bnet_restore_blocking(bsock, flags);
+ bsock->restore_blocking(flags);
/* Clear timer */
bsock->timer_start = 0;
+ bsock->set_killable(true);
return stat;
}
bool tls_bsock_connect(BSOCK *bsock)
{
/* SSL_connect(bsock->tls) */
- return (openssl_bsock_session_start(bsock, false));
+ return openssl_bsock_session_start(bsock, false);
}
/*
bool tls_bsock_accept(BSOCK *bsock)
{
/* SSL_accept(bsock->tls) */
- return (openssl_bsock_session_start(bsock, true));
+ return openssl_bsock_session_start(bsock, true);
}
/*
* Shutdown TLS_CONNECTION instance
*/
-void tls_bsock_shutdown (BSOCK *bsock)
+void tls_bsock_shutdown(BSOCK *bsock)
{
/*
* SSL_shutdown must be called twice to fully complete the process -
* The first time to initiate the shutdown handshake, and the second to
* receive the peer's reply.
*
- * However, it is valid to close the SSL connection after the initial
- * shutdown notification is sent to the peer, without waiting for the
- * peer's reply, as long as you do not plan to re-use that particular
- * SSL connection object.
- *
- * Because we do not re-use SSL connection objects, I do not bother
- * calling SSL_shutdown a second time.
- *
* In addition, if the underlying socket is blocking, SSL_shutdown()
* will not return until the current stage of the shutdown process has
* completed or an error has occured. By setting the socket blocking
* we can avoid the ugly for()/switch()/select() loop.
*/
int err;
- int flags;
+
+ btimer_t *tid;
/* Set socket blocking for shutdown */
- flags = bnet_set_blocking(bsock);
+ bsock->set_blocking();
+ tid = start_bsock_timer(bsock, 60 * 2);
err = SSL_shutdown(bsock->tls->openssl);
+ stop_bsock_timer(tid);
+ if (err == 0) {
+ /* Complete shutdown */
+ tid = start_bsock_timer(bsock, 60 * 2);
+ err = SSL_shutdown(bsock->tls->openssl);
+ stop_bsock_timer(tid);
+ }
+
switch (SSL_get_error(bsock->tls->openssl, err)) {
- case SSL_ERROR_NONE:
- break;
- case SSL_ERROR_ZERO_RETURN:
- /* TLS connection was shut down on us via a TLS protocol-level closure */
- openssl_post_errors(M_ERROR, _("TLS shutdown failure."));
- break;
- default:
- /* Socket Error Occured */
- openssl_post_errors(M_ERROR, _("TLS shutdown failure."));
- break;
+ case SSL_ERROR_NONE:
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ /* TLS connection was shut down on us via a TLS protocol-level closure */
+ openssl_post_errors(bsock->get_jcr(), M_ERROR, _("TLS shutdown failure."));
+ break;
+ default:
+ /* Socket Error Occurred */
+ openssl_post_errors(bsock->get_jcr(), M_ERROR, _("TLS shutdown failure."));
+ break;
}
-
- /* Restore saved flags */
- bnet_restore_blocking(bsock, flags);
}
/* Does all the manual labor for tls_bsock_readn() and tls_bsock_writen() */
/* Zero the fdset, we'll set our fd prior to each invocation of select() */
FD_ZERO(&fdset);
- tv.tv_sec = 10;
- tv.tv_usec = 0;
- fdmax = bsock->fd + 1;
+ fdmax = bsock->m_fd + 1;
/* Ensure that socket is non-blocking */
- flags = bnet_set_nonblocking(bsock);
+ flags = bsock->set_nonblocking();
/* start timer */
bsock->timer_start = watchdog_time;
- bsock->timed_out = 0;
+ bsock->clear_timed_out();
+ bsock->set_killable(false);
nleft = nbytes;
while (nleft > 0) {
-
if (write) {
nwritten = SSL_write(tls->openssl, ptr, nleft);
} else {
/* Handle errors */
switch (SSL_get_error(tls->openssl, nwritten)) {
- case SSL_ERROR_NONE:
- nleft -= nwritten;
- if (nleft) {
- ptr += nwritten;
+ case SSL_ERROR_NONE:
+ nleft -= nwritten;
+ if (nleft) {
+ ptr += nwritten;
+ }
+ break;
+
+ case SSL_ERROR_SYSCALL:
+ if (nwritten == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ if (errno == EAGAIN) {
+ bmicrosleep(0, 20000); /* try again in 20 ms */
+ continue;
}
- break;
- case SSL_ERROR_ZERO_RETURN:
- /* TLS connection was cleanly shut down */
- openssl_post_errors(M_ERROR, _("TLS read/write failure."));
- goto cleanup;
- case SSL_ERROR_WANT_READ:
- /* If we timeout of a select, this will be unset */
- FD_SET((unsigned) bsock->fd, &fdset);
- /* Block until we can read */
- select(fdmax, &fdset, NULL, &fdset, &tv);
- break;
- case SSL_ERROR_WANT_WRITE:
- /* If we timeout of a select, this will be unset */
- FD_SET((unsigned) bsock->fd, &fdset);
- /* Block until we can write */
- select(fdmax, NULL, &fdset, &fdset, &tv);
- break;
- default:
- /* Socket Error Occured */
- openssl_post_errors(M_ERROR, _("TLS read/write failure."));
- goto cleanup;
+ }
+ openssl_post_errors(bsock->get_jcr(), M_FATAL, _("TLS read/write failure."));
+ goto cleanup;
+
+ case SSL_ERROR_WANT_READ:
+ /* If we timeout on a select, this will be unset */
+ FD_SET((unsigned)bsock->m_fd, &fdset);
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ /* Block until we can read */
+ select(fdmax, &fdset, NULL, NULL, &tv);
+ break;
+
+ case SSL_ERROR_WANT_WRITE:
+ /* If we timeout on a select, this will be unset */
+ FD_SET((unsigned)bsock->m_fd, &fdset);
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ /* Block until we can write */
+ select(fdmax, NULL, &fdset, NULL, &tv);
+ break;
+
+ case SSL_ERROR_ZERO_RETURN:
+ /* TLS connection was cleanly shut down */
+ /* Fall through wanted */
+ default:
+ /* Socket Error Occured */
+ openssl_post_errors(bsock->get_jcr(), M_FATAL, _("TLS read/write failure."));
+ goto cleanup;
}
/* Everything done? */
}
/* Timeout/Termination, let's take what we can get */
- if (bsock->timed_out || bsock->terminated) {
+ if (bsock->is_timed_out() || bsock->is_terminated()) {
goto cleanup;
}
}
cleanup:
/* Restore saved flags */
- bnet_restore_blocking(bsock, flags);
+ bsock->restore_blocking(flags);
/* Clear timer */
bsock->timer_start = 0;
-
+ bsock->set_killable(true);
return nbytes - nleft;
}
-int tls_bsock_writen(BSOCK *bsock, char *ptr, int32_t nbytes) {
- /* SSL_write(bsock->tls->openssl, ptr, nbytes) */
- return (openssl_bsock_readwrite(bsock, ptr, nbytes, true));
-}
-
-int tls_bsock_readn(BSOCK *bsock, char *ptr, int32_t nbytes) {
- /* SSL_read(bsock->tls->openssl, ptr, nbytes) */
- return (openssl_bsock_readwrite(bsock, ptr, nbytes, false));
-}
-
-/*
- * Return an OpenSSL thread ID
- * Returns: thread ID
- *
- */
-static unsigned long get_openssl_thread_id (void)
+int tls_bsock_writen(BSOCK *bsock, char *ptr, int32_t nbytes)
{
- /* Comparison without use of pthread_equal() is mandated by the OpenSSL API */
- return ((unsigned long) pthread_self());
-}
-
-/*
- * Allocate a dynamic OpenSSL mutex
- */
-static struct CRYPTO_dynlock_value *openssl_create_dynamic_mutex (const char *file, int line)
-{
- struct CRYPTO_dynlock_value *dynlock;
- int stat;
-
- dynlock = (struct CRYPTO_dynlock_value *) malloc(sizeof(struct CRYPTO_dynlock_value));
-
- if ((stat = pthread_mutex_init(&dynlock->mutex, NULL)) != 0) {
- Emsg1(M_ABORT, 0, "Unable to init mutex: ERR=%s\n", strerror(stat));
- }
-
- return dynlock;
+ /* SSL_write(bsock->tls->openssl, ptr, nbytes) */
+ return openssl_bsock_readwrite(bsock, ptr, nbytes, true);
}
-static void openssl_update_dynamic_mutex (int mode, struct CRYPTO_dynlock_value *dynlock, const char *file, int line)
+int tls_bsock_readn(BSOCK *bsock, char *ptr, int32_t nbytes)
{
- if (mode & CRYPTO_LOCK) {
- P(dynlock->mutex);
- } else {
- V(dynlock->mutex);
- }
+ /* SSL_read(bsock->tls->openssl, ptr, nbytes) */
+ return openssl_bsock_readwrite(bsock, ptr, nbytes, false);
}
-static void openssl_destroy_dynamic_mutex (struct CRYPTO_dynlock_value *dynlock, const char *file, int line)
-{
- int stat;
+#else /* HAVE_OPENSSL */
+# error No TLS implementation available.
+#endif /* !HAVE_OPENSSL */
- if ((stat = pthread_mutex_destroy(&dynlock->mutex)) != 0) {
- Emsg1(M_ABORT, 0, "Unable to destroy mutex: ERR=%s\n", strerror(stat));
- }
- free(dynlock);
-}
+#else /* TLS NOT enabled, dummy routines substituted */
-/*
- * (Un)Lock a static OpenSSL mutex
- */
-static void openssl_update_static_mutex (int mode, int i, const char *file, int line)
-{
- if (mode & CRYPTO_LOCK) {
- P(mutexes[i]);
- } else {
- V(mutexes[i]);
- }
-}
-/*
- * Initialize OpenSSL thread support
- * Returns: 0 on success
- * errno on failure
- */
-static int openssl_init_threads (void)
+/* Dummy routines */
+TLS_CONTEXT *new_tls_context(const char *ca_certfile, const char *ca_certdir,
+ const char *certfile, const char *keyfile,
+ CRYPTO_PEM_PASSWD_CB *pem_callback,
+ const void *pem_userdata, const char *dhfile,
+ bool verify_peer)
{
- int i, numlocks;
- int stat;
-
-
- /* Set thread ID callback */
- CRYPTO_set_id_callback(get_openssl_thread_id);
-
- /* Initialize static locking */
- numlocks = CRYPTO_num_locks();
- mutexes = (pthread_mutex_t *) malloc(numlocks * sizeof(pthread_mutex_t));
- for (i = 0; i < numlocks; i++) {
- if ((stat = pthread_mutex_init(&mutexes[i], NULL)) != 0) {
- Emsg1(M_ERROR, 0, "Unable to init mutex: ERR=%s\n", strerror(stat));
- return stat;
- }
- }
-
- /* Set static locking callback */
- CRYPTO_set_locking_callback(openssl_update_static_mutex);
-
- /* Initialize dyanmic locking */
- CRYPTO_set_dynlock_create_callback(openssl_create_dynamic_mutex);
- CRYPTO_set_dynlock_lock_callback(openssl_update_dynamic_mutex);
- CRYPTO_set_dynlock_destroy_callback(openssl_destroy_dynamic_mutex);
-
- return 0;
+ return NULL;
}
+void free_tls_context(TLS_CONTEXT *ctx) { }
-/*
- * Clean up OpenSSL threading support
- */
-static void openssl_cleanup_threads (void)
-{
- int i, numlocks;
- int stat;
-
- /* Unset thread ID callback */
- CRYPTO_set_id_callback(NULL);
-
- /* Deallocate static lock mutexes */
- numlocks = CRYPTO_num_locks();
- for (i = 0; i < numlocks; i++) {
- if ((stat = pthread_mutex_destroy(&mutexes[i])) != 0) {
- /* We don't halt execution, reporting the error should be sufficient */
- Emsg1(M_ERROR, 0, "Unable to destroy mutex: ERR=%s\n", strerror(stat));
- }
- }
-
- /* Unset static locking callback */
- CRYPTO_set_locking_callback(NULL);
+void tls_bsock_shutdown(BSOCK *bsock) { }
- /* Free static lock array */
- free(mutexes);
+void free_tls_connection(TLS_CONNECTION *tls) { }
- /* Unset dynamic locking callbacks */
- CRYPTO_set_dynlock_create_callback(NULL);
- CRYPTO_set_dynlock_lock_callback(NULL);
- CRYPTO_set_dynlock_destroy_callback(NULL);
-}
-
-
-/*
- * Seed TLS PRNG
- * Returns: 1 on success
- * 0 on failure
- */
-static int seed_tls_prng (void)
+bool get_tls_require(TLS_CONTEXT *ctx)
{
- const char *names[] = { "/dev/urandom", "/dev/random", NULL };
- int i;
-
- // ***FIXME***
- // Win32 Support
- // Read saved entropy?
-
- for (i = 0; names[i]; i++) {
- if (RAND_load_file(names[i], 1024) != -1) {
- /* Success */
- return 1;
- }
- }
-
- /* Fail */
- return 0;
-}
-
-/*
- * Save TLS Entropy
- * Returns: 1 on success
- * 0 on failure
- */
-static int save_tls_prng (void)
-{
- // ***FIXME***
- // Implement PRNG state save
- return 1;
-}
-
-/*
- * Perform global initialization of TLS
- * This function is not thread safe.
- * Returns: 0 on success
- * errno on failure
- */
-int init_tls (void)
-{
- int stat;
-
- if ((stat = openssl_init_threads()) != 0) {
- Emsg1(M_ABORT, 0, "Unable to init OpenSSL threading: ERR=%s\n", strerror(stat));
- }
-
- /* Load libssl and libcrypto human-readable error strings */
- SSL_load_error_strings();
-
- /* Register OpenSSL ciphers */
- SSL_library_init();
-
- if (!seed_tls_prng()) {
- Emsg0(M_ERROR_TERM, 0, _("Failed to seed OpenSSL PRNG\n"));
- }
-
- tls_initialized = true;
-
- return stat;
+ return false;
}
-/*
- * Perform global cleanup of TLS
- * All TLS connections must be closed before calling this function.
- * This function is not thread safe.
- * Returns: 0 on success
- * errno on failure
- */
-int cleanup_tls (void)
+bool get_tls_enable(TLS_CONTEXT *ctx)
{
- /*
- * Ensure that we've actually been initialized; Doing this here decreases the
- * complexity of client's termination/cleanup code.
- */
- if (!tls_initialized) {
- return 0;
- }
-
- if (!save_tls_prng()) {
- Emsg0(M_ERROR, 0, _("Failed to save OpenSSL PRNG\n"));
- }
-
- openssl_cleanup_threads();
-
- /* Free libssl and libcrypto error strings */
- ERR_free_strings();
-
- /* Free memory used by PRNG */
- RAND_cleanup();
-
- tls_initialized = false;
-
- return 0;
+ return false;
}
-#else /* HAVE_OPENSSL */
-# error No TLS implementation available.
-#endif /* !HAVE_OPENSSL */
-
-#else
-
-int init_tls(void) { return 0; }
-
-
#endif /* HAVE_TLS */