*/
-
#include "bacula.h"
#include <assert.h>
/* 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;
- CRYPTO_PEM_PASSWD_CB *pem_callback;
+ TLS_PEM_PASSWD_CB *pem_callback;
const void *pem_userdata;
};
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.
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 tls_pem_callback_dispatch (char *buf, int size, int rwflag, void *userdata)
+static int openssl_pem_callback_dispatch (char *buf, int size, int rwflag, void *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,
- CRYPTO_PEM_PASSWD_CB *pem_callback,
+ TLS_PEM_PASSWD_CB *pem_callback,
const void *pem_userdata, const char *dhfile,
bool verify_peer)
{
ctx->pem_callback = pem_callback;
ctx->pem_userdata = pem_userdata;
} else {
- ctx->pem_callback = crypto_default_pem_callback;
+ ctx->pem_callback = tls_default_pem_callback;
ctx->pem_userdata = NULL;
}
- SSL_CTX_set_default_passwd_cb(ctx->openssl, tls_pem_callback_dispatch);
+ SSL_CTX_set_default_passwd_cb(ctx->openssl, openssl_pem_callback_dispatch);
SSL_CTX_set_default_passwd_cb_userdata(ctx->openssl, (void *) ctx);
/*
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->value, host) == 0) {
+ if (strcasecmp(nval->name, host) == 0) {
auth_success = true;
goto success;
}
return (openssl_bsock_readwrite(bsock, ptr, nbytes, false));
}
+/*
+ * Return an OpenSSL thread ID
+ * Returns: thread ID
+ *
+ */
+static unsigned long get_openssl_thread_id (void)
+{
+ /* 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;
+}
+
+static void openssl_update_dynamic_mutex (int mode, struct CRYPTO_dynlock_value *dynlock, const char *file, int line)
+{
+ if (mode & CRYPTO_LOCK) {
+ P(dynlock->mutex);
+ } else {
+ V(dynlock->mutex);
+ }
+}
+
+static void openssl_destroy_dynamic_mutex (struct CRYPTO_dynlock_value *dynlock, const char *file, int line)
+{
+ int stat;
+
+ if ((stat = pthread_mutex_destroy(&dynlock->mutex)) != 0) {
+ Emsg1(M_ABORT, 0, _("Unable to destroy mutex: ERR=%s\n"), strerror(stat));
+ }
+
+ free(dynlock);
+}
+
+/*
+ * (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)
+{
+ 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;
+}
+
+/*
+ * 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);
+
+ /* Free static lock array */
+ free(mutexes);
+
+ /* 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)
+{
+ 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;
+}
+
+/*
+ * 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)
+{
+ /*
+ * 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;
+}
+
#else /* HAVE_OPENSSL */
# error No TLS implementation available.
#endif /* !HAVE_OPENSSL */
#else
/* Dummy routines */
+int init_tls(void) { return 0; }
+int cleanup_tls (void) { return 0; }
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,
+ TLS_PEM_PASSWD_CB *pem_callback,
const void *pem_userdata, const char *dhfile,
bool verify_peer)
{