diff --git a/.lgtm.yml b/.lgtm.yml new file mode 100644 index 00000000..97091b11 --- /dev/null +++ b/.lgtm.yml @@ -0,0 +1,6 @@ +extraction: + cpp: + configure: + command: + - autoreconf -fvi + - ./configure --enable-tls-openssl diff --git a/.travis.yml b/.travis.yml index 0fc62faa..c649651f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,8 +35,18 @@ script: # - export CFLAGS="-g -fsanitize=address" - autoreconf -fvi - ./configure --enable-tls - - make - - make distcheck V=0 + - make -j2 + - make check + - cat tests/test-suite.log +# - make distcheck V=0 + # and now with openssl + - make clean + - ./configure --enable-tls-openssl + - make -j2 + - make check + - cat tests/test-suite.log +# - make distcheck V=0 + # - cat /home/travis/build/rsyslog/librelp/librelp-1.2.16.master/_build/tests/test-suite.log # - sudo make install # now we use the rsyslog testbench for testing. This also means we need to diff --git a/configure.ac b/configure.ac index 79106afb..f7b24f8a 100644 --- a/configure.ac +++ b/configure.ac @@ -66,6 +66,8 @@ fi AC_PROG_LIBTOOL +PKG_PROG_PKG_CONFIG + if test "$GCC" = "yes" then m4_ifdef([AX_IS_RELEASE], [ @@ -120,7 +122,7 @@ AC_CHECK_FUNCS([strerror_r strdup epoll_create epoll_create1]) # enable TLS (may not be possible on platforms with too-old GnuTLS) AC_ARG_ENABLE(tls, - [AS_HELP_STRING([--enable-tls],[Enable TLS support @<:@default=yes@:>@])], + [AS_HELP_STRING([--enable-tls],[Enable TLS support @<:@default=no@:>@])], [case "${enableval}" in yes) enable_tls="yes" ;; no) enable_tls="no" ;; @@ -128,6 +130,37 @@ AC_ARG_ENABLE(tls, esac], [enable_tls="yes"] ) +# enable Openssl TLS +AC_ARG_ENABLE(tls-openssl, + [AS_HELP_STRING([--enable-tls-openssl],[Enable OpenSSL TLS support @<:@default=yes@:>@])], + [case "${enableval}" in + yes) enable_tls_openssl="yes" ;; + no) enable_tls_openssl="no" ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-tls-openssl) ;; + esac], + [enable_tls_openssl="no"] +) + +if test "$enable_tls_openssl" = "yes"; then + PKG_CHECK_MODULES(OPENSSL, openssl) + AC_DEFINE([ENABLE_TLS_OPENSSL], [1], [Indicator that openssl is present]) + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $OPENSSL_CFLAGS" + save_LIBS="$LIBS" + LIBS="$LIBS $OPENSSL_LIBS" + + # Make sure GNUTLS is disabled + if test "$enable_tls" = "yes"; then + AC_MSG_WARN([Cannot compile GNUTLS and OpenSSL at the same time. Disabling gnutls. ]) + + # Disable GNUTLS + enable_tls="no" + have_gnutls_certificate_set_verify_function="no" + fi + +fi +AM_CONDITIONAL(ENABLE_TLS_OPENSSL, test x$enable_tls_openssl = xyes) + if test "$enable_tls" = "yes"; then PKG_CHECK_MODULES(GNUTLS, gnutls >= 2.0.0) AC_DEFINE(ENABLE_TLS, 1, [Defined if TLS support is enabled]) @@ -156,7 +189,6 @@ if test "$enable_tls" = "yes"; then LIBS="$save_LIBS" fi - # debug mode settings AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug],[Enable debug mode @<:@default=no@:>@])], @@ -176,20 +208,16 @@ fi # valgrind AC_ARG_ENABLE(valgrind, - [AS_HELP_STRING([--enable-valgrind],[Enable valgrind tests@<:@default=no@:>@])], + [AS_HELP_STRING([--enable-valgrind],[Enable valgrind tests@<:@default=yes@:>@])], [case "${enableval}" in yes) enable_valgrind="yes" ;; no) enable_valgrind="no" ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-valgrind) ;; esac], - [enable_valgrind="no"] + [enable_valgrind="yes"] ) if test "$enable_valgrind" = "yes"; then AC_CHECK_PROG(VALGRIND, [valgrind], [valgrind], [no]) - - if test "x$VALGRIND" = "xno"; then - AC_MSG_ERROR([valgrind is missing but forced with --enable-valgrind. Either install valgrind or remove the option!]) - fi fi AM_CONDITIONAL([HAVE_VALGRIND], test "$enable_valgrind" == "yes") @@ -206,8 +234,9 @@ AC_OUTPUT echo "*****************************************************" echo "librelp will be compiled with the following settings:" echo -echo "run valgrind in testbench: $enable_valgrind" -echo "Debug mode enabled: $enable_debug" -echo "TLS enabled: $enable_tls" -echo "TLS authentication supported: $have_gnutls_certificate_set_verify_function" +echo "run valgrind in testbench: $enable_valgrind" +echo "Debug mode enabled: $enable_debug" +echo "GNUTLS enabled: $enable_tls" +echo "GNUTLS authentication supported: $have_gnutls_certificate_set_verify_function" +echo "OPENSSL enabled: $enable_tls_openssl" diff --git a/devtools/run-configure.sh b/devtools/run-configure.sh index 84592a3c..e10b5e67 100755 --- a/devtools/run-configure.sh +++ b/devtools/run-configure.sh @@ -1,4 +1,4 @@ #!/bin/bash printf "running configure with\nCC:\t$CC\nCFLAGS:\t$CFLAGS\n" autoreconf -fvi -./configure --enable-tls +./configure $PROJ_CONFIGURE_OPTIONS diff --git a/devtools/travis-run-compile-tests.sh b/devtools/travis-run-compile-tests.sh index cd6722c7..d70d8164 100755 --- a/devtools/travis-run-compile-tests.sh +++ b/devtools/travis-run-compile-tests.sh @@ -10,12 +10,48 @@ DO_IN_CONTAINER="$PROJ_HOME/devtools/devcontainer.sh" printf "\n\n============ STEP: check code style ================\n\n\n" $DO_IN_CONTAINER devtools/check-codestyle.sh + +echo ==================== compile using gnutls ==================== +export PROJ_CONFIGURE_OPTIONS=--enable-tls + + printf "\n\n============ STEP: run static analyzer ================\n\n\n" $DO_IN_CONTAINER devtools/run-static-analyzer.sh # #################### newer compilers #################### +printf "\n\n============ STEP: gcc-7 compile test ================\n\n\n" +export CC=gcc-7 +export CFLAGS= +$DO_IN_CONTAINER devtools/run-configure.sh +$DO_IN_CONTAINER make check TESTS="" + +$DO_IN_CONTAINER make clean +printf "\n\n============ STEP: clang-5.0 compile test ================\n\n\n" +export CC=clang-5.0 +export CFLAGS= +$DO_IN_CONTAINER devtools/run-configure.sh +$DO_IN_CONTAINER make check TESTS="" + +exit 0 + +# #################### older style compile tests#################### +$DO_IN_CONTAINER make clean +printf "\n\n============ STEP: testing alpine build ================\n\n\n" +$PROJ_HOME/tests/travis/docker-alpine.sh + + + +echo ==================== compile using openssl ==================== +export PROJ_CONFIGURE_OPTIONS=--enable-tls-openssl + + +printf "\n\n============ STEP: run static analyzer ================\n\n\n" $DO_IN_CONTAINER make clean +$DO_IN_CONTAINER devtools/run-static-analyzer.sh + +# #################### newer compilers #################### + printf "\n\n============ STEP: gcc-7 compile test ================\n\n\n" export CC=gcc-7 export CFLAGS= diff --git a/src/Makefile.am b/src/Makefile.am index 895802e9..855877f5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -44,8 +44,8 @@ librelp_la_SOURCES = \ cserverclose.c \ dbllinklist.h \ cmdif.h -librelp_la_CPPFLAGS = $(AM_CLFAGS) $(PTHREADS_CFLAGS) $(GNUTLS_CFLAGS) $(WARN_CFLAGS) -librelp_la_LIBADD = $(rt_libs) $(GNUTLS_LIBS) +librelp_la_CPPFLAGS = $(AM_CLFAGS) $(PTHREADS_CFLAGS) $(GNUTLS_CFLAGS) $(OPENSSL_CFLAGS) $(WARN_CFLAGS) +librelp_la_LIBADD = $(rt_libs) $(GNUTLS_LIBS) $(OPENSSL_LIBS) # info on version-info: # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html librelp_la_LDFLAGS = -version-info 4:0:4 diff --git a/src/relp.c b/src/relp.c index 8207e910..afe599b1 100644 --- a/src/relp.c +++ b/src/relp.c @@ -56,12 +56,12 @@ relpEngineCallOnGenericErr(relpEngine_t *pThis, char *eobj, relpRetVal ecode, ch { va_list ap; char emsg[1024]; - + va_start(ap, fmt); vsnprintf(emsg, sizeof(emsg), fmt, ap); emsg[sizeof(emsg)/sizeof(char) - 1] = '\0'; /* just to be on the safe side... */ va_end(ap); - + pThis->dbgprint("librelp: generic error: ecode %d, eobj %s," "emsg '%s'\n", ecode, eobj, emsg); if(pThis->onGenericErr != NULL) { @@ -692,7 +692,7 @@ handleSessIO(relpEngine_t *pThis, epolld_t *epd) { relpEngSessLst_t *pSessEtry; relpTcp_t *pTcp; -# ifdef ENABLE_TLS +# if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) relpRetVal localRet; # endif @@ -704,7 +704,7 @@ handleSessIO(relpEngine_t *pThis, epolld_t *epd) } else if(relpTcpRtryOp(pTcp) == relpTCP_RETRY_recv) { doRecv(pThis, pSessEtry, epd->sock); } else { -# ifdef ENABLE_TLS +# if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) localRet = relpTcpRtryHandshake(pTcp); if(localRet != RELP_RET_OK) { pThis->dbgprint("relp session %d handshake iRet %d, tearing it down\n", @@ -715,7 +715,7 @@ handleSessIO(relpEngine_t *pThis, epolld_t *epd) pThis->dbgprint("librelp error: handshake retry requested in " "non-TLS mode"); -# endif /* #ifdef ENABLE_TLS */ +# endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL*/ } } else { if(doRecv(pThis, pSessEtry, epd->sock) == RELP_RET_OK) { @@ -748,7 +748,7 @@ engineEventLoopRun(relpEngine_t *pThis) */ for(pSessEtry = pThis->pSessLstRoot ; pSessEtry != NULL ; pSessEtry = pSessEtry->pNext) { sock = relpSessGetSock(pSessEtry->pSess); -# ifdef ENABLE_TLS +# if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) if(relpSessTcpRequiresRtry(pSessEtry->pSess)) { pThis->dbgprint("librelp: retry op requested for sock %d\n", sock); if(relpTcpGetRtryDirection(pSessEtry->pSess->pTcp) == 0) { @@ -757,7 +757,7 @@ engineEventLoopRun(relpEngine_t *pThis) epoll_set_events(pThis, pSessEtry, sock, EPOLLOUT); } } else -# endif /* #ifdef ENABLE_TLS */ +# endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL */ { /* now check if a send request is outstanding and, if so, add it */ if(relpSendqIsEmpty(pSessEtry->pSess->pSendq)) { @@ -834,7 +834,7 @@ engineEventLoopRun(relpEngine_t *pThis) /* Add all sessions for reception and sending (they all have just one socket) */ for(pSessEtry = pThis->pSessLstRoot ; pSessEtry != NULL ; pSessEtry = pSessEtry->pNext) { sock = relpSessGetSock(pSessEtry->pSess); -# ifdef ENABLE_TLS +# if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) if(relpSessTcpRequiresRtry(pSessEtry->pSess)) { pThis->dbgprint("librelp: retry op requested for sock %d\n", sock); if(relpTcpGetRtryDirection(pSessEtry->pSess->pTcp) == 0) { @@ -843,7 +843,7 @@ engineEventLoopRun(relpEngine_t *pThis) FD_SET(sock, &writefds); } } else -# endif /* #ifdef ENABLE_TLS */ +# endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL*/ { FD_SET(sock, &readfds); /* now check if a send request is outstanding and, if so, add it */ @@ -877,7 +877,7 @@ engineEventLoopRun(relpEngine_t *pThis) } continue; } - + /* and then start again with the servers (new connection request) */ for(pSrvEtry = pThis->pSrvLstRoot ; nfds && pSrvEtry != NULL ; pSrvEtry = pSrvEtry->pNext) { for(iSocks = 1 ; nfds && iSocks <= relpSrvGetNumLstnSocks(pSrvEtry->pSrv) ; ++iSocks) { @@ -905,7 +905,7 @@ engineEventLoopRun(relpEngine_t *pThis) doRecv(pThis, pSessEtry, sock); --nfds; /* indicate we have processed one */ } else { -# ifdef ENABLE_TLS +# if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) localRet = relpTcpRtryHandshake(pSessEtry->pSess->pTcp); if(localRet != RELP_RET_OK) { pThis->dbgprint("relp session %d handshake " @@ -917,7 +917,7 @@ engineEventLoopRun(relpEngine_t *pThis) pThis->dbgprint("librelp error: handshake retry " "requested in non-TLS mode"); -# endif /* #ifdef ENABLE_TLS */ +# endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL */ } } } else { diff --git a/src/relpsess.c b/src/relpsess.c index 6caf80ee..a495637c 100644 --- a/src/relpsess.c +++ b/src/relpsess.c @@ -171,6 +171,7 @@ relpSessDestruct(relpSess_t **ppThis) free(pThis->srvAddr); free(pThis->clientIP); free(pThis->pristring); + free(pThis->caCertFile); free(pThis->ownCertFile); free(pThis->privKeyFile); relpSessFreePermittedPeers(pThis); @@ -457,7 +458,7 @@ relpSessGetUnacked(relpSess_t *pThis, relpSendbuf_t **ppSendbuf, relpTxnr_t txnr ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); assert(ppSendbuf != NULL); - + for( pUnackedEtry = pThis->pUnackedLstRoot ; pUnackedEtry != NULL && pUnackedEtry->pSendbuf->txnr != txnr ; pUnackedEtry = pUnackedEtry->pNext) @@ -960,7 +961,7 @@ relpSessSetPermittedPeers(relpSess_t *pThis, relpPermittedPeers_t *pPeers) ENTER_RELPFUNC; int i; RELPOBJ_assert(pThis, Sess); - + relpSessFreePermittedPeers(pThis); if(pPeers->nmemb != 0) { if((pThis->permittedPeers.name = @@ -1028,7 +1029,7 @@ relpSessSetGnuTLSPriString(relpSess_t *pThis, char *pristr) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); - + free(pThis->pristring); if(pristr == NULL) { pThis->pristring = NULL; @@ -1045,7 +1046,7 @@ relpSessSetCACert(relpSess_t *pThis, char *cert) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); - + free(pThis->caCertFile); if(cert == NULL) { pThis->caCertFile = NULL; @@ -1062,7 +1063,7 @@ relpSessSetOwnCert(relpSess_t *pThis, char *cert) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); - + free(pThis->ownCertFile); if(cert == NULL) { pThis->ownCertFile = NULL; @@ -1079,7 +1080,7 @@ relpSessSetPrivKey(relpSess_t *pThis, char *cert) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Sess); - + free(pThis->privKeyFile); if(cert == NULL) { pThis->privKeyFile = NULL; diff --git a/src/relpsrv.c b/src/relpsrv.c index 5175b1a0..46399de0 100644 --- a/src/relpsrv.c +++ b/src/relpsrv.c @@ -338,22 +338,22 @@ relpRetVal relpSrvEnableTLS2(relpSrv_t __attribute__((unused)) *pThis) { ENTER_RELPFUNC; -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) pThis->bEnableTLS = 1; #else iRet = RELP_RET_ERR_NO_TLS; -#endif /* #ifdef ENABLE_TLS */ +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL */ LEAVE_RELPFUNC; } relpRetVal relpSrvEnableTLSZip2(relpSrv_t __attribute__((unused)) *pThis) { ENTER_RELPFUNC; -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) pThis->bEnableTLSZip = 1; #else iRet = RELP_RET_ERR_NO_TLS; -#endif /* #ifdef ENABLE_TLS */ +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL */ LEAVE_RELPFUNC; } void diff --git a/src/tcp.c b/src/tcp.c index f35eb84e..fe1d98f2 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -62,6 +62,24 @@ # endif static int called_gnutls_global_init = 0; #endif +#ifdef ENABLE_TLS_OPENSSL +# include +# include +# include +# include +/* OpenSSL API differences */ +# if OPENSSL_VERSION_NUMBER >= 0x10100000L +# define RSYSLOG_X509_NAME_oneline(X509CERT) X509_get_subject_name(X509CERT) +# define RSYSLOG_BIO_method_name(SSLBIO) BIO_method_name(SSLBIO) +# define RSYSLOG_BIO_number_read(SSLBIO) BIO_number_read(SSLBIO) +# define RSYSLOG_BIO_number_written(SSLBIO) BIO_number_written(SSLBIO) +# else +# define RSYSLOG_X509_NAME_oneline(X509CERT) (X509CERT != NULL ? X509CERT->cert_info->subject : NULL) +# define RSYSLOG_BIO_method_name(SSLBIO) SSLBIO->method->name +# define RSYSLOG_BIO_number_read(SSLBIO) SSLBIO->num +# define RSYSLOG_BIO_number_written(SSLBIO) SSLBIO->num +# endif +#endif /* #ifdef ENABLE_TLS_OPENSSL */ #ifndef SOL_TCP @@ -74,11 +92,18 @@ #endif -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) /* forward definitions */ #ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION static int relpTcpVerifyCertificateCallback(gnutls_session_t session); #endif /* #ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION */ +#if defined(HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION) || defined(ENABLE_TLS_OPENSSL) +static void relpTcpChkOnePeerName(relpTcp_t *const pThis, char *peername, int *pbFoundPositiveMatch); +static int relpTcpAddToCertNamesBuffer(relpTcp_t *const pThis, char *const buf, + const size_t buflen, int *p_currIdx, const char *const certName); + +#endif /* defined(HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION) || defined(ENABLE_TLS_OPENSSL) */ + static relpRetVal relpTcpPermittedPeerWildcardCompile(tcpPermittedPeerEntry_t *pEtry); /* helper to free permittedPeer structure */ @@ -89,8 +114,475 @@ relpTcpFreePermittedPeers(relpTcp_t *const pThis) for(i = 0 ; i < pThis->permittedPeers.nmemb ; ++i) free(pThis->permittedPeers.peer[i].name); pThis->permittedPeers.nmemb = 0; + if(pThis->permittedPeers.peer != NULL) + free(pThis->permittedPeers.peer); } -#endif /* #ifdef ENABLE_TLS */ + +/* helper to call onAuthErr if set */ +static inline void +callOnAuthErr(relpTcp_t *const pThis, char *authdata, char *emsg, relpRetVal ecode) +{ + pThis->pEngine->dbgprint("librelp: auth error: authdata:'%s', ecode %d, " + "emsg '%s'\n", authdata, ecode, emsg); + if(pThis->pEngine->onAuthErr != NULL) { + pThis->pEngine->onAuthErr(pThis->pUsr, authdata, emsg, ecode); + } +} + +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL */ + +#ifdef ENABLE_TLS_OPENSSL +/*--------------------------------------MT OpenSSL helpers ------------------------------------------*/ +static MUTEX_TYPE *mutex_buf = NULL; + +void locking_function(int mode, int n, + __attribute__((unused)) const char * file, __attribute__((unused)) int line) +{ + if (mode & CRYPTO_LOCK) + MUTEX_LOCK(mutex_buf[n]); + else + MUTEX_UNLOCK(mutex_buf[n]); +} + +unsigned long id_function(void) +{ + return ((unsigned long)THREAD_ID); +} + + +struct CRYPTO_dynlock_value * dyn_create_function( + __attribute__((unused)) const char *file, __attribute__((unused)) int line) +{ + struct CRYPTO_dynlock_value *value; + value = (struct CRYPTO_dynlock_value *)malloc(sizeof(struct CRYPTO_dynlock_value)); + if (!value) + return NULL; + + MUTEX_SETUP(value->mutex); + return value; +} + +void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l, + __attribute__((unused)) const char *file, __attribute__((unused)) int line) +{ + if (mode & CRYPTO_LOCK) + MUTEX_LOCK(l->mutex); + else + MUTEX_UNLOCK(l->mutex); +} + +void dyn_destroy_function(struct CRYPTO_dynlock_value *l, + __attribute__((unused)) const char *file, __attribute__((unused)) int line) +{ + MUTEX_CLEANUP(l->mutex); + free(l); +} + +/* set up support functions for openssl multi-threading. This must + * be done at library initialisation. If the function fails, + * processing can not continue normally. On failure, 0 is + * returned, on success 1. + */ +int opensslh_THREAD_setup(void) +{ + int i; + mutex_buf = (MUTEX_TYPE *)malloc(CRYPTO_num_locks( ) * sizeof(MUTEX_TYPE)); + if (mutex_buf == NULL) + return 0; + for (i = 0; i < CRYPTO_num_locks( ); i++) + MUTEX_SETUP(mutex_buf[i]); + + CRYPTO_set_id_callback(id_function); + CRYPTO_set_locking_callback(locking_function); + /* The following three CRYPTO_... functions are the OpenSSL functions + for registering the callbacks we implemented above */ + CRYPTO_set_dynlock_create_callback(dyn_create_function); + CRYPTO_set_dynlock_lock_callback(dyn_lock_function); + CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function); + +// DBGPRINTF("openssl: multithread setup finished\n"); + return 1; +} + +/* shut down openssl - do this only when you are totally done + * with openssl. + */ +int opensslh_THREAD_cleanup(void) +{ + int i; + if (!mutex_buf) + return 0; + + CRYPTO_set_id_callback(NULL); + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + + for (i = 0; i < CRYPTO_num_locks( ); i++) + MUTEX_CLEANUP(mutex_buf[i]); + + free(mutex_buf); + mutex_buf = NULL; + +// DBGPRINTF("openssl: multithread cleanup finished\n"); + return 1; +} +/*--------------------------------------MT OpenSSL helpers ------------------------------------------*/ +/* OpenSSL Helper functions */ +/* OpenSSL implementation of TLS funtions. + * alorbach, 2018-06-11 + */ + +long BIO_debug_callback(BIO *bio, int cmd, const char __attribute__((unused)) *argp, + int argi, long __attribute__((unused)) argl, long ret) +{ + long r = 1; + + relpTcp_t* pThis = (relpTcp_t*) (void *) BIO_get_callback_arg(bio); + + if (BIO_CB_RETURN & cmd) + r = ret; + + switch (cmd) { + case BIO_CB_FREE: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: Free - %s\n", + (void *)bio, RSYSLOG_BIO_method_name(bio)); + break; + /* Disabled due API changes for OpenSSL 1.1.0+ */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + case BIO_CB_READ: + if (bio->method->type & BIO_TYPE_DESCRIPTOR) + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: read(%d,%lu) - %s fd=%d\n", + (void *)bio, + RSYSLOG_BIO_number_read(bio), (unsigned long)argi, + RSYSLOG_BIO_method_name(bio), RSYSLOG_BIO_number_read(bio)); + else + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: read(%d,%lu) - %s\n", + (void *)bio, + RSYSLOG_BIO_number_read(bio), (unsigned long)argi, RSYSLOG_BIO_method_name(bio)); + break; + case BIO_CB_WRITE: + if (bio->method->type & BIO_TYPE_DESCRIPTOR) + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: write(%d,%lu) - %s fd=%d\n", + (void *)bio, + RSYSLOG_BIO_number_written(bio), (unsigned long)argi, + RSYSLOG_BIO_method_name(bio), RSYSLOG_BIO_number_written(bio)); + else + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: write(%d,%lu) - %s\n", + (void *)bio, + RSYSLOG_BIO_number_written(bio), (unsigned long)argi, RSYSLOG_BIO_method_name(bio)); + break; +#else + case BIO_CB_READ: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: read %s\n", + (void *)bio, + RSYSLOG_BIO_method_name(bio)); + break; + case BIO_CB_WRITE: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: write %s\n", + (void *)bio, + RSYSLOG_BIO_method_name(bio)); + break; +#endif + case BIO_CB_PUTS: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: puts() - %s\n", + (void *)bio, + RSYSLOG_BIO_method_name(bio)); + break; + case BIO_CB_GETS: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: gets(%lu) - %s\n", + (void *)bio, + (unsigned long)argi, + RSYSLOG_BIO_method_name(bio)); + break; + case BIO_CB_CTRL: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: ctrl(%lu) - %s\n", + (void *)bio, + (unsigned long)argi, + RSYSLOG_BIO_method_name(bio)); + break; + case BIO_CB_RETURN | BIO_CB_READ: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: read return %ld\n", + (void *)bio, + ret); + break; + case BIO_CB_RETURN | BIO_CB_WRITE: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: write return %ld\n", + (void *)bio, + ret); + break; + case BIO_CB_RETURN | BIO_CB_GETS: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: gets return %ld\n", + (void *)bio, + ret); + break; + case BIO_CB_RETURN | BIO_CB_PUTS: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: puts return %ld\n", + (void *)bio, + ret); + break; + case BIO_CB_RETURN | BIO_CB_CTRL: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: ctrl return %ld\n", + (void *)bio, + ret); + break; + default: + pThis->pEngine->dbgprint("openssl debugmsg: BIO[%p]: bio callback - unknown type (%d)\n", + (void *)bio, + cmd); + break; + } + + return (r); +} + +void relpTcpLastSSLErrorMsg(int ret, relpTcp_t *pThis, const char* pszCallSource) +{ + unsigned long un_error = 0; + char psz[256]; + long iMyRet = SSL_get_error(pThis->ssl, ret); + + /* Check which kind of error we have */ + pThis->pEngine->dbgprint("relpTcpLastSSLErrorMsg: openssl error '%s' with error code=%ld\n", + pszCallSource, iMyRet); + if(iMyRet == SSL_ERROR_SSL) { + /* Loop through errors */ + while ((un_error = ERR_get_error()) != 0){ + ERR_error_string_n(un_error, psz, 256); + pThis->pEngine->dbgprint("relpTcpLastSSLErrorMsg: Errorstack: %s\n", psz); + } + + } else if(iMyRet == SSL_ERROR_SYSCALL){ + iMyRet = ERR_get_error(); + if(ret == 0) { + iMyRet = SSL_get_error(pThis->ssl, iMyRet); + if(iMyRet == 0) { + *psz = '\0'; + } else { + ERR_error_string_n(iMyRet, psz, 256); + } + pThis->pEngine->dbgprint("relpTcpLastSSLErrorMsg: SysErr: %s\n", psz); + } else { + /* Loop through errors */ + while ((un_error = ERR_get_error()) != 0){ + ERR_error_string_n(un_error, psz, 256); + pThis->pEngine->dbgprint("relpTcpLastSSLErrorMsg: Errorstack: %s\n", psz); + } + } + } else { + pThis->pEngine->dbgprint("relpTcpLastSSLErrorMsg: Unknown SSL Error in '%s' (%d), SSL_get_error: %ld\n", + pszCallSource, ret, iMyRet); + } +} + +/* Convert a fingerprint to printable data. The conversion is carried out + * according IETF I-D syslog-transport-tls-12. The fingerprint string is + */ +static relpRetVal +GenFingerprintStr(unsigned char *pFingerprint, size_t sizeFingerprint, + char * fpBuf,const size_t bufLen) +{ + char buf[4]; + size_t iSrc, iDst; + size_t i; + ENTER_RELPFUNC; + + if (bufLen <= 5) { + ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); + } + + strncpy(fpBuf,"SHA1", bufLen); + iDst=4; + + for(iSrc = 0; iSrc < sizeFingerprint && iSrc < bufLen; ++iSrc, iDst += 3) { + sprintf(fpBuf+iDst, ":%2.2X", (unsigned char) pFingerprint[iSrc]); + } + +finalize_it: + LEAVE_RELPFUNC; +} + +/* Check the peer's ID in fingerprint auth mode. */ +static int +relpTcpChkPeerFingerprint(relpTcp_t *const pThis, X509* cert) +{ + int i; + unsigned int n; +// char fingerprint[126]; + unsigned char fingerprint[20 /*EVP_MAX_MD_SIZE**/]; + char fpPrintable[256]; + + size_t size; + int bFoundPositiveMatch; + const EVP_MD *fdig = EVP_sha1(); + ENTER_RELPFUNC; + + /* obtain the SHA1 fingerprint */ + size = sizeof(fingerprint); + if (!X509_digest(cert,fdig,fingerprint,&n)) { + pThis->pEngine->dbgprint("relpTcpChkPeerFingerprint: error X509cert is not valid!\n"); + ABORT_FINALIZE(RELP_RET_ERR_TLS); + } + + GenFingerprintStr(fingerprint, size, fpPrintable,sizeof(fpPrintable)); + pThis->pEngine->dbgprint("relpTcpChkPeerFingerprint: peer's certificate SHA1 fingerprint: %s\n", fpPrintable); + + /* now search through the permitted peers to see if we can find a permitted one */ + bFoundPositiveMatch = 0; + for(i = 0 ; i < pThis->permittedPeers.nmemb ; ++i) + { + pThis->pEngine->dbgprint("relpTcpChkPeerFingerprint: checking peer '%s','%s'\n", + fpPrintable, pThis->permittedPeers.peer[i].name); + if(!strcmp(fpPrintable, pThis->permittedPeers.peer[i].name)) { + pThis->pEngine->dbgprint("relpTcpChkPeerFingerprint: peer's certificate MATCH found: %s\n", + pThis->permittedPeers.peer[i].name); + bFoundPositiveMatch = 1; + break; + } + } + + if(!bFoundPositiveMatch) { + pThis->pEngine->dbgprint("relpTcpChkPeerFingerprint: invalid peer fingerprint, " + "not permitted to talk to it\n"); + callOnAuthErr(pThis, fpPrintable, "non-permited fingerprint", RELP_RET_AUTH_ERR_FP); + ABORT_FINALIZE(RELP_RET_AUTH_ERR_FP); + } +finalize_it: + LEAVE_RELPFUNC; +} + +/* Check the peer's ID in name auth mode. */ +static int +relpTcpChkPeerName(relpTcp_t *const pThis, X509* cert) +{ + unsigned char lnBuf[256]; + int bFoundPositiveMatch = 0; + char *x509name = NULL; + char allNames[32*1024]; /* for error-reporting */ + int iAllNames = 0; + + /* Helpers for altName */ + int iLoop; + int san_names_nb = -1; + GENERAL_NAMES *san_names = NULL; + GENERAL_NAME *gnDnsName; + int gtype; + ASN1_STRING *asDnsName; + const char *szAltName; + + ENTER_RELPFUNC; + + bFoundPositiveMatch = 0; + + /* Obtain Namex509 name from subject */ + x509name = X509_NAME_oneline(RSYSLOG_X509_NAME_oneline(cert), NULL, 0); + + pThis->pEngine->dbgprint("relpTcpChkPeerName: checking - peername '%s'\n", x509name); + snprintf((char*)lnBuf, sizeof(lnBuf), "name: %s; ", x509name); +// CHKRet(osslChkOnePeerName(pThis, cert, (unsigned char*)x509name, &bFoundPositiveMatch)); + relpTcpChkOnePeerName(pThis, x509name, &bFoundPositiveMatch); + + if(!bFoundPositiveMatch) { + /* Try to extract altname within the SAN extension from the certificate */ + san_names = X509_get_ext_d2i((X509 *) cert, NID_subject_alt_name, NULL, NULL); + if (san_names == NULL) { + pThis->pEngine->dbgprint("relpTcpChkPeerName: X509_get_ext_d2i returned no ALTNAMES\n"); + } else { + /* Loop through each name within the extension */ + san_names_nb = sk_GENERAL_NAME_num(san_names); + for (iLoop=0; iLoop= 0x10100000L + szAltName = (const char *)ASN1_STRING_get0_data(asDnsName); +# else + szAltName = (const char *)ASN1_STRING_data(asDnsName); +# endif + + pThis->pEngine->dbgprint("relpTcpChkPeerName: checking - altName: '%s'\n", szAltName); + + /* Add to Names Buffer first */ + if (relpTcpAddToCertNamesBuffer(pThis, allNames, sizeof(allNames), + &iAllNames, (char *)szAltName) != 0) + ABORT_FINALIZE(RELP_RET_AUTH_CERT_INVL); + + /* Perform PeerName check on AltName */ + relpTcpChkOnePeerName(pThis, (char *)szAltName, &bFoundPositiveMatch); + + /* Stop if match was found */ + if (bFoundPositiveMatch) + break; + } + } + } + + if(!bFoundPositiveMatch) { + pThis->pEngine->dbgprint("relpTcpChkPeerName: invalid peername, not permitted to talk to it\n"); + callOnAuthErr(pThis, allNames, "no permited name found", RELP_RET_AUTH_ERR_NAME); + ABORT_FINALIZE(RELP_RET_AUTH_ERR_NAME); + } else { + pThis->pEngine->dbgprint("relpTcpChkPeerName: permitted to talk!\n"); + } + +finalize_it: + /* Free mem */ + if (x509name != NULL){ + OPENSSL_free(x509name); + } + if (san_names != NULL){ + GENERAL_NAMES_free(san_names); + } + + LEAVE_RELPFUNC; +} + +int verify_callback(int status, X509_STORE_CTX *store) +{ + char szdbgdata1[256]; + char szdbgdata2[256]; + + /* Get current SSL object in order to obtain relpTcp_t obj */ + SSL* ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()); + relpTcp_t *pThis = (relpTcp_t *) SSL_get_ex_data(ssl, 0); + assert(pThis != NULL); + + X509 *cert = X509_STORE_CTX_get_current_cert(store); + int depth = X509_STORE_CTX_get_error_depth(store); + int err = X509_STORE_CTX_get_error(store); + + /* Already failed ? */ + if(status == 0) { + pThis->pEngine->dbgprint("verify_callback: certificate validation failed!\n"); + X509_NAME_oneline(X509_get_issuer_name(cert), szdbgdata1, sizeof(szdbgdata1)); + X509_NAME_oneline(RSYSLOG_X509_NAME_oneline(cert), szdbgdata2, sizeof(szdbgdata2)); + + /* Log Warning only on EXPIRED */ + if (err == X509_V_OK || err == X509_V_ERR_CERT_HAS_EXPIRED) { + pThis->pEngine->dbgprint( + "verify_callback: Certificate warning at depth: %d \n\t" + "issuer = %s\n\t" + "subject = %s\n\t" + "err %d:%s\n", + depth, szdbgdata1, szdbgdata2, err, X509_verify_cert_error_string(err)); + /* Set Status to OK*/ + status = 1; + } else { + pThis->pEngine->dbgprint( + "verify_callback: Certificate error at depth: %d \n\t" + "issuer = %s\n\t" + "subject = %s\n\t" + "err %d:%s\n", + depth, szdbgdata1, szdbgdata2, err, X509_verify_cert_error_string(err)); + } + } else { + pThis->pEngine->dbgprint("verify_callback: certificate validation success!\n"); + } + return status; +} +#endif /* #ifdef ENABLE_TLS_OPENSSL */ /** Construct a RELP tcp instance * This is the first thing that a caller must do before calling any @@ -129,6 +621,7 @@ relpTcpConstruct(relpTcp_t **ppThis, relpEngine_t *const pEngine, pThis->privKeyFile = NULL; pThis->pUsr = NULL; pThis->permittedPeers.nmemb = 0; + pThis->permittedPeers.peer = NULL; *ppThis = pThis; @@ -144,9 +637,12 @@ relpTcpDestruct(relpTcp_t **ppThis) { relpTcp_t *pThis; int i; -#ifdef ENABLE_TLS - int gnuRet; -#endif /* #ifdef ENABLE_TLS */ +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) + int sslRet; +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL */ +#if defined(ENABLE_TLS_OPENSSL) + int sslErr; +#endif /* #ifdef ENABLE_TLS_OPENSSL */ ENTER_RELPFUNC; assert(ppThis != NULL); @@ -167,21 +663,63 @@ relpTcpDestruct(relpTcp_t **ppThis) #ifdef ENABLE_TLS if(pThis->bTLSActive) { - gnuRet = gnutls_bye(pThis->session, GNUTLS_SHUT_RDWR); - while(gnuRet == GNUTLS_E_INTERRUPTED || gnuRet == GNUTLS_E_AGAIN) { - gnuRet = gnutls_bye(pThis->session, GNUTLS_SHUT_RDWR); + sslRet = gnutls_bye(pThis->session, GNUTLS_SHUT_RDWR); + while(sslRet == GNUTLS_E_INTERRUPTED || sslRet == GNUTLS_E_AGAIN) { + sslRet = gnutls_bye(pThis->session, GNUTLS_SHUT_RDWR); } gnutls_deinit(pThis->session); } - relpTcpFreePermittedPeers(pThis); #endif /* #ifdef ENABLE_TLS */ - free(pThis->pRemHostIP); - free(pThis->pRemHostName); - free(pThis->pristring); - free(pThis->caCertFile); - free(pThis->ownCertFile); - free(pThis->privKeyFile); +#ifdef ENABLE_TLS_OPENSSL + if( pThis->bTLSActive && + pThis->ssl != NULL) { + + /* Try shutdown */ + pThis->pEngine->dbgprint("relpTcpDestruct: try shutdown #1 for [%p]\n", (void *) pThis->ssl); + sslRet = SSL_shutdown(pThis->ssl); + if (sslRet <= 0) { + sslErr = SSL_get_error(pThis->ssl, sslRet); + pThis->pEngine->dbgprint("relpTcpDestruct: shutdown failed with err = %d, " + "forcing ssl shutdown!\n", sslErr); + + /* ignore those SSL Errors on shutdown */ + if( sslErr != SSL_ERROR_SYSCALL && + sslErr != SSL_ERROR_ZERO_RETURN && + sslErr != SSL_ERROR_WANT_READ && + sslErr != SSL_ERROR_WANT_WRITE) { + /* Output Warning only */ + relpTcpLastSSLErrorMsg(sslRet, pThis, "relpTcpDestruct"); + } + + pThis->pEngine->dbgprint("relpTcpDestruct: session closed (un)successfully \n"); + } else { + pThis->pEngine->dbgprint("relpTcpDestruct: session closed successfully \n"); + } + + /* Session closed */ + pThis->bTLSActive = 0; + SSL_free(pThis->ssl); + pThis->ssl = NULL; + } +#endif /* #ifdef ENABLE_TLS_OPENSSL */ + +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) + relpTcpFreePermittedPeers(pThis); +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL */ + + if (pThis->pRemHostIP != NULL) + free(pThis->pRemHostIP); + if (pThis->pRemHostName != NULL) + free(pThis->pRemHostName); + if (pThis->pristring != NULL) + free(pThis->pristring); + if (pThis->caCertFile != NULL) + free(pThis->caCertFile); + if (pThis->ownCertFile != NULL) + free(pThis->ownCertFile); + if (pThis->privKeyFile != NULL) + free(pThis->privKeyFile); /* done with de-init work, now free tcp object itself */ free(pThis); @@ -243,17 +781,6 @@ chkGnutlsCode(relpTcp_t *const pThis, char *emsg, relpRetVal ecode, const int gn } return r; } - -/* helper to call onAuthErr if set */ -static inline void -callOnAuthErr(relpTcp_t *const pThis, char *authdata, char *emsg, relpRetVal ecode) -{ - pThis->pEngine->dbgprint("librelp: auth error: authdata:'%s', ecode %d, " - "emsg '%s'\n", authdata, ecode, emsg); - if(pThis->pEngine->onAuthErr != NULL) { - pThis->pEngine->onAuthErr(pThis->pUsr, authdata, emsg, ecode); - } -} #endif /* #ifdef ENABLE_TLS */ /* abort a tcp connection. This is much like relpTcpDestruct(), but tries @@ -322,7 +849,7 @@ relpTcpSetRemHost(relpTcp_t *const pThis, struct sockaddr *pAddr) unsigned char szHname[NI_MAXHOST] = ""; struct addrinfo hints, *res; size_t len; - + ENTER_RELPFUNC; RELPOBJ_assert(pThis, Tcp); pEngine = pThis->pEngine; @@ -389,7 +916,7 @@ relpTcpSetPermittedPeers(relpTcp_t __attribute__((unused)) *pThis, relpPermittedPeers_t __attribute__((unused)) *pPeers) { ENTER_RELPFUNC; -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) int i; relpTcpFreePermittedPeers(pThis); if(pPeers->nmemb != 0) { @@ -409,7 +936,7 @@ relpTcpSetPermittedPeers(relpTcp_t __attribute__((unused)) *pThis, pThis->permittedPeers.nmemb = pPeers->nmemb; #else ABORT_FINALIZE(RELP_RET_ERR_NO_TLS); -#endif /* #ifdef ENABLE_TLS */ +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL */ finalize_it: LEAVE_RELPFUNC; } @@ -446,7 +973,7 @@ relpTcpSetGnuTLSPriString(relpTcp_t *const pThis, char *pristr) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Tcp); - + free(pThis->pristring); if(pristr == NULL) { pThis->pristring = NULL; @@ -463,7 +990,7 @@ relpTcpSetCACert(relpTcp_t *const pThis, char *cert) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Tcp); - + free(pThis->caCertFile); if(cert == NULL) { pThis->caCertFile = NULL; @@ -480,7 +1007,7 @@ relpTcpSetOwnCert(relpTcp_t *const pThis, char *cert) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Tcp); - + free(pThis->ownCertFile); if(cert == NULL) { pThis->ownCertFile = NULL; @@ -497,12 +1024,12 @@ relpTcpSetPrivKey(relpTcp_t *const pThis, char *cert) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Tcp); - + free(pThis->privKeyFile); if(cert == NULL) { pThis->privKeyFile = NULL; } else { -# ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION +#if defined(HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION) || defined(ENABLE_TLS_OPENSSL) if((pThis->privKeyFile = strdup(cert)) == NULL) ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); # else @@ -520,11 +1047,11 @@ relpTcpEnableTLS(relpTcp_t __attribute__((unused)) *pThis) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Tcp); -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) pThis->bEnableTLS = 1; #else iRet = RELP_RET_ERR_NO_TLS; -#endif /* #ifdef ENABLE_TLS */ +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL */ LEAVE_RELPFUNC; } @@ -533,7 +1060,8 @@ relpTcpEnableTLSZip(relpTcp_t __attribute__((unused)) *pThis) { ENTER_RELPFUNC; RELPOBJ_assert(pThis, Tcp); -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) +/* || defined(ENABLE_TLS_OPENSSL)*/ pThis->bEnableTLSZip = 1; #else iRet = RELP_RET_ERR_NO_TLS; @@ -550,7 +1078,7 @@ relpTcpSetDHBits(relpTcp_t *const pThis, const int bits) LEAVE_RELPFUNC; } -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) /* set TLS priority string, common code both for client and server */ static relpRetVal relpTcpTLSSetPrio(relpTcp_t *const pThis) @@ -561,29 +1089,54 @@ relpTcpTLSSetPrio(relpTcp_t *const pThis) ENTER_RELPFUNC; /* Compute priority string (in simple cases where the user does not care...) */ if(pThis->pristring == NULL) { +#if defined(ENABLE_TLS) if(pThis->bEnableTLSZip) { strncpy(pristringBuf, "NORMAL:+ANON-DH:+COMP-ALL", sizeof(pristringBuf)); } else { strncpy(pristringBuf, "NORMAL:+ANON-DH:+COMP-NULL", sizeof(pristringBuf)); } +#endif /* defined(ENABLE_TLS)*/ +#if defined(ENABLE_TLS_OPENSSL) + if (pThis->authmode == eRelpAuthMode_None) + strncpy(pristringBuf, "ALL:+COMPLEMENTOFDEFAULT:+ADH:+ECDH:+aNULL" /* :+aNULL:+eNULL */, + sizeof(pristringBuf)); + else + strncpy(pristringBuf, "DEFAULT", sizeof(pristringBuf)); + +#endif /* defined(ENABLE_TLS_OPENSSL)*/ pristringBuf[sizeof(pristringBuf)-1] = '\0'; pristring = pristringBuf; } else { pristring = pThis->pristring; } +#if defined(ENABLE_TLS) r = gnutls_priority_set_direct(pThis->session, pristring, NULL); if(r == GNUTLS_E_INVALID_REQUEST) { ABORT_FINALIZE(RELP_RET_INVLD_TLS_PRIO); } else if(r != GNUTLS_E_SUCCESS) { ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP); } +#endif /* defined(ENABLE_TLS)*/ +#if defined(ENABLE_TLS_OPENSSL) + if ( SSL_set_cipher_list(pThis->ssl, pristring) == 0 ){ + pThis->pEngine->dbgprint("relpTcpTLSSetPrio: Error setting ciphers '%s'\n", pristring); + ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP); + } +#endif /* defined(ENABLE_TLS_OPENSSL)*/ + finalize_it: + pThis->pEngine->dbgprint("relpTcpTLSSetPrio: Setting ciphers '%s' iRet=%d\n", pristring, iRet); + +#if defined(ENABLE_TLS) if(iRet != RELP_RET_OK) chkGnutlsCode(pThis, "Failed to set GnuTLS priority", iRet, r); +#endif /* defined(ENABLE_TLS)*/ LEAVE_RELPFUNC; } +#endif /* defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL)*/ +#ifdef ENABLE_TLS #ifndef _AIX #pragma GCC diagnostic push /* per https://lists.gnupg.org/pipermail/gnutls-help/2004-August/000154.html This is expected */ @@ -600,13 +1153,12 @@ relpTcpAcceptConnReqInitTLS(relpTcp_t *const pThis, relpSrv_t *const pSrv) ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP); } - gnutls_session_set_ptr(pThis->session, pThis); - if(pSrv->pTcp->pristring != NULL) pThis->pristring = strdup(pSrv->pTcp->pristring); pThis->authmode = pSrv->pTcp->authmode; pThis->pUsr = pSrv->pUsr; CHKRet(relpTcpTLSSetPrio(pThis)); + gnutls_session_set_ptr(pThis->session, pThis); if(isAnonAuth(pSrv->pTcp)) { r = gnutls_credentials_set(pThis->session, GNUTLS_CRD_ANON, pSrv->pTcp->anoncredSrv); @@ -645,59 +1197,510 @@ relpTcpAcceptConnReqInitTLS(relpTcp_t *const pThis, relpSrv_t *const pSrv) #endif #endif /* #ifdef ENABLE_TLS */ -/* Enable KEEPALIVE handling on the socket. */ -static void -EnableKeepAlive(const relpTcp_t *__restrict__ const pThis, - const relpSrv_t *__restrict__ const pSrv, - const int sock) +#ifdef ENABLE_TLS_OPENSSL +#ifndef _AIX +#pragma GCC diagnostic push +/* per https://lists.gnupg.org/pipermail/gnutls-help/2004-August/000154.html This is expected */ +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" +#endif + +/* global init OpenSSL + */ +static relpRetVal +relpTcpInitTLS(relpTcp_t *const pThis) { - int ret; - int optval; - socklen_t optlen; + int iErr = 0; + ENTER_RELPFUNC; + pThis->pEngine->dbgprint("relpTcpInitTLS: Init OpenSSL library\n"); - optval = 1; - optlen = sizeof(optval); - ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); - if(ret < 0) { - pThis->pEngine->dbgprint("librelp: EnableKeepAlive socket call " - "returns error %d\n", ret); - goto done; + /* Setup OpenSSL library */ + if((opensslh_THREAD_setup() == 0) || !SSL_library_init()) { + pThis->pEngine->dbgprint("relpTcpInitTLS: Error OpenSSL initialization failed\n"); + ABORT_FINALIZE(RELP_RET_IO_ERR); } -# if defined(TCP_KEEPCNT) - if(pSrv->iKeepAliveProbes > 0) { - optval = pSrv->iKeepAliveProbes; - optlen = sizeof(optval); - ret = setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &optval, optlen); - } else { - ret = 0; - } -# else - ret = -1; -# endif - if(ret < 0) { - callOnErr(pThis, "librelp cannot set keepalive probes - ignored", - RELP_RET_WRN_NO_KEEPALIVE); - } + /* Load readable error strings */ + SSL_load_error_strings(); + ERR_load_BIO_strings(); + ERR_load_crypto_strings(); -# if defined(TCP_KEEPCNT) - if(pSrv->iKeepAliveTime > 0) { - optval = pSrv->iKeepAliveTime; - optlen = sizeof(optval); - ret = setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &optval, optlen); - } else { - ret = 0; - } -# else - ret = -1; -# endif - if(ret < 0) { - callOnErr(pThis, "librelp cannot set keepalive time - ignored", - RELP_RET_WRN_NO_KEEPALIVE); - } + /* Create main CTX Object */ + ctx = SSL_CTX_new(SSLv23_method()); -# if defined(TCP_KEEPCNT) - if(pSrv->iKeepAliveIntvl > 0) { + /* Set CTX Options */ + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); /* Disable insecure SSLv2 Protocol */ + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); /* Disable insecure SSLv3 Protocol */ + SSL_CTX_sess_set_cache_size(ctx,1024); /* TODO: make configurable? */ + + #if OPENSSL_VERSION_NUMBER >= 0x10002000L + /* Enable Support for automatic EC temporary key parameter selection. */ + SSL_CTX_set_ecdh_auto(ctx, 1); + #else + pThis->pEngine->dbgprint("relpTcpInitTLS: openssl to old, cannot use SSL_CTX_set_ecdh_auto." + "Using SSL_CTX_set_tmp_ecdh with NID_X9_62_prime256v1/() instead.\n"); + SSL_CTX_set_tmp_ecdh(ctx, EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + + #endif + + SSL_CTX_set_timeout(ctx, 30); /* Default Session Timeout, TODO: Make configureable */ + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + + /* After CA Cert Init, set default VERIFY Options for OpenSSL CTX - and CALLBACK */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, verify_callback); + + /* Init CA Certificate first */ + if( pThis->caCertFile != NULL ) { + if (SSL_CTX_load_verify_locations(ctx, pThis->caCertFile, NULL) != 1) { + pThis->pEngine->dbgprint("relpTcpInitTLS: Error, CA certificate could not be accessed." + " Is the file at the right path? And do we have the permissions?\n"); + ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP); + } else + pThis->pEngine->dbgprint("relpTcpInitTLS: Successfully initialized CA certificate\n"); + } else + pThis->pEngine->dbgprint("relpTcpInitTLS: CA certificate MISSING\n"); + + called_openssl_global_init = 1; +finalize_it: + LEAVE_RELPFUNC; +} + +static void +relpTcpExitTLS(void) +{ + SSL_CTX_free(ctx); + ENGINE_cleanup(); + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +} + +/* Perform additional certificate name checks + */ +relpRetVal +relpTcpChkPeerAuth(relpTcp_t *const pThis) +{ + X509* certpeer; + int r; + ENTER_RELPFUNC; + + /* Get peer certificate from SSL */ + certpeer = SSL_get_peer_certificate(pThis->ssl); + if ( certpeer == NULL ) { + if(pThis->authmode == eRelpAuthMode_None ) { + pThis->pEngine->dbgprint("relpTcpChkPeerAuth: peer certificate for [%p] invalid, " + "but allowed in anon auth mode\n", (void *) pThis); + } else + ABORT_FINALIZE(RELP_RET_AUTH_NO_CERT); + } else { + /* verify client cert at first **/ + r = SSL_get_verify_result(pThis->ssl); + if (r != X509_V_OK) { + if (r == X509_V_ERR_CERT_HAS_EXPIRED) { + callOnAuthErr(pThis, (char*) X509_verify_cert_error_string(r), + "certificate validation failed, certificate expired!", + RELP_RET_AUTH_CERT_INVL); + ABORT_FINALIZE(RELP_RET_AUTH_CERT_INVL); + } else { + callOnAuthErr(pThis, (char*) X509_verify_cert_error_string(r), + "certificate validation failed", + RELP_RET_AUTH_CERT_INVL); + ABORT_FINALIZE(RELP_RET_AUTH_CERT_INVL); + } + } + + /* Now check for auth modes */ + if(pThis->authmode == eRelpAuthMode_Name ) { + CHKRet(relpTcpChkPeerName(pThis, certpeer)); + pThis->pEngine->dbgprint("relpTcpChkPeerAuth: name mode - success\n"); + } else if(pThis->authmode == eRelpAuthMode_Fingerprint) { + CHKRet(relpTcpChkPeerFingerprint(pThis, certpeer)); + pThis->pEngine->dbgprint("relpTcpChkPeerAuth: fingerprint mode - success\n"); + } else { + pThis->pEngine->dbgprint("relpTcpChkPeerAuth: anon mode - success\n"); + } + } + +finalize_it: + LEAVE_RELPFUNC; +} + + +/* Perform all necessary checks after Handshake + */ +relpRetVal +relpTcpPostHandshakeCheck(relpTcp_t *const pThis) +{ + ENTER_RELPFUNC; + char szDbg[255]; + const SSL_CIPHER* sslCipher; + + /* Some extra output for debugging openssl */ + if (SSL_get_shared_ciphers(pThis->ssl,szDbg, sizeof szDbg) != NULL) + pThis->pEngine->dbgprint("relpTcpPostHandshakeCheck: Debug Shared ciphers = %s\n", szDbg); + sslCipher = (const SSL_CIPHER*) SSL_get_current_cipher(pThis->ssl); + if (sslCipher != NULL) + pThis->pEngine->dbgprint("relpTcpPostHandshakeCheck: Debug Version: %s Name: %s\n", + SSL_CIPHER_get_version(sslCipher), SSL_CIPHER_get_name(sslCipher)); + + FINALIZE; + +finalize_it: + LEAVE_RELPFUNC; +} + +/* Perform all necessary checks after Handshake + */ +relpRetVal +relpTcpSslInitCerts(relpTcp_t *const pThis, char *ownCertFile, char *privKeyFile) +{ + ENTER_RELPFUNC; + + if( ownCertFile!= NULL ) { + if (SSL_use_certificate_file(pThis->ssl, ownCertFile, SSL_FILETYPE_PEM) != 1) { + pThis->pEngine->dbgprint("relpTcpSslInitCerts: error, Certificate file could not be " + "accessed. Is the file at the right path? And do we have the " + "permissions?"); + ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP); + } else + pThis->pEngine->dbgprint("relpTcpSslInitCerts: Successfully initialized certificate file\n"); + } else + pThis->pEngine->dbgprint("relpTcpSslInitCerts: certificate file MISSING\n"); + + if( privKeyFile!= NULL ) { + if (SSL_use_PrivateKey_file(pThis->ssl, privKeyFile, SSL_FILETYPE_PEM) != 1) { + pThis->pEngine->dbgprint("relpTcpSslInitCerts: Error, Key file could not be accessed. " + "Is the file at the right path? And do we have the permissions?"); + ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP); + } else + pThis->pEngine->dbgprint("relpTcpSslInitCerts: Successfully initialized key file\n"); + } else + pThis->pEngine->dbgprint("relpTcpSslInitCerts: key file MISSING\n"); + FINALIZE; + +finalize_it: + LEAVE_RELPFUNC; +} + +/* return direction in which retry must be done. We maps openssl retry state to + * "0 if trying to read data, 1 if trying to write data." + */ +int +relpTcpGetRtryDirection(relpTcp_t *const pThis) +{ + if (pThis->rtryOp == relpTCP_RETRY_send) + return 1; /* try to write data */ + else + return 0; /* try to read data = default */ +} + +/* Perform all necessary actions for Handshake + */ +relpRetVal +relpTcpRtryHandshake(relpTcp_t *const pThis) +{ + int res, resErr; + ENTER_RELPFUNC; + pThis->pEngine->dbgprint("relpTcpRtryHandshake: Starting TLS Handshake for ssl[%p]\n", (void *)pThis->ssl); + + if (pThis->sslState == osslServer) { + /* Handle Server SSL Object */ + if((res = SSL_accept(pThis->ssl)) <= 0) { + /* Obtain SSL Error code */ + resErr = SSL_get_error(pThis->ssl, res); + if( resErr == SSL_ERROR_WANT_READ || + resErr == SSL_ERROR_WANT_WRITE) { + pThis->rtryOp = relpTCP_RETRY_handshake; +// pThis->rtryOsslErr = resErr; /* Store SSL ErrorCode into*/ + pThis->pEngine->dbgprint("relpTcpRtryHandshake: Server handshake does not " + "complete immediately - setting to retry (this is OK and normal)\n"); + FINALIZE; + } else if(resErr == SSL_ERROR_SYSCALL) { + pThis->pEngine->dbgprint("relpTcpRtryHandshake: Server handshake failed with " + "SSL_ERROR_SYSCALL - Aborting handshake.\n"); + relpTcpLastSSLErrorMsg(res, pThis, "relpTcpRtryHandshake Server"); + ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP); + } else { + relpTcpLastSSLErrorMsg(res, pThis, "relpTcpRtryHandshake Server"); + ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP); + } + } else + pThis->pEngine->dbgprint("relpTcpRtryHandshake: Server handshake finished for ssl[%p]\n", + (void *)pThis->ssl); + } else { + /* Handle Client SSL Object */ + if((res = SSL_do_handshake(pThis->ssl)) <= 0) { + /* Obtain SSL Error code */ + resErr = SSL_get_error(pThis->ssl, res); + if( resErr == SSL_ERROR_WANT_READ || + resErr == SSL_ERROR_WANT_WRITE) { + pThis->rtryOp = relpTCP_RETRY_handshake; +// pThis->rtryOsslErr = resErr; /* Store SSL ErrorCode into*/ + pThis->pEngine->dbgprint("relpTcpRtryHandshake: Client handshake does not complete " + "immediately - setting to retry (this is OK and normal)\n"); + FINALIZE; + } else if(resErr == SSL_ERROR_SYSCALL) { + pThis->pEngine->dbgprint("relpTcpRtryHandshake: Client handshake failed with " + "SSL_ERROR_SYSCALL - Aborting handshake.\n"); + relpTcpLastSSLErrorMsg(res, pThis, "relpTcpRtryHandshake Client"); + ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP /*RS_RET_RETRY*/); + } else { + relpTcpLastSSLErrorMsg(res, pThis, "relpTcpRtryHandshake Client"); + ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP); + } + } else + pThis->pEngine->dbgprint("relpTcpRtryHandshake: Client handshake finished for ssl[%p]\n", + (void *)pThis->ssl); + } + + /* Handshake finished */ + pThis->rtryOp = relpTCP_RETRY_none; + + /* Do post handshake stuff */ + CHKRet(relpTcpPostHandshakeCheck(pThis)); + + /* Now check authorization */ + CHKRet(relpTcpChkPeerAuth(pThis)); + +finalize_it: + LEAVE_RELPFUNC; +} + +static relpRetVal +relpTcpAcceptConnReqInitTLS(relpTcp_t *const pThis, relpSrv_t *const pSrv) +{ + int r; + BIO *client = NULL; + ENTER_RELPFUNC; + + pThis->pEngine->dbgprint("relpTcpAcceptConnReqInitTLS: : Accepting connection for [%p] ... \n", (void *)pThis); + + if(!(pThis->ssl = SSL_new(ctx))) { + relpTcpLastSSLErrorMsg(0, pThis, "relpTcpAcceptConnReqInitTLS"); + } + pThis->authmode = pSrv->pTcp->authmode; + pThis->pUsr = pSrv->pUsr; + + if(!isAnonAuth(pThis->pSrv->pTcp)) { + CHKRet(relpTcpSslInitCerts(pThis, pThis->pSrv->ownCertFile, pThis->pSrv->privKey)); + } else + pThis->authmode = eRelpAuthMode_None; + + CHKRet(relpTcpTLSSetPrio(pThis)); + SSL_set_ex_data(pThis->ssl, 0, pThis); + + if (pThis->authmode != eRelpAuthMode_None) { + pThis->pEngine->dbgprint("relpTcpAcceptConnReqInitTLS: enable certificate checking\n"); + /* Enable certificate valid checking */ + SSL_set_verify(pThis->ssl, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); + SSL_set_verify_depth(pThis->ssl, 4); + } else { + SSL_set_verify(pThis->ssl, SSL_VERIFY_NONE, verify_callback); + } + + /* Create BIO from ptcp socket! */ + client = BIO_new_socket(pThis->sock, BIO_CLOSE /*BIO_NOCLOSE*/); + pThis->pEngine->dbgprint("relpTcpAcceptConnReqInitTLS: Init client BIO[%p] done\n", (void *)client); + + /* Set debug Callback for client BIO as well! */ + BIO_set_callback(client, BIO_debug_callback); + BIO_set_callback_arg(client, (char *)pThis); + +/* TODO: still needed? Set to NON blocking ! */ +BIO_set_nbio( client, 1 ); + + SSL_set_bio(pThis->ssl, client, client); + SSL_set_accept_state(pThis->ssl); /* sets ssl to work in server mode. */ + + pThis->bTLSActive = 1; + pThis->sslState = osslServer; /*set Server state */ + + /* We now do the handshake */ + CHKRet(relpTcpRtryHandshake(pThis)); + +finalize_it: + /* Accept appears to be done here */ + pThis->pEngine->dbgprint("relpTcpAcceptConnReqInitTLS: END iRet = %d, pThis=[%p], pThis->rtryCall=%d\n", + iRet, (void *) pThis, pThis->rtryOp); + if(iRet != RELP_RET_OK) { + if (client != NULL) { + BIO_free(client); + } + if (pThis->ssl != NULL) { + SSL_free(pThis->ssl); + pThis->ssl = NULL; + } + } + + LEAVE_RELPFUNC; +} + + +/* Wrapper function for openssl lib client init */ +static relpRetVal +relpTcpConnectTLSInit(relpTcp_t *const pThis) +{ + int r; + int sockflags; + ENTER_RELPFUNC; + RELPOBJ_assert(pThis, Tcp); + pThis->pEngine->dbgprint("relpTcpConnectTLSInit openssl\n"); + + /* SSL Objects */ + BIO *conn = NULL; + + /* We expect a non blocking socket to establish a tls session */ + if((sockflags = fcntl(pThis->sock, F_GETFL)) != -1) { + sockflags &= ~O_NONBLOCK; + sockflags = fcntl(pThis->sock, F_SETFL, sockflags); + } + + if(sockflags == -1) { + pThis->pEngine->dbgprint("error %d unsetting fcntl(O_NONBLOCK) on relp socket", errno); + ABORT_FINALIZE(RELP_RET_IO_ERR); + } + + if(!called_openssl_global_init) { + /* Init OpenSSL lib */ + CHKRet(relpTcpInitTLS(pThis)); + } + + /* Create BIO from ptcp socket! */ + conn = BIO_new_socket(pThis->sock, BIO_CLOSE /*BIO_NOCLOSE*/); + pThis->pEngine->dbgprint("relpTcpConnectTLSInit: Init conn BIO[%p] done\n", (void *)conn); + + /* Set debug Callback for client BIO as well! */ + BIO_set_callback(conn, BIO_debug_callback); + BIO_set_callback_arg(conn, (char *)pThis); + +/* TODO: still needed? Set to NON blocking ! */ +BIO_set_nbio( conn, 1 ); + + /*if we reach this point we are in tls mode */ + pThis->pEngine->dbgprint("relpTcpConnectTLSInit: TLS Mode\n"); + if(!(pThis->ssl = SSL_new(ctx))) { + relpTcpLastSSLErrorMsg(0, pThis, "relpTcpConnectTLSInit"); +/* errmsg.LogError(0, RS_RET_NO_ERRCODE, "Error creating an SSL context"); */ + ABORT_FINALIZE(RELP_RET_IO_ERR); + } + + /* Load certificate data into SSL object */ + if(!isAnonAuth(pThis)) { + pThis->pEngine->dbgprint("relpTcpConnectTLSInit: Init Client Certs \n"); + CHKRet(relpTcpSslInitCerts(pThis, pThis->ownCertFile, pThis->privKeyFile)); + } else + pThis->authmode = eRelpAuthMode_None; + + CHKRet(relpTcpTLSSetPrio(pThis)); + SSL_set_ex_data(pThis->ssl, 0, (void*)pThis); + + SSL_set_bio(pThis->ssl, conn, conn); + SSL_set_connect_state(pThis->ssl); /*sets ssl to work in client mode.*/ + pThis->sslState = osslClient; /*set client state */ + + /* Perform the TLS handshake */ + pThis->pEngine->dbgprint("relpTcpConnectTLSInit: try handshake for [%p]\n", (void *)pThis); + CHKRet(relpTcpRtryHandshake(pThis)); + + /* set the socket to non-blocking IO (we do this on the recv() for non-TLS */ + if((sockflags = fcntl(pThis->sock, F_GETFL)) != -1) { + sockflags |= O_NONBLOCK; + /* SETFL could fail too, so get it caught by the subsequent + * error check. */ + if(fcntl(pThis->sock, F_SETFL, sockflags) == -1) { + callOnErr(pThis, "error setting socket to non-blocking", + RELP_RET_ERR_TLS_SETUP); + ABORT_FINALIZE(RELP_RET_ERR_TLS_SETUP); + } + } + +finalize_it: + /* Connect appears to be done here */ + pThis->pEngine->dbgprint("relpTcpConnectTLSInit: END iRet = %d, pThis=[%p], pNsd->rtryOp=%d\n", + iRet, (void *) pThis, pThis->rtryOp); + if(iRet != RELP_RET_OK) { + if (conn != NULL) { + BIO_free(conn); + } + } + LEAVE_RELPFUNC; +} + +/* Wrapper function for openssl lib server init */ +static relpRetVal +relpTcpLstnInitTLS(relpTcp_t *const pThis) +{ + int r; + ENTER_RELPFUNC; + RELPOBJ_assert(pThis, Tcp); + + if(!called_openssl_global_init) { + CHKRet(relpTcpInitTLS(pThis)); + } + pThis->pEngine->dbgprint("relpTcpLstnInitTLS openssl init done \n"); + +finalize_it: + LEAVE_RELPFUNC; +} + + +#ifndef _AIX +#pragma GCC diagnostic pop +#endif +#endif /* #ifdef ENABLE_TLS_OPENSSL */ + +/* Enable KEEPALIVE handling on the socket. */ +static void +EnableKeepAlive(const relpTcp_t *__restrict__ const pThis, + const relpSrv_t *__restrict__ const pSrv, + const int sock) +{ + int ret; + int optval; + socklen_t optlen; + + optval = 1; + optlen = sizeof(optval); + ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); + if(ret < 0) { + pThis->pEngine->dbgprint("librelp: EnableKeepAlive socket call " + "returns error %d\n", ret); + goto done; + } + +# if defined(TCP_KEEPCNT) + if(pSrv->iKeepAliveProbes > 0) { + optval = pSrv->iKeepAliveProbes; + optlen = sizeof(optval); + ret = setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &optval, optlen); + } else { + ret = 0; + } +# else + ret = -1; +# endif + if(ret < 0) { + callOnErr(pThis, "librelp cannot set keepalive probes - ignored", + RELP_RET_WRN_NO_KEEPALIVE); + } + +# if defined(TCP_KEEPCNT) + if(pSrv->iKeepAliveTime > 0) { + optval = pSrv->iKeepAliveTime; + optlen = sizeof(optval); + ret = setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &optval, optlen); + } else { + ret = 0; + } +# else + ret = -1; +# endif + if(ret < 0) { + callOnErr(pThis, "librelp cannot set keepalive time - ignored", + RELP_RET_WRN_NO_KEEPALIVE); + } + +# if defined(TCP_KEEPCNT) + if(pSrv->iKeepAliveIntvl > 0) { optval = pSrv->iKeepAliveIntvl; optlen = sizeof(optval); ret = setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &optval, optlen); @@ -753,350 +1756,59 @@ relpTcpAcceptConnReq(relpTcp_t **ppThis, const int sock, relpSrv_t *const pSrv) if(iNewSock < 0) { pSrv->pEngine->dbgprint("error during accept, sleeping 20ms: %s\n", strerror(errnosave)); - doSleep(0, 20000); - pSrv->pEngine->dbgprint("END SLEEP\n"); - ABORT_FINALIZE(RELP_RET_ACCEPT_ERR); - } - - /* construct our object so that we can use it... */ - CHKRet(relpTcpConstruct(&pThis, pEngine, RELP_SRV_CONN, pSrv)); - - if(pSrv->bKeepAlive) - EnableKeepAlive(pThis, pSrv, iNewSock); - - /* TODO: obtain hostname, normalize (callback?), save it */ - CHKRet(relpTcpSetRemHost(pThis, (struct sockaddr*) &addr)); - pThis->pEngine->dbgprint("remote host is '%s', ip '%s'\n", pThis->pRemHostName, pThis->pRemHostIP); - - /* set the new socket to non-blocking IO */ - if((sockflags = fcntl(iNewSock, F_GETFL)) != -1) { - sockflags |= O_NONBLOCK; - /* SETFL could fail too, so get it caught by the subsequent - * error check. - */ - sockflags = fcntl(iNewSock, F_SETFL, sockflags); - } - if(sockflags == -1) { - pThis->pEngine->dbgprint("error %d setting fcntl(O_NONBLOCK) on relp socket %d", errno, iNewSock); - ABORT_FINALIZE(RELP_RET_IO_ERR); - } - - pThis->sock = iNewSock; -#ifdef ENABLE_TLS - if(pSrv->pTcp->bEnableTLS) { - pThis->bEnableTLS = 1; - pThis->pSrv = pSrv; - CHKRet(relpTcpSetPermittedPeers(pThis, &(pSrv->permittedPeers))); - CHKRet(relpTcpAcceptConnReqInitTLS(pThis, pSrv)); - } -#endif /* #ifdef ENABLE_TLS */ - - *ppThis = pThis; - -finalize_it: - if(iRet != RELP_RET_OK) { - if(pThis != NULL) - relpTcpDestruct(&pThis); - /* the close may be redundant, but that doesn't hurt... */ - if(iNewSock >= 0) - close(iNewSock); - } - - LEAVE_RELPFUNC; -} - -#ifdef ENABLE_TLS -#ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION - -/* Glue to use the right type of function depending of the version */ -#if GNUTLS_VERSION_NUMBER < 0x030202 - //gnutls_mac_algorithm_t and gnutls_digest_algorithm_t are aligned - //So we can use the result to get the fingerprint without trouble - typedef gnutls_mac_algorithm_t digest_id_t; - digest_id_t digest_get_id(const char * name){return gnutls_mac_get_id (name);} - const char* digest_get_name(digest_id_t id){return gnutls_mac_get_name (id);} -# define UNK_DIGEST GNUTLS_MAC_UNKNOWN - -#else - typedef gnutls_digest_algorithm_t digest_id_t; - digest_id_t digest_get_id(const char * name){return gnutls_digest_get_id (name);} - const char* digest_get_name(digest_id_t id){return gnutls_digest_get_name (id);} -# define UNK_DIGEST GNUTLS_DIG_UNKNOWN -#endif - -/* Convert a fingerprint to printable data. The function must be provided a - * sufficiently large buffer. 1024 bytes shall always do (for sha512 and future). - * To the size of the hexadecimal fingerprint, we must add the name of the fingerprint, and the separators. - * Warn : if the size of the buffer isn't sufficient, then the data is truncated. - */ - -static void -GenFingerprintStr(const char *pFingerprint,const int sizeFingerprint, - char * fpBuf,const size_t bufLen, - const digest_id_t type,relpEngine_t * pEngine) -{ - int iSrc, iDst; - - size_t sizeTotal=0,sizeDigest=0; - //statically assigned char*, no free. - const char* digestType=digest_get_name(type); - if (NULL==digestType) - { - if (pEngine!=NULL) - pEngine->dbgprint("warn : the signature type %d is unknown\n",type); - digestType="0000"; - } - sizeDigest=strlen(digestType); - //digestname + 3 char by byte (:xx) + last '\0' - sizeTotal=sizeDigest+(sizeFingerprint*3)+1; - if (sizeTotal =1){ - if (pEngine!=NULL) - pEngine->dbgprint("warn: buffer overflow for %s signature\n",digestType); - fpBuf[0]='\0';//an empty string - }else{ - if (pEngine!=NULL) - pEngine->dbgprint("warn: buffer empty, unable to print the signature\n"); - } -} - -#define MAX_DIGEST_PEER 10 -static size_t ListDigestPeer(digest_id_t* listSigPeer, - tcpPermittedPeers_t* listPeers,relpEngine_t* pEngine) -{ - int i; - int maxDigest=0; - //No signature name of more than 32 bytes. - //Most take 4. Some are slightly longer (SHA3_256, ...) - char digest[32]; - if (NULL==listPeers || listPeers->nmemb<=0 ) - { - if (pEngine!=NULL) pEngine->dbgprint("warn: no PermittedPeer listed\n"); - return 0; - } - for (i=0; inmemb;++i) - { - if ((listPeers->peer[i].name)!=NULL) - { - char*eow=strchr(listPeers->peer[i].name,':');//The first separator - if (eow!=NULL) - { - int sizeDigest=(int)(eow-(listPeers->peer[i].name)); - //31= sizeof(digest)-1; - sizeDigest = sizeDigest > 31 ? 31: sizeDigest; - strncpy(digest,listPeers->peer[i].name,sizeDigest); - digest[sizeDigest]='\0'; - digest_id_t actualDigest=digest_get_id(digest); - if (actualDigest!=UNK_DIGEST) - { - int alreadyExist=0; - int j; - for (j=0;jdbgprint("DDDD: adding digest %s\n", - digest); - listSigPeer[maxDigest++]=actualDigest; - } - } - } - } - } - return maxDigest; -} -/* Check the peer's ID in fingerprint auth mode. */ -static int -relpTcpChkPeerFingerprint(relpTcp_t *const pThis, gnutls_x509_crt_t cert) -{ - int r = 0; - int i; - char fingerprint[126]; - char fpPrintable[256]; - digest_id_t listSigPeer[MAX_DIGEST_PEER]; - size_t maxDigest,k; - size_t size; - int8_t found; - - - /* List which digest we have in our permittedPeer list. We verify only the first MAX_DIGEST_PEER.*/ - maxDigest=ListDigestPeer(listSigPeer,&(pThis->permittedPeers),pThis->pEngine); - - /* obtain the SHA1 fingerprint */ - found = 0; - for(k=0; kpEngine); - pThis->pEngine->dbgprint("peer's certificate %s fingerprint: %s\n", - digest_get_name(digest), fpPrintable); - - /* now search through the permitted peers to see if we can find a permitted one */ - pThis->pEngine->dbgprint("n peers %d\n", pThis->permittedPeers.nmemb); - for(i = 0 ; i < pThis->permittedPeers.nmemb ; ++i) { - pThis->pEngine->dbgprint("checking peer '%s','%s'\n", - fpPrintable, pThis->permittedPeers.peer[i].name); - if(!strcmp(fpPrintable, pThis->permittedPeers.peer[i].name)) { - found = 1; - break; - } - } - } - if(!found) { - r = GNUTLS_E_CERTIFICATE_ERROR; goto done; - } -done: - if(r != 0) { - callOnAuthErr(pThis, fpPrintable, "non-permited fingerprint", RELP_RET_AUTH_ERR_FP); - } - return r; -} -#endif /* #ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION */ - -/* add a wildcard entry to this permitted peer. Entries are always - * added at the tail of the list. pszStr and lenStr identify the wildcard - * entry to be added. Note that the string is NOT \0 terminated, so - * we must rely on lenStr for when it is finished. - * rgerhards, 2008-05-27 - */ -static relpRetVal -AddPermittedPeerWildcard(tcpPermittedPeerEntry_t *pEtry, char* pszStr, const int lenStr) -{ - tcpPermittedPeerWildcardComp_t *pNew = NULL; - int iSrc; - int iDst; - ENTER_RELPFUNC; - - if((pNew = calloc(1, sizeof(tcpPermittedPeerWildcardComp_t))) == NULL) { - ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); - } - - if(lenStr == 0) { - pNew->wildcardType = tcpPEER_WILDCARD_EMPTY_COMPONENT; - FINALIZE; - } else { - /* alloc memory for the domain component. We may waste a byte or - * two, but that's ok. - */ - if((pNew->pszDomainPart = malloc(lenStr +1 )) == NULL) { - ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); - } - } - - if(pszStr[0] == '*') { - pNew->wildcardType = tcpPEER_WILDCARD_AT_START; - iSrc = 1; /* skip '*' */ - } else { - iSrc = 0; - } - - for(iDst = 0 ; iSrc < lenStr && pszStr[iSrc] != '*' ; ++iSrc, ++iDst) { - pNew->pszDomainPart[iDst] = pszStr[iSrc]; - } - - if(iSrc < lenStr) { - if(iSrc + 1 == lenStr && pszStr[iSrc] == '*') { - if(pNew->wildcardType == tcpPEER_WILDCARD_AT_START) { - ABORT_FINALIZE(RELP_RET_INVLD_WILDCARD); - } else { - pNew->wildcardType = tcpPEER_WILDCARD_AT_END; - } - } else { - /* we have an invalid wildcard, something follows the asterisk! */ - ABORT_FINALIZE(RELP_RET_INVLD_WILDCARD); - } - } - - if(lenStr == 1 && pNew->wildcardType == tcpPEER_WILDCARD_AT_START) { - pNew->wildcardType = tcpPEER_WILDCARD_MATCH_ALL; - } - - /* if we reach this point, we had a valid wildcard. We now need to - * properly terminate the domain component string. - */ - pNew->pszDomainPart[iDst] = '\0'; - pNew->lenDomainPart = strlen((char*)pNew->pszDomainPart); - -finalize_it: - if(iRet != RELP_RET_OK) { - if(pNew != NULL) { - if(pNew->pszDomainPart != NULL) - free(pNew->pszDomainPart); - free(pNew); - } - } else { - /* add the element to linked list */ - if(pEtry->wildcardRoot == NULL) { - pEtry->wildcardRoot = pNew; - pEtry->wildcardLast = pNew; - } else { - pEtry->wildcardLast->pNext = pNew; - } - pEtry->wildcardLast = pNew; + doSleep(0, 20000); + pSrv->pEngine->dbgprint("END SLEEP\n"); + ABORT_FINALIZE(RELP_RET_ACCEPT_ERR); } - LEAVE_RELPFUNC; -} -/* Compile a wildcard - must not yet be compiled */ -static relpRetVal -relpTcpPermittedPeerWildcardCompile(tcpPermittedPeerEntry_t *pEtry) -{ - char *pC; - char *pStart; - ENTER_RELPFUNC; + /* construct our object so that we can use it... */ + CHKRet(relpTcpConstruct(&pThis, pEngine, RELP_SRV_CONN, pSrv)); - /* first check if we have a wildcard */ - for(pC = pEtry->name ; *pC != '\0' && *pC != '*' ; ++pC) - /*EMPTY, just skip*/; + if(pSrv->bKeepAlive) + EnableKeepAlive(pThis, pSrv, iNewSock); - if(*pC == '\0') { /* no wildcard found, we are done */ - FINALIZE; + /* TODO: obtain hostname, normalize (callback?), save it */ + CHKRet(relpTcpSetRemHost(pThis, (struct sockaddr*) &addr)); + pThis->pEngine->dbgprint("remote host is '%s', ip '%s'\n", pThis->pRemHostName, pThis->pRemHostIP); + + /* set the new socket to non-blocking IO */ + if((sockflags = fcntl(iNewSock, F_GETFL)) != -1) { + sockflags |= O_NONBLOCK; + /* SETFL could fail too, so get it caught by the subsequent + * error check. + */ + sockflags = fcntl(iNewSock, F_SETFL, sockflags); + } + if(sockflags == -1) { + pThis->pEngine->dbgprint("error %d setting fcntl(O_NONBLOCK) on relp socket %d", errno, iNewSock); + ABORT_FINALIZE(RELP_RET_IO_ERR); } - /* if we reach this point, the string contains wildcards. So let's - * compile the structure. To do so, we must parse from dot to dot - * and create a wildcard entry for each domain component we find. - * We must also flag problems if we have an asterisk in the middle - * of the text (it is supported at the start or end only). - */ - pC = pEtry->name; - while(*pC) { - pStart = pC; - /* find end of domain component */ - for( ; *pC != '\0' && *pC != '.' ; ++pC) - /*EMPTY, just skip*/; - CHKRet(AddPermittedPeerWildcard(pEtry, pStart, pC - pStart)); - /* now check if we have an empty component at end of string */ - if(*pC == '.' && *(pC + 1) == '\0') { - /* pStart is a dummy, it is not used if length is 0 */ - CHKRet(AddPermittedPeerWildcard(pEtry, pStart, 0)); - } - if(*pC != '\0') - ++pC; + pThis->sock = iNewSock; +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) + if(pSrv->pTcp->bEnableTLS) { + pThis->bEnableTLS = 1; + pThis->pSrv = pSrv; + CHKRet(relpTcpSetPermittedPeers(pThis, &(pSrv->permittedPeers))); + CHKRet(relpTcpAcceptConnReqInitTLS(pThis, pSrv)); } +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL */ + + *ppThis = pThis; finalize_it: + if(iRet != RELP_RET_OK) { + if(pThis != NULL) + relpTcpDestruct(&pThis); + /* the close may be redundant, but that doesn't hurt... */ + if(iNewSock >= 0) + close(iNewSock); + } + LEAVE_RELPFUNC; } -#ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION +#if defined(HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION) || defined(ENABLE_TLS_OPENSSL) /* check a peer against a wildcard entry. This is a more lengthy * operation. */ @@ -1176,7 +1888,7 @@ relpTcpChkOnePeerWildcard(tcpPermittedPeerWildcardComp_t *pRoot, if(*pC == '.') ++pC; } - + /* we need to adjust for a border case, that is if the last component is * empty. That happens frequently if the domain root (e.g. "example.com.") * is properly given. @@ -1194,32 +1906,229 @@ relpTcpChkOnePeerWildcard(tcpPermittedPeerWildcardComp_t *pRoot, done: return; } -/* 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). - */ -static void -relpTcpChkOnePeerName(relpTcp_t *const pThis, char *peername, int *pbFoundPositiveMatch) -{ - int i; +/* 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). + */ +static void +relpTcpChkOnePeerName(relpTcp_t *const pThis, char *peername, int *pbFoundPositiveMatch) +{ + int i; + + for(i = 0 ; i < pThis->permittedPeers.nmemb ; ++i) { + pThis->pEngine->dbgprint("relpTcpChkOnePeerName: compare peername '%s' with permitted name '%s'\n", + peername, pThis->permittedPeers.peer[i].name); + + if(pThis->permittedPeers.peer[i].wildcardRoot == NULL) { + /* simple string, only, no wildcards */ + if(!strcmp(peername, pThis->permittedPeers.peer[i].name)) { + *pbFoundPositiveMatch = 1; + break; + } + } else { + relpTcpChkOnePeerWildcard(pThis->permittedPeers.peer[i].wildcardRoot, + peername, pbFoundPositiveMatch, pThis->pEngine); + if (*pbFoundPositiveMatch) + break; + } + } +} + +/* helper to consistently add names to error message buffer */ +static int +relpTcpAddToCertNamesBuffer(relpTcp_t *const pThis, + char *const buf, + const size_t buflen, + int *p_currIdx, + const char *const certName) +{ + int r = 0; + assert(buf != NULL); + assert(p_currIdx != NULL); + const int currIdx = *p_currIdx; + const int n = snprintf(buf + currIdx, buflen - currIdx, + "DNSname: %s; ", certName); + if(n < 0 || n >= (int) (buflen - currIdx)) { + callOnAuthErr(pThis, "", "certificate validation failed, names " + "inside certifcate are way to long (> 32KiB)", + RELP_RET_AUTH_CERT_INVL); + r = RELP_RET_PARAM_ERROR; + } else { + *p_currIdx += n; + } + return r; +} +#endif /* defined(HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION) || defined(ENABLE_TLS_OPENSSL) */ + + +#if defined(ENABLE_TLS) +#ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION + +/* Glue to use the right type of function depending of the version */ +#if GNUTLS_VERSION_NUMBER < 0x030202 + //gnutls_mac_algorithm_t and gnutls_digest_algorithm_t are aligned + //So we can use the result to get the fingerprint without trouble + typedef gnutls_mac_algorithm_t digest_id_t; + digest_id_t digest_get_id(const char * name){return gnutls_mac_get_id (name);} + const char* digest_get_name(digest_id_t id){return gnutls_mac_get_name (id);} +# define UNK_DIGEST GNUTLS_MAC_UNKNOWN + +#else + typedef gnutls_digest_algorithm_t digest_id_t; + digest_id_t digest_get_id(const char * name){return gnutls_digest_get_id (name);} + const char* digest_get_name(digest_id_t id){return gnutls_digest_get_name (id);} +# define UNK_DIGEST GNUTLS_DIG_UNKNOWN +#endif + +/* Convert a fingerprint to printable data. The function must be provided a + * sufficiently large buffer. 1024 bytes shall always do (for sha512 and future). + * To the size of the hexadecimal fingerprint, we must add the name of the fingerprint, and the separators. + * Warn : if the size of the buffer isn't sufficient, then the data is truncated. + */ + +static void +GenFingerprintStr(const char *pFingerprint,const int sizeFingerprint, + char * fpBuf,const size_t bufLen, + const digest_id_t type,relpEngine_t * pEngine) +{ + int iSrc, iDst; + + size_t sizeTotal=0,sizeDigest=0; + //statically assigned char*, no free. + const char* digestType=digest_get_name(type); + if (NULL==digestType) + { + if (pEngine!=NULL) + pEngine->dbgprint("warn : the signature type %d is unknown\n",type); + digestType="0000"; + } + sizeDigest=strlen(digestType); + //digestname + 3 char by byte (:xx) + last '\0' + sizeTotal=sizeDigest+(sizeFingerprint*3)+1; + if (sizeTotal =1){ + if (pEngine!=NULL) + pEngine->dbgprint("warn: buffer overflow for %s signature\n",digestType); + fpBuf[0]='\0';//an empty string + }else{ + if (pEngine!=NULL) + pEngine->dbgprint("warn: buffer empty, unable to print the signature\n"); + } +} + +#define MAX_DIGEST_PEER 10 +static size_t ListDigestPeer(digest_id_t* listSigPeer, + tcpPermittedPeers_t* listPeers,relpEngine_t* pEngine) +{ + int i; + int maxDigest=0; + //No signature name of more than 32 bytes. + //Most take 4. Some are slightly longer (SHA3_256, ...) + char digest[32]; + if (NULL==listPeers || listPeers->nmemb<=0 ) + { + if (pEngine!=NULL) pEngine->dbgprint("warn: no PermittedPeer listed\n"); + return 0; + } + for (i=0; inmemb;++i) + { + if ((listPeers->peer[i].name)!=NULL) + { + char*eow=strchr(listPeers->peer[i].name,':');//The first separator + if (eow!=NULL) + { + int sizeDigest=(int)(eow-(listPeers->peer[i].name)); + //31= sizeof(digest)-1; + sizeDigest = sizeDigest > 31 ? 31: sizeDigest; + strncpy(digest,listPeers->peer[i].name,sizeDigest); + digest[sizeDigest]='\0'; + digest_id_t actualDigest=digest_get_id(digest); + if (actualDigest!=UNK_DIGEST) + { + int alreadyExist=0; + int j; + for (j=0;jdbgprint("DDDD: adding digest %s\n", + digest); + listSigPeer[maxDigest++]=actualDigest; + } + } + } + } + } + return maxDigest; +} +/* Check the peer's ID in fingerprint auth mode. */ +static int +relpTcpChkPeerFingerprint(relpTcp_t *const pThis, gnutls_x509_crt_t cert) +{ + int r = 0; + int i; + char fingerprint[126]; + char fpPrintable[256]; + digest_id_t listSigPeer[MAX_DIGEST_PEER]; + size_t maxDigest,k; + size_t size; + int8_t found; + + + /* List which digest we have in our permittedPeer list. We verify only the first MAX_DIGEST_PEER.*/ + maxDigest=ListDigestPeer(listSigPeer,&(pThis->permittedPeers),pThis->pEngine); + + /* obtain the SHA1 fingerprint */ + found = 0; + for(k=0; kpEngine); + pThis->pEngine->dbgprint("peer's certificate %s fingerprint: %s\n", + digest_get_name(digest), fpPrintable); - for(i = 0 ; i < pThis->permittedPeers.nmemb ; ++i) { - if(pThis->permittedPeers.peer[i].wildcardRoot == NULL) { - /* simple string, only, no wildcards */ - if(!strcmp(peername, pThis->permittedPeers.peer[i].name)) { - *pbFoundPositiveMatch = 1; + /* now search through the permitted peers to see if we can find a permitted one */ + pThis->pEngine->dbgprint("n peers %d\n", pThis->permittedPeers.nmemb); + for(i = 0 ; i < pThis->permittedPeers.nmemb ; ++i) { + pThis->pEngine->dbgprint("checking peer '%s','%s'\n", + fpPrintable, pThis->permittedPeers.peer[i].name); + if(!strcmp(fpPrintable, pThis->permittedPeers.peer[i].name)) { + found = 1; break; } - } else { - relpTcpChkOnePeerWildcard(pThis->permittedPeers.peer[i].wildcardRoot, - peername, pbFoundPositiveMatch, pThis->pEngine); - if (*pbFoundPositiveMatch) - break; } } + if(!found) { + r = GNUTLS_E_CERTIFICATE_ERROR; goto done; + } +done: + if(r != 0) { + callOnAuthErr(pThis, fpPrintable, "non-permited fingerprint", RELP_RET_AUTH_ERR_FP); + } + return r; } +#endif /* #ifdef HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION */ + +#if defined(HAVE_GNUTLS_CERTIFICATE_SET_VERIFY_FUNCTION) /* Obtain the CN from the DN field and hand it back to the caller * (which is responsible for destructing it). We try to follow @@ -1291,36 +2200,10 @@ relpTcpGetCN(relpTcp_t *const pThis, gnutls_x509_crt_t cert, char *namebuf, cons } -/* helper to consistently add names to error message buffer */ -static int -relpTcpAddToCertNamesBuffer(relpTcp_t *const pThis, - char *const buf, - const size_t buflen, - int *p_currIdx, - const char *const certName) -{ - int r = 0; - assert(buf != NULL); - assert(p_currIdx != NULL); - const int currIdx = *p_currIdx; - const int n = snprintf(buf + currIdx, buflen - currIdx, - "DNSname: %s; ", certName); - if(n < 0 || n >= (int) (buflen - currIdx)) { - callOnAuthErr(pThis, "", "certificate validation failed, names " - "inside certifcate are way to long (> 32KiB)", - RELP_RET_AUTH_CERT_INVL); - r = GNUTLS_E_CERTIFICATE_ERROR; - } else { - *p_currIdx += n; - } - return r; -} - /* Check the peer's ID in name auth mode. */ static int relpTcpChkPeerName(relpTcp_t *const pThis, gnutls_x509_crt_t cert) { - int r = 0; int ret; unsigned int status = 0; char cnBuf[1024]; /* this is sufficient for the DNSNAME... */ @@ -1331,17 +2214,18 @@ relpTcpChkPeerName(relpTcp_t *const pThis, gnutls_x509_crt_t cert) size_t szAltNameLen; int bFoundPositiveMatch; int gnuRet; + ENTER_RELPFUNC; ret = gnutls_certificate_verify_peers2(pThis->session, &status); if(ret < 0) { callOnAuthErr(pThis, "", "certificate validation failed", RELP_RET_AUTH_CERT_INVL); - r = GNUTLS_E_CERTIFICATE_ERROR; goto done; + ABORT_FINALIZE(GNUTLS_E_CERTIFICATE_ERROR); } if(status != 0) { /* Certificate is not trusted */ callOnAuthErr(pThis, "", "certificate validation failed", RELP_RET_AUTH_CERT_INVL); - r = GNUTLS_E_CERTIFICATE_ERROR; goto done; + ABORT_FINALIZE(GNUTLS_E_CERTIFICATE_ERROR); } bFoundPositiveMatch = 0; @@ -1356,10 +2240,11 @@ relpTcpChkPeerName(relpTcp_t *const pThis, gnutls_x509_crt_t cert) if(gnuRet < 0) break; else if(gnuRet == GNUTLS_SAN_DNSNAME) { - pThis->pEngine->dbgprint("librelp: subject alt dnsName: '%s'\n", szAltName); - r = relpTcpAddToCertNamesBuffer(pThis, allNames, sizeof(allNames), - &iAllNames, szAltName); - if(r != 0) goto done; + pThis->pEngine->dbgprint("relpTcpChkPeerName: subject alt dnsName: '%s'\n", szAltName); + if (relpTcpAddToCertNamesBuffer(pThis, allNames, sizeof(allNames), + &iAllNames, szAltName) != 0) { + ABORT_FINALIZE(GNUTLS_E_CERTIFICATE_ERROR); + } relpTcpChkOnePeerName(pThis, szAltName, &bFoundPositiveMatch); /* do NOT break, because there may be multiple dNSName's! */ } @@ -1369,21 +2254,21 @@ relpTcpChkPeerName(relpTcp_t *const pThis, gnutls_x509_crt_t cert) if(!bFoundPositiveMatch) { /* if we did not succeed so far, we try the CN part of the DN... */ if(relpTcpGetCN(pThis, cert, cnBuf, sizeof(cnBuf)) == 0) { - pThis->pEngine->dbgprint("librelp: relpTcp now checking auth for CN '%s'\n", cnBuf); - r = relpTcpAddToCertNamesBuffer(pThis, allNames, sizeof(allNames), - &iAllNames, cnBuf); - if(r != 0) goto done; + pThis->pEngine->dbgprint("relpTcpChkPeerName: relpTcp now checking auth for CN '%s'\n", cnBuf); + if (relpTcpAddToCertNamesBuffer(pThis, allNames, sizeof(allNames), &iAllNames, cnBuf)) { + ABORT_FINALIZE(GNUTLS_E_CERTIFICATE_ERROR); + } relpTcpChkOnePeerName(pThis, cnBuf, &bFoundPositiveMatch); } } if(!bFoundPositiveMatch) { callOnAuthErr(pThis, allNames, "no permited name found", RELP_RET_AUTH_ERR_NAME); - r = GNUTLS_E_CERTIFICATE_ERROR; goto done; + ABORT_FINALIZE(GNUTLS_E_CERTIFICATE_ERROR); } - r = 0; -done: - return r; +finalize_it: + + LEAVE_RELPFUNC; } /* This function will verify the peer's certificate, and check @@ -1510,7 +2395,138 @@ relpTcpLstnInitTLS(relpTcp_t *const pThis) finalize_it: LEAVE_RELPFUNC; } -#endif /* #ifdef ENABLE_TLS */ +// !!!!!!!!!!! #endif /* #if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) */ +#endif /* defined(ENABLE_TLS)*/ + + +/* add a wildcard entry to this permitted peer. Entries are always + * added at the tail of the list. pszStr and lenStr identify the wildcard + * entry to be added. Note that the string is NOT \0 terminated, so + * we must rely on lenStr for when it is finished. + * rgerhards, 2008-05-27 + */ +static relpRetVal +AddPermittedPeerWildcard(tcpPermittedPeerEntry_t *pEtry, char* pszStr, const int lenStr) +{ + tcpPermittedPeerWildcardComp_t *pNew = NULL; + int iSrc; + int iDst; + ENTER_RELPFUNC; + + if((pNew = calloc(1, sizeof(tcpPermittedPeerWildcardComp_t))) == NULL) { + ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); + } + + if(lenStr == 0) { + pNew->wildcardType = tcpPEER_WILDCARD_EMPTY_COMPONENT; + FINALIZE; + } else { + /* alloc memory for the domain component. We may waste a byte or + * two, but that's ok. + */ + if((pNew->pszDomainPart = malloc(lenStr +1 )) == NULL) { + ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); + } + } + + if(pszStr[0] == '*') { + pNew->wildcardType = tcpPEER_WILDCARD_AT_START; + iSrc = 1; /* skip '*' */ + } else { + iSrc = 0; + } + + for(iDst = 0 ; iSrc < lenStr && pszStr[iSrc] != '*' ; ++iSrc, ++iDst) { + pNew->pszDomainPart[iDst] = pszStr[iSrc]; + } + + if(iSrc < lenStr) { + if(iSrc + 1 == lenStr && pszStr[iSrc] == '*') { + if(pNew->wildcardType == tcpPEER_WILDCARD_AT_START) { + ABORT_FINALIZE(RELP_RET_INVLD_WILDCARD); + } else { + pNew->wildcardType = tcpPEER_WILDCARD_AT_END; + } + } else { + /* we have an invalid wildcard, something follows the asterisk! */ + ABORT_FINALIZE(RELP_RET_INVLD_WILDCARD); + } + } + + if(lenStr == 1 && pNew->wildcardType == tcpPEER_WILDCARD_AT_START) { + pNew->wildcardType = tcpPEER_WILDCARD_MATCH_ALL; + } + + /* if we reach this point, we had a valid wildcard. We now need to + * properly terminate the domain component string. + */ + pNew->pszDomainPart[iDst] = '\0'; + pNew->lenDomainPart = strlen((char*)pNew->pszDomainPart); + +finalize_it: + if(iRet != RELP_RET_OK) { + if(pNew != NULL) { + if(pNew->pszDomainPart != NULL) + free(pNew->pszDomainPart); + free(pNew); + } + } else { + /* add the element to linked list */ + if(pEtry->wildcardRoot == NULL) { + pEtry->wildcardRoot = pNew; + pEtry->wildcardLast = pNew; + } else { + pEtry->wildcardLast->pNext = pNew; + } + pEtry->wildcardLast = pNew; + } + LEAVE_RELPFUNC; +} + +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) +/* Compile a wildcard - must not yet be compiled */ +static relpRetVal +relpTcpPermittedPeerWildcardCompile(tcpPermittedPeerEntry_t *pEtry) +{ + char *pC; + char *pStart; + ENTER_RELPFUNC; + + /* first check if we have a wildcard */ + for(pC = pEtry->name ; *pC != '\0' && *pC != '*' ; ++pC) + /*EMPTY, just skip*/; + + if(*pC == '\0') { /* no wildcard found, we are done */ + FINALIZE; + } + + /* if we reach this point, the string contains wildcards. So let's + * compile the structure. To do so, we must parse from dot to dot + * and create a wildcard entry for each domain component we find. + * We must also flag problems if we have an asterisk in the middle + * of the text (it is supported at the start or end only). + */ + pC = pEtry->name; + while(*pC) { + pStart = pC; + /* find end of domain component */ + for( ; *pC != '\0' && *pC != '.' ; ++pC) + /*EMPTY, just skip*/; + CHKRet(AddPermittedPeerWildcard(pEtry, pStart, pC - pStart)); + /* now check if we have an empty component at end of string */ + if(*pC == '.' && *(pC + 1) == '\0') { + /* pStart is a dummy, it is not used if length is 0 */ + CHKRet(AddPermittedPeerWildcard(pEtry, pStart, 0)); + } + if(*pC != '\0') + ++pC; + } + +finalize_it: + LEAVE_RELPFUNC; +} + +#endif /* defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) */ /* initialize the tcp socket for a listner @@ -1520,7 +2536,7 @@ relpTcpLstnInitTLS(relpTcp_t *const pThis) * perlei, 2018-04-19 */ relpRetVal -relpTcpLstnInit(relpTcp_t *pThis, unsigned char *pLstnPort, unsigned char *pLstnAddr, int ai_family) +relpTcpLstnInit(relpTcp_t *const pThis, unsigned char *pLstnPort, unsigned char *pLstnAddr, int ai_family) { struct addrinfo hints, *res = NULL, *r; int error, maxs, *s, on = 1; @@ -1532,7 +2548,7 @@ relpTcpLstnInit(relpTcp_t *pThis, unsigned char *pLstnPort, unsigned char *pLstn pLstnPt = pLstnPort; assert(pLstnPt != NULL); - + pThis->pEngine->dbgprint("creating relp tcp listen socket on port %s\n", pLstnPt); memset(&hints, 0, sizeof(hints)); @@ -1552,7 +2568,7 @@ relpTcpLstnInit(relpTcp_t *pThis, unsigned char *pLstnPort, unsigned char *pLstn pThis->socks = malloc((maxs+1) * sizeof(int)); if (pThis->socks == NULL) { pThis->pEngine->dbgprint("couldn't allocate memory for TCP listen sockets, " - "suspending RELP message reception."); + "suspending RELP message reception.\n"); ABORT_FINALIZE(RELP_RET_OUT_OF_MEMORY); } @@ -1562,7 +2578,7 @@ relpTcpLstnInit(relpTcp_t *pThis, unsigned char *pLstnPort, unsigned char *pLstn *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); if (*s < 0) { if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT)) - pThis->pEngine->dbgprint("creating relp tcp listen socket"); + pThis->pEngine->dbgprint("creating relp tcp listen socket\n"); /* it is debatable if PF_INET with EAFNOSUPPORT should * also be ignored... */ @@ -1596,17 +2612,17 @@ relpTcpLstnInit(relpTcp_t *pThis, unsigned char *pLstnPort, unsigned char *pLstn sockflags = fcntl(*s, F_SETFL, sockflags); } if(sockflags == -1) { - pThis->pEngine->dbgprint("error %d setting fcntl(O_NONBLOCK) on relp socket", errno); + pThis->pEngine->dbgprint("error %d setting fcntl(O_NONBLOCK) on relp socket\n", errno); close(*s); *s = -1; continue; } -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) if(pThis->bEnableTLS) { CHKRet(relpTcpLstnInitTLS(pThis)); } -#endif /* #ifdef ENABLE_TLS */ +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL*/ if( (bind(*s, r->ai_addr, r->ai_addrlen) < 0) #ifndef IPV6_V6ONLY @@ -1629,8 +2645,8 @@ relpTcpLstnInit(relpTcp_t *pThis, unsigned char *pLstnPort, unsigned char *pLstn * to a fixed, reasonable, limit that should work. Only if * that fails, too, we give up. */ - pThis->pEngine->dbgprint("listen with a backlog of %d failed - retrying with default of 32.", - pThis->iSessMax / 10 + 5); + pThis->pEngine->dbgprint("listen with a backlog of %d failed - retrying with default of 32.\n", + pThis->iSessMax / 10 + 5); if(listen(*s, 32) < 0) { pThis->pEngine->dbgprint("relp listen error %d, suspending\n", errno); close(*s); @@ -1650,7 +2666,9 @@ relpTcpLstnInit(relpTcp_t *pThis, unsigned char *pLstnPort, unsigned char *pLstn if(*pThis->socks == 0) { pThis->pEngine->dbgprint("No RELP TCP listen socket could successfully be initialized, " "message reception via RELP disabled.\n"); - free(pThis->socks); + /* free(pThis->socks); + * DONE IN relpTcpDestruct! + */ ABORT_FINALIZE(RELP_RET_COULD_NOT_BIND); } @@ -1674,30 +2692,63 @@ relpRetVal relpTcpRcv(relpTcp_t *const pThis, relpOctet_t *pRcvBuf, ssize_t *pLenBuf) { ENTER_RELPFUNC; + int lenRcvd; +#if defined(ENABLE_TLS_OPENSSL) + int err; +#endif RELPOBJ_assert(pThis, Tcp); -#ifdef ENABLE_TLS - int r; +#if defined(ENABLE_TLS) if(pThis->bEnableTLS) { - r = gnutls_record_recv(pThis->session, pRcvBuf, *pLenBuf); - if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) { + lenRcvd = gnutls_record_recv(pThis->session, pRcvBuf, *pLenBuf); + if(lenRcvd == GNUTLS_E_INTERRUPTED || lenRcvd == GNUTLS_E_AGAIN) { pThis->pEngine->dbgprint("librelp: gnutls_record_recv must be retried\n"); pThis->rtryOp = relpTCP_RETRY_recv; } else { - if(r < 0) - chkGnutlsCode(pThis, "TLS record reception failed", RELP_RET_IO_ERR, r); + if(lenRcvd < 0) + chkGnutlsCode(pThis, "TLS record reception failed", RELP_RET_IO_ERR, lenRcvd); pThis->rtryOp = relpTCP_RETRY_none; } - *pLenBuf = (r < 0) ? -1 : r; + *pLenBuf = (lenRcvd < 0) ? -1 : lenRcvd; + } else { +#elif defined(ENABLE_TLS_OPENSSL) + if(pThis->bEnableTLS) { + lenRcvd = SSL_read(pThis->ssl, pRcvBuf, *pLenBuf); + if(lenRcvd > 0) { + pThis->pEngine->dbgprint("relpTcpRcv: SSL_read SUCCESS\n"); + *pLenBuf = lenRcvd; + } else { + *pLenBuf = -1; + + err = SSL_get_error(pThis->ssl, lenRcvd); + if( err == SSL_ERROR_ZERO_RETURN ) { + pThis->pEngine->dbgprint("relpTcpRcv: SSL_ERROR_ZERO_RETURN received, " + "connection may closed already\n"); + pThis->rtryOp = relpTCP_RETRY_none; + ABORT_FINALIZE(RELP_RET_IO_ERR); + } + else if(err != SSL_ERROR_WANT_READ && + err != SSL_ERROR_WANT_WRITE) { + /* Output error and abort */ + relpTcpLastSSLErrorMsg(lenRcvd, pThis, "relpTcpRcv"); + pThis->rtryOp = relpTCP_RETRY_none; + ABORT_FINALIZE(RELP_RET_IO_ERR); + } else { + pThis->pEngine->dbgprint("relpTcpRcv: SSL_get_error = %d\n", err); + pThis->rtryOp = relpTCP_RETRY_recv; + } + } } else { #endif /* #ifdef ENABLE_TLS */ - *pLenBuf = recv(pThis->sock, pRcvBuf, *pLenBuf, MSG_DONTWAIT); + *pLenBuf = lenRcvd = recv(pThis->sock, pRcvBuf, *pLenBuf, MSG_DONTWAIT); pThis->pEngine->dbgprint("relpTcpRcv: read %zd bytes from sock %d\n", *pLenBuf, pThis->sock); -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) } -#endif /* #ifdef ENABLE_TLS */ - +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL*/ +finalize_it: + pThis->pEngine->dbgprint("relpTcpRcv return. relptcp [%p], iRet %d, lenRcvd %d, pLenBuf %zd\n", + (void *) pThis, iRet, lenRcvd, *pLenBuf); LEAVE_RELPFUNC; } @@ -1749,13 +2800,16 @@ relpRetVal relpTcpSend(relpTcp_t *const pThis, relpOctet_t *pBuf, ssize_t *pLenBuf) { ssize_t written; +#if defined(ENABLE_TLS_OPENSSL) + int err; +#endif ENTER_RELPFUNC; RELPOBJ_assert(pThis, Tcp); -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) if(pThis->bEnableTLS) { written = gnutls_record_send(pThis->session, pBuf, *pLenBuf); - pThis->pEngine->dbgprint("librelp: TLS send returned %d\n", (int) written); + pThis->pEngine->dbgprint("relpTcpSend: TLS send returned %d\n", (int) written); if(written == GNUTLS_E_AGAIN || written == GNUTLS_E_INTERRUPTED) { pThis->rtryOp = relpTCP_RETRY_send; written = 0; @@ -1767,7 +2821,34 @@ relpTcpSend(relpTcp_t *const pThis, relpOctet_t *pBuf, ssize_t *pLenBuf) } } } else { -#endif /* #ifdef ENABLE_TLS */ +#elif defined(ENABLE_TLS_OPENSSL) + if(pThis->bEnableTLS) { + written = SSL_write(pThis->ssl, pBuf, *pLenBuf); + if(written > 0) { + pThis->pEngine->dbgprint("relpTcpSend: SSL_write SUCCESS\n"); + } else { + err = SSL_get_error(pThis->ssl, written); + if( err == SSL_ERROR_ZERO_RETURN ) { + pThis->pEngine->dbgprint("relpTcpSend: SSL_ERROR_ZERO_RETURN received, " + "retry next time\n"); + pThis->rtryOp = relpTCP_RETRY_send; + written = 0; + } + else if(err != SSL_ERROR_WANT_READ && + err != SSL_ERROR_WANT_WRITE) { + /* Output error and abort */ + relpTcpLastSSLErrorMsg( (int)written, pThis, "relpTcpSend"); + ABORT_FINALIZE(RELP_RET_IO_ERR); + } else { + /* Check for SSL Shutdown */ + if (SSL_get_shutdown(pThis->ssl) == SSL_RECEIVED_SHUTDOWN) { + pThis->pEngine->dbgprint("relpTcpSend: received SSL_RECEIVED_SHUTDOWN!\n"); + ABORT_FINALIZE(RELP_RET_IO_ERR); + } + } + } + } else { +#endif /* defined(ENABLE_TLS) | defined(ENABLE_TLS_OPENSSL */ written = send(pThis->sock, pBuf, *pLenBuf, 0); const int errno_save = errno; pThis->pEngine->dbgprint("relpTcpSend: sock %d, lenbuf %zd, send returned %d [errno %d]\n", @@ -1784,9 +2865,9 @@ relpTcpSend(relpTcp_t *const pThis, relpOctet_t *pBuf, ssize_t *pLenBuf) break; } } -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) } -#endif /* #ifdef ENABLE_TLS */ +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL*/ *pLenBuf = written; finalize_it: @@ -1807,6 +2888,7 @@ relpTcpConnectTLSInit(relpTcp_t *const pThis) int sockflags; ENTER_RELPFUNC; RELPOBJ_assert(pThis, Tcp); + pThis->pEngine->dbgprint("relpTcpConnectTLSInit gnutls\n"); /* We expect a non blocking socket to establish a tls session */ if((sockflags = fcntl(pThis->sock, F_GETFL)) != -1) { @@ -1950,7 +3032,7 @@ relpTcpConnect(relpTcp_t *const pThis, pThis->pEngine->dbgprint("error %d in getaddrinfo\n", errno); ABORT_FINALIZE(RELP_RET_IO_ERR); } - + if((pThis->sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) { ABORT_FINALIZE(RELP_RET_IO_ERR); } @@ -1994,13 +3076,12 @@ relpTcpConnect(relpTcp_t *const pThis, ABORT_FINALIZE(RELP_RET_IO_ERR); } - -#ifdef ENABLE_TLS +#if defined(ENABLE_TLS) || defined(ENABLE_TLS_OPENSSL) if(pThis->bEnableTLS) { CHKRet(relpTcpConnectTLSInit(pThis)); pThis->bTLSActive = 1; } -#endif /* #ifdef ENABLE_TLS */ +#endif /* #ifdef ENABLE_TLS | ENABLE_TLS_OPENSSL*/ finalize_it: if(res != NULL) diff --git a/src/tcp.h b/src/tcp.h index 70039c3c..8f375d73 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -37,6 +37,9 @@ #ifdef ENABLE_TLS # include #endif +#ifdef ENABLE_TLS_OPENSSL +# include +#endif #include "relp.h" typedef enum { relpTCP_RETRY_none = 0, @@ -83,6 +86,19 @@ typedef struct tcpPermittedPeers_s { tcpPermittedPeerEntry_t *peer; } tcpPermittedPeers_t; +#ifdef ENABLE_TLS_OPENSSL +typedef enum { + osslRtry_None = 0, /**< no call needs to be retried */ + osslRtry_handshake = 1, + osslRtry_recv = 2 +} osslRtryCall_t; /**< IDs of calls that needs to be retried */ + +typedef enum { + osslServer = 0, /**< Server SSL Object */ + osslClient = 1 /**< Client SSL Object */ +} osslSslState_t; +#endif + /* the RELPTCP object * rgerhards, 2008-03-16 */ @@ -120,6 +136,10 @@ typedef struct relpTcp_s { #ifdef ENABLE_TLS gnutls_session_t session; gnutls_dh_params_t dh_params; /**< server DH parameters for anon mode */ +#endif +#ifdef ENABLE_TLS_OPENSSL + SSL *ssl; /* OpenSSL main SSL obj */ + osslSslState_t sslState;/**< what must we retry? */ #endif relpTcpRtryState_t rtryOp; } relpTcp_t; @@ -163,4 +183,36 @@ void relpTcpHintBurstEnd(relpTcp_t *pThis); int relpTcpGetRtryDirection(relpTcp_t *pThis); int relpTcpWaitWriteable(relpTcp_t *pThis, struct timespec *timeout); +#ifdef ENABLE_TLS_OPENSSL +/*-----------------------------------------------------------------------------*/ +#define MUTEX_TYPE pthread_mutex_t +#define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL) +#define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x)) +#define MUTEX_LOCK(x) pthread_mutex_lock(&(x)) +#define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x)) +#define THREAD_ID pthread_self() + +/* This array will store all of the mutexes available to OpenSSL. */ +struct CRYPTO_dynlock_value +{ + MUTEX_TYPE mutex; +}; + +void dyn_destroy_function(struct CRYPTO_dynlock_value *l, + __attribute__((unused)) const char *file, __attribute__((unused)) int line); +void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l, + __attribute__((unused)) const char *file, __attribute__((unused)) int line); +struct CRYPTO_dynlock_value * dyn_create_function( + __attribute__((unused)) const char *file, __attribute__((unused)) int line); +unsigned long id_function(void); +void locking_function(int mode, int n, + __attribute__((unused)) const char * file, __attribute__((unused)) int line); +/*-----------------------------------------------------------------------------*/ + +/* Helper to detect when OpenSSL is initialized */ +static int called_openssl_global_init = 0; +/* Main OpenSSL CTX pointer */ +static SSL_CTX *ctx; +#endif + #endif /* #ifndef RELPTCP_H_INCLUDED */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 55196831..0e27f4a3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -14,8 +14,9 @@ VALGRIND_TESTS= \ tls-basic-vg.sh TESTS= basic.sh \ - tls-basic.sh \ tls-basic-anon.sh \ + tls-basic.sh \ + tls-basic-fingerprint.sh \ tls-wrong-permittedPeer.sh \ tls-wrong-authname.sh \ tls-missing-param-sender.sh \ diff --git a/tests/basic.sh b/tests/basic.sh index d3eaa177..2f81a6e0 100755 --- a/tests/basic.sh +++ b/tests/basic.sh @@ -1,35 +1,10 @@ #!/bin/bash . ${srcdir}/test-framework.sh - -echo 'relp-basic.sh' -echo '=============' - -TESTPORT=20514 - -echo 'Start Receiver...' -./receive -p $TESTPORT > librelp.out.log & -PID=$! - -sleep 1 +startup_receiver $OPT_VERBOSE echo 'Send Message...' -./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" - - -echo 'Stop Receiver...' -kill $PID - - -grep "testmessage" librelp.out.log > /dev/null -if [ $? -ne 0 ]; then - echo - echo "FAIL: expected error message not found. librelp.out.log is:" - cat librelp.out.log - exit 1 -fi - -echo '--------------------------------------------------------------------' -rm librelp.out.log -exit - +./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" $OPT_VERBOSE +stop_receiver +check_output "testmessage" +terminate diff --git a/tests/oversize-msg-abort-errmsg.sh b/tests/oversize-msg-abort-errmsg.sh index 26c6bb00..bee79400 100755 --- a/tests/oversize-msg-abort-errmsg.sh +++ b/tests/oversize-msg-abort-errmsg.sh @@ -1,13 +1,13 @@ #!/bin/bash # check if oversize message error is generated by receiver . ${srcdir}/test-framework.sh -startup_receiver 2> server.err.log +startup_receiver echo 'Send Message...' ./send -t 127.0.0.1 -p $TESTPORT -m "testmessage1" -d 150000 ./send -t 127.0.0.1 -p $TESTPORT -m "testmessage2" stop_receiver -check_output "error.*frame too long" server.err.log +check_output "error.*frame too long" librelp.out.log check_output "testmessage2" terminate diff --git a/tests/oversize-msg-accept-errmsg.sh b/tests/oversize-msg-accept-errmsg.sh index 629c4f44..883221d6 100755 --- a/tests/oversize-msg-accept-errmsg.sh +++ b/tests/oversize-msg-accept-errmsg.sh @@ -2,12 +2,12 @@ # Check if oversize message error is generated by receiver # and message is accepted correctly. . ${srcdir}/test-framework.sh -startup_receiver -m 140 -o "accept" 2> server.err.log +startup_receiver -m 140 -o "accept" echo 'Send Message...' -./send -t 127.0.0.1 -p $TESTPORT -m "testmessage1" -d 150 +./send -t 127.0.0.1 -p $TESTPORT -m "testmessage1" -d 150 1>>client.err.log 2>&1 stop_receiver -check_output "error.*frame too long.*will still be accepted" server.err.log +check_output "error.*frame too long.*will still be accepted" librelp.out.log check_output "^testmessage1012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567$" terminate diff --git a/tests/receive.c b/tests/receive.c index ab4a60d0..c6485628 100644 --- a/tests/receive.c +++ b/tests/receive.c @@ -25,7 +25,7 @@ #include #include "librelp.h" -#define TRY(f) if(f != RELP_RET_OK) { printf("failure in: %s\n", #f); return 1; } +#define TRY(f) if(f != RELP_RET_OK) { fprintf(stderr, "receive.c: FAILURE in '%s'\n", #f); return 1; } static relpEngine_t *pRelpEngine; @@ -38,7 +38,8 @@ dbgprintf(char *fmt, ...) va_start(ap, fmt); vsnprintf(pszWriteBuf, sizeof(pszWriteBuf), fmt, ap); va_end(ap); - fprintf(stderr, "receive.c: %s", pszWriteBuf); fflush(stderr); + fprintf(stderr, "receive.c: %s", pszWriteBuf); + fflush(stderr); } static relpRetVal onSyslogRcv(unsigned char *pHostname __attribute__((unused)), diff --git a/tests/send.c b/tests/send.c index ac8ec09c..2285f827 100644 --- a/tests/send.c +++ b/tests/send.c @@ -24,7 +24,7 @@ #include #include "librelp.h" -#define TRY(f) if(f != RELP_RET_OK) { printf("failure in: %s\n", #f); return 1; } +#define TRY(f) if(f != RELP_RET_OK) { fprintf(stderr, "send.c: FAILURE in: '%s'\n", #f); return 1; } static relpEngine_t *pRelpEngine; @@ -38,10 +38,9 @@ dbgprintf(char *fmt, ...) vsnprintf(pszWriteBuf, sizeof(pszWriteBuf), fmt, ap); va_end(ap); fprintf(stderr, "send.c: %s", pszWriteBuf); + fflush(stderr); } - - void print_usage(void) { printf("Usage: send -t -p -m \n"); diff --git a/tests/test-framework.sh b/tests/test-framework.sh index 068a9b7e..fb376181 100644 --- a/tests/test-framework.sh +++ b/tests/test-framework.sh @@ -5,8 +5,9 @@ # "config settings" for the testbench TB_TIMEOUT_STARTUP=400 # 40 seconds - Solaris sometimes needs this... -TESTPORT=30514 -#OPT_VERBOSE=-v # uncomment to get verbose output +TESTPORT=31514 +export valgrind="valgrind --malloc-fill=ff --free-fill=fe --log-fd=1" +export OPT_VERBOSE=-v # We need verbose now for propper error checking! ###################################################################### # functions @@ -28,23 +29,41 @@ function wait_process_startup_via_pidfile() { printf "program started up, pidfile $1 contains $(cat $1)\n" } +# start receiver WITH valgrind, add receiver command line parameters after function name +function startup_receiver_valgrind() { + printf 'Starting Receiver...\n' + $valgrind ./receive -p $TESTPORT -F $srcdir/receive.pid $OPT_VERBOSE $* 1>>librelp.out.log 2>&1 & + export RECEIVE_PID=$! + printf "got receive pid $RECEIVE_PID\n" + wait_process_startup_via_pidfile $srcdir/receive.pid + sleep 1 + printf 'Receiver running\n' +} # start receiver, add receiver command line parameters after function name function startup_receiver() { printf 'Starting Receiver...\n' - ./receive -p $TESTPORT -F receive.pid $OPT_VERBOSE $* > librelp.out.log & + ./receive -p $TESTPORT -F $srcdir/receive.pid $OPT_VERBOSE $* 1>>librelp.out.log 2>&1 & export RECEIVE_PID=$! printf "got receive pid $RECEIVE_PID\n" - wait_process_startup_via_pidfile receive.pid + wait_process_startup_via_pidfile $srcdir/receive.pid sleep 1 printf 'Receiver running\n' } # stop receiver function stop_receiver() { - kill $(cat receive.pid) - wait $(cat receive.pid) &> /dev/null - #kill $RECEIVE_PID + if [ -f $srcdir/receive.pid ]; then + kill $(cat $srcdir/receive.pid) &> /dev/null + fi + wait -n 5 $(cat $srcdir/receive.pid) &> /dev/null +#kill $RECEIVE_PID + if [ -f $srcdir/receive.pid ]; then + # FORCE + kill -9 $(cat $srcdir/receive.pid) &> /dev/null + fi + sleep 1 + printf "receiver stopped\n" } @@ -67,6 +86,28 @@ function check_output() { fi } +# $1 is the value to check for +# $2 (optinal) is the file to check +function check_output_only() { + EXPECTED="$1" + if [ "$2" == "" ] ; then + FILE_TO_CHECK="librelp.out.log" + else + FILE_TO_CHECK="$2" + fi +# printf "\ncheck_output_only on $FILE_TO_CHECK with '$EXPECTED'\n" + grep -q "$EXPECTED" $FILE_TO_CHECK; + if [ $? -ne 0 ]; then + # False +# printf "\ncheck_output_only FALSE \n"; + return 1; + else + # true +# printf "\ncheck_output_only TRUE \n"; + return 0; + fi +} + # cleanup temporary # note: on solaris, # get full command line: /usr/ucb/ps awwx @@ -78,6 +119,11 @@ function cleanup() { pkill -x receive echo pkill result $? fi + + if [ -f $srcdir/receive.pid ]; then + kill -9 `cat $srcdir/receive.pid` &> /dev/null + fi + rm -f receive.pid librelp.out.log *.err.log } diff --git a/tests/tls-basic-anon.sh b/tests/tls-basic-anon.sh index 03d249b7..5449b5cd 100755 --- a/tests/tls-basic-anon.sh +++ b/tests/tls-basic-anon.sh @@ -3,8 +3,18 @@ startup_receiver -T echo 'Send Message...' -./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T +./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T $OPT_VERBOSE 1>>librelp.out.log 2>&1 +# "relpTcpLastSSLErrorMsg: Errorstack: error:1417A0C1:SSL routines:tls_post_process_client_hello:no shared cipher" stop_receiver +if check_output_only "relpTcpLastSSLErrorMsg\: Errorstack\: error\:.*\:no shared cipher"; then + printf "\nSKIP: openssl reported 'no shared cipher'\n" + printf "\nDEBUG: content of librelp.out.log\n" + cat $FILE_TO_CHECK + + terminate + exit 77; +fi + check_output "testmessage" terminate diff --git a/tests/tls-basic-fingerprint.sh b/tests/tls-basic-fingerprint.sh new file mode 100755 index 00000000..1a6a9165 --- /dev/null +++ b/tests/tls-basic-fingerprint.sh @@ -0,0 +1,10 @@ +#!/bin/bash +. ${srcdir}/test-framework.sh +startup_receiver -T -a "fingerprint" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P 'SHA1:5C:C6:62:D5:9D:25:9F:BC:F3:CB:61:FA:D2:B3:8B:61:88:D7:06:C3' + +echo 'Send Message...' +./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T -a "fingerprint" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P 'SHA1:5C:C6:62:D5:9D:25:9F:BC:F3:CB:61:FA:D2:B3:8B:61:88:D7:06:C3' $OPT_VERBOSE 1>>librelp.out.log 2>&1 + +stop_receiver +check_output "testmessage" +terminate diff --git a/tests/tls-basic-vg.sh b/tests/tls-basic-vg.sh index e73a9004..a5af445a 100755 --- a/tests/tls-basic-vg.sh +++ b/tests/tls-basic-vg.sh @@ -10,19 +10,13 @@ fi . ${srcdir}/test-framework.sh -TESTPORT=20514 echo 'Start Receiver...' -valgrind ./receive -p $TESTPORT -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "rsyslog-client" > librelp.out.log & -PID=$! - -sleep 1 +startup_receiver_valgrind -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "rsyslog-client" echo 'Send Message...' -valgrind ./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "rsyslog-client" +$valgrind ./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "rsyslog-client" $OPT_VERBOSE 1>>librelp.out.log 2>&1 -echo 'Stop Receiver...' -kill $PID +stop_receiver check_output "testmessage" terminate - diff --git a/tests/tls-basic.sh b/tests/tls-basic.sh index b4128c32..b095853e 100755 --- a/tests/tls-basic.sh +++ b/tests/tls-basic.sh @@ -1,11 +1,10 @@ #!/bin/bash . ${srcdir}/test-framework.sh -startup_receiver -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "rsyslog-client" +startup_receiver -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P 'testbench.rsyslog.com' echo 'Send Message...' -./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "rsyslog-client" +./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P 'testbench.rsyslog.com' $OPT_VERBOSE stop_receiver check_output "testmessage" terminate - diff --git a/tests/tls-missing-param-receiver.sh b/tests/tls-missing-param-receiver.sh index b9b34c9f..2e04acb5 100755 --- a/tests/tls-missing-param-receiver.sh +++ b/tests/tls-missing-param-receiver.sh @@ -3,6 +3,7 @@ echo 'Start Receiver...' . ${srcdir}/test-framework.sh +# NOT USING startup_receiver! ./receive -p $TESTPORT -T -a "name" -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "rsyslog" > librelp.out.log check_output "receive: parameter missing; certificates and permittedPeer required" diff --git a/tests/tls-wrong-authname.sh b/tests/tls-wrong-authname.sh index a477c1d2..aec27d79 100755 --- a/tests/tls-wrong-authname.sh +++ b/tests/tls-wrong-authname.sh @@ -1,10 +1,11 @@ #!/bin/bash . ${srcdir}/test-framework.sh - -./receive -p $TESTPORT -T -a "anon" -x tls-certs/ca.pem -y tls-certs/cert.pem -z tls-certs/key.pem -P "rsyslog" &> librelp.out.log +# NOT USING startup_receiver here! +./receive -p $TESTPORT -T -a "anon" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "rsyslog" $OPT_VERBOSE $* 1>>librelp.out.log 2>&1 check_output "relpSrvSetAuthMode(pRelpSrv, authMode)" -./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T -a "anon" -x tls-certs/ca.pem -y tls-certs/cert.pem -z tls-certs/key.pem -P "rsyslog" -v &> librelp.out.log -check_output "relpCltSetAuthMode(pRelpClt, authMode)" +echo 'Send Message...' +./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T -a "anon" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "rsyslog" $OPT_VERBOSE 1>>client.err.log 2>&1 +check_output "relpCltSetAuthMode(pRelpClt, authMode)" client.err.log terminate diff --git a/tests/tls-wrong-permittedPeer.sh b/tests/tls-wrong-permittedPeer.sh index f6287e79..919d3780 100755 --- a/tests/tls-wrong-permittedPeer.sh +++ b/tests/tls-wrong-permittedPeer.sh @@ -3,8 +3,10 @@ startup_receiver -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "wrong name" echo 'Send Message...' -./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "wrong name" -v 2>&1 | tee librelp.out.log +./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -T -a "name" -x ${srcdir}/tls-certs/ca.pem -y ${srcdir}/tls-certs/cert.pem -z ${srcdir}/tls-certs/key.pem -P "wrong name" $OPT_VERBOSE 1>>librelp.out.log 2>&1 stop_receiver -check_output "librelp: auth error: authdata:'DNSname: testbench.rsyslog.com; DNSname: rsyslog-client; ', ecode 10034, emsg 'no permited name found'" + +check_output "librelp\: auth error\: authdata\:'DNSname\: testbench.rsyslog.com\; " + terminate diff --git a/tests/truncate-oversize-msg.sh b/tests/truncate-oversize-msg.sh index aee1a92b..0fb6fa78 100755 --- a/tests/truncate-oversize-msg.sh +++ b/tests/truncate-oversize-msg.sh @@ -1,13 +1,13 @@ #!/bin/bash . ${srcdir}/test-framework.sh -startup_receiver -o truncate -m 144 &> librelp.err.log +startup_receiver -o truncate -m 144 echo 'Send Message...' -./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -d 154 +./send -t 127.0.0.1 -p $TESTPORT -m "testmessage" -d 154 1>>client.err.log 2>&1 stop_receiver # ^-sign symbolizes the beginning of the message and $-sign the expected end. check_output "^testmessage0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012$" -check_output "error.*frame too long" librelp.err.log +check_output "error.*frame too long" librelp.out.log cat librelp.out.log terminate