From 83b43443e51b5db06184750fb874e1e8d7ece95a Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Mon, 2 Jul 2018 13:03:12 +0200 Subject: threads: Automatically call ssh_init on load This makes unnecessary to call ssh_init() when the library is dynamically loaded. Also removes the threads shared library. The used threads implementation is chosen in configuration time, changing the ssh_threads_get_default() depending on the available threads library. Internally, it is expected a threads implementation providing: - void ssh_mutex_lock(void **mutex); - void ssh_mutex_unlock(void **mutex); - struct ssh_threads_callbacks_struct *ssh_threads_get_default(void); and a crypto implementation providing: - int crypto_thread_init(struct ssh_threads_callbacks_struct *user_callbacks); - void crypto_thread_finalize(void); This adds internal threads implementation for pthreads and noop. Signed-off-by: Anderson Toshiyuki Sasaki Reviewed-by: Andreas Schneider --- CMakeLists.txt | 16 +-- include/libssh/threads.h | 25 +++++ src/CMakeLists.txt | 19 +++- src/init.c | 182 +++++++++++++++++++++++++++------ src/threads.c | 222 +++++++---------------------------------- src/threads/CMakeLists.txt | 132 ------------------------ src/threads/libcrypto.c | 125 +++++++++++++++++++++++ src/threads/libgcrypt.c | 74 ++++++++++++++ src/threads/mbedtls.c | 65 ++++++++++++ src/threads/noop.c | 74 ++++++++++++++ src/threads/pthread.c | 139 +++++++++++++++++--------- tests/CMakeLists.txt | 7 -- tests/fuzz/CMakeLists.txt | 4 - tests/pkd/CMakeLists.txt | 3 +- tests/unittests/CMakeLists.txt | 5 +- 15 files changed, 662 insertions(+), 430 deletions(-) delete mode 100644 src/threads/CMakeLists.txt create mode 100644 src/threads/libcrypto.c create mode 100644 src/threads/libgcrypt.c create mode 100644 src/threads/mbedtls.c create mode 100644 src/threads/noop.c diff --git a/CMakeLists.txt b/CMakeLists.txt index fa0678de..c6223715 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ endif(WITH_GCRYPT) # Find out if we have threading available set(CMAKE_THREAD_PREFER_PTHREADS ON) +set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads) if (WITH_GSSAPI) @@ -101,30 +102,15 @@ configure_file(libssh.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libssh.pc) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/libssh.pc - ${CMAKE_CURRENT_BINARY_DIR}/libssh_threads.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig COMPONENT pkgconfig ) - - if (LIBSSH_THREADS) - configure_file(libssh_threads.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/libssh_threads.pc) - install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/libssh.pc - ${CMAKE_CURRENT_BINARY_DIR}/libssh_threads.pc - DESTINATION - ${LIB_INSTALL_DIR}/pkgconfig - COMPONENT - pkgconfig - ) - endif (LIBSSH_THREADS) endif (UNIX) # cmake config files set(LIBSSH_LIBRARY_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}ssh${CMAKE_SHARED_LIBRARY_SUFFIX}) -set(LIBSSH_THREADS_LIBRARY_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}ssh${CMAKE_SHARED_LIBRARY_SUFFIX}) configure_file(${PROJECT_NAME}-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake @ONLY) configure_file(${PROJECT_NAME}-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake @ONLY) diff --git a/include/libssh/threads.h b/include/libssh/threads.h index 70072648..750793b2 100644 --- a/include/libssh/threads.h +++ b/include/libssh/threads.h @@ -24,8 +24,33 @@ #include #include +#if HAVE_PTHREAD + +#include +#define SSH_MUTEX pthread_mutex_t + +#if defined _GNU_SOURCE +#define SSH_MUTEX_STATIC_INIT PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP +#else +#define SSH_MUTEX_STATIC_INIT PTHREAD_MUTEX_INITIALIZER +#endif + +#else + +# define SSH_MUTEX void * +#define SSH_MUTEX_STATIC_INIT NULL + +#endif + int ssh_threads_init(void); void ssh_threads_finalize(void); const char *ssh_threads_get_type(void); +void ssh_mutex_lock(SSH_MUTEX *mutex); +void ssh_mutex_unlock(SSH_MUTEX *mutex); + +struct ssh_threads_callbacks_struct *ssh_threads_get_default(void); +int crypto_thread_init(struct ssh_threads_callbacks_struct *user_callbacks); +void crypto_thread_finalize(void); + #endif /* THREADS_H_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 819d05c2..c37ce4d3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -167,9 +167,23 @@ set(libssh_SRCS chachapoly.c ) +if (CMAKE_USE_PTHREADS_INIT) + set(libssh_SRCS + ${libssh_SRCS} + threads/noop.c + threads/pthread.c + ) +else() + set(libssh_SRCS + ${libssh_SRCS} + threads/noop.c + ) +endif() + if (WITH_GCRYPT) set(libssh_SRCS ${libssh_SRCS} + threads/libgcrypt.c libgcrypt.c gcrypt_missing.c pki_gcrypt.c @@ -178,6 +192,7 @@ if (WITH_GCRYPT) elseif (WITH_MBEDTLS) set(libssh_SRCS ${libssh_SRCS} + threads/mbedtls.c libmbedcrypto.c mbedcrypto_missing.c pki_mbedcrypto.c @@ -186,6 +201,7 @@ elseif (WITH_MBEDTLS) else (WITH_GCRYPT) set(libssh_SRCS ${libssh_SRCS} + threads/libcrypto.c pki_crypto.c ecdh_crypto.c libcrypto.c @@ -319,6 +335,3 @@ if (WITH_STATIC_LIB) endif (WITH_STATIC_LIB) message(STATUS "Threads_FOUND=${Threads_FOUND}") -if (Threads_FOUND) - add_subdirectory(threads) -endif (Threads_FOUND) diff --git a/src/init.c b/src/init.c index f3b6d72c..e984beed 100644 --- a/src/init.c +++ b/src/init.c @@ -32,12 +32,94 @@ #include #endif +#define CONSTRUCTOR_ATTRIBUTE __attribute__((constructor)) +#define DESTRUCTOR_ATTRIBUTE __attribute__((destructor)) + +/* Declare static mutex */ +static SSH_MUTEX ssh_init_mutex = SSH_MUTEX_STATIC_INIT; + +/* Counter for initializations */ +static int _ssh_initialized = 0; + +/* Cache the returned value */ +static int _ssh_init_ret = 0; + +void libssh_constructor(void) CONSTRUCTOR_ATTRIBUTE; +void libssh_destructor(void) DESTRUCTOR_ATTRIBUTE; + +static int _ssh_init(unsigned constructor) { + + int rc = 0; + + if (!constructor) { + ssh_mutex_lock(&ssh_init_mutex); + } + + _ssh_initialized++; + + if (_ssh_initialized > 1) { + rc = _ssh_init_ret; + goto _ret; + } + + rc = ssh_threads_init(); + if (rc) { + goto _ret; + } + + rc = ssh_crypto_init(); + if (rc) { + goto _ret; + } + + rc = ssh_dh_init(); + if (rc) { + goto _ret; + } + + rc = ssh_socket_init(); + if (rc) { + goto _ret; + } + +_ret: + _ssh_init_ret = rc; + + if (!constructor) { + ssh_mutex_unlock(&ssh_init_mutex); + } + + return rc; +} + +/** + * @brief Initialize global cryptographic data structures. + * + * This functions is automatically called when the library is loaded. + * + * @returns 0 on success, -1 if an error occured. + */ +void libssh_constructor(void) +{ + + int rc; + + rc = _ssh_init(1); + + if (rc < 0) { + fprintf(stderr, "Error in auto_init()\n"); + } + + return; +} + /** * @defgroup libssh The libssh API * * The libssh library is implementing the SSH protocols and some of its - * extensions. This group of functions is mostly used to implment a SSH client. - * Some function are needed to implement a SSH server too. + * extensions. This group of functions is mostly used to implement an SSH + * client. + * Some function are needed to implement an SSH server too. * * @{ */ @@ -45,57 +127,97 @@ /** * @brief Initialize global cryptographic data structures. * - * This function should only be called once, at the beginning of the program, in - * the main thread. It may be omitted if your program is not multithreaded. + * Since version 0.8.0, it is not necessary to call this function on systems + * which are fully supported with regards to threading (that is, system with + * pthreads available). + * + * If the library is already initialized, increments the _ssh_initialized + * counter and return the error code cached in _ssh_init_ret. * * @returns 0 on success, -1 if an error occured. */ int ssh_init(void) { - int rc; + return _ssh_init(0); +} - rc = ssh_threads_init(); - if (rc != SSH_OK) { - return -1; - } +static int _ssh_finalize(unsigned destructor) { - rc = ssh_crypto_init(); - if (rc != SSH_OK) { - return -1; + if (!destructor) { + ssh_mutex_lock(&ssh_init_mutex); } - rc = ssh_dh_init(); - if (rc != SSH_OK) { - return -1; - } + if (_ssh_initialized == 1) { + _ssh_initialized = 0; + + if (_ssh_init_ret < 0) { + goto _ret; + } + + ssh_dh_finalize(); + ssh_crypto_finalize(); + ssh_socket_cleanup(); + /* It is important to finalize threading after CRYPTO because + * it still depends on it */ + ssh_threads_finalize(); - rc = ssh_socket_init(); - if (rc != SSH_OK) { - return -1; + } + else { + if (_ssh_initialized > 0) { + _ssh_initialized--; + } } +_ret: + if (!destructor) { + ssh_mutex_unlock(&ssh_init_mutex); + } return 0; } - /** * @brief Finalize and cleanup all libssh and cryptographic data structures. * - * This function should only be called once, at the end of the program! + * This function is automatically called when the library is unloaded. * * @returns 0 on succes, -1 if an error occured. * - @returns 0 otherwise */ -int ssh_finalize(void) +void libssh_destructor(void) { - ssh_dh_finalize(); - ssh_crypto_finalize(); - ssh_socket_cleanup(); - /* It is important to finalize threading after CRYPTO because - * it still depends on it */ - ssh_threads_finalize(); + int rc; - return 0; + rc = _ssh_finalize(1); + + if (rc < 0) { + fprintf(stderr, "Error in libssh_destructor()\n"); + } + + /* Detect if ssh_init() was called without matching ssh_finalize() */ + if (_ssh_initialized > 0) { + fprintf(stderr, + "Error: ssh still initialized; probably ssh_init() was" + " called more than once\n"); + } +} + +/** + * @brief Finalize and cleanup all libssh and cryptographic data structures. + * + * Since version 0.8.0, it is not necessary to call this function, since it is + * automatically called when the library is unloaded. + * + * If ssh_init() is called explicitly, then ssh_finalize() must be called + * explicitly. + * + * When called, decrements the counter _ssh_initialized. If the counter reaches + * zero, then the libssh and cryptographic data structures are cleaned up. + * + * @returns 0 on succes, -1 if an error occured. + * + @returns 0 otherwise + */ +int ssh_finalize(void) { + return _ssh_finalize(0); } /** @} */ diff --git a/src/threads.c b/src/threads.c index 52d437df..792b976d 100644 --- a/src/threads.c +++ b/src/threads.c @@ -33,213 +33,63 @@ #include "libssh/crypto.h" #include "libssh/threads.h" -#ifdef HAVE_LIBMBEDCRYPTO -#include -#endif +static struct ssh_threads_callbacks_struct *user_callbacks = NULL; -static int threads_noop (void **lock){ - (void)lock; - return 0; -} - -static unsigned long threads_id_noop (void){ - return 1; -} +/** @internal + * @brief inits the threading with the backend cryptographic libraries + */ -static struct ssh_threads_callbacks_struct ssh_threads_noop = +int ssh_threads_init(void) { - "threads_noop", - threads_noop, - threads_noop, - threads_noop, - threads_noop, - threads_id_noop -}; - -struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void) { - return &ssh_threads_noop; -} - -static struct ssh_threads_callbacks_struct *user_callbacks =&ssh_threads_noop; - -#ifdef HAVE_LIBGCRYPT -#if (GCRYPT_VERSION_NUMBER >= 0x010600) -/* libgcrypt >= 1.6 does not support custom callbacks */ -GCRY_THREAD_OPTION_PTHREAD_IMPL; - -static int libgcrypt_thread_init(void){ - if(user_callbacks == NULL) - return SSH_ERROR; - if(user_callbacks == &ssh_threads_noop) - return SSH_OK; - if (strcmp(user_callbacks->type, "threads_pthread") == 0){ - gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); - return SSH_OK; - } else { - /* not supported */ - SSH_LOG(SSH_LOG_WARN, "Custom thread handlers not supported with libgcrypt >=1.6, using pthreads"); - gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); - return SSH_OK; - } -} + static int threads_initialized = 0; + int rc; -#else -/* Libgcrypt < 1.6 specific way of handling thread callbacks */ - -static struct gcry_thread_cbs gcrypt_threads_callbacks; - -static int libgcrypt_thread_init(void){ - if(user_callbacks == NULL) - return SSH_ERROR; - if(user_callbacks == &ssh_threads_noop){ - gcrypt_threads_callbacks.option= GCRY_THREAD_OPTION_VERSION << 8 || GCRY_THREAD_OPTION_DEFAULT; - } else { - gcrypt_threads_callbacks.option= GCRY_THREAD_OPTION_VERSION << 8 || GCRY_THREAD_OPTION_USER; - } - gcrypt_threads_callbacks.mutex_init=user_callbacks->mutex_init; - gcrypt_threads_callbacks.mutex_destroy=user_callbacks->mutex_destroy; - gcrypt_threads_callbacks.mutex_lock=user_callbacks->mutex_lock; - gcrypt_threads_callbacks.mutex_unlock=user_callbacks->mutex_unlock; - gcry_control(GCRYCTL_SET_THREAD_CBS, &gcrypt_threads_callbacks); - return SSH_OK; -} -#endif /* GCRYPT_VERSION_NUMBER */ -#elif defined HAVE_LIBMBEDCRYPTO -static int libmbedcrypto_thread_init(void) -{ - if (user_callbacks == NULL) { - return SSH_ERROR; + if (threads_initialized) { + return SSH_OK; } - if (user_callbacks == &ssh_threads_noop) { - return SSH_OK; + /* first initialize the user_callbacks with our default handlers if not + * already the case + */ + if (user_callbacks == NULL){ + user_callbacks = ssh_threads_get_default(); } -#ifdef MBEDTLS_THREADING_ALT - else { - mbedtls_threading_set_alt(user_callbacks->mutex_init, - user_callbacks->mutex_destroy, user_callbacks->mutex_lock, - user_callbacks->mutex_unlock); + + /* Then initialize the crypto libraries threading callbacks */ + rc = crypto_thread_init(user_callbacks); + if (rc == SSH_OK) { + threads_initialized = 1; } -#elif defined MBEDTLS_THREADING_PTHREAD - return SSH_OK; -#else - return SSH_ERROR; -#endif + return rc; } -#else /* HAVE_LIBGCRYPT */ - -/* Libcrypto specific stuff */ - -static void **libcrypto_mutexes; -void libcrypto_lock_callback(int mode, int i, const char *file, int line); - -void libcrypto_lock_callback(int mode, int i, const char *file, int line) +void ssh_threads_finalize(void) { - (void)file; - (void)line; - if(mode & CRYPTO_LOCK){ - user_callbacks->mutex_lock(&libcrypto_mutexes[i]); - } else { - user_callbacks->mutex_unlock(&libcrypto_mutexes[i]); - } + crypto_thread_finalize(); } -#ifdef HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK -static void libcrypto_THREADID_callback(CRYPTO_THREADID *id) +int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct *cb) { - unsigned long thread_id = (*user_callbacks->thread_id)(); - - CRYPTO_THREADID_set_numeric(id, thread_id); -} -#endif /* HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK */ - -static int libcrypto_thread_init(void){ - int n=CRYPTO_num_locks(); - int i; - if(user_callbacks == &ssh_threads_noop) - return SSH_OK; - libcrypto_mutexes = calloc(n, sizeof(void *)); - if (libcrypto_mutexes == NULL) - return SSH_ERROR; - for (i=0;imutex_init(&libcrypto_mutexes[i]); - } - -#ifdef HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK - CRYPTO_THREADID_set_callback(libcrypto_THREADID_callback); -#else - CRYPTO_set_id_callback(user_callbacks->thread_id); -#endif - - CRYPTO_set_locking_callback(libcrypto_lock_callback); - - return SSH_OK; -} -static void libcrypto_thread_finalize(void){ - int n=CRYPTO_num_locks(); - int i; - if (libcrypto_mutexes==NULL) - return; - for (i=0;imutex_destroy(&libcrypto_mutexes[i]); - } - SAFE_FREE(libcrypto_mutexes); + int rc; -} - -#endif - -/** @internal - * @brief inits the threading with the backend cryptographic libraries - */ + if (user_callbacks != NULL) { + crypto_thread_finalize(); + } -int ssh_threads_init(void){ - static int threads_initialized=0; - int ret; - if(threads_initialized) - return SSH_OK; - /* first initialize the user_callbacks with our default handlers if not - * already the case - */ - if(user_callbacks == NULL){ - user_callbacks=&ssh_threads_noop; - } - - /* Then initialize the crypto libraries threading callbacks */ -#ifdef HAVE_LIBGCRYPT - ret = libgcrypt_thread_init(); -#elif HAVE_LIBMBEDCRYPTO - ret = libmbedcrypto_thread_init(); -#else /* Libcrypto */ - ret = libcrypto_thread_init(); -#endif - if(ret == SSH_OK) - threads_initialized=1; - return ret; -} + user_callbacks = cb; -void ssh_threads_finalize(void){ -#ifdef HAVE_LIBGCRYPT -#elif HAVE_LIBMBEDCRYPTO -#ifdef MBEDTLS_THREADING_ALT - mbedtls_threading_free_alt(); -#endif -#else - libcrypto_thread_finalize(); -#endif -} + rc = crypto_thread_init(user_callbacks); -int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct *cb){ - user_callbacks=cb; - return SSH_OK; + return rc; } -const char *ssh_threads_get_type(void) { - if(user_callbacks != NULL) - return user_callbacks->type; - return NULL; +const char *ssh_threads_get_type(void) +{ + if (user_callbacks != NULL) { + return user_callbacks->type; + } + return NULL; } /** diff --git a/src/threads/CMakeLists.txt b/src/threads/CMakeLists.txt deleted file mode 100644 index 2ab4e1ba..00000000 --- a/src/threads/CMakeLists.txt +++ /dev/null @@ -1,132 +0,0 @@ -project(libssh-threads C) - -set(LIBSSH_THREADS_PUBLIC_INCLUDE_DIRS - ${CMAKE_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_SOURCE_DIR} - CACHE INTERNAL "libssh public include directories" -) - -set(LIBSSH_THREADS_PRIVATE_INCLUDE_DIRS - ${CMAKE_BINARY_DIR} -) - -set(LIBSSH_THREADS_SHARED_LIBRARY - ssh_threads_shared - CACHE INTERNAL "libssh threads shared library" -) - -if (WITH_STATIC_LIB) - set(LIBSSH_THREADS_STATIC_LIBRARY - ssh_threads_static - CACHE INTERNAL "libssh threads static library" - ) -endif (WITH_STATIC_LIB) - -set(LIBSSH_THREADS_LINK_LIBRARIES - ${LIBSSH_SHARED_LIBRARY} -) - -message(STATUS "threads library: Threads_FOUND=${Threads_FOUND}") - -set(libssh_threads_SRCS) # empty SRC - -# build and link pthread -if (CMAKE_USE_PTHREADS_INIT) - set(libssh_threads_SRCS - ${libssh_threads_SRCS} - pthread.c - ) - - set(LIBSSH_THREADS_LINK_LIBRARIES - ${LIBSSH_THREADS_LINK_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} - ) - - message(STATUS "libssh_threads_SRCS=${libssh_threads_SRCS}") -endif (CMAKE_USE_PTHREADS_INIT) - -set(LIBSSH_THREADS_LINK_LIBRARIES - ${LIBSSH_THREADS_LINK_LIBRARIES} - CACHE INTERNAL "libssh threads link libraries" -) - -include_directories( - ${LIBSSH_THREADS_PUBLIC_INCLUDE_DIRS} - ${LIBSSH_THREADS_PRIVATE_INCLUDE_DIRS} -) - -if (libssh_threads_SRCS) - set(LIBSSH_THREADS ON CACHE "libssh threads lib" INTERNAL) - - add_library(${LIBSSH_THREADS_SHARED_LIBRARY} SHARED ${libssh_threads_SRCS}) - - target_link_libraries(${LIBSSH_THREADS_SHARED_LIBRARY} ${LIBSSH_THREADS_LINK_LIBRARIES}) - - set_target_properties( - ${LIBSSH_THREADS_SHARED_LIBRARY} - PROPERTIES - VERSION - ${LIBRARY_VERSION} - SOVERSION - ${LIBRARY_SOVERSION} - OUTPUT_NAME - ssh_threads - DEFINE_SYMBOL - LIBSSH_EXPORTS - ) - - if (WITH_VISIBILITY_HIDDEN) - set_target_properties(${LIBSSH_THREADS_SHARED_LIBRARY} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") - endif (WITH_VISIBILITY_HIDDEN) - - install( - TARGETS - ${LIBSSH_THREADS_SHARED_LIBRARY} - RUNTIME DESTINATION ${BIN_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - ARCHIVE DESTINATION ${LIB_INSTALL_DIR} - COMPONENT libraries - ) - - if (WITH_STATIC_LIB) - add_library(${LIBSSH_THREADS_STATIC_LIBRARY} STATIC ${libssh_threads_SRCS}) - - if (MSVC) - set(OUTPUT_SUFFIX static) - else (MSVC) - set(OUTPUT_SUFFIX ) - endif (MSVC) - - set_target_properties( - ${LIBSSH_THREADS_STATIC_LIBRARY} - PROPERTIES - VERSION - ${LIBRARY_VERSION} - SOVERSION - ${LIBRARY_SOVERSION} - OUTPUT_NAME - ssh_threads - ARCHIVE_OUTPUT_DIRECTORY - ${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_SUFFIX} - ) - - if (WIN32) - set_target_properties( - ${LIBSSH_THREADS_STATIC_LIBRARY} - PROPERTIES - COMPILE_FLAGS - "-DLIBSSH_STATIC" - ) - endif (WIN32) - - install( - TARGETS - ${LIBSSH_THREADS_STATIC_LIBRARY} - DESTINATION - ${LIB_INSTALL_DIR}/${OUTPUT_SUFFIX} - COMPONENT - libraries - ) - endif (WITH_STATIC_LIB) -endif (libssh_threads_SRCS) diff --git a/src/threads/libcrypto.c b/src/threads/libcrypto.c new file mode 100644 index 00000000..156e5041 --- /dev/null +++ b/src/threads/libcrypto.c @@ -0,0 +1,125 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2018 by Anderson Toshiyuki Sasaki + * + * The SSH 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. + * + * The SSH 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 the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/crypto.h" +#include "libssh/threads.h" +#include + +#if (OPENSSL_VERSION_NUMBER >= 0x10100000) + +int crypto_thread_init(struct ssh_threads_callbacks_struct *cb) +{ + (void) cb; + return SSH_OK; +} + +void crypto_thread_finalize(void) +{ + return; +} + +#else + +static struct ssh_threads_callbacks_struct *user_callbacks = NULL; + +static void **libcrypto_mutexes; + +void libcrypto_lock_callback(int mode, int i, const char *file, int line); + +void libcrypto_lock_callback(int mode, int i, const char *file, int line) +{ + (void)file; + (void)line; + + if (mode & CRYPTO_LOCK) { + user_callbacks->mutex_lock(&libcrypto_mutexes[i]); + } else { + user_callbacks->mutex_unlock(&libcrypto_mutexes[i]); + } +} + +#ifdef HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK +static void libcrypto_THREADID_callback(CRYPTO_THREADID *id) +{ + unsigned long thread_id = (*user_callbacks->thread_id)(); + + CRYPTO_THREADID_set_numeric(id, thread_id); +} +#endif /* HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK */ + +int crypto_thread_init(struct ssh_threads_callbacks_struct *cb) +{ + int n = CRYPTO_num_locks(); + int cmp; + int i; + + if (cb == NULL) { + return SSH_OK; + } + + if (user_callbacks != NULL) { + crypto_thread_finalize(); + } + + user_callbacks = cb; + + cmp = strcmp(user_callbacks->type, "threads_noop"); + if (cmp == 0) { + return SSH_OK; + } + + libcrypto_mutexes = calloc(n, sizeof(void *)); + if (libcrypto_mutexes == NULL) { + return SSH_ERROR; + } + + for (i = 0; i < n; ++i){ + user_callbacks->mutex_init(&libcrypto_mutexes[i]); + } + +#ifdef HAVE_OPENSSL_CRYPTO_THREADID_SET_CALLBACK + CRYPTO_THREADID_set_callback(libcrypto_THREADID_callback); +#else + CRYPTO_set_id_callback(user_callbacks->thread_id); +#endif + + CRYPTO_set_locking_callback(libcrypto_lock_callback); + + return SSH_OK; +} + +void crypto_thread_finalize(void) +{ + int n = CRYPTO_num_locks(); + int i; + + if (libcrypto_mutexes == NULL) { + return; + } + + for (i = 0; i < n; ++i) { + user_callbacks->mutex_destroy(&libcrypto_mutexes[i]); + } + SAFE_FREE(libcrypto_mutexes); +} + +#endif diff --git a/src/threads/libgcrypt.c b/src/threads/libgcrypt.c new file mode 100644 index 00000000..3560dc55 --- /dev/null +++ b/src/threads/libgcrypt.c @@ -0,0 +1,74 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2018 by Anderson Toshiyuki Sasaki + * + * The SSH 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. + * + * The SSH 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 the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/crypto.h" +#include "libssh/threads.h" +#include + +#if (GCRYPT_VERSION_NUMBER >= 0x010600) +/* libgcrypt >= 1.6 does not support custom callbacks */ +GCRY_THREAD_OPTION_PTHREAD_IMPL; + +int crypto_thread_init(struct ssh_threads_callbacks_struct *user_callbacks) +{ + (void) user_callbacks; + + return SSH_OK; +} + +#else +/* Libgcrypt < 1.6 specific way of handling thread callbacks */ + +static struct gcry_thread_cbs gcrypt_threads_callbacks; + +int crypto_thread_init(struct ssh_threads_callbacks_struct *user_callbacks) +{ + int cmp; + + if (user_callbacks == NULL) { + return SSH_OK; + } + + cmp = strcmp(user_callbacks->type, "threads_noop"); + if (cmp == 0) { + gcrypt_threads_callbacks.option= GCRY_THREAD_OPTION_VERSION << 8 || + GCRY_THREAD_OPTION_DEFAULT; + } else { + gcrypt_threads_callbacks.option= GCRY_THREAD_OPTION_VERSION << 8 || + GCRY_THREAD_OPTION_USER; + } + + gcrypt_threads_callbacks.mutex_init = user_callbacks->mutex_init; + gcrypt_threads_callbacks.mutex_destroy = user_callbacks->mutex_destroy; + gcrypt_threads_callbacks.mutex_lock = user_callbacks->mutex_lock; + gcrypt_threads_callbacks.mutex_unlock = user_callbacks->mutex_unlock; + gcry_control(GCRYCTL_SET_THREAD_CBS, &gcrypt_threads_callbacks); + + return SSH_OK; +} + +#endif /* GCRYPT_VERSION_NUMBER */ + +void crypto_thread_finalize(void) +{ + return; +} diff --git a/src/threads/mbedtls.c b/src/threads/mbedtls.c new file mode 100644 index 00000000..6cc3fa53 --- /dev/null +++ b/src/threads/mbedtls.c @@ -0,0 +1,65 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2018 by Anderson Toshiyuki Sasaki + * + * The SSH 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. + * + * The SSH 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 the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/crypto.h" +#include "libssh/threads.h" +#include + +#include + +int crypto_thread_init(struct ssh_threads_callbacks_struct *user_callbacks) +{ + int cmp; + + if (user_callbacks == NULL) { + return SSH_OK; + } + + cmp = strcmp(user_callbacks->type, "threads_noop"); + if (cmp == 0) { + return SSH_OK; + } +#ifdef MBEDTLS_THREADING_ALT + else { + if (user_callbacks != NULL) { + crypto_thread_finalize(); + } + + mbedtls_threading_set_alt(user_callbacks->mutex_init, + user_callbacks->mutex_destroy, + user_callbacks->mutex_lock, + user_callbacks->mutex_unlock); + } +#elif defined MBEDTLS_THREADING_PTHREAD + return SSH_OK; +#else + return SSH_ERROR; +#endif +} + +void crypto_thread_finalize(void) +{ +#ifdef MBEDTLS_THREADING_ALT + mbedtls_threading_free_alt(); +#endif + return; +} diff --git a/src/threads/noop.c b/src/threads/noop.c new file mode 100644 index 00000000..aad4934e --- /dev/null +++ b/src/threads/noop.c @@ -0,0 +1,74 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2018 by Anderson Toshiyuki Sasaki + * + * The SSH 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. + * + * The SSH 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 the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/threads.h" +#include + +static int threads_noop(void **lock) +{ + (void)lock; + + return 0; +} + +static unsigned long threads_id_noop (void) +{ + return 1; +} + +static struct ssh_threads_callbacks_struct ssh_threads_noop = +{ + .type = "threads_noop", + .mutex_init = threads_noop, + .mutex_destroy = threads_noop, + .mutex_lock = threads_noop, + .mutex_unlock = threads_noop, + .thread_id = threads_id_noop +}; + +/* Threads interface implementation */ + +#if !(HAVE_PTHREAD) +void ssh_mutex_lock(SSH_MUTEX *mutex) +{ + (void) mutex; + + return; +} + +void ssh_mutex_unlock(SSH_MUTEX *mutex) +{ + (void) mutex; + + return; +} + +struct ssh_threads_callbacks_struct *ssh_threads_get_default(void) +{ + return &ssh_threads_noop; +} +#endif + +struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void) +{ + return &ssh_threads_noop; +} diff --git a/src/threads/pthread.c b/src/threads/pthread.c index a0a266d7..422dd854 100644 --- a/src/threads/pthread.c +++ b/src/threads/pthread.c @@ -20,61 +20,64 @@ */ #include "config.h" +#include "libssh/threads.h" #include -#ifdef HAVE_PTHREAD - #include #include #include -/** @brief Defines the needed callbacks for pthread. Use this if your - * OS supports libpthread and want to use it for threading. - * @code - * #include - * #include - * #include - * SSH_THREADS_PTHREAD(ssh_pthread_callbacks); - * int main(){ - * ssh_init_set_threads_callbacks(&ssh_pthread_callbacks); - * ssh_init(); - * ... - * } - * @endcode - * @param name name of the structure to be declared, containing the - * callbacks for threading - * - */ +static int ssh_pthread_mutex_init (void **mutex) +{ + int rc = 0; + + if (mutex == NULL) { + return EINVAL; + } -static int ssh_pthread_mutex_init (void **priv){ - int err = 0; - *priv = malloc (sizeof (pthread_mutex_t)); - if (*priv==NULL) - return ENOMEM; - err = pthread_mutex_init (*priv, NULL); - if (err != 0){ - free (*priv); - *priv=NULL; - } - return err; + *mutex = malloc(sizeof(pthread_mutex_t)); + if (*mutex == NULL) { + return ENOMEM; + } + + rc = pthread_mutex_init ((pthread_mutex_t *)*mutex, NULL); + if (rc){ + free (*mutex); + *mutex = NULL; + } + + return rc; } -static int ssh_pthread_mutex_destroy (void **lock) { - int err = pthread_mutex_destroy (*lock); - free (*lock); - *lock=NULL; - return err; +static int ssh_pthread_mutex_destroy (void **mutex) +{ + + int rc = 0; + + if (mutex == NULL) { + return EINVAL; + } + + rc = pthread_mutex_destroy ((pthread_mutex_t *)*mutex); + + free (*mutex); + *mutex = NULL; + + return rc; } -static int ssh_pthread_mutex_lock (void **lock) { - return pthread_mutex_lock (*lock); +static int ssh_pthread_mutex_lock (void **mutex) +{ + return pthread_mutex_lock((pthread_mutex_t *)*mutex); } -static int ssh_pthread_mutex_unlock (void **lock){ - return pthread_mutex_unlock (*lock); +static int ssh_pthread_mutex_unlock (void **mutex) +{ + return pthread_mutex_unlock((pthread_mutex_t *)*mutex); } -static unsigned long ssh_pthread_thread_id (void){ +static unsigned long ssh_pthread_thread_id (void) +{ #if defined(_WIN32) && !defined(__WINPTHREADS_VERSION) return (unsigned long) pthread_self().p; #else @@ -84,16 +87,54 @@ static unsigned long ssh_pthread_thread_id (void){ static struct ssh_threads_callbacks_struct ssh_threads_pthread = { - .type="threads_pthread", - .mutex_init=ssh_pthread_mutex_init, - .mutex_destroy=ssh_pthread_mutex_destroy, - .mutex_lock=ssh_pthread_mutex_lock, - .mutex_unlock=ssh_pthread_mutex_unlock, - .thread_id=ssh_pthread_thread_id + .type = "threads_pthread", + .mutex_init = ssh_pthread_mutex_init, + .mutex_destroy = ssh_pthread_mutex_destroy, + .mutex_lock = ssh_pthread_mutex_lock, + .mutex_unlock = ssh_pthread_mutex_unlock, + .thread_id = ssh_pthread_thread_id }; -struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void) { - return &ssh_threads_pthread; +/* Threads interface implementation */ + +#if (HAVE_PTHREAD) +void ssh_mutex_lock(SSH_MUTEX *mutex) +{ + int rc; + + if (mutex == NULL) { + exit(EINVAL); + } + + rc = pthread_mutex_lock(mutex); + + if (rc) { + exit(rc); + } +} + +void ssh_mutex_unlock(SSH_MUTEX *mutex) +{ + int rc; + + if (mutex == NULL) { + exit(EINVAL); + } + + rc = pthread_mutex_unlock(mutex); + + if (rc) { + exit(rc); + } +} + +struct ssh_threads_callbacks_struct *ssh_threads_get_default(void) +{ + return &ssh_threads_pthread; } +#endif -#endif /* HAVE_PTHREAD */ +struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void) +{ + return &ssh_threads_pthread; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4eba739c..001f9760 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,13 +22,6 @@ set(TORTURE_LINK_LIBRARIES ${LIBSSH_STATIC_LIBRARY} ${LIBSSH_LINK_LIBRARIES}) -if (LIBSSH_THREADS) - set(TORTURE_LINK_LIBRARIES - ${TORTURE_LINK_LIBRARIES} - ${LIBSSH_THREADS_STATIC_LIBRARY} - ${LIBSSH_THREADS_LINK_LIBRARIES}) -endif (LIBSSH_THREADS) - # create test library add_library(${TORTURE_LIBRARY} STATIC diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt index c3e350be..5c1e63b0 100644 --- a/tests/fuzz/CMakeLists.txt +++ b/tests/fuzz/CMakeLists.txt @@ -6,8 +6,4 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") PROPERTIES COMPILE_FLAGS "-fsanitize=fuzzer" LINK_FLAGS "-fsanitize=fuzzer") - - target_link_libraries(ssh_server_fuzzer - ${LIBSSH_THREADS_STATIC_LIBRARY} - ${LIBSSH_THREADS_LINK_LIBRARIES}) endif() diff --git a/tests/pkd/CMakeLists.txt b/tests/pkd/CMakeLists.txt index a07021ec..d2da5766 100644 --- a/tests/pkd/CMakeLists.txt +++ b/tests/pkd/CMakeLists.txt @@ -24,9 +24,8 @@ set(pkd_libs ${CMOCKA_LIBRARY} ${LIBSSH_STATIC_LIBRARY} ${LIBSSH_LINK_LIBRARIES} - ${LIBSSH_THREADS_STATIC_LIBRARY} - ${LIBSSH_THREADS_LINK_LIBRARIES} ${ARGP_LIBRARIES} + pthread ) add_executable(pkd_hello ${pkd_hello_src}) diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt index d518dd21..e833481a 100644 --- a/tests/unittests/CMakeLists.txt +++ b/tests/unittests/CMakeLists.txt @@ -32,11 +32,12 @@ if (UNIX AND NOT WIN32) # requires /dev/null add_cmocka_test(torture_channel torture_channel.c ${TORTURE_LIBRARY}) # requires pthread - if (LIBSSH_THREADS) + if (CMAKE_USE_PTHREADS_INIT) add_cmocka_test(torture_rand torture_rand.c ${TORTURE_LIBRARY}) + target_link_libraries(torture_rand Threads::Threads) # Not working correctly #if (WITH_SERVER) # add_cmocka_test(torture_server_x11 torture_server_x11.c ${TORTURE_LIBRARY}) #endif (WITH_SERVER) - endif (LIBSSH_THREADS) + endif () endif (UNIX AND NOT WIN32) -- cgit v1.2.3