summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2008-04-18 18:29:02 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2008-04-18 18:29:02 +0200
commit2069ab114e2aac9c243aff72042912cac7ef6126 (patch)
tree19bd5df7fb640b754ec6b91954d0153ed0f2eac5 /runtime
parent032b9c1f64691e868b14e6d271ebfc2d093b0c66 (diff)
downloadrsyslog-2069ab114e2aac9c243aff72042912cac7ef6126.tar.gz
rsyslog-2069ab114e2aac9c243aff72042912cac7ef6126.tar.xz
rsyslog-2069ab114e2aac9c243aff72042912cac7ef6126.zip
first working TLS-enabled plain TCP sender
implemented a first working version of a TLS-enabled plain TCP sender (but, of course, the implementation is insecure as it is)
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Makefile.am4
-rw-r--r--runtime/netstrm.c35
-rw-r--r--runtime/nsd.h5
-rw-r--r--runtime/nsd_gtls.c117
-rw-r--r--runtime/nsd_gtls.h2
-rw-r--r--runtime/nsd_ptcp.c21
-rw-r--r--runtime/rsyslog.h1
7 files changed, 166 insertions, 19 deletions
diff --git a/runtime/Makefile.am b/runtime/Makefile.am
index 23e62deb..1d07e79c 100644
--- a/runtime/Makefile.am
+++ b/runtime/Makefile.am
@@ -110,8 +110,8 @@ endif # if ENABLE_INET
if ENABLE_GNUTLS
pkglib_LTLIBRARIES += lmnsd_gtls.la
lmnsd_gtls_la_SOURCES = nsd_gtls.c nsd_gtls.h
-lmnsd_gtls_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags)
+lmnsd_gtls_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) $(gnutls_cflags)
lmnsd_gtls_la_LDFLAGS = -module -avoid-version
-lmnsd_gtls_la_LIBADD =
+lmnsd_gtls_la_LIBADD = $(gnutls_libs)
endif
diff --git a/runtime/netstrm.c b/runtime/netstrm.c
index 274a92d7..a304ada4 100644
--- a/runtime/netstrm.c
+++ b/runtime/netstrm.c
@@ -84,18 +84,22 @@ loadDrvr(netstrm_t *pThis)
uchar *pDrvrName;
DEFiRet;
- pDrvrName = pThis->pDrvrName;
- if(pDrvrName == NULL) /* if no drvr name is set, use system default */
- pDrvrName = glbl.GetDfltNetstrmDrvr();
-
- pThis->Drvr.ifVersion = nsdCURR_IF_VERSION;
- /* The pDrvrName+2 below is a hack to obtain the object name. It
- * safes us to have yet another variable with the name without "lm" in
- * front of it. If we change the module load interface, we may re-think
- * about this hack, but for the time being it is efficient and clean
- * enough. -- rgerhards, 2008-04-18
- */
- CHKiRet(obj.UseObj(__FILE__, pDrvrName+2, pDrvrName, (void*) &pThis->Drvr));
+ if(pThis->Drvr.ifIsLoaded == 0) {
+ pDrvrName = pThis->pDrvrName;
+ if(pDrvrName == NULL) { /* if no drvr name is set, use system default */
+ pDrvrName = glbl.GetDfltNetstrmDrvr();
+ pThis->pDrvrName = (uchar*)strdup((char*)pDrvrName); // TODO: use set method once it exists
+ }
+
+ pThis->Drvr.ifVersion = nsdCURR_IF_VERSION;
+ /* The pDrvrName+2 below is a hack to obtain the object name. It
+ * safes us to have yet another variable with the name without "lm" in
+ * front of it. If we change the module load interface, we may re-think
+ * about this hack, but for the time being it is efficient and clean
+ * enough. -- rgerhards, 2008-04-18
+ */
+ CHKiRet(obj.UseObj(__FILE__, pDrvrName+2, pDrvrName, (void*) &pThis->Drvr));
+ }
finalize_it:
RETiRet;
}
@@ -111,6 +115,13 @@ BEGINobjDestruct(netstrm) /* be sure to specify the object type also in END and
CODESTARTobjDestruct(netstrm)
if(pThis->pDrvrData != NULL)
iRet = pThis->Drvr.Destruct(&pThis->pDrvrData);
+
+ /* driver can only be released after all data has been destructed */
+ if(pThis->Drvr.ifIsLoaded == 1) {
+ obj.ReleaseObj(__FILE__, pThis->pDrvrName+2, pThis->pDrvrName, (void*) &pThis->Drvr);
+ }
+ if(pThis->pDrvrName != NULL)
+ free(pThis->pDrvrName);
ENDobjDestruct(netstrm)
diff --git a/runtime/nsd.h b/runtime/nsd.h
index c8bc95d0..203a65d6 100644
--- a/runtime/nsd.h
+++ b/runtime/nsd.h
@@ -41,6 +41,11 @@ BEGINinterface(nsd) /* name must also be changed in ENDinterface macro! */
rsRetVal (*Rcv)(nsd_t *pThis, uchar *pRcvBuf, ssize_t *pLenBuf);
rsRetVal (*Send)(nsd_t *pThis, uchar *pBuf, ssize_t *pLenBuf);
rsRetVal (*Connect)(nsd_t *pThis, int family, unsigned char *port, unsigned char *host);
+ rsRetVal (*GetSock)(nsd_t *pThis, int *pSock);
+ /* GetSock() returns an error if the driver does not use plain
+ * OS sockets. This interface is primarily meant as an internal aid for
+ * those drivers that utilize the nsd_ptcp to do some of their work.
+ */
ENDinterface(nsd)
#define nsdCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c
index 6fe0cd8a..d1b44fc5 100644
--- a/runtime/nsd_gtls.c
+++ b/runtime/nsd_gtls.c
@@ -37,6 +37,7 @@
#include <fnmatch.h>
#include <fcntl.h>
#include <unistd.h>
+#include <gnutls/gnutls.h>
#include "rsyslog.h"
#include "syslogd-types.h"
@@ -57,15 +58,72 @@ DEFobjCurrIf(glbl)
DEFobjCurrIf(nsd_ptcp)
+/* a macro to check GnuTLS calls against unexpected errors */
+#define CHKgnutls(x) \
+ if((gnuRet = (x)) != 0) { \
+ dbgprintf("unexpected GnuTLS error %d in %s:%d\n", gnuRet, __FILE__, __LINE__); \
+ gnutls_perror(gnuRet); /* TODO: can we do better? */ \
+ ABORT_FINALIZE(RS_RET_GNUTLS_ERR); \
+ }
+
+#define CAFILE "ca.pem" // TODO: allow to specify
+
+/* ------------------------------ GnuTLS specifics ------------------------------ */
+static gnutls_certificate_credentials xcred;
+
+/* globally initialize GnuTLS */
+static rsRetVal
+gtlsGlblInit(void)
+{
+ int gnuRet;
+ DEFiRet;
+
+ CHKgnutls(gnutls_global_init());
+
+ /* X509 stuff */
+ CHKgnutls(gnutls_certificate_allocate_credentials(&xcred));
+
+ /* sets the trusted cas file */
+ gnutls_certificate_set_x509_trust_file(xcred, CAFILE, GNUTLS_X509_FMT_PEM);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* globally de-initialize GnuTLS */
+static rsRetVal
+gtlsGlblExit(void)
+{
+ DEFiRet;
+ /* X509 stuff */
+ gnutls_certificate_free_credentials(xcred);
+ gnutls_global_deinit(); /* we are done... */
+ RETiRet;
+}
+
+
+/* ---------------------------- 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->iMode = 1; /* TODO: must be made configurable */
ENDobjConstruct(nsd_gtls)
/* destructor for the nsd_gtls object */
BEGINobjDestruct(nsd_gtls) /* be sure to specify the object type also in END and CODESTART macros! */
CODESTARTobjDestruct(nsd_gtls)
+ if(pThis->iMode == 1) {
+ if(pThis->bHaveSess) {
+ // TODO: Check for EAGAIN et al
+ gnutls_bye(pThis->sess, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(pThis->sess);
+ }
+ }
+
if(pThis->pTcp != NULL)
nsd_ptcp.Destruct(&pThis->pTcp);
ENDobjDestruct(nsd_gtls)
@@ -150,36 +208,82 @@ finalize_it:
static rsRetVal
Send(nsd_t *pNsd, uchar *pBuf, ssize_t *pLenBuf)
{
+ int iSent;
nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
DEFiRet;
ISOBJ_TYPE_assert(pThis, nsd_gtls);
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);
+RUNLOG_VAR("%d", iSent);
+ if(iSent >= 0) {
+ *pLenBuf = iSent;
+ break;
+ }
+ if(iSent != GNUTLS_E_INTERRUPTED && iSent != GNUTLS_E_AGAIN)
+ ABORT_FINALIZE(RS_RET_GNUTLS_ERR);
}
+
finalize_it:
RETiRet;
}
-/* open a connection to a remote host (server).
+/* 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
*/
static rsRetVal
Connect(nsd_t *pNsd, int family, uchar *port, uchar *host)
{
nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+ int sock;
+ int gnuRet;
+static const int cert_type_priority[3] = { GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0 };
DEFiRet;
ISOBJ_TYPE_assert(pThis, nsd_gtls);
assert(port != NULL);
assert(host != NULL);
- if(pThis->iMode == 0) {
- CHKiRet(nsd_ptcp.Connect(pThis->pTcp, family, port, host));
- }
+ CHKiRet(nsd_ptcp.Connect(pThis->pTcp, family, port, host));
+
+ if(pThis->iMode == 0)
+ FINALIZE;
+
+ /* we reach this point if in TLS mode */
+ CHKgnutls(gnutls_init(&pThis->sess, GNUTLS_CLIENT));
+ pThis->bHaveSess = 1;
+
+ /* Use default priorities */
+ CHKgnutls(gnutls_set_default_priority(pThis->sess));
+ CHKgnutls(gnutls_certificate_type_set_priority(pThis->sess, cert_type_priority));
+
+ /* put the x509 credentials to the current session */
+ CHKgnutls(gnutls_credentials_set(pThis->sess, GNUTLS_CRD_CERTIFICATE, xcred));
+
+ /* assign the socket to GnuTls */
+ CHKiRet(nsd_ptcp.GetSock(pThis->pTcp, &sock));
+ gnutls_transport_set_ptr(pThis->sess, (gnutls_transport_ptr)sock);
+
+ /* and perform the handshake */
+ CHKgnutls(gnutls_handshake(pThis->sess));
+ dbgprintf("GnuTLS handshake succeeded\n");
finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pThis->bHaveSess) {
+ gnutls_deinit(pThis->sess);
+ pThis->bHaveSess = 0;
+ }
+ }
+
RETiRet;
}
@@ -212,6 +316,8 @@ ENDobjQueryInterface(nsd_gtls)
*/
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(glbl, CORE_COMPONENT);
@@ -229,7 +335,8 @@ BEGINObjClassInit(nsd_gtls, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(nsd_ptcp, LM_NSD_PTCP_FILENAME));
- /* set our own handlers */
+ /* now do global TLS init stuff */
+ CHKiRet(gtlsGlblInit());
ENDObjClassInit(nsd_gtls)
diff --git a/runtime/nsd_gtls.h b/runtime/nsd_gtls.h
index ddd561de..c193f57c 100644
--- a/runtime/nsd_gtls.h
+++ b/runtime/nsd_gtls.h
@@ -33,6 +33,8 @@ struct nsd_gtls_s {
BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */
nsd_t *pTcp; /**< our aggregated nsd_ptcp data */
int iMode; /* 0 - plain tcp, 1 - TLS */
+ gnutls_session sess;
+ int bHaveSess;
};
/* interface is defined in nsd.h, we just implement it! */
diff --git a/runtime/nsd_ptcp.c b/runtime/nsd_ptcp.c
index 6f7dd04d..c9df8f8c 100644
--- a/runtime/nsd_ptcp.c
+++ b/runtime/nsd_ptcp.c
@@ -87,6 +87,26 @@ CODESTARTobjDestruct(nsd_ptcp)
ENDobjDestruct(nsd_ptcp)
+/* 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
+ * TODO: what about the server socket structure?
+ */
+static rsRetVal
+GetSock(nsd_t *pNsd, int *pSock)
+{
+ nsd_ptcp_t *pThis = (nsd_ptcp_t*) pNsd;
+ DEFiRet;
+
+ ISOBJ_TYPE_assert((pThis), nsd_ptcp);
+ assert(pSock != NULL);
+
+ *pSock = pThis->sock;
+
+ RETiRet;
+}
+
+
/* abort a connection. This is meant to be called immediately
* before the Destruct call. -- rgerhards, 2008-03-24
*/
@@ -519,6 +539,7 @@ CODESTARTobjQueryInterface(nsd_ptcp)
pIf->Construct = (rsRetVal(*)(nsd_t**)) nsd_ptcpConstruct;
pIf->Destruct = (rsRetVal(*)(nsd_t**)) nsd_ptcpDestruct;
pIf->Abort = Abort;
+ pIf->GetSock = GetSock;
pIf->LstnInit = LstnInit;
pIf->AcceptConnReq = AcceptConnReq;
pIf->Rcv = Rcv;
diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h
index 61ddd3d9..8da59089 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -209,6 +209,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth
RS_RET_INVALID_HNAME = -2075, /**< remote peer's hostname invalid or unobtainable */
RS_RET_INVALID_PORT = -2076, /**< invalid port value */
RS_RET_COULD_NOT_BIND = -2077, /**< could not bind socket, defunct */
+ RS_RET_GNUTLS_ERR = -2078, /**< (unexpected) error in GnuTLS call */
/* RainerScript error messages (range 1000.. 1999) */
RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */