diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/Makefile.am | 12 | ||||
-rw-r--r-- | runtime/glbl.c | 4 | ||||
-rw-r--r-- | runtime/netstrm.c | 7 | ||||
-rw-r--r-- | runtime/nsd.h | 1 | ||||
-rw-r--r-- | runtime/nsd_gtls.c | 383 | ||||
-rw-r--r-- | runtime/nsd_gtls.h | 49 | ||||
-rw-r--r-- | runtime/nsd_ptcp.c | 20 | ||||
-rw-r--r-- | runtime/nsd_ptcp.h | 3 | ||||
-rw-r--r-- | runtime/rsyslog.c | 13 | ||||
-rw-r--r-- | runtime/rsyslog.h | 12 |
10 files changed, 499 insertions, 5 deletions
diff --git a/runtime/Makefile.am b/runtime/Makefile.am index 7cb1b9bb..15efcbb8 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -124,3 +124,15 @@ lmnsdsel_ptcp_la_CPPFLAGS = $(pthreads_cflags) $(rsrt_cflags) lmnsdsel_ptcp_la_LDFLAGS = -module -avoid-version lmnsdsel_ptcp_la_LIBADD = endif # if ENABLE_INET + +# +# GnuTLS netstream driver +# +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) $(gnutls_cflags) +lmnsd_gtls_la_LDFLAGS = -module -avoid-version +lmnsd_gtls_la_LIBADD = $(gnutls_libs) +endif + diff --git a/runtime/glbl.c b/runtime/glbl.c index 1e51b0e0..787b6ab7 100644 --- a/runtime/glbl.c +++ b/runtime/glbl.c @@ -40,7 +40,9 @@ /* some defaults */ #ifndef DFLT_NETSTRM_DRVR -# define DFLT_NETSTRM_DRVR ((uchar*)"lmnsd_ptcp") +// TESTING ONLY# define DFLT_NETSTRM_DRVR ((uchar*)"lmnsd_ptcp") +#warning "define must be restored for non-testing!" +# define DFLT_NETSTRM_DRVR ((uchar*)"lmnsd_gtls") #endif /* static data */ diff --git a/runtime/netstrm.c b/runtime/netstrm.c index be754aae..5e073899 100644 --- a/runtime/netstrm.c +++ b/runtime/netstrm.c @@ -68,6 +68,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 ff12ecb0..d6fa9e0d 100644 --- a/runtime/nsd.h +++ b/runtime/nsd.h @@ -24,7 +24,6 @@ * 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. */ - #ifndef INCLUDED_NSD_H #define INCLUDED_NSD_H diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c new file mode 100644 index 00000000..648b843e --- /dev/null +++ b/runtime/nsd_gtls.c @@ -0,0 +1,383 @@ +/* nsd_gtls.c + * + * An implementation of the nsd interface for GnuTLS. + * + * Copyright 2007, 2008 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 "rsyslog.h" +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <ctype.h> +#include <netdb.h> +#include <fnmatch.h> +#include <fcntl.h> +#include <unistd.h> +#include <gnutls/gnutls.h> + +#include "rsyslog.h" +#include "syslogd-types.h" +#include "module-template.h" +#include "parse.h" +#include "srUtils.h" +#include "obj.h" +#include "errmsg.h" +#include "nsd_ptcp.h" +#include "nsd_gtls.h" + +MODULE_TYPE_LIB + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(errmsg) +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 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) { + gnuRet = gnutls_bye(pThis->sess, GNUTLS_SHUT_RDWR); + while(gnuRet == GNUTLS_E_INTERRUPTED || gnuRet == GNUTLS_E_AGAIN) { + gnuRet = gnutls_bye(pThis->sess, GNUTLS_SHUT_RDWR); + } + gnutls_deinit(pThis->sess); + } + 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) { + gtlsEndSess(pThis); + } + + if(pThis->pTcp != NULL) + nsd_ptcp.Destruct(&pThis->pTcp); +ENDobjDestruct(nsd_gtls) + + +/* 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; +} + + + +/* initialize the tcp socket for a listner + * pLstnPort must point to a port name or number. NULL is NOT permitted + * (hint: we need to be careful when we use this module together with librelp, + * there NULL indicates the default port + * default is used. + * gerhards, 2008-03-17 + */ +static rsRetVal +LstnInit(nsd_t *pNsd, uchar *pLstnPort) +{ + nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + assert(pLstnPort != NULL); + + if(pThis->iMode == 0) { + CHKiRet(nsd_ptcp.LstnInit(pThis->pTcp, pLstnPort)); + } + +finalize_it: + 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 (or -1 in case of error) 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. If *pLenBuf is -1, an error occured and + * errno holds the exact error cause. + * rgerhards, 2008-03-17 + */ +static rsRetVal +Rcv(nsd_t *pNsd, uchar *pBuf, ssize_t *pLenBuf) +{ + DEFiRet; + nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd; + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + if(pThis->iMode == 0) { + CHKiRet(nsd_ptcp.Rcv(pThis->pTcp, pBuf, pLenBuf)); + } + +finalize_it: + 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; + 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). 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); + + 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; +} + + +/* 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; +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(glbl, CORE_COMPONENT); + objRelease(errmsg, 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(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); + 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 + nsd_gtlsClassExit(); +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 */ +ENDmodInit +/* vi:set ai: + */ diff --git a/runtime/nsd_gtls.h b/runtime/nsd_gtls.h new file mode 100644 index 00000000..c193f57c --- /dev/null +++ b/runtime/nsd_gtls.h @@ -0,0 +1,49 @@ +/* An implementation of the nsd interface for GnuTLS. + * + * Copyright 2008 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. + */ + +#ifndef INCLUDED_NSD_GTLS_H +#define INCLUDED_NSD_GTLS_H + +#include "nsd.h" + +typedef nsd_if_t nsd_gtls_if_t; /* we just *implement* this interface */ + +/* the nsd_gtls object */ +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! */ +#define nsd_gtlsCURR_IF_VERSION nsdCURR_IF_VERSION + +/* prototypes */ +PROTOTYPEObj(nsd_gtls); + +/* the name of our library binary */ +#define LM_NSD_GTLS_FILENAME "lmnsd_gtls" + +#endif /* #ifndef INCLUDED_NSD_GTLS_H */ diff --git a/runtime/nsd_ptcp.c b/runtime/nsd_ptcp.c index c5cba12b..2b77787e 100644 --- a/runtime/nsd_ptcp.c +++ b/runtime/nsd_ptcp.c @@ -90,6 +90,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 */ diff --git a/runtime/nsd_ptcp.h b/runtime/nsd_ptcp.h index ac11d528..efd3ed05 100644 --- a/runtime/nsd_ptcp.h +++ b/runtime/nsd_ptcp.h @@ -29,13 +29,14 @@ typedef nsd_if_t nsd_ptcp_if_t; /* we just *implement* this interface */ /* the nsd_ptcp object */ struct nsd_ptcp_s { - BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ + BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ uchar *pRemHostIP; /**< IP address of remote peer (currently used in server mode, only) */ uchar *pRemHostName; /**< host name of remote peer (currently used in server mode, only) */ int sock; /**< the socket we use for regular, single-socket, operations */ }; /* interface is defined in nsd.h, we just implement it! */ +#define nsd_ptcpCURR_IF_VERSION nsdCURR_IF_VERSION /* prototypes */ PROTOTYPEObj(nsd_ptcp); diff --git a/runtime/rsyslog.c b/runtime/rsyslog.c index 95ac23ef..c05119d8 100644 --- a/runtime/rsyslog.c +++ b/runtime/rsyslog.c @@ -103,6 +103,19 @@ static rsRetVal dfltErrLogger(uchar *errMsg) } +/* set the error log function + * rgerhards, 2008-04-18 + */ +rsRetVal +rsrtSetErrLogger(rsRetVal (*errLogger)(uchar*)) +{ + DEFiRet; + assert(errLogger != NULL); + glblErrLogger = errLogger; + RETiRet; +} + + /* globally initialze the runtime system * NOTE: this is NOT thread safe and must not be called concurrently. If that * ever poses a problem, we may use proper mutex calls - not considered needed yet. diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 6cd9d94d..f59c38bf 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -69,7 +69,13 @@ typedef struct netstrm_s netstrm_t; typedef struct nssel_s nssel_t; typedef enum nsdsel_waitOp_e nsdsel_waitOp_t; typedef struct nsd_ptcp_s nsd_ptcp_t; +<<<<<<< HEAD:runtime/rsyslog.h +typedef struct nsd_gtls_s nsd_gtls_t; +typedef struct nsd_gsspi_s nsd_gsspi_t; +typedef struct nsd_nss_s nsd_nss_t; +======= typedef struct nsdsel_ptcp_s nsdsel_ptcp_t; +>>>>>>> a7040a9623e228043209da897dbf30b9ab02d771:runtime/rsyslog.h typedef obj_t nsd_t; typedef obj_t nsdsel_t; typedef struct msg msg_t; @@ -212,8 +218,9 @@ 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_MAX_SESS_REACHED = -2078, /**< max nbr of sessions reached, can not create more */ - RS_RET_MAX_LSTN_REACHED = -2079, /**< max nbr of listeners reached, can not create more */ + RS_RET_GNUTLS_ERR = -2078, /**< (unexpected) error in GnuTLS call */ + RS_RET_MAX_SESS_REACHED = -2079, /**< max nbr of sessions reached, can not create more */ + RS_RET_MAX_LSTN_REACHED = -2080, /**< max nbr of listeners reached, can not create more */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ @@ -319,6 +326,7 @@ extern rsRetVal (*glblErrLogger)(uchar*); rsRetVal rsrtInit(char **ppErrObj, obj_if_t *pObjIF); rsRetVal rsrtExit(void); int rsrtIsInit(void); +rsRetVal rsrtSetErrLogger(rsRetVal (*errLogger)(uchar*)); #endif /* multi-include protection */ /* vim:set ai: |