Blame winpr/libwinpr/utils/ssl.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * WinPR: Windows Portable Runtime
Packit 1fb8d4
 * OpenSSL Library Initialization
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright 2014 Thincast Technologies GmbH
Packit 1fb8d4
 * Copyright 2014 Norbert Federa <norbert.federa@thincast.com>
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/crt.h>
Packit 1fb8d4
#include <winpr/synch.h>
Packit 1fb8d4
#include <winpr/ssl.h>
Packit 1fb8d4
#include <winpr/thread.h>
Packit 1fb8d4
#include <winpr/crypto.h>
Packit 1fb8d4
Packit 1fb8d4
#ifdef WITH_OPENSSL
Packit 1fb8d4
Packit 1fb8d4
#include <openssl/ssl.h>
Packit 1fb8d4
#include <openssl/err.h>
Packit 1fb8d4
Packit 1fb8d4
#include "../log.h"
Packit 1fb8d4
#define TAG WINPR_TAG("utils.ssl")
Packit 1fb8d4
Packit 1fb8d4
static BOOL g_winpr_openssl_initialized_by_winpr = FALSE;
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * Note from OpenSSL 1.1.0 "CHANGES":
Packit 1fb8d4
 * OpenSSL now uses a new threading API. It is no longer necessary to
Packit 1fb8d4
 * set locking callbacks to use OpenSSL in a multi-threaded environment.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
Packit 1fb8d4
Packit 1fb8d4
#define WINPR_OPENSSL_LOCKING_REQUIRED 1
Packit 1fb8d4
Packit 1fb8d4
static int g_winpr_openssl_num_locks = 0;
Packit 1fb8d4
static HANDLE* g_winpr_openssl_locks = NULL;
Packit 1fb8d4
Packit 1fb8d4
struct CRYPTO_dynlock_value
Packit 1fb8d4
{
Packit 1fb8d4
	HANDLE mutex;
Packit 1fb8d4
};
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
Packit 1fb8d4
static unsigned long _winpr_openssl_id(void)
Packit 1fb8d4
{
Packit 1fb8d4
	return (unsigned long)GetCurrentThreadId();
Packit 1fb8d4
}
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
static void _winpr_openssl_locking(int mode, int type, const char* file, int line)
Packit 1fb8d4
{
Packit 1fb8d4
	if (mode & CRYPTO_LOCK)
Packit 1fb8d4
	{
Packit 1fb8d4
		WaitForSingleObject(g_winpr_openssl_locks[type], INFINITE);
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		ReleaseMutex(g_winpr_openssl_locks[type]);
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static struct CRYPTO_dynlock_value* _winpr_openssl_dynlock_create(const char* file, int line)
Packit 1fb8d4
{
Packit 1fb8d4
	struct CRYPTO_dynlock_value* dynlock;
Packit 1fb8d4
Packit 1fb8d4
	if (!(dynlock = (struct CRYPTO_dynlock_value*) malloc(sizeof(struct CRYPTO_dynlock_value))))
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	if (!(dynlock->mutex = CreateMutex(NULL, FALSE, NULL)))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(dynlock);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return dynlock;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void _winpr_openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value* dynlock,
Packit 1fb8d4
                                        const char* file, int line)
Packit 1fb8d4
{
Packit 1fb8d4
	if (mode & CRYPTO_LOCK)
Packit 1fb8d4
	{
Packit 1fb8d4
		WaitForSingleObject(dynlock->mutex, INFINITE);
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		ReleaseMutex(dynlock->mutex);
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void _winpr_openssl_dynlock_destroy(struct CRYPTO_dynlock_value* dynlock, const char* file,
Packit 1fb8d4
        int line)
Packit 1fb8d4
{
Packit 1fb8d4
	CloseHandle(dynlock->mutex);
Packit 1fb8d4
	free(dynlock);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL _winpr_openssl_initialize_locking(void)
Packit 1fb8d4
{
Packit 1fb8d4
	int i, count;
Packit 1fb8d4
Packit 1fb8d4
	/* OpenSSL static locking */
Packit 1fb8d4
Packit 1fb8d4
	if (CRYPTO_get_locking_callback())
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_WARN(TAG, "OpenSSL static locking callback is already set");
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		if ((count = CRYPTO_num_locks()) > 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			HANDLE* locks;
Packit 1fb8d4
Packit 1fb8d4
			if (!(locks = calloc(count, sizeof(HANDLE))))
Packit 1fb8d4
			{
Packit 1fb8d4
				WLog_ERR(TAG, "error allocating lock table");
Packit 1fb8d4
				return FALSE;
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			for (i = 0; i < count; i++)
Packit 1fb8d4
			{
Packit 1fb8d4
				if (!(locks[i] = CreateMutex(NULL, FALSE, NULL)))
Packit 1fb8d4
				{
Packit 1fb8d4
					WLog_ERR(TAG, "error creating lock #%d", i);
Packit 1fb8d4
Packit 1fb8d4
					while (i--)
Packit 1fb8d4
					{
Packit 1fb8d4
						if (locks[i])
Packit 1fb8d4
							CloseHandle(locks[i]);
Packit 1fb8d4
					}
Packit 1fb8d4
Packit 1fb8d4
					free(locks);
Packit 1fb8d4
					return FALSE;
Packit 1fb8d4
				}
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
			g_winpr_openssl_locks = locks;
Packit 1fb8d4
			g_winpr_openssl_num_locks = count;
Packit 1fb8d4
			CRYPTO_set_locking_callback(_winpr_openssl_locking);
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* OpenSSL dynamic locking */
Packit 1fb8d4
Packit 1fb8d4
	if (CRYPTO_get_dynlock_create_callback() ||
Packit 1fb8d4
	    CRYPTO_get_dynlock_lock_callback()   ||
Packit 1fb8d4
	    CRYPTO_get_dynlock_destroy_callback())
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_WARN(TAG, "dynamic locking callbacks are already set");
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		CRYPTO_set_dynlock_create_callback(_winpr_openssl_dynlock_create);
Packit 1fb8d4
		CRYPTO_set_dynlock_lock_callback(_winpr_openssl_dynlock_lock);
Packit 1fb8d4
		CRYPTO_set_dynlock_destroy_callback(_winpr_openssl_dynlock_destroy);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* Use the deprecated CRYPTO_get_id_callback() if building against OpenSSL < 1.0.0 */
Packit 1fb8d4
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
Packit 1fb8d4
Packit 1fb8d4
	if (CRYPTO_get_id_callback())
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_WARN(TAG, "OpenSSL id_callback is already set");
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		CRYPTO_set_id_callback(_winpr_openssl_id);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL _winpr_openssl_cleanup_locking(void)
Packit 1fb8d4
{
Packit 1fb8d4
	/* undo our static locking modifications */
Packit 1fb8d4
	if (CRYPTO_get_locking_callback() == _winpr_openssl_locking)
Packit 1fb8d4
	{
Packit 1fb8d4
		int i;
Packit 1fb8d4
		CRYPTO_set_locking_callback(NULL);
Packit 1fb8d4
Packit 1fb8d4
		for (i = 0; i < g_winpr_openssl_num_locks; i++)
Packit 1fb8d4
		{
Packit 1fb8d4
			CloseHandle(g_winpr_openssl_locks[i]);
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		g_winpr_openssl_num_locks = 0;
Packit 1fb8d4
		free(g_winpr_openssl_locks);
Packit 1fb8d4
		g_winpr_openssl_locks = NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* unset our dynamic locking callbacks */
Packit 1fb8d4
Packit 1fb8d4
	if (CRYPTO_get_dynlock_create_callback() == _winpr_openssl_dynlock_create)
Packit 1fb8d4
	{
Packit 1fb8d4
		CRYPTO_set_dynlock_create_callback(NULL);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (CRYPTO_get_dynlock_lock_callback() == _winpr_openssl_dynlock_lock)
Packit 1fb8d4
	{
Packit 1fb8d4
		CRYPTO_set_dynlock_lock_callback(NULL);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (CRYPTO_get_dynlock_destroy_callback() == _winpr_openssl_dynlock_destroy)
Packit 1fb8d4
	{
Packit 1fb8d4
		CRYPTO_set_dynlock_destroy_callback(NULL);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
Packit 1fb8d4
Packit 1fb8d4
	if (CRYPTO_get_id_callback() == _winpr_openssl_id)
Packit 1fb8d4
	{
Packit 1fb8d4
		CRYPTO_set_id_callback(NULL);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#endif /* OpenSSL < 1.1.0 */
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
static BOOL CALLBACK _winpr_openssl_initialize(PINIT_ONCE once, PVOID param, PVOID* context)
Packit 1fb8d4
{
Packit 1fb8d4
	DWORD flags = param ? *(PDWORD)param : WINPR_SSL_INIT_DEFAULT;
Packit 1fb8d4
Packit 1fb8d4
	if (flags & WINPR_SSL_INIT_ALREADY_INITIALIZED)
Packit 1fb8d4
	{
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
Packit 1fb8d4
Packit 1fb8d4
	if (flags & WINPR_SSL_INIT_ENABLE_LOCKING)
Packit 1fb8d4
	{
Packit 1fb8d4
		if (!_winpr_openssl_initialize_locking())
Packit 1fb8d4
		{
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
	/* SSL_load_error_strings() is void */
Packit 1fb8d4
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
Packit 1fb8d4
	SSL_load_error_strings();
Packit 1fb8d4
	/* SSL_library_init() always returns "1" */
Packit 1fb8d4
	SSL_library_init();
Packit 1fb8d4
	OpenSSL_add_all_digests();
Packit 1fb8d4
	OpenSSL_add_all_ciphers();
Packit 1fb8d4
#else
Packit 1fb8d4
Packit 1fb8d4
	if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS |
Packit 1fb8d4
	                     OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
Packit 1fb8d4
	                     OPENSSL_INIT_ADD_ALL_CIPHERS |
Packit 1fb8d4
	                     OPENSSL_INIT_ADD_ALL_DIGESTS |
Packit 1fb8d4
	                     OPENSSL_INIT_ENGINE_ALL_BUILTIN, NULL) != 1)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
	g_winpr_openssl_initialized_by_winpr = TRUE;
Packit 1fb8d4
Packit 1fb8d4
	if (flags & WINPR_SSL_INIT_ENABLE_FIPS)
Packit 1fb8d4
	{
Packit 1fb8d4
#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER)
Packit 1fb8d4
		WLog_ERR(TAG, "Openssl fips mode ENable not available on openssl versions less than 1.0.1!");
Packit 1fb8d4
#else
Packit 1fb8d4
		WLog_DBG(TAG, "Ensuring openssl fips mode is ENabled");
Packit 1fb8d4
Packit 1fb8d4
		if (FIPS_mode() != 1)
Packit 1fb8d4
		{
Packit 1fb8d4
			if (FIPS_mode_set(1))
Packit 1fb8d4
				WLog_INFO(TAG, "Openssl fips mode ENabled!");
Packit 1fb8d4
			else
Packit 1fb8d4
				WLog_ERR(TAG, "Openssl fips mode ENable failed!");
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
/* exported functions */
Packit 1fb8d4
Packit 1fb8d4
BOOL winpr_InitializeSSL(DWORD flags)
Packit 1fb8d4
{
Packit 1fb8d4
	static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
Packit 1fb8d4
	return InitOnceExecuteOnce(&once, _winpr_openssl_initialize, &flags, NULL);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL winpr_CleanupSSL(DWORD flags)
Packit 1fb8d4
{
Packit 1fb8d4
	if (flags & WINPR_SSL_CLEANUP_GLOBAL)
Packit 1fb8d4
	{
Packit 1fb8d4
		if (!g_winpr_openssl_initialized_by_winpr)
Packit 1fb8d4
		{
Packit 1fb8d4
			WLog_WARN(TAG, "ssl was not initialized by winpr");
Packit 1fb8d4
			return FALSE;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		g_winpr_openssl_initialized_by_winpr = FALSE;
Packit 1fb8d4
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
Packit 1fb8d4
		_winpr_openssl_cleanup_locking();
Packit 1fb8d4
#endif
Packit 1fb8d4
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
Packit 1fb8d4
		CRYPTO_cleanup_all_ex_data();
Packit 1fb8d4
		ERR_free_strings();
Packit 1fb8d4
		EVP_cleanup();
Packit 1fb8d4
#endif
Packit 1fb8d4
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
Packit 1fb8d4
		flags |= WINPR_SSL_CLEANUP_THREAD;
Packit 1fb8d4
#endif
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
Packit 1fb8d4
Packit 1fb8d4
	if (flags & WINPR_SSL_CLEANUP_THREAD)
Packit 1fb8d4
	{
Packit 1fb8d4
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
Packit 1fb8d4
		ERR_remove_state(0);
Packit 1fb8d4
#else
Packit 1fb8d4
		ERR_remove_thread_state(NULL);
Packit 1fb8d4
#endif
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL winpr_FIPSMode(void)
Packit 1fb8d4
{
Packit 1fb8d4
#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER)
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
#else
Packit 1fb8d4
	return (FIPS_mode() == 1);
Packit 1fb8d4
#endif
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#else
Packit 1fb8d4
Packit 1fb8d4
BOOL winpr_InitializeSSL(DWORD flags)
Packit 1fb8d4
{
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL winpr_CleanupSSL(DWORD flags)
Packit 1fb8d4
{
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL winpr_FIPSMode(void)
Packit 1fb8d4
{
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#endif