diff --git a/libknet/crypto.c b/libknet/crypto.c index b9aadafe..43c21994 100644 --- a/libknet/crypto.c +++ b/libknet/crypto.c @@ -1,212 +1,212 @@ /* * Copyright (C) 2012-2018 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include "crypto.h" #include "crypto_model.h" #include "internals.h" #include "logging.h" #include "common.h" /* * internal module switch data */ crypto_model_t crypto_modules_cmds[] = { { "nss", WITH_CRYPTO_NSS, 0, NULL }, { "openssl", WITH_CRYPTO_OPENSSL, 0, NULL }, { NULL, 0, 0, NULL } }; static int crypto_get_model(const char *model) { int idx = 0; while (crypto_modules_cmds[idx].model_name != NULL) { if (!strcmp(crypto_modules_cmds[idx].model_name, model)) return idx; idx++; } return -1; } /* * exported API */ int crypto_encrypt_and_sign ( knet_handle_t knet_h, const unsigned char *buf_in, const ssize_t buf_in_len, unsigned char *buf_out, ssize_t *buf_out_len) { return crypto_modules_cmds[knet_h->crypto_instance->model].ops->crypt(knet_h, buf_in, buf_in_len, buf_out, buf_out_len); } int crypto_encrypt_and_signv ( knet_handle_t knet_h, const struct iovec *iov_in, int iovcnt_in, unsigned char *buf_out, ssize_t *buf_out_len) { return crypto_modules_cmds[knet_h->crypto_instance->model].ops->cryptv(knet_h, iov_in, iovcnt_in, buf_out, buf_out_len); } int crypto_authenticate_and_decrypt ( knet_handle_t knet_h, const unsigned char *buf_in, const ssize_t buf_in_len, unsigned char *buf_out, ssize_t *buf_out_len) { return crypto_modules_cmds[knet_h->crypto_instance->model].ops->decrypt(knet_h, buf_in, buf_in_len, buf_out, buf_out_len); } int crypto_init( knet_handle_t knet_h, struct knet_handle_crypto_cfg *knet_handle_crypto_cfg) { int savederrno = 0; int model = 0; model = crypto_get_model(knet_handle_crypto_cfg->crypto_model); if (model < 0) { log_err(knet_h, KNET_SUB_CRYPTO, "model %s not supported", knet_handle_crypto_cfg->crypto_model); return -1; } if (crypto_modules_cmds[model].built_in == 0) { log_err(knet_h, KNET_SUB_CRYPTO, "this version of libknet was built without %s support. Please contact your vendor or fix the build.", knet_handle_crypto_cfg->crypto_model); return -1; } savederrno = pthread_rwlock_wrlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_CRYPTO, "Unable to get write lock: %s", strerror(savederrno)); return -1; } if (!crypto_modules_cmds[model].loaded) { crypto_modules_cmds[model].ops = load_module (knet_h, "crypto", crypto_modules_cmds[model].model_name); if (!crypto_modules_cmds[model].ops) { savederrno = errno; log_err(knet_h, KNET_SUB_CRYPTO, "Unable to load %s lib", crypto_modules_cmds[model].model_name); goto out_err; } if (crypto_modules_cmds[model].ops->abi_ver != KNET_CRYPTO_MODEL_ABI) { log_err(knet_h, KNET_SUB_CRYPTO, "ABI mismatch loading module %s. knet ver: %d, module ver: %d", crypto_modules_cmds[model].model_name, KNET_CRYPTO_MODEL_ABI, crypto_modules_cmds[model].ops->abi_ver); savederrno = EINVAL; goto out_err; } crypto_modules_cmds[model].loaded = 1; } log_debug(knet_h, KNET_SUB_CRYPTO, "Initizializing crypto module [%s/%s/%s]", knet_handle_crypto_cfg->crypto_model, knet_handle_crypto_cfg->crypto_cipher_type, knet_handle_crypto_cfg->crypto_hash_type); knet_h->crypto_instance = malloc(sizeof(struct crypto_instance)); if (!knet_h->crypto_instance) { log_err(knet_h, KNET_SUB_CRYPTO, "Unable to allocate memory for crypto instance"); pthread_rwlock_unlock(&shlib_rwlock); savederrno = ENOMEM; goto out_err; } /* * if crypto_modules_cmds.ops->init fails, it is expected that * it will clean everything by itself. * crypto_modules_cmds.ops->fini is not invoked on error. */ knet_h->crypto_instance->model = model; if (crypto_modules_cmds[knet_h->crypto_instance->model].ops->init(knet_h, knet_handle_crypto_cfg)) { - savederrno = EPIPE; + savederrno = errno; goto out_err; } log_debug(knet_h, KNET_SUB_CRYPTO, "security network overhead: %zu", knet_h->sec_header_size); pthread_rwlock_unlock(&shlib_rwlock); return 0; out_err: if (knet_h->crypto_instance) { free(knet_h->crypto_instance); knet_h->crypto_instance = NULL; } pthread_rwlock_unlock(&shlib_rwlock); errno = savederrno; return -1; } void crypto_fini( knet_handle_t knet_h) { int savederrno = 0; int model = 0; savederrno = pthread_rwlock_wrlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_CRYPTO, "Unable to get write lock: %s", strerror(savederrno)); return; } if (knet_h->crypto_instance) { model = knet_h->crypto_instance->model; if (crypto_modules_cmds[model].ops->fini != NULL) { crypto_modules_cmds[model].ops->fini(knet_h); } free(knet_h->crypto_instance); knet_h->crypto_instance = NULL; } pthread_rwlock_unlock(&shlib_rwlock); return; } int knet_get_crypto_list(struct knet_crypto_info *crypto_list, size_t *crypto_list_entries) { int err = 0; int idx = 0; int outidx = 0; if (!crypto_list_entries) { errno = EINVAL; return -1; } while (crypto_modules_cmds[idx].model_name != NULL) { if (crypto_modules_cmds[idx].built_in) { if (crypto_list) { crypto_list[outidx].name = crypto_modules_cmds[idx].model_name; } outidx++; } idx++; } *crypto_list_entries = outidx; return err; } diff --git a/libknet/crypto_openssl.c b/libknet/crypto_openssl.c index 60a6a385..a82ce87e 100644 --- a/libknet/crypto_openssl.c +++ b/libknet/crypto_openssl.c @@ -1,602 +1,609 @@ /* * Copyright (C) 2017-2018 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #define KNET_MODULE #include "config.h" #include #include #include #include #include #include #include #include #include "logging.h" #include "crypto_model.h" /* * 1.0.2 requires at least 120 bytes * 1.1.0 requires at least 256 bytes */ #define SSLERR_BUF_SIZE 512 /* * crypto definitions and conversion tables */ #define SALT_SIZE 16 struct opensslcrypto_instance { void *private_key; int private_key_len; const EVP_CIPHER *crypto_cipher_type; const EVP_MD *crypto_hash_type; }; /* * crypt/decrypt functions openssl1.0 */ #ifdef BUILDCRYPTOOPENSSL10 static int encrypt_openssl( knet_handle_t knet_h, const struct iovec *iov, int iovcnt, unsigned char *buf_out, ssize_t *buf_out_len) { struct opensslcrypto_instance *instance = knet_h->crypto_instance->model_instance; EVP_CIPHER_CTX ctx; int tmplen = 0, offset = 0; unsigned char *salt = buf_out; unsigned char *data = buf_out + SALT_SIZE; int err = 0; int i; char sslerr[SSLERR_BUF_SIZE]; EVP_CIPHER_CTX_init(&ctx); /* * contribute to PRNG for each packet we send/receive */ RAND_seed((unsigned char *)iov[iovcnt - 1].iov_base, iov[iovcnt - 1].iov_len); if (!RAND_bytes(salt, SALT_SIZE)) { ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to get random salt data: %s", sslerr); err = -1; goto out; } /* * add warning re keylength */ EVP_EncryptInit_ex(&ctx, instance->crypto_cipher_type, NULL, instance->private_key, salt); for (i=0; icrypto_instance->model_instance; EVP_CIPHER_CTX ctx; int tmplen1 = 0, tmplen2 = 0; unsigned char *salt = (unsigned char *)buf_in; unsigned char *data = salt + SALT_SIZE; int datalen = buf_in_len - SALT_SIZE; int err = 0; char sslerr[SSLERR_BUF_SIZE]; EVP_CIPHER_CTX_init(&ctx); /* * contribute to PRNG for each packet we send/receive */ RAND_seed(buf_in, buf_in_len); /* * add warning re keylength */ EVP_DecryptInit_ex(&ctx, instance->crypto_cipher_type, NULL, instance->private_key, salt); if (!EVP_DecryptUpdate(&ctx, buf_out, &tmplen1, data, datalen)) { ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to decrypt: %s", sslerr); err = -1; goto out; } if (!EVP_DecryptFinal_ex(&ctx, buf_out + tmplen1, &tmplen2)) { ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to finalize decrypt: %s", sslerr); err = -1; goto out; } *buf_out_len = tmplen1 + tmplen2; out: EVP_CIPHER_CTX_cleanup(&ctx); return err; } #endif #ifdef BUILDCRYPTOOPENSSL11 static int encrypt_openssl( knet_handle_t knet_h, const struct iovec *iov, int iovcnt, unsigned char *buf_out, ssize_t *buf_out_len) { struct opensslcrypto_instance *instance = knet_h->crypto_instance->model_instance; EVP_CIPHER_CTX *ctx; int tmplen = 0, offset = 0; unsigned char *salt = buf_out; unsigned char *data = buf_out + SALT_SIZE; int err = 0; int i; char sslerr[SSLERR_BUF_SIZE]; ctx = EVP_CIPHER_CTX_new(); /* * contribute to PRNG for each packet we send/receive */ RAND_seed((unsigned char *)iov[iovcnt - 1].iov_base, iov[iovcnt - 1].iov_len); if (!RAND_bytes(salt, SALT_SIZE)) { ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to get random salt data: %s", sslerr); err = -1; goto out; } /* * add warning re keylength */ EVP_EncryptInit_ex(ctx, instance->crypto_cipher_type, NULL, instance->private_key, salt); for (i=0; icrypto_instance->model_instance; EVP_CIPHER_CTX *ctx; int tmplen1 = 0, tmplen2 = 0; unsigned char *salt = (unsigned char *)buf_in; unsigned char *data = salt + SALT_SIZE; int datalen = buf_in_len - SALT_SIZE; int err = 0; char sslerr[SSLERR_BUF_SIZE]; ctx = EVP_CIPHER_CTX_new(); /* * contribute to PRNG for each packet we send/receive */ RAND_seed(buf_in, buf_in_len); /* * add warning re keylength */ EVP_DecryptInit_ex(ctx, instance->crypto_cipher_type, NULL, instance->private_key, salt); if (!EVP_DecryptUpdate(ctx, buf_out, &tmplen1, data, datalen)) { ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to decrypt: %s", sslerr); err = -1; goto out; } if (!EVP_DecryptFinal_ex(ctx, buf_out + tmplen1, &tmplen2)) { ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to finalize decrypt: %s", sslerr); err = -1; goto out; } *buf_out_len = tmplen1 + tmplen2; out: EVP_CIPHER_CTX_free(ctx); return err; } #endif /* * hash/hmac/digest functions */ static int calculate_openssl_hash( knet_handle_t knet_h, const unsigned char *buf, const size_t buf_len, unsigned char *hash) { struct opensslcrypto_instance *instance = knet_h->crypto_instance->model_instance; unsigned int hash_len = 0; unsigned char *hash_out = NULL; char sslerr[SSLERR_BUF_SIZE]; hash_out = HMAC(instance->crypto_hash_type, instance->private_key, instance->private_key_len, buf, buf_len, hash, &hash_len); if ((!hash_out) || (hash_len != knet_h->sec_hash_size)) { ERR_error_string_n(ERR_get_error(), sslerr, sizeof(sslerr)); log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to calculate hash: %s", sslerr); return -1; } return 0; } /* * exported API */ static int opensslcrypto_encrypt_and_signv ( knet_handle_t knet_h, const struct iovec *iov_in, int iovcnt_in, unsigned char *buf_out, ssize_t *buf_out_len) { struct opensslcrypto_instance *instance = knet_h->crypto_instance->model_instance; int i; if (instance->crypto_cipher_type) { if (encrypt_openssl(knet_h, iov_in, iovcnt_in, buf_out, buf_out_len) < 0) { return -1; } } else { *buf_out_len = 0; for (i=0; icrypto_hash_type) { if (calculate_openssl_hash(knet_h, buf_out, *buf_out_len, buf_out + *buf_out_len) < 0) { return -1; } *buf_out_len = *buf_out_len + knet_h->sec_hash_size; } return 0; } static int opensslcrypto_encrypt_and_sign ( knet_handle_t knet_h, const unsigned char *buf_in, const ssize_t buf_in_len, unsigned char *buf_out, ssize_t *buf_out_len) { struct iovec iov_in; memset(&iov_in, 0, sizeof(iov_in)); iov_in.iov_base = (unsigned char *)buf_in; iov_in.iov_len = buf_in_len; return opensslcrypto_encrypt_and_signv(knet_h, &iov_in, 1, buf_out, buf_out_len); } static int opensslcrypto_authenticate_and_decrypt ( knet_handle_t knet_h, const unsigned char *buf_in, const ssize_t buf_in_len, unsigned char *buf_out, ssize_t *buf_out_len) { struct opensslcrypto_instance *instance = knet_h->crypto_instance->model_instance; ssize_t temp_len = buf_in_len; if (instance->crypto_hash_type) { unsigned char tmp_hash[knet_h->sec_hash_size]; ssize_t temp_buf_len = buf_in_len - knet_h->sec_hash_size; if ((temp_buf_len < 0) || (temp_buf_len > KNET_MAX_PACKET_SIZE)) { log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Incorrect packet size."); return -1; } if (calculate_openssl_hash(knet_h, buf_in, temp_buf_len, tmp_hash) < 0) { return -1; } if (memcmp(tmp_hash, buf_in + temp_buf_len, knet_h->sec_hash_size) != 0) { log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Digest does not match"); return -1; } temp_len = temp_len - knet_h->sec_hash_size; *buf_out_len = temp_len; } if (instance->crypto_cipher_type) { if (decrypt_openssl(knet_h, buf_in, temp_len, buf_out, buf_out_len) < 0) { return -1; } } else { memmove(buf_out, buf_in, temp_len); *buf_out_len = temp_len; } return 0; } #ifdef BUILDCRYPTOOPENSSL10 static pthread_mutex_t *openssl_internal_lock; static void openssl_internal_locking_callback(int mode, int type, char *file, int line) { if (mode & CRYPTO_LOCK) { pthread_mutex_lock(&(openssl_internal_lock[type])); } else { pthread_mutex_unlock(&(openssl_internal_lock[type])); } } static unsigned long openssl_internal_thread_id(void) { return (unsigned long)pthread_self(); } static void openssl_internal_lock_cleanup(void) { int i; CRYPTO_set_locking_callback(NULL); CRYPTO_set_id_callback(NULL); for (i = 0; i < CRYPTO_num_locks(); i++) { pthread_mutex_destroy(&(openssl_internal_lock[i])); } if (openssl_internal_lock) { free(openssl_internal_lock); } return; } static int openssl_internal_lock_setup(void) { int savederrno = 0, err = 0; int i; openssl_internal_lock = malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); if (!openssl_internal_lock) { savederrno = errno; err = -1; goto out; } for (i = 0; i < CRYPTO_num_locks(); i++) { savederrno = pthread_mutex_init(&(openssl_internal_lock[i]), NULL); if (savederrno) { err = -1; goto out; } } CRYPTO_set_id_callback((unsigned long (*)(void))openssl_internal_thread_id); CRYPTO_set_locking_callback((void *)&openssl_internal_locking_callback); out: if (err) { openssl_internal_lock_cleanup(); } errno = savederrno; return err; } #endif static void opensslcrypto_fini( knet_handle_t knet_h) { struct opensslcrypto_instance *opensslcrypto_instance = knet_h->crypto_instance->model_instance; if (opensslcrypto_instance) { #ifdef BUILDCRYPTOOPENSSL10 openssl_internal_lock_cleanup(); #endif if (opensslcrypto_instance->private_key) { free(opensslcrypto_instance->private_key); opensslcrypto_instance->private_key = NULL; } free(opensslcrypto_instance); knet_h->crypto_instance->model_instance = NULL; knet_h->sec_header_size = 0; } return; } static int opensslcrypto_init( knet_handle_t knet_h, struct knet_handle_crypto_cfg *knet_handle_crypto_cfg) { static int openssl_is_init = 0; struct opensslcrypto_instance *opensslcrypto_instance = NULL; + int savederrno; log_debug(knet_h, KNET_SUB_OPENSSLCRYPTO, "Initizializing openssl crypto module [%s/%s]", knet_handle_crypto_cfg->crypto_cipher_type, knet_handle_crypto_cfg->crypto_hash_type); if (!openssl_is_init) { #ifdef BUILDCRYPTOOPENSSL10 ERR_load_crypto_strings(); OPENSSL_add_all_algorithms_noconf(); #endif #ifdef BUILDCRYPTOOPENSSL11 if (!OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ | OPENSSL_INIT_ADD_ALL_DIGESTS, NULL)) { log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to init openssl"); errno = EAGAIN; return -1; } #endif openssl_is_init = 1; } #ifdef BUILDCRYPTOOPENSSL10 if (openssl_internal_lock_setup() < 0) { log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to init openssl"); errno = EAGAIN; return -1; } #endif knet_h->crypto_instance->model_instance = malloc(sizeof(struct opensslcrypto_instance)); if (!knet_h->crypto_instance->model_instance) { log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to allocate memory for openssl model instance"); + errno = ENOMEM; return -1; } opensslcrypto_instance = knet_h->crypto_instance->model_instance; memset(opensslcrypto_instance, 0, sizeof(struct opensslcrypto_instance)); if (strcmp(knet_handle_crypto_cfg->crypto_cipher_type, "none") == 0) { opensslcrypto_instance->crypto_cipher_type = NULL; } else { opensslcrypto_instance->crypto_cipher_type = EVP_get_cipherbyname(knet_handle_crypto_cfg->crypto_cipher_type); if (!opensslcrypto_instance->crypto_cipher_type) { log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "unknown crypto cipher type requested"); + savederrno = ENXIO; goto out_err; } } if (strcmp(knet_handle_crypto_cfg->crypto_hash_type, "none") == 0) { opensslcrypto_instance->crypto_hash_type = NULL; } else { opensslcrypto_instance->crypto_hash_type = EVP_get_digestbyname(knet_handle_crypto_cfg->crypto_hash_type); if (!opensslcrypto_instance->crypto_hash_type) { log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "unknown crypto hash type requested"); + savederrno = ENXIO; goto out_err; } } if ((opensslcrypto_instance->crypto_cipher_type) && (!opensslcrypto_instance->crypto_hash_type)) { log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "crypto communication requires hash specified"); + savederrno = EINVAL; goto out_err; } opensslcrypto_instance->private_key = malloc(knet_handle_crypto_cfg->private_key_len); if (!opensslcrypto_instance->private_key) { log_err(knet_h, KNET_SUB_OPENSSLCRYPTO, "Unable to allocate memory for openssl private key"); + savederrno = ENOMEM; goto out_err; } memmove(opensslcrypto_instance->private_key, knet_handle_crypto_cfg->private_key, knet_handle_crypto_cfg->private_key_len); opensslcrypto_instance->private_key_len = knet_handle_crypto_cfg->private_key_len; knet_h->sec_header_size = 0; if (opensslcrypto_instance->crypto_hash_type) { knet_h->sec_hash_size = EVP_MD_size(opensslcrypto_instance->crypto_hash_type); knet_h->sec_header_size += knet_h->sec_hash_size; } if (opensslcrypto_instance->crypto_cipher_type) { int block_size; block_size = EVP_CIPHER_block_size(opensslcrypto_instance->crypto_cipher_type); if (block_size < 0) { goto out_err; } knet_h->sec_header_size += (block_size * 2); knet_h->sec_header_size += SALT_SIZE; knet_h->sec_salt_size = SALT_SIZE; knet_h->sec_block_size = block_size; } return 0; out_err: opensslcrypto_fini(knet_h); + errno = savederrno; return -1; } crypto_ops_t crypto_model = { KNET_CRYPTO_MODEL_ABI, opensslcrypto_init, opensslcrypto_fini, opensslcrypto_encrypt_and_sign, opensslcrypto_encrypt_and_signv, opensslcrypto_authenticate_and_decrypt };