summaryrefslogtreecommitdiffstats
path: root/runtime/net_ossl.c
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/net_ossl.c')
-rw-r--r--runtime/net_ossl.c1217
1 files changed, 1217 insertions, 0 deletions
diff --git a/runtime/net_ossl.c b/runtime/net_ossl.c
new file mode 100644
index 0000000..60e3fa2
--- /dev/null
+++ b/runtime/net_ossl.c
@@ -0,0 +1,1217 @@
+/* net.c
+ * Implementation of network-related stuff.
+ *
+ * File begun on 2023-08-29 by Alorbach (extracted from net.c)
+ *
+ * Copyright 2023 Andre Lorbach and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <strings.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include "rsyslog.h"
+#include "syslogd-types.h"
+#include "module-template.h"
+#include "parse.h"
+#include "srUtils.h"
+#include "obj.h"
+#include "errmsg.h"
+#include "net.h"
+#include "net_ossl.h"
+#include "nsd_ptcp.h"
+#include "rsconf.h"
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(net)
+DEFobjCurrIf(nsd_ptcp)
+
+/*--------------------------------------MT OpenSSL helpers ------------------------------------------*/
+static MUTEX_TYPE *mutex_buf = NULL;
+static sbool openssl_initialized = 0; // Avoid multiple initialization / deinitialization
+
+void locking_function(int mode, int n,
+ __attribute__((unused)) const char * file, __attribute__((unused)) int line)
+{
+ if (mode & CRYPTO_LOCK)
+ MUTEX_LOCK(mutex_buf[n]);
+ else
+ MUTEX_UNLOCK(mutex_buf[n]);
+}
+
+unsigned long id_function(void)
+{
+ return ((unsigned long)THREAD_ID);
+}
+
+
+struct CRYPTO_dynlock_value * dyn_create_function(
+ __attribute__((unused)) const char *file, __attribute__((unused)) int line)
+{
+ struct CRYPTO_dynlock_value *value;
+ value = (struct CRYPTO_dynlock_value *)malloc(sizeof(struct CRYPTO_dynlock_value));
+ if (!value)
+ return NULL;
+
+ MUTEX_SETUP(value->mutex);
+ return value;
+}
+
+void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l,
+ __attribute__((unused)) const char *file, __attribute__((unused)) int line)
+{
+ if (mode & CRYPTO_LOCK)
+ MUTEX_LOCK(l->mutex);
+ else
+ MUTEX_UNLOCK(l->mutex);
+}
+
+void dyn_destroy_function(struct CRYPTO_dynlock_value *l,
+ __attribute__((unused)) const char *file, __attribute__((unused)) int line)
+{
+ MUTEX_CLEANUP(l->mutex);
+ free(l);
+}
+
+/* set up support functions for openssl multi-threading. This must
+ * be done at library initialisation. If the function fails,
+ * processing can not continue normally. On failure, 0 is
+ * returned, on success 1.
+ */
+int opensslh_THREAD_setup(void)
+{
+ int i;
+ if (openssl_initialized == 1) {
+ DBGPRINTF("openssl: multithread setup already initialized\n");
+ return 1;
+ }
+
+ mutex_buf = (MUTEX_TYPE *)malloc(CRYPTO_num_locks( ) * sizeof(MUTEX_TYPE));
+ if (mutex_buf == NULL)
+ return 0;
+ for (i = 0; i < CRYPTO_num_locks( ); i++)
+ MUTEX_SETUP(mutex_buf[i]);
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ CRYPTO_set_id_callback(id_function);
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+ CRYPTO_set_locking_callback(locking_function);
+ /* The following three CRYPTO_... functions are the OpenSSL functions
+ for registering the callbacks we implemented above */
+ CRYPTO_set_dynlock_create_callback(dyn_create_function);
+ CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
+ CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
+
+ DBGPRINTF("openssl: multithread setup finished\n");
+ openssl_initialized = 1;
+ return 1;
+}
+
+/* shut down openssl - do this only when you are totally done
+ * with openssl.
+ */
+int opensslh_THREAD_cleanup(void)
+{
+ int i;
+ if (openssl_initialized == 0) {
+ DBGPRINTF("openssl: multithread cleanup already done\n");
+ return 1;
+ }
+ if (!mutex_buf)
+ return 0;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ CRYPTO_set_id_callback(NULL);
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+ CRYPTO_set_locking_callback(NULL);
+ CRYPTO_set_dynlock_create_callback(NULL);
+ CRYPTO_set_dynlock_lock_callback(NULL);
+ CRYPTO_set_dynlock_destroy_callback(NULL);
+
+ for (i = 0; i < CRYPTO_num_locks( ); i++)
+ MUTEX_CLEANUP(mutex_buf[i]);
+
+ free(mutex_buf);
+ mutex_buf = NULL;
+
+ DBGPRINTF("openssl: multithread cleanup finished\n");
+ openssl_initialized = 0;
+ return 1;
+}
+/*-------------------------------------- MT OpenSSL helpers -----------------------------------------*/
+
+
+/*--------------------------------------OpenSSL helpers ------------------------------------------*/
+
+/* globally initialize OpenSSL
+ */
+void
+osslGlblInit(void)
+{
+ DBGPRINTF("openssl: entering osslGlblInit\n");
+
+ if((opensslh_THREAD_setup() == 0) ||
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ /* Setup OpenSSL library < 1.1.0 */
+ !SSL_library_init()
+#else
+ /* Setup OpenSSL library >= 1.1.0 with system default settings */
+ OPENSSL_init_ssl(0, NULL) == 0
+#endif
+ ) {
+ LogError(0, RS_RET_NO_ERRCODE, "Error: OpenSSL initialization failed!");
+ }
+
+ /* Load readable error strings */
+ SSL_load_error_strings();
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ /*
+ * ERR_load_*(), ERR_func_error_string(), ERR_get_error_line(), ERR_get_error_line_data(), ERR_get_state()
+ * OpenSSL now loads error strings automatically so these functions are not needed.
+ * SEE FOR MORE:
+ * https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+ *
+ */
+#else
+ /* Load error strings into mem*/
+ ERR_load_BIO_strings();
+ ERR_load_crypto_strings();
+#endif
+}
+
+/* globally de-initialize OpenSSL */
+void
+osslGlblExit(void)
+{
+ DBGPRINTF("openssl: entering osslGlblExit\n");
+ ENGINE_cleanup();
+ ERR_free_strings();
+ EVP_cleanup();
+ CRYPTO_cleanup_all_ex_data();
+}
+
+
+/* initialize openssl context; called on
+ * - listener creation
+ * - outbound connection creation
+ * Once created, the ctx object is used by-subobjects (accepted inbound connections)
+ */
+static rsRetVal
+net_ossl_osslCtxInit(net_ossl_t *pThis, const SSL_METHOD *method)
+{
+ DEFiRet;
+ int bHaveCA;
+ int bHaveCRL;
+ int bHaveCert;
+ int bHaveKey;
+ int bHaveExtraCAFiles;
+ const char *caFile, *crlFile, *certFile, *keyFile;
+ char *extraCaFiles, *extraCaFile;
+ /* Setup certificates */
+ caFile = (char*) ((pThis->pszCAFile == NULL) ? glbl.GetDfltNetstrmDrvrCAF(runConf) : pThis->pszCAFile);
+ if(caFile == NULL) {
+ LogMsg(0, RS_RET_CA_CERT_MISSING, LOG_WARNING,
+ "Warning: CA certificate is not set");
+ bHaveCA = 0;
+ } else {
+ dbgprintf("osslCtxInit: OSSL CA file: '%s'\n", caFile);
+ bHaveCA = 1;
+ }
+ crlFile = (char*) ((pThis->pszCRLFile == NULL) ? glbl.GetDfltNetstrmDrvrCRLF(runConf) : pThis->pszCRLFile);
+ if(crlFile == NULL) {
+ bHaveCRL = 0;
+ } else {
+ dbgprintf("osslCtxInit: OSSL CRL file: '%s'\n", crlFile);
+ bHaveCRL = 1;
+ }
+ certFile = (char*) ((pThis->pszCertFile == NULL) ?
+ glbl.GetDfltNetstrmDrvrCertFile(runConf) : pThis->pszCertFile);
+ if(certFile == NULL) {
+ LogMsg(0, RS_RET_CERT_MISSING, LOG_WARNING,
+ "Warning: Certificate file is not set");
+ bHaveCert = 0;
+ } else {
+ dbgprintf("osslCtxInit: OSSL CERT file: '%s'\n", certFile);
+ bHaveCert = 1;
+ }
+ keyFile = (char*) ((pThis->pszKeyFile == NULL) ? glbl.GetDfltNetstrmDrvrKeyFile(runConf) : pThis->pszKeyFile);
+ if(keyFile == NULL) {
+ LogMsg(0, RS_RET_CERTKEY_MISSING, LOG_WARNING,
+ "Warning: Key file is not set");
+ bHaveKey = 0;
+ } else {
+ dbgprintf("osslCtxInit: OSSL KEY file: '%s'\n", keyFile);
+ bHaveKey = 1;
+ }
+ extraCaFiles = (char*) ((pThis->pszExtraCAFiles == NULL) ? glbl.GetNetstrmDrvrCAExtraFiles(runConf) :
+ pThis->pszExtraCAFiles);
+ if(extraCaFiles == NULL) {
+ bHaveExtraCAFiles = 0;
+ } else {
+ dbgprintf("osslCtxInit: OSSL EXTRA CA files: '%s'\n", extraCaFiles);
+ bHaveExtraCAFiles = 1;
+ }
+
+ /* Create main CTX Object based on method parameter */
+ pThis->ctx = SSL_CTX_new(method);
+
+ if(bHaveExtraCAFiles == 1) {
+ while((extraCaFile = strsep(&extraCaFiles, ","))) {
+ if(SSL_CTX_load_verify_locations(pThis->ctx, extraCaFile, NULL) != 1) {
+ LogError(0, RS_RET_TLS_CERT_ERR, "Error: Extra Certificate file could not be accessed. "
+ "Check at least: 1) file path is correct, 2) file exist, "
+ "3) permissions are correct, 4) file content is correct. "
+ "OpenSSL error info may follow in next messages");
+ net_ossl_lastOpenSSLErrorMsg(NULL, 0, NULL, LOG_ERR, "osslCtxInit",
+ "SSL_CTX_load_verify_locations");
+ ABORT_FINALIZE(RS_RET_TLS_CERT_ERR);
+ }
+ }
+ }
+ if(bHaveCA == 1 && SSL_CTX_load_verify_locations(pThis->ctx, caFile, NULL) != 1) {
+ LogError(0, RS_RET_TLS_CERT_ERR, "Error: CA certificate could not be accessed. "
+ "Check at least: 1) file path is correct, 2) file exist, "
+ "3) permissions are correct, 4) file content is correct. "
+ "OpenSSL error info may follow in next messages");
+ net_ossl_lastOpenSSLErrorMsg(NULL, 0, NULL, LOG_ERR, "osslCtxInit", "SSL_CTX_load_verify_locations");
+ ABORT_FINALIZE(RS_RET_TLS_CERT_ERR);
+ }
+ if(bHaveCRL == 1) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ // Get X509_STORE reference
+ X509_STORE *store = SSL_CTX_get_cert_store(pThis->ctx);
+ if (!X509_STORE_load_file(store, crlFile)) {
+ LogError(0, RS_RET_CRL_INVALID, "Error: CRL could not be accessed. "
+ "Check at least: 1) file path is correct, 2) file exist, "
+ "3) permissions are correct, 4) file content is correct. "
+ "OpenSSL error info may follow in next messages");
+ net_ossl_lastOpenSSLErrorMsg(NULL, 0, NULL, LOG_ERR, "osslCtxInit", "X509_STORE_load_file");
+ ABORT_FINALIZE(RS_RET_CRL_INVALID);
+ }
+ X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK);
+#else
+# if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ // Get X509_STORE reference
+ X509_STORE *store = SSL_CTX_get_cert_store(pThis->ctx);
+ // Load the CRL PEM file
+ FILE *fp = fopen(crlFile, "r");
+ if(fp == NULL) {
+ LogError(0, RS_RET_CRL_MISSING, "Error: CRL could not be accessed. "
+ "Check at least: 1) file path is correct, 2) file exist, "
+ "3) permissions are correct, 4) file content is correct. "
+ "OpenSSL error info may follow in next messages");
+ net_ossl_lastOpenSSLErrorMsg(NULL, 0, NULL, LOG_ERR, "osslCtxInit", "fopen");
+ ABORT_FINALIZE(RS_RET_CRL_MISSING);
+ }
+ X509_CRL *crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL);
+ fclose(fp);
+ if(crl == NULL) {
+ LogError(0, RS_RET_CRL_INVALID, "Error: Unable to read CRL."
+ "OpenSSL error info may follow in next messages");
+ net_ossl_lastOpenSSLErrorMsg(NULL, 0, NULL, LOG_ERR, "osslCtxInit", "PEM_read_X509_CRL");
+ ABORT_FINALIZE(RS_RET_CRL_INVALID);
+ }
+ // Add the CRL to the X509_STORE
+ if(!X509_STORE_add_crl(store, crl)) {
+ LogError(0, RS_RET_CRL_INVALID, "Error: Unable to add CRL to store."
+ "OpenSSL error info may follow in next messages");
+ net_ossl_lastOpenSSLErrorMsg(NULL, 0, NULL, LOG_ERR, "osslCtxInit", "X509_STORE_add_crl");
+ X509_CRL_free(crl);
+ ABORT_FINALIZE(RS_RET_CRL_INVALID);
+ }
+ // Set the X509_STORE to the SSL_CTX
+ // SSL_CTX_set_cert_store(pThis->ctx, store);
+ // Enable CRL checking
+ X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
+ X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
+ SSL_CTX_set1_param(pThis->ctx, param);
+ X509_VERIFY_PARAM_free(param);
+# else
+ LogError(0, RS_RET_SYS_ERR, "Warning: TLS library does not support X509_STORE_load_file"
+ "(requires OpenSSL 3.x or higher). Cannot use Certificate revocation list (CRL) '%s'.",
+ crlFile);
+# endif
+#endif
+ }
+ if(bHaveCert == 1 && SSL_CTX_use_certificate_chain_file(pThis->ctx, certFile) != 1) {
+ LogError(0, RS_RET_TLS_CERT_ERR, "Error: Certificate file could not be accessed. "
+ "Check at least: 1) file path is correct, 2) file exist, "
+ "3) permissions are correct, 4) file content is correct. "
+ "OpenSSL error info may follow in next messages");
+ net_ossl_lastOpenSSLErrorMsg(NULL, 0, NULL, LOG_ERR, "osslCtxInit",
+ "SSL_CTX_use_certificate_chain_file");
+ ABORT_FINALIZE(RS_RET_TLS_CERT_ERR);
+ }
+ if(bHaveKey == 1 && SSL_CTX_use_PrivateKey_file(pThis->ctx, keyFile, SSL_FILETYPE_PEM) != 1) {
+ LogError(0, RS_RET_TLS_KEY_ERR , "Error: Key could not be accessed. "
+ "Check at least: 1) file path is correct, 2) file exist, "
+ "3) permissions are correct, 4) file content is correct. "
+ "OpenSSL error info may follow in next messages");
+ net_ossl_lastOpenSSLErrorMsg(NULL, 0, NULL, LOG_ERR, "osslCtxInit", "SSL_CTX_use_PrivateKey_file");
+ ABORT_FINALIZE(RS_RET_TLS_KEY_ERR);
+ }
+
+ /* Set CTX Options */
+ SSL_CTX_set_options(pThis->ctx, SSL_OP_NO_SSLv2); /* Disable insecure SSLv2 Protocol */
+ SSL_CTX_set_options(pThis->ctx, SSL_OP_NO_SSLv3); /* Disable insecure SSLv3 Protocol */
+ SSL_CTX_sess_set_cache_size(pThis->ctx,1024); /* TODO: make configurable? */
+
+ /* Set default VERIFY Options for OpenSSL CTX - and CALLBACK */
+ if (pThis->authMode == OSSL_AUTH_CERTANON) {
+ dbgprintf("osslCtxInit: SSL_VERIFY_NONE\n");
+ net_ossl_set_ctx_verify_callback(pThis->ctx, SSL_VERIFY_NONE);
+ } else {
+ dbgprintf("osslCtxInit: SSL_VERIFY_PEER\n");
+ net_ossl_set_ctx_verify_callback(pThis->ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT);
+ }
+
+ SSL_CTX_set_timeout(pThis->ctx, 30); /* Default Session Timeout, TODO: Make configureable */
+ SSL_CTX_set_mode(pThis->ctx, SSL_MODE_AUTO_RETRY);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+# if OPENSSL_VERSION_NUMBER <= 0x101010FFL
+ /* Enable Support for automatic EC temporary key parameter selection. */
+ SSL_CTX_set_ecdh_auto(pThis->ctx, 1);
+# else
+ /*
+ * SSL_CTX_set_ecdh_auto and SSL_CTX_set_tmp_ecdh are depreceated in higher
+ * OpenSSL Versions, so we no more need them - see for more:
+ * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_ecdh_auto.html
+ */
+# endif
+#else
+ dbgprintf("osslCtxInit: openssl to old, cannot use SSL_CTX_set_ecdh_auto."
+ "Using SSL_CTX_set_tmp_ecdh with NID_X9_62_prime256v1/() instead.\n");
+ SSL_CTX_set_tmp_ecdh(pThis->ctx, EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+#endif
+finalize_it:
+ RETiRet;
+}
+
+/* Helper function to print usefull OpenSSL errors
+ */
+void net_ossl_lastOpenSSLErrorMsg
+ (uchar *fromHost, int ret, SSL *ssl, int severity, const char* pszCallSource, const char* pszOsslApi)
+{
+ unsigned long un_error = 0;
+ int iSSLErr = 0;
+ if (ssl == NULL) {
+ /* Output Error Info*/
+ DBGPRINTF("lastOpenSSLErrorMsg: Error in '%s' with ret=%d\n", pszCallSource, ret);
+ } else {
+ /* if object is set, get error code */
+ iSSLErr = SSL_get_error(ssl, ret);
+ /* Output Debug as well */
+ DBGPRINTF("lastOpenSSLErrorMsg: %s Error in '%s': '%s(%d)' with ret=%d, errno=%d(%s), sslapi='%s'\n",
+ (iSSLErr == SSL_ERROR_SSL ? "SSL_ERROR_SSL" :
+ (iSSLErr == SSL_ERROR_SYSCALL ? "SSL_ERROR_SYSCALL" : "SSL_ERROR_UNKNOWN")),
+ pszCallSource, ERR_error_string(iSSLErr, NULL),
+ iSSLErr,
+ ret,
+ errno,
+ strerror(errno),
+ pszOsslApi);
+
+ /* Output error message */
+ LogMsg(0, RS_RET_NO_ERRCODE, severity,
+ "%s Error in '%s': '%s(%d)' with ret=%d, errno=%d(%s), sslapi='%s'\n",
+ (iSSLErr == SSL_ERROR_SSL ? "SSL_ERROR_SSL" :
+ (iSSLErr == SSL_ERROR_SYSCALL ? "SSL_ERROR_SYSCALL" : "SSL_ERROR_UNKNOWN")),
+ pszCallSource, ERR_error_string(iSSLErr, NULL),
+ iSSLErr,
+ ret,
+ errno,
+ strerror(errno),
+ pszOsslApi);
+ }
+
+ /* Loop through ERR_get_error */
+ while ((un_error = ERR_get_error()) > 0){
+ LogMsg(0, RS_RET_NO_ERRCODE, severity,
+ "net_ossl:remote '%s' OpenSSL Error Stack: %s", fromHost, ERR_error_string(un_error, NULL) );
+ }
+}
+
+/* initialize tls config commands in openssl context
+ */
+rsRetVal net_ossl_apply_tlscgfcmd(net_ossl_t *pThis, uchar *tlscfgcmd)
+{
+ DEFiRet;
+ char *pCurrentPos;
+ char *pNextPos;
+ char *pszCmd;
+ char *pszValue;
+ int iConfErr;
+
+ if (tlscfgcmd == NULL) {
+ FINALIZE;
+ }
+
+ dbgprintf("net_ossl_apply_tlscgfcmd: Apply tlscfgcmd: '%s'\n", tlscfgcmd);
+
+ /* Set working pointer */
+ pCurrentPos = (char*) tlscfgcmd;
+ if (pCurrentPos != NULL && strlen(pCurrentPos) > 0) {
+ // Create CTX Config Helper
+ SSL_CONF_CTX *cctx;
+ cctx = SSL_CONF_CTX_new();
+ if (pThis->sslState == osslServer) {
+ SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER);
+ } else {
+ SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT);
+ }
+ SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE);
+ SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SHOW_ERRORS);
+ SSL_CONF_CTX_set_ssl_ctx(cctx, pThis->ctx);
+
+ do
+ {
+ pNextPos = index(pCurrentPos, '=');
+ if (pNextPos != NULL) {
+ while ( *pCurrentPos != '\0' &&
+ (*pCurrentPos == ' ' || *pCurrentPos == '\t') )
+ pCurrentPos++;
+ pszCmd = strndup(pCurrentPos, pNextPos-pCurrentPos);
+ pCurrentPos = pNextPos+1;
+ pNextPos = index(pCurrentPos, '\n');
+ pNextPos = (pNextPos == NULL ? index(pCurrentPos, ';') : pNextPos);
+ pszValue = (pNextPos == NULL ?
+ strdup(pCurrentPos) :
+ strndup(pCurrentPos, pNextPos - pCurrentPos));
+ pCurrentPos = (pNextPos == NULL ? NULL : pNextPos+1);
+
+ /* Add SSL Conf Command */
+ iConfErr = SSL_CONF_cmd(cctx, pszCmd, pszValue);
+ if (iConfErr > 0) {
+ dbgprintf("net_ossl_apply_tlscgfcmd: Successfully added Command "
+ "'%s':'%s'\n",
+ pszCmd, pszValue);
+ }
+ else {
+ LogError(0, RS_RET_SYS_ERR, "Failed to added Command: %s:'%s' "
+ "in net_ossl_apply_tlscgfcmd with error '%d'",
+ pszCmd, pszValue, iConfErr);
+ }
+
+ free(pszCmd);
+ free(pszValue);
+ } else {
+ /* Abort further parsing */
+ pCurrentPos = NULL;
+ }
+ }
+ while (pCurrentPos != NULL);
+
+ /* Finalize SSL Conf */
+ iConfErr = SSL_CONF_CTX_finish(cctx);
+ if (!iConfErr) {
+ LogError(0, RS_RET_SYS_ERR, "Error: setting openssl command parameters: %s"
+ "OpenSSL error info may follow in next messages",
+ tlscfgcmd);
+ net_ossl_lastOpenSSLErrorMsg(NULL, 0, NULL, LOG_ERR,
+ "net_ossl_apply_tlscgfcmd", "SSL_CONF_CTX_finish");
+ }
+ SSL_CONF_CTX_free(cctx);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Convert a fingerprint to printable data. The conversion is carried out
+ * according IETF I-D syslog-transport-tls-12. The fingerprint string is
+ * returned in a new cstr object. It is the caller's responsibility to
+ * destruct that object.
+ * rgerhards, 2008-05-08
+ */
+static rsRetVal
+net_ossl_genfingerprintstr(uchar *pFingerprint, size_t sizeFingerprint, cstr_t **ppStr, const char* prefix)
+{
+ cstr_t *pStr = NULL;
+ uchar buf[4];
+ size_t i;
+ DEFiRet;
+
+ CHKiRet(rsCStrConstruct(&pStr));
+ CHKiRet(rsCStrAppendStrWithLen(pStr, (uchar*) prefix, strlen(prefix)));
+ for(i = 0 ; i < sizeFingerprint ; ++i) {
+ snprintf((char*)buf, sizeof(buf), ":%2.2X", pFingerprint[i]);
+ CHKiRet(rsCStrAppendStrWithLen(pStr, buf, 3));
+ }
+ cstrFinalize(pStr);
+
+ *ppStr = pStr;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pStr != NULL)
+ rsCStrDestruct(&pStr);
+ }
+ RETiRet;
+}
+
+
+
+/* Perform a match on ONE peer name obtained from the certificate. This name
+ * is checked against the set of configured credentials. *pbFoundPositiveMatch is
+ * set to 1 if the ID matches. *pbFoundPositiveMatch must have been initialized
+ * to 0 by the caller (this is a performance enhancement as we expect to be
+ * called multiple times).
+ * TODO: implemet wildcards?
+ * rgerhards, 2008-05-26
+ */
+static rsRetVal
+net_ossl_chkonepeername(net_ossl_t *pThis, X509 *certpeer, uchar *pszPeerID, int *pbFoundPositiveMatch)
+{
+ permittedPeers_t *pPeer;
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ int osslRet;
+#endif
+ char *x509name = NULL;
+ DEFiRet;
+
+ if (certpeer == NULL) {
+ ABORT_FINALIZE(RS_RET_TLS_NO_CERT);
+ }
+
+ ISOBJ_TYPE_assert(pThis, net_ossl);
+ assert(pszPeerID != NULL);
+ assert(pbFoundPositiveMatch != NULL);
+
+ /* Obtain Namex509 name from subject */
+ x509name = X509_NAME_oneline(RSYSLOG_X509_NAME_oneline(certpeer), NULL, 0);
+
+ if(pThis->pPermPeers) { /* do we have configured peer IDs? */
+ pPeer = pThis->pPermPeers;
+ while(pPeer != NULL) {
+ CHKiRet(net.PermittedPeerWildcardMatch(pPeer, pszPeerID, pbFoundPositiveMatch));
+ if(*pbFoundPositiveMatch)
+ break;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ /* if we did not succeed so far, try ossl X509_check_host
+ * ( Includes check against SubjectAlternativeName )
+ */
+ osslRet = X509_check_host( certpeer, (const char*)pPeer->pszID,
+ strlen((const char*)pPeer->pszID), 0, NULL);
+ if ( osslRet == 1 ) {
+ /* Found Peer cert in allowed Peerslist */
+ dbgprintf("net_ossl_chkonepeername: Client ('%s') is allowed (X509_check_host)\n",
+ x509name);
+ *pbFoundPositiveMatch = 1;
+ break;
+ } else if ( osslRet < 0 ) {
+ net_ossl_lastOpenSSLErrorMsg(NULL, osslRet, NULL, LOG_ERR,
+ "net_ossl_chkonepeername", "X509_check_host");
+ ABORT_FINALIZE(RS_RET_NO_ERRCODE);
+ }
+#endif
+ /* Check next peer */
+ pPeer = pPeer->pNext;
+ }
+ } else {
+ LogMsg(0, RS_RET_TLS_NO_CERT, LOG_WARNING,
+ "net_ossl_chkonepeername: Peername check could not be done: "
+ "no peernames configured.");
+ }
+finalize_it:
+ if (x509name != NULL){
+ OPENSSL_free(x509name);
+ }
+
+ RETiRet;
+}
+
+
+/* Check the peer's ID in fingerprint auth mode.
+ * rgerhards, 2008-05-22
+ */
+rsRetVal
+net_ossl_peerfingerprint(net_ossl_t *pThis, X509* certpeer, uchar *fromHostIP)
+{
+ DEFiRet;
+ unsigned int n;
+ uchar fingerprint[20 /*EVP_MAX_MD_SIZE**/];
+ uchar fingerprintSha256[32 /*EVP_MAX_MD_SIZE**/];
+ size_t size;
+ size_t sizeSha256;
+ cstr_t *pstrFingerprint = NULL;
+ cstr_t *pstrFingerprintSha256 = NULL;
+ int bFoundPositiveMatch;
+ permittedPeers_t *pPeer;
+ const EVP_MD *fdig = EVP_sha1();
+ const EVP_MD *fdigSha256 = EVP_sha256();
+
+ ISOBJ_TYPE_assert(pThis, net_ossl);
+
+ if (certpeer == NULL) {
+ ABORT_FINALIZE(RS_RET_TLS_NO_CERT);
+ }
+
+ /* obtain the SHA1 fingerprint */
+ size = sizeof(fingerprint);
+ if (!X509_digest(certpeer,fdig,fingerprint,&n)) {
+ dbgprintf("net_ossl_peerfingerprint: error X509cert is not valid!\n");
+ ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT);
+ }
+ sizeSha256 = sizeof(fingerprintSha256);
+ if (!X509_digest(certpeer,fdigSha256,fingerprintSha256,&n)) {
+ dbgprintf("net_ossl_peerfingerprint: error X509cert is not valid!\n");
+ ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT);
+ }
+ CHKiRet(net_ossl_genfingerprintstr(fingerprint, size, &pstrFingerprint, "SHA1"));
+ dbgprintf("net_ossl_peerfingerprint: peer's certificate SHA1 fingerprint: %s\n",
+ cstrGetSzStrNoNULL(pstrFingerprint));
+ CHKiRet(net_ossl_genfingerprintstr(fingerprintSha256, sizeSha256, &pstrFingerprintSha256, "SHA256"));
+ dbgprintf("net_ossl_peerfingerprint: peer's certificate SHA256 fingerprint: %s\n",
+ cstrGetSzStrNoNULL(pstrFingerprintSha256));
+
+ /* now search through the permitted peers to see if we can find a permitted one */
+ bFoundPositiveMatch = 0;
+ pPeer = pThis->pPermPeers;
+ while(pPeer != NULL && !bFoundPositiveMatch) {
+ if(!rsCStrSzStrCmp(pstrFingerprint, pPeer->pszID, strlen((char*) pPeer->pszID))) {
+ dbgprintf("net_ossl_peerfingerprint: peer's certificate SHA1 MATCH found: %s\n",
+ pPeer->pszID);
+ bFoundPositiveMatch = 1;
+ } else if(!rsCStrSzStrCmp(pstrFingerprintSha256, pPeer->pszID, strlen((char*) pPeer->pszID))) {
+ dbgprintf("net_ossl_peerfingerprint: peer's certificate SHA256 MATCH found: %s\n",
+ pPeer->pszID);
+ bFoundPositiveMatch = 1;
+ } else {
+ dbgprintf("net_ossl_peerfingerprint: NOMATCH peer certificate: %s\n", pPeer->pszID);
+ pPeer = pPeer->pNext;
+ }
+ }
+
+ if(!bFoundPositiveMatch) {
+ dbgprintf("net_ossl_peerfingerprint: invalid peer fingerprint, not permitted to talk to it\n");
+ if(pThis->bReportAuthErr == 1) {
+ errno = 0;
+ LogMsg(0, RS_RET_INVALID_FINGERPRINT, LOG_WARNING,
+ "nsd_ossl:TLS session terminated with remote syslog server '%s': "
+ "Fingerprint check failed, not permitted to talk to %s",
+ fromHostIP, cstrGetSzStrNoNULL(pstrFingerprint));
+ pThis->bReportAuthErr = 0;
+ }
+ ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT);
+ }
+
+finalize_it:
+ if(pstrFingerprint != NULL)
+ cstrDestruct(&pstrFingerprint);
+ RETiRet;
+}
+
+
+/* Check the peer's ID in name auth mode.
+ */
+rsRetVal
+net_ossl_chkpeername(net_ossl_t *pThis, X509* certpeer, uchar *fromHostIP)
+{
+ DEFiRet;
+ uchar lnBuf[256];
+ int bFoundPositiveMatch;
+ cstr_t *pStr = NULL;
+ char *x509name = NULL;
+
+ ISOBJ_TYPE_assert(pThis, net_ossl);
+
+ bFoundPositiveMatch = 0;
+ CHKiRet(rsCStrConstruct(&pStr));
+
+ /* Obtain Namex509 name from subject */
+ x509name = X509_NAME_oneline(RSYSLOG_X509_NAME_oneline(certpeer), NULL, 0);
+
+ dbgprintf("net_ossl_chkpeername: checking - peername '%s' on server '%s'\n", x509name, fromHostIP);
+ snprintf((char*)lnBuf, sizeof(lnBuf), "name: %s; ", x509name);
+ CHKiRet(rsCStrAppendStr(pStr, lnBuf));
+ CHKiRet(net_ossl_chkonepeername(pThis, certpeer, (uchar*)x509name, &bFoundPositiveMatch));
+
+ if(!bFoundPositiveMatch) {
+ dbgprintf("net_ossl_chkpeername: invalid peername, not permitted to talk to it\n");
+ if(pThis->bReportAuthErr == 1) {
+ cstrFinalize(pStr);
+ errno = 0;
+ LogMsg(0, RS_RET_INVALID_FINGERPRINT, LOG_WARNING,
+ "nsd_ossl:TLS session terminated with remote syslog server: "
+ "peer name not authorized, not permitted to talk to %s",
+ cstrGetSzStrNoNULL(pStr));
+ pThis->bReportAuthErr = 0;
+ }
+ ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT);
+ } else {
+ dbgprintf("net_ossl_chkpeername: permitted to talk!\n");
+ }
+
+finalize_it:
+ if (x509name != NULL){
+ OPENSSL_free(x509name);
+ }
+
+ if(pStr != NULL)
+ rsCStrDestruct(&pStr);
+ RETiRet;
+}
+
+
+/* check the ID of the remote peer - used for both fingerprint and
+ * name authentication.
+ */
+X509*
+net_ossl_getpeercert(net_ossl_t *pThis, SSL *ssl, uchar *fromHostIP)
+{
+ X509* certpeer;
+
+ ISOBJ_TYPE_assert(pThis, net_ossl);
+
+ /* Get peer certificate from SSL */
+ certpeer = SSL_get_peer_certificate(ssl);
+ if ( certpeer == NULL ) {
+ if(pThis->bReportAuthErr == 1 && 1) {
+ errno = 0;
+ pThis->bReportAuthErr = 0;
+ LogMsg(0, RS_RET_TLS_NO_CERT, LOG_WARNING,
+ "nsd_ossl:TLS session terminated with remote syslog server '%s': "
+ "Peer check failed, peer did not provide a certificate.", fromHostIP);
+ }
+ }
+ return certpeer;
+}
+
+
+/* Verify the validity of the remote peer's certificate.
+ */
+rsRetVal
+net_ossl_chkpeercertvalidity(net_ossl_t __attribute__((unused)) *pThis, SSL *ssl, uchar *fromHostIP)
+{
+ DEFiRet;
+ int iVerErr = X509_V_OK;
+
+ ISOBJ_TYPE_assert(pThis, net_ossl);
+ PermitExpiredCerts* pPermitExpiredCerts = (PermitExpiredCerts*) SSL_get_ex_data(ssl, 1);
+
+ iVerErr = SSL_get_verify_result(ssl);
+ if (iVerErr != X509_V_OK) {
+ if (iVerErr == X509_V_ERR_CERT_HAS_EXPIRED) {
+ if (pPermitExpiredCerts != NULL && *pPermitExpiredCerts == OSSL_EXPIRED_DENY) {
+ LogMsg(0, RS_RET_CERT_EXPIRED, LOG_INFO,
+ "net_ossl:TLS session terminated with remote syslog server '%s': "
+ "not permitted to talk to peer, certificate invalid: "
+ "Certificate expired: %s",
+ fromHostIP, X509_verify_cert_error_string(iVerErr));
+ ABORT_FINALIZE(RS_RET_CERT_EXPIRED);
+ } else if (pPermitExpiredCerts != NULL && *pPermitExpiredCerts == OSSL_EXPIRED_WARN) {
+ LogMsg(0, RS_RET_NO_ERRCODE, LOG_WARNING,
+ "net_ossl:CertValidity check - warning talking to peer '%s': "
+ "certificate expired: %s",
+ fromHostIP, X509_verify_cert_error_string(iVerErr));
+ } else {
+ dbgprintf("net_ossl_chkpeercertvalidity: talking to peer '%s': "
+ "certificate expired: %s\n",
+ fromHostIP, X509_verify_cert_error_string(iVerErr));
+ }/* Else do nothing */
+ } else if (iVerErr == X509_V_ERR_CERT_REVOKED) {
+ LogMsg(0, RS_RET_CERT_REVOKED, LOG_INFO,
+ "net_ossl:TLS session terminated with remote syslog server '%s': "
+ "not permitted to talk to peer, certificate invalid: "
+ "certificate revoked '%s'",
+ fromHostIP, X509_verify_cert_error_string(iVerErr));
+ ABORT_FINALIZE(RS_RET_CERT_EXPIRED);
+ } else {
+ LogMsg(0, RS_RET_CERT_INVALID, LOG_INFO,
+ "net_ossl:TLS session terminated with remote syslog server '%s': "
+ "not permitted to talk to peer, certificate validation failed: %s",
+ fromHostIP, X509_verify_cert_error_string(iVerErr));
+ ABORT_FINALIZE(RS_RET_CERT_INVALID);
+ }
+ } else {
+ dbgprintf("net_ossl_chkpeercertvalidity: client certificate validation success: %s\n",
+ X509_verify_cert_error_string(iVerErr));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+/* Verify Callback for X509 Certificate validation. Force visibility as this function is not called anywhere but
+* only used as callback!
+*/
+int
+net_ossl_verify_callback(int status, X509_STORE_CTX *store)
+{
+ char szdbgdata1[256];
+ char szdbgdata2[256];
+
+ dbgprintf("verify_callback: status %d\n", status);
+
+ if(status == 0) {
+ /* Retrieve all needed pointers */
+ X509 *cert = X509_STORE_CTX_get_current_cert(store);
+ int depth = X509_STORE_CTX_get_error_depth(store);
+ int err = X509_STORE_CTX_get_error(store);
+ SSL* ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx());
+ int iVerifyMode = SSL_get_verify_mode(ssl);
+ nsd_t *pNsdTcp = (nsd_t*) SSL_get_ex_data(ssl, 0);
+ PermitExpiredCerts* pPermitExpiredCerts = (PermitExpiredCerts*) SSL_get_ex_data(ssl, 1);
+
+ dbgprintf("verify_callback: Certificate validation failed, Mode (%d)!\n", iVerifyMode);
+
+ X509_NAME_oneline(X509_get_issuer_name(cert), szdbgdata1, sizeof(szdbgdata1));
+ X509_NAME_oneline(RSYSLOG_X509_NAME_oneline(cert), szdbgdata2, sizeof(szdbgdata2));
+
+ uchar *fromHost = NULL;
+ if (pNsdTcp != NULL) {
+ nsd_ptcp.GetRemoteHName(pNsdTcp, &fromHost);
+ }
+
+ if (iVerifyMode != SSL_VERIFY_NONE) {
+ /* Handle expired Certificates **/
+ if (err == X509_V_OK || err == X509_V_ERR_CERT_HAS_EXPIRED) {
+ if (pPermitExpiredCerts != NULL && *pPermitExpiredCerts == OSSL_EXPIRED_PERMIT) {
+ dbgprintf("verify_callback: EXPIRED cert but PERMITTED at depth: %d \n\t"
+ "issuer = %s\n\t"
+ "subject = %s\n\t"
+ "err %d:%s\n", depth, szdbgdata1, szdbgdata2,
+ err, X509_verify_cert_error_string(err));
+
+ /* Set Status to OK*/
+ status = 1;
+ }
+ else if (pPermitExpiredCerts != NULL && *pPermitExpiredCerts == OSSL_EXPIRED_WARN) {
+ LogMsg(0, RS_RET_CERT_EXPIRED, LOG_WARNING,
+ "Certificate EXPIRED warning at depth: %d \n\t"
+ "issuer = %s\n\t"
+ "subject = %s\n\t"
+ "err %d:%s peer '%s'",
+ depth, szdbgdata1, szdbgdata2,
+ err, X509_verify_cert_error_string(err), fromHost);
+
+ /* Set Status to OK*/
+ status = 1;
+ }
+ else /* also default - if (pPermitExpiredCerts == OSSL_EXPIRED_DENY)*/ {
+ LogMsg(0, RS_RET_CERT_EXPIRED, LOG_ERR,
+ "Certificate EXPIRED at depth: %d \n\t"
+ "issuer = %s\n\t"
+ "subject = %s\n\t"
+ "err %d:%s\n\t"
+ "not permitted to talk to peer '%s', certificate invalid: "
+ "certificate expired",
+ depth, szdbgdata1, szdbgdata2,
+ err, X509_verify_cert_error_string(err), fromHost);
+ }
+ } else if (err == X509_V_ERR_CERT_REVOKED) {
+ LogMsg(0, RS_RET_CERT_REVOKED, LOG_ERR,
+ "Certificate REVOKED at depth: %d \n\t"
+ "issuer = %s\n\t"
+ "subject = %s\n\t"
+ "err %d:%s\n\t"
+ "not permitted to talk to peer '%s', certificate invalid: "
+ "certificate revoked",
+ depth, szdbgdata1, szdbgdata2,
+ err, X509_verify_cert_error_string(err), fromHost);
+ } else {
+ /* all other error codes cause failure */
+ LogMsg(0, RS_RET_NO_ERRCODE, LOG_ERR,
+ "Certificate error at depth: %d \n\t"
+ "issuer = %s\n\t"
+ "subject = %s\n\t"
+ "err %d:%s - peer '%s'",
+ depth, szdbgdata1, szdbgdata2,
+ err, X509_verify_cert_error_string(err), fromHost);
+ }
+ } else {
+ /* do not verify certs in ANON mode, just log into debug */
+ dbgprintf("verify_callback: Certificate validation DISABLED but Error at depth: %d \n\t"
+ "issuer = %s\n\t"
+ "subject = %s\n\t"
+ "err %d:%s\n", depth, szdbgdata1, szdbgdata2,
+ err, X509_verify_cert_error_string(err));
+
+ /* Set Status to OK*/
+ status = 1;
+ }
+ free(fromHost);
+ }
+
+ return status;
+}
+
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+static long
+RSYSLOG_BIO_debug_callback_ex(BIO *bio, int cmd, const char __attribute__((unused)) *argp,
+ size_t __attribute__((unused)) len, int argi, long __attribute__((unused)) argl,
+ int ret, size_t __attribute__((unused)) *processed)
+#else
+static long
+RSYSLOG_BIO_debug_callback(BIO *bio, int cmd, const char __attribute__((unused)) *argp,
+ int argi, long __attribute__((unused)) argl, long ret)
+#endif
+{
+ long ret2 = ret;
+ long r = 1;
+ if (BIO_CB_RETURN & cmd)
+ r = ret;
+ dbgprintf("openssl debugmsg: BIO[%p]: ", (void *)bio);
+ switch (cmd) {
+ case BIO_CB_FREE:
+ dbgprintf("Free - %s\n", RSYSLOG_BIO_method_name(bio));
+ break;
+/* Disabled due API changes for OpenSSL 1.1.0+ */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ case BIO_CB_READ:
+ if (bio->method->type & BIO_TYPE_DESCRIPTOR)
+ dbgprintf("read(%d,%lu) - %s fd=%d\n",
+ RSYSLOG_BIO_number_read(bio), (unsigned long)argi,
+ RSYSLOG_BIO_method_name(bio), RSYSLOG_BIO_number_read(bio));
+ else
+ dbgprintf("read(%d,%lu) - %s\n", RSYSLOG_BIO_number_read(bio),
+ (unsigned long)argi, RSYSLOG_BIO_method_name(bio));
+ break;
+ case BIO_CB_WRITE:
+ if (bio->method->type & BIO_TYPE_DESCRIPTOR)
+ dbgprintf("write(%d,%lu) - %s fd=%d\n",
+ RSYSLOG_BIO_number_written(bio), (unsigned long)argi,
+ RSYSLOG_BIO_method_name(bio), RSYSLOG_BIO_number_written(bio));
+ else
+ dbgprintf("write(%d,%lu) - %s\n",
+ RSYSLOG_BIO_number_written(bio),
+ (unsigned long)argi,
+ RSYSLOG_BIO_method_name(bio));
+ break;
+#else
+ case BIO_CB_READ:
+ dbgprintf("read %s\n", RSYSLOG_BIO_method_name(bio));
+ break;
+ case BIO_CB_WRITE:
+ dbgprintf("write %s\n", RSYSLOG_BIO_method_name(bio));
+ break;
+#endif
+ case BIO_CB_PUTS:
+ dbgprintf("puts() - %s\n", RSYSLOG_BIO_method_name(bio));
+ break;
+ case BIO_CB_GETS:
+ dbgprintf("gets(%lu) - %s\n", (unsigned long)argi,
+ RSYSLOG_BIO_method_name(bio));
+ break;
+ case BIO_CB_CTRL:
+ dbgprintf("ctrl(%lu) - %s\n", (unsigned long)argi,
+ RSYSLOG_BIO_method_name(bio));
+ break;
+ case BIO_CB_RETURN | BIO_CB_READ:
+ dbgprintf("read return %ld\n", ret2);
+ break;
+ case BIO_CB_RETURN | BIO_CB_WRITE:
+ dbgprintf("write return %ld\n", ret2);
+ break;
+ case BIO_CB_RETURN | BIO_CB_GETS:
+ dbgprintf("gets return %ld\n", ret2);
+ break;
+ case BIO_CB_RETURN | BIO_CB_PUTS:
+ dbgprintf("puts return %ld\n", ret2);
+ break;
+ case BIO_CB_RETURN | BIO_CB_CTRL:
+ dbgprintf("ctrl return %ld\n", ret2);
+ break;
+ default:
+ dbgprintf("bio callback - unknown type (%d)\n", cmd);
+ break;
+ }
+
+ return (r);
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+// Requires at least OpenSSL v1.1.1
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+static int
+net_ossl_generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)
+{
+ unsigned char result[EVP_MAX_MD_SIZE];
+ unsigned int resultlength;
+ unsigned char *sslHello;
+ unsigned int length;
+
+ sslHello = (unsigned char *) "rsyslog";
+ length = strlen((char *)sslHello);
+
+ // Generate the cookie using SHA256 hash
+ if (!EVP_Digest(sslHello, length, result, &resultlength, EVP_sha256(), NULL)) {
+ return 0;
+ }
+
+ memcpy(cookie, result, resultlength);
+ *cookie_len = resultlength;
+ dbgprintf("net_ossl_generate_cookie: generate cookie SUCCESS\n");
+
+ return 1;
+}
+#pragma GCC diagnostic pop
+
+static int
+net_ossl_verify_cookie(SSL *ssl, const unsigned char *cookie, unsigned int cookie_len)
+{
+ unsigned char cookie_gen[EVP_MAX_MD_SIZE];
+ unsigned int cookie_gen_len;
+
+ // Generate a cookie using the same method as in net_ossl_generate_cookie
+ if (!net_ossl_generate_cookie(ssl, cookie_gen, &cookie_gen_len)) {
+ dbgprintf("net_ossl_verify_cookie: generate cookie FAILED\n");
+ return 0;
+ }
+
+ // Check if the generated cookie matches the cookie received
+ if (cookie_len == cookie_gen_len && memcmp(cookie, cookie_gen, cookie_len) == 0) {
+ dbgprintf("net_ossl_verify_cookie: compare cookie SUCCESS\n");
+ return 1;
+ }
+
+ dbgprintf("net_ossl_verify_cookie: compare cookie FAILED\n");
+ return 0;
+}
+
+static rsRetVal
+net_ossl_ctx_init_cookie(net_ossl_t *pThis)
+{
+ DEFiRet;
+ // Set our cookie generation and verification callbacks
+ SSL_CTX_set_options(pThis->ctx, SSL_OP_COOKIE_EXCHANGE);
+ SSL_CTX_set_cookie_generate_cb(pThis->ctx, net_ossl_generate_cookie);
+ SSL_CTX_set_cookie_verify_cb(pThis->ctx, net_ossl_verify_cookie);
+ RETiRet;
+}
+#endif // OPENSSL_VERSION_NUMBER >= 0x10100000L
+
+/* ------------------------------ end OpenSSL helpers ------------------------------------------*/
+
+/* ------------------------------ OpenSSL Callback set helpers ---------------------------------*/
+void
+net_ossl_set_ssl_verify_callback(SSL *pSsl, int flags)
+{
+ /* Enable certificate valid checking */
+ SSL_set_verify(pSsl, flags, net_ossl_verify_callback);
+}
+
+void
+net_ossl_set_ctx_verify_callback(SSL_CTX *pCtx, int flags)
+{
+ /* Enable certificate valid checking */
+ SSL_CTX_set_verify(pCtx, flags, net_ossl_verify_callback);
+}
+
+void
+net_ossl_set_bio_callback(BIO *conn)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ BIO_set_callback_ex(conn, RSYSLOG_BIO_debug_callback_ex);
+#else
+ BIO_set_callback(conn, RSYSLOG_BIO_debug_callback);
+#endif // OPENSSL_VERSION_NUMBER >= 0x10100000L
+}
+/* ------------------------------ End OpenSSL Callback set helpers -----------------------------*/
+
+
+/* Standard-Constructor */
+BEGINobjConstruct(net_ossl) /* be sure to specify the object type also in END macro! */
+ DBGPRINTF("net_ossl_construct: [%p]\n", pThis);
+ pThis->bReportAuthErr = 1;
+ENDobjConstruct(net_ossl)
+
+/* destructor for the net_ossl object */
+BEGINobjDestruct(net_ossl) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(net_ossl)
+ DBGPRINTF("net_ossl_destruct: [%p]\n", pThis);
+ /* Free SSL obj also if we do not have a session - or are NOT in TLS mode! */
+ if (pThis->ssl != NULL) {
+ DBGPRINTF("net_ossl_destruct: [%p] FREE pThis->ssl \n", pThis);
+ SSL_free(pThis->ssl);
+ pThis->ssl = NULL;
+ }
+ if(pThis->ctx != NULL && !pThis->ctx_is_copy) {
+ SSL_CTX_free(pThis->ctx);
+ }
+ free((void*) pThis->pszCAFile);
+ free((void*) pThis->pszCRLFile);
+ free((void*) pThis->pszKeyFile);
+ free((void*) pThis->pszCertFile);
+ free((void*) pThis->pszExtraCAFiles);
+ENDobjDestruct(net_ossl)
+
+/* queryInterface function */
+BEGINobjQueryInterface(net_ossl)
+CODESTARTobjQueryInterface(net_ossl)
+ DBGPRINTF("netosslQueryInterface\n");
+ if(pIf->ifVersion != net_osslCURR_IF_VERSION) {/* check for current version, increment on each change */
+ ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
+ }
+ pIf->Construct = (rsRetVal(*)(net_ossl_t**)) net_osslConstruct;
+ pIf->Destruct = (rsRetVal(*)(net_ossl_t**)) net_osslDestruct;
+ pIf->osslCtxInit = net_ossl_osslCtxInit;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ pIf->osslCtxInitCookie = net_ossl_ctx_init_cookie;
+#endif
+finalize_it:
+ENDobjQueryInterface(net_ossl)
+
+
+/* exit our class
+ */
+BEGINObjClassExit(net_ossl, OBJ_IS_CORE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(net_ossl)
+ DBGPRINTF("netosslClassExit\n");
+ /* release objects we no longer need */
+ objRelease(nsd_ptcp, LM_NSD_PTCP_FILENAME);
+ objRelease(net, LM_NET_FILENAME);
+ objRelease(glbl, CORE_COMPONENT);
+ /* shut down OpenSSL */
+ osslGlblExit();
+ENDObjClassExit(net_ossl)
+
+
+/* Initialize the net_ossl class. Must be called as the very first method
+ * before anything else is called inside this class.
+ */
+BEGINObjClassInit(net_ossl, 1, OBJ_IS_CORE_MODULE) /* class, version */
+ DBGPRINTF("net_osslClassInit\n");
+ // request objects we use
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(nsd_ptcp, LM_NSD_PTCP_FILENAME));
+ // Do global TLS init stuff
+ osslGlblInit();
+ENDObjClassInit(net_ossl)
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+/* vi:set ai:
+ */