diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/CMakeLists.txt | 20 | ||||
-rw-r--r-- | src/lib/config.h.in | 33 | ||||
-rw-r--r-- | src/lib/crypt.c | 589 | ||||
-rw-r--r-- | src/lib/crypt/openssl.c | 788 | ||||
-rw-r--r-- | src/lib/crypt/openssl.h | 112 | ||||
-rw-r--r-- | src/lib/dev.c | 131 | ||||
-rw-r--r-- | src/lib/frct.c | 26 | ||||
-rw-r--r-- | src/lib/irm.c | 26 | ||||
-rw-r--r-- | src/lib/notifier.c | 18 | ||||
-rw-r--r-- | src/lib/pb/enroll.proto | 1 | ||||
-rw-r--r-- | src/lib/pb/ipcp_config.proto | 54 | ||||
-rw-r--r-- | src/lib/pb/irm.proto | 25 | ||||
-rw-r--r-- | src/lib/pb/model.proto | 11 | ||||
-rw-r--r-- | src/lib/protobuf.c | 348 | ||||
-rw-r--r-- | src/lib/random.c | 20 | ||||
-rw-r--r-- | src/lib/serdes-irm.c | 7 | ||||
-rw-r--r-- | src/lib/sockets.c | 22 | ||||
-rw-r--r-- | src/lib/tests/CMakeLists.txt | 13 | ||||
-rw-r--r-- | src/lib/tests/auth_test.c | 643 | ||||
-rw-r--r-- | src/lib/tests/crypt_test.c | 258 | ||||
-rw-r--r-- | src/lib/tests/sockets_test.c | 98 | ||||
-rw-r--r-- | src/lib/tests/time_test.c | 543 | ||||
-rw-r--r-- | src/lib/tests/tpm_test.c | 104 | ||||
-rw-r--r-- | src/lib/tpm.c | 197 | ||||
-rw-r--r-- | src/lib/utils.c | 22 |
25 files changed, 3482 insertions, 627 deletions
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index a6d7ac98..04a8f089 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -46,6 +46,9 @@ if (HAVE_ROBUST_MUTEX) message(STATUS "Robust mutex support disabled by user") unset(HAVE_ROBUST_MUTEX) endif () +else() + message(STATUS "Robust mutex support not available") + unset(HAVE_ROBUST_MUTEX) endif () find_library(FUSE_LIBRARIES fuse QUIET) @@ -112,18 +115,21 @@ if (OPENSSL_FOUND) set(DISABLE_OPENSSL FALSE CACHE BOOL "Disable OpenSSL support") if (NOT DISABLE_OPENSSL) message(STATUS "OpenSSL support enabled") - set(HAVE_OPENSSL TRUE) + set(HAVE_OPENSSL TRUE CACHE INTERNAL "") else() message(STATUS "OpenSSL support disabled") unset(HAVE_OPENSSL) endif() endif () -endif () - -if (NOT HAVE_OPENSSL_RNG) + set(OPENSSL_SOURCES crypt/openssl.c) +else() + message(STATUS "Install openSSL version >= \"1.1.0\" to enable OpenSSL support") + unset(HAVE_OPENSSL_RNG) + unset(HAVE_OPENSSL) set(OPENSSL_INCLUDE_DIR "") set(OPENSSL_LIBRARIES "") set(OPENSSL_CRYPTO_LIBRARY "") + set(OPENSSL_SOURCES "") endif () if (APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") @@ -228,6 +234,10 @@ set(ACK_WHEEL_SLOTS 256 CACHE STRING "Number of slots in the acknowledgment wheel, must be a power of 2") set(ACK_WHEEL_RESOLUTION 18 CACHE STRING "Minimum acknowledgment delay (ns), as a power to 2") +set(TPM_DEBUG_REPORT_INTERVAL 0 CACHE STRING + "Interval at wich the TPM will report long running threads (s), 0 disables") +set(TPM_DEBUG_ABORT_TIMEOUT 0 CACHE STRING + "TPM abort process after a thread reaches this timeout (s), 0 disables") if (HAVE_FUSE) set(PROC_FLOW_STATS TRUE CACHE BOOL @@ -280,7 +290,7 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" add_library(ouroboros-common SHARED ${SOURCE_FILES_COMMON} ${IRM_PROTO_SRCS} ${IPCP_PROTO_SRCS} ${IPCP_CONFIG_PROTO_SRCS} ${MODEL_PROTO_SRCS} - ${ENROLL_PROTO_SRCS}) + ${ENROLL_PROTO_SRCS} ${OPENSSL_SOURCES}) add_library(ouroboros-dev SHARED ${SOURCE_FILES_DEV} ${CEP_PROTO_SRCS}) diff --git a/src/lib/config.h.in b/src/lib/config.h.in index 604038b4..8326a332 100644 --- a/src/lib/config.h.in +++ b/src/lib/config.h.in @@ -28,21 +28,24 @@ #define HAVE_ENCRYPTION #endif -#define SYS_MAX_FLOWS @SYS_MAX_FLOWS@ - -#cmakedefine SHM_RBUFF_LOCKLESS -#cmakedefine SHM_RDRB_MULTI_BLOCK -#cmakedefine QOS_DISABLE_CRC -#cmakedefine HAVE_OPENSSL_RNG - -#define SHM_RBUFF_PREFIX "@SHM_RBUFF_PREFIX@" -#define SHM_LOCKFILE_NAME "@SHM_LOCKFILE_NAME@" -#define SHM_FLOW_SET_PREFIX "@SHM_FLOW_SET_PREFIX@" -#define SHM_RDRB_NAME "@SHM_RDRB_NAME@" -#define SHM_RDRB_BLOCK_SIZE @SHM_RDRB_BLOCK_SIZE@ -#define SHM_BUFFER_SIZE @SHM_BUFFER_SIZE@ -#define SHM_RBUFF_SIZE @SHM_RBUFF_SIZE@ -#define FLOW_ALLOC_TIMEOUT @FLOW_ALLOC_TIMEOUT@ +#define SYS_MAX_FLOWS @SYS_MAX_FLOWS@ + +#cmakedefine SHM_RBUFF_LOCKLESS +#cmakedefine SHM_RDRB_MULTI_BLOCK +#cmakedefine QOS_DISABLE_CRC +#cmakedefine HAVE_OPENSSL_RNG + +#define SHM_RBUFF_PREFIX "@SHM_RBUFF_PREFIX@" +#define SHM_LOCKFILE_NAME "@SHM_LOCKFILE_NAME@" +#define SHM_FLOW_SET_PREFIX "@SHM_FLOW_SET_PREFIX@" +#define SHM_RDRB_NAME "@SHM_RDRB_NAME@" +#define SHM_RDRB_BLOCK_SIZE @SHM_RDRB_BLOCK_SIZE@ +#define SHM_BUFFER_SIZE @SHM_BUFFER_SIZE@ +#define SHM_RBUFF_SIZE @SHM_RBUFF_SIZE@ +#define FLOW_ALLOC_TIMEOUT @FLOW_ALLOC_TIMEOUT@ + +#define TPM_DEBUG_REPORT_INTERVAL @TPM_DEBUG_REPORT_INTERVAL@ +#define TPM_DEBUG_ABORT_TIMEOUT @TPM_DEBUG_ABORT_TIMEOUT@ #if defined(__linux__) || (defined(__MACH__) && !defined(__APPLE__)) /* Avoid a bug in robust mutex implementation of glibc 2.25 */ diff --git a/src/lib/crypt.c b/src/lib/crypt.c index ad679501..8b18140e 100644 --- a/src/lib/crypt.c +++ b/src/lib/crypt.c @@ -1,8 +1,7 @@ /* * Ouroboros - Copyright (C) 2016 - 2024 * - * Elliptic curve Diffie-Hellman key exchange and - * AES encryption for flows using OpenSSL + * Cryptographic operations * * Dimitri Staessens <dimitri@ouroboros.rocks> * Sander Vrijders <sander@ouroboros.rocks> @@ -20,436 +19,420 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., http://www.fsf.org/about/contact/. */ + #include <config.h> #include <ouroboros/crypt.h> #include <ouroboros/errno.h> +#ifdef HAVE_OPENSSL + #include "crypt/openssl.h" +#endif /* HAVE_OPENSSL */ #include <assert.h> #include <string.h> -#ifdef HAVE_OPENSSL - -#include <ouroboros/hash.h> -#include <ouroboros/random.h> - -#include <openssl/evp.h> -#include <openssl/ec.h> -#include <openssl/pem.h> - -#include <openssl/bio.h> +struct crypt_ctx { + void * ctx; + uint8_t key[SYMMKEYSZ]; +}; -#define IVSZ 16 -/* SYMMKEYSZ defined in dev.c */ +struct auth_ctx { + void * store; +}; -/* - * Derive the common secret from - * your public key pair (kp) - * the remote public key (pub). - * Store it in a preallocated buffer (s). - */ -static int __openssl_ecdh_derive_secret(EVP_PKEY * kp, - EVP_PKEY * pub, - uint8_t * s) +int crypt_dh_pkp_create(void ** pkp, + uint8_t * pk) { - EVP_PKEY_CTX * ctx; - int ret; - uint8_t * secret; - size_t secret_len; - - ctx = EVP_PKEY_CTX_new(kp, NULL); - if (ctx == NULL) - goto fail_new; - - ret = EVP_PKEY_derive_init(ctx); - if (ret != 1) - goto fail_ctx; - - ret = EVP_PKEY_derive_set_peer(ctx, pub); - if (ret != 1) - goto fail_ctx; - - ret = EVP_PKEY_derive(ctx, NULL, &secret_len); - if (ret != 1) - goto fail_ctx; - - if (secret_len < SYMMKEYSZ) - goto fail_ctx; - - secret = OPENSSL_malloc(secret_len); - if (secret == NULL) - goto fail_ctx; - - ret = EVP_PKEY_derive(ctx, secret, &secret_len); - if (ret != 1) - goto fail_derive; - - /* Hash the secret for use as AES key. */ - mem_hash(HASH_SHA3_256, s, secret, secret_len); +#ifdef HAVE_OPENSSL + assert(pkp != NULL); + *pkp = NULL; + return openssl_ecdh_pkp_create(pkp, pk); +#else + (void) pkp; + (void) pk; - OPENSSL_free(secret); - EVP_PKEY_CTX_free(ctx); + *pkp = NULL; return 0; - - fail_derive: - OPENSSL_free(secret); - fail_ctx: - EVP_PKEY_CTX_free(ctx); - fail_new: - return -ECRYPT; +#endif } -static int __openssl_ecdh_gen_key(void ** kp) +void crypt_dh_pkp_destroy(void * pkp) { - EVP_PKEY_CTX * ctx = NULL; - EVP_PKEY_CTX * kctx = NULL; - EVP_PKEY * params = NULL; - int ret; + if (pkp == NULL) + return; +#ifdef HAVE_OPENSSL + openssl_ecdh_pkp_destroy(pkp); +#else + (void) pkp; - ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); - if (ctx == NULL) - goto fail_new_id; + return; +#endif +} - ret = EVP_PKEY_paramgen_init(ctx); - if (ret != 1) - goto fail_paramgen; +int crypt_dh_derive(void * pkp, + buffer_t pk, + uint8_t * s) +{ +#ifdef HAVE_OPENSSL + return openssl_ecdh_derive(pkp, pk, s); +#else + (void) pkp; + (void) pk; - ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_X9_62_prime256v1); - if (ret != 1) - goto fail_paramgen; + memset(s, 0, SYMMKEYSZ); - ret = EVP_PKEY_paramgen(ctx, ¶ms); - if (ret != 1) - goto fail_paramgen; + return -ECRYPT; +#endif +} - kctx = EVP_PKEY_CTX_new(params, NULL); - if (kctx == NULL) - goto fail_keygen_init; +int crypt_encrypt(struct crypt_ctx * ctx, + buffer_t in, + buffer_t * out) +{ + assert(ctx != NULL); + assert(ctx->ctx != NULL); - ret = EVP_PKEY_keygen_init(kctx); - if (ret != 1) - goto fail_keygen; +#ifdef HAVE_OPENSSL + return openssl_encrypt(ctx->ctx, ctx->key, in, out); +#else + (void) ctx; + (void) in; + (void) out; - ret = EVP_PKEY_keygen(kctx, (EVP_PKEY **) kp); - if (ret != 1) - goto fail_keygen; + return -ECRYPT; +#endif +} - EVP_PKEY_free(params); - EVP_PKEY_CTX_free(kctx); - EVP_PKEY_CTX_free(ctx); +int crypt_decrypt(struct crypt_ctx * ctx, + buffer_t in, + buffer_t * out) +{ + assert(ctx != NULL); + assert(ctx->ctx != NULL); - return 0; +#ifdef HAVE_OPENSSL + return openssl_decrypt(ctx->ctx, ctx->key, in, out); +#else + (void) ctx; + (void) in; + (void) out; - fail_keygen: - EVP_PKEY_CTX_free(kctx); - fail_keygen_init: - EVP_PKEY_free(params); - fail_paramgen: - EVP_PKEY_CTX_free(ctx); - fail_new_id: return -ECRYPT; +#endif } -static ssize_t openssl_ecdh_pkp_create(void ** pkp, - uint8_t * pk) +struct crypt_ctx * crypt_create_ctx(const uint8_t * key) { - uint8_t * pos; - ssize_t len; + struct crypt_ctx * crypt; - assert(pkp != NULL); - assert(*pkp == NULL); - assert(pk != NULL); - - if (__openssl_ecdh_gen_key(pkp) < 0) - return -ECRYPT; - - assert(*pkp != NULL); + crypt = malloc(sizeof(*crypt)); + if (crypt == NULL) + goto fail_crypt; - pos = pk; /* i2d_PUBKEY increments the pointer, don't use buf! */ - len = i2d_PUBKEY(*pkp, &pos); - if (len < 0) { - EVP_PKEY_free(*pkp); - return -ECRYPT; - } + memset(crypt, 0, sizeof(*crypt)); - return len; + if (key != NULL) + memcpy(crypt->key, key, SYMMKEYSZ); +#ifdef HAVE_OPENSSL + crypt->ctx=openssl_crypt_create_ctx(); + if (crypt->ctx == NULL) + goto fail_ctx; +#endif + return crypt; +#ifdef HAVE_OPENSSL + fail_ctx: + free(crypt); +#endif + fail_crypt: + return NULL; } -static void openssl_ecdh_pkp_destroy(void * pkp) +void crypt_destroy_ctx(struct crypt_ctx * crypt) { - EVP_PKEY_free((EVP_PKEY *) pkp); + if (crypt == NULL) + return; + +#ifdef HAVE_OPENSSL + assert(crypt->ctx != NULL); + openssl_crypt_destroy_ctx(crypt->ctx); +#else + assert(crypt->ctx == NULL); +#endif + free(crypt); } -static int openssl_ecdh_derive(void * pkp, - buffer_t pk, - uint8_t * s) +int crypt_load_privkey_file(const char * path, + void ** key) { - uint8_t * pos; - EVP_PKEY * pub; + *key = NULL; - pos = pk.data; /* d2i_PUBKEY increments the pointer, don't use key ptr! */ - pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) pk.len); - if (pub == NULL) - return -ECRYPT; - - if (__openssl_ecdh_derive_secret(pkp, pub, s) < 0) { - EVP_PKEY_free(pub); - return -ECRYPT; - } - - EVP_PKEY_free(pub); +#ifdef HAVE_OPENSSL + return openssl_load_privkey_file(path, key); +#else + (void) path; return 0; +#endif } -/* - * AES encryption calls. If FRCT is disabled, we should generate a - * 128-bit random IV and append it to the packet. If the flow is - * reliable, we could initialize the context once, and consider the - * stream a single encrypted message to avoid initializing the - * encryption context for each packet. - */ - -static int openssl_encrypt(void * ctx, - uint8_t * key, - struct shm_du_buff * sdb) +int crypt_load_privkey_str(const char * str, + void ** key) { - uint8_t * out; - uint8_t * in; - uint8_t * head; - uint8_t iv[IVSZ]; - int in_sz; - int out_sz; - int tmp_sz; - int ret; - - in = shm_du_buff_head(sdb); - in_sz = shm_du_buff_tail(sdb) - in; - - assert(in_sz > 0); - - if (random_buffer(iv, IVSZ) < 0) - goto fail_iv; - - out = malloc(in_sz + EVP_MAX_BLOCK_LENGTH); - if (out == NULL) - goto fail_iv; + *key = NULL; - EVP_CIPHER_CTX_reset(ctx); - - ret = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); - if (ret != 1) - goto fail_encrypt_init; - - ret = EVP_EncryptUpdate(ctx, out, &tmp_sz, in, in_sz); - if (ret != 1) - goto fail_encrypt; - - out_sz = tmp_sz; - ret = EVP_EncryptFinal_ex(ctx, out + tmp_sz, &tmp_sz); - if (ret != 1) - goto fail_encrypt; - - out_sz += tmp_sz; - - EVP_CIPHER_CTX_cleanup(ctx); +#ifdef HAVE_OPENSSL + return openssl_load_privkey_str(str, key); +#else + (void) str; - assert(out_sz >= in_sz); + return 0; +#endif +} - head = shm_du_buff_head_alloc(sdb, IVSZ); - if (head == NULL) - goto fail_encrypt; +int crypt_load_pubkey_str(const char * str, + void ** key) +{ + *key = NULL; - if (shm_du_buff_tail_alloc(sdb, out_sz - in_sz) == NULL) - goto fail_tail_alloc; +#ifdef HAVE_OPENSSL + return openssl_load_pubkey_str(str, key); +#else + (void) str; - memcpy(head, iv, IVSZ); - memcpy(in, out, out_sz); + return 0; +#endif +} - free(out); +int crypt_cmp_key(const void * key1, + const void * key2) +{ +#ifdef HAVE_OPENSSL + return openssl_cmp_key(key1, key2); +#else + (void) key1; + (void) key2; return 0; - - fail_tail_alloc: - shm_du_buff_head_release(sdb, IVSZ); - fail_encrypt: - EVP_CIPHER_CTX_cleanup(ctx); - fail_encrypt_init: - free(out); - fail_iv: - return -ECRYPT; +#endif } -static int openssl_decrypt(void * ctx, - uint8_t * key, - struct shm_du_buff * sdb) +void crypt_free_key(void * key) { - uint8_t * in; - uint8_t * out; - uint8_t iv[IVSZ]; - int ret; - int out_sz; - int in_sz; - int tmp_sz; + if (key == NULL) + return; - in_sz = shm_du_buff_len(sdb); - if (in_sz < IVSZ) - return -ECRYPT; +#ifdef HAVE_OPENSSL + openssl_free_key(key); +#endif +} - in = shm_du_buff_head_release(sdb, IVSZ); +int crypt_load_crt_file(const char * path, + void ** crt) +{ + assert(crt != NULL); - memcpy(iv, in, IVSZ); + *crt = NULL; - in = shm_du_buff_head(sdb); - in_sz = shm_du_buff_tail(sdb) - in; +#ifdef HAVE_OPENSSL + return openssl_load_crt_file(path, crt); +#else + (void) path; - out = malloc(in_sz); - if (out == NULL) - goto fail_malloc; + return 0; +#endif +} - EVP_CIPHER_CTX_reset(ctx); +int crypt_load_crt_str(const char * str, + void ** crt) +{ + assert(crt != NULL); - ret = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); - if (ret != 1) - goto fail_decrypt_init; + *crt = NULL; - ret = EVP_DecryptUpdate(ctx, out, &tmp_sz, in, in_sz); - if (ret != 1) - goto fail_decrypt; +#ifdef HAVE_OPENSSL + return openssl_load_crt_str(str, crt); +#else + (void) str; - out_sz = tmp_sz; + return 0; +#endif +} - ret = EVP_DecryptFinal_ex(ctx, out + tmp_sz, &tmp_sz); - if (ret != 1) - goto fail_decrypt; +int crypt_load_crt_der(const buffer_t buf, + void ** crt) +{ + assert(crt != NULL); +#ifdef HAVE_OPENSSL + return openssl_load_crt_der(buf, crt); +#else + *crt = NULL; - out_sz += tmp_sz; + (void) buf; - assert(out_sz <= in_sz); + return 0; +#endif +} - shm_du_buff_tail_release(sdb, in_sz - out_sz); +int crypt_get_pubkey_crt(void * crt, + void ** pk) +{ + assert(crt != NULL); + assert(pk != NULL); - memcpy(in, out, out_sz); +#ifdef HAVE_OPENSSL + return openssl_get_pubkey_crt(crt, pk); +#else + (void) crt; - free(out); + clrbuf(*pk); return 0; +#endif +} - fail_decrypt: - EVP_CIPHER_CTX_cleanup(ctx); - fail_decrypt_init: - free(out); - fail_malloc: - return -ECRYPT; - +void crypt_free_crt(void * crt) +{ + if (crt == NULL) + return; +#ifdef HAVE_OPENSSL + openssl_free_crt(crt); +#endif } -static int openssl_crypt_init(void ** ctx) +int crypt_crt_str(const void * crt, + char * buf) { - *ctx = EVP_CIPHER_CTX_new(); - if (*ctx == NULL) - return -ECRYPT; +#ifdef HAVE_OPENSSL + return openssl_crt_str(crt, buf); +#else + (void) crt; + (void) buf; return 0; +#endif } -static void openssl_crypt_fini(void * ctx) +int crypt_crt_der(const void * crt, + buffer_t * buf) { - EVP_CIPHER_CTX_free(ctx); -} + assert(crt != NULL); + assert(buf != NULL); -#endif /* HAVE_OPENSSL */ - -int crypt_dh_pkp_create(void ** pkp, - uint8_t * pk) -{ #ifdef HAVE_OPENSSL - assert(pkp != NULL); - *pkp = NULL; - return openssl_ecdh_pkp_create(pkp, pk); + return openssl_crt_der(crt, buf); #else - (void) pkp; - (void) pk; + (void) crt; - *pkp = NULL; + clrbuf(*buf); return 0; #endif } -void crypt_dh_pkp_destroy(void * pkp) +int crypt_check_crt_name(void * crt, + const char * name) { #ifdef HAVE_OPENSSL - openssl_ecdh_pkp_destroy(pkp); + return openssl_check_crt_name(crt, name); #else - (void) pkp; - return; + (void) crt; + (void) name; + + return 0; #endif } -int crypt_dh_derive(void * pkp, - buffer_t pk, - uint8_t * s) +struct auth_ctx * auth_create_ctx(void) { -#ifdef HAVE_OPENSSL - return openssl_ecdh_derive(pkp, pk, s); -#else - (void) pkp; - (void) pk; + struct auth_ctx * ctx; - memset(s, 0, SYMMKEYSZ); + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) + goto fail_malloc; - return -ECRYPT; + memset(ctx, 0, sizeof(*ctx)); +#ifdef HAVE_OPENSSL + ctx->store = openssl_auth_create_store(); + if (ctx->store == NULL) + goto fail_store; #endif + return ctx; +#ifdef HAVE_OPENSSL + fail_store: + free(ctx); +#endif + fail_malloc: + return NULL; } -int crypt_encrypt(struct crypt_info * info, - struct shm_du_buff * sdb) +void auth_destroy_ctx(struct auth_ctx * ctx) { - if (info->flags == 0) - return 0; + if (ctx == NULL) + return; +#ifdef HAVE_OPENSSL + openssl_auth_destroy_store(ctx->store); +#endif + free(ctx); +} + +int auth_add_crt_to_store(struct auth_ctx * ctx, + void * crt) +{ + assert(ctx != NULL); + assert(crt != NULL); #ifdef HAVE_OPENSSL - return openssl_encrypt(info->ctx, info->key, sdb); + return openssl_auth_add_crt_to_store(ctx->store, crt); #else - (void) sdb; + (void) ctx; + (void) crt; return 0; #endif } -int crypt_decrypt(struct crypt_info * info, - struct shm_du_buff * sdb) +int auth_verify_crt(struct auth_ctx * ctx, + void * crt) { - if (info->flags == 0) - return 0; - #ifdef HAVE_OPENSSL - return openssl_decrypt(info->ctx, info->key, sdb); + return openssl_verify_crt(ctx->store, crt); #else - (void) sdb; + (void) ctx; + (void) crt; - return -ECRYPT; + return 0; #endif } -int crypt_init(struct crypt_info * info) +int auth_sign(void * pkp, + buffer_t msg, + buffer_t * sig) { #ifdef HAVE_OPENSSL - return openssl_crypt_init(&info->ctx); + return openssl_sign(pkp, msg, sig); #else - info->ctx = NULL; + (void) pkp; + (void) msg; + (void) sig; + + clrbuf(*sig); + return 0; #endif } -void crypt_fini(struct crypt_info * info) +int auth_verify_sig(void * pk, + buffer_t msg, + buffer_t sig) { #ifdef HAVE_OPENSSL - openssl_crypt_fini(info->ctx); + return openssl_verify_sig(pk, msg, sig); #else - (void) info; - assert(info->ctx == NULL); + (void) pk; + (void) msg; + (void) sig; + + return 0; #endif } diff --git a/src/lib/crypt/openssl.c b/src/lib/crypt/openssl.c new file mode 100644 index 00000000..291a3418 --- /dev/null +++ b/src/lib/crypt/openssl.c @@ -0,0 +1,788 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * OpenSSL based cryptographic operations + * Elliptic curve Diffie-Hellman key exchange + * AES encryption + # Authentication + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * 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., http://www.fsf.org/about/contact/. + */ + +#include <ouroboros/errno.h> +#include <ouroboros/crypt.h> +#include <ouroboros/hash.h> +#include <ouroboros/random.h> +#include <ouroboros/utils.h> + +#include <openssl/evp.h> +#include <openssl/bio.h> +#include <openssl/ec.h> +#include <openssl/pem.h> +#include <openssl/sha.h> +#include <openssl/x509v3.h> +#include <openssl/x509_vfy.h> + +#include <assert.h> + +/* + * Derive the common secret from + * - your public key pair (kp) + * - the remote public key (pub). + * Store it in a preallocated buffer (s). + */ +static int __openssl_ecdh_derive_secret(EVP_PKEY * kp, + EVP_PKEY * pub, + uint8_t * s) +{ + EVP_PKEY_CTX * ctx; + int ret; + uint8_t * secret; + size_t secret_len; + + ctx = EVP_PKEY_CTX_new(kp, NULL); + if (ctx == NULL) + goto fail_new; + + ret = EVP_PKEY_derive_init(ctx); + if (ret != 1) + goto fail_ctx; + + ret = EVP_PKEY_derive_set_peer(ctx, pub); + if (ret != 1) + goto fail_ctx; + + ret = EVP_PKEY_derive(ctx, NULL, &secret_len); + if (ret != 1) + goto fail_ctx; + + if (secret_len < SYMMKEYSZ) + goto fail_ctx; + + secret = OPENSSL_malloc(secret_len); + if (secret == NULL) + goto fail_ctx; + + ret = EVP_PKEY_derive(ctx, secret, &secret_len); + if (ret != 1) + goto fail_derive; + + /* Hash the secret for use as AES key. */ + mem_hash(HASH_SHA3_256, s, secret, secret_len); + + OPENSSL_free(secret); + EVP_PKEY_CTX_free(ctx); + + return 0; + fail_derive: + OPENSSL_free(secret); + fail_ctx: + EVP_PKEY_CTX_free(ctx); + fail_new: + return -ECRYPT; +} + +static int __openssl_ecdh_gen_key(void ** kp) +{ + EVP_PKEY_CTX * ctx = NULL; + EVP_PKEY_CTX * kctx = NULL; + EVP_PKEY * params = NULL; + int ret; + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (ctx == NULL) + goto fail_new_id; + + ret = EVP_PKEY_paramgen_init(ctx); + if (ret != 1) + goto fail_paramgen; + + ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_X9_62_prime256v1); + if (ret != 1) + goto fail_paramgen; + + ret = EVP_PKEY_paramgen(ctx, ¶ms); + if (ret != 1) + goto fail_paramgen; + + kctx = EVP_PKEY_CTX_new(params, NULL); + if (kctx == NULL) + goto fail_keygen_init; + + ret = EVP_PKEY_keygen_init(kctx); + if (ret != 1) + goto fail_keygen; + + ret = EVP_PKEY_keygen(kctx, (EVP_PKEY **) kp); + if (ret != 1) + goto fail_keygen; + + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(kctx); + EVP_PKEY_CTX_free(ctx); + + return 0; + fail_keygen: + EVP_PKEY_CTX_free(kctx); + fail_keygen_init: + EVP_PKEY_free(params); + fail_paramgen: + EVP_PKEY_CTX_free(ctx); + fail_new_id: + return -ECRYPT; +} + +ssize_t openssl_ecdh_pkp_create(void ** pkp, + uint8_t * pk) +{ + uint8_t * pos; + ssize_t len; + + assert(pkp != NULL); + assert(*pkp == NULL); + assert(pk != NULL); + + if (__openssl_ecdh_gen_key(pkp) < 0) + goto fail_key; + + pos = pk; /* i2d_PUBKEY increments the pointer, don't use pk! */ + len = i2d_PUBKEY(*pkp, &pos); + if (len < 0) + goto fail_pubkey; + + return len; + fail_pubkey: + EVP_PKEY_free(*pkp); + fail_key: + return -ECRYPT; +} + +void openssl_ecdh_pkp_destroy(void * pkp) +{ + EVP_PKEY_free((EVP_PKEY *) pkp); +} + +int openssl_ecdh_derive(void * pkp, + buffer_t pk, + uint8_t * s) +{ + uint8_t * pos; + EVP_PKEY * pub; + + pos = pk.data; /* d2i_PUBKEY increments pos, don't use key ptr! */ + pub = d2i_PUBKEY(NULL, (const uint8_t **) &pos, (long) pk.len); + if (pub == NULL) + goto fail_pubkey; + + if (__openssl_ecdh_derive_secret(pkp, pub, s) < 0) + goto fail_key; + + EVP_PKEY_free(pub); + + return 0; + fail_pubkey: + EVP_PKEY_free(pub); + fail_key: + return -ECRYPT; +} + +/* + * AES encryption calls. If FRCT is disabled, we should generate a + * 128-bit random IV and append it to the packet. If the flow is + * reliable, we could initialize the context once, and consider the + * stream a single encrypted message to avoid initializing the + * encryption context for each packet. + */ + +int openssl_encrypt(void * ctx, + uint8_t * key, + buffer_t in, + buffer_t * out) +{ + uint8_t * ptr; + uint8_t * iv; + int in_sz; + int out_sz; + int tmp_sz; + int ret; + + in_sz = (int) in.len; + + out->data = malloc(in.len + EVP_MAX_BLOCK_LENGTH + IVSZ); + if (out->data == NULL) + goto fail_malloc; + + iv = out->data; + ptr = out->data + IVSZ; + + if (random_buffer(iv, IVSZ) < 0) + goto fail_iv; + + EVP_CIPHER_CTX_reset(ctx); + + ret = EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); + if (ret != 1) + goto fail_iv; + + ret = EVP_EncryptUpdate(ctx, ptr, &tmp_sz, in.data, in_sz); + if (ret != 1) + goto fail_encrypt; + + out_sz = tmp_sz; + ret = EVP_EncryptFinal_ex(ctx, ptr + tmp_sz, &tmp_sz); + if (ret != 1) + goto fail_encrypt; + + out_sz += tmp_sz; + + EVP_CIPHER_CTX_cleanup(ctx); + + assert(out_sz >= in_sz); + + out->len = (size_t) out_sz + IVSZ; + + return 0; + fail_encrypt: + EVP_CIPHER_CTX_cleanup(ctx); + fail_iv: + free(out->data); + fail_malloc: + clrbuf(*out); + return -ECRYPT; +} + +int openssl_decrypt(void * ctx, + uint8_t * key, + buffer_t in, + buffer_t * out) +{ + uint8_t * ptr; + uint8_t * iv; + uint8_t * input; + int ret; + int out_sz; + int in_sz; + int tmp_sz; + + in_sz = (int) in.len - IVSZ; + if (in_sz < 0) + return -ECRYPT; + + out->data = malloc(in_sz); + if (out->data == NULL) + goto fail_malloc; + + iv = in.data; + ptr = out->data; + input = in.data + IVSZ; + + EVP_CIPHER_CTX_reset(ctx); + + ret = EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); + if (ret != 1) + goto fail_decrypt_init; + + ret = EVP_DecryptUpdate(ctx, ptr, &tmp_sz, input, in_sz); + if (ret != 1) + goto fail_decrypt; + + out_sz = tmp_sz; + ret = EVP_DecryptFinal_ex(ctx, ptr + tmp_sz, &tmp_sz); + if (ret != 1) + goto fail_decrypt; + + out_sz += tmp_sz; + + assert(out_sz <= in_sz); + + out->len = (size_t) out_sz; + + return 0; + fail_decrypt: + EVP_CIPHER_CTX_cleanup(ctx); + fail_decrypt_init: + free(out->data); + fail_malloc: + clrbuf(*out); + return -ECRYPT; +} + +void * openssl_crypt_create_ctx(void) +{ + return (void *) EVP_CIPHER_CTX_new(); +} + +void openssl_crypt_destroy_ctx(void * ctx) +{ + EVP_CIPHER_CTX_free((EVP_CIPHER_CTX *) ctx); +} + +/* AUTHENTICATION */ + +int openssl_load_crt_file(const char * path, + void ** crt) +{ + FILE * fp; + X509 * xcrt; + + fp = fopen(path, "r"); + if (fp == NULL) + goto fail_file; + + xcrt = PEM_read_X509(fp, NULL, NULL, NULL); + if (xcrt == NULL) + goto fail_crt; + + fclose(fp); + + *crt = (void *) xcrt; + + return 0; + fail_crt: + fclose(fp); + fail_file: + *crt = NULL; + return -1; +} + +int openssl_load_crt_str(const char * str, + void ** crt) +{ + BIO * bio; + X509 * xcrt; + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) + goto fail_bio; + + if (BIO_write(bio, str, strlen(str)) < 0) + goto fail_crt; + + xcrt = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (xcrt == NULL) + goto fail_crt; + + BIO_free(bio); + + *crt = (void *) xcrt; + + return 0; + fail_crt: + BIO_free(bio); + fail_bio: + *crt = NULL; + return -1; +} + +int openssl_load_crt_der(buffer_t buf, + void ** crt) +{ + const uint8_t * p; + X509 * xcrt; + + assert(crt != NULL); + + p = buf.data; + + xcrt = d2i_X509(NULL, &p, buf.len); + if (xcrt == NULL) + goto fail_crt; + + *crt = (void *) xcrt; + + return 0; + fail_crt: + *crt = NULL; + return -1; +} + +int openssl_get_pubkey_crt(void * crt, + void ** key) +{ + EVP_PKEY * pk; + X509 * xcrt; + + assert(crt != NULL); + assert(key != NULL); + + xcrt = (X509 *) crt; + + pk = X509_get_pubkey(xcrt); + if (pk == NULL) + goto fail_key; + + *key = (void *) pk; + + return 0; + fail_key: + return -1; +} + +void openssl_free_crt(void * crt) +{ + X509_free((X509 *) crt); +} + +int openssl_load_privkey_file(const char * path, + void ** key) +{ + FILE * fp; + EVP_PKEY * pkey; + + fp = fopen(path, "r"); + if (fp == NULL) + goto fail_file; + + pkey = PEM_read_PrivateKey(fp, NULL, NULL, ""); + if (pkey == NULL) + goto fail_key; + + fclose(fp); + + *key = (void *) pkey; + + return 0; + fail_key: + fclose(fp); + fail_file: + *key = NULL; + return -1; +} + +int openssl_load_privkey_str(const char * str, + void ** key) +{ + BIO * bio; + EVP_PKEY * pkey; + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) + goto fail_bio; + + if (BIO_write(bio, str, strlen(str)) < 0) + goto fail_key; + + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (pkey == NULL) + goto fail_key; + + BIO_free(bio); + + *key = (void *) pkey; + + return 0; + fail_key: + BIO_free(bio); + fail_bio: + *key = NULL; + return -1; +} + +int openssl_load_pubkey_file(const char * path, + void ** key) +{ + FILE * fp; + EVP_PKEY * pkey; + + fp = fopen(path, "r"); + if (fp == NULL) + goto fail_file; + + pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL); + if (pkey == NULL) + goto fail_key; + + fclose(fp); + + *key = (void *) pkey; + + return 0; + fail_key: + fclose(fp); + fail_file: + *key = NULL; + return -1; +} + +int openssl_load_pubkey_str(const char * str, + void ** key) +{ + BIO * bio; + EVP_PKEY * pkey; + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) + goto fail_bio; + + if (BIO_write(bio, str, strlen(str)) < 0) + goto fail_key; + + pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + if (pkey == NULL) + goto fail_key; + + BIO_free(bio); + + *key = (void *) pkey; + + return 0; + fail_key: + BIO_free(bio); + fail_bio: + *key = NULL; + return -1; +} + +int openssl_cmp_key(const void * key1, + const void * key2) +{ + EVP_PKEY * pkey1; + EVP_PKEY * pkey2; + + assert(key1 != NULL); + assert(key2 != NULL); + + pkey1 = (EVP_PKEY *) key1; + pkey2 = (EVP_PKEY *) key2; + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + return EVP_PKEY_eq(pkey1, pkey2) == 1 ? 0 : -1; +#else + return EVP_PKEY_cmp(pkey1, pkey2) == 1 ? 0 : -1; +#endif +} + +void openssl_free_key(void * key) +{ + EVP_PKEY_free((EVP_PKEY *) key); +} + +int openssl_check_crt_name(void * crt, + const char * name) +{ + char * subj; + char * cn; + X509 * xcrt; + + xcrt = (X509 *) crt; + + subj = X509_NAME_oneline(X509_get_subject_name(xcrt), NULL, 0); + if (subj == NULL) + goto fail_subj; + + cn = strstr(subj, "CN="); + if (cn == NULL) + goto fail_cn; + + if (strcmp(cn + 3, name) != 0) + goto fail_cn; + + free(subj); + + return 0; + fail_cn: + free(subj); + fail_subj: + return -1; +} + +int openssl_crt_str(const void * crt, + char * str) +{ + BIO * bio; + X509 * xcrt; + char * p; + + xcrt = (X509 *) crt; + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) + goto fail_bio; + + X509_print(bio, xcrt); + + BIO_get_mem_data(bio, &p); + if (p == NULL) + goto fail_p; + + sprintf(str, "%s", p); + + BIO_free(bio); + + return 0; + fail_p: + BIO_free(bio); + fail_bio: + return -1; +} + +int openssl_crt_der(const void * crt, + buffer_t * buf) +{ + int len; + + assert(crt != NULL); + assert(buf != NULL); + + len = i2d_X509((X509 *) crt, &buf->data); + if (len < 0) + goto fail_der; + + buf->len = (size_t) len; + + return 0; + + fail_der: + clrbuf(*buf); + return -1; +} + + +void * openssl_auth_create_store(void) +{ + return X509_STORE_new(); +} + +void openssl_auth_destroy_store(void * ctx) +{ + X509_STORE_free((X509_STORE *) ctx); +} + +int openssl_auth_add_crt_to_store(void * store, + void * crt) +{ + int ret; + + ret = X509_STORE_add_cert((X509_STORE *) store, (X509 *) crt); + + return ret == 1 ? 0 : -1; +} + +int openssl_verify_crt(void * store, + void * crt) +{ + X509_STORE_CTX * ctx; + X509_STORE * _store; + X509* _crt; + int ret; + + _store = (X509_STORE *) store; + _crt = (X509 *) crt; + + ctx = X509_STORE_CTX_new(); + if (ctx == NULL) + goto fail_store_ctx; + + ret = X509_STORE_CTX_init(ctx, _store, _crt, NULL); + if (ret != 1) + goto fail_ca; + + ret = X509_verify_cert(ctx); + if (ret != 1) + goto fail_ca; + + X509_STORE_CTX_free(ctx); + + return 0; + fail_ca: + X509_STORE_CTX_free(ctx); + fail_store_ctx: + return -1; +} + +int openssl_sign(void * pkp, + buffer_t msg, + buffer_t * sig) +{ + EVP_PKEY * pkey; + EVP_MD_CTX * mdctx; + size_t required; + + assert(pkp != NULL); + assert(sig != NULL); + + pkey = (EVP_PKEY *) pkp; + + mdctx = EVP_MD_CTX_new(); + if (!mdctx) + goto fail_ctx; + + if (EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, pkey) != 1) + goto fail_digest; + + if (EVP_DigestSignUpdate(mdctx, msg.data, msg.len) != 1) + goto fail_digest; + + if (EVP_DigestSignFinal(mdctx, NULL, &required) != 1) + goto fail_digest; + + sig->data = malloc(required); + if (sig->data == NULL) + goto fail_digest; + + if (EVP_DigestSignFinal(mdctx, sig->data, &required) != 1) + goto fail_sign; + + sig->len = required; + + EVP_MD_CTX_free(mdctx); + + return 0; + fail_sign: + freebuf(*sig); + fail_digest: + EVP_MD_CTX_free(mdctx); + fail_ctx: + clrbuf(*sig); + return -1; +} + +int openssl_verify_sig(void * pk, + buffer_t msg, + buffer_t sig) +{ + EVP_PKEY * pkey; + EVP_MD_CTX * mdctx; + int ret; + + assert(pk != NULL); + + pkey = (EVP_PKEY *) pk; + + mdctx = EVP_MD_CTX_new(); + if (!mdctx) + goto fail_ctx; + + if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pkey) != 1) + goto fail_digest; + + if (EVP_DigestVerifyUpdate(mdctx, msg.data, msg.len) != 1) + goto fail_digest; + + ret = EVP_DigestVerifyFinal(mdctx, sig.data, sig.len); + if (ret != 1) + goto fail_digest; + + EVP_MD_CTX_free(mdctx); + + return 0; + fail_digest: + EVP_MD_CTX_free(mdctx); + fail_ctx: + clrbuf(sig); + return -1; +} diff --git a/src/lib/crypt/openssl.h b/src/lib/crypt/openssl.h new file mode 100644 index 00000000..d4ee73b9 --- /dev/null +++ b/src/lib/crypt/openssl.h @@ -0,0 +1,112 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * OpenSSL based cryptographic operations + * Elliptic curve Diffie-Hellman key exchange + * AES encryption + # Authentication + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * 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., http://www.fsf.org/about/contact/. + */ + +#ifndef OUROBOROS_LIB_CRYPT_OPENSSL_H +#define OUROBOROS_LIB_CRYPT_OPENSSL_H + +ssize_t openssl_ecdh_pkp_create(void ** pkp, + uint8_t * pk); + +void openssl_ecdh_pkp_destroy(void * pkp); + +int openssl_ecdh_derive(void * pkp, + buffer_t pk, + uint8_t * s); + +int openssl_encrypt(void * ctx, + uint8_t * key, + buffer_t in, + buffer_t * out); + +int openssl_decrypt(void * ctx, + uint8_t * key, + buffer_t in, + buffer_t * out); + +void * openssl_crypt_create_ctx(void); + +void openssl_crypt_destroy_ctx(void * ctx); + +/* AUTHENTICATION */ + +int openssl_load_crt_file(const char * path, + void ** crt); + +int openssl_load_crt_str(const char * str, + void ** crt); + +int openssl_load_crt_der(buffer_t buf, + void ** crt); + +int openssl_get_pubkey_crt(void * crt, + void ** pk); + +void openssl_free_crt(void * crt); + +int openssl_load_privkey_file(const char * path, + void ** key); + +int openssl_load_privkey_str(const char * str, + void ** key); + +int openssl_load_pubkey_file(const char * path, + void ** key); + +int openssl_load_pubkey_str(const char * str, + void ** key); + +int openssl_cmp_key(const void * key1, + const void * key2); + +void openssl_free_key(void * key); + +int openssl_check_crt_name(void * crt, + const char * name); + +int openssl_crt_str(const void * crt, + char * str); + +int openssl_crt_der(const void * crt, + buffer_t * buf); + +void * openssl_auth_create_store(void); + +void openssl_auth_destroy_store(void * store); + +int openssl_auth_add_crt_to_store(void * store, + void * crt); + +int openssl_verify_crt(void * store, + void * crt); + +int openssl_sign(void * pkp, + buffer_t msg, + buffer_t * sig); + +int openssl_verify_sig(void * pk, + buffer_t msg, + buffer_t sig); + +#endif /* OUROBOROS_LIB_CRYPT_OPENSSL_H */ diff --git a/src/lib/dev.c b/src/lib/dev.c index 92310b9e..c0cd11a3 100644 --- a/src/lib/dev.c +++ b/src/lib/dev.c @@ -99,7 +99,7 @@ struct flow { uint16_t oflags; ssize_t part_idx; - struct crypt_info crypt; + struct crypt_ctx * crypt; struct timespec snd_act; struct timespec rcv_act; @@ -227,7 +227,7 @@ static enum flow_state flow_wait_assign(int flow_id) static int proc_announce(const char * prog) { uint8_t buf[SOCK_BUF_SIZE]; - buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t msg = {SOCK_BUF_SIZE, buf}; int err; if (proc_announce__irm_req_ser(&msg, prog) < 0) @@ -244,7 +244,7 @@ static int proc_announce(const char * prog) static void proc_exit(void) { uint8_t buf[SOCK_BUF_SIZE]; - buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t msg = {SOCK_BUF_SIZE, buf}; if (proc_exit__irm_req_ser(&msg) < 0) return; @@ -252,6 +252,69 @@ static void proc_exit(void) send_recv_msg(&msg); } +static int sdb_encrypt(struct flow * flow, + struct shm_du_buff * sdb) +{ + buffer_t in; + buffer_t out; + uint8_t * head; + uint8_t * tail; + + if (flow->crypt == NULL) + return 0; /* No encryption */ + + in.data = shm_du_buff_head(sdb); + in.len = shm_du_buff_len(sdb); + + if (crypt_encrypt(flow->crypt, in, &out) < 0) + goto fail_encrypt; + + head = shm_du_buff_head_alloc(sdb, IVSZ); + if (head == NULL) + goto fail_alloc; + + tail = shm_du_buff_tail_alloc(sdb, (out.len - in.len) - IVSZ); + if (tail == NULL) + goto fail_alloc; + + memcpy(head, out.data, out.len); + + freebuf(out); + + return 0; + fail_alloc: + freebuf(out); + fail_encrypt: + return -ECRYPT; +} + +static int sdb_decrypt(struct flow * flow, + struct shm_du_buff * sdb) +{ + buffer_t in; + buffer_t out; + uint8_t * head; + + if (flow->crypt == NULL) + return 0; /* No decryption */ + + in.data = shm_du_buff_head(sdb); + in.len = shm_du_buff_len(sdb); + + if (crypt_decrypt(flow->crypt, in, &out) < 0) + return -ENOMEM; + + + head = shm_du_buff_head_release(sdb, IVSZ) + IVSZ; + shm_du_buff_tail_release(sdb, (in.len - out.len) - IVSZ); + + memcpy(head, out.data, out.len); + + freebuf(out); + + return 0; +} + #include "frct.c" void * flow_tx(void * o) @@ -309,18 +372,18 @@ static void _flow_keepalive(struct flow * flow) timeo = flow->info.qs.timeout; acl = shm_rbuff_get_acl(flow->rx_rb); - if (timeo == 0 || acl & (ACL_FLOWPEER | ACL_FLOWDOWN)) + if (timeo == 0 || acl & (ACL_FLOWPEER | ACL_FLOWDOWN)) return; clock_gettime(PTHREAD_COND_CLOCK, &now); - if (ts_diff_ns(&r_act, &now) > (int64_t) timeo * MILLION) { + if (ts_diff_ns(&now, &r_act) > (int64_t) timeo * MILLION) { shm_rbuff_set_acl(flow->rx_rb, ACL_FLOWPEER); shm_flow_set_notify(ai.fqset, flow_id, FLOW_PEER); return; } - if (ts_diff_ns(&s_act, &now) > (int64_t) timeo * (MILLION >> 2)) { + if (ts_diff_ns(&now, &s_act) > (int64_t) timeo * (MILLION >> 2)) { pthread_rwlock_unlock(&ai.lock); flow_send_keepalive(flow, now); @@ -423,7 +486,7 @@ static void __flow_fini(int fd) shm_flow_set_close(ai.flows[fd].set); } - crypt_fini(&ai.flows[fd].crypt); + crypt_destroy_ctx(ai.flows[fd].crypt); list_del(&ai.flows[fd].next); @@ -477,16 +540,14 @@ static int flow_init(struct flow_info * info, flow->part_idx = NO_PART; flow->snd_act = now; flow->rcv_act = now; + flow->crypt = NULL; - flow->crypt.flags = info->qs.cypher_s; /* TODO: move cypher_s */ - - memset(flow->crypt.key, 0, SYMMKEYSZ); - - if (flow->crypt.flags > 0 && sk!= NULL && sk->data != NULL) - memcpy(flow->crypt.key, sk->data , sk->len); - - if (crypt_init(&flow->crypt) < 0) - goto fail_crypt; + if (sk!= NULL && sk->data != NULL) { + assert(sk->len == SYMMKEYSZ); + flow->crypt = crypt_create_ctx(sk->data); + if (flow->crypt == NULL) + goto fail_crypt; + } assert(flow->frcti == NULL); @@ -519,7 +580,7 @@ static int flow_init(struct flow_info * info, fail_flow_set_add: frcti_destroy(flow->frcti); fail_frcti: - crypt_fini(&flow->crypt); + crypt_destroy_ctx(flow->crypt); fail_crypt: shm_flow_set_close(flow->set); fail_set: @@ -764,7 +825,7 @@ int flow_accept(qosspec_t * qs, { struct flow_info flow; uint8_t buf[SOCK_BUF_SIZE]; - buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t msg = {SOCK_BUF_SIZE, buf}; buffer_t sk; int fd; int err; @@ -805,7 +866,7 @@ int flow_alloc(const char * dst, { struct flow_info flow; uint8_t buf[SOCK_BUF_SIZE]; - buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t msg = {SOCK_BUF_SIZE, buf}; buffer_t sk; /* symmetric key */ int fd; int err; @@ -824,8 +885,10 @@ int flow_alloc(const char * dst, return -ENOMEM; err = send_recv_msg(&msg); - if (err < 0) + if (err < 0) { + printf("send_recv_msg error %d\n", err); return err; + } err = flow__irm_result_des(&msg, &flow, &sk); if (err < 0) @@ -847,7 +910,7 @@ int flow_join(const char * dst, { struct flow_info flow; uint8_t buf[SOCK_BUF_SIZE]; - buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t msg = {SOCK_BUF_SIZE, buf}; int fd; int err; @@ -855,9 +918,6 @@ int flow_join(const char * dst, if (qs != NULL) qs->ber = 1; #endif - if (qs != NULL && qs->cypher_s > 0) - return -ENOTSUP; /* TODO: Encrypted broadcast */ - memset(&flow, 0, sizeof(flow)); flow.n_pid = getpid(); @@ -888,7 +948,7 @@ int flow_dealloc(int fd) struct flow_info info; uint8_t pkt[PKT_BUF_LEN]; uint8_t buf[SOCK_BUF_SIZE]; - buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t msg = {SOCK_BUF_SIZE, buf}; struct timespec tic = TIMESPEC_INIT_NS(TICTIME); struct timespec timeo = TIMESPEC_INIT_S(0); struct flow * flow; @@ -962,7 +1022,7 @@ int ipcp_flow_dealloc(int fd) { struct flow_info info; uint8_t buf[SOCK_BUF_SIZE]; - buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t msg = {SOCK_BUF_SIZE, buf}; struct flow * flow; int err; @@ -1200,7 +1260,7 @@ static int flow_tx_sdb(struct flow * flow, if (frcti_snd(flow->frcti, sdb) < 0) goto enomem; - if (crypt_encrypt(&flow->crypt, sdb) < 0) + if (sdb_encrypt(flow, sdb) < 0) goto enomem; if (flow->info.qs.ber == 0 && add_crc(sdb) != 0) @@ -1302,7 +1362,7 @@ static bool invalid_pkt(struct flow * flow, if (flow->info.qs.ber == 0 && chk_crc(sdb) != 0) return true; - if (crypt_decrypt(&flow->crypt, sdb) < 0) + if (sdb_decrypt(flow, sdb) < 0) return true; return false; @@ -1330,6 +1390,7 @@ static ssize_t flow_rx_sdb(struct flow * flow, pthread_rwlock_unlock(&ai.lock); *sdb = shm_rdrbuff_get(ai.rdrb, idx); + if (invalid_pkt(flow, *sdb)) { shm_rdrbuff_remove(ai.rdrb, idx); return -EAGAIN; @@ -1767,11 +1828,12 @@ int np1_flow_dealloc(int flow_id, return fd; } -int np1_flow_resp(int flow_id) +int np1_flow_resp(int flow_id, + int resp) { int fd; - if (flow_wait_assign(flow_id) != FLOW_ALLOCATED) + if (resp == 0 && flow_wait_assign(flow_id) != FLOW_ALLOCATED) return -1; pthread_rwlock_rdlock(&ai.lock); @@ -1786,7 +1848,7 @@ int np1_flow_resp(int flow_id) int ipcp_create_r(const struct ipcp_info * info) { uint8_t buf[SOCK_BUF_SIZE]; - buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t msg = {SOCK_BUF_SIZE, buf}; int err; if (ipcp_create_r__irm_req_ser(&msg,info) < 0) @@ -1806,7 +1868,7 @@ int ipcp_flow_req_arr(const buffer_t * dst, { struct flow_info flow; uint8_t buf[SOCK_BUF_SIZE]; - buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t msg = {SOCK_BUF_SIZE, buf}; int err; memset(&flow, 0, sizeof(flow)); @@ -1832,6 +1894,7 @@ int ipcp_flow_req_arr(const buffer_t * dst, flow.n_1_pid = flow.n_pid; flow.n_pid = getpid(); flow.mpl = 0; + flow.qs = qos_np1; return flow_init(&flow, NULL); } @@ -1843,7 +1906,7 @@ int ipcp_flow_alloc_reply(int fd, { struct flow_info flow; uint8_t buf[SOCK_BUF_SIZE]; - buffer_t msg = {buf, SOCK_BUF_SIZE}; + buffer_t msg = {SOCK_BUF_SIZE, buf}; int err; assert(fd >= 0 && fd < SYS_MAX_FLOWS); @@ -1943,7 +2006,7 @@ int np1_flow_read(int fd, pthread_rwlock_rdlock(&ai.lock); - idx = shm_rbuff_read(flow->rx_rb);; + idx = shm_rbuff_read(flow->rx_rb); if (idx < 0) { pthread_rwlock_unlock(&ai.lock); return idx; diff --git a/src/lib/frct.c b/src/lib/frct.c index c6fef35c..08c5ea80 100644 --- a/src/lib/frct.c +++ b/src/lib/frct.c @@ -137,11 +137,11 @@ static int frct_rib_read(const char * path, "Retransmit timeout RTO (ns): %20ld\n" "Sender left window edge: %20u\n" "Sender right window edge: %20u\n" - "Sender inactive (ns): %20ld\n" + "Sender inactive (ns): %20lld\n" "Sender current sequence number: %20u\n" "Receiver left window edge: %20u\n" "Receiver right window edge: %20u\n" - "Receiver inactive (ns): %20ld\n" + "Receiver inactive (ns): %20lld\n" "Receiver last ack: %20u\n" "Number of pkt retransmissions: %20zu\n" "Number of rtt probes: %20zu\n" @@ -159,11 +159,11 @@ static int frct_rib_read(const char * path, frcti->rto, frcti->snd_cr.lwe, frcti->snd_cr.rwe, - ts_diff_ns(&frcti->snd_cr.act, &now), + ts_diff_ns(&now, &frcti->snd_cr.act), frcti->snd_cr.seqno, frcti->rcv_cr.lwe, frcti->rcv_cr.rwe, - ts_diff_ns(&frcti->rcv_cr.act, &now), + ts_diff_ns(&now, &frcti->rcv_cr.act), frcti->rcv_cr.seqno, frcti->n_rtx, frcti->n_prb, @@ -261,7 +261,7 @@ static void __send_frct_pkt(int fd, f = &ai.flows[fd]; - if (crypt_encrypt(&f->crypt, sdb) < 0) + if (sdb_encrypt(f, sdb) < 0) goto fail; #ifdef RXM_BLOCKING @@ -303,13 +303,13 @@ static void send_frct_pkt(struct frcti * frcti) ackno = frcti->rcv_cr.lwe; rwe = frcti->rcv_cr.rwe; - diff = ts_diff_ns(&frcti->rcv_cr.act, &now); + diff = ts_diff_ns(&now, &frcti->rcv_cr.act); if (diff > frcti->a) { pthread_rwlock_unlock(&frcti->lock); return; } - diff = ts_diff_ns(&frcti->snd_cr.act, &now); + diff = ts_diff_ns(&now, &frcti->snd_cr.act); if (diff < TICTIME) { pthread_rwlock_unlock(&frcti->lock); return; @@ -339,7 +339,7 @@ static struct frcti * frcti_create(int fd, #ifdef PROC_FLOW_STATS char frctstr[FRCT_NAME_STRLEN + 1]; #endif - mpl *= BILLION; + mpl *= MILLION; a *= BILLION; r *= BILLION; @@ -517,14 +517,14 @@ static bool __frcti_is_window_open(struct frcti * frcti) frcti->t_rdvs = now; } else { time_t diff; - diff = ts_diff_ns(&frcti->t_wnd, &now); + diff = ts_diff_ns(&now, &frcti->t_wnd); if (diff > MAX_RDV) { pthread_mutex_unlock(&frcti->mtx); pthread_rwlock_unlock(&frcti->lock); return false; } - diff = ts_diff_ns(&frcti->t_rdvs, &now); + diff = ts_diff_ns(&now, &frcti->t_rdvs); if (diff > frcti->rdv) { frcti->t_rdvs = now; __send_rdv(frcti->fd); @@ -580,13 +580,13 @@ static int __frcti_window_wait(struct frcti * frcti, clock_gettime(PTHREAD_COND_CLOCK, &now); - diff = ts_diff_ns(&frcti->t_wnd, &now); + diff = ts_diff_ns(&now, &frcti->t_wnd); if (diff > MAX_RDV) { pthread_mutex_unlock(&frcti->mtx); return -ECONNRESET; /* write fails! */ } - diff = ts_diff_ns(&frcti->t_rdvs, &now); + diff = ts_diff_ns(&now, &frcti->t_rdvs); if (diff > frcti->rdv) { frcti->t_rdvs = now; __send_rdv(frcti->fd); @@ -855,7 +855,7 @@ static void __frcti_rcv(struct frcti * frcti, if (!(pci->flags & FRCT_DATA)) frcti->n_dak++; #endif - rtt_estimator(frcti, ts_diff_ns(&frcti->t_probe, &now)); + rtt_estimator(frcti, ts_diff_ns(&now, &frcti->t_probe)); frcti->probe = false; } } diff --git a/src/lib/irm.c b/src/lib/irm.c index d25101f3..8333d0d3 100644 --- a/src/lib/irm.c +++ b/src/lib/irm.c @@ -523,32 +523,23 @@ int irm_unbind_process(pid_t pid, return ret; } -int irm_create_name(const char * name, - enum pol_balance pol) +int irm_create_name(struct name_info * info) { irm_msg_t msg = IRM_MSG__INIT; - name_info_msg_t ni_msg = NAME_INFO_MSG__INIT; irm_msg_t * recv_msg; int ret; - if (name == NULL) + if (info == NULL) return -EINVAL; - msg.code = IRM_MSG_CODE__IRM_CREATE_NAME; - ni_msg.name = (char *) name; - ni_msg.pol_lb = pol; - msg.n_names = 1; - - msg.names = malloc(sizeof(*msg.names)); - if (msg.names == NULL) { - return -ENOMEM; - } - - msg.names[0] = &ni_msg; + msg.code = IRM_MSG_CODE__IRM_CREATE_NAME; + msg.name_info = name_info_s_to_msg(info); + if (msg.name_info == NULL) + goto fail_info_msg; recv_msg = send_recv_irm_msg(&msg); - free(msg.names); + name_info_msg__free_unpacked(msg.name_info, NULL); if (recv_msg == NULL) return -EIRMD; @@ -562,6 +553,9 @@ int irm_create_name(const char * name, irm_msg__free_unpacked(recv_msg, NULL); return ret; + + fail_info_msg: + return -ENOMEM; } int irm_destroy_name(const char * name) diff --git a/src/lib/notifier.c b/src/lib/notifier.c index 45745b9a..4fccd371 100644 --- a/src/lib/notifier.c +++ b/src/lib/notifier.c @@ -95,18 +95,14 @@ int notifier_reg(notifier_fn_t callback, pthread_rwlock_wrlock(¬ifier.lock); list_for_each(p, ¬ifier.listeners) { - struct listener * l = list_entry(p, struct listener, next); - if (l->callback == callback) { - pthread_rwlock_unlock(¬ifier.lock); - return -EPERM; - } + l = list_entry(p, struct listener, next); + if (l->callback == callback) + goto fail; } l = malloc(sizeof(*l)); - if (l == NULL) { - pthread_rwlock_unlock(¬ifier.lock); - return -ENOMEM; - } + if (l == NULL) + goto fail; l->callback = callback; l->obj = obj; @@ -116,6 +112,10 @@ int notifier_reg(notifier_fn_t callback, pthread_rwlock_unlock(¬ifier.lock); return 0; + fail: + pthread_rwlock_unlock(¬ifier.lock); + return -1; + } void notifier_unreg(notifier_fn_t callback) diff --git a/src/lib/pb/enroll.proto b/src/lib/pb/enroll.proto index 7fe612a8..60e964c6 100644 --- a/src/lib/pb/enroll.proto +++ b/src/lib/pb/enroll.proto @@ -24,7 +24,6 @@ syntax = "proto2"; import "ipcp_config.proto"; message enroll_req_msg { - /* TODO authentication */ required bytes id = 1; } diff --git a/src/lib/pb/ipcp_config.proto b/src/lib/pb/ipcp_config.proto index 28528b0c..1308c6d1 100644 --- a/src/lib/pb/ipcp_config.proto +++ b/src/lib/pb/ipcp_config.proto @@ -24,17 +24,45 @@ syntax = "proto2"; import "model.proto"; + +message ls_config_msg { + required uint32 pol = 1; + required uint32 t_recalc = 2; + required uint32 t_update = 3; + required uint32 t_timeo = 4; +} + +message routing_config_msg { + required uint32 pol = 1; + optional ls_config_msg ls = 2; +} + message dt_config_msg { - required uint32 addr_size = 1; - required uint32 eid_size = 2; - required uint32 max_ttl = 3; - required uint32 routing_type = 4; + required uint32 addr_size = 1; + required uint32 eid_size = 2; + required uint32 max_ttl = 3; + required routing_config_msg routing = 4; +} + +message dir_dht_config_msg { + required uint32 alpha = 1; + required uint32 k = 2; + required uint32 t_expire = 3; + required uint32 t_refresh = 4; + required uint32 t_replicate = 5; + required uint64 peer = 6; +} + +message dir_config_msg { + required uint32 pol = 1; + optional dir_dht_config_msg dht = 2; } message uni_config_msg { required dt_config_msg dt = 1; - required uint32 addr_auth_type = 2; - required uint32 cong_avoid = 3; + required dir_config_msg dir = 2; + required uint32 addr_auth_type = 3; + required uint32 cong_avoid = 4; } message eth_config_msg { @@ -42,16 +70,24 @@ message eth_config_msg { required uint32 ethertype = 2; } -message udp_config_msg { +message udp4_config_msg { required uint32 ip_addr = 1; required uint32 port = 2; required uint32 dns_addr = 3; /* set to 0 if unused */ } +message udp6_config_msg { + required bytes ip_addr = 1; + required uint32 port = 2; + required bytes dns_addr = 3; /* set to NULL if unused */ +} + + message ipcp_config_msg { required layer_info_msg layer_info = 1; required uint32 ipcp_type = 2; optional uni_config_msg unicast = 3; - optional udp_config_msg udp = 4; - optional eth_config_msg eth = 5; + optional udp4_config_msg udp4 = 4; + optional udp6_config_msg udp6 = 5; + optional eth_config_msg eth = 6; } diff --git a/src/lib/pb/irm.proto b/src/lib/pb/irm.proto index da3bd982..75f5f350 100644 --- a/src/lib/pb/irm.proto +++ b/src/lib/pb/irm.proto @@ -75,18 +75,19 @@ message irm_msg { optional string name = 4; optional flow_info_msg flow_info = 5; optional ipcp_info_msg ipcp_info = 6; - optional string layer = 7; - repeated string exec = 8; - optional sint32 response = 9; - optional string dst = 10; - optional bytes hash = 11; - optional sint32 flow_id = 12; - optional qosspec_msg qosspec = 13; - optional ipcp_config_msg conf = 14; - optional uint32 opts = 15; - repeated ipcp_list_msg ipcps = 16; - repeated name_info_msg names = 17; - optional timespec_msg timeo = 18; + optional name_info_msg name_info = 7; + optional string layer = 8; + repeated string exec = 9; + optional sint32 response = 10; + optional string dst = 11; + optional bytes hash = 12; + optional sint32 flow_id = 13; + optional qosspec_msg qosspec = 14; + optional ipcp_config_msg conf = 15; + optional uint32 opts = 16; + repeated ipcp_list_msg ipcps = 17; + repeated name_info_msg names = 18; + optional timespec_msg timeo = 19; optional sint32 mpl = 20; optional string comp = 21; optional bytes pk = 22; /* piggyback */ diff --git a/src/lib/pb/model.proto b/src/lib/pb/model.proto index f1e401f9..7b06e434 100644 --- a/src/lib/pb/model.proto +++ b/src/lib/pb/model.proto @@ -30,8 +30,7 @@ message qosspec_msg { required uint32 ber = 5; /* Bit error rate, ppb. */ required uint32 in_order = 6; /* In-order delivery. */ required uint32 max_gap = 7; /* In ms. */ - required uint32 cypher_s = 8; /* Crypto strength in bits. */ - required uint32 timeout = 9; /* Timeout in ms. */ + required uint32 timeout = 8; /* Timeout in ms. */ } message flow_info_msg { @@ -44,8 +43,12 @@ message flow_info_msg { } message name_info_msg { - required string name = 1; - required uint32 pol_lb = 2; + required string name = 1; + required uint32 pol_lb = 2; + required string skey = 3; + required string scrt = 4; + required string ckey = 5; + required string ccrt = 6; } message layer_info_msg { diff --git a/src/lib/protobuf.c b/src/lib/protobuf.c index b586168c..6df4e810 100644 --- a/src/lib/protobuf.c +++ b/src/lib/protobuf.c @@ -106,6 +106,66 @@ struct flow_info flow_info_msg_to_s(const flow_info_msg_t * msg) return s; } +name_info_msg_t * name_info_s_to_msg(const struct name_info * info) +{ + name_info_msg_t * msg; + + assert(info != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + name_info_msg__init(msg); + + msg->name = strdup(info->name); + if (msg->name == NULL) + goto fail_msg; + + msg->skey = strdup(info->s.key); + if (msg->skey == NULL) + goto fail_msg; + + msg->scrt = strdup(info->s.crt); + if (msg->scrt == NULL) + goto fail_msg; + + msg->ckey = strdup(info->c.key); + if (msg->skey == NULL) + goto fail_msg; + + msg->ccrt = strdup(info->c.crt); + if (msg->ccrt == NULL) + goto fail_msg; + + msg->pol_lb = info->pol_lb; + + return msg; + + fail_msg: + name_info_msg__free_unpacked(msg, NULL); + fail_malloc: + return NULL; +} + +struct name_info name_info_msg_to_s(const name_info_msg_t * msg) +{ + struct name_info s; + + assert(msg != NULL); + assert(strlen(msg->name) <= NAME_SIZE); + + strcpy(s.name, msg->name); + strcpy(s.s.key, msg->skey); + strcpy(s.s.crt, msg->scrt); + strcpy(s.c.key, msg->ckey); + strcpy(s.c.crt, msg->ccrt); + + s.pol_lb = msg->pol_lb; + + return s; +} + layer_info_msg_t * layer_info_s_to_msg(const struct layer_info * s) { layer_info_msg_t * msg; @@ -188,6 +248,95 @@ struct ipcp_info ipcp_info_msg_to_s(const ipcp_info_msg_t * msg) return s; } +ls_config_msg_t * ls_config_s_to_msg(const struct ls_config * s) +{ + ls_config_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + ls_config_msg__init(msg); + + msg->pol = s->pol; + msg->t_recalc = s->t_recalc; + msg->t_update = s->t_update; + msg->t_timeo = s->t_timeo; + + return msg; + + fail_malloc: + return NULL; +} + +struct ls_config ls_config_msg_to_s(const ls_config_msg_t * msg) +{ + struct ls_config s; + + assert(msg != NULL); + + s.pol = msg->pol; + s.t_recalc = msg->t_recalc; + s.t_update = msg->t_update; + s.t_timeo = msg->t_timeo; + + return s; +} + +routing_config_msg_t * routing_config_s_to_msg(const struct routing_config * s) +{ + routing_config_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + routing_config_msg__init(msg); + + switch (s->pol) { + case ROUTING_LINK_STATE: + msg->ls = ls_config_s_to_msg(&s->ls); + if (msg->ls == NULL) + goto fail_ls; + break; + default: + /* No checks here */ + break; + } + + msg->pol = s->pol; + + return msg; + + fail_ls: + routing_config_msg__free_unpacked(msg, NULL); + return NULL; +} + +struct routing_config routing_config_msg_to_s(const routing_config_msg_t * msg) +{ + struct routing_config s; + + assert(msg != NULL); + + switch (msg->pol) { + case ROUTING_LINK_STATE: + s.ls = ls_config_msg_to_s(msg->ls); + break; + default: + /* No checks here */ + break; + } + + s.pol = msg->pol; + + return s; +} + dt_config_msg_t * dt_config_s_to_msg(const struct dt_config * s) { dt_config_msg_t * msg; @@ -203,9 +352,14 @@ dt_config_msg_t * dt_config_s_to_msg(const struct dt_config * s) msg->addr_size = s->addr_size; msg->eid_size = s->eid_size; msg->max_ttl = s->max_ttl; - msg->routing_type = s->routing_type; + msg->routing = routing_config_s_to_msg(&s->routing); + if (msg->routing == NULL) + goto fail_routing; return msg; + fail_routing: + dt_config_msg__free_unpacked(msg, NULL); + return NULL; } struct dt_config dt_config_msg_to_s(const dt_config_msg_t * msg) @@ -217,11 +371,102 @@ struct dt_config dt_config_msg_to_s(const dt_config_msg_t * msg) s.addr_size = msg->addr_size; s.eid_size = msg->eid_size; s.max_ttl = msg->max_ttl; - s.routing_type = msg->routing_type; + s.routing = routing_config_msg_to_s(msg->routing); return s; } +struct dir_dht_config dir_dht_config_msg_to_s(const dir_dht_config_msg_t * msg) +{ + struct dir_dht_config s; + + assert(msg != NULL); + + s.params.alpha = msg->alpha; + s.params.k = msg->k; + s.params.t_expire = msg->t_expire; + s.params.t_refresh = msg->t_refresh; + s.params.t_replicate = msg->t_replicate; + s.peer = msg->peer; + + return s; +} + +dir_dht_config_msg_t * dir_dht_config_s_to_msg(const struct dir_dht_config * s) +{ + dir_dht_config_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + dir_dht_config_msg__init(msg); + + msg->alpha = s->params.alpha; + msg->k = s->params.k; + msg->t_expire = s->params.t_expire; + msg->t_refresh = s->params.t_refresh; + msg->t_replicate = s->params.t_replicate; + msg->peer = s->peer; + + return msg; +} + +struct dir_config dir_config_msg_to_s(const dir_config_msg_t * msg) +{ + struct dir_config s; + + assert(msg != NULL); + + switch (msg->pol) { + case DIR_DHT: + s.dht = dir_dht_config_msg_to_s(msg->dht); + break; + default: + /* No checks here */ + break; + } + + s.pol = msg->pol; + + return s; +} + +dir_config_msg_t * dir_config_s_to_msg(const struct dir_config * s) +{ + dir_config_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + dir_config_msg__init(msg); + + switch (s->pol) { + case DIR_DHT: + msg->dht = dir_dht_config_s_to_msg(&s->dht); + if (msg->dht == NULL) + goto fail_msg; + break; + default: + /* No checks here */ + break; + } + + msg->pol = s->pol; + + return msg; + + fail_msg: + dir_config_msg__free_unpacked(msg, NULL); + return NULL; +} + + uni_config_msg_t * uni_config_s_to_msg(const struct uni_config * s) { uni_config_msg_t * msg; @@ -238,6 +483,11 @@ uni_config_msg_t * uni_config_s_to_msg(const struct uni_config * s) if (msg->dt == NULL) goto fail_msg; + msg->dir = dir_config_s_to_msg(&s->dir); + if (msg->dir == NULL) + goto fail_msg; + + msg->addr_auth_type = s->addr_auth_type; msg->cong_avoid = s->cong_avoid; @@ -254,6 +504,7 @@ struct uni_config uni_config_msg_to_s(const uni_config_msg_t * msg) struct uni_config s; s.dt = dt_config_msg_to_s(msg->dt); + s.dir = dir_config_msg_to_s(msg->dir); s.addr_auth_type = msg->addr_auth_type; s.cong_avoid = msg->cong_avoid; @@ -261,9 +512,9 @@ struct uni_config uni_config_msg_to_s(const uni_config_msg_t * msg) return s; } -udp_config_msg_t * udp_config_s_to_msg(const struct udp_config * s) +udp4_config_msg_t * udp4_config_s_to_msg(const struct udp4_config * s) { - udp_config_msg_t * msg; + udp4_config_msg_t * msg; assert(s != NULL); @@ -271,24 +522,77 @@ udp_config_msg_t * udp_config_s_to_msg(const struct udp_config * s) if (msg == NULL) return NULL; - udp_config_msg__init(msg); + udp4_config_msg__init(msg); - msg->ip_addr = s->ip_addr; - msg->dns_addr = s->dns_addr; + msg->ip_addr = s->ip_addr.s_addr; + msg->dns_addr = s->dns_addr.s_addr; msg->port = s->port; return msg; } -struct udp_config udp_config_msg_to_s(const udp_config_msg_t * msg) +struct udp4_config udp4_config_msg_to_s(const udp4_config_msg_t * msg) +{ + struct udp4_config s; + + assert(msg != NULL); + + s.ip_addr.s_addr = msg->ip_addr; + s.dns_addr.s_addr = msg->dns_addr; + s.port = msg->port; + + return s; +} + +#define IN6_LEN sizeof(struct in6_addr) +udp6_config_msg_t * udp6_config_s_to_msg(const struct udp6_config * s) +{ + udp6_config_msg_t * msg; + + assert(s != NULL); + + msg = malloc(sizeof(*msg)); + if (msg == NULL) + goto fail_malloc; + + udp6_config_msg__init(msg); + + msg->ip_addr.data = malloc(IN6_LEN); + if (msg->ip_addr.data == NULL) + goto fail_msg; + + msg->ip_addr.len = IN6_LEN; + memcpy(msg->ip_addr.data, &s->ip_addr.s6_addr, IN6_LEN); + + msg->dns_addr.data = malloc(IN6_LEN); + if (msg->dns_addr.data == NULL) + goto fail_msg; + + msg->dns_addr.len = IN6_LEN; + memcpy(msg->dns_addr.data, &s->dns_addr.s6_addr, IN6_LEN); + + msg->port = s->port; + + return msg; + + fail_msg: + udp6_config_msg__free_unpacked(msg, NULL); + fail_malloc: + return NULL; +} + +struct udp6_config udp6_config_msg_to_s(const udp6_config_msg_t * msg) { - struct udp_config s; + struct udp6_config s; assert(msg != NULL); - s.ip_addr = msg->ip_addr; - s.dns_addr = msg->dns_addr; - s.port = msg->port; + assert(msg->ip_addr.len == IN6_LEN); + assert(msg->dns_addr.len == IN6_LEN); + + memcpy(&s.ip_addr.s6_addr, msg->ip_addr.data, IN6_LEN); + memcpy(&s.dns_addr.s6_addr, msg->dns_addr.data, IN6_LEN); + s.port = msg->port; return s; } @@ -362,9 +666,14 @@ ipcp_config_msg_t * ipcp_config_s_to_msg(const struct ipcp_config * s) if (msg->eth == NULL) goto fail_msg; break; - case IPCP_UDP: - msg->udp = udp_config_s_to_msg(&s->udp); - if (msg->udp == NULL) + case IPCP_UDP4: + msg->udp4 = udp4_config_s_to_msg(&s->udp4); + if (msg->udp4 == NULL) + goto fail_msg; + break; + case IPCP_UDP6: + msg->udp6 = udp6_config_s_to_msg(&s->udp6); + if (msg->udp6 == NULL) goto fail_msg; break; default: @@ -407,8 +716,11 @@ struct ipcp_config ipcp_config_msg_to_s(const ipcp_config_msg_t * msg) case IPCP_ETH_DIX: s.eth = eth_config_msg_to_s(msg->eth); break; - case IPCP_UDP: - s.udp = udp_config_msg_to_s(msg->udp); + case IPCP_UDP4: + s.udp4 = udp4_config_msg_to_s(msg->udp4); + break; + case IPCP_UDP6: + s.udp6 = udp6_config_msg_to_s(msg->udp6); break; case IPCP_BROADCAST: break; @@ -439,7 +751,6 @@ qosspec_msg_t * qos_spec_s_to_msg(const struct qos_spec * s) msg->ber = s->ber; msg->in_order = s->in_order; msg->max_gap = s->max_gap; - msg->cypher_s = s->cypher_s; msg->timeout = s->timeout; return msg; @@ -458,7 +769,6 @@ struct qos_spec qos_spec_msg_to_s(const qosspec_msg_t * msg) s.ber = msg->ber; s.in_order = msg->in_order; s.max_gap = msg->max_gap; - s.cypher_s = msg->cypher_s; s.timeout = msg->timeout; return s; diff --git a/src/lib/random.c b/src/lib/random.c index 09e0b844..2dc5f02f 100644 --- a/src/lib/random.c +++ b/src/lib/random.c @@ -24,22 +24,12 @@ #include <ouroboros/random.h> -#if defined(__APPLE__) /* Barf */ -#undef __OSX_AVAILABLE -#define __OSX_AVAILABLE(arg) -#undef __IOS_AVAILABLE -#define __IOS_AVAILABLE(arg) -#undef __TVOS_AVAILABLE -#define __TVOS_AVAILABLE(arg) -#undef __WATCHOS_AVAILABLE -#define __WATCHOS_AVAILABLE(arg) -#include <sys/random.h> +#if defined(__APPLE__) || defined(__FreeBSD__) +#include <stdlib.h> #elif defined(HAVE_SYS_RANDOM) #include <sys/random.h> #elif defined(HAVE_LIBGCRYPT) #include <gcrypt.h> -#elif defined(__FreeBSD__) -#include <stdlib.h> #elif defined(HAVE_OPENSSL_RNG) #include <openssl/rand.h> #include <limits.h> @@ -48,13 +38,11 @@ int random_buffer(void * buf, size_t len) { -#if defined(__APPLE__) - return getentropy(buf, len); -#elif defined(__FreeBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) arc4random_buf(buf, len); return 0; #elif defined(HAVE_SYS_RANDOM) - return getrandom(buf, len, GRND_NONBLOCK); /* glibc 2.25 */ + return getrandom(buf, len, GRND_NONBLOCK); #elif defined(HAVE_LIBGCRYPT) gcry_randomize(buf, len, GCRY_STRONG_RANDOM); return 0; diff --git a/src/lib/serdes-irm.c b/src/lib/serdes-irm.c index c4ba3053..3aea0617 100644 --- a/src/lib/serdes-irm.c +++ b/src/lib/serdes-irm.c @@ -166,12 +166,7 @@ int flow__irm_result_des(buffer_t * buf, *flow = flow_info_msg_to_s(msg->flow_info); - if (flow->qs.cypher_s > 0 && sk != NULL) { - if (msg->symmkey.data == NULL || msg->symmkey.len == 0) { - err = -ECRYPT; - goto fail; - } - + if (sk != NULL) { sk->len = msg->symmkey.len; sk->data = msg->symmkey.data; diff --git a/src/lib/sockets.c b/src/lib/sockets.c index 13219db0..5dfbcb5c 100644 --- a/src/lib/sockets.c +++ b/src/lib/sockets.c @@ -154,12 +154,13 @@ int send_recv_msg(buffer_t * msg) return len < 0 ? -1 : 0; } -char * ipcp_sock_path(pid_t pid) +static char * __sock_path(pid_t pid, + const char * prefix, + const char * suffix) { char * full_name = NULL; char * pid_string = NULL; size_t len = 0; - char * delim = "_"; len = n_digits(pid); pid_string = malloc(len + 1); @@ -168,9 +169,9 @@ char * ipcp_sock_path(pid_t pid) sprintf(pid_string, "%d", pid); - len += strlen(IPCP_SOCK_PATH_PREFIX); - len += strlen(delim); - len += strlen(SOCK_PATH_SUFFIX); + len += strlen(prefix); + len += strlen(pid_string); + len += strlen(suffix); full_name = malloc(len + 1); if (full_name == NULL) { @@ -178,12 +179,17 @@ char * ipcp_sock_path(pid_t pid) return NULL; } - strcpy(full_name, IPCP_SOCK_PATH_PREFIX); - strcat(full_name, delim); + strcpy(full_name, prefix); strcat(full_name, pid_string); - strcat(full_name, SOCK_PATH_SUFFIX); + strcat(full_name, suffix); free(pid_string); return full_name; } + +char * sock_path(pid_t pid, + const char * prefix) +{ + return __sock_path(pid, prefix, SOCK_PATH_SUFFIX); +} diff --git a/src/lib/tests/CMakeLists.txt b/src/lib/tests/CMakeLists.txt index 0e114548..2791dd0d 100644 --- a/src/lib/tests/CMakeLists.txt +++ b/src/lib/tests/CMakeLists.txt @@ -3,14 +3,18 @@ get_filename_component(PARENT_DIR ${PARENT_PATH} NAME) create_test_sourcelist(${PARENT_DIR}_tests test_suite.c # Add new tests here + auth_test.c bitmap_test.c btree_test.c crc32_test.c + crypt_test.c hash_test.c md5_test.c sha3_test.c shm_rbuff_test.c + sockets_test.c time_test.c + tpm_test.c ) add_executable(${PARENT_DIR}_test EXCLUDE_FROM_ALL ${${PARENT_DIR}_tests}) @@ -20,9 +24,16 @@ target_link_libraries(${PARENT_DIR}_test ouroboros-common) add_dependencies(check ${PARENT_DIR}_test) set(tests_to_run ${${PARENT_DIR}_tests}) -remove(tests_to_run test_suite.c) +if(CMAKE_VERSION VERSION_LESS "3.29.0") + remove(tests_to_run test_suite.c) +else () + list(POP_FRONT tests_to_run) +endif() foreach (test ${tests_to_run}) get_filename_component(test_name ${test} NAME_WE) add_test(${test_name} ${C_TEST_PATH}/${PARENT_DIR}_test ${test_name}) endforeach (test) + +set_property(TEST auth_test PROPERTY SKIP_RETURN_CODE 1) +set_property(TEST crypt_test PROPERTY SKIP_RETURN_CODE 1) diff --git a/src/lib/tests/auth_test.c b/src/lib/tests/auth_test.c new file mode 100644 index 00000000..ede294b8 --- /dev/null +++ b/src/lib/tests/auth_test.c @@ -0,0 +1,643 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Test of the authentication functions + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "config.h" + +#include <ouroboros/test.h> +#include <ouroboros/crypt.h> +#include <ouroboros/random.h> +#include <ouroboros/utils.h> + +#define TEST_MSG_SIZE 1500 + +/* +* Certificates created following the guide +* Building an openssl certificate authority +* on +* https://community.f5.com/kb/technicalarticles/ +*/ + +/* Root certificate for CA ca.unittest.o7s */ +static const char * root_ca_crt = \ +"-----BEGIN CERTIFICATE-----\n" +"MIICXTCCAgOgAwIBAgIURlENlCOy1OsA/AXFscPUQ2li8OYwCgYIKoZIzj0EAwIw\n" +"fDELMAkGA1UEBhMCQkUxDDAKBgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAK\n" +"BgNVBAoMA283czEVMBMGA1UECwwMdW5pdHRlc3QubzdzMRgwFgYDVQQDDA9jYS51\n" +"bml0dGVzdC5vN3MxEDAOBgkqhkiG9w0BCQEWASAwHhcNMjUwODAzMTg1MzE1WhcN\n" +"NDUwNzI5MTg1MzE1WjB8MQswCQYDVQQGEwJCRTEMMAoGA1UECAwDT1ZMMQ4wDAYD\n" +"VQQHDAVHaGVudDEMMAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3Mx\n" +"GDAWBgNVBAMMD2NhLnVuaXR0ZXN0Lm83czEQMA4GCSqGSIb3DQEJARYBIDBZMBMG\n" +"ByqGSM49AgEGCCqGSM49AwEHA0IABEPMseCScbd/d5TlHmyYVszn/YGVeNdUCnFR\n" +"naOr95WlTNo3MyKKBuoiEFwHhjPASgXr/VDVjJLSyM3JUPebAcGjYzBhMB0GA1Ud\n" +"DgQWBBQkxjMILHH6lZ+rnCMnD/63GO3y1zAfBgNVHSMEGDAWgBQkxjMILHH6lZ+r\n" +"nCMnD/63GO3y1zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAKBggq\n" +"hkjOPQQDAgNIADBFAiEA1jVJWW4idkCgAYv0m2LT9C33Dq42aLyRkJ+9YdzDqLwC\n" +"IHT6MS4I0k52YP/hxoqWVBbpOW79PKYMRLyXTk1r7+Fa\n" +"-----END CERTIFICATE-----\n"; + + +/* Certificate for intermediary im.unittest.o7s used for signing */ +static const char * intermediate_ca_crt = \ +"-----BEGIN CERTIFICATE-----\n" +"MIICbTCCAhOgAwIBAgICEAMwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCQkUxDDAK\n" +"BgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAKBgNVBAoMA283czEVMBMGA1UE\n" +"CwwMdW5pdHRlc3QubzdzMRgwFgYDVQQDDA9jYS51bml0dGVzdC5vN3MxEDAOBgkq\n" +"hkiG9w0BCQEWASAwHhcNMjUwODAzMTkwMjU3WhcNNDUwNzI5MTkwMjU3WjBaMQsw\n" +"CQYDVQQGEwJCRTEMMAoGA1UECAwDT1ZMMQwwCgYDVQQKDANvN3MxFTATBgNVBAsM\n" +"DHVuaXR0ZXN0Lm83czEYMBYGA1UEAwwPaW0udW5pdHRlc3QubzdzMFkwEwYHKoZI\n" +"zj0CAQYIKoZIzj0DAQcDQgAEdlra08XItIPtVl5veaq4UF6LIcBXj2mZFqKNEXFh\n" +"l9uAz6UAbIc+FUPNfom6dwKbg/AjQ82a100eh6K/jCY7eKOBpjCBozAdBgNVHQ4E\n" +"FgQUy8Go8BIO6i0lJ+mgBr9lvh2L0eswHwYDVR0jBBgwFoAUJMYzCCxx+pWfq5wj\n" +"Jw/+txjt8tcwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEQYD\n" +"VR0fBAowCDAGoASgAoYAMCoGCCsGAQUFBwEBBB4wHDAMBggrBgEFBQcwAoYAMAwG\n" +"CCsGAQUFBzABhgAwCgYIKoZIzj0EAwIDSAAwRQIhAN3ZYhqu6mVLGidmONsbANk5\n" +"rzT6aHJcmvj19OxMusaXAiBKy0gBFCri/GLizi4wZo09wf31yZMqfr8IrApvPaLw\n" +"qA==\n" +"-----END CERTIFICATE-----\n"; + +/* Server test-1.unittest.o7s private-public key pair */ +static const char * server_ec_pkp = \ +"-----BEGIN EC PRIVATE KEY-----\n" +"MHcCAQEEIA4/bcmquVvGrY4+TtfnFSy1SpXs896r5xJjGuD6NmGRoAoGCCqGSM49\n" +"AwEHoUQDQgAE4BSOhv36q4bCMLSkJaCvzwZ3pPy2M0YzRKFKeV48tG5eD+MBaTrT\n" +"eoHUcRfpz0EO/inq3FVDzEoAQ2NWpnz0kA==\n" +"-----END EC PRIVATE KEY-----\n"; + +/* Public key for the Private key */ +static const char * server_ec_pk = \ +"-----BEGIN PUBLIC KEY-----\n" +"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4BSOhv36q4bCMLSkJaCvzwZ3pPy2\n" +"M0YzRKFKeV48tG5eD+MBaTrTeoHUcRfpz0EO/inq3FVDzEoAQ2NWpnz0kA==\n" +"-----END PUBLIC KEY-----\n"; + +/* Valid signed server certificate for test-1.unittest.o7s */ +static const char * signed_server_crt = \ +"-----BEGIN CERTIFICATE-----\n" +"MIIDiTCCAy+gAwIBAgICEAUwCgYIKoZIzj0EAwIwWjELMAkGA1UEBhMCQkUxDDAK\n" +"BgNVBAgMA09WTDEMMAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3Mx\n" +"GDAWBgNVBAMMD2ltLnVuaXR0ZXN0Lm83czAeFw0yNTA4MDgxODQ4NTNaFw00NTA4\n" +"MDMxODQ4NTNaMG4xCzAJBgNVBAYTAkJFMQwwCgYDVQQIDANPVkwxDjAMBgNVBAcM\n" +"BUdoZW50MQwwCgYDVQQKDANvN3MxFTATBgNVBAsMDHVuaXR0ZXN0Lm83czEcMBoG\n" +"A1UEAwwTdGVzdC0xLnVuaXR0ZXN0Lm83czBZMBMGByqGSM49AgEGCCqGSM49AwEH\n" +"A0IABOAUjob9+quGwjC0pCWgr88Gd6T8tjNGM0ShSnlePLRuXg/jAWk603qB1HEX\n" +"6c9BDv4p6txVQ8xKAENjVqZ89JCjggHPMIIByzAJBgNVHRMEAjAAMBEGCWCGSAGG\n" +"+EIBAQQEAwIGQDA4BglghkgBhvhCAQ0EKxYpbzdzIHVuaXR0ZXN0IEdlbmVyYXRl\n" +"ZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFI+htsK0xxy6e1CqCyxn7mqi\n" +"wRrpMIGoBgNVHSMEgaAwgZ2AFMvBqPASDuotJSfpoAa/Zb4di9HroYGApH4wfDEL\n" +"MAkGA1UEBhMCQkUxDDAKBgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAKBgNV\n" +"BAoMA283czEVMBMGA1UECwwMdW5pdHRlc3QubzdzMRgwFgYDVQQDDA9jYS51bml0\n" +"dGVzdC5vN3MxEDAOBgkqhkiG9w0BCQEWASCCAhADMA4GA1UdDwEB/wQEAwIFoDAT\n" +"BgNVHSUEDDAKBggrBgEFBQcDATAoBgNVHR8EITAfMB2gG6AZhhdodHRwczovL291\n" +"cm9ib3Jvcy5yb2NrczBYBggrBgEFBQcBAQRMMEowIwYIKwYBBQUHMAKGF2h0dHBz\n" +"Oi8vb3Vyb2Jvcm9zLnJvY2tzMCMGCCsGAQUFBzABhhdodHRwczovL291cm9ib3Jv\n" +"cy5yb2NrczAKBggqhkjOPQQDAgNIADBFAiBZuw/Yb2pq925H7pEiOXr4fMo0wknz\n" +"ktkxoHAFbjQEPQIhAMInHI7lvRmS0IMw1wBF/WlUZWKvhyU/TeMIZfk/JGCS\n" +"-----END CERTIFICATE-----\n"; + +/* Self-signed by server test-1.unittest.o7s using its key */ +static const char * server_crt = \ +"-----BEGIN CERTIFICATE-----\n" +"MIIBfjCCASWgAwIBAgIUB5VYxp7i+sgYjvLiwfpf0W5NfqQwCgYIKoZIzj0EAwIw\n" +"HjEcMBoGA1UEAwwTdGVzdC0xLnVuaXR0ZXN0Lm83czAeFw0yNTA4MDMxOTI4MzVa\n" +"Fw00NTA3MjkxOTI4MzVaMB4xHDAaBgNVBAMME3Rlc3QtMS51bml0dGVzdC5vN3Mw\n" +"WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATgFI6G/fqrhsIwtKQloK/PBnek/LYz\n" +"RjNEoUp5Xjy0bl4P4wFpOtN6gdRxF+nPQQ7+KercVUPMSgBDY1amfPSQo0EwPzAe\n" +"BgNVHREEFzAVghN0ZXN0LTEudW5pdHRlc3QubzdzMB0GA1UdDgQWBBSPobbCtMcc\n" +"untQqgssZ+5qosEa6TAKBggqhkjOPQQDAgNHADBEAiAoFC/rqgrRXmMUx4y5cPbv\n" +"jOKpoL3FpehRgGkPatmL/QIgMRHc2TSGo6q1SG22Xt1dHAIBsaN2AlSfhjKULMH5\n" +"gRo=\n" +"-----END CERTIFICATE-----\n"; + +static int test_auth_create_destroy_ctx(void) +{ + struct auth_ctx * ctx; + + TEST_START(); + + ctx = auth_create_ctx(); + if (ctx == NULL) { + printf("Failed to create auth context.\n"); + goto fail_create; + } + + auth_destroy_ctx(ctx); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_create: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_load_free_crt(void) +{ + void * crt; + + TEST_START(); + + if (crypt_load_crt_str(root_ca_crt, &crt) < 0) { + printf("Failed to load certificate string.\n"); + goto fail_load; + } + + crypt_free_crt(crt); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_load: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_crypt_get_pubkey_crt(void) +{ + void * pk; + void * crt; + + TEST_START(); + + if (crypt_load_crt_str(signed_server_crt, &crt) < 0) { + printf("Failed to load server certificate from string.\n"); + goto fail_load; + } + + if (crypt_get_pubkey_crt(crt, &pk) < 0) { + printf("Failed to get public key from certificate.\n"); + goto fail_get_pubkey; + } + + crypt_free_key(pk); + crypt_free_crt(crt); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_get_pubkey: + crypt_free_crt(crt); + fail_load: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_check_crt_name(void) +{ + void * crt; + + TEST_START(); + + if (crypt_load_crt_str(signed_server_crt, &crt) < 0) { + printf("Failed to load certificate from string.\n"); + goto fail_load; + } + + if (crypt_check_crt_name(crt, "test-1.unittest.o7s") < 0) { + printf("Failed to verify correct name.\n"); + goto fail_check; + } + + if (crypt_check_crt_name(crt, "bogus.name") == 0) { + printf("Failed to detect incorrect name.\n"); + goto fail_check; + } + + crypt_free_crt(crt); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_check: + crypt_free_crt(crt); + fail_load: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_load_free_privkey(void) +{ + void * key; + + TEST_START(); + + if (crypt_load_privkey_str(server_ec_pkp, &key) < 0) { + printf("Failed to load server key pair from string.\n"); + goto fail_load; + } + + crypt_free_key(key); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_load: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_load_free_pubkey(void) +{ + void * key; + + TEST_START(); + + if (crypt_load_pubkey_str(server_ec_pk, &key) < 0) { + printf("Failed to load server public key from string.\n"); + goto fail_load; + } + + crypt_free_key(key); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_load: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_crypt_check_pubkey_crt(void) +{ + void * pk; + void * crt_pk; + void * crt; + + TEST_START(); + + if (crypt_load_crt_str(signed_server_crt, &crt) < 0) { + printf("Failed to load public certificate from string.\n"); + goto fail_crt; + } + + if (crypt_load_pubkey_str(server_ec_pk, &pk) < 0) { + printf("Failed to load public key from string.\n"); + goto fail_pubkey; + } + + if (crypt_get_pubkey_crt(crt, &crt_pk) < 0) { + printf("Failed to get public key from certificate.\n"); + goto fail_get_pubkey; + } + + if (crypt_cmp_key(pk, crt_pk) != 0) { + printf("Public keys do not match .\n"); + goto fail_check; + } + + + crypt_free_key(crt_pk); + crypt_free_key(pk); + crypt_free_crt(crt); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_check: + crypt_free_key(crt_pk); + fail_get_pubkey: + crypt_free_key(pk); + fail_pubkey: + crypt_free_crt(crt); + fail_crt: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_store_add(void) +{ + struct auth_ctx * ctx; + void * _root_ca_crt; + + TEST_START(); + + ctx = auth_create_ctx(); + if (ctx == NULL) { + printf("Failed to create auth context.\n"); + goto fail_create; + } + + if (crypt_load_crt_str(root_ca_crt, &_root_ca_crt) < 0) { + printf("Failed to load root crt from string.\n"); + goto fail_load; + } + + if (auth_add_crt_to_store(ctx, _root_ca_crt) < 0) { + printf("Failed to add root crt to auth store.\n"); + goto fail_add; + } + + crypt_free_crt(_root_ca_crt); + auth_destroy_ctx(ctx); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_add: + crypt_free_crt(_root_ca_crt); + fail_load: + crypt_free_crt(_root_ca_crt); + fail_create: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_verify_crt(void) +{ + struct auth_ctx * auth; + void * _server_crt; + void * _signed_server_crt; + void * _root_ca_crt; + void * _intermediate_ca_crt; + + TEST_START(); + + auth = auth_create_ctx(); + if (auth == NULL) { + printf("Failed to create auth context.\n"); + goto fail_create_ctx; + } + + if (crypt_load_crt_str(server_crt, &_server_crt) < 0) { + printf("Failed to load self-signed crt from string.\n"); + goto fail_load_server_crt; + } + + if (crypt_load_crt_str(signed_server_crt, &_signed_server_crt) < 0) { + printf("Failed to load signed crt from string.\n"); + goto fail_load_signed_server_crt; + } + + if (crypt_load_crt_str(root_ca_crt, &_root_ca_crt) < 0) { + printf("Failed to load root crt from string.\n"); + goto fail_load_root_ca_crt; + } + + if (crypt_load_crt_str(intermediate_ca_crt, &_intermediate_ca_crt) < 0) { + printf("Failed to load intermediate crt from string.\n"); + goto fail_load_intermediate_ca_crt; + } + + if (auth_add_crt_to_store(auth, _root_ca_crt) < 0) { + printf("Failed to add root ca crt to auth store.\n"); + goto fail_verify; + } + + if (auth_add_crt_to_store(auth, _intermediate_ca_crt) < 0) { + printf("Failed to add intermediate ca crt to auth store.\n"); + goto fail_verify; + } + + if (auth_verify_crt(auth, _signed_server_crt) < 0) { + printf("Failed to verify signed crt with ca crt.\n"); + goto fail_verify; + } + + if (auth_verify_crt(auth, _server_crt) == 0) { + printf("Failed to detect untrusted crt.\n"); + goto fail_verify; + } + + crypt_free_crt(_intermediate_ca_crt); + crypt_free_crt(_root_ca_crt); + crypt_free_crt(_signed_server_crt); + crypt_free_crt(_server_crt); + + auth_destroy_ctx(auth); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_verify: + crypt_free_crt(_intermediate_ca_crt); + fail_load_intermediate_ca_crt: + crypt_free_crt(_root_ca_crt); + fail_load_root_ca_crt: + crypt_free_crt(_signed_server_crt); + fail_load_signed_server_crt: + crypt_free_crt(_server_crt); + fail_load_server_crt: + auth_destroy_ctx(auth); + fail_create_ctx: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +int test_auth_sign(void) +{ + uint8_t buf[TEST_MSG_SIZE]; + void * pkp; + void * pk; + buffer_t msg; + buffer_t sig; + + TEST_START(); + + msg.data = buf; + msg.len = sizeof(buf); + + if (random_buffer(msg.data, msg.len) < 0) { + printf("Failed to generate random message.\n"); + goto fail_init; + } + + if (crypt_load_privkey_str(server_ec_pkp, &pkp) < 0) { + printf("Failed to load server key pair from string.\n"); + goto fail_init; + } + + if (crypt_load_pubkey_str(server_ec_pk, &pk) < 0) { + printf("Failed to load public key.\n"); + goto fail_pubkey; + } + + if (auth_sign(pkp, msg, &sig) < 0) { + printf("Failed to sign message.\n"); + goto fail_sign; + } + + if (auth_verify_sig(pk, msg, sig) < 0) { + printf("Failed to verify signature.\n"); + goto fail_verify; + } + + freebuf(sig); + + crypt_free_key(pk); + crypt_free_key(pkp); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_verify: + freebuf(sig); + fail_sign: + crypt_free_key(pk); + fail_pubkey: + crypt_free_key(pkp); + fail_init: + return TEST_RC_FAIL; +} + +int test_auth_bad_signature(void) +{ + uint8_t buf[TEST_MSG_SIZE]; + void * pkp; + void * pk; + buffer_t msg; + buffer_t sig; + buffer_t fake_sig; + + TEST_START(); + + msg.data = buf; + msg.len = sizeof(buf); + + if (random_buffer(msg.data, msg.len) < 0) { + printf("Failed to generate random message.\n"); + goto fail_init; + } + + if (crypt_load_privkey_str(server_ec_pkp, &pkp) < 0) { + printf("Failed to load server key pair from string.\n"); + goto fail_init; + } + + if (crypt_load_pubkey_str(server_ec_pk, &pk) < 0) { + printf("Failed to load public key.\n"); + goto fail_pubkey; + } + + if (auth_sign(pkp, msg, &sig) < 0) { + printf("Failed to sign message.\n"); + goto fail_sign; + } + + fake_sig.data = malloc(sig.len); + if (fake_sig.data == NULL) { + printf("Failed to allocate memory for fake signature.\n"); + goto fail_malloc; + } + + fake_sig.len = sig.len; + if (random_buffer(fake_sig.data, fake_sig.len) < 0) { + printf("Failed to generate random fake signature.\n"); + goto fail_malloc; + } + + if (auth_verify_sig(pk, msg, fake_sig) == 0) { + printf("Failed to detect bad signature.\n"); + goto fail_verify; + } + + freebuf(fake_sig); + freebuf(sig); + + crypt_free_key(pk); + crypt_free_key(pkp); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_verify: + freebuf(fake_sig); + fail_malloc: + freebuf(sig); + fail_sign: + crypt_free_key(pk); + fail_pubkey: + crypt_free_key(pkp); + fail_init: + return TEST_RC_FAIL; +} + +int test_crt_str(void) +{ + char str[2295]; + void * crt; + + TEST_START(); + + if (crypt_load_crt_str(signed_server_crt, &crt) < 0) { + printf("Failed to load certificate from string.\n"); + goto fail_load; + } + + if (crypt_crt_str(crt, str) < 0) { + printf("Failed to convert certificate to string.\n"); + goto fail_to_str; + } + + printf("Certificate string:\n%s\n", str); + + crypt_free_crt(crt); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + + fail_to_str: + crypt_free_crt(crt); + fail_load: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +int auth_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_auth_create_destroy_ctx(); +#ifdef HAVE_OPENSSL + ret |= test_load_free_crt(); + ret |= test_check_crt_name(); + ret |= test_crypt_get_pubkey_crt(); + ret |= test_load_free_privkey(); + ret |= test_load_free_pubkey(); + ret |= test_crypt_check_pubkey_crt(); + ret |= test_store_add(); + ret |= test_verify_crt(); + ret |= test_auth_sign(); + ret |= test_auth_bad_signature(); + ret |= test_crt_str(); +#else + (void) test_load_free_crt; + (void) test_check_crt_name; + (void) test_crypt_get_pubkey_crt; + (void) test_load_free_privkey; + (void) test_load_free_pubkey; + (void) test_crypt_check_pubkey_crt; + (void) test_store_add; + (void) test_verify_crt; + (void) test_auth_sign; + (void) test_auth_bad_signature; + (void) test_crt_str; + + ret = TEST_RC_SKIP; +#endif + return ret; +} diff --git a/src/lib/tests/crypt_test.c b/src/lib/tests/crypt_test.c new file mode 100644 index 00000000..e7a09e8f --- /dev/null +++ b/src/lib/tests/crypt_test.c @@ -0,0 +1,258 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Test of the cryptography functions + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#include "config.h" + +#include <ouroboros/test.h> +#include <ouroboros/crypt.h> +#include <ouroboros/random.h> +#include <ouroboros/utils.h> + +#define TEST_PACKET_SIZE 1500 + +static int test_crypt_create_destroy(void) +{ + struct crypt_ctx * ctx; + + TEST_START(); + + ctx = crypt_create_ctx(NULL); + if (ctx == NULL) { + printf("Failed to initialize cryptography.\n"); + goto fail; + } + + crypt_destroy_ctx(ctx); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_crypt_create_destroy_with_key(void) +{ + struct crypt_ctx * ctx; + uint8_t key[SYMMKEYSZ]; + + TEST_START(); + + memset(key, 0, sizeof(key)); + + ctx = crypt_create_ctx(key); + if (ctx == NULL) { + printf("Failed to initialize cryptography.\n"); + goto fail; + } + + crypt_destroy_ctx(ctx); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_crypt_dh_pkp_create_destroy(void) +{ + void * pkp; + uint8_t buf[MSGBUFSZ]; + + TEST_START(); + + if (crypt_dh_pkp_create(&pkp, buf) < 0) { + printf("Failed to create DH PKP."); + goto fail; + } + + crypt_dh_pkp_destroy(pkp); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_crypt_dh_derive(void) +{ + void * pkp1; + void * pkp2; + buffer_t pk1; + buffer_t pk2; + ssize_t len; + uint8_t buf1[MSGBUFSZ]; + uint8_t buf2[MSGBUFSZ]; + uint8_t s1[SYMMKEYSZ]; + uint8_t s2[SYMMKEYSZ]; + + TEST_START(); + + len = crypt_dh_pkp_create(&pkp1, buf1); + if (len < 0) { + printf("Failed to create first key pair."); + goto fail_pkp1; + } + + pk1.len = (size_t) len; + pk1.data = buf1; + + len = crypt_dh_pkp_create(&pkp2, buf2); + if (len < 0) { + printf("Failed to create second key pair."); + goto fail_pkp2; + } + + pk2.len = (size_t) len; + pk2.data = buf2; + + if (crypt_dh_derive(pkp1, pk2, s1) < 0) { + printf("Failed to derive first key."); + goto fail; + } + + if (crypt_dh_derive(pkp2, pk1, s2) < 0) { + printf("Failed to derive second key."); + goto fail; + } + + if (memcmp(s1, s2, SYMMKEYSZ) != 0) { + printf("Derived keys do not match."); + goto fail; + } + + crypt_dh_pkp_destroy(pkp2); + crypt_dh_pkp_destroy(pkp1); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + crypt_dh_pkp_destroy(pkp2); + fail_pkp2: + crypt_dh_pkp_destroy(pkp1); + fail_pkp1: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +int test_crypt_encrypt_decrypt(void) +{ + uint8_t pkt[TEST_PACKET_SIZE]; + uint8_t key[SYMMKEYSZ]; + struct crypt_ctx * ctx; + buffer_t in; + buffer_t out; + buffer_t out2; + + TEST_START(); + + if (random_buffer(key, sizeof(key)) < 0) { + printf("Failed to generate random key.\n"); + goto fail_init; + } + + if (random_buffer(pkt, sizeof(pkt)) < 0) { + printf("Failed to generate random data.\n"); + goto fail_init; + } + + ctx = crypt_create_ctx(key); + if (ctx == NULL) { + printf("Failed to initialize cryptography.\n"); + goto fail_init; + } + + in.len = sizeof(pkt); + in.data = pkt; + + if (crypt_encrypt(ctx, in, &out) < 0) { + printf("Encryption failed.\n"); + goto fail_encrypt; + } + + if (out.len < in.len) { + printf("Encryption returned too little data.\n"); + goto fail_encrypt; + } + + if (crypt_decrypt(ctx, out, &out2) < 0) { + printf("Decryption failed.\n"); + goto fail_decrypt; + } + + if (out2.len != in.len) { + printf("Decrypted data length does not match original.\n"); + goto fail_chk; + } + + if (memcmp(in.data, out2.data, in.len) != 0) { + printf("Decrypted data does not match original.\n"); + goto fail_chk; + } + + crypt_destroy_ctx(ctx); + freebuf(out2); + freebuf(out); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_chk: + freebuf(out2); + fail_decrypt: + freebuf(out); + fail_encrypt: + crypt_destroy_ctx(ctx); + fail_init: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +int crypt_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_crypt_create_destroy(); + ret |= test_crypt_create_destroy_with_key(); +#ifdef HAVE_OPENSSL + ret |= test_crypt_dh_pkp_create_destroy(); + ret |= test_crypt_dh_derive(); + ret |= test_crypt_encrypt_decrypt(); +#else + (void) test_crypt_dh_pkp_create_destroy; + (void) test_crypt_dh_derive; + (void) test_crypt_encrypt_decrypt; + + ret = TEST_RC_SKIP; +#endif + return ret; +} diff --git a/src/lib/tests/sockets_test.c b/src/lib/tests/sockets_test.c new file mode 100644 index 00000000..bbf2323b --- /dev/null +++ b/src/lib/tests/sockets_test.c @@ -0,0 +1,98 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Tests for socket.c + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + +#define _POSIX_C_SOURCE 200112L + +#include <ouroboros/sockets.h> +#include <ouroboros/test.h> + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#define TEST_PID 1234 +#define TEST_PID_STR "1234" +#define TEST_SERVER_PATH "/tmp/test.sock" +#define TEST_SERVER_PREFIX "/tmp/ouroboros/test." +#define TEST_SOCK_PATH_PREFIX "var/run/ouroboros/test." + +static int test_sock_path(void) +{ + char * path; + char * exp = TEST_SOCK_PATH_PREFIX TEST_PID_STR SOCK_PATH_SUFFIX; + + TEST_START(); + + path = sock_path(TEST_PID, TEST_SOCK_PATH_PREFIX); + if (path == NULL) { + printf("Path is NULL.\n"); + goto fail_path; + } + + if (strcmp(path, exp) != 0) { + printf("Expected path '%s', got '%s'.\n", exp, path); + goto fail_cmp; + } + + free(path); + + TEST_SUCCESS(); + return TEST_RC_SUCCESS; + fail_cmp: + free(path); + fail_path: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_server_socket_open(void) +{ + int sockfd; + + TEST_START(); + + sockfd = server_socket_open(TEST_SERVER_PATH); + if (sockfd < 0) { + printf("Failed to open server socket.\n"); + goto fail_sock; + } + + close(sockfd); + + unlink(TEST_SERVER_PATH); + + TEST_SUCCESS(); + return TEST_RC_SUCCESS; + fail_sock: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +int sockets_test(void) +{ + int ret = 0; + + ret |= test_sock_path(); + ret |= test_server_socket_open(); + + return ret; +} diff --git a/src/lib/tests/time_test.c b/src/lib/tests/time_test.c index 65f896bb..2b75b873 100644 --- a/src/lib/tests/time_test.c +++ b/src/lib/tests/time_test.c @@ -22,143 +22,508 @@ #define _POSIX_C_SOURCE 200809L +#include <ouroboros/test.h> #include <ouroboros/time.h> #include <stdio.h> -static void ts_print(struct timespec * s) +static int ts_check(struct timespec * s, + time_t sec, + time_t nsec) { - printf("timespec is %zd:%ld.\n", (ssize_t) s->tv_sec, s->tv_nsec); + return s->tv_sec == sec && s->tv_nsec == nsec; } -static void tv_print(struct timeval * v) +static int tv_check(struct timeval * v, + time_t sec, + time_t usec) { - printf("timeval is %zd:%zu.\n", (ssize_t) v->tv_sec, (size_t) v->tv_usec); + return v->tv_sec == sec && v->tv_usec == usec; } -static void ts_init(struct timespec * s, - time_t sec, - time_t nsec) + +static int test_time_ts_init(void) { - s->tv_sec = sec; - s->tv_nsec = nsec; + struct timespec s = TIMESPEC_INIT_S (100); + struct timespec ms = TIMESPEC_INIT_MS(100); + struct timespec us = TIMESPEC_INIT_US(100); + struct timespec ns = TIMESPEC_INIT_NS(100); + + TEST_START(); + + if (!ts_check(&s, 100, 0)) { + printf("timespec_init_s failed.\n"); + goto fail; + } + + if (!ts_check(&ms, 0, 100 * MILLION)) { + printf("timespec_init_ms failed.\n"); + goto fail; + } + + if (!ts_check(&us, 0, 100* 1000L)) { + printf("timespec_init_us failed.\n"); + goto fail; + } + + if (!ts_check(&ns, 0, 100)) { + printf("timespec_init_ns failed.\n"); + goto fail; + } + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + TEST_FAIL(); + return TEST_RC_FAIL; } -static void tv_init(struct timeval * v, - time_t sec, - time_t usec) +static int test_time_tv_init(void) { - v->tv_sec = sec; - v->tv_usec = usec; + struct timeval s = TIMEVAL_INIT_S (100); + struct timeval ms = TIMEVAL_INIT_MS(100); + struct timeval us = TIMEVAL_INIT_US(100); + + TEST_START(); + + if (!tv_check(&s, 100, 0)) { + printf("timeval_init_s failed.\n"); + goto fail; + } + + if (!tv_check(&ms, 0, 100 * 1000L)) { + printf("timeval_init_ms failed.\n"); + goto fail; + } + + if (!tv_check(&us, 0, 100)) { + printf("timeval_init_us failed.\n"); + goto fail; + } + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + TEST_FAIL(); + return TEST_RC_FAIL; } -static int ts_check(struct timespec * s, - time_t sec, - time_t nsec) +static int test_ts_diff(void) { - return s->tv_sec == sec && s->tv_nsec == nsec; + struct timespec s0 = TIMESPEC_INIT_S (100); + struct timespec s1 = TIMESPEC_INIT_S (200); + struct timespec ms0 = TIMESPEC_INIT_MS(100); + struct timespec ms1 = TIMESPEC_INIT_MS(200); + struct timespec us0 = TIMESPEC_INIT_US(100); + struct timespec us1 = TIMESPEC_INIT_US(200); + struct timespec ns0 = TIMESPEC_INIT_NS(100); + struct timespec ns1 = TIMESPEC_INIT_NS(200); + struct timespec res; + + TEST_START(); + + ts_diff(&s0, &s1, &res); + if (!ts_check(&res, -100, 0)) { + printf("timespec_diff failed at s0 - s1.\n"); + goto fail; + } + + ts_diff(&s1, &s0, &res); + if (!ts_check(&res, 100, 0)) { + printf("timespec_diff failed at s1 - s0.\n"); + goto fail; + } + + ts_diff(&ms0, &ms1, &res); + if (!ts_check(&res, -1, 900 * MILLION)) { + printf("timespec_diff failed at ms0 - ms1.\n"); + goto fail; + } + + ts_diff(&ms1, &ms0, &res); + if (!ts_check(&res, 0, 100 * MILLION)) { + printf("timespec_diff failed at ms1 - ms0.\n"); + goto fail; + } + + ts_diff(&us0, &us1, &res); + if (!ts_check(&res, -1, 999900 * 1000L)) { + printf("timespec_diff failed at us0 - us1.\n"); + goto fail; + } + + ts_diff(&us1, &us0, &res); + if (!ts_check(&res, 0, 100 * 1000L)) { + printf("timespec_diff failed at us1 - us0.\n"); + goto fail; + } + + ts_diff(&ns0, &ns1, &res); + if (!ts_check(&res, -1, 999999900)) { + printf("timespec_diff failed at ns0 - ns1.\n"); + goto fail; + } + + ts_diff(&ns1, &ns0, &res); + if (!ts_check(&res, 0, 100)) { + printf("timespec_diff failed at ns1 - ns0.\n"); + goto fail; + } + + ts_diff(&s0, &ms0, &res); + if (!ts_check(&res, 99, 900 * MILLION)) { + printf("timespec_diff failed at s0 - ms0.\n"); + goto fail; + } + + ts_diff(&s0, &us0, &res); + if (!ts_check(&res, 99, 999900 * 1000L)) { + printf("timespec_diff failed at s0 - us0.\n"); + goto fail; + } + + ts_diff(&s0, &ns0, &res); + if (!ts_check(&res, 99, 999999900)) { + printf("timespec_diff failed at s0 - ns0.\n"); + goto fail; + } + + ts_diff(&ms0, &us0, &res); + if (!ts_check(&res, 0, 99900 * 1000L)) { + printf("timespec_diff failed at ms0 - us0.\n"); + goto fail; + } + + ts_diff(&ms0, &ns0, &res); + if (!ts_check(&res, 0, 99999900)) { + printf("timespec_diff failed at ms0 - ns0.\n"); + goto fail; + } + + ts_diff(&us0, &ns0, &res); + if (!ts_check(&res, 0, 99900)) { + printf("timespec_diff failed at us0 - ns0.\n"); + goto fail; + } + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + TEST_FAIL(); + return TEST_RC_FAIL; } -static int tv_check(struct timeval * v, - time_t sec, - time_t usec) +static int test_tv_diff(void) { - return v->tv_sec == sec && v->tv_usec == usec; + struct timeval s0 = TIMEVAL_INIT_S (100); + struct timeval s1 = TIMEVAL_INIT_S (200); + struct timeval ms0 = TIMEVAL_INIT_MS(100); + struct timeval ms1 = TIMEVAL_INIT_MS(200); + struct timeval us0 = TIMEVAL_INIT_US(100); + struct timeval us1 = TIMEVAL_INIT_US(200); + struct timeval res; + + TEST_START(); + + tv_diff(&s0, &s1, &res); + if (!tv_check(&res, -100, 0)) { + printf("timeval_diff failed at s0 - s1.\n"); + goto fail; + } + + tv_diff(&s1, &s0, &res); + if (!tv_check(&res, 100, 0)) { + printf("timeval_diff failed at s1 - s0.\n"); + goto fail; + } + + tv_diff(&ms0, &ms1, &res); + if (!tv_check(&res, -1, 900 * 1000L)) { + printf("timeval_diff failed at ms0 - ms1.\n"); + goto fail; + } + + tv_diff(&ms1, &ms0, &res); + if (!tv_check(&res, 0, 100 * 1000L)) { + printf("timeval_diff failed at ms1 - ms0.\n"); + goto fail; + } + + tv_diff(&us0, &us1, &res); + if (!tv_check(&res, -1, 999900)) { + printf("timeval_diff failed at us0 - us1.\n"); + goto fail; + } + + tv_diff(&us1, &us0, &res); + if (!tv_check(&res, 0, 100)) { + printf("timeval_diff failed at us1 - us0.\n"); + goto fail; + } + + tv_diff(&s0, &ms0, &res); + if (!tv_check(&res, 99, 900 * 1000L)) { + printf("timeval_diff failed at s0 - ms0.\n"); + goto fail; + } + + tv_diff(&s0, &us0, &res); + if (!tv_check(&res, 99, 999900)) { + printf("timeval_diff failed at s0 - us0.\n"); + goto fail; + } + + tv_diff(&ms0, &us0, &res); + if (!tv_check(&res, 0, 99900)) { + printf("timeval_diff failed at ms0 - us0.\n"); + goto fail; + } + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + TEST_FAIL(); + return TEST_RC_FAIL; } -int time_test(int argc, - char ** argv) +static int test_ts_diff_time(void) { - struct timespec s0; - struct timespec s1; - struct timespec s2; + struct timespec s0 = TIMESPEC_INIT_S (100); + struct timespec s1 = TIMESPEC_INIT_S (200); + struct timespec ms0 = TIMESPEC_INIT_MS(100); + struct timespec ms1 = TIMESPEC_INIT_MS(200); + struct timespec us0 = TIMESPEC_INIT_US(100); + struct timespec us1 = TIMESPEC_INIT_US(200); + struct timespec ns0 = TIMESPEC_INIT_NS(100); + struct timespec ns1 = TIMESPEC_INIT_NS(200); - struct timeval v0; - struct timeval v1; - struct timeval v2; + TEST_START(); - (void) argc; - (void) argv; + if (ts_diff_ms(&s0, &s1) != -100 * 1000L) { + printf("timespec_diff_ms failed at s0 - s1.\n"); + goto fail; + } + + if (ts_diff_ms(&s1, &s0) != 100 * 1000L) { + printf("timespec_diff_ms failed at s1 - s0.\n"); + goto fail; + } + + if (ts_diff_us(&s0, &s1) != -100 * MILLION) { + printf("timespec_diff_us failed at s1 - s0.\n"); + goto fail; + } + + if (ts_diff_us(&s1, &s0) != 100 * MILLION) { + printf("timespec_diff_us failed at s0 - s1.\n"); + goto fail; + } + + if (ts_diff_ns(&s0, &s1) != -100 * BILLION) { + printf("timespec_diff_ns failed at s0 - s1.\n"); + goto fail; + } + + if (ts_diff_ns(&s1, &s0) != 100 * BILLION) { + printf("timespec_diff_ns failed at s1 - s0.\n"); + goto fail; + } + + if (ts_diff_ms(&ms0, &ms1) != -100) { + printf("timespec_diff_ms failed at ms0 - ms1.\n"); + goto fail; + } + + if (ts_diff_ms(&ms1, &ms0) != 100) { + printf("timespec_diff_ms failed at ms1 - ms0.\n"); + goto fail; + } - ts_init(&s0, 0, 0); - ts_init(&s1, 5, 0); + if (ts_diff_us(&ms0, &ms1) != -100 * 1000L) { + printf("timespec_diff_us failed at ms0 - ms1.\n"); + goto fail; + } - ts_add(&s0, &s1, &s2); - if (!ts_check(&s2, 5, 0)) { - printf("ts_add failed.\n"); - ts_print(&s2); - return -1; + if (ts_diff_us(&ms1, &ms0) != 100 * 1000L) { + printf("timespec_diff_us failed at ms1 - ms0.\n"); + goto fail; } - tv_init(&v0, 0, 0); - tv_init(&v1, 5, 0); + if (ts_diff_ns(&ms0, &ms1) != -100 * MILLION) { + printf("timespec_diff_ns failed at ms0 - ms1.\n"); + goto fail; + } - tv_add(&v0, &v1, &v2); - if (!tv_check(&v2, 5, 0)) { - printf("tv_add failed.\n"); - tv_print(&v2); - return -1; + if (ts_diff_ns(&ms1, &ms0) != 100 * MILLION) { + printf("timespec_diff_ns failed at ms1 - ms0.\n"); + goto fail; } - ts_init(&s0, 0, 500 * MILLION); - ts_init(&s1, 0, 600 * MILLION); + if (ts_diff_ms(&us0, &us1) != 0) { + printf("timespec_diff_ms failed at us0 - us1.\n"); + goto fail; + } - ts_add(&s0, &s1, &s2); - if (!ts_check(&s2, 1, 100 * MILLION)) { - printf("ts_add with nano overflow failed.\n"); - ts_print(&s2); - return -1; + if (ts_diff_ms(&us1, &us0) != 0) { + printf("timespec_diff_ms failed at us1 - us0.\n"); + goto fail; } - tv_init(&v0, 0, 500 * 1000); - tv_init(&v1, 0, 600 * 1000); + if (ts_diff_us(&us0, &us1) != -100) { + printf("timespec_diff_us failed at us0 - us1.\n"); + goto fail; + } - tv_add(&v0, &v1, &v2); - if (!tv_check(&v2, 1, 100 * 1000)) { - printf("tv_add with nano overflow failed.\n"); - tv_print(&v2); - return -1; + if (ts_diff_us(&us1, &us0) != 100) { + printf("timespec_diff_us failed at us1 - us0.\n"); + goto fail; } - ts_init(&s0, 0, 0); - ts_init(&s1, 5, 0); + if (ts_diff_ns(&us0, &us1) != -100 * 1000L) { + printf("timespec_diff_ns failed at us0 - us1.\n"); + goto fail; + } - ts_diff(&s0, &s1, &s2); - if (!ts_check(&s2, -5, 0)) { - printf("ts_diff failed.\n"); - ts_print(&s2); - return -1; + if (ts_diff_ns(&us1, &us0) != 100 * 1000L) { + printf("timespec_diff_ns failed at us1 - us0.\n"); + goto fail; } - tv_init(&v0, 0, 0); - tv_init(&v1, 5, 0); + if (ts_diff_ms(&ns0, &ns1) != 0) { + printf("timespec_diff_ms failed at ns0 - ns1.\n"); + goto fail; + } - tv_diff(&v0, &v1, &v2); - if (!tv_check(&v2, -5, 0)) { - printf("tv_diff failed.\n"); - tv_print(&v2); - return -1; + if (ts_diff_ms(&ns1, &ns0) != 0) { + printf("timespec_diff_ms failed at ns1 - ns0.\n"); + goto fail; } - ts_init(&s0, 0, 500 * MILLION); - ts_init(&s1, 0, 600 * MILLION); + if (ts_diff_us(&ns0, &ns1) != 0) { + printf("timespec_diff_us failed at ns0 - ns1.\n"); + goto fail; + } - ts_diff(&s0, &s1, &s2); - if (!ts_check(&s2, -1, 900 * MILLION)) { - printf("ts_diff with nano underflow failed.\n"); - ts_print(&s2); - return -1; + if (ts_diff_us(&ns1, &ns0) != 0) { + printf("timespec_diff_us failed at ns1 - ns0.\n"); + goto fail; } - tv_init(&v0, 0, 500 * 1000); - tv_init(&v1, 0, 600 * 1000); + if (ts_diff_ns(&ns0, &ns1) != -100) { + printf("timespec_diff_ns failed at ns0 - ns1.\n"); + goto fail; + } - tv_diff(&v0, &v1, &v2); - if (!tv_check(&v2, -1, 900 * 1000)) { - printf("tv_diff with nano underflow failed.\n"); - tv_print(&v2); - return -1; + if (ts_diff_ns(&ns1, &ns0) != 100) { + printf("timespec_diff_ns failed at ns1 - ns0.\n"); + goto fail; } - return 0; + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_tv_diff_time(void) +{ + struct timeval s0 = TIMEVAL_INIT_S (100); + struct timeval s1 = TIMEVAL_INIT_S (200); + struct timeval ms0 = TIMEVAL_INIT_MS(100); + struct timeval ms1 = TIMEVAL_INIT_MS(200); + struct timeval us0 = TIMEVAL_INIT_US(100); + struct timeval us1 = TIMEVAL_INIT_US(200); + + TEST_START(); + + if (tv_diff_ms(&s0, &s1) != -100 * 1000L) { + printf("timeval_diff_ms failed at s0 - s1.\n"); + goto fail; + } + + if (tv_diff_ms(&s1, &s0) != 100 * 1000L) { + printf("timeval_diff_ms failed at s1 - s0.\n"); + goto fail; + } + + if (tv_diff_us(&s0, &s1) != -100 * MILLION) { + printf("timeval_diff_us failed at s0 - s1.\n"); + goto fail; + } + + if (tv_diff_us(&s1, &s0) != 100 * MILLION) { + printf("timeval_diff_us failed at s1 - s0.\n"); + goto fail; + } + + if (tv_diff_ms(&ms0, &ms1) != -100) { + printf("timeval_diff_ms failed at ms0 - ms1.\n"); + goto fail; + } + + if (tv_diff_ms(&ms1, &ms0) != 100) { + printf("timeval_diff_ms failed at ms1 - ms0.\n"); + goto fail; + } + + if (tv_diff_us(&ms0, &ms1) != -100 * 1000L) { + printf("timeval_diff_us failed at ms0 - ms1.\n"); + goto fail; + } + + if (tv_diff_us(&ms1, &ms0) != 100 * 1000L) { + printf("timeval_diff_us failed at ms1 - ms0.\n"); + goto fail; + } + + if (tv_diff_ms(&us0, &us1) != 0) { + printf("timeval_diff_ms failed at us0 - us1.\n"); + goto fail; + } + + if (tv_diff_ms(&us1, &us0) != 0) { + printf("timeval_diff_ms failed at us1 - us0.\n"); + goto fail; + } + + if (tv_diff_us(&us0, &us1) != -100) { + printf("timeval_diff_us failed at us0 - us1.\n"); + goto fail; + } + + if (tv_diff_us(&us1, &us0) != 100) { + printf("timeval_diff_us failed at us1 - us0.\n"); + goto fail; + } + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +int time_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_time_ts_init(); + ret |= test_time_tv_init(); + ret |= test_ts_diff(); + ret |= test_tv_diff(); + ret |= test_ts_diff_time(); + ret |= test_tv_diff_time(); + + return ret; } diff --git a/src/lib/tests/tpm_test.c b/src/lib/tests/tpm_test.c new file mode 100644 index 00000000..98d4fab3 --- /dev/null +++ b/src/lib/tests/tpm_test.c @@ -0,0 +1,104 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2024 + * + * Tests for the threadpool manager + * + * Dimitri Staessens <dimitri@ouroboros.rocks> + * Sander Vrijders <sander@ouroboros.rocks> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., http://www.fsf.org/about/contact/. + */ + + +#include "tpm.c" + +#include <ouroboros/test.h> + +static void * test_func(void * o) +{ + (void) o; + + while(1) + sleep(1); + + return NULL; +} + +static int test_tpm_create_destroy(void) +{ + struct tpm *tpm; + + TEST_START(); + + tpm = tpm_create(2, 2, &test_func, NULL); + if (tpm == NULL) { + printf("Failed to initialize TPM.\n"); + goto fail; + } + + tpm_destroy(tpm); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +static int test_tpm_start_stop(void * (* fn)(void *), + void * o) +{ + struct tpm *tpm; + + TEST_START(); + + tpm = tpm_create(2, 2, fn, o); + if (tpm == NULL) { + printf("Failed to initialize TPM.\n"); + goto fail_create; + } + + if (tpm_start(tpm) < 0) { + printf("Failed to start TPM.\n"); + goto fail_start; + } + + tpm_stop(tpm); + + tpm_destroy(tpm); + + TEST_SUCCESS(); + + return TEST_RC_SUCCESS; + fail_start: + tpm_destroy(tpm); + fail_create: + TEST_FAIL(); + return TEST_RC_FAIL; +} + +int tpm_test(int argc, + char ** argv) +{ + int ret = 0; + + (void) argc; + (void) argv; + + ret |= test_tpm_create_destroy(); + ret |= test_tpm_start_stop(&test_func, NULL); + + return ret; +} diff --git a/src/lib/tpm.c b/src/lib/tpm.c index 0ef1fda8..7a17ef6b 100644 --- a/src/lib/tpm.c +++ b/src/lib/tpm.c @@ -26,21 +26,32 @@ #include <ouroboros/errno.h> #include <ouroboros/list.h> +#include <ouroboros/pthread.h> #include <ouroboros/time.h> #include <ouroboros/tpm.h> +#ifdef CONFIG_OUROBOROS_DEBUG +#define OUROBOROS_PREFIX "tpm" +#include <ouroboros/logs.h> +#endif + #include <assert.h> -#include <pthread.h> #include <stdlib.h> +#include <string.h> +#include <unistd.h> -#define TPM_TIMEOUT 1000 +#define TPM_TIMEOUT 1000 struct pthr_el { struct list_head next; bool kill; bool busy; - + bool wait; +#ifdef CONFIG_OUROBOROS_DEBUG + struct timespec start; + struct timespec last; +#endif pthread_t thr; }; @@ -63,16 +74,45 @@ struct tpm { enum tpm_state state; pthread_cond_t cond; - pthread_mutex_t lock; + pthread_mutex_t mtx; pthread_t mgr; }; +#ifdef CONFIG_OUROBOROS_DEBUG +#define BETWEEN(a, x, y) ((a) > (x) && (a) <= (y)) +static void tpm_debug_thread(struct pthr_el * e) +{ + struct timespec now; + time_t diff; + time_t intv; + + if (e->wait || !e->busy) + return; + + clock_gettime(CLOCK_REALTIME, &now); + + diff = ts_diff_ms(&now, &e->start) / 1000; + intv = ts_diff_ms(&now, &e->last) / 1000; + + (void) diff; /* Never read if both build options off (0) */ + (void) intv; /* Never read if report option off (0) */ + + if (BETWEEN(TPM_DEBUG_REPORT_INTERVAL, 0, intv)) { + log_dbg("Thread %d:%lx running for %ld s.\n", + getpid(),e->thr, diff); + e->last = now; + } + + if (BETWEEN(TPM_DEBUG_ABORT_TIMEOUT, 0, diff)) + assert(false); /* TODO: Grab a coffee and fire up GDB */ +} +#endif + static void tpm_join(struct tpm * tpm) { struct list_head * p; struct list_head * h; - list_for_each_safe(p, h, &tpm->pool) { struct pthr_el * e = list_entry(p, struct pthr_el, next); if (tpm->state != TPM_RUNNING) { @@ -86,15 +126,18 @@ static void tpm_join(struct tpm * tpm) list_for_each_safe(p, h, &tpm->pool) { struct pthr_el * e = list_entry(p, struct pthr_el, next); +#ifdef CONFIG_OUROBOROS_DEBUG + tpm_debug_thread(e); +#endif if (e->kill) { pthread_t thr = e->thr; list_del(&e->next); free(e); - pthread_mutex_unlock(&tpm->lock); + pthread_mutex_unlock(&tpm->mtx); pthread_join(thr, NULL); - pthread_mutex_lock(&tpm->lock); + pthread_mutex_lock(&tpm->mtx); } } } @@ -114,56 +157,59 @@ static void tpm_kill(struct tpm * tpm) } } -static void * tpmgr(void * o) +static int __tpm(struct tpm * tpm) { struct timespec dl; struct timespec to = TIMESPEC_INIT_MS(TPM_TIMEOUT); - struct tpm * tpm = (struct tpm *) o; - - while (true) { - clock_gettime(PTHREAD_COND_CLOCK, &dl); - ts_add(&dl, &to, &dl); - pthread_mutex_lock(&tpm->lock); + clock_gettime(PTHREAD_COND_CLOCK, &dl); + ts_add(&dl, &to, &dl); - if (tpm->state != TPM_RUNNING) { - tpm_join(tpm); - pthread_mutex_unlock(&tpm->lock); - break; - } + pthread_mutex_lock(&tpm->mtx); + if (tpm->state != TPM_RUNNING) { tpm_join(tpm); + pthread_mutex_unlock(&tpm->mtx); + return -1; + } - if (tpm->cur - tpm->wrk < tpm->min) { - size_t i; - for (i = 0; i < tpm->inc; ++i) { - struct pthr_el * e = malloc(sizeof(*e)); - if (e == NULL) - break; + tpm_join(tpm); - e->kill = false; - e->busy = false; + if (tpm->cur - tpm->wrk < tpm->min) { + size_t i; + for (i = 0; i < tpm->inc; ++i) { + struct pthr_el * e = malloc(sizeof(*e)); + if (e == NULL) + break; - if (pthread_create(&e->thr, NULL, - tpm->func, tpm->o)) { - free(e); - break; - } + memset(e, 0, sizeof(*e)); - list_add(&e->next, &tpm->pool); + if (pthread_create(&e->thr, NULL, tpm->func, tpm->o)) { + free(e); + break; } - tpm->cur += i; + list_add(&e->next, &tpm->pool); } - if (pthread_cond_timedwait(&tpm->cond, &tpm->lock, &dl) - == ETIMEDOUT) - if (tpm->cur - tpm->wrk > tpm->min) - tpm_kill(tpm); - - pthread_mutex_unlock(&tpm->lock); + tpm->cur += i; } + pthread_cleanup_push(__cleanup_mutex_unlock, &tpm->mtx); + + if (pthread_cond_timedwait(&tpm->cond, &tpm->mtx, &dl) == ETIMEDOUT) + if (tpm->cur - tpm->wrk > tpm->min) + tpm_kill(tpm); + + pthread_cleanup_pop(true); + + return 0; +} + +static void * tpmgr(void * o) +{ + while (__tpm((struct tpm *) o) == 0); + return (void *) 0; } @@ -175,11 +221,14 @@ struct tpm * tpm_create(size_t min, struct tpm * tpm; pthread_condattr_t cattr; + assert(func != NULL); + assert(inc > 0); + tpm = malloc(sizeof(*tpm)); if (tpm == NULL) goto fail_malloc; - if (pthread_mutex_init(&tpm->lock, NULL)) + if (pthread_mutex_init(&tpm->mtx, NULL)) goto fail_lock; if (pthread_condattr_init(&cattr)) @@ -208,7 +257,7 @@ struct tpm * tpm_create(size_t min, fail_cond: pthread_condattr_destroy(&cattr); fail_cattr: - pthread_mutex_destroy(&tpm->lock); + pthread_mutex_destroy(&tpm->mtx); fail_lock: free(tpm); fail_malloc: @@ -217,34 +266,39 @@ struct tpm * tpm_create(size_t min, int tpm_start(struct tpm * tpm) { - pthread_mutex_lock(&tpm->lock); + pthread_mutex_lock(&tpm->mtx); if (pthread_create(&tpm->mgr, NULL, tpmgr, tpm)) { - pthread_mutex_unlock(&tpm->lock); + pthread_mutex_unlock(&tpm->mtx); return -1; } tpm->state = TPM_RUNNING; - pthread_mutex_unlock(&tpm->lock); + pthread_mutex_unlock(&tpm->mtx); return 0; } void tpm_stop(struct tpm * tpm) { - pthread_mutex_lock(&tpm->lock); + pthread_mutex_lock(&tpm->mtx); + + if (tpm->state != TPM_RUNNING) { + pthread_mutex_unlock(&tpm->mtx); + return; + } tpm->state = TPM_NULL; - pthread_mutex_unlock(&tpm->lock); + pthread_mutex_unlock(&tpm->mtx); pthread_join(tpm->mgr, NULL); } void tpm_destroy(struct tpm * tpm) { - pthread_mutex_destroy(&tpm->lock); + pthread_mutex_destroy(&tpm->mtx); pthread_cond_destroy(&tpm->cond); free(tpm); @@ -260,40 +314,61 @@ static struct pthr_el * tpm_pthr_el(struct tpm * tpm, e = list_entry(p, struct pthr_el, next); if (e->thr == thr) return e; - } return NULL; } -void tpm_inc(struct tpm * tpm) +void tpm_begin_work(struct tpm * tpm) { struct pthr_el * e; - pthread_mutex_lock(&tpm->lock); +#ifdef CONFIG_OUROBOROS_DEBUG + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); +#endif + + pthread_mutex_lock(&tpm->mtx); e = tpm_pthr_el(tpm, pthread_self()); if (e != NULL) { - e->busy = false; - --tpm->wrk; + e->busy = true; + ++tpm->wrk; +#ifdef CONFIG_OUROBOROS_DEBUG + e->start = now; + e->last = now; +#endif } - pthread_mutex_unlock(&tpm->lock); + pthread_cond_signal(&tpm->cond); + + pthread_mutex_unlock(&tpm->mtx); } -void tpm_dec(struct tpm * tpm) +void tpm_wait_work(struct tpm * tpm) { struct pthr_el * e; - pthread_mutex_lock(&tpm->lock); + pthread_mutex_lock(&tpm->mtx); + + e = tpm_pthr_el(tpm, pthread_self()); + if (e != NULL) + e->wait = true; + + pthread_mutex_unlock(&tpm->mtx); +} + +void tpm_end_work(struct tpm * tpm) +{ + struct pthr_el * e; + + pthread_mutex_lock(&tpm->mtx); e = tpm_pthr_el(tpm, pthread_self()); if (e != NULL) { - e->busy = true; - ++tpm->wrk; + e->busy = false; + --tpm->wrk; } - pthread_cond_signal(&tpm->cond); - - pthread_mutex_unlock(&tpm->lock); + pthread_mutex_unlock(&tpm->mtx); } diff --git a/src/lib/utils.c b/src/lib/utils.c index fdbcd9d9..fd275f63 100644 --- a/src/lib/utils.c +++ b/src/lib/utils.c @@ -27,16 +27,26 @@ #include <stdlib.h> #include <string.h> +int bufcmp(const buffer_t * a, + const buffer_t * b) +{ + if (a->len != b->len) + return a->len < b->len ? -1 : 1; + + return memcmp(a->data, b->data, a->len); +} + + int n_digits(unsigned i) { - int n = 1; + int n = 1; - while (i > 9) { - ++n; - i /= 10; - } + while (i > 9) { + ++n; + i /= 10; + } - return n; + return n; } char * path_strip(const char * src) |