rsyslog/runtime/net_ossl.c
Andre Lorbach 35710d4716 Fix: Remove unsafe function pointer casting in cfsysline.c
- Replaced function pointer casting with direct handler calls for type safety
- Fixes crashes (BUS errors) on ARM64 macOS 14+ due to strict calling conventions
- Root cause identified by ThreadSanitizer
- Eliminates undefined behavior, improves code safety
2025-08-01 11:49:10 +02:00

1292 lines
51 KiB
C

/* 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)
/* Prototypes for openssl helper functions */
void net_ossl_lastOpenSSLErrorMsg(
uchar *fromHost, int ret, SSL *ssl, int severity, const char *pszCallSource, const char *pszOsslApi);
void net_ossl_set_ssl_verify_callback(SSL *pSsl, int flags);
void net_ossl_set_ctx_verify_callback(SSL_CTX *pCtx, int flags);
void net_ossl_set_bio_callback(BIO *conn);
int net_ossl_verify_callback(int status, X509_STORE_CTX *store);
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
rsRetVal net_ossl_apply_tlscgfcmd(net_ossl_t *pThis, uchar *tlscfgcmd);
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
rsRetVal net_ossl_chkpeercertvalidity(net_ossl_t *pThis, SSL *ssl, uchar *fromHostIP);
X509 *net_ossl_getpeercert(net_ossl_t *pThis, SSL *ssl, uchar *fromHostIP);
rsRetVal net_ossl_peerfingerprint(net_ossl_t *pThis, X509 *certpeer, uchar *fromHostIP);
rsRetVal net_ossl_chkpeername(net_ossl_t *pThis, X509 *certpeer, uchar *fromHostIP);
/*--------------------------------------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("osslGlblInit: ENTER\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
PRAGMA_DIAGNOSTIC_PUSH
PRAGMA_IGNORE_Wdeprecated_declarations
#ifndef OPENSSL_NO_ENGINE
// Initialize OpenSSL engine library
ENGINE_load_builtin_engines();
/* Register all of them for every algorithm they collectively implement */
ENGINE_register_all_complete();
// Iterate through all available engines
ENGINE *osslEngine = ENGINE_get_first();
const char *engine_id = NULL;
const char *engine_name = NULL;
while (osslEngine) {
// Print engine ID and name if the engine is loaded
if (ENGINE_get_init_function(osslEngine)) { // Check if engine is initialized
engine_id = ENGINE_get_id(osslEngine);
engine_name = ENGINE_get_name(osslEngine);
DBGPRINTF("osslGlblInit: Loaded Engine: ID = %s, Name = %s\n", engine_id, engine_name);
}
osslEngine = ENGINE_get_next(osslEngine);
}
// Free the engine reference when done
ENGINE_free(osslEngine);
#else
DBGPRINTF("osslGlblInit: OpenSSL compiled without ENGINE support - ENGINE support disabled\n");
#endif /* OPENSSL_NO_ENGINE */
PRAGMA_DIAGNOSTIC_POP
}
/* globally de-initialize OpenSSL */
void osslGlblExit(void) {
DBGPRINTF("openssl: entering osslGlblExit\n");
#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
#endif
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 >= 0x10100000L
/* Enable Support for automatic ephemeral/temporary DH parameter selection. */
SSL_CTX_set_dh_auto(pThis->ctx, 1);
#endif
#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));
}
}
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
/* 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;
}
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
/* 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;
unsigned int x509flags = 0;
#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 )
* if prioritizeSAN set, only check against SAN
*/
if (pThis->bSANpriority == 1) {
#if OPENSSL_VERSION_NUMBER >= 0x10100004L && !defined(LIBRESSL_VERSION_NUMBER)
x509flags = X509_CHECK_FLAG_NEVER_CHECK_SUBJECT;
#else
dbgprintf("net_ossl_chkonepeername: PrioritizeSAN not supported before OpenSSL 1.1.0\n");
#endif // OPENSSL_VERSION_NUMBER >= 0x10100004L
}
osslRet = X509_check_host(certpeer, (const char *)pPeer->pszID, strlen((const char *)pPeer->pszID),
x509flags, 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,
"net_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);
if (pstrFingerprintSha256 != NULL) cstrDestruct(&pstrFingerprintSha256);
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,
"net_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,
"net_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_DIAGNOSTIC_PUSH
PRAGMA_IGNORE_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_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_init_engine(__attribute__((unused)) net_ossl_t *pThis) {
// OpenSSL Engine Support feature relies on an outdated version of OpenSSL and is
// strictly experimental. No support or guarantees are provided. Use at your own risk.
DEFiRet;
#ifndef OPENSSL_NO_ENGINE
const char *engine_id = NULL;
const char *engine_name = NULL;
PRAGMA_DIAGNOSTIC_PUSH
PRAGMA_IGNORE_Wdeprecated_declarations
// Get the default RSA engine
ENGINE *default_engine = ENGINE_get_default_RSA();
if (default_engine) {
engine_id = ENGINE_get_id(default_engine);
engine_name = ENGINE_get_name(default_engine);
DBGPRINTF("net_ossl_init_engine: Default RSA Engine: ID = %s, Name = %s\n", engine_id, engine_name);
// Free the engine reference when done
ENGINE_free(default_engine);
} else {
DBGPRINTF("net_ossl_init_engine: No default RSA Engine set.\n");
}
/* Setting specific Engine */
if (runConf != NULL && glbl.GetDfltOpensslEngine(runConf) != NULL) {
default_engine = ENGINE_by_id((char *)glbl.GetDfltOpensslEngine(runConf));
if (default_engine && ENGINE_init(default_engine)) {
/* engine initialised */
ENGINE_set_default_DSA(default_engine);
ENGINE_set_default_ciphers(default_engine);
/* Switch to Engine */
DBGPRINTF("net_ossl_init_engine: Changed default Engine to %s\n", glbl.GetDfltOpensslEngine(runConf));
/* Release the functional reference from ENGINE_init() */
ENGINE_finish(default_engine);
} else {
LogError(0, RS_RET_VALUE_NOT_SUPPORTED,
"error: ENGINE_init failed to load Engine '%s'"
"ossl netstream driver",
glbl.GetDfltOpensslEngine(runConf));
net_ossl_lastOpenSSLErrorMsg(NULL, 0, NULL, LOG_ERR, "net_ossl_init_engine", "ENGINE_init");
}
// Free the engine reference when done
ENGINE_free(default_engine);
} else {
DBGPRINTF("net_ossl_init_engine: use openssl default Engine");
}
PRAGMA_DIAGNOSTIC_POP
#else
DBGPRINTF("net_ossl_init_engine: OpenSSL compiled without ENGINE support - ENGINE support disabled\n");
#endif /* OPENSSL_NO_ENGINE */
RETiRet;
}
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;
pThis->bSANpriority = 0;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
CHKiRet(net_ossl_init_engine(pThis));
finalize_it:
#endif
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;
pIf->osslChkpeername = net_ossl_chkpeername;
pIf->osslPeerfingerprint = net_ossl_peerfingerprint;
pIf->osslGetpeercert = net_ossl_getpeercert;
pIf->osslChkpeercertvalidity = net_ossl_chkpeercertvalidity;
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
pIf->osslApplyTlscgfcmd = net_ossl_apply_tlscgfcmd;
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
pIf->osslSetBioCallback = net_ossl_set_bio_callback;
pIf->osslSetCtxVerifyCallback = net_ossl_set_ctx_verify_callback;
pIf->osslSetSslVerifyCallback = net_ossl_set_ssl_verify_callback;
pIf->osslLastOpenSSLErrorMsg = net_ossl_lastOpenSSLErrorMsg;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
pIf->osslCtxInitCookie = net_ossl_ctx_init_cookie;
pIf->osslInitEngine = net_ossl_init_engine;
#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:
*/