Code

SSL utils: Fixed a memory leak in a couple of error conditions.
[sysdb.git] / src / utils / ssl.c
index f75223c03667d18d077be5bc770b9198d254aefc..fdb170d2b2da42b588dbaec40b33bd2afde5ab8a 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include <pthread.h>
+
 #include <openssl/ssl.h>
 #include <openssl/bio.h>
+#include <openssl/crypto.h>
 #include <openssl/x509.h>
 #include <openssl/err.h>
 
@@ -60,6 +63,40 @@ struct sdb_ssl_session {
        SSL *ssl;
 };
 
+/*
+ * OpenSSL helper functions
+ */
+
+static pthread_mutex_t *mutexes = NULL;
+static int mutexes_num = 0;
+
+static void
+locking_callback(int mode, int n, const char *file, int line)
+{
+       if (! mutexes) {
+               sdb_log(SDB_LOG_EMERG, "ssl: CRYPTO_lock called from %s:%d "
+                               "before initializing SSL", file, line);
+               return;
+       }
+       if (n >= mutexes_num) {
+               sdb_log(SDB_LOG_EMERG, "ssl: CRYPTO_lock called from %s:%d "
+                               "for mutex %d but only %d are available",
+                               file, line, n, mutexes_num);
+               return;
+       }
+
+       if (mode & CRYPTO_LOCK)
+               pthread_mutex_lock(&mutexes[n]);
+       else
+               pthread_mutex_unlock(&mutexes[n]);
+} /* locking_callback */
+
+static void
+threadid_callback(CRYPTO_THREADID *id)
+{
+       CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
+} /* threadid_callback */
+
 /*
  * private helper functions
  */
@@ -137,27 +174,30 @@ ssl_log_err(int prio, SSL *ssl, int status, const char *prefix, ...)
 } /* ssl_log_err */
 
 static int
-copy_options(sdb_ssl_options_t *dst, sdb_ssl_options_t *src)
+copy_options(sdb_ssl_options_t *dst, const sdb_ssl_options_t *src)
 {
+       sdb_ssl_options_t tmp;
        sdb_ssl_options_t def = SDB_SSL_DEFAULT_OPTIONS;
 
-       if (! src)
-               src = &def;
-
-       if (! src->ca_file)
-               src->ca_file = def.ca_file;
-       if (! src->key_file)
-               src->key_file = def.key_file;
-       if (! src->cert_file)
-               src->cert_file = def.cert_file;
-
-       dst->ca_file = strdup(src->ca_file);
-       dst->key_file = strdup(src->key_file);
-       dst->cert_file = strdup(src->cert_file);
+       if (src)
+               tmp = *src;
+       else
+               tmp = def;
+
+       if (! tmp.ca_file)
+               tmp.ca_file = def.ca_file;
+       if (! tmp.key_file)
+               tmp.key_file = def.key_file;
+       if (! tmp.cert_file)
+               tmp.cert_file = def.cert_file;
+
+       dst->ca_file = strdup(tmp.ca_file);
+       dst->key_file = strdup(tmp.key_file);
+       dst->cert_file = strdup(tmp.cert_file);
        if ((! dst->ca_file) || (! dst->key_file) || (! dst->cert_file))
                return -1;
-       if (src->crl_file) {
-               dst->crl_file = strdup(src->crl_file);
+       if (tmp.crl_file) {
+               dst->crl_file = strdup(tmp.crl_file);
                if (! dst->crl_file)
                        return -1;
        }
@@ -168,8 +208,54 @@ copy_options(sdb_ssl_options_t *dst, sdb_ssl_options_t *src)
  * public API
  */
 
+int
+sdb_ssl_init(void)
+{
+       int i;
+
+       SSL_load_error_strings();
+       OpenSSL_add_ssl_algorithms();
+
+       mutexes = calloc(CRYPTO_num_locks(), sizeof(*mutexes));
+       if (! mutexes) {
+               char errbuf[1024];
+               sdb_log(SDB_LOG_ERR, "ssl: Failed to allocate mutexes: %s",
+                               sdb_strerror(errno, errbuf, sizeof(errbuf)));
+               return -1;
+       }
+       for (i = 0; i < CRYPTO_num_locks(); ++i) {
+               errno = pthread_mutex_init(&mutexes[i], /* attr = */ NULL);
+               if (errno) {
+                       char errbuf[1024];
+                       sdb_log(SDB_LOG_ERR, "ssl: Failed to initialize mutex: %s",
+                                       sdb_strerror(errno, errbuf, sizeof(errbuf)));
+                       return -1;
+               }
+               mutexes_num = i + 1;
+       }
+
+       CRYPTO_set_locking_callback(locking_callback);
+       CRYPTO_THREADID_set_callback(threadid_callback);
+       return 0;
+} /* sdb_ssl_init */
+
+void
+sdb_ssl_shutdown(void)
+{
+       int i;
+
+       ERR_free_strings();
+
+       for (i = 0; i < mutexes_num; ++i)
+               pthread_mutex_destroy(&mutexes[i]);
+       if (mutexes)
+               free(mutexes);
+       mutexes = NULL;
+       mutexes_num = 0;
+} /* sdb_ssl_shutdown */
+
 sdb_ssl_client_t *
-sdb_ssl_client_create(sdb_ssl_options_t *opts)
+sdb_ssl_client_create(const sdb_ssl_options_t *opts)
 {
        sdb_ssl_client_t *client;
 
@@ -191,7 +277,8 @@ sdb_ssl_client_create(sdb_ssl_options_t *opts)
 
        if (! SSL_CTX_load_verify_locations(client->ctx,
                                client->opts.ca_file, NULL)) {
-               ssl_log(SDB_LOG_ERR, "ssl: Failed to load CA file");
+               ssl_log(SDB_LOG_ERR, "ssl: Failed to load CA file '%s'",
+                               client->opts.ca_file);
                sdb_ssl_client_destroy(client);
                return NULL;
        }
@@ -277,7 +364,7 @@ sdb_ssl_client_connect(sdb_ssl_client_t *client, int fd)
 } /* sdb_ssl_client_connect */
 
 sdb_ssl_server_t *
-sdb_ssl_server_create(sdb_ssl_options_t *opts)
+sdb_ssl_server_create(const sdb_ssl_options_t *opts)
 {
        sdb_ssl_server_t *server;
 
@@ -312,6 +399,7 @@ sdb_ssl_server_create(sdb_ssl_options_t *opts)
                                server->opts.ca_file, NULL)) {
                ssl_log(SDB_LOG_ERR, "Failed to load CA file %s",
                                server->opts.ca_file);
+               sdb_ssl_server_destroy(server);
                return NULL;
        }
        SSL_CTX_set_client_CA_list(server->ctx,
@@ -321,16 +409,19 @@ sdb_ssl_server_create(sdb_ssl_options_t *opts)
                                server->opts.cert_file, SSL_FILETYPE_PEM)) {
                ssl_log(SDB_LOG_ERR, "Failed to load SSL cert file %s",
                                server->opts.cert_file);
+               sdb_ssl_server_destroy(server);
                return NULL;
        }
        if (! SSL_CTX_use_PrivateKey_file(server->ctx,
                                server->opts.key_file, SSL_FILETYPE_PEM)) {
                ssl_log(SDB_LOG_ERR, "Failed to load SSL key file %s",
                                server->opts.key_file);
+               sdb_ssl_server_destroy(server);
                return NULL;
        }
        if (! SSL_CTX_check_private_key(server->ctx)) {
                ssl_log(SDB_LOG_ERR, "Failed to verify SSL private key");
+               sdb_ssl_server_destroy(server);
                return NULL;
        }