Skip to content

Commit

Permalink
PR5175: Add TLS CRL Support for GnuTLS driver and OpenSSL 1.0.2+
Browse files Browse the repository at this point in the history
- Add TLS CRL support tp GnuTLS driver using gnutls_certificate_set_x509_crl_file.
- Add code in OpenSSL driver that works with OpenSSL 1.0.2 and higher.
  Disable feature on older features with error message.
- Some cosmetic changes
- testbench: Add revoked certificate for testing (Including CRL PEM and other files)
- testbench: Add testcase for gtls and ossl testing revoked certificates
  • Loading branch information
alorbach committed Jul 7, 2023
1 parent 844d179 commit 6c83bff
Show file tree
Hide file tree
Showing 24 changed files with 1,012 additions and 34 deletions.
28 changes: 21 additions & 7 deletions runtime/nsd_gtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@
#include "unicode-helper.h"
#include "rsconf.h"

/* things to move to some better place/functionality - TODO */
#define CRLFILE "crl.pem"


#if GNUTLS_VERSION_NUMBER <= 0x020b00
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
Expand Down Expand Up @@ -732,7 +728,8 @@ gtlsInitCred(nsd_gtls_t *const pThis )
} else if(gnuRet < 0) {
/* TODO; a more generic error-tracking function (this one based on CHKgnutls()) */
uchar *pErr = gtlsStrerror(gnuRet);
LogError(0, RS_RET_GNUTLS_ERR, "unexpected GnuTLS error %d in %s:%d: %s\n",
LogError(0, RS_RET_GNUTLS_ERR,
"unexpected GnuTLS error reading CA certificate file %d in %s:%d: %s\n",
gnuRet, __FILE__, __LINE__, pErr);
free(pErr);
ABORT_FINALIZE(RS_RET_GNUTLS_ERR);
Expand All @@ -741,8 +738,24 @@ gtlsInitCred(nsd_gtls_t *const pThis )

crlfile = (pThis->pszCRLFile == NULL) ? glbl.GetDfltNetstrmDrvrCRLF(runConf) : pThis->pszCRLFile;
if(crlfile == NULL) {
LogMsg(0, RS_RET_VALUE_NOT_SUPPORTED, LOG_WARNING,
"Warning: CRL not supported with gtls netstream driver");
dbgprintf("Certificate revocation list (CRL) file not set.");
} else {
dbgprintf("GTLS CRL file: '%s'\n", crlfile);
gnuRet = gnutls_certificate_set_x509_crl_file(pThis->xcred, (char*)crlfile, GNUTLS_X509_FMT_PEM);
if(gnuRet == GNUTLS_E_FILE_ERROR) {
LogError(0, RS_RET_GNUTLS_ERR,
"error reading Certificate revocation list (CRL) '%s' - a common cause is that the "
"file does not exist", crlfile);
ABORT_FINALIZE(RS_RET_GNUTLS_ERR);
} else if(gnuRet < 0) {
/* TODO; a more generic error-tracking function (this one based on CHKgnutls()) */
uchar *pErr = gtlsStrerror(gnuRet);
LogError(0, RS_RET_GNUTLS_ERR,
"unexpected GnuTLS error reading Certificate revocation list (CRL) %d in %s:%d: %s\n",
gnuRet, __FILE__, __LINE__, pErr);
free(pErr);
ABORT_FINALIZE(RS_RET_GNUTLS_ERR);
}
}

finalize_it:
Expand Down Expand Up @@ -1235,6 +1248,7 @@ gtlsChkPeerCertValidity(nsd_gtls_t *pThis)
} else if(stateCert & GNUTLS_CERT_REVOKED) {
pszErrCause = "certificate revoked";
bAbort = RSTRUE;
iAbortCode = RS_RET_CERT_REVOKED;
#ifdef EXTENDED_CERT_CHECK_AVAILABLE
} else if(stateCert & GNUTLS_CERT_PURPOSE_MISMATCH) {
pszErrCause = "key purpose OID does not match";
Expand Down
108 changes: 87 additions & 21 deletions runtime/nsd_ossl.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@
#include "nsd_ossl.h"
#include "unicode-helper.h"
#include "rsconf.h"
/* things to move to some better place/functionality - TODO */
// #define CRLFILE "crl.pem"


MODULE_TYPE_LIB
MODULE_TYPE_KEEP
Expand All @@ -86,6 +83,7 @@ DEFobjCurrIf(nsd_ptcp)
#endif

static rsRetVal applyGnutlsPriorityString(nsd_ossl_t *const pNsd);
static rsRetVal osslChkPeerCertValidity(nsd_ossl_t *pThis);

/*--------------------------------------MT OpenSSL helpers ------------------------------------------*/
static MUTEX_TYPE *mutex_buf = NULL;
Expand Down Expand Up @@ -256,7 +254,7 @@ int verify_callback(int status, X509_STORE_CTX *store)
status = 1;
}
else if (pThis->permitExpiredCerts == OSSL_EXPIRED_WARN) {
LogMsg(0, RS_RET_NO_ERRCODE, LOG_WARNING,
LogMsg(0, RS_RET_CERT_EXPIRED, LOG_WARNING,
"Certificate EXPIRED warning at depth: %d \n\t"
"issuer = %s\n\t"
"subject = %s\n\t"
Expand All @@ -268,22 +266,35 @@ int verify_callback(int status, X509_STORE_CTX *store)
status = 1;
}
else /* also default - if (pThis->permitExpiredCerts == OSSL_EXPIRED_DENY)*/ {
LogMsg(0, RS_RET_NO_ERRCODE, LOG_ERR,
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",
"err %d:%s\n\t"
"not permitted to talk to peer, certificate invalid: "
"certificate expired",
depth, szdbgdata1, szdbgdata2,
err, X509_verify_cert_error_string(err));
}
} 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, certificate invalid: "
"certificate revoked",
depth, szdbgdata1, szdbgdata2,
err, X509_verify_cert_error_string(err));
} 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",
depth, szdbgdata1, szdbgdata2, err, X509_verify_cert_error_string(err));
depth, szdbgdata1, szdbgdata2,
err, X509_verify_cert_error_string(err));
}
} else {
/* do not verify certs in ANON mode, just log into debug */
Expand Down Expand Up @@ -871,7 +882,7 @@ osslChkPeerCertValidity(nsd_ossl_t *pThis)
if (pThis->permitExpiredCerts == OSSL_EXPIRED_DENY) {
LogMsg(0, RS_RET_CERT_EXPIRED, LOG_INFO,
"nsd_ossl:TLS session terminated with remote syslog server '%s': "
"not permitted to talk to peer, "
"not permitted to talk to peer, certificate invalid: "
"Certificate expired: %s",
fromHostIP, X509_verify_cert_error_string(iVerErr));
ABORT_FINALIZE(RS_RET_CERT_EXPIRED);
Expand All @@ -885,11 +896,17 @@ osslChkPeerCertValidity(nsd_ossl_t *pThis)
"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,
"nsd_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,
"nsd_ossl:TLS session terminated with remote syslog server '%s': "
"not permitted to talk to peer, "
"Certificate validation failed: %s",
"not permitted to talk to peer, certificate validation failed: %s",
fromHostIP, X509_verify_cert_error_string(iVerErr));
ABORT_FINALIZE(RS_RET_CERT_INVALID);
}
Expand Down Expand Up @@ -1288,14 +1305,15 @@ osslInit_ctx(nsd_ossl_t *const pThis)
"Warning: CA certificate is not set");
bHaveCA = 0;
} else {
dbgprintf("OSSL CA file: '%s'\n", caFile);
bHaveCA = 1;
}
crlFile = (char*) ((pThis->pszCRLFile == NULL) ? glbl.GetDfltNetstrmDrvrCRLF(runConf) : pThis->pszCRLFile);
if(crlFile == NULL) {
LogMsg(0, RS_RET_CA_CERT_MISSING, LOG_WARNING,
"Warning: CRL file is not set");
dbgprintf("Certificate revocation list (CRL) file not set.");
bHaveCRL = 0;
} else {
dbgprintf("OSSL CRL file: '%s'\n", crlFile);
bHaveCRL = 1;
}
certFile = (char*) ((pThis->pszCertFile == NULL) ?
Expand All @@ -1305,6 +1323,7 @@ osslInit_ctx(nsd_ossl_t *const pThis)
"Warning: Certificate file is not set");
bHaveCert = 0;
} else {
dbgprintf("OSSL CERT file: '%s'\n", certFile);
bHaveCert = 1;
}
keyFile = (char*) ((pThis->pszKeyFile == NULL) ? glbl.GetDfltNetstrmDrvrKeyFile(runConf) : pThis->pszKeyFile);
Expand All @@ -1313,13 +1332,15 @@ osslInit_ctx(nsd_ossl_t *const pThis)
"Warning: Key file is not set");
bHaveKey = 0;
} else {
dbgprintf("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("OSSL EXTRA CA files: '%s'\n", extraCaFiles);
bHaveExtraCAFiles = 1;
}

Expand All @@ -1331,7 +1352,7 @@ osslInit_ctx(nsd_ossl_t *const pThis)
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. "
"Open ssl error info may follow in next messages");
"OpenSSL error info may follow in next messages");
osslLastSSLErrorMsg(0, NULL, LOG_ERR, "osslGlblInit", "SSL_CTX_load_verify_locations");
ABORT_FINALIZE(RS_RET_TLS_CERT_ERR);
}
Expand All @@ -1341,35 +1362,80 @@ osslInit_ctx(nsd_ossl_t *const pThis)
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. "
"Open ssl error info may follow in next messages");
"OpenSSL error info may follow in next messages");
osslLastSSLErrorMsg(0, NULL, LOG_ERR, "osslGlblInit", "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_TLS_CERT_ERR, "Error: CRL could not be accessed. "
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. "
"Open ssl error info may follow in next messages");
osslLastSSLErrorMsg(0, NULL, LOG_ERR, "osslGlblInit");
ABORT_FINALIZE(RS_RET_TLS_CERT_ERR);
"OpenSSL error info may follow in next messages");
osslLastSSLErrorMsg(0, NULL, LOG_ERR, "osslGlblInit", "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");
osslLastSSLErrorMsg(0, NULL, LOG_ERR, "osslGlblInit", "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");
osslLastSSLErrorMsg(0, NULL, LOG_ERR, "osslGlblInit", "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");
osslLastSSLErrorMsg(0, NULL, LOG_ERR, "osslGlblInit", "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. "
"Open ssl error info may follow in next messages");
"OpenSSL error info may follow in next messages");
osslLastSSLErrorMsg(0, NULL, LOG_ERR, "osslGlblInit", "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. "
"Open ssl error info may follow in next messages");
"OpenSSL error info may follow in next messages");
osslLastSSLErrorMsg(0, NULL, LOG_ERR, "osslGlblInit", "SSL_CTX_use_PrivateKey_file");
ABORT_FINALIZE(RS_RET_TLS_KEY_ERR);
}
Expand Down Expand Up @@ -2040,7 +2106,7 @@ applyGnutlsPriorityString(nsd_ossl_t *const pThis)
iConfErr = SSL_CONF_CTX_finish(cctx);
if (!iConfErr) {
LogError(0, RS_RET_SYS_ERR, "Error: setting openssl command parameters: %s"
"Open ssl error info may follow in next messages",
"OpenSSL error info may follow in next messages",
pThis->gnutlsPriorityString);
osslLastSSLErrorMsg(0, NULL, LOG_ERR, "SetGnutlsPriorityString", "SSL_CONF_CTX_finish");
}
Expand Down
3 changes: 3 additions & 0 deletions runtime/rsyslog.h
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,9 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
RS_RET_CA_CERT_MISSING = -2329,/**< a CA cert is missing where one is required (e.g. TLS) */
RS_RET_CERT_MISSING = -2330,/**< a cert is missing where one is required (e.g. TLS) */
RS_RET_CERTKEY_MISSING = -2331,/**< a cert (private) key is missing where one is required (e.g. TLS) */
RS_RET_CRL_MISSING = -2332,/**< a CRL file is missing but not required (e.g. TLS) */
RS_RET_CRL_INVALID = -2333, /**< a CRL file PEM file failed validation */
RS_RET_CERT_REVOKED = -2334, /**< a certificate has been revoked */
RS_RET_STRUC_DATA_INVLD = -2349,/**< structured data is malformed */

/* up to 2350 reserved for 7.4 */
Expand Down
21 changes: 21 additions & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,7 @@ TESTS += \
sndrcv_tls_certvalid_action_level.sh \
sndrcv_tls_certvalid_expired.sh \
sndrcv_tls_certvalid_expired_defaultmode.sh \
sndrcv_tls_certvalid_revoked.sh \
imtcp-tls-no-lstn-startup.sh \
imtcp-tls-gtls-x509fingerprint-invld.sh \
imtcp-tls-gtls-x509fingerprint.sh \
Expand Down Expand Up @@ -1443,6 +1444,7 @@ TESTS += \
sndrcv_tls_ossl_certvalid_expired.sh \
sndrcv_tls_ossl_certvalid_tlscommand.sh \
sndrcv_tls_ossl_certvalid_ciphers.sh \
sndrcv_tls_ossl_certvalid_revoked.sh \
imtcp-tls-ossl-x509valid.sh \
imtcp-tls-ossl-x509name.sh \
imtcp-tls-ossl-x509fingerprint.sh \
Expand Down Expand Up @@ -1859,6 +1861,23 @@ EXTRA_DIST= \
tls-certs/ca-fail.pem \
tls-certs/cert-fail.pem \
tls-certs/key-fail.pem \
testsuites/x.509/ca.srl \
testsuites/x.509/client-cert-new.pem \
testsuites/x.509/client-new.csr \
testsuites/x.509/client-revoked-key.pem \
testsuites/x.509/client-revoked-valid.pem \
testsuites/x.509/client-revoked.csr \
testsuites/x.509/client-revoked.pem \
testsuites/x.509/crl.pem \
testsuites/x.509/index.txt \
testsuites/x.509/index.txt.attr \
testsuites/x.509/newcerts/01.pem \
testsuites/x.509/newcerts/02.pem \
testsuites/x.509/newcerts/03.pem \
testsuites/x.509/newcerts/04.pem \
testsuites/x.509/openssl-cmds.sh \
testsuites/x.509/openssl.cnf \
testsuites/x.509/serial \
testsuites/x.509/ca.pem \
testsuites/x.509/ca-key.pem \
testsuites/x.509/client-cert.pem \
Expand Down Expand Up @@ -2366,6 +2385,7 @@ EXTRA_DIST= \
sndrcv_tls_ossl_certvalid_expired.sh \
sndrcv_tls_ossl_certvalid_tlscommand.sh \
sndrcv_tls_ossl_certvalid_ciphers.sh \
sndrcv_tls_ossl_certvalid_revoked.sh \
imtcp-tls-ossl-x509valid.sh \
imtcp-tls-ossl-x509name.sh \
imtcp-tls-ossl-x509fingerprint.sh \
Expand Down Expand Up @@ -2927,6 +2947,7 @@ EXTRA_DIST= \
sndrcv_tls_certvalid_action_level.sh \
sndrcv_tls_certvalid_expired.sh \
sndrcv_tls_certvalid_expired_defaultmode.sh \
sndrcv_tls_certvalid_revoked.sh \
sndrcv_tls_certless_clientonly.sh \
sndrcv_tls_gtls_servercert_gtls_clientanon.sh \
sndrcv_tls_gtls_servercert_gtls_clientanon_legacy.sh \
Expand Down

0 comments on commit 6c83bff

Please sign in to comment.