summaryrefslogtreecommitdiffstats
path: root/runtime/nsd_gtls.c
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/nsd_gtls.c')
-rw-r--r--runtime/nsd_gtls.c2451
1 files changed, 2451 insertions, 0 deletions
diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c
new file mode 100644
index 0000000..b9c0f8a
--- /dev/null
+++ b/runtime/nsd_gtls.c
@@ -0,0 +1,2451 @@
+/* nsd_gtls.c
+ *
+ * An implementation of the nsd interface for GnuTLS.
+ *
+ * Copyright (C) 2007-2021 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of the rsyslog runtime library.
+ *
+ * The rsyslog runtime library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The rsyslog runtime library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the rsyslog runtime library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution.
+ */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#if GNUTLS_VERSION_NUMBER <= 0x020b00
+# include <gcrypt.h>
+#endif
+#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 "cfsysline.h"
+#include "obj.h"
+#include "stringbuf.h"
+#include "errmsg.h"
+#include "net.h"
+#include "datetime.h"
+#include "netstrm.h"
+#include "netstrms.h"
+#include "nsd_ptcp.h"
+#include "nsdsel_gtls.h"
+#include "nsd_gtls.h"
+#include "unicode-helper.h"
+#include "rsconf.h"
+
+#if GNUTLS_VERSION_NUMBER <= 0x020b00
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+#endif
+MODULE_TYPE_LIB
+MODULE_TYPE_KEEP
+
+/* static data */
+DEFobjStaticHelpers
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(net)
+DEFobjCurrIf(datetime)
+DEFobjCurrIf(nsd_ptcp)
+
+/* Static Helper variables for certless communication */
+static gnutls_anon_client_credentials_t anoncred; /**< client anon credentials */
+static gnutls_anon_server_credentials_t anoncredSrv; /**< server anon credentials */
+static int dhBits = 2048; /**< number of bits for Diffie-Hellman key */
+static int dhMinBits = 512; /**< minimum number of bits for Diffie-Hellman key */
+
+static pthread_mutex_t mutGtlsStrerror;
+/*< a mutex protecting the potentially non-reentrant gtlStrerror() function */
+
+static gnutls_dh_params_t dh_params; /**< server DH parameters for anon mode */
+
+/* a macro to abort if GnuTLS error is not acceptable. We split this off from
+ * CHKgnutls() to avoid some Coverity report in cases where we know GnuTLS
+ * failed. Note: gnuRet must already be set accordingly!
+ */
+#define ABORTgnutls { \
+ uchar *pErr = gtlsStrerror(gnuRet); \
+ LogError(0, RS_RET_GNUTLS_ERR, "unexpected GnuTLS error %d in %s:%d: %s\n", \
+ gnuRet, __FILE__, __LINE__, pErr); \
+ free(pErr); \
+ ABORT_FINALIZE(RS_RET_GNUTLS_ERR); \
+}
+/* a macro to check GnuTLS calls against unexpected errors */
+#define CHKgnutls(x) { \
+ gnuRet = (x); \
+ if(gnuRet == GNUTLS_E_FILE_ERROR) { \
+ LogError(0, RS_RET_GNUTLS_ERR, "error reading file - a common cause is that the " \
+ "file does not exist"); \
+ ABORT_FINALIZE(RS_RET_GNUTLS_ERR); \
+ } else if(gnuRet != 0) { \
+ ABORTgnutls; \
+ } \
+}
+
+
+/* ------------------------------ GnuTLS specifics ------------------------------ */
+
+/* This defines a log function to be provided to GnuTLS. It hopefully
+ * helps us track down hard to find problems.
+ * rgerhards, 2008-06-20
+ */
+static void logFunction(int level, const char *msg)
+{
+ dbgprintf("GnuTLS log msg, level %d: %s\n", level, msg);
+}
+
+/* read in the whole content of a file. The caller is responsible for
+ * freeing the buffer. To prevent DOS, this function can NOT read
+ * files larger than 1MB (which still is *very* large).
+ * rgerhards, 2008-05-26
+ */
+static rsRetVal
+readFile(const uchar *const pszFile, gnutls_datum_t *const pBuf)
+{
+ int fd;
+ struct stat stat_st;
+ DEFiRet;
+
+ assert(pszFile != NULL);
+ assert(pBuf != NULL);
+
+ pBuf->data = NULL;
+
+ if((fd = open((char*)pszFile, O_RDONLY)) == -1) {
+ LogError(errno, RS_RET_FILE_NOT_FOUND, "can not read file '%s'", pszFile);
+ ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
+ }
+
+ if(fstat(fd, &stat_st) == -1) {
+ LogError(errno, RS_RET_FILE_NO_STAT, "can not stat file '%s'", pszFile);
+ ABORT_FINALIZE(RS_RET_FILE_NO_STAT);
+ }
+
+ /* 1MB limit */
+ if(stat_st.st_size > 1024 * 1024) {
+ LogError(0, RS_RET_FILE_TOO_LARGE, "file '%s' too large, max 1MB", pszFile);
+ ABORT_FINALIZE(RS_RET_FILE_TOO_LARGE);
+ }
+
+ CHKmalloc(pBuf->data = malloc(stat_st.st_size));
+ pBuf->size = stat_st.st_size;
+ if(read(fd, pBuf->data, stat_st.st_size) != stat_st.st_size) {
+ LogError(0, RS_RET_IO_ERROR, "error or incomplete read of file '%s'", pszFile);
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ }
+
+finalize_it:
+ if(fd != -1)
+ close(fd);
+ if(iRet != RS_RET_OK) {
+ if(pBuf->data != NULL) {
+ free(pBuf->data);
+ pBuf->data = NULL;
+ pBuf->size = 0;
+ }
+ }
+ RETiRet;
+}
+
+
+/* Load the certificate and the private key into our own store. We need to do
+ * this in the client case, to support fingerprint authentication. In that case,
+ * we may be presented no matching root certificate, but we must provide ours.
+ * The only way to do that is via the cert callback interface, but for it we
+ * need to load certificates into our private store.
+ * rgerhards, 2008-05-26
+ */
+static rsRetVal
+gtlsLoadOurCertKey(nsd_gtls_t *pThis)
+{
+ DEFiRet;
+ int gnuRet;
+ gnutls_datum_t data = { NULL, 0 };
+ const uchar *keyFile;
+ const uchar *certFile;
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ certFile = (pThis->pszCertFile == NULL) ? glbl.GetDfltNetstrmDrvrCertFile(runConf) : pThis->pszCertFile;
+ keyFile = (pThis->pszKeyFile == NULL) ? glbl.GetDfltNetstrmDrvrKeyFile(runConf) : pThis->pszKeyFile;
+
+ if(certFile == NULL || keyFile == NULL) {
+ /* in this case, we can not set our certificate. If we are
+ * a client and the server is running in "anon" auth mode, this
+ * may be well acceptable. In other cases, we will see some
+ * more error messages down the road. -- rgerhards, 2008-07-02
+ */
+ dbgprintf("gtlsLoadOurCertKey our certificate is not set, file name values are cert: '%s', key: '%s'\n",
+ certFile, keyFile);
+ ABORT_FINALIZE(RS_RET_CERTLESS);
+ }
+
+ /* try load certificate */
+ CHKiRet(readFile(certFile, &data));
+ pThis->nOurCerts = sizeof(pThis->pOurCerts) / sizeof(gnutls_x509_crt_t);
+ gnuRet = gnutls_x509_crt_list_import(pThis->pOurCerts, &pThis->nOurCerts,
+ &data, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
+ if(gnuRet < 0) {
+ ABORTgnutls;
+ }
+ pThis->bOurCertIsInit = 1;
+ free(data.data);
+ data.data = NULL;
+
+ /* try load private key */
+ CHKiRet(readFile(keyFile, &data));
+ CHKgnutls(gnutls_x509_privkey_init(&pThis->ourKey));
+ pThis->bOurKeyIsInit = 1;
+ CHKgnutls(gnutls_x509_privkey_import(pThis->ourKey, &data, GNUTLS_X509_FMT_PEM));
+ free(data.data);
+
+
+finalize_it:
+ if(iRet == RS_RET_CERTLESS) {
+ dbgprintf("gtlsLoadOurCertKey certless exit\n");
+ pThis->bOurCertIsInit = 0;
+ pThis->bOurKeyIsInit = 0;
+ } else if(iRet != RS_RET_OK) {
+ dbgprintf("gtlsLoadOurCertKey error exit\n");
+ if(data.data != NULL)
+ free(data.data);
+ if(pThis->bOurCertIsInit) {
+ for(unsigned i=0; i<pThis->nOurCerts; ++i) {
+ gnutls_x509_crt_deinit(pThis->pOurCerts[i]);
+ }
+ pThis->bOurCertIsInit = 0;
+ }
+ if(pThis->bOurKeyIsInit) {
+ gnutls_x509_privkey_deinit(pThis->ourKey);
+ pThis->bOurKeyIsInit = 0;
+ }
+ } else {
+ dbgprintf("gtlsLoadOurCertKey Successfully Loaded cert '%s' and key: '%s'\n", certFile, keyFile);
+ }
+ RETiRet;
+}
+
+
+/* This callback must be associated with a session by calling
+ * gnutls_certificate_client_set_retrieve_function(session, cert_callback),
+ * before a handshake. We will always return the configured certificate,
+ * even if it does not match the peer's trusted CAs. This is necessary
+ * to use self-signed certs in fingerprint mode. And, yes, this usage
+ * of the callback is quite a hack. But it seems the only way to
+ * obey to the IETF -transport-tls I-D.
+ * Note: GnuTLS requires the function to return 0 on success and
+ * -1 on failure.
+ * rgerhards, 2008-05-27
+ */
+static int
+gtlsClientCertCallback(gnutls_session_t session,
+ __attribute__((unused)) const gnutls_datum_t* req_ca_rdn,
+ int __attribute__((unused)) nreqs,
+ __attribute__((unused)) const gnutls_pk_algorithm_t* sign_algos,
+ int __attribute__((unused)) sign_algos_length,
+#if HAVE_GNUTLS_CERTIFICATE_SET_RETRIEVE_FUNCTION
+ gnutls_retr2_st* st
+#else
+ gnutls_retr_st *st
+#endif
+ )
+{
+ nsd_gtls_t *pThis;
+
+ pThis = (nsd_gtls_t*) gnutls_session_get_ptr(session);
+
+#if HAVE_GNUTLS_CERTIFICATE_SET_RETRIEVE_FUNCTION
+ st->cert_type = GNUTLS_CRT_X509;
+#else
+ st->type = GNUTLS_CRT_X509;
+#endif
+ st->ncerts = pThis->nOurCerts;
+ st->cert.x509 = pThis->pOurCerts;
+ st->key.x509 = pThis->ourKey;
+ st->deinit_all = 0;
+
+ return 0;
+}
+
+
+/* This function extracts some information about this session's peer
+ * certificate. Works for X.509 certificates only. Adds all
+ * of the info to a cstr_t, which is handed over to the caller.
+ * Caller must destruct it when no longer needed.
+ * rgerhards, 2008-05-21
+ */
+static rsRetVal
+gtlsGetCertInfo(nsd_gtls_t *const pThis, cstr_t **ppStr)
+{
+ uchar szBufA[1024];
+ uchar *szBuf = szBufA;
+ size_t szBufLen = sizeof(szBufA), tmp;
+ unsigned int algo, bits;
+ time_t expiration_time, activation_time;
+ const gnutls_datum_t *cert_list;
+ unsigned cert_list_size = 0;
+ gnutls_x509_crt_t cert;
+ cstr_t *pStr = NULL;
+ int gnuRet;
+ DEFiRet;
+ unsigned iAltName;
+
+ assert(ppStr != NULL);
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ if(gnutls_certificate_type_get(pThis->sess) != GNUTLS_CRT_X509)
+ return RS_RET_TLS_CERT_ERR;
+
+ cert_list = gnutls_certificate_get_peers(pThis->sess, &cert_list_size);
+ CHKiRet(rsCStrConstructFromszStrf(&pStr, "peer provided %d certificate(s). ", cert_list_size));
+
+ if(cert_list_size > 0) {
+ /* we only print information about the first certificate */
+ CHKgnutls(gnutls_x509_crt_init(&cert));
+ CHKgnutls(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER));
+
+ expiration_time = gnutls_x509_crt_get_expiration_time(cert);
+ activation_time = gnutls_x509_crt_get_activation_time(cert);
+ ctime_r(&activation_time, (char*)szBuf);
+ szBuf[ustrlen(szBuf) - 1] = '\0'; /* strip linefeed */
+ CHKiRet(rsCStrAppendStrf(pStr, "Certificate 1 info: "
+ "certificate valid from %s ", szBuf));
+ ctime_r(&expiration_time, (char*)szBuf);
+ szBuf[ustrlen(szBuf) - 1] = '\0'; /* strip linefeed */
+ CHKiRet(rsCStrAppendStrf(pStr, "to %s; ", szBuf));
+
+ /* Extract some of the public key algorithm's parameters */
+ algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits);
+ CHKiRet(rsCStrAppendStrf(pStr, "Certificate public key: %s; ",
+ gnutls_pk_algorithm_get_name(algo)));
+
+ /* names */
+ tmp = szBufLen;
+ if(gnutls_x509_crt_get_dn(cert, (char*)szBuf, &tmp)
+ == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ szBufLen = tmp;
+ szBuf = malloc(tmp);
+ gnutls_x509_crt_get_dn(cert, (char*)szBuf, &tmp);
+ }
+ CHKiRet(rsCStrAppendStrf(pStr, "DN: %s; ", szBuf));
+
+ tmp = szBufLen;
+ if(gnutls_x509_crt_get_issuer_dn(cert, (char*)szBuf, &tmp)
+ == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ szBufLen = tmp;
+ szBuf = realloc((szBuf == szBufA) ? NULL : szBuf, tmp);
+ gnutls_x509_crt_get_issuer_dn(cert, (char*)szBuf, &tmp);
+ }
+ CHKiRet(rsCStrAppendStrf(pStr, "Issuer DN: %s; ", szBuf));
+
+ /* dNSName alt name */
+ iAltName = 0;
+ while(1) { /* loop broken below */
+ tmp = szBufLen;
+ gnuRet = gnutls_x509_crt_get_subject_alt_name(cert, iAltName,
+ szBuf, &tmp, NULL);
+ if(gnuRet == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ szBufLen = tmp;
+ szBuf = realloc((szBuf == szBufA) ? NULL : szBuf, tmp);
+ continue;
+ } else if(gnuRet < 0)
+ break;
+ else if(gnuRet == GNUTLS_SAN_DNSNAME) {
+ /* we found it! */
+ CHKiRet(rsCStrAppendStrf(pStr, "SAN:DNSname: %s; ", szBuf));
+ /* do NOT break, because there may be multiple dNSName's! */
+ }
+ ++iAltName;
+ }
+
+ gnutls_x509_crt_deinit(cert);
+ }
+
+ cstrFinalize(pStr);
+ *ppStr = pStr;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pStr != NULL)
+ rsCStrDestruct(&pStr);
+ }
+ if(szBuf != szBufA)
+ free(szBuf);
+
+ RETiRet;
+}
+
+
+
+#if 0 /* we may need this in the future - code needs to be looked at then! */
+/* This function will print some details of the
+ * given pThis->sess.
+ */
+static rsRetVal
+print_info(nsd_gtls_t *pThis)
+{
+ const char *tmp;
+ gnutls_credentials_type cred;
+ gnutls_kx_algorithm kx;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ /* print the key exchange's algorithm name
+ */
+ kx = gnutls_kx_get(pThis->sess);
+ tmp = gnutls_kx_get_name(kx);
+ dbgprintf("- Key Exchange: %s\n", tmp);
+
+ /* Check the authentication type used and switch
+ * to the appropriate.
+ */
+ cred = gnutls_auth_get_type(pThis->sess);
+ switch (cred) {
+ case GNUTLS_CRD_ANON: /* anonymous authentication */
+ dbgprintf("- Anonymous DH using prime of %d bits\n",
+ gnutls_dh_get_prime_bits(pThis->sess));
+ break;
+ case GNUTLS_CRD_CERTIFICATE: /* certificate authentication */
+ /* Check if we have been using ephemeral Diffie Hellman.
+ */
+ if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) {
+ dbgprintf("\n- Ephemeral DH using prime of %d bits\n",
+ gnutls_dh_get_prime_bits(pThis->sess));
+ }
+
+ /* if the certificate list is available, then
+ * print some information about it.
+ */
+ gtlsPrintCert(pThis);
+ break;
+ case GNUTLS_CRD_SRP: /* certificate authentication */
+ dbgprintf("GNUTLS_CRD_SRP/IA");
+ break;
+ case GNUTLS_CRD_PSK: /* certificate authentication */
+ dbgprintf("GNUTLS_CRD_PSK");
+ break;
+ case GNUTLS_CRD_IA: /* certificate authentication */
+ dbgprintf("GNUTLS_CRD_IA");
+ break;
+ } /* switch */
+
+ /* print the protocol's name (ie TLS 1.0) */
+ tmp = gnutls_protocol_get_name(gnutls_protocol_get_version(pThis->sess));
+ dbgprintf("- Protocol: %s\n", tmp);
+
+ /* print the certificate type of the peer.
+ * ie X.509
+ */
+ tmp = gnutls_certificate_type_get_name(
+ gnutls_certificate_type_get(pThis->sess));
+
+ dbgprintf("- Certificate Type: %s\n", tmp);
+
+ /* print the compression algorithm (if any)
+ */
+ tmp = gnutls_compression_get_name( gnutls_compression_get(pThis->sess));
+ dbgprintf("- Compression: %s\n", tmp);
+
+ /* print the name of the cipher used.
+ * ie 3DES.
+ */
+ tmp = gnutls_cipher_get_name(gnutls_cipher_get(pThis->sess));
+ dbgprintf("- Cipher: %s\n", tmp);
+
+ /* Print the MAC algorithms name.
+ * ie SHA1
+ */
+ tmp = gnutls_mac_get_name(gnutls_mac_get(pThis->sess));
+ dbgprintf("- MAC: %s\n", tmp);
+
+ RETiRet;
+}
+#endif
+
+
+/* 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
+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;
+}
+
+
+/* a thread-safe variant of gnutls_strerror
+ * The caller must free the returned string.
+ * rgerhards, 2008-04-30
+ */
+uchar *gtlsStrerror(int error)
+{
+ uchar *pErr;
+
+ pthread_mutex_lock(&mutGtlsStrerror);
+ pErr = (uchar*) strdup(gnutls_strerror(error));
+ pthread_mutex_unlock(&mutGtlsStrerror);
+
+ return pErr;
+}
+
+
+/* try to receive a record from the remote peer. This works with
+ * our own abstraction and handles local buffering and EAGAIN.
+ * See details on local buffering in Rcv(9 header-comment.
+ * This function MUST only be called when the local buffer is
+ * empty. Calling it otherwise will cause losss of current buffer
+ * data.
+ * rgerhards, 2008-06-24
+ */
+rsRetVal
+gtlsRecordRecv(nsd_gtls_t *pThis)
+{
+ ssize_t lenRcvd;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ DBGPRINTF("gtlsRecordRecv: start (Pending Data: %zd | Wanted Direction: %s)\n",
+ gnutls_record_check_pending(pThis->sess),
+ (gnutls_record_get_direction(pThis->sess) == gtlsDir_READ ? "READ" : "WRITE") );
+ lenRcvd = gnutls_record_recv(pThis->sess, pThis->pszRcvBuf, NSD_GTLS_MAX_RCVBUF);
+ if(lenRcvd >= 0) {
+ DBGPRINTF("gtlsRecordRecv: gnutls_record_recv received %zd bytes\n", lenRcvd);
+ pThis->lenRcvBuf = lenRcvd;
+ pThis->ptrRcvBuf = 0;
+
+ /* Check for additional data in SSL buffer */
+ size_t stBytesLeft = gnutls_record_check_pending(pThis->sess);
+ if (stBytesLeft > 0 ){
+ DBGPRINTF("gtlsRecordRecv: %zd Bytes pending after gnutls_record_recv, expand buffer.\n",
+ stBytesLeft);
+ /* realloc buffer size and preserve char content */
+ char *const newbuf = realloc(pThis->pszRcvBuf, NSD_GTLS_MAX_RCVBUF+stBytesLeft);
+ CHKmalloc(newbuf);
+ pThis->pszRcvBuf = newbuf;
+
+ /* 2nd read will read missing bytes from the current SSL Packet */
+ lenRcvd = gnutls_record_recv(pThis->sess, pThis->pszRcvBuf+NSD_GTLS_MAX_RCVBUF, stBytesLeft);
+ if(lenRcvd > 0) {
+ DBGPRINTF("gtlsRecordRecv: 2nd SSL_read received %zd bytes\n",
+ (NSD_GTLS_MAX_RCVBUF+lenRcvd));
+ pThis->lenRcvBuf = NSD_GTLS_MAX_RCVBUF+lenRcvd;
+ } else {
+ if (lenRcvd == GNUTLS_E_AGAIN || lenRcvd == GNUTLS_E_INTERRUPTED) {
+ goto sslerragain; /* Go to ERR AGAIN handling */
+ } else {
+ /* Do all other error handling */
+ int gnuRet = lenRcvd;
+ ABORTgnutls;
+ }
+ }
+ }
+ } else if(lenRcvd == GNUTLS_E_AGAIN || lenRcvd == GNUTLS_E_INTERRUPTED) {
+sslerragain:
+ /* Check if the underlaying file descriptor needs to read or write data!*/
+ if (gnutls_record_get_direction(pThis->sess) == gtlsDir_READ) {
+ pThis->rtryCall = gtlsRtry_recv;
+ dbgprintf("GnuTLS receive requires a retry, this most probably is OK and no error condition\n");
+ ABORT_FINALIZE(RS_RET_RETRY);
+ } else {
+ uchar *pErr = gtlsStrerror(lenRcvd);
+ LogError(0, RS_RET_GNUTLS_ERR, "GnuTLS receive error %zd has wrong read direction(wants write) "
+ "- this could be caused by a broken connection. GnuTLS reports: %s\n",
+ lenRcvd, pErr);
+ free(pErr);
+ ABORT_FINALIZE(RS_RET_GNUTLS_ERR);
+ }
+ } else {
+ int gnuRet = lenRcvd;
+ ABORTgnutls;
+ }
+
+finalize_it:
+ dbgprintf("gtlsRecordRecv return. nsd %p, iRet %d, lenRcvd %d, lenRcvBuf %d, ptrRcvBuf %d\n",
+ pThis, iRet, (int) lenRcvd, pThis->lenRcvBuf, pThis->ptrRcvBuf);
+ RETiRet;
+}
+
+
+/* add our own certificate to the certificate set, so that the peer
+ * can identify us. Please note that we try to use mutual authentication,
+ * so we always add a cert, even if we are in the client role (later,
+ * this may be controlled by a config setting).
+ * rgerhards, 2008-05-15
+ */
+static rsRetVal
+gtlsAddOurCert(nsd_gtls_t *const pThis)
+{
+ int gnuRet = 0;
+ const uchar *keyFile;
+ const uchar *certFile;
+ uchar *pGnuErr; /* for GnuTLS error reporting */
+ DEFiRet;
+
+ certFile = (pThis->pszCertFile == NULL) ? glbl.GetDfltNetstrmDrvrCertFile(runConf) : pThis->pszCertFile;
+ keyFile = (pThis->pszKeyFile == NULL) ? glbl.GetDfltNetstrmDrvrKeyFile(runConf) : pThis->pszKeyFile;
+ dbgprintf("GTLS certificate file: '%s'\n", certFile);
+ dbgprintf("GTLS key file: '%s'\n", keyFile);
+ if(certFile == NULL) {
+ LogMsg(0, RS_RET_CERT_MISSING, LOG_WARNING, "warning: certificate file is not set");
+ }
+ if(keyFile == NULL) {
+ LogMsg(0, RS_RET_CERTKEY_MISSING, LOG_WARNING, "warning: key file is not set");
+ }
+
+ /* set certificate in gnutls */
+ if(certFile != NULL && keyFile != NULL) {
+ CHKgnutls(gnutls_certificate_set_x509_key_file(pThis->xcred, (char*)certFile, (char*)keyFile,
+ GNUTLS_X509_FMT_PEM));
+ }
+
+finalize_it:
+ if(iRet != RS_RET_OK && iRet != RS_RET_CERT_MISSING && iRet != RS_RET_CERTKEY_MISSING) {
+ pGnuErr = gtlsStrerror(gnuRet);
+ errno = 0;
+ LogError(0, iRet, "error adding our certificate. GnuTLS error %d, message: '%s', "
+ "key: '%s', cert: '%s'", gnuRet, pGnuErr, keyFile, certFile);
+ free(pGnuErr);
+ }
+ RETiRet;
+}
+
+/*
+* removecomment ifdef out if needed
+*/
+#ifdef false
+
+static void print_cipher_suite_list(const char *priorities)
+{
+ size_t i;
+ int ret;
+ unsigned int idx;
+ const char *name;
+ const char *err;
+ unsigned char id[2];
+ gnutls_protocol_t version;
+ gnutls_priority_t pcache;
+
+ if (priorities != NULL) {
+ printf("print_cipher_suite_list: Cipher suites for %s\n", priorities);
+
+ ret = gnutls_priority_init(&pcache, priorities, &err);
+ if (ret < 0) {
+ fprintf(stderr, "print_cipher_suite_list: Syntax error at: %s\n", err);
+ exit(1);
+ }
+
+ for (i = 0;; i++) {
+ ret = gnutls_priority_get_cipher_suite_index(pcache, i, &idx);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ if (ret == GNUTLS_E_UNKNOWN_CIPHER_SUITE)
+ continue;
+
+ name = gnutls_cipher_suite_info(idx, id, NULL, NULL, NULL, &version);
+
+ if (name != NULL)
+ dbgprintf("print_cipher_suite_list: %-50s\t0x%02x, 0x%02x\t%s\n",
+ name, (unsigned char) id[0],
+ (unsigned char) id[1],
+ gnutls_protocol_get_name(version));
+ }
+
+ return;
+ }
+}
+#endif
+
+/* initialize GnuTLS credential structure (certs etc) */
+static rsRetVal
+gtlsInitCred(nsd_gtls_t *const pThis )
+{
+ int gnuRet;
+ const uchar *cafile, *crlfile;
+ DEFiRet;
+
+ /* X509 stuff */
+ if (pThis->xcred == NULL) {
+ /* Allocate only ONCE */
+ CHKgnutls(gnutls_certificate_allocate_credentials(&pThis->xcred));
+ }
+
+ /* sets the trusted cas file */
+ cafile = (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");
+ } else {
+ dbgprintf("GTLS CA file: '%s'\n", cafile);
+ gnuRet = gnutls_certificate_set_x509_trust_file(pThis->xcred, (char*)cafile, GNUTLS_X509_FMT_PEM);
+ if(gnuRet == GNUTLS_E_FILE_ERROR) {
+ LogError(0, RS_RET_GNUTLS_ERR,
+ "error reading certificate file '%s' - a common cause is that the "
+ "file does not exist", cafile);
+ 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 CA certificate file %d in %s:%d: %s\n",
+ gnuRet, __FILE__, __LINE__, pErr);
+ free(pErr);
+ ABORT_FINALIZE(RS_RET_GNUTLS_ERR);
+ }
+ }
+
+ crlfile = (pThis->pszCRLFile == NULL) ? glbl.GetDfltNetstrmDrvrCRLF(runConf) : pThis->pszCRLFile;
+ if(crlfile == NULL) {
+ 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:
+ RETiRet;
+}
+
+
+/* globally initialize GnuTLS */
+static rsRetVal
+gtlsGlblInit(void)
+{
+ int gnuRet;
+ DEFiRet;
+
+ dbgprintf("gtlsGlblInit: Running Version: '%#010x'\n", GNUTLS_VERSION_NUMBER);
+
+ /* gcry_control must be called first, so that the thread system is correctly set up */
+ #if GNUTLS_VERSION_NUMBER <= 0x020b00
+ gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+ #endif
+ CHKgnutls(gnutls_global_init());
+
+ if(GetGnuTLSLoglevel(runConf) > 0){
+ gnutls_global_set_log_function(logFunction);
+ gnutls_global_set_log_level(GetGnuTLSLoglevel(runConf));
+ /* 0 (no) to 9 (most), 10 everything */
+ }
+
+ /* Init Anon cipher helpers */
+ CHKgnutls(gnutls_dh_params_init(&dh_params));
+ CHKgnutls(gnutls_dh_params_generate2(dh_params, dhBits));
+
+ /* Allocate ANON Client Cred */
+ CHKgnutls(gnutls_anon_allocate_client_credentials(&anoncred));
+
+ /* Allocate ANON Server Cred */
+ CHKgnutls(gnutls_anon_allocate_server_credentials(&anoncredSrv));
+ gnutls_anon_set_server_dh_params(anoncredSrv, dh_params);
+
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+gtlsInitSession(nsd_gtls_t *pThis)
+{
+ DEFiRet;
+ int gnuRet = 0;
+ gnutls_session_t session;
+
+ gnutls_init(&session, GNUTLS_SERVER);
+ pThis->bHaveSess = 1;
+ pThis->bIsInitiator = 0;
+ pThis->sess = session;
+
+ /* Moved CertKey Loading to top */
+# if HAVE_GNUTLS_CERTIFICATE_SET_RETRIEVE_FUNCTION
+ /* store a pointer to ourselfs (needed by callback) */
+ gnutls_session_set_ptr(pThis->sess, (void*)pThis);
+ iRet = gtlsLoadOurCertKey(pThis); /* first load .pem files */
+ if(iRet == RS_RET_OK) {
+ dbgprintf("gtlsInitSession: enable certificate checking (VerifyDepth=%d)\n", pThis->DrvrVerifyDepth);
+ gnutls_certificate_set_retrieve_function(pThis->xcred, gtlsClientCertCallback);
+ if (pThis->DrvrVerifyDepth != 0){
+ gnutls_certificate_set_verify_limits(pThis->xcred, 8200, pThis->DrvrVerifyDepth);
+ }
+ } else if(iRet == RS_RET_CERTLESS) {
+ dbgprintf("gtlsInitSession: certificates not configured, not loaded.\n");
+ } else {
+ ABORT_FINALIZE(iRet); /* we have an error case! */
+ }
+# endif
+
+ /* avoid calling all the priority functions, since the defaults are adequate. */
+ CHKgnutls(gnutls_credentials_set(pThis->sess, GNUTLS_CRD_CERTIFICATE, pThis->xcred));
+
+ /* check for anon authmode */
+ if (pThis->authMode == GTLS_AUTH_CERTANON) {
+ dbgprintf("gtlsInitSession: anon authmode, gnutls_credentials_set GNUTLS_CRD_ANON\n");
+ CHKgnutls(gnutls_credentials_set(pThis->sess, GNUTLS_CRD_ANON, anoncredSrv));
+ gnutls_dh_set_prime_bits(pThis->sess, dhMinBits);
+ }
+
+ /* request client certificate if any. */
+ gnutls_certificate_server_set_request( pThis->sess, GNUTLS_CERT_REQUEST);
+
+
+finalize_it:
+ if(iRet != RS_RET_OK && iRet != RS_RET_CERTLESS) {
+ LogError(0, iRet, "gtlsInitSession failed to INIT Session %d", gnuRet);
+ }
+
+ RETiRet;
+}
+
+
+/* Obtain the CN from the DN field and hand it back to the caller
+ * (which is responsible for destructing it). We try to follow
+ * RFC2253 as far as it makes sense for our use-case. This function
+ * is considered a compromise providing good-enough correctness while
+ * limiting code size and complexity. If a problem occurs, we may enhance
+ * this function. A (pointer to a) certificate must be caller-provided.
+ * If no CN is contained in the cert, no string is returned
+ * (*ppstrCN remains NULL). *ppstrCN MUST be NULL on entry!
+ * rgerhards, 2008-05-22
+ */
+static rsRetVal
+gtlsGetCN(gnutls_x509_crt_t *pCert, cstr_t **ppstrCN)
+{
+ DEFiRet;
+ int gnuRet;
+ int i;
+ int bFound;
+ cstr_t *pstrCN = NULL;
+ size_t size;
+ /* big var the last, so we hope to have all we usually neeed within one mem cache line */
+ uchar szDN[1024]; /* this should really be large enough for any non-malicious case... */
+
+ assert(pCert != NULL);
+ assert(ppstrCN != NULL);
+ assert(*ppstrCN == NULL);
+
+ size = sizeof(szDN);
+ CHKgnutls(gnutls_x509_crt_get_dn(*pCert, (char*)szDN, &size));
+
+ /* now search for the CN part */
+ i = 0;
+ bFound = 0;
+ while(!bFound && szDN[i] != '\0') {
+ /* note that we do not overrun our string due to boolean shortcut
+ * operations. If we have '\0', the if does not match and evaluation
+ * stops. Order of checks is obviously important!
+ */
+ if(szDN[i] == 'C' && szDN[i+1] == 'N' && szDN[i+2] == '=') {
+ bFound = 1;
+ i += 2;
+ }
+ i++;
+
+ }
+
+ if(!bFound) {
+ FINALIZE; /* we are done */
+ }
+
+ /* we found a common name, now extract it */
+ CHKiRet(cstrConstruct(&pstrCN));
+ while(szDN[i] != '\0' && szDN[i] != ',') {
+ if(szDN[i] == '\\') {
+ /* hex escapes are not implemented */
+ ++i; /* escape char processed */
+ if(szDN[i] == '\0')
+ ABORT_FINALIZE(RS_RET_CERT_INVALID_DN);
+ CHKiRet(cstrAppendChar(pstrCN, szDN[i]));
+ } else {
+ CHKiRet(cstrAppendChar(pstrCN, szDN[i]));
+ }
+ ++i; /* char processed */
+ }
+ cstrFinalize(pstrCN);
+
+ /* we got it - we ignore the rest of the DN string (if any). So we may
+ * not detect if it contains more than one CN
+ */
+
+ *ppstrCN = pstrCN;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pstrCN != NULL)
+ cstrDestruct(&pstrCN);
+ }
+
+ RETiRet;
+}
+
+
+/* Check the peer's ID in fingerprint auth mode.
+ * rgerhards, 2008-05-22
+ */
+static rsRetVal
+gtlsChkPeerFingerprint(nsd_gtls_t *pThis, gnutls_x509_crt_t *pCert)
+{
+ uchar fingerprint[20];
+ uchar fingerprintSha256[32];
+ size_t size;
+ size_t sizeSha256;
+ cstr_t *pstrFingerprint = NULL;
+ cstr_t *pstrFingerprintSha256 = NULL;
+ int bFoundPositiveMatch;
+ permittedPeers_t *pPeer;
+ int gnuRet;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ /* obtain the SHA1 fingerprint */
+ size = sizeof(fingerprint);
+ sizeSha256 = sizeof(fingerprintSha256);
+ CHKgnutls(gnutls_x509_crt_get_fingerprint(*pCert, GNUTLS_DIG_SHA1, fingerprint, &size));
+ CHKgnutls(gnutls_x509_crt_get_fingerprint(*pCert, GNUTLS_DIG_SHA256, fingerprintSha256, &sizeSha256));
+ CHKiRet(GenFingerprintStr(fingerprint, size, &pstrFingerprint, "SHA1"));
+ CHKiRet(GenFingerprintStr(fingerprintSha256, sizeSha256, &pstrFingerprintSha256, "SHA256"));
+ dbgprintf("peer's certificate SHA1 fingerprint: %s\n", cstrGetSzStrNoNULL(pstrFingerprint));
+ dbgprintf("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("gtlsChkPeerFingerprint: peer's certificate SHA1 MATCH found: %s\n", pPeer->pszID);
+ bFoundPositiveMatch = 1;
+ } else if(!rsCStrSzStrCmp(pstrFingerprintSha256 , pPeer->pszID, strlen((char*) pPeer->pszID))) {
+ dbgprintf("gtlsChkPeerFingerprint: peer's certificate SHA256 MATCH found: %s\n", pPeer->pszID);
+ bFoundPositiveMatch = 1;
+ }
+ else {
+ pPeer = pPeer->pNext;
+ }
+ }
+
+ if(!bFoundPositiveMatch) {
+ dbgprintf("invalid peer fingerprint, not permitted to talk to it\n");
+ if(pThis->bReportAuthErr == 1) {
+ errno = 0;
+ LogError(0, RS_RET_INVALID_FINGERPRINT, "error: peer fingerprint '%s' unknown - we are "
+ "not permitted to talk to it", cstrGetSzStrNoNULL(pstrFingerprint));
+ pThis->bReportAuthErr = 0;
+ }
+ ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT);
+ }
+
+finalize_it:
+ if(pstrFingerprint != NULL)
+ cstrDestruct(&pstrFingerprint);
+ 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
+gtlsChkOnePeerName(nsd_gtls_t *pThis, uchar *pszPeerID, int *pbFoundPositiveMatch)
+{
+ permittedPeers_t *pPeer;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ assert(pszPeerID != NULL);
+ assert(pbFoundPositiveMatch != NULL);
+
+ if(pThis->pPermPeers) { /* do we have configured peer IDs? */
+ pPeer = pThis->pPermPeers;
+ while(pPeer != NULL) {
+ CHKiRet(net.PermittedPeerWildcardMatch(pPeer, pszPeerID, pbFoundPositiveMatch));
+ if(*pbFoundPositiveMatch)
+ break;
+ pPeer = pPeer->pNext;
+ }
+ } else {
+ /* we do not have configured peer IDs, so we use defaults */
+ if( pThis->pszConnectHost
+ && !strcmp((char*)pszPeerID, (char*)pThis->pszConnectHost)) {
+ *pbFoundPositiveMatch = 1;
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Check the peer's ID in name auth mode.
+ * rgerhards, 2008-05-22
+ */
+static rsRetVal
+gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt_t *pCert)
+{
+ uchar lnBuf[256];
+ char szAltName[1024]; /* this is sufficient for the DNSNAME... */
+ int iAltName;
+ size_t szAltNameLen;
+ int bFoundPositiveMatch;
+ int bHaveSAN = 0;
+ cstr_t *pStr = NULL;
+ cstr_t *pstrCN = NULL;
+ int gnuRet;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ bFoundPositiveMatch = 0;
+ CHKiRet(rsCStrConstruct(&pStr));
+
+ /* first search through the dNSName subject alt names */
+ iAltName = 0;
+ while(!bFoundPositiveMatch) { /* loop broken below */
+ szAltNameLen = sizeof(szAltName);
+ gnuRet = gnutls_x509_crt_get_subject_alt_name(*pCert, iAltName,
+ szAltName, &szAltNameLen, NULL);
+ if(gnuRet < 0)
+ break;
+ else if(gnuRet == GNUTLS_SAN_DNSNAME) {
+ bHaveSAN = 1;
+ dbgprintf("subject alt dnsName: '%s'\n", szAltName);
+ snprintf((char*)lnBuf, sizeof(lnBuf), "DNSname: %s; ", szAltName);
+ CHKiRet(rsCStrAppendStr(pStr, lnBuf));
+ CHKiRet(gtlsChkOnePeerName(pThis, (uchar*)szAltName, &bFoundPositiveMatch));
+ /* do NOT break, because there may be multiple dNSName's! */
+ }
+ ++iAltName;
+ }
+
+ /* Check also CN only if not configured per stricter RFC 6125 or no SAN present*/
+ if(!bFoundPositiveMatch && (!pThis->bSANpriority || !bHaveSAN)) {
+ CHKiRet(gtlsGetCN(pCert, &pstrCN));
+ if(pstrCN != NULL) { /* NULL if there was no CN present */
+ dbgprintf("gtls now checking auth for CN '%s'\n", cstrGetSzStrNoNULL(pstrCN));
+ snprintf((char*)lnBuf, sizeof(lnBuf), "CN: %s; ", cstrGetSzStrNoNULL(pstrCN));
+ CHKiRet(rsCStrAppendStr(pStr, lnBuf));
+ CHKiRet(gtlsChkOnePeerName(pThis, cstrGetSzStrNoNULL(pstrCN), &bFoundPositiveMatch));
+ }
+ }
+
+ if(!bFoundPositiveMatch) {
+ dbgprintf("invalid peer name, not permitted to talk to it\n");
+ if(pThis->bReportAuthErr == 1) {
+ cstrFinalize(pStr);
+ errno = 0;
+ LogError(0, RS_RET_INVALID_FINGERPRINT, "error: peer name not authorized - "
+ "not permitted to talk to it. Names: %s",
+ cstrGetSzStrNoNULL(pStr));
+ pThis->bReportAuthErr = 0;
+ }
+ ABORT_FINALIZE(RS_RET_INVALID_FINGERPRINT);
+ }
+
+finalize_it:
+ if(pStr != NULL)
+ rsCStrDestruct(&pStr);
+ if(pstrCN != NULL)
+ rsCStrDestruct(&pstrCN);
+ RETiRet;
+}
+
+
+/* check the ID of the remote peer - used for both fingerprint and
+ * name authentication. This is common code. Will call into specific
+ * drivers once the certificate has been obtained.
+ * rgerhards, 2008-05-08
+ */
+static rsRetVal
+gtlsChkPeerID(nsd_gtls_t *pThis)
+{
+ const gnutls_datum_t *cert_list;
+ unsigned int list_size = 0;
+ gnutls_x509_crt_t cert;
+ int bMustDeinitCert = 0;
+ int gnuRet;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ /* This function only works for X.509 certificates. */
+ if(gnutls_certificate_type_get(pThis->sess) != GNUTLS_CRT_X509)
+ return RS_RET_TLS_CERT_ERR;
+
+ cert_list = gnutls_certificate_get_peers(pThis->sess, &list_size);
+
+ if(list_size < 1) {
+ if(pThis->bReportAuthErr == 1) {
+ uchar *fromHost = NULL;
+ errno = 0;
+ pThis->bReportAuthErr = 0;
+ nsd_ptcp.GetRemoteHName((nsd_t*)pThis->pTcp, &fromHost);
+ LogError(0, RS_RET_TLS_NO_CERT, "error: peer %s did not provide a certificate, "
+ "not permitted to talk to it", fromHost);
+ free(fromHost);
+ }
+ ABORT_FINALIZE(RS_RET_TLS_NO_CERT);
+ }
+
+ /* If we reach this point, we have at least one valid certificate.
+ * We always use only the first certificate. As of GnuTLS documentation, the
+ * first certificate always contains the remote peer's own certificate. All other
+ * certificates are issuer's certificates (up the chain). We are only interested
+ * in the first certificate, which is our peer. -- rgerhards, 2008-05-08
+ */
+ CHKgnutls(gnutls_x509_crt_init(&cert));
+ bMustDeinitCert = 1; /* indicate cert is initialized and must be freed on exit */
+ CHKgnutls(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER));
+
+ /* Now we see which actual authentication code we must call. */
+ if(pThis->authMode == GTLS_AUTH_CERTFINGERPRINT) {
+ CHKiRet(gtlsChkPeerFingerprint(pThis, &cert));
+ } else {
+ assert(pThis->authMode == GTLS_AUTH_CERTNAME);
+ CHKiRet(gtlsChkPeerName(pThis, &cert));
+ }
+
+finalize_it:
+ if(bMustDeinitCert)
+ gnutls_x509_crt_deinit(cert);
+
+ RETiRet;
+}
+
+
+/* Verify the validity of the remote peer's certificate.
+ * rgerhards, 2008-05-21
+ */
+static rsRetVal
+gtlsChkPeerCertValidity(nsd_gtls_t *pThis)
+{
+ DEFiRet;
+ const char *pszErrCause;
+ int gnuRet;
+ cstr_t *pStr = NULL;
+ unsigned stateCert;
+ const gnutls_datum_t *cert_list;
+ unsigned cert_list_size = 0;
+ gnutls_x509_crt_t cert;
+ unsigned i;
+ time_t ttCert;
+ time_t ttNow;
+ sbool bAbort = RSFALSE;
+ int iAbortCode = RS_RET_OK;
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ /* check if we have at least one cert */
+ cert_list = gnutls_certificate_get_peers(pThis->sess, &cert_list_size);
+ if(cert_list_size < 1) {
+ errno = 0;
+ uchar *fromHost = NULL;
+ nsd_ptcp.GetRemoteHName((nsd_t*)pThis->pTcp, &fromHost);
+ LogError(0, RS_RET_TLS_NO_CERT,
+ "peer %s did not provide a certificate, not permitted to talk to it",
+ fromHost);
+ free(fromHost);
+ ABORT_FINALIZE(RS_RET_TLS_NO_CERT);
+ }
+#ifdef EXTENDED_CERT_CHECK_AVAILABLE
+ if (pThis->dataTypeCheck == GTLS_NONE) {
+#endif
+ CHKgnutls(gnutls_certificate_verify_peers2(pThis->sess, &stateCert));
+#ifdef EXTENDED_CERT_CHECK_AVAILABLE
+ } else { /* we have configured data to check in addition to cert */
+ gnutls_typed_vdata_st data;
+ data.type = GNUTLS_DT_KEY_PURPOSE_OID;
+ if (pThis->bIsInitiator) { /* client mode */
+ data.data = (uchar *)GNUTLS_KP_TLS_WWW_SERVER;
+ } else { /* server mode */
+ data.data = (uchar *)GNUTLS_KP_TLS_WWW_CLIENT;
+ }
+ data.size = ustrlen(data.data);
+ CHKgnutls(gnutls_certificate_verify_peers(pThis->sess, &data, 1, &stateCert));
+ }
+#endif
+
+ if(stateCert & GNUTLS_CERT_INVALID) {
+ /* Default abort code */
+ iAbortCode = RS_RET_CERT_INVALID;
+
+ /* provide error details if we have them */
+ if (stateCert & GNUTLS_CERT_EXPIRED ) {
+ dbgprintf("GnuTLS returned GNUTLS_CERT_EXPIRED, handling mode %d ...\n",
+ pThis->permitExpiredCerts);
+ /* Handle expired certs */
+ if (pThis->permitExpiredCerts == GTLS_EXPIRED_DENY) {
+ bAbort = RSTRUE;
+ iAbortCode = RS_RET_CERT_EXPIRED;
+ } else if (pThis->permitExpiredCerts == GTLS_EXPIRED_WARN) {
+ LogMsg(0, RS_RET_NO_ERRCODE, LOG_WARNING,
+ "Warning, certificate expired but expired certs are permitted");
+ } else {
+ dbgprintf("GnuTLS returned GNUTLS_CERT_EXPIRED, but expired certs are permitted.\n");
+ }
+ pszErrCause = "certificate expired";
+ } else if(stateCert & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+ pszErrCause = "signer not found";
+ bAbort = RSTRUE;
+ } else if(stateCert & GNUTLS_CERT_SIGNER_NOT_CA) {
+ pszErrCause = "signer is not a CA";
+ bAbort = RSTRUE;
+ } else if(stateCert & GNUTLS_CERT_INSECURE_ALGORITHM) {
+ pszErrCause = "insecure algorithm";
+ bAbort = RSTRUE;
+ } 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";
+ bAbort = RSTRUE;
+#endif
+ } else {
+ pszErrCause = "GnuTLS returned no specific reason";
+ dbgprintf("GnuTLS returned no specific reason for GNUTLS_CERT_INVALID, certificate "
+ "status is %d\n", stateCert);
+ bAbort = RSTRUE;
+ }
+ }
+
+ if (bAbort == RSTRUE) {
+ uchar *fromHost = NULL;
+ nsd_ptcp.GetRemoteHName((nsd_t*)pThis->pTcp, &fromHost);
+ LogError(0, NO_ERRCODE, "not permitted to talk to peer '%s', certificate invalid: %s",
+ fromHost, pszErrCause);
+ free(fromHost);
+ gtlsGetCertInfo(pThis, &pStr);
+ LogError(0, NO_ERRCODE, "invalid cert info: %s", cstrGetSzStrNoNULL(pStr));
+ cstrDestruct(&pStr);
+ ABORT_FINALIZE(iAbortCode);
+ }
+
+ /* get current time for certificate validation */
+ if(datetime.GetTime(&ttNow) == -1)
+ ABORT_FINALIZE(RS_RET_SYS_ERR);
+
+ /* as it looks, we need to validate the expiration dates ourselves...
+ * We need to loop through all certificates as we need to make sure the
+ * interim certificates are also not expired.
+ */
+ for(i = 0 ; i < cert_list_size ; ++i) {
+ CHKgnutls(gnutls_x509_crt_init(&cert));
+ CHKgnutls(gnutls_x509_crt_import(cert, &cert_list[i], GNUTLS_X509_FMT_DER));
+ ttCert = gnutls_x509_crt_get_activation_time(cert);
+ if(ttCert == -1)
+ ABORT_FINALIZE(RS_RET_TLS_CERT_ERR);
+ else if(ttCert > ttNow) {
+ uchar *fromHost = NULL;
+ nsd_ptcp.GetRemoteHName((nsd_t*)pThis->pTcp, &fromHost);
+ LogError(0, RS_RET_CERT_NOT_YET_ACTIVE, "not permitted to talk to peer '%s': "
+ "certificate %d not yet active", fromHost, i);
+ free(fromHost);
+ gtlsGetCertInfo(pThis, &pStr);
+ LogError(0, RS_RET_CERT_NOT_YET_ACTIVE,
+ "invalid cert info: %s", cstrGetSzStrNoNULL(pStr));
+ cstrDestruct(&pStr);
+ ABORT_FINALIZE(RS_RET_CERT_NOT_YET_ACTIVE);
+ }
+
+ gnutls_x509_crt_deinit(cert);
+
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* check if it is OK to talk to the remote peer
+ * rgerhards, 2008-05-21
+ */
+rsRetVal
+gtlsChkPeerAuth(nsd_gtls_t *pThis)
+{
+ DEFiRet;
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ /* call the actual function based on current auth mode */
+ switch(pThis->authMode) {
+ case GTLS_AUTH_CERTNAME:
+ /* if we check the name, we must ensure the cert is valid */
+ CHKiRet(gtlsChkPeerCertValidity(pThis));
+ CHKiRet(gtlsChkPeerID(pThis));
+ break;
+ case GTLS_AUTH_CERTFINGERPRINT:
+ CHKiRet(gtlsChkPeerID(pThis));
+ break;
+ case GTLS_AUTH_CERTVALID:
+ CHKiRet(gtlsChkPeerCertValidity(pThis));
+ break;
+ case GTLS_AUTH_CERTANON:
+ FINALIZE;
+ break;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* globally de-initialize GnuTLS */
+static rsRetVal
+gtlsGlblExit(void)
+{
+ DEFiRet;
+ gnutls_anon_free_server_credentials(anoncredSrv);
+ gnutls_dh_params_deinit(dh_params);
+ gnutls_global_deinit();
+ RETiRet;
+}
+
+
+/* end a GnuTLS session
+ * The function checks if we have a session and ends it only if so. So it can
+ * always be called, even if there currently is no session.
+ */
+static rsRetVal
+gtlsEndSess(nsd_gtls_t *pThis)
+{
+ int gnuRet;
+ DEFiRet;
+
+ if(pThis->bHaveSess) {
+ if(pThis->bIsInitiator) {
+ gnuRet = gnutls_bye(pThis->sess, GNUTLS_SHUT_WR);
+ while(gnuRet == GNUTLS_E_INTERRUPTED || gnuRet == GNUTLS_E_AGAIN) {
+ gnuRet = gnutls_bye(pThis->sess, GNUTLS_SHUT_WR);
+ }
+ }
+ gnutls_deinit(pThis->sess);
+ pThis->bHaveSess = 0;
+ }
+ RETiRet;
+}
+
+
+/* a small wrapper for gnutls_transport_set_ptr(). The main intension for
+ * creating this wrapper is to get the annoying "cast to pointer from different
+ * size" compiler warning just once. There seems to be no way around it, see:
+ * http://lists.gnu.org/archive/html/help-gnutls/2008-05/msg00000.html
+ * rgerhards, 2008.05-07
+ */
+#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
+static inline void
+gtlsSetTransportPtr(nsd_gtls_t *pThis, int sock)
+{
+ /* Note: the compiler warning for the next line is OK - see header comment! */
+ gnutls_transport_set_ptr(pThis->sess, (gnutls_transport_ptr_t) sock);
+}
+#pragma GCC diagnostic warning "-Wint-to-pointer-cast"
+
+/* ---------------------------- end GnuTLS specifics ---------------------------- */
+
+
+/* Standard-Constructor */
+BEGINobjConstruct(nsd_gtls) /* be sure to specify the object type also in END macro! */
+ iRet = nsd_ptcp.Construct(&pThis->pTcp);
+ pThis->bReportAuthErr = 1;
+ENDobjConstruct(nsd_gtls)
+
+
+/* destructor for the nsd_gtls object */
+PROTOTYPEobjDestruct(nsd_gtls);
+BEGINobjDestruct(nsd_gtls) /* be sure to specify the object type also in END and CODESTART macros! */
+CODESTARTobjDestruct(nsd_gtls)
+ if(pThis->iMode == 1) {
+ gtlsEndSess(pThis);
+ }
+
+ if(pThis->pTcp != NULL) {
+ nsd_ptcp.Destruct(&pThis->pTcp);
+ }
+
+ free(pThis->pszConnectHost);
+ free(pThis->pszRcvBuf);
+ free((void*) pThis->pszCAFile);
+ free((void*) pThis->pszCRLFile);
+
+ if(pThis->bOurCertIsInit)
+ for(unsigned i=0; i<pThis->nOurCerts; ++i) {
+ gnutls_x509_crt_deinit(pThis->pOurCerts[i]);
+ }
+ if(pThis->bOurKeyIsInit)
+ gnutls_x509_privkey_deinit(pThis->ourKey);
+ if(pThis->bHaveSess)
+ gnutls_deinit(pThis->sess);
+ if(pThis->xcred != NULL
+ && (pThis->bIsInitiator || (!pThis->xcred_is_copy && (!pThis->bIsInitiator || pThis->bHaveSess)))
+ ) {
+ gnutls_certificate_free_credentials(pThis->xcred);
+ free((void*) pThis->pszKeyFile);
+ free((void*) pThis->pszCertFile);
+ }
+ENDobjDestruct(nsd_gtls)
+
+
+/* Set the driver mode. For us, this has the following meaning:
+ * 0 - work in plain tcp mode, without tls (e.g. before a STARTTLS)
+ * 1 - work in TLS mode
+ * rgerhards, 2008-04-28
+ */
+static rsRetVal
+SetMode(nsd_t *const pNsd, const int mode)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ dbgprintf("(tls) mode: %d\n", mode);
+ if(mode != 0 && mode != 1) {
+ LogError(0, RS_RET_INVALID_DRVR_MODE, "error: driver mode %d not supported by "
+ "gtls netstream driver", mode);
+ ABORT_FINALIZE(RS_RET_INVALID_DRVR_MODE);
+ }
+
+ pThis->iMode = mode;
+
+finalize_it:
+ RETiRet;
+}
+
+/* Set the authentication mode. For us, the following is supported:
+ * anon - no certificate checks whatsoever (discouraged, but supported)
+ * x509/certvalid - (just) check certificate validity
+ * x509/fingerprint - certificate fingerprint
+ * x509/name - cerfificate name check
+ * mode == NULL is valid and defaults to x509/name
+ * rgerhards, 2008-05-16
+ */
+static rsRetVal
+SetAuthMode(nsd_t *pNsd, uchar *mode)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ if(mode == NULL || !strcasecmp((char*)mode, "x509/name")) {
+ pThis->authMode = GTLS_AUTH_CERTNAME;
+ } else if(!strcasecmp((char*) mode, "x509/fingerprint")) {
+ pThis->authMode = GTLS_AUTH_CERTFINGERPRINT;
+ } else if(!strcasecmp((char*) mode, "x509/certvalid")) {
+ pThis->authMode = GTLS_AUTH_CERTVALID;
+ } else if(!strcasecmp((char*) mode, "anon")) {
+ pThis->authMode = GTLS_AUTH_CERTANON;
+ } else {
+ LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: authentication mode '%s' not supported by "
+ "gtls netstream driver", mode);
+ ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED);
+ }
+
+ dbgprintf("SetAuthMode to %s\n", (mode != NULL ? (char*)mode : "NULL"));
+/* TODO: clear stored IDs! */
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Set the PermitExpiredCerts mode. For us, the following is supported:
+ * on - fail if certificate is expired
+ * off - ignore expired certificates
+ * warn - warn if certificate is expired
+ * alorbach, 2018-12-20
+ */
+static rsRetVal
+SetPermitExpiredCerts(nsd_t *pNsd, uchar *mode)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ /* default is set to off! */
+ if(mode == NULL || !strcasecmp((char*)mode, "off")) {
+ pThis->permitExpiredCerts = GTLS_EXPIRED_DENY;
+ } else if(!strcasecmp((char*) mode, "warn")) {
+ pThis->permitExpiredCerts = GTLS_EXPIRED_WARN;
+ } else if(!strcasecmp((char*) mode, "on")) {
+ pThis->permitExpiredCerts = GTLS_EXPIRED_PERMIT;
+ } else {
+ LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: permitexpiredcerts mode '%s' not supported by "
+ "gtls netstream driver", mode);
+ ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED);
+ }
+
+ dbgprintf("SetPermitExpiredCerts: Set Mode %s/%d\n",
+ (mode != NULL ? (char*)mode : "NULL"), pThis->permitExpiredCerts);
+
+/* TODO: clear stored IDs! */
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Set permitted peers. It is depending on the auth mode if this are
+ * fingerprints or names. -- rgerhards, 2008-05-19
+ */
+static rsRetVal
+SetPermPeers(nsd_t *pNsd, permittedPeers_t *pPermPeers)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ if(pPermPeers == NULL)
+ FINALIZE;
+
+ if(pThis->authMode != GTLS_AUTH_CERTFINGERPRINT && pThis->authMode != GTLS_AUTH_CERTNAME) {
+ LogError(0, RS_RET_VALUE_NOT_IN_THIS_MODE, "authentication not supported by "
+ "gtls netstream driver in the configured authentication mode - ignored");
+ ABORT_FINALIZE(RS_RET_VALUE_NOT_IN_THIS_MODE);
+ }
+
+ pThis->pPermPeers = pPermPeers;
+
+finalize_it:
+ RETiRet;
+}
+
+/* gnutls priority string
+ * PascalWithopf 2017-08-16
+ */
+static rsRetVal
+SetGnutlsPriorityString(nsd_t *pNsd, uchar *gnutlsPriorityString)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ pThis->gnutlsPriorityString = gnutlsPriorityString;
+ dbgprintf("gnutlsPriorityString: set to '%s'\n",
+ (gnutlsPriorityString != NULL ? (char*)gnutlsPriorityString : "NULL"));
+ RETiRet;
+}
+
+/* Set the driver cert extended key usage check setting
+ * 0 - ignore contents of extended key usage
+ * 1 - verify that cert contents is compatible with appropriate OID
+ * jvymazal, 2019-08-16
+ */
+static rsRetVal
+SetCheckExtendedKeyUsage(nsd_t *pNsd, int ChkExtendedKeyUsage)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ if(ChkExtendedKeyUsage != 0 && ChkExtendedKeyUsage != 1) {
+ LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: driver ChkExtendedKeyUsage %d "
+ "not supported by gtls netstream driver", ChkExtendedKeyUsage);
+ ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED);
+ }
+
+ pThis->dataTypeCheck = ChkExtendedKeyUsage;
+
+finalize_it:
+ RETiRet;
+}
+
+/* Set the driver name checking strictness
+ * 0 - less strict per RFC 5280, section 4.1.2.6 - either SAN or CN match is good
+ * 1 - more strict per RFC 6125 - if any SAN present it must match (CN is ignored)
+ * jvymazal, 2019-08-16
+ */
+static rsRetVal
+SetPrioritizeSAN(nsd_t *pNsd, int prioritizeSan)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ if(prioritizeSan != 0 && prioritizeSan != 1) {
+ LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: driver prioritizeSan %d "
+ "not supported by gtls netstream driver", prioritizeSan);
+ ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED);
+ }
+
+ pThis->bSANpriority = prioritizeSan;
+
+finalize_it:
+ RETiRet;
+}
+
+/* Set the driver tls verifyDepth
+ * alorbach, 2019-12-20
+ */
+static rsRetVal
+SetTlsVerifyDepth(nsd_t *pNsd, int verifyDepth)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ if (verifyDepth == 0) {
+ FINALIZE;
+ }
+ assert(verifyDepth >= 2);
+ pThis->DrvrVerifyDepth = verifyDepth;
+
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+SetTlsCAFile(nsd_t *pNsd, const uchar *const caFile)
+{
+ DEFiRet;
+ nsd_gtls_t *const pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ if(caFile == NULL) {
+ pThis->pszCAFile = NULL;
+ } else {
+ CHKmalloc(pThis->pszCAFile = (const uchar*) strdup((const char*) caFile));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+SetTlsCRLFile(nsd_t *pNsd, const uchar *const crlFile)
+{
+ DEFiRet;
+ nsd_gtls_t *const pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ if(crlFile == NULL) {
+ pThis->pszCRLFile = NULL;
+ } else {
+ CHKmalloc(pThis->pszCRLFile = (const uchar*) strdup((const char*) crlFile));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+SetTlsKeyFile(nsd_t *pNsd, const uchar *const pszFile)
+{
+ DEFiRet;
+ nsd_gtls_t *const pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ if(pszFile == NULL) {
+ pThis->pszKeyFile = NULL;
+ } else {
+ CHKmalloc(pThis->pszKeyFile = (const uchar*) strdup((const char*) pszFile));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+static rsRetVal
+SetTlsCertFile(nsd_t *pNsd, const uchar *const pszFile)
+{
+ DEFiRet;
+ nsd_gtls_t *const pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ if(pszFile == NULL) {
+ pThis->pszCertFile = NULL;
+ } else {
+ CHKmalloc(pThis->pszCertFile = (const uchar*) strdup((const char*) pszFile));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+/* Provide access to the underlying OS socket. This is primarily
+ * useful for other drivers (like nsd_gtls) who utilize ourselfs
+ * for some of their functionality. -- rgerhards, 2008-04-18
+ */
+static rsRetVal
+SetSock(nsd_t *pNsd, int sock)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ assert(sock >= 0);
+
+ nsd_ptcp.SetSock(pThis->pTcp, sock);
+
+ RETiRet;
+}
+
+
+/* Keep Alive Options
+ */
+static rsRetVal
+SetKeepAliveIntvl(nsd_t *pNsd, int keepAliveIntvl)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ assert(keepAliveIntvl >= 0);
+
+ nsd_ptcp.SetKeepAliveIntvl(pThis->pTcp, keepAliveIntvl);
+
+ RETiRet;
+}
+
+
+/* Keep Alive Options
+ */
+static rsRetVal
+SetKeepAliveProbes(nsd_t *pNsd, int keepAliveProbes)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ assert(keepAliveProbes >= 0);
+
+ nsd_ptcp.SetKeepAliveProbes(pThis->pTcp, keepAliveProbes);
+
+ RETiRet;
+}
+
+
+/* Keep Alive Options
+ */
+static rsRetVal
+SetKeepAliveTime(nsd_t *pNsd, int keepAliveTime)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ assert(keepAliveTime >= 0);
+
+ nsd_ptcp.SetKeepAliveTime(pThis->pTcp, keepAliveTime);
+
+ RETiRet;
+}
+
+
+/* abort a connection. This is meant to be called immediately
+ * before the Destruct call. -- rgerhards, 2008-03-24
+ */
+static rsRetVal
+Abort(nsd_t *pNsd)
+{
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+
+ if(pThis->iMode == 0) {
+ nsd_ptcp.Abort(pThis->pTcp);
+ }
+
+ RETiRet;
+}
+
+
+/* Callback after netstrm obj init in nsd_ptcp - permits us to add some data */
+static rsRetVal
+LstnInitDrvr(netstrm_t *const pThis)
+{
+ DEFiRet;
+ CHKiRet(gtlsInitCred((nsd_gtls_t*) pThis->pDrvrData));
+ CHKiRet(gtlsAddOurCert((nsd_gtls_t*) pThis->pDrvrData));
+finalize_it:
+ RETiRet;
+}
+
+
+
+/* initialize the tcp socket for a listner
+ * Here, we use the ptcp driver - because there is nothing special
+ * at this point with GnuTLS. Things become special once we accept
+ * a session, but not during listener setup.
+ * gerhards, 2008-04-25
+ */
+static rsRetVal ATTR_NONNULL(1,3,5)
+LstnInit(netstrms_t *pNS, void *pUsr, rsRetVal(*fAddLstn)(void*,netstrm_t*),
+ const int iSessMax, const tcpLstnParams_t *const cnf_params)
+{
+ DEFiRet;
+ pNS->fLstnInitDrvr = LstnInitDrvr;
+ iRet = nsd_ptcp.LstnInit(pNS, pUsr, fAddLstn, iSessMax, cnf_params);
+//finalize_it:
+ RETiRet;
+}
+
+
+/* This function checks if the connection is still alive - well, kind of...
+ * This is a dummy here. For details, check function common in ptcp driver.
+ * rgerhards, 2008-06-09
+ */
+static rsRetVal
+CheckConnection(nsd_t __attribute__((unused)) *pNsd)
+{
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ dbgprintf("CheckConnection for %p\n", pNsd);
+ return nsd_ptcp.CheckConnection(pThis->pTcp);
+}
+
+
+/* get the remote hostname. The returned hostname must be freed by the caller.
+ * rgerhards, 2008-04-25
+ */
+static rsRetVal
+GetRemoteHName(nsd_t *pNsd, uchar **ppszHName)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ iRet = nsd_ptcp.GetRemoteHName(pThis->pTcp, ppszHName);
+ RETiRet;
+}
+
+
+/* Provide access to the sockaddr_storage of the remote peer. This
+ * is needed by the legacy ACL system. --- gerhards, 2008-12-01
+ */
+static rsRetVal
+GetRemAddr(nsd_t *pNsd, struct sockaddr_storage **ppAddr)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ iRet = nsd_ptcp.GetRemAddr(pThis->pTcp, ppAddr);
+ RETiRet;
+}
+
+
+/* get the remote host's IP address. Caller must Destruct the object. */
+static rsRetVal
+GetRemoteIP(nsd_t *pNsd, prop_t **ip)
+{
+ DEFiRet;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ iRet = nsd_ptcp.GetRemoteIP(pThis->pTcp, ip);
+ RETiRet;
+}
+
+
+/* accept an incoming connection request - here, we do the usual accept
+ * handling. TLS specific handling is done thereafter (and if we run in TLS
+ * mode at this time).
+ * rgerhards, 2008-04-25
+ */
+static rsRetVal
+AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew)
+{
+ DEFiRet;
+ int gnuRet;
+ nsd_gtls_t *pNew = NULL;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ const char *error_position = NULL;
+
+ ISOBJ_TYPE_assert((pThis), nsd_gtls);
+ CHKiRet(nsd_gtlsConstruct(&pNew)); // TODO: prevent construct/destruct!
+ CHKiRet(nsd_ptcp.Destruct(&pNew->pTcp));
+ CHKiRet(nsd_ptcp.AcceptConnReq(pThis->pTcp, &pNew->pTcp));
+
+ if(pThis->iMode == 0) {
+ /* we are in non-TLS mode, so we are done */
+ *ppNew = (nsd_t*) pNew;
+ FINALIZE;
+ }
+ /* copy Properties to pnew first */
+ pNew->authMode = pThis->authMode;
+ pNew->permitExpiredCerts = pThis->permitExpiredCerts;
+ pNew->pPermPeers = pThis->pPermPeers;
+ pNew->gnutlsPriorityString = pThis->gnutlsPriorityString;
+ pNew->DrvrVerifyDepth = pThis->DrvrVerifyDepth;
+ pNew->dataTypeCheck = pThis->dataTypeCheck;
+ pNew->bSANpriority = pThis->bSANpriority;
+ pNew->pszCertFile = pThis->pszCertFile;
+ pNew->pszKeyFile = pThis->pszKeyFile;
+ pNew->xcred = pThis->xcred; // TODO: verify once again; xcred is read only at this stage
+ pNew->xcred_is_copy = 1; // do not free on pNew Destruction
+
+ /* if we reach this point, we are in TLS mode */
+ iRet = gtlsInitSession(pNew);
+ if (iRet != RS_RET_OK) {
+ if (iRet == RS_RET_CERTLESS) {
+ dbgprintf("AcceptConnReq certless mode\n");
+ /* Set status to OK */
+ iRet = RS_RET_OK;
+ } else {
+ goto finalize_it;
+ }
+ }
+ gtlsSetTransportPtr(pNew, ((nsd_ptcp_t*) (pNew->pTcp))->sock);
+
+ dbgprintf("AcceptConnReq bOurCertIsInit=%hu bOurKeyIsInit=%hu \n",
+ pNew->bOurCertIsInit, pNew->bOurKeyIsInit);
+
+ /* here is the priorityString set */
+ if(pNew->gnutlsPriorityString != NULL) {
+ dbgprintf("AcceptConnReq setting configured priority string (ciphers)\n");
+ if(gnutls_priority_set_direct(pNew->sess,
+ (const char*) pNew->gnutlsPriorityString,
+ &error_position)==GNUTLS_E_INVALID_REQUEST) {
+ LogError(0, RS_RET_GNUTLS_ERR, "Syntax Error in"
+ " Priority String: \"%s\"\n", error_position);
+ }
+ } else {
+ if(pThis->authMode == GTLS_AUTH_CERTANON) {
+ /* Allow ANON Ciphers */
+ dbgprintf("AcceptConnReq setting anon ciphers Try1: %s\n", GTLS_ANON_PRIO_NOTLSV13);
+ if(gnutls_priority_set_direct(pNew->sess,(const char*) GTLS_ANON_PRIO_NOTLSV13,
+ &error_position)==GNUTLS_E_INVALID_REQUEST) {
+ dbgprintf("AcceptConnReq setting anon ciphers Try2 (TLS1.3 unknown): %s\n",
+ GTLS_ANON_PRIO);
+ CHKgnutls(gnutls_priority_set_direct(pNew->sess, GTLS_ANON_PRIO, &error_position));
+ }
+ /* Uncomment for DEBUG
+ print_cipher_suite_list("NORMAL:+ANON-DH:+ANON-ECDH:+COMP-ALL"); */
+ } else {
+ /* Use default priorities */
+ dbgprintf("AcceptConnReq setting default ciphers\n");
+ CHKgnutls(gnutls_set_default_priority(pNew->sess));
+ }
+ }
+
+ /* we now do the handshake. This is a bit complicated, because we are
+ * on non-blocking sockets. Usually, the handshake will not complete
+ * immediately, so that we need to retry it some time later.
+ */
+ gnuRet = gnutls_handshake(pNew->sess);
+ if(gnuRet == GNUTLS_E_AGAIN || gnuRet == GNUTLS_E_INTERRUPTED) {
+ pNew->rtryCall = gtlsRtry_handshake;
+ dbgprintf("GnuTLS handshake does not complete immediately - "
+ "setting to retry (this is OK and normal)\n");
+ } else if(gnuRet == 0) {
+ /* we got a handshake, now check authorization */
+ CHKiRet(gtlsChkPeerAuth(pNew));
+ } else {
+ uchar *pGnuErr = gtlsStrerror(gnuRet);
+ LogError(0, RS_RET_TLS_HANDSHAKE_ERR,
+ "gnutls returned error on handshake: %s\n", pGnuErr);
+ free(pGnuErr);
+ ABORT_FINALIZE(RS_RET_TLS_HANDSHAKE_ERR);
+ }
+
+ pNew->iMode = 1; /* this session is now in TLS mode! */
+
+ *ppNew = (nsd_t*) pNew;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+if (error_position != NULL) {
+ dbgprintf("AcceptConnReq error_position=%s\n", error_position);
+}
+
+ if(pNew != NULL)
+ nsd_gtlsDestruct(&pNew);
+ }
+ RETiRet;
+}
+
+
+/* receive data from a tcp socket
+ * The lenBuf parameter must contain the max buffer size on entry and contains
+ * the number of octets read on exit. This function
+ * never blocks, not even when called on a blocking socket. That is important
+ * for client sockets, which are set to block during send, but should not
+ * block when trying to read data. -- rgerhards, 2008-03-17
+ * The function now follows the usual iRet calling sequence.
+ * With GnuTLS, we may need to restart a recv() system call. If so, we need
+ * to supply the SAME buffer on the retry. We can not assure this, as the
+ * caller is free to call us with any buffer location (and in current
+ * implementation, it is on the stack and extremely likely to change). To
+ * work-around this problem, we allocate a buffer ourselfs and always receive
+ * into that buffer. We pass data on to the caller only after we have received it.
+ * To save some space, we allocate that internal buffer only when it is actually
+ * needed, which means when we reach this function for the first time. To keep
+ * the algorithm simple, we always supply data only from the internal buffer,
+ * even if it is a single byte. As we have a stream, the caller must be prepared
+ * to accept messages in any order, so we do not need to take care about this.
+ * Please note that the logic also forces us to do some "faking" in select(), as
+ * we must provide a fake "is ready for readign" status if we have data inside our
+ * buffer. -- rgerhards, 2008-06-23
+ */
+static rsRetVal
+Rcv(nsd_t *pNsd, uchar *pBuf, ssize_t *pLenBuf, int *const oserr)
+{
+ DEFiRet;
+ ssize_t iBytesCopy; /* how many bytes are to be copied to the client buffer? */
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ if(pThis->bAbortConn)
+ ABORT_FINALIZE(RS_RET_CONNECTION_ABORTREQ);
+
+ if(pThis->iMode == 0) {
+ CHKiRet(nsd_ptcp.Rcv(pThis->pTcp, pBuf, pLenBuf, oserr));
+ FINALIZE;
+ }
+
+ /* --- in TLS mode now --- */
+
+ /* Buffer logic applies only if we are in TLS mode. Here we
+ * assume that we will switch from plain to TLS, but never back. This
+ * assumption may be unsafe, but it is the model for the time being and I
+ * do not see any valid reason why we should switch back to plain TCP after
+ * we were in TLS mode. However, in that case we may lose something that
+ * is already in the receive buffer ... risk accepted. -- rgerhards, 2008-06-23
+ */
+
+ if(pThis->pszRcvBuf == NULL) {
+ /* we have no buffer, so we need to malloc one */
+ CHKmalloc(pThis->pszRcvBuf = malloc(NSD_GTLS_MAX_RCVBUF));
+ pThis->lenRcvBuf = -1;
+ }
+
+ /* now check if we have something in our buffer. If so, we satisfy
+ * the request from buffer contents.
+ */
+ if(pThis->lenRcvBuf == -1) { /* no data present, must read */
+ CHKiRet(gtlsRecordRecv(pThis));
+ }
+
+ if(pThis->lenRcvBuf == 0) { /* EOS */
+ *oserr = errno;
+ ABORT_FINALIZE(RS_RET_CLOSED);
+ }
+
+ /* if we reach this point, data is present in the buffer and must be copied */
+ iBytesCopy = pThis->lenRcvBuf - pThis->ptrRcvBuf;
+ if(iBytesCopy > *pLenBuf) {
+ iBytesCopy = *pLenBuf;
+ } else {
+ pThis->lenRcvBuf = -1; /* buffer will be emptied below */
+ }
+
+ memcpy(pBuf, pThis->pszRcvBuf + pThis->ptrRcvBuf, iBytesCopy);
+ pThis->ptrRcvBuf += iBytesCopy;
+ *pLenBuf = iBytesCopy;
+
+finalize_it:
+ if (iRet != RS_RET_OK &&
+ iRet != RS_RET_RETRY) {
+ /* We need to free the receive buffer in error error case unless a retry is wanted. , if we
+ * allocated one. -- rgerhards, 2008-12-03 -- moved here by alorbach, 2015-12-01
+ */
+ *pLenBuf = 0;
+ free(pThis->pszRcvBuf);
+ pThis->pszRcvBuf = NULL;
+ }
+ dbgprintf("gtlsRcv return. nsd %p, iRet %d, lenRcvBuf %d, ptrRcvBuf %d\n", pThis,
+ iRet, pThis->lenRcvBuf, pThis->ptrRcvBuf);
+ RETiRet;
+}
+
+
+/* send a buffer. On entry, pLenBuf contains the number of octets to
+ * write. On exit, it contains the number of octets actually written.
+ * If this number is lower than on entry, only a partial buffer has
+ * been written.
+ * rgerhards, 2008-03-19
+ */
+static rsRetVal
+Send(nsd_t *pNsd, uchar *pBuf, ssize_t *pLenBuf)
+{
+ int iSent;
+ int wantsWriteData = 0;
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ DEFiRet;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+
+ if(pThis->bAbortConn)
+ ABORT_FINALIZE(RS_RET_CONNECTION_ABORTREQ);
+
+ if(pThis->iMode == 0) {
+ CHKiRet(nsd_ptcp.Send(pThis->pTcp, pBuf, pLenBuf));
+ FINALIZE;
+ }
+
+ /* in TLS mode now */
+ while(1) { /* loop broken inside */
+ iSent = gnutls_record_send(pThis->sess, pBuf, *pLenBuf);
+ if(iSent >= 0) {
+ *pLenBuf = iSent;
+ break;
+ }
+ if(iSent != GNUTLS_E_INTERRUPTED && iSent != GNUTLS_E_AGAIN) {
+ /* Check if the underlaying file descriptor needs to read or write data!*/
+ wantsWriteData = gnutls_record_get_direction(pThis->sess);
+ uchar *pErr = gtlsStrerror(iSent);
+ LogError(0, RS_RET_GNUTLS_ERR, "unexpected GnuTLS error %d, wantsWriteData=%d - this "
+ "could be caused by a broken connection. GnuTLS reports: %s\n",
+ iSent, wantsWriteData, pErr);
+ free(pErr);
+ gnutls_perror(iSent);
+ ABORT_FINALIZE(RS_RET_GNUTLS_ERR);
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+/* Enable KEEPALIVE handling on the socket.
+ * rgerhards, 2009-06-02
+ */
+static rsRetVal
+EnableKeepAlive(nsd_t *pNsd)
+{
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ return nsd_ptcp.EnableKeepAlive(pThis->pTcp);
+}
+
+
+/*
+ * SNI should not be used if the hostname is a bare IP address
+ */
+static int
+SetServerNameIfPresent(nsd_gtls_t *pThis, uchar *host) {
+ struct sockaddr_in sa;
+ struct sockaddr_in6 sa6;
+
+ int inet_pton_ret = inet_pton(AF_INET, CHAR_CONVERT(host), &(sa.sin_addr));
+
+ if (inet_pton_ret == 0) { // host wasn't a bare IPv4 address: try IPv6
+ inet_pton_ret = inet_pton(AF_INET6, CHAR_CONVERT(host), &(sa6.sin6_addr));
+ }
+
+ switch(inet_pton_ret) {
+ case 1: // host is a valid IP address: don't use SNI
+ return 0;
+ case 0: // host isn't a valid IP address: assume it's a domain name, use SNI
+ return gnutls_server_name_set(pThis->sess, GNUTLS_NAME_DNS, host, ustrlen(host));
+ default: // unexpected error
+ return -1;
+ }
+
+}
+
+/* open a connection to a remote host (server). With GnuTLS, we always
+ * open a plain tcp socket and then, if in TLS mode, do a handshake on it.
+ * rgerhards, 2008-03-19
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations" /* TODO: FIX Warnings! */
+static rsRetVal
+Connect(nsd_t *pNsd, int family, uchar *port, uchar *host, char *device)
+{
+ nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ int sock;
+ int gnuRet;
+ const char *error_position;
+# ifdef HAVE_GNUTLS_CERTIFICATE_TYPE_SET_PRIORITY
+ static const int cert_type_priority[2] = { GNUTLS_CRT_X509, 0 };
+# endif
+ DEFiRet;
+ dbgprintf("Connect to %s:%s\n", host, port);
+
+ ISOBJ_TYPE_assert(pThis, nsd_gtls);
+ assert(port != NULL);
+ assert(host != NULL);
+
+ CHKiRet(gtlsInitCred(pThis));
+ CHKiRet(gtlsAddOurCert(pThis));
+ CHKiRet(nsd_ptcp.Connect(pThis->pTcp, family, port, host, device));
+
+ if(pThis->iMode == 0)
+ FINALIZE;
+
+ /* we reach this point if in TLS mode */
+ CHKgnutls(gnutls_init(&pThis->sess, GNUTLS_CLIENT));
+ pThis->bHaveSess = 1;
+ pThis->bIsInitiator = 1;
+
+ CHKgnutls(SetServerNameIfPresent(pThis, host));
+
+ /* in the client case, we need to set a callback that ensures our certificate
+ * will be presented to the server even if it is not signed by one of the server's
+ * trusted roots. This is necessary to support fingerprint authentication.
+ */
+ /* store a pointer to ourselfs (needed by callback) */
+ gnutls_session_set_ptr(pThis->sess, (void*)pThis);
+ iRet = gtlsLoadOurCertKey(pThis); /* first load .pem files */
+ if(iRet == RS_RET_OK) {
+# if HAVE_GNUTLS_CERTIFICATE_SET_RETRIEVE_FUNCTION
+ gnutls_certificate_set_retrieve_function(pThis->xcred, gtlsClientCertCallback);
+# else
+ gnutls_certificate_client_set_retrieve_function(pThis->xcred, gtlsClientCertCallback);
+# endif
+ dbgprintf("Connect: enable certificate checking (VerifyDepth=%d)\n", pThis->DrvrVerifyDepth);
+ if (pThis->DrvrVerifyDepth != 0) {
+ gnutls_certificate_set_verify_limits(pThis->xcred, 8200, pThis->DrvrVerifyDepth);
+ }
+ } else if(iRet == RS_RET_CERTLESS) {
+ dbgprintf("Connect: certificates not configured, not loaded.\n");
+ } else {
+ LogError(0, iRet, "Connect failed to INIT Session %d", gnuRet);
+ ABORT_FINALIZE(iRet);; /* we have an error case! */
+ }
+
+ /*priority string setzen*/
+ if(pThis->gnutlsPriorityString != NULL) {
+ dbgprintf("Connect: setting configured priority string (ciphers)\n");
+ if(gnutls_priority_set_direct(pThis->sess,
+ (const char*) pThis->gnutlsPriorityString,
+ &error_position)==GNUTLS_E_INVALID_REQUEST) {
+ LogError(0, RS_RET_GNUTLS_ERR, "Syntax Error in"
+ " Priority String: \"%s\"\n", error_position);
+ }
+ } else {
+ if(pThis->authMode == GTLS_AUTH_CERTANON || pThis->bOurCertIsInit == 0) {
+ /* Allow ANON Ciphers */
+ dbgprintf("Connect: setting anon ciphers Try1: %s\n", GTLS_ANON_PRIO_NOTLSV13);
+ if(gnutls_priority_set_direct(pThis->sess,(const char*) GTLS_ANON_PRIO_NOTLSV13,
+ &error_position)==GNUTLS_E_INVALID_REQUEST) {
+ dbgprintf("Connect: setting anon ciphers Try2 (TLS1.3 unknown): %s\n", GTLS_ANON_PRIO);
+ CHKgnutls(gnutls_priority_set_direct(pThis->sess, GTLS_ANON_PRIO, &error_position));
+ }
+ /* Uncomment for DEBUG
+ print_cipher_suite_list("NORMAL:+ANON-DH:+ANON-ECDH:+COMP-ALL"); */
+ } else {
+ /* Use default priorities */
+ dbgprintf("Connect: setting default ciphers\n");
+ CHKgnutls(gnutls_set_default_priority(pThis->sess));
+ }
+ }
+
+# ifdef HAVE_GNUTLS_CERTIFICATE_TYPE_SET_PRIORITY
+ /* The gnutls_certificate_type_set_priority function is deprecated
+ * and not available in recent GnuTLS versions. However, there is no
+ * doc how to properly replace it with gnutls_priority_set_direct.
+ * A lot of folks have simply removed it, when they also called
+ * gnutls_set_default_priority. This is what we now also do. If
+ * this causes problems or someone has an idea of how to replace
+ * the deprecated function in a better way, please let us know!
+ * In any case, we use it as long as it is available and let
+ * not insult us by the deprecation warnings.
+ * 2015-05-18 rgerhards
+ */
+ CHKgnutls(gnutls_certificate_type_set_priority(pThis->sess, cert_type_priority));
+# endif
+
+ /* put the x509 credentials to the current session */
+ CHKgnutls(gnutls_credentials_set(pThis->sess, GNUTLS_CRD_CERTIFICATE, pThis->xcred));
+
+ /* check for anon authmode */
+ if (pThis->authMode == GTLS_AUTH_CERTANON) {
+ dbgprintf("Connect: anon authmode, gnutls_credentials_set GNUTLS_CRD_ANON\n");
+ CHKgnutls(gnutls_credentials_set(pThis->sess, GNUTLS_CRD_ANON, anoncred));
+ gnutls_dh_set_prime_bits(pThis->sess, dhMinBits);
+ }
+
+ /* assign the socket to GnuTls */
+ CHKiRet(nsd_ptcp.GetSock(pThis->pTcp, &sock));
+ gtlsSetTransportPtr(pThis, sock);
+
+ /* we need to store the hostname as an alternate mean of authentication if no
+ * permitted peer names are given. Using the hostname is quite useful. It permits
+ * auto-configuration of security if a commen root cert is present. -- rgerhards, 2008-05-26
+ */
+ CHKmalloc(pThis->pszConnectHost = (uchar*)strdup((char*)host));
+
+ /* and perform the handshake */
+ CHKgnutls(gnutls_handshake(pThis->sess));
+ dbgprintf("GnuTLS handshake succeeded\n");
+
+ /* now check if the remote peer is permitted to talk to us - ideally, we
+ * should do this during the handshake, but GnuTLS does not yet provide
+ * the necessary callbacks -- rgerhards, 2008-05-26
+ */
+ CHKiRet(gtlsChkPeerAuth(pThis));
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pThis->bHaveSess) {
+ gnutls_deinit(pThis->sess);
+ pThis->bHaveSess = 0;
+ /* Free memory using gnutls api first*/
+ gnutls_certificate_free_credentials(pThis->xcred);
+ pThis->xcred = NULL;
+ /* Free other memory */
+ free(pThis->pszConnectHost);
+ pThis->pszConnectHost = NULL;
+ }
+ }
+
+ RETiRet;
+}
+#pragma GCC diagnostic pop
+
+
+/* queryInterface function */
+BEGINobjQueryInterface(nsd_gtls)
+CODESTARTobjQueryInterface(nsd_gtls)
+ if(pIf->ifVersion != nsdCURR_IF_VERSION) {/* check for current version, increment on each change */
+ ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED);
+ }
+
+ /* ok, we have the right interface, so let's fill it
+ * Please note that we may also do some backwards-compatibility
+ * work here (if we can support an older interface version - that,
+ * of course, also affects the "if" above).
+ */
+ pIf->Construct = (rsRetVal(*)(nsd_t**)) nsd_gtlsConstruct;
+ pIf->Destruct = (rsRetVal(*)(nsd_t**)) nsd_gtlsDestruct;
+ pIf->Abort = Abort;
+ pIf->LstnInit = LstnInit;
+ pIf->AcceptConnReq = AcceptConnReq;
+ pIf->Rcv = Rcv;
+ pIf->Send = Send;
+ pIf->Connect = Connect;
+ pIf->SetSock = SetSock;
+ pIf->SetMode = SetMode;
+ pIf->SetAuthMode = SetAuthMode;
+ pIf->SetPermitExpiredCerts = SetPermitExpiredCerts;
+ pIf->SetPermPeers =SetPermPeers;
+ pIf->CheckConnection = CheckConnection;
+ pIf->GetRemoteHName = GetRemoteHName;
+ pIf->GetRemoteIP = GetRemoteIP;
+ pIf->GetRemAddr = GetRemAddr;
+ pIf->EnableKeepAlive = EnableKeepAlive;
+ pIf->SetKeepAliveIntvl = SetKeepAliveIntvl;
+ pIf->SetKeepAliveProbes = SetKeepAliveProbes;
+ pIf->SetKeepAliveTime = SetKeepAliveTime;
+ pIf->SetGnutlsPriorityString = SetGnutlsPriorityString;
+ pIf->SetCheckExtendedKeyUsage = SetCheckExtendedKeyUsage;
+ pIf->SetPrioritizeSAN = SetPrioritizeSAN;
+ pIf->SetTlsVerifyDepth = SetTlsVerifyDepth;
+ pIf->SetTlsCAFile = SetTlsCAFile;
+ pIf->SetTlsCRLFile = SetTlsCRLFile;
+ pIf->SetTlsKeyFile = SetTlsKeyFile;
+ pIf->SetTlsCertFile = SetTlsCertFile;
+finalize_it:
+ENDobjQueryInterface(nsd_gtls)
+
+
+/* exit our class
+ */
+BEGINObjClassExit(nsd_gtls, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */
+CODESTARTObjClassExit(nsd_gtls)
+ gtlsGlblExit(); /* shut down GnuTLS */
+
+ /* release objects we no longer need */
+ objRelease(nsd_ptcp, LM_NSD_PTCP_FILENAME);
+ objRelease(net, LM_NET_FILENAME);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ENDObjClassExit(nsd_gtls)
+
+
+/* Initialize the nsd_gtls class. Must be called as the very first method
+ * before anything else is called inside this class.
+ * rgerhards, 2008-02-19
+ */
+BEGINObjClassInit(nsd_gtls, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */
+ /* request objects we use */
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(nsd_ptcp, LM_NSD_PTCP_FILENAME));
+
+ /* now do global TLS init stuff */
+ CHKiRet(gtlsGlblInit());
+ENDObjClassInit(nsd_gtls)
+
+
+/* --------------- here now comes the plumbing that makes as a library module --------------- */
+
+
+BEGINmodExit
+CODESTARTmodExit
+ nsdsel_gtlsClassExit();
+ nsd_gtlsClassExit();
+ pthread_mutex_destroy(&mutGtlsStrerror);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_LIB_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+
+ /* Initialize all classes that are in our module - this includes ourselfs */
+ CHKiRet(nsd_gtlsClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+ CHKiRet(nsdsel_gtlsClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */
+
+ pthread_mutex_init(&mutGtlsStrerror, NULL);
+ENDmodInit
+/* vi:set ai:
+ */
+