diff options
author | Rainer Gerhards <rgerhards@adiscon.com> | 2008-04-18 18:29:02 +0200 |
---|---|---|
committer | Rainer Gerhards <rgerhards@adiscon.com> | 2008-04-18 18:29:02 +0200 |
commit | 2069ab114e2aac9c243aff72042912cac7ef6126 (patch) | |
tree | 19bd5df7fb640b754ec6b91954d0153ed0f2eac5 /runtime | |
parent | 032b9c1f64691e868b14e6d271ebfc2d093b0c66 (diff) | |
download | rsyslog-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.am | 4 | ||||
-rw-r--r-- | runtime/netstrm.c | 35 | ||||
-rw-r--r-- | runtime/nsd.h | 5 | ||||
-rw-r--r-- | runtime/nsd_gtls.c | 117 | ||||
-rw-r--r-- | runtime/nsd_gtls.h | 2 | ||||
-rw-r--r-- | runtime/nsd_ptcp.c | 21 | ||||
-rw-r--r-- | runtime/rsyslog.h | 1 |
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) */ |