diff options
-rw-r--r-- | plugins/imtcp/imtcp.c | 9 | ||||
-rw-r--r-- | runtime/nsd_gtls.c | 127 | ||||
-rw-r--r-- | runtime/nsd_gtls.h | 9 | ||||
-rw-r--r-- | runtime/nsd_ptcp.c | 3 | ||||
-rw-r--r-- | tcpsrv.c | 28 |
5 files changed, 157 insertions, 19 deletions
diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c index bb5f4fe5..a9924365 100644 --- a/plugins/imtcp/imtcp.c +++ b/plugins/imtcp/imtcp.c @@ -44,6 +44,7 @@ #include "module-template.h" #include "net.h" #include "netstrm.h" +#include "errmsg.h" #include "tcpsrv.h" MODULE_TYPE_INPUT @@ -54,6 +55,7 @@ DEFobjCurrIf(tcpsrv) DEFobjCurrIf(tcps_sess) DEFobjCurrIf(net) DEFobjCurrIf(netstrm) +DEFobjCurrIf(errmsg) /* Module static data */ static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */ @@ -134,6 +136,11 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa } finalize_it: + if(iRet != RS_RET_OK) { + errmsg.LogError(NO_ERRCODE, "error %d trying to add listener", iRet); + if(pOurTcpsrv != NULL) + tcpsrv.Destruct(&pOurTcpsrv); + } RETiRet; } @@ -179,6 +186,7 @@ CODESTARTmodExit objRelease(netstrm, LM_NETSTRMS_FILENAME); objRelease(tcps_sess, LM_TCPSRV_FILENAME); objRelease(tcpsrv, LM_TCPSRV_FILENAME); + objRelease(errmsg, CORE_COMPONENT); ENDmodExit @@ -207,6 +215,7 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(netstrm, LM_NETSTRMS_FILENAME)); CHKiRet(objUse(tcps_sess, LM_TCPSRV_FILENAME)); CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME)); + CHKiRet(objUse(errmsg, CORE_COMPONENT)); /* register config file handlers */ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverrun", 0, eCmdHdlrGetWord, diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c index a06d7cca..51d32b6c 100644 --- a/runtime/nsd_gtls.c +++ b/runtime/nsd_gtls.c @@ -35,6 +35,13 @@ #include "nsd_ptcp.h" #include "nsd_gtls.h" +/* things to move to some better place/functionality - TODO */ +#define DH_BITS 1024 +#define CAFILE "ca.pem" // TODO: allow to specify +#define KEYFILE "key.pem" +#define CERTFILE "cert.pem" +#define CRLFILE "crl.pem" + MODULE_TYPE_LIB /* static data */ @@ -43,19 +50,36 @@ DEFobjCurrIf(errmsg) DEFobjCurrIf(glbl) DEFobjCurrIf(nsd_ptcp) +static int bGlblSrvrInitDone = 0; /**< 0 - server global init not yet done, 1 - already done */ /* 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? */ \ + uchar *pErr = gtlsStrerror(gnuRet); \ + dbgprintf("unexpected GnuTLS error %d in %s:%d: %s\n", gnuRet, __FILE__, __LINE__, pErr); \ + free(pErr); \ ABORT_FINALIZE(RS_RET_GNUTLS_ERR); \ } -#define CAFILE "ca.pem" // TODO: allow to specify /* ------------------------------ GnuTLS specifics ------------------------------ */ static gnutls_certificate_credentials xcred; +static gnutls_dh_params dh_params; + +/* a thread-safe variant of gnutls_strerror - TODO: implement it! + * The caller must free the returned string. + * rgerhards, 2008-04-30 + */ +uchar *gtlsStrerror(int error) +{ + uchar *pErr; + + // TODO: guard by mutex! + pErr = (uchar*) strdup(gnutls_strerror(error)); + + return pErr; +} + /* globally initialize GnuTLS */ static rsRetVal @@ -76,6 +100,71 @@ finalize_it: RETiRet; } +static rsRetVal +gtlsInitSession(nsd_gtls_t *pThis) +{ + DEFiRet; + int gnuRet; + gnutls_session session; + + gnutls_init(&session, GNUTLS_SERVER); + pThis->bHaveSess = 1; + pThis->bIsInitiator = 0; + + /* avoid calling all the priority functions, since the defaults are adequate. */ + CHKgnutls(gnutls_set_default_priority(session)); + CHKgnutls(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred)); + + /* request client certificate if any. */ + gnutls_certificate_server_set_request( session, GNUTLS_CERT_REQUEST); + gnutls_dh_set_prime_bits(session, DH_BITS); + + pThis->sess = session; + +finalize_it: + RETiRet; +} + + + +static rsRetVal +generate_dh_params(void) +{ + int gnuRet; + DEFiRet; + /* Generate Diffie Hellman parameters - for use with DHE + * kx algorithms. These should be discarded and regenerated + * once a day, once a week or once a month. Depending on the + * security requirements. + */ + CHKgnutls(gnutls_dh_params_init( &dh_params)); + CHKgnutls(gnutls_dh_params_generate2( dh_params, DH_BITS)); +finalize_it: + RETiRet; +} + + +/* set up all global things that are needed for server operations + * rgerhards, 2008-04-30 + */ +static rsRetVal +gtlsGlblInitLstn(void) +{ + int gnuRet; + DEFiRet; + + if(bGlblSrvrInitDone == 0) { + //CHKgnutls(gnutls_certificate_set_x509_crl_file(xcred, CRLFILE, GNUTLS_X509_FMT_PEM)); + CHKgnutls(gnutls_certificate_set_x509_key_file(xcred, CERTFILE, KEYFILE, GNUTLS_X509_FMT_PEM)); + CHKiRet(generate_dh_params()); + gnutls_certificate_set_dh_params(xcred, dh_params); /* this is void */ + bGlblSrvrInitDone = 1; /* we are all set now */ + } + +finalize_it: + RETiRet; +} + /* globally de-initialize GnuTLS */ static rsRetVal @@ -100,9 +189,11 @@ gtlsEndSess(nsd_gtls_t *pThis) DEFiRet; if(pThis->bHaveSess) { - gnuRet = gnutls_bye(pThis->sess, GNUTLS_SHUT_RDWR); - while(gnuRet == GNUTLS_E_INTERRUPTED || gnuRet == GNUTLS_E_AGAIN) { + if(pThis->bIsInitiator) { 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); } @@ -116,6 +207,7 @@ gtlsEndSess(nsd_gtls_t *pThis) /* 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: remove! ENDobjConstruct(nsd_gtls) @@ -204,7 +296,9 @@ LstnInit(netstrms_t *pNS, void *pUsr, rsRetVal(*fAddLstn)(void*,netstrm_t*), uchar *pLstnPort, uchar *pLstnIP, int iSessMax) { DEFiRet; + CHKiRet(gtlsGlblInitLstn()); iRet = nsd_ptcp.LstnInit(pNS, pUsr, fAddLstn, pLstnPort, pLstnIP, iSessMax); +finalize_it: RETiRet; } @@ -247,6 +341,7 @@ 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; @@ -256,6 +351,23 @@ AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew) 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; + } + + /* if we reach this point, we are in TLS mode */ + CHKiRet(gtlsInitSession(pNew)); + gnutls_transport_set_ptr(pNew->sess, (gnutls_transport_ptr)((nsd_ptcp_t*) (pNew->pTcp))->sock); + + /* 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. + */ + CHKgnutls(gnutls_handshake(pNew->sess)); + pThis->iMode = 1; /* this session is now in TLS mode! */ + *ppNew = (nsd_t*) pNew; finalize_it: @@ -280,6 +392,8 @@ static rsRetVal Rcv(nsd_t *pNsd, uchar *pBuf, ssize_t *pLenBuf) { DEFiRet; + int gnuRet; + ssize_t lenRcvd; nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd; ISOBJ_TYPE_assert(pThis, nsd_gtls); @@ -289,6 +403,8 @@ Rcv(nsd_t *pNsd, uchar *pBuf, ssize_t *pLenBuf) } /* in TLS mode now */ + lenRcvd = gnutls_record_recv(pThis->sess, pBuf, *pLenBuf); + *pLenBuf = lenRcvd; finalize_it: RETiRet; @@ -358,6 +474,7 @@ Connect(nsd_t *pNsd, int family, uchar *port, uchar *host) /* we reach this point if in TLS mode */ CHKgnutls(gnutls_init(&pThis->sess, GNUTLS_CLIENT)); pThis->bHaveSess = 1; + pThis->bIsInitiator = 1; /* Use default priorities */ CHKgnutls(gnutls_set_default_priority(pThis->sess)); diff --git a/runtime/nsd_gtls.h b/runtime/nsd_gtls.h index c193f57c..492a2da1 100644 --- a/runtime/nsd_gtls.h +++ b/runtime/nsd_gtls.h @@ -26,6 +26,10 @@ #include "nsd.h" +typedef enum { + gtlsRtry_None = 0 /**< no call needs to be retried */ +} gtlsRtryCall_t; /**< IDs of calls that needs to be retried */ + typedef nsd_if_t nsd_gtls_if_t; /* we just *implement* this interface */ /* the nsd_gtls object */ @@ -33,8 +37,11 @@ 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 */ + gtlsRtryCall_t rtryCall;/**< what must we retry? */ + int bIsInitiator; /**< 0 if socket is the server end (listener), 1 if it is the initiator */ gnutls_session sess; - int bHaveSess; + int bHaveSess; /* as we don't know exactly which gnutls_session values are invalid, we use this one + to flag whether or not we are in a session (same as -1 for a socket meaning no sess) */ }; /* interface is defined in nsd.h, we just implement it! */ diff --git a/runtime/nsd_ptcp.c b/runtime/nsd_ptcp.c index e26347c3..c4fb1cf1 100644 --- a/runtime/nsd_ptcp.c +++ b/runtime/nsd_ptcp.c @@ -273,6 +273,8 @@ AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew) CHKiRet(FillRemHost(pNew, (struct sockaddr*) &addr)); +#warning "socket set to blocking for tls testing - re-enable non-blocking mode!" +#if 0 /* set the new socket to non-blocking IO -TODO:do we really need to do this here? Do we always want it? */ if((sockflags = fcntl(iNewSock, F_GETFL)) != -1) { sockflags |= O_NONBLOCK; @@ -285,6 +287,7 @@ AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew) dbgprintf("error %d setting fcntl(O_NONBLOCK) on tcp socket %d", errno, iNewSock); ABORT_FINALIZE(RS_RET_IO_ERROR); } +#endif pNew->sock = iNewSock; *ppNew = (nsd_t*) pNew; @@ -181,12 +181,15 @@ TCPSessGetNxtSess(tcpsrv_t *pThis, int iCurr) { register int i; + BEGINfunc ISOBJ_TYPE_assert(pThis, tcpsrv); + assert(pThis->pSessions != NULL); for(i = iCurr + 1 ; i < pThis->iSessMax ; ++i) { if(pThis->pSessions[i] != NULL) break; } + ENDfunc return((i < pThis->iSessMax) ? i : -1); } @@ -202,20 +205,20 @@ static void deinit_tcp_listener(tcpsrv_t *pThis) int i; ISOBJ_TYPE_assert(pThis, tcpsrv); - assert(pThis->pSessions != NULL); - /* close all TCP connections! */ - i = TCPSessGetNxtSess(pThis, -1); - while(i != -1) { - tcps_sess.Destruct(&pThis->pSessions[i]); - /* now get next... */ - i = TCPSessGetNxtSess(pThis, i); + if(pThis->pSessions != NULL) { + /* close all TCP connections! */ + i = TCPSessGetNxtSess(pThis, -1); + while(i != -1) { + tcps_sess.Destruct(&pThis->pSessions[i]); + /* now get next... */ + i = TCPSessGetNxtSess(pThis, i); + } + + /* we are done with the session table - so get rid of it... */ + free(pThis->pSessions); + pThis->pSessions = NULL; /* just to make sure... */ } - - /* we are done with the session table - so get rid of it... - */ - free(pThis->pSessions); - pThis->pSessions = NULL; /* just to make sure... */ if(pThis->TCPLstnPort != NULL) free(pThis->TCPLstnPort); @@ -413,7 +416,6 @@ Run(tcpsrv_t *pThis) /* Add the TCP listen sockets to the list of read descriptors. */ for(i = 0 ; i < pThis->iLstnMax ; ++i) { -RUNLOG_VAR("%d", i); CHKiRet(nssel.Add(pSel, pThis->ppLstn[i], NSDSEL_RD)); } |