From 0f87c631e7eac2ccbd36cd875d64de29dd7c714c Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Tue, 2 Apr 2013 11:31:09 +0200 Subject: add configure check for libgcrypt --- configure.ac | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/configure.ac b/configure.ac index 0c877eea..d6d4c203 100644 --- a/configure.ac +++ b/configure.ac @@ -755,6 +755,38 @@ if test "x$enable_gnutls" = "xyes"; then fi AM_CONDITIONAL(ENABLE_GNUTLS, test x$enable_gnutls = xyes) +# libgcrypt support +AC_ARG_ENABLE(libgcrypt, + [AS_HELP_STRING([--enable-libgcrypt],[Enable libgcrypt support @<:@default=yes@:>@])], + [case "${enableval}" in + yes) enable_libgcrypt="yes" ;; + no) enable_libgcrypt="no" ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-libgcrypt) ;; + esac], + [enable_libgcrypt=yes] +) +if test "x$enable_libgcrypt" = "xyes"; then + AC_CHECK_PROG( + [HAVE_LIBGCRYPT_CONFIG], + [libgcrypt-config], + [yes],,, + ) + if test "x${HAVE_LIBGCRYPT_CONFIG}" != "xyes"; then + AC_MSG_FAILURE([libgcrypt-config not found in PATH]) + fi + AC_CHECK_LIB( + [gcrypt], + [gcry_cipher_open], + [LIBGCRYPT_CFLAGS="`libgcrypt-config --cflags`" + LIBGCRYPT_LIBS="`libgcrypt-config --libs`" + ], + [AC_MSG_FAILURE([libgrypt is missing])], + [`libgcrypt-config --libs --cflags`] + ) + AC_DEFINE([ENABLE_LIBGCRYPT], [1], [Indicator that LIBGCRYPT is present]) +fi +AM_CONDITIONAL(ENABLE_GNUTLS, test x$enable_gnutls = xyes) + # support for building the rsyslogd runtime AC_ARG_ENABLE(rsyslogrt, @@ -1406,6 +1438,7 @@ echo " GUI components will be built: $enable_gui" echo " Unlimited select() support enabled: $enable_unlimited_select" echo " uuid support enabled: $enable_uuid" echo " GuardTime signature support enabled: $enable_guardtime" +echo " libgcrypt support enabled: $enable_libgcrypt" echo " anonymization support enabled: $enable_mmanon" echo echo "---{ input plugins }---" -- cgit v1.2.3 From 0cee769fcdc9716ccb2a60b6473062a60f640bb3 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 5 Apr 2013 12:08:49 +0200 Subject: log encryption: initial PoC implementation rough baseline, needs to be extended for actual use. --- configure.ac | 2 +- runtime/Makefile.am | 22 ++++- runtime/cryprov.h | 41 ++++++++++ runtime/libgcry.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++ runtime/libgcry.h | 47 +++++++++++ runtime/lmcry_gcry.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++ runtime/lmcry_gcry.h | 39 +++++++++ runtime/rsyslog.h | 1 + runtime/stream.c | 27 ++++++- runtime/stream.h | 11 ++- tools/Makefile.am | 2 +- tools/omfile.c | 71 +++++++++++++++- 12 files changed, 701 insertions(+), 10 deletions(-) create mode 100644 runtime/cryprov.h create mode 100644 runtime/libgcry.c create mode 100644 runtime/libgcry.h create mode 100644 runtime/lmcry_gcry.c create mode 100644 runtime/lmcry_gcry.h diff --git a/configure.ac b/configure.ac index d6d4c203..0ec29f0d 100644 --- a/configure.ac +++ b/configure.ac @@ -785,7 +785,7 @@ if test "x$enable_libgcrypt" = "xyes"; then ) AC_DEFINE([ENABLE_LIBGCRYPT], [1], [Indicator that LIBGCRYPT is present]) fi -AM_CONDITIONAL(ENABLE_GNUTLS, test x$enable_gnutls = xyes) +AM_CONDITIONAL(ENABLE_LIBGCRYPT, test x$enable_libgcrypt = xyes) # support for building the rsyslogd runtime diff --git a/runtime/Makefile.am b/runtime/Makefile.am index c05cc773..be68ce60 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -96,12 +96,12 @@ librsyslog_la_SOURCES = \ # if WITH_MODDIRS -librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools +librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) $(LIBGCRYPT_CFLAGS) -I\$(top_srcdir)/tools else -librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools -I\$(top_srcdir)/grammar +librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/\" -I$(top_srcdir) $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) $(LIBGCRYPT_CFLAGS) -I\$(top_srcdir)/tools -I\$(top_srcdir)/grammar endif #librsyslog_la_LDFLAGS = -module -avoid-version -librsyslog_la_LIBADD = $(DL_LIBS) $(RT_LIBS) $(LIBEE_LIBS) +librsyslog_la_LIBADD = $(DL_LIBS) $(RT_LIBS) $(LIBGCRYPT_LIBS) $(LIBEE_LIBS) # # regular expression support @@ -174,6 +174,22 @@ lmnsd_gtls_la_LDFLAGS = -module -avoid-version lmnsd_gtls_la_LIBADD = $(GNUTLS_LIBS) endif +# +# support library for libgcrypt +# +if ENABLE_LIBGCRYPT +#noinst_LTLIBRARIES += libgcry.la +#libgcry_la_SOURCES = libgcry.c libgcry.h +#libcgry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) + pkglib_LTLIBRARIES += lmcry_gcry.la + lmcry_gcry_la_SOURCES = lmcry_gcry.c lmcry_gcry.h libgcry.c libgcry.h + lmcry_gcry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) + lmcry_gcry_la_LDFLAGS = -module -avoid-version +#lmcry_gcry_la_LIBADD = libgcry.la $(LIBGCRYPT_LIBS) + lmcry_gcry_la_LIBADD = $(LIBGCRYPT_LIBS) +endif + + # # support library for guardtime # diff --git a/runtime/cryprov.h b/runtime/cryprov.h new file mode 100644 index 00000000..c5ee95a0 --- /dev/null +++ b/runtime/cryprov.h @@ -0,0 +1,41 @@ +/* The interface definition for (file) crypto providers. + * + * This is just an abstract driver interface, which needs to be + * implemented by concrete classes. + * + * Copyright 2013 Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef INCLUDED_CRYPROV_H +#define INCLUDED_CRYPROV_H + +#include + +/* interface */ +BEGINinterface(cryprov) /* name must also be changed in ENDinterface macro! */ + rsRetVal (*Construct)(void *ppThis); + rsRetVal (*SetCnfParam)(void *ppThis, struct nvlst *lst); + rsRetVal (*Destruct)(void *ppThis); + rsRetVal (*OnFileOpen)(void *pThis, uchar *fn, void *pFileInstData); + rsRetVal (*Encrypt)(void *pFileInstData, uchar *buf, size_t *lenBuf); + rsRetVal (*OnFileClose)(void *pFileInstData); +ENDinterface(cryprov) +#define cryprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +rsRetVal initCrypt(int gcry_mode, char * iniVector); +rsRetVal doCrypt(char *buf, size_t *len); +#endif /* #ifndef INCLUDED_CRYPROV_H */ diff --git a/runtime/libgcry.c b/runtime/libgcry.c new file mode 100644 index 00000000..8184c160 --- /dev/null +++ b/runtime/libgcry.c @@ -0,0 +1,224 @@ +/* gcry.c - rsyslog's libgcrypt based crypto provider + * + * Copyright 2013 Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include + +#include "rsyslog.h" +#include "libgcry.h" + + +static inline gcryfile +gcryfileConstruct(gcryctx ctx) +{ + gcryfile gf; + if((gf = calloc(1, sizeof(struct gcryfile_s))) == NULL) + goto done; + gf->ctx = ctx; +done: return gf; +} +gcryctx +gcryCtxNew(void) +{ + gcryctx ctx; + ctx = calloc(1, sizeof(struct gcryctx_s)); + return ctx; +} + +int +gcryfileDestruct(gcryfile gf) +{ + int r = 0; + if(gf == NULL) + goto done; + + free(gf); +done: return r; +} +void +rsgcryCtxDel(gcryctx ctx) +{ + if(ctx != NULL) { + free(ctx); + } +} + +static inline void +addPadding(gcryfile pF, uchar *buf, size_t *plen) +{ + unsigned i; + size_t nPad; + nPad = (pF->blkLength - *plen % pF->blkLength) % pF->blkLength; + dbgprintf("DDDD: addPadding %d chars, blkLength %d, mod %d, pad %d\n", + *plen, pF->blkLength, *plen % pF->blkLength, nPad); + for(i = 0 ; i < nPad ; ++i) + buf[(*plen)+i] = 0x00; + (*plen)+= nPad; +} + +static inline void +removePadding(char *buf, size_t *plen) +{ + unsigned len = (unsigned) *plen; + unsigned iSrc, iDst; + char *frstNUL; + + frstNUL = strchr(buf, 0x00); + if(frstNUL == NULL) + goto done; + iDst = iSrc = frstNUL - buf; + + while(iSrc < len) { + if(buf[iSrc] != 0x00) + buf[iDst++] = buf[iSrc]; + ++iSrc; + } + + *plen = iDst; +done: return; +} + +rsRetVal +rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char *iniVector) +{ + #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable + size_t keyLength; + char *aesSymKey = "123456789012345678901234"; // TODO: TEST ONLY + gcry_error_t gcryError; + gcryfile gf = NULL; + DEFiRet; + + CHKmalloc(gf = gcryfileConstruct(ctx)); + + gf->blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); + keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER); + + gcryError = gcry_cipher_open( + &gf->chd, // gcry_cipher_hd_t * + GCRY_CIPHER, // int + gcry_mode, // int + 0); // unsigned int + if (gcryError) { + dbgprintf("gcry_cipher_open failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + ABORT_FINALIZE(RS_RET_ERR); + } + + gcryError = gcry_cipher_setkey(gf->chd, aesSymKey, keyLength); + if (gcryError) { + dbgprintf("gcry_cipher_setkey failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + ABORT_FINALIZE(RS_RET_ERR); + } + + gcryError = gcry_cipher_setiv(gf->chd, iniVector, gf->blkLength); + if (gcryError) { + dbgprintf("gcry_cipher_setiv failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + ABORT_FINALIZE(RS_RET_ERR); + } + *pgf = gf; +finalize_it: + if(iRet != RS_RET_OK && gf != NULL) + gcryfileDestruct(gf); + RETiRet; +} + +int +rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len) +{ + int gcryError; + DEFiRet; + + if(*len == 0) + FINALIZE; + + addPadding(pF, buf, len); + gcryError = gcry_cipher_encrypt(pF->chd, buf, *len, NULL, 0); + if(gcryError) { + dbgprintf("gcry_cipher_encrypt failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + ABORT_FINALIZE(RS_RET_ERR); + } +finalize_it: + RETiRet; +} + + +/* module-init dummy for potential later use */ +int +rsgcryInit(void) +{ + return 0; +} + +/* module-deinit dummy for potential later use */ +void +rsgcryExit(void) +{ + return; +} + +#if 0 // we use this for the tool, only! +static void +doDeCrypt(FILE *fpin, FILE *fpout) +{ + gcry_error_t gcryError; + char buf[64*1024]; + size_t nRead, nWritten; + size_t nPad; + + while(1) { + nRead = fread(buf, 1, sizeof(buf), fpin); + if(nRead == 0) + break; + nPad = (blkLength - nRead % blkLength) % blkLength; + fprintf(stderr, "read %d chars, blkLength %d, mod %d, pad %d\n", nRead, blkLength, + nRead % blkLength, nPad); + gcryError = gcry_cipher_decrypt( + gcryCipherHd, // gcry_cipher_hd_t + buf, // void * + nRead, // size_t + NULL, // const void * + 0); // size_t + if (gcryError) { + fprintf(stderr, "gcry_cipher_encrypt failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + return; + } +fprintf(stderr, "in remove pad, %d\n", nRead); + removePadding(buf, &nRead); +fprintf(stderr, "out remove pad %d\n", nRead); + nWritten = fwrite(buf, 1, nRead, fpout); + if(nWritten != nRead) { + perror("fpout"); + return; + } + } +} +#endif diff --git a/runtime/libgcry.h b/runtime/libgcry.h new file mode 100644 index 00000000..0405162f --- /dev/null +++ b/runtime/libgcry.h @@ -0,0 +1,47 @@ +/* libgcry.h - rsyslog's guardtime support library + * + * Copyright 2013 Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef INCLUDED_LIBGCRY_H +#define INCLUDED_LIBGCRY_H +#include + + +struct gcryctx_s { + void *usrptr; /* for error function */ +}; +typedef struct gcryctx_s *gcryctx; +typedef struct gcryfile_s *gcryfile; + +/* this describes a file, as far as libgcry is concerned */ +struct gcryfile_s { + gcry_cipher_hd_t chd; /* cypher handle */ + size_t blkLength; /* size of low-level crypto block */ + gcryctx ctx; +}; + +int rsgcryInit(void); +void rsgcryExit(void); +gcryctx gcryCtxNew(void); +void rsgcryCtxDel(gcryctx ctx); +int gcryfileDestruct(gcryfile gf); +rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char * iniVector); +int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); + +#endif /* #ifndef INCLUDED_LIBGCRY_H */ diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c new file mode 100644 index 00000000..6800055d --- /dev/null +++ b/runtime/lmcry_gcry.c @@ -0,0 +1,224 @@ +/* lmcry_gcry.c + * + * An implementation of the cryprov interface for libgcrypt. + * + * Copyright 2013 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" + +#include "rsyslog.h" +#include +#include +#include + +#include "module-template.h" +#include "glbl.h" +#include "errmsg.h" +#include "cryprov.h" +#include "libgcry.h" +#include "lmcry_gcry.h" + +MODULE_TYPE_LIB +MODULE_TYPE_NOKEEP + +/* static data */ +DEFobjStaticHelpers +DEFobjCurrIf(errmsg) +DEFobjCurrIf(glbl) + +/* tables for interfacing with the v6 config system */ +static struct cnfparamdescr cnfpdescr[] = { + { "cry.mode", eCmdHdlrGetWord, 0 }, /* CBC, ECB, etc */ + { "cry.algo", eCmdHdlrGetWord, 0 } +}; +static struct cnfparamblk pblk = + { CNFPARAMBLK_VERSION, + sizeof(cnfpdescr)/sizeof(struct cnfparamdescr), + cnfpdescr + }; + + +#if 0 +static void +errfunc(__attribute__((unused)) void *usrptr, uchar *emsg) +{ + errmsg.LogError(0, RS_RET_CRYPROV_ERR, "Crypto Provider" + "Error: %s - disabling encryption", emsg); +} +#endif + +/* Standard-Constructor + */ +BEGINobjConstruct(lmcry_gcry) + dbgprintf("DDDD: lmcry_gcry: called construct\n"); + pThis->ctx = gcryCtxNew(); +ENDobjConstruct(lmcry_gcry) + + +/* destructor for the lmcry_gcry object */ +BEGINobjDestruct(lmcry_gcry) /* be sure to specify the object type also in END and CODESTART macros! */ +CODESTARTobjDestruct(lmcry_gcry) + dbgprintf("DDDD: lmcry_gcry: called destruct\n"); + rsgcryCtxDel(pThis->ctx); +ENDobjDestruct(lmcry_gcry) + + +/* apply all params from param block to us. This must be called + * after construction, but before the OnFileOpen() entry point. + * Defaults are expected to have been set during construction. + */ +rsRetVal +SetCnfParam(void *pT, struct nvlst *lst) +{ + lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; + int i; + uchar *cstr; + struct cnfparamvals *pvals; + pvals = nvlstGetParams(lst, &pblk, NULL); + if(Debug) { + dbgprintf("param blk in lmcry_gcry:\n"); + cnfparamsPrint(&pblk, pvals); + } + + for(i = 0 ; i < pblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; +#if 0 + if(!strcmp(pblk.descr[i].name, "sig.hashfunction")) { + cstr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + if(gcrySetHashFunction(pThis->ctx, (char*)cstr) != 0) { + errmsg.LogError(0, RS_RET_ERR, "Hash function " + "'%s' unknown - using default", cstr); + } + free(cstr); + } else if(!strcmp(pblk.descr[i].name, "sig.timestampservice")) { + cstr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + gcrySetTimestamper(pThis->ctx, (char*) cstr); + free(cstr); + } else if(!strcmp(pblk.descr[i].name, "sig.block.sizelimit")) { + gcrySetBlockSizeLimit(pThis->ctx, pvals[i].val.d.n); + } else if(!strcmp(pblk.descr[i].name, "sig.keeprecordhashes")) { + gcrySetKeepRecordHashes(pThis->ctx, pvals[i].val.d.n); + } else if(!strcmp(pblk.descr[i].name, "sig.keeptreehashes")) { + gcrySetKeepTreeHashes(pThis->ctx, pvals[i].val.d.n); + } else { + DBGPRINTF("lmcry_gcry: program error, non-handled " + "param '%s'\n", pblk.descr[i].name); + } +#endif + } + cnfparamvalsDestruct(pvals, &pblk); + return RS_RET_OK; +} + + +static rsRetVal +OnFileOpen(void *pT, uchar *fn, void *pGF) +{ + lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; + gcryfile *pgf = (gcryfile*) pGF; + DEFiRet; +dbgprintf("DDDD: cry: onFileOpen: %s\n", fn); + /* note: if *pgf is set to NULL, this auto-disables GT functions */ + //*pgf = gcryCtxOpenFile(pThis->ctx, fn); + + CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, GCRY_CIPHER_MODE_CBC, "TODO: init value")); +finalize_it: + RETiRet; +} + +static rsRetVal +Encrypt(void *pF, uchar *rec, size_t *lenRec) +{ + DEFiRet; +dbgprintf("DDDD: Encrypt (%u): %s\n", *lenRec-1, rec); + iRet = rsgcryEncrypt(pF, rec, lenRec); + + RETiRet; +} + +static rsRetVal +OnFileClose(void *pF) +{ + DEFiRet; +dbgprintf("DDDD: onFileClose\n"); + gcryfileDestruct(pF); + + RETiRet; +} + +BEGINobjQueryInterface(lmcry_gcry) +CODESTARTobjQueryInterface(lmcry_gcry) + if(pIf->ifVersion != cryprovCURR_IF_VERSION) {/* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + pIf->Construct = (rsRetVal(*)(void*)) lmcry_gcryConstruct; + pIf->SetCnfParam = SetCnfParam; + pIf->Destruct = (rsRetVal(*)(void*)) lmcry_gcryDestruct; + pIf->OnFileOpen = OnFileOpen; + pIf->Encrypt = Encrypt; + pIf->OnFileClose = OnFileClose; +finalize_it: +ENDobjQueryInterface(lmcry_gcry) + + +BEGINObjClassExit(lmcry_gcry, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */ +CODESTARTObjClassExit(lmcry_gcry) + /* release objects we no longer need */ + objRelease(errmsg, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); + + rsgcryExit(); +ENDObjClassExit(lmcry_gcry) + + +BEGINObjClassInit(lmcry_gcry, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); + + if(rsgcryInit() != 0) { + errmsg.LogError(0, RS_RET_CRYPROV_ERR, "error initializing " + "crypto provider - cannot encrypt"); + ABORT_FINALIZE(RS_RET_CRYPROV_ERR); + } +ENDObjClassInit(lmcry_gcry) + + +/* --------------- here now comes the plumbing that makes as a library module --------------- */ + + +BEGINmodExit +CODESTARTmodExit + lmcry_gcryClassExit(); +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_LIB_QUERIES +ENDqueryEtryPt + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ + /* Initialize all classes that are in our module - this includes ourselfs */ + CHKiRet(lmcry_gcryClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */ +ENDmodInit diff --git a/runtime/lmcry_gcry.h b/runtime/lmcry_gcry.h new file mode 100644 index 00000000..c0205ab9 --- /dev/null +++ b/runtime/lmcry_gcry.h @@ -0,0 +1,39 @@ +/* An implementation of the cryprov interface for libgcrypt. + * + * Copyright 2013 Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef INCLUDED_LMCRY_GCRY_H +#define INCLUDED_LMCRY_GCRY_H +#include "cryprov.h" + +/* interface is defined in cryprov.h, we just implement it! */ +#define lmcry_gcryCURR_IF_VERSION cryprovCURR_IF_VERSION +typedef cryprov_if_t lmcry_gcry_if_t; + +/* the lmcry_gcry object */ +struct lmcry_gcry_s { + BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ + gcryctx ctx; +}; +typedef struct lmcry_gcry_s lmcry_gcry_t; + +/* prototypes */ +PROTOTYPEObj(lmcry_gcry); + +#endif /* #ifndef INCLUDED_LMCRY_GCRY_H */ diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index 5ba6ede7..cbc0401b 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -403,6 +403,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_INVLD_ANON_BITS = -2312,/**< mmanon: invalid number of bits to anonymize specified */ RS_RET_REPLCHAR_IGNORED = -2313,/**< mmanon: replacementChar parameter is ignored */ RS_RET_SIGPROV_ERR = -2320,/**< error in signature provider */ + RS_RET_CRYPROV_ERR = -2321,/**< error in cryptography encryption provider */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ diff --git a/runtime/stream.c b/runtime/stream.c index 00afcdaa..941fc39d 100644 --- a/runtime/stream.c +++ b/runtime/stream.c @@ -16,7 +16,7 @@ * it turns out to be problematic. Then, we need to quasi-refcount the number of accesses * to the object. * - * Copyright 2008-2012 Rainer Gerhards and Adiscon GmbH. + * Copyright 2008-2013 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * @@ -56,6 +56,7 @@ #include "stream.h" #include "unicode-helper.h" #include "module-template.h" +#include "cryprov.h" #if HAVE_SYS_PRCTL_H # include #endif @@ -253,6 +254,12 @@ doPhysOpen(strm_t *pThis) pThis->bIsTTY = 0; } +dbgprintf("DDDD: cryprov %p\n", pThis->cryprov); + if(pThis->cryprov != NULL) { + iRet = pThis->cryprov->OnFileOpen(pThis->cryprovData, + pThis->pszCurrFName, &pThis->cryprovFileData); +dbgprintf("DDDD: iREt cryprov->onFileOpen: %d\n", iRet); + } finalize_it: RETiRet; } @@ -405,6 +412,10 @@ static rsRetVal strmCloseFile(strm_t *pThis) close(pThis->fd); pThis->fd = -1; pThis->inode = 0; + if(pThis->cryprov != NULL) { + pThis->cryprov->OnFileClose(pThis->cryprovFileData); + pThis->cryprovFileData = NULL; + } } if(pThis->fdDir != -1) { @@ -1200,10 +1211,18 @@ strmPhysWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf) DEFiRet; ISOBJ_TYPE_assert(pThis, strm); - DBGPRINTF("strmPhysWrite, stream %p, len %d\n", pThis, (int) lenBuf); + DBGPRINTF("strmPhysWrite, stream %p, len %u\n", pThis, (unsigned)lenBuf); if(pThis->fd == -1) CHKiRet(strmOpenFile(pThis)); + /* here we place our crypto interface */ +dbgprintf("DDDD: doing crypto, len %d\n", lenBuf); + if(pThis->cryprov != NULL) { + pThis->cryprov->Encrypt(pThis->cryprovFileData, pBuf, &lenBuf); + } +dbgprintf("DDDD: done crypto, len %d\n", lenBuf); + /* end crypto */ + iWritten = lenBuf; CHKiRet(doWriteCall(pThis, pBuf, &iWritten)); @@ -1600,6 +1619,8 @@ DEFpropSetMeth(strm, sIOBufSize, size_t) DEFpropSetMeth(strm, iSizeLimit, off_t) DEFpropSetMeth(strm, iFlushInterval, int) DEFpropSetMeth(strm, pszSizeLimitCmd, uchar*) +DEFpropSetMeth(strm, cryprov, cryprov_if_t*) +DEFpropSetMeth(strm, cryprovData, void*) static rsRetVal strmSetiMaxFiles(strm_t *pThis, int iNewVal) { @@ -1935,6 +1956,8 @@ CODESTARTobjQueryInterface(strm) pIf->SetiSizeLimit = strmSetiSizeLimit; pIf->SetiFlushInterval = strmSetiFlushInterval; pIf->SetpszSizeLimitCmd = strmSetpszSizeLimitCmd; + pIf->Setcryprov = strmSetcryprov; + pIf->SetcryprovData = strmSetcryprovData; finalize_it: ENDobjQueryInterface(strm) diff --git a/runtime/stream.h b/runtime/stream.h index b7cc6d36..61d5ede2 100644 --- a/runtime/stream.h +++ b/runtime/stream.h @@ -41,7 +41,7 @@ * deflateInit2(zstrmptr, 6, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY); * -------------------------------------------------------------------------- * - * Copyright 2008, 2009 Rainer Gerhards and Adiscon GmbH. + * Copyright 2008-2013 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * @@ -70,6 +70,7 @@ #include "glbl.h" #include "stream.h" #include "zlibw.h" +#include "cryprov.h" /* stream types */ typedef enum { @@ -134,6 +135,9 @@ typedef struct strm_s { pthread_cond_t isEmpty; unsigned short iEnq; /* this MUST be unsigned as we use module arithmetic (else invalid indexing happens!) */ unsigned short iDeq; /* this MUST be unsigned as we use module arithmetic (else invalid indexing happens!) */ + cryprov_if_t *cryprov; /* ptr to crypto provider; NULL = do not encrypt */ + void *cryprovData; /* opaque data ptr for provider use */ + void *cryprovFileData;/* opaque data ptr for file instance */ short iCnt; /* current nbr of elements in buffer */ z_stream zstrm; /* zip stream to use */ struct { @@ -190,8 +194,11 @@ BEGINinterface(strm) /* name must also be changed in ENDinterface macro! */ INTERFACEpropSetMeth(strm, bVeryReliableZip, int); /* v8 added 2013-03-21 */ rsRetVal (*CheckFileChange)(strm_t *pThis); + /* v9 added 2013-04-04 */ + INTERFACEpropSetMeth(strm, cryprov, cryprov_if_t*); + INTERFACEpropSetMeth(strm, cryprovData, void*); ENDinterface(strm) -#define strmCURR_IF_VERSION 8 /* increment whenever you change the interface structure! */ +#define strmCURR_IF_VERSION 9 /* increment whenever you change the interface structure! */ static inline int strmGetCurrFileNum(strm_t *pStrm) { diff --git a/tools/Makefile.am b/tools/Makefile.am index 21a32868..be093957 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -41,7 +41,7 @@ rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) # otherwise dependencies are not properly calculated (resulting in a # potentially incomplete build, a problem we had several times...) rsyslogd_LDADD = ../grammar/libgrammar.la ../runtime/librsyslog.la $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBEE_LIBS) $(LIBLOGNORM_LIBS) $(LIBUUID_LIBS) -rsyslogd_LDFLAGS = -export-dynamic +rsyslogd_LDFLAGS = -export-dynamic `libgcrypt-config --libs` EXTRA_DIST = $(man_MANS) \ rsgtutil.rst \ diff --git a/tools/omfile.c b/tools/omfile.c index faf3c24f..dfd52d80 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -70,6 +70,7 @@ #include "atomic.h" #include "statsobj.h" #include "sigprov.h" +#include "cryprov.h" MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP @@ -151,6 +152,12 @@ typedef struct _instanceData { void *sigprovData; /* opaque data ptr for provider use */ void *sigprovFileData;/* opaque data ptr for file instance */ sbool useSigprov; /* quicker than checkig ptr (1 vs 8 bytes!) */ + uchar *cryprovName; /* crypto provider */ + uchar *cryprovNameFull;/* full internal crypto provider name */ + void *cryprovData; /* opaque data ptr for provider use */ + void *cryprovFileData;/* opaque data ptr for file instance */ + cryprov_if_t cryprov; /* ptr to crypto provider interface */ + sbool useCryprov; /* quicker than checkig ptr (1 vs 8 bytes!) */ int iCurrElt; /* currently active cache element (-1 = none) */ int iCurrCacheSize; /* currently cache size (1-based) */ int iDynaFileCacheSize; /* size of file handle cache */ @@ -237,6 +244,7 @@ static struct cnfparamdescr actpdescr[] = { { "file", eCmdHdlrString, 0 }, /* either "file" or ... */ { "dynafile", eCmdHdlrString, 0 }, /* "dynafile" MUST be present */ { "sig.provider", eCmdHdlrGetWord, 0 }, + { "cry.provider", eCmdHdlrGetWord, 0 }, { "template", eCmdHdlrGetWord, 0 } }; static struct cnfparamblk actpblk = @@ -589,6 +597,10 @@ prepareFile(instanceData *pData, uchar *newFileName) CHKiRet(strm.SetbSync(pData->pStrm, pData->bSyncFile)); CHKiRet(strm.SetsType(pData->pStrm, STREAMTYPE_FILE_SINGLE)); CHKiRet(strm.SetiSizeLimit(pData->pStrm, pData->iSizeLimit)); + if(pData->useCryprov) { + CHKiRet(strm.Setcryprov(pData->pStrm, &pData->cryprov)); + CHKiRet(strm.SetcryprovData(pData->pStrm, pData->cryprovData)); + } /* set the flush interval only if we actually use it - otherwise it will activate * async processing, which is a real performance waste if we do not do buffered * writes! -- rgerhards, 2009-07-06 @@ -689,7 +701,7 @@ prepareDynFile(instanceData *pData, uchar *newFileName, unsigned iMsgOpts) * but it could be triggered in the common case of a failed open() system call. * rgerhards, 2010-03-22 */ - pData->pStrm = pData->sigprovFileData = NULL; + pData->pStrm = NULL, pData->sigprovFileData = NULL; if(iFirstFree == -1 && (pData->iCurrCacheSize < pData->iDynaFileCacheSize)) { /* there is space left, so set it to that index */ @@ -885,6 +897,13 @@ CODESTARTfreeInstance free(pData->sigprovName); free(pData->sigprovNameFull); } + if(pData->useCryprov) { + pData->cryprov.Destruct(&pData->cryprovData); + obj.ReleaseObj(__FILE__, pData->cryprovNameFull+2, pData->cryprovNameFull, + (void*) &pData->cryprov); + free(pData->cryprovName); + free(pData->cryprovNameFull); + } ENDfreeInstance @@ -951,7 +970,9 @@ setInstParamDefaults(instanceData *pData) pData->iFlushInterval = FLUSH_INTRVL_DFLT; pData->bUseAsyncWriter = USE_ASYNCWRITER_DFLT; pData->sigprovName = NULL; + pData->cryprovName = NULL; pData->useSigprov = 0; + pData->useCryprov = 0; } @@ -1033,6 +1054,48 @@ initSigprov(instanceData *pData, struct nvlst *lst) done: return; } +static inline void +initCryprov(instanceData *pData, struct nvlst *lst) +{ + uchar szDrvrName[1024]; + + if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmcry_%s", pData->cryprovName) + == sizeof(szDrvrName)) { + errmsg.LogError(0, RS_RET_ERR, "omfile: crypto provider " + "name is too long: '%s' - encryption disabled", + pData->cryprovName); + goto done; + } + pData->cryprovNameFull = ustrdup(szDrvrName); + + pData->cryprov.ifVersion = cryprovCURR_IF_VERSION; + /* The pDrvrName+2 below is a hack to obtain the object name. It + * safes us to have yet another variable with the name without "lm" in + * front of it. If we change the module load interface, we may re-think + * about this hack, but for the time being it is efficient and clean enough. + */ + if(obj.UseObj(__FILE__, szDrvrName, szDrvrName, (void*) &pData->cryprov) + != RS_RET_OK) { + errmsg.LogError(0, RS_RET_LOAD_ERROR, "omfile: could not load " + "crypto provider '%s' - encryption disabled", + szDrvrName); + goto done; + } + + if(pData->cryprov.Construct(&pData->cryprovData) != RS_RET_OK) { + errmsg.LogError(0, RS_RET_SIGPROV_ERR, "omfile: error constructing " + "crypto provider %s dataset - encryption disabled", + szDrvrName); + goto done; + } + pData->cryprov.SetCnfParam(pData->cryprovData, lst); + + dbgprintf("loaded crypto provider %s, data instance at %p\n", + szDrvrName, pData->cryprovData); + pData->useCryprov = 1; +done: return; +} + BEGINnewActInst struct cnfparamvals *pvals; uchar *tplToUse; @@ -1102,6 +1165,8 @@ CODESTARTnewActInst pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(actpblk.descr[i].name, "sig.provider")) { pData->sigprovName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "cry.provider")) { + pData->cryprovName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else { dbgprintf("omfile: program error, non-handled " "param '%s'\n", actpblk.descr[i].name); @@ -1118,6 +1183,10 @@ CODESTARTnewActInst initSigprov(pData, lst); } + if(pData->cryprovName != NULL) { + initCryprov(pData, lst); + } + tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName); CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, OMSR_NO_RQD_TPL_OPTS)); -- cgit v1.2.3 From 5242a0b4a351d41ea6f20adc359bbcde8e1b3cfe Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 5 Apr 2013 18:52:52 +0200 Subject: log encryption: add rscrytool to decrypt log files also add test parameter to be able to set key --- runtime/libgcry.c | 27 ++++-- runtime/libgcry.h | 4 +- runtime/lmcry_gcry.c | 31 ++++--- runtime/stream.c | 5 +- tools/Makefile.am | 17 +++- tools/omfile.c | 18 ++-- tools/rscryutil.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/rscryutil.rst | 80 +++++++++++++++++ 8 files changed, 386 insertions(+), 32 deletions(-) create mode 100644 tools/rscryutil.c create mode 100644 tools/rscryutil.rst diff --git a/runtime/libgcry.c b/runtime/libgcry.c index 8184c160..5f1dbf58 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -27,6 +27,7 @@ #include "rsyslog.h" #include "libgcry.h" +#define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable static inline gcryfile gcryfileConstruct(gcryctx ctx) @@ -98,20 +99,34 @@ removePadding(char *buf, size_t *plen) done: return; } +/* returns 0 on succes, positive if key length does not match and key + * of return value size is required. + */ +int +rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen) +{ + uint16_t reqKeyLen = gcry_cipher_get_algo_keylen(GCRY_CIPHER); + int r; + + if(keyLen != reqKeyLen) + r = reqKeyLen; + ctx->keyLen = keyLen; + ctx->key = malloc(keyLen); + memcpy(ctx->key, key, keyLen); + r = 0; +done: return r; +} + rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char *iniVector) { - #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable - size_t keyLength; - char *aesSymKey = "123456789012345678901234"; // TODO: TEST ONLY - gcry_error_t gcryError; + gcry_error_t gcryError; gcryfile gf = NULL; DEFiRet; CHKmalloc(gf = gcryfileConstruct(ctx)); gf->blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); - keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER); gcryError = gcry_cipher_open( &gf->chd, // gcry_cipher_hd_t * @@ -125,7 +140,7 @@ rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char *iniVector) ABORT_FINALIZE(RS_RET_ERR); } - gcryError = gcry_cipher_setkey(gf->chd, aesSymKey, keyLength); + gcryError = gcry_cipher_setkey(gf->chd, gf->ctx->key, gf->ctx->keyLen); if (gcryError) { dbgprintf("gcry_cipher_setkey failed: %s/%s\n", gcry_strsource(gcryError), diff --git a/runtime/libgcry.h b/runtime/libgcry.h index 0405162f..608abd6c 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -24,7 +24,8 @@ struct gcryctx_s { - void *usrptr; /* for error function */ + uchar *key; + size_t keyLen; }; typedef struct gcryctx_s *gcryctx; typedef struct gcryfile_s *gcryfile; @@ -38,6 +39,7 @@ struct gcryfile_s { int rsgcryInit(void); void rsgcryExit(void); +int rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen); gcryctx gcryCtxNew(void); void rsgcryCtxDel(gcryctx ctx); int gcryfileDestruct(gcryfile gf); diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index 6800055d..ce0fef2f 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -44,6 +44,7 @@ DEFobjCurrIf(glbl) /* tables for interfacing with the v6 config system */ static struct cnfparamdescr cnfpdescr[] = { + { "cry.key", eCmdHdlrGetWord, 0 }, { "cry.mode", eCmdHdlrGetWord, 0 }, /* CBC, ECB, etc */ { "cry.algo", eCmdHdlrGetWord, 0 } }; @@ -83,12 +84,13 @@ ENDobjDestruct(lmcry_gcry) * after construction, but before the OnFileOpen() entry point. * Defaults are expected to have been set during construction. */ -rsRetVal +static rsRetVal SetCnfParam(void *pT, struct nvlst *lst) { lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; - int i; + int i, r; uchar *cstr; + uchar *key = NULL; struct cnfparamvals *pvals; pvals = nvlstGetParams(lst, &pblk, NULL); if(Debug) { @@ -99,14 +101,9 @@ SetCnfParam(void *pT, struct nvlst *lst) for(i = 0 ; i < pblk.nParams ; ++i) { if(!pvals[i].bUsed) continue; + if(!strcmp(pblk.descr[i].name, "cry.key")) { + key = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); #if 0 - if(!strcmp(pblk.descr[i].name, "sig.hashfunction")) { - cstr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); - if(gcrySetHashFunction(pThis->ctx, (char*)cstr) != 0) { - errmsg.LogError(0, RS_RET_ERR, "Hash function " - "'%s' unknown - using default", cstr); - } - free(cstr); } else if(!strcmp(pblk.descr[i].name, "sig.timestampservice")) { cstr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); gcrySetTimestamper(pThis->ctx, (char*) cstr); @@ -120,10 +117,24 @@ SetCnfParam(void *pT, struct nvlst *lst) } else { DBGPRINTF("lmcry_gcry: program error, non-handled " "param '%s'\n", pblk.descr[i].name); - } #endif + } } + if(key != NULL) { + errmsg.LogError(0, RS_RET_ERR, "Note: specifying an actual key directly from the " + "config file is highly insecure - DO NOT USE FOR PRODUCTION"); + r = rsgcrySetKey(pThis->ctx, key, strlen((char*)key)); + if(r > 0) { + errmsg.LogError(0, RS_RET_ERR, "Key length %d expected, but " + "key of length %d given", r, strlen((char*)key)); + } + } + cnfparamvalsDestruct(pvals, &pblk); + if(key != NULL) { + memset(key, 0, strlen((char*)key)); + free(key); + } return RS_RET_OK; } diff --git a/runtime/stream.c b/runtime/stream.c index 941fc39d..b31520b0 100644 --- a/runtime/stream.c +++ b/runtime/stream.c @@ -256,9 +256,8 @@ doPhysOpen(strm_t *pThis) dbgprintf("DDDD: cryprov %p\n", pThis->cryprov); if(pThis->cryprov != NULL) { - iRet = pThis->cryprov->OnFileOpen(pThis->cryprovData, - pThis->pszCurrFName, &pThis->cryprovFileData); -dbgprintf("DDDD: iREt cryprov->onFileOpen: %d\n", iRet); + CHKiRet(pThis->cryprov->OnFileOpen(pThis->cryprovData, + pThis->pszCurrFName, &pThis->cryprovFileData)); } finalize_it: RETiRet; diff --git a/tools/Makefile.am b/tools/Makefile.am index be093957..8957d713 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -42,6 +42,7 @@ rsyslogd_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) # potentially incomplete build, a problem we had several times...) rsyslogd_LDADD = ../grammar/libgrammar.la ../runtime/librsyslog.la $(ZLIB_LIBS) $(PTHREADS_LIBS) $(RSRT_LIBS) $(SOL_LIBS) $(LIBEE_LIBS) $(LIBLOGNORM_LIBS) $(LIBUUID_LIBS) rsyslogd_LDFLAGS = -export-dynamic `libgcrypt-config --libs` +#rsyslogd_LDFLAGS = -export-dynamic $(LIBGCRYPT_LIBS) EXTRA_DIST = $(man_MANS) \ rsgtutil.rst \ @@ -64,10 +65,6 @@ logctl_LDADD = $(LIBMONGO_CLIENT_LIBS) endif if ENABLE_GUARDTIME bin_PROGRAMS += rsgtutil -#bin_PROGRAMS += logsigner rsgtutil -#logsigner = logsigner.c -#logsigner_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) -#logsigner_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) rsgtutil = rsgtutil.c rsgtutil_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) rsgtutil_LDADD = ../runtime/librsgt.la $(GUARDTIME_LIBS) @@ -77,4 +74,16 @@ man1_MANS = rsgtutil.1 CLEANFILES = rsgtutil.1 EXTRA_DIST+= rsgtutil.1 endif +if ENABLE_LIBGCRYPT +bin_PROGRAMS += rscryutil +rscryutil = rscryutil.c +rscryutil_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) +rscryutil_LDFLAGS = `libgcrypt-config --libs` +#rscryutil_LDFLAGS = $(LIBGCRYPT_LIBS) +rscryutil.1: rscryutil.rst + $(AM_V_GEN) $(RST2MAN) $< $@ +man1_MANS = rscryutil.1 +CLEANFILES = rscryutil.1 +EXTRA_DIST+= rscryutil.1 +endif endif diff --git a/tools/omfile.c b/tools/omfile.c index dfd52d80..46d882bf 100644 --- a/tools/omfile.c +++ b/tools/omfile.c @@ -1054,17 +1054,18 @@ initSigprov(instanceData *pData, struct nvlst *lst) done: return; } -static inline void +static inline rsRetVal initCryprov(instanceData *pData, struct nvlst *lst) { uchar szDrvrName[1024]; + DEFiRet; if(snprintf((char*)szDrvrName, sizeof(szDrvrName), "lmcry_%s", pData->cryprovName) == sizeof(szDrvrName)) { errmsg.LogError(0, RS_RET_ERR, "omfile: crypto provider " "name is too long: '%s' - encryption disabled", pData->cryprovName); - goto done; + ABORT_FINALIZE(RS_RET_ERR); } pData->cryprovNameFull = ustrdup(szDrvrName); @@ -1079,21 +1080,22 @@ initCryprov(instanceData *pData, struct nvlst *lst) errmsg.LogError(0, RS_RET_LOAD_ERROR, "omfile: could not load " "crypto provider '%s' - encryption disabled", szDrvrName); - goto done; + ABORT_FINALIZE(RS_RET_CRYPROV_ERR); } if(pData->cryprov.Construct(&pData->cryprovData) != RS_RET_OK) { - errmsg.LogError(0, RS_RET_SIGPROV_ERR, "omfile: error constructing " + errmsg.LogError(0, RS_RET_CRYPROV_ERR, "omfile: error constructing " "crypto provider %s dataset - encryption disabled", szDrvrName); - goto done; + ABORT_FINALIZE(RS_RET_CRYPROV_ERR); } - pData->cryprov.SetCnfParam(pData->cryprovData, lst); + CHKiRet(pData->cryprov.SetCnfParam(pData->cryprovData, lst)); dbgprintf("loaded crypto provider %s, data instance at %p\n", szDrvrName, pData->cryprovData); pData->useCryprov = 1; -done: return; +finalize_it: + RETiRet; } BEGINnewActInst @@ -1184,7 +1186,7 @@ CODESTARTnewActInst } if(pData->cryprovName != NULL) { - initCryprov(pData, lst); + CHKiRet(initCryprov(pData, lst)); } tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName); diff --git a/tools/rscryutil.c b/tools/rscryutil.c new file mode 100644 index 00000000..755371f2 --- /dev/null +++ b/tools/rscryutil.c @@ -0,0 +1,236 @@ +/* This is a tool for dumpoing the content of GuardTime TLV + * files in a (somewhat) human-readable manner. + * + * Copyright 2013 Adiscon GmbH + * + * This file is part of rsyslog. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either exprs or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include + + +static enum { MD_DECRYPT +} mode = MD_DECRYPT; +static int verbose = 0; +static gcry_cipher_hd_t gcry_chd; +static size_t blkLength; + +static int +initCrypt(int gcry_mode, char *iv, char *key) +{ + #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable + int r = 0; + gcry_error_t gcryError; + + blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); + size_t keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER); + if(strlen(key) != keyLength) { + fprintf(stderr, "invalid key lengtjh; key is %u characters, but " + "exactly %u characters are required\n", strlen(key), + keyLength); + r = 1; goto done; + } + + gcryError = gcry_cipher_open(&gcry_chd, GCRY_CIPHER, gcry_mode, 0); + if (gcryError) { + printf("gcry_cipher_open failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + r = 1; goto done; + } + + gcryError = gcry_cipher_setkey(gcry_chd, key, keyLength); + if (gcryError) { + printf("gcry_cipher_setkey failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + r = 1; goto done; + } + + gcryError = gcry_cipher_setiv(gcry_chd, iv, blkLength); + if (gcryError) { + printf("gcry_cipher_setiv failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + r = 1; goto done; + } +done: return r; +} + +static inline void +removePadding(char *buf, size_t *plen) +{ + unsigned len = (unsigned) *plen; + unsigned iSrc, iDst; + char *frstNUL; + + frstNUL = strchr(buf, 0x00); + if(frstNUL == NULL) + goto done; + iDst = iSrc = frstNUL - buf; + + while(iSrc < len) { + if(buf[iSrc] != 0x00) + buf[iDst++] = buf[iSrc]; + ++iSrc; + } + + *plen = iDst; +done: return; +} + +static void +doDeCrypt(FILE *fpin, FILE *fpout) +{ + gcry_error_t gcryError; + char buf[64*1024]; + size_t nRead, nWritten; + size_t nPad; + + while(1) { + nRead = fread(buf, 1, sizeof(buf), fpin); + if(nRead == 0) + break; + nPad = (blkLength - nRead % blkLength) % blkLength; + fprintf(stderr, "--->read %d chars, blkLength %d, mod %d, pad %d\n", nRead, blkLength, + nRead % blkLength, nPad); + gcryError = gcry_cipher_decrypt( + gcry_chd, // gcry_cipher_hd_t + buf, // void * + nRead, // size_t + NULL, // const void * + 0); // size_t + if (gcryError) { + fprintf(stderr, "gcry_cipher_encrypt failed: %s/%s\n", + gcry_strsource(gcryError), + gcry_strerror(gcryError)); + return; + } + removePadding(buf, &nRead); + nWritten = fwrite(buf, 1, nRead, fpout); + if(nWritten != nRead) { + perror("fpout"); + return; + } + } +} + + +static void +decrypt(char *name, char *key) +{ + FILE *logfp = NULL; + //, *sigfp = NULL; + int r = 0; + //char sigfname[4096]; + + if(!strcmp(name, "-")) { + fprintf(stderr, "decrypt mode cannot work on stdin\n"); + goto err; + } else { + if((logfp = fopen(name, "r")) == NULL) { + perror(name); + goto err; + } +#if 0 + snprintf(sigfname, sizeof(sigfname), "%s.gtsig", name); + sigfname[sizeof(sigfname)-1] = '\0'; + if((sigfp = fopen(sigfname, "r")) == NULL) { + perror(sigfname); + goto err; + } +#endif + } + + if(initCrypt(GCRY_CIPHER_MODE_CBC, "TODO: init value", key) != 0) + goto err; + doDeCrypt(logfp, stdout); + gcry_cipher_close(gcry_chd); + fclose(logfp); logfp = NULL; + return; + +err: + fprintf(stderr, "error %d processing file %s\n", r, name); + if(logfp != NULL) + fclose(logfp); +} + + +static struct option long_options[] = +{ + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"decrypt", no_argument, NULL, 'd'}, + {"key", required_argument, NULL, 'k'}, + {NULL, 0, NULL, 0} +}; + +int +main(int argc, char *argv[]) +{ + int i; + int opt; + char *key = ""; + + while(1) { + opt = getopt_long(argc, argv, "dk:vV", long_options, NULL); + if(opt == -1) + break; + switch(opt) { + case 'd': + mode = MD_DECRYPT; + break; + case 'k': + fprintf(stderr, "WARNING: specifying the actual key " + "via the command line is highly insecure\n" + "Do NOT use this for PRODUCTION use.\n"); + key = optarg; + break; + case 'v': + verbose = 1; + break; + case 'V': + fprintf(stderr, "rsgtutil " VERSION "\n"); + exit(0); + break; + case '?': + break; + default:fprintf(stderr, "getopt_long() returns unknown value %d\n", opt); + return 1; + } + } + + if(optind == argc) + decrypt("-", key); + else { + for(i = optind ; i < argc ; ++i) + decrypt(argv[i], key); /* currently only mode ;) */ + } + + memset(key, 0, strlen(key)); /* zero-out key store */ + return 0; +} + //char *aesSymKey = "123456789012345678901234"; // TODO: TEST ONLY diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst new file mode 100644 index 00000000..7e3ab5b4 --- /dev/null +++ b/tools/rscryutil.rst @@ -0,0 +1,80 @@ +========= +rscryutil +========= + +-------------------------- +Manage Encrypted Log Files +-------------------------- + +:Author: Rainer Gerhards +:Date: 2013-04-08 +:Manual section: 1 + +SYNOPSIS +======== + +:: + + rscryutil [OPTIONS] [FILE] ... + + +DESCRIPTION +=========== + +This tool performs various operations on encrypted log files. +Most importantly, it provides the ability to decrypt them. + + +OPTIONS +======= + +-d, --decrypt + Select decryption mode. This is the default mode. + +-v, --verbose + Select verbose mode. + +-k, --key + TESTING AID, NOT FOR PRODUCTION USE. This uses the KEY specified + on the command line. This is the actual key, and as such this mode + is highly insecure. However, it can be useful for intial testing + steps. This option may be removed in the future. + + +OPERATION MODES +=============== + +The operation mode specifies what exactly the tool does with the provided +files. The default operation mode is "dump", but this may change in the future. +Thus, it is recommended to always set the operations mode explicitely. If +multiple operations mode are set on the command line, results are +unpredictable. + +decrypt +------- + +The provided log files are decrypted. + +EXIT CODES +========== + +The command returns an exit code of 0 if everything went fine, and some +other code in case of failures. + + +EXAMPLES +======== + +**rscryutil logfile** + +Decrypts "logfile" and sends data to stdout. + +SEE ALSO +======== +**rsyslogd(8)** + +COPYRIGHT +========= + +This page is part of the *rsyslog* project, and is available under +LGPLv2. -- cgit v1.2.3 From 078b010af6317ad325a9f10a901f8a279c8d0899 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 11 Apr 2013 09:57:18 +0200 Subject: log encryption: check length of "key" parameter --- runtime/Makefile.am | 2 +- runtime/libgcry.c | 4 +++- runtime/lmcry_gcry.c | 8 ++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/runtime/Makefile.am b/runtime/Makefile.am index be68ce60..e1f0673c 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -184,7 +184,7 @@ if ENABLE_LIBGCRYPT pkglib_LTLIBRARIES += lmcry_gcry.la lmcry_gcry_la_SOURCES = lmcry_gcry.c lmcry_gcry.h libgcry.c libgcry.h lmcry_gcry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) - lmcry_gcry_la_LDFLAGS = -module -avoid-version + lmcry_gcry_la_LDFLAGS = -module -avoid-version `libgcrypt-config --libs` #lmcry_gcry_la_LIBADD = libgcry.la $(LIBGCRYPT_LIBS) lmcry_gcry_la_LIBADD = $(LIBGCRYPT_LIBS) endif diff --git a/runtime/libgcry.c b/runtime/libgcry.c index 5f1dbf58..c1ab3abf 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -108,8 +108,10 @@ rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen) uint16_t reqKeyLen = gcry_cipher_get_algo_keylen(GCRY_CIPHER); int r; - if(keyLen != reqKeyLen) + if(keyLen != reqKeyLen) { r = reqKeyLen; + goto done; + } ctx->keyLen = keyLen; ctx->key = malloc(keyLen); memcpy(ctx->key, key, keyLen); diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index ce0fef2f..31e648fc 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -92,6 +92,8 @@ SetCnfParam(void *pT, struct nvlst *lst) uchar *cstr; uchar *key = NULL; struct cnfparamvals *pvals; + DEFiRet; + pvals = nvlstGetParams(lst, &pblk, NULL); if(Debug) { dbgprintf("param blk in lmcry_gcry:\n"); @@ -125,8 +127,9 @@ SetCnfParam(void *pT, struct nvlst *lst) "config file is highly insecure - DO NOT USE FOR PRODUCTION"); r = rsgcrySetKey(pThis->ctx, key, strlen((char*)key)); if(r > 0) { - errmsg.LogError(0, RS_RET_ERR, "Key length %d expected, but " + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "Key length %d expected, but " "key of length %d given", r, strlen((char*)key)); + ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } } @@ -135,7 +138,8 @@ SetCnfParam(void *pT, struct nvlst *lst) memset(key, 0, strlen((char*)key)); free(key); } - return RS_RET_OK; +finalize_it: + RETiRet; } -- cgit v1.2.3 From 7ab02dce5c60453c454dd8645a63a5ef33f6a546 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 11 Apr 2013 10:08:05 +0200 Subject: cleanup --- runtime/libgcry.c | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/runtime/libgcry.c b/runtime/libgcry.c index c1ab3abf..af70e581 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -199,43 +199,3 @@ rsgcryExit(void) { return; } - -#if 0 // we use this for the tool, only! -static void -doDeCrypt(FILE *fpin, FILE *fpout) -{ - gcry_error_t gcryError; - char buf[64*1024]; - size_t nRead, nWritten; - size_t nPad; - - while(1) { - nRead = fread(buf, 1, sizeof(buf), fpin); - if(nRead == 0) - break; - nPad = (blkLength - nRead % blkLength) % blkLength; - fprintf(stderr, "read %d chars, blkLength %d, mod %d, pad %d\n", nRead, blkLength, - nRead % blkLength, nPad); - gcryError = gcry_cipher_decrypt( - gcryCipherHd, // gcry_cipher_hd_t - buf, // void * - nRead, // size_t - NULL, // const void * - 0); // size_t - if (gcryError) { - fprintf(stderr, "gcry_cipher_encrypt failed: %s/%s\n", - gcry_strsource(gcryError), - gcry_strerror(gcryError)); - return; - } -fprintf(stderr, "in remove pad, %d\n", nRead); - removePadding(buf, &nRead); -fprintf(stderr, "out remove pad %d\n", nRead); - nWritten = fwrite(buf, 1, nRead, fpout); - if(nWritten != nRead) { - perror("fpout"); - return; - } - } -} -#endif -- cgit v1.2.3 From 5f72832b3d6c2acd076b9ebfd3484877fc506ebc Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 11 Apr 2013 14:49:06 +0200 Subject: logenc: milestone: rsyslog writes .encinfo side files --- runtime/cryprov.h | 2 +- runtime/libgcry.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++--- runtime/libgcry.h | 12 ++- runtime/lmcry_gcry.c | 12 +-- runtime/rsyslog.h | 4 + runtime/stream.c | 5 +- 6 files changed, 229 insertions(+), 19 deletions(-) diff --git a/runtime/cryprov.h b/runtime/cryprov.h index c5ee95a0..5b694f46 100644 --- a/runtime/cryprov.h +++ b/runtime/cryprov.h @@ -33,7 +33,7 @@ BEGINinterface(cryprov) /* name must also be changed in ENDinterface macro! */ rsRetVal (*Destruct)(void *ppThis); rsRetVal (*OnFileOpen)(void *pThis, uchar *fn, void *pFileInstData); rsRetVal (*Encrypt)(void *pFileInstData, uchar *buf, size_t *lenBuf); - rsRetVal (*OnFileClose)(void *pFileInstData); + rsRetVal (*OnFileClose)(void *pFileInstData, off64_t offsLogfile); ENDinterface(cryprov) #define cryprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ rsRetVal initCrypt(int gcry_mode, char * iniVector); diff --git a/runtime/libgcry.c b/runtime/libgcry.c index af70e581..94e087ac 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -2,6 +2,22 @@ * * Copyright 2013 Adiscon GmbH. * + * We need to store some additional information in support of encryption. + * For this, we create a side-file, which is named like the actual log + * file, but with the suffix ".encinfo" appended. It contains the following + * records: + * IV: The initial vector used at block start. Also indicates start + * start of block. + * END: The end offset of the block, as uint64_t in decimal notation. + * This is used during encryption to know when the current + * encryption block ends. + * For the current implementation, there must always be an IV record + * followed by an END record. Each records is LF-terminated. Record + * types can simply be extended in the future by specifying new + * keywords (like "IV") before the colon. + * To identify a file as rsyslog encryption info file, it must start with + * the line "FILETYPE:rsyslog-enrcyption-info" + * * This file is part of rsyslog. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,21 +39,166 @@ #endif #include #include +#include +#include +#include +#include #include "rsyslog.h" #include "libgcry.h" #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable -static inline gcryfile -gcryfileConstruct(gcryctx ctx) + +static rsRetVal +eiWriteRec(gcryfile gf, char *recHdr, size_t lenRecHdr, char *buf, size_t lenBuf) +{ + struct iovec iov[3]; + ssize_t nwritten, towrite; + DEFiRet; + + iov[0].iov_base = recHdr; + iov[0].iov_len = lenRecHdr; + iov[1].iov_base = buf; + iov[1].iov_len = lenBuf; + iov[2].iov_base = "\n"; + iov[2].iov_len = 1; + towrite = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; + nwritten = writev(gf->fd, iov, sizeof(iov)/sizeof(struct iovec)); + if(nwritten != towrite) { + DBGPRINTF("eiWrite%s: error writing file, towrite %d, " + "nwritten %d\n", recHdr, (int) towrite, (int) nwritten); + ABORT_FINALIZE(RS_RET_EI_WR_ERR); + } + DBGPRINTF("encryption info file %s: written %s, len %d\n", + recHdr, gf->eiName, (int) nwritten); +finalize_it: + RETiRet; +} + +static rsRetVal +eiOpenRead(gcryfile gf) +{ + DEFiRet; + gf->fd = open((char*)gf->eiName, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if(gf->fd == -1) { + ABORT_FINALIZE(errno == ENOENT ? RS_RET_EI_NO_EXISTS : RS_RET_EI_OPN_ERR); + } +finalize_it: + RETiRet; +} + + +static rsRetVal +eiCheckFiletype(gcryfile gf) +{ + char hdrBuf[128]; + size_t toRead, didRead; + DEFiRet; + + CHKiRet(eiOpenRead(gf)); + if(Debug) memset(hdrBuf, 0, sizeof(hdrBuf)); /* for dbgprintf below! */ + toRead = sizeof("FILETYPE:")-1 + sizeof(RSGCRY_FILETYPE_NAME)-1 + 1; + didRead = read(gf->fd, hdrBuf, toRead); + close(gf->fd); + DBGPRINTF("eiCheckFiletype read %d bytes: '%s'\n", didRead, hdrBuf); + if( didRead != toRead + || strncmp(hdrBuf, "FILETYPE:" RSGCRY_FILETYPE_NAME "\n", toRead)) + iRet = RS_RET_EI_INVLD_FILE; +finalize_it: + RETiRet; +} + +static rsRetVal +eiOpenAppend(gcryfile gf) +{ + rsRetVal localRet; + DEFiRet; + localRet = eiCheckFiletype(gf); + if(localRet == RS_RET_OK) { + gf->fd = open((char*)gf->eiName, + O_WRONLY|O_APPEND|O_NOCTTY|O_CLOEXEC, 0600); + if(gf->fd == -1) { + ABORT_FINALIZE(RS_RET_EI_OPN_ERR); + } + } else if(localRet == RS_RET_EI_NO_EXISTS) { + /* looks like we need to create a new file */ + gf->fd = open((char*)gf->eiName, + O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); + if(gf->fd == -1) { + ABORT_FINALIZE(RS_RET_EI_OPN_ERR); + } + CHKiRet(eiWriteRec(gf, "FILETYPE:", 9, RSGCRY_FILETYPE_NAME, + sizeof(RSGCRY_FILETYPE_NAME)-1)); + } else { + gf->fd = -1; + ABORT_FINALIZE(localRet); + } + DBGPRINTF("encryption info file %s: opened as #%d\n", + gf->eiName, gf->fd); +finalize_it: + RETiRet; +} + +static rsRetVal +eiWriteIV(gcryfile gf, uchar *iv) { + static const char hexchars[16] = + {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + unsigned iSrc, iDst; + char hex[4096]; + DEFiRet; + + if(gf->blkLength > sizeof(hex)/2) { + DBGPRINTF("eiWriteIV: crypto block len way too large, aborting " + "write"); + ABORT_FINALIZE(RS_RET_ERR); + } + + for(iSrc = iDst = 0 ; iSrc < gf->blkLength ; ++iSrc) { + hex[iDst++] = hexchars[iv[iSrc]>>4]; + hex[iDst++] = hexchars[iv[iSrc]&0x0f]; + } + + iRet = eiWriteRec(gf, "IV:", 3, hex, gf->blkLength*2); +finalize_it: + RETiRet; +} + +/* we do not return an error state, as we MUST close the file, + * no matter what happens. + */ +static void +eiClose(gcryfile gf, off64_t offsLogfile) +{ + char offs[21]; + size_t len; + if(gf->fd == -1) + return; + /* 2^64 is 20 digits, so the snprintf buffer is large enough */ + len = snprintf(offs, sizeof(offs), "%lld", offsLogfile); + eiWriteRec(gf, "END:", 4, offs, len); + close(gf->fd); + DBGPRINTF("encryption info file %s: closed\n", gf->eiName); +} + +static rsRetVal +gcryfileConstruct(gcryctx ctx, gcryfile *pgf, uchar *logfn) +{ + char fn[MAXFNAME+1]; gcryfile gf; - if((gf = calloc(1, sizeof(struct gcryfile_s))) == NULL) - goto done; + DEFiRet; + + CHKmalloc(gf = calloc(1, sizeof(struct gcryfile_s))); gf->ctx = ctx; -done: return gf; + snprintf(fn, sizeof(fn), "%s.encinfo", logfn); + fn[MAXFNAME] = '\0'; /* be on save side */ + gf->eiName = (uchar*) strdup(fn); + *pgf = gf; +finalize_it: + RETiRet; } + gcryctx gcryCtxNew(void) { @@ -47,12 +208,14 @@ gcryCtxNew(void) } int -gcryfileDestruct(gcryfile gf) +gcryfileDestruct(gcryfile gf, off64_t offsLogfile) { int r = 0; if(gf == NULL) goto done; + eiClose(gf, offsLogfile); + free(gf->eiName); free(gf); done: return r; } @@ -119,14 +282,40 @@ rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen) done: return r; } +/* As of some Linux and security expert I spoke to, /dev/urandom + * provides very strong random numbers, even if it runs out of + * entropy. As far as he knew, this is save for all applications + * (and he had good proof that I currently am not permitted to + * reproduce). -- rgerhards, 2013-03-04 + */ +void +seedIV(gcryfile gf, uchar **iv) +{ + int fd; + + *iv = malloc(gf->blkLength); /* do NOT zero-out! */ + /* if we cannot obtain data from /dev/urandom, we use whatever + * is present at the current memory location as random data. Of + * course, this is very weak and we should consider a different + * option, especially when not running under Linux (for Linux, + * unavailability of /dev/urandom is just a theoretic thing, it + * will always work...). -- TODO -- rgerhards, 2013-03-06 + */ + if((fd = open("/dev/urandom", O_RDONLY)) > 0) { + if(read(fd, *iv, gf->blkLength)) {}; /* keep compiler happy */ + close(fd); + } +} + rsRetVal -rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char *iniVector) +rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, uchar *fname) { gcry_error_t gcryError; gcryfile gf = NULL; + uchar *iv = NULL; DEFiRet; - CHKmalloc(gf = gcryfileConstruct(ctx)); + CHKiRet(gcryfileConstruct(ctx, &gf, fname)); gf->blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); @@ -150,17 +339,21 @@ rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char *iniVector) ABORT_FINALIZE(RS_RET_ERR); } - gcryError = gcry_cipher_setiv(gf->chd, iniVector, gf->blkLength); + seedIV(gf, &iv); + gcryError = gcry_cipher_setiv(gf->chd, iv, gf->blkLength); if (gcryError) { dbgprintf("gcry_cipher_setiv failed: %s/%s\n", gcry_strsource(gcryError), gcry_strerror(gcryError)); ABORT_FINALIZE(RS_RET_ERR); } + CHKiRet(eiOpenAppend(gf)); + CHKiRet(eiWriteIV(gf, iv)); *pgf = gf; finalize_it: + free(iv); if(iRet != RS_RET_OK && gf != NULL) - gcryfileDestruct(gf); + gcryfileDestruct(gf, -1); RETiRet; } diff --git a/runtime/libgcry.h b/runtime/libgcry.h index 608abd6c..6e677130 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -34,6 +34,8 @@ typedef struct gcryfile_s *gcryfile; struct gcryfile_s { gcry_cipher_hd_t chd; /* cypher handle */ size_t blkLength; /* size of low-level crypto block */ + uchar *eiName; /* name of .encinfo file */ + int fd; /* descriptor of .encinfo file (-1 if not open) */ gcryctx ctx; }; @@ -42,8 +44,14 @@ void rsgcryExit(void); int rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen); gcryctx gcryCtxNew(void); void rsgcryCtxDel(gcryctx ctx); -int gcryfileDestruct(gcryfile gf); -rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, char * iniVector); +int gcryfileDestruct(gcryfile gf, off64_t offsLogfile); +rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, uchar *fname); int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); +/* error states */ +#define RSGCRYE_EI_OPEN 1 /* error opening .encinfo file */ +#define RSGCRYE_OOM 4 /* ran out of memory */ + +#define RSGCRY_FILETYPE_NAME "rsyslog-enrcyption-info" + #endif /* #ifndef INCLUDED_LIBGCRY_H */ diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index 31e648fc..881d047d 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -150,11 +150,13 @@ OnFileOpen(void *pT, uchar *fn, void *pGF) gcryfile *pgf = (gcryfile*) pGF; DEFiRet; dbgprintf("DDDD: cry: onFileOpen: %s\n", fn); - /* note: if *pgf is set to NULL, this auto-disables GT functions */ - //*pgf = gcryCtxOpenFile(pThis->ctx, fn); - CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, GCRY_CIPHER_MODE_CBC, "TODO: init value")); + CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, GCRY_CIPHER_MODE_CBC, fn)); finalize_it: + /* TODO: enable this error message (need to cleanup loop first ;)) + errmsg.LogError(0, iRet, "Encryption Provider" + "Error: cannot open .encinfo file - disabling log file"); + */ RETiRet; } @@ -169,11 +171,11 @@ dbgprintf("DDDD: Encrypt (%u): %s\n", *lenRec-1, rec); } static rsRetVal -OnFileClose(void *pF) +OnFileClose(void *pF, off64_t offsLogfile) { DEFiRet; dbgprintf("DDDD: onFileClose\n"); - gcryfileDestruct(pF); + gcryfileDestruct(pF, offsLogfile); RETiRet; } diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index cbc0401b..ab57eace 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -404,6 +404,10 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_REPLCHAR_IGNORED = -2313,/**< mmanon: replacementChar parameter is ignored */ RS_RET_SIGPROV_ERR = -2320,/**< error in signature provider */ RS_RET_CRYPROV_ERR = -2321,/**< error in cryptography encryption provider */ + RS_RET_EI_OPN_ERR = -2322,/**< error opening an .encinfo file */ + RS_RET_EI_NO_EXISTS = -2323,/**< .encinfo file does not exist (status, not necessarily error!)*/ + RS_RET_EI_WR_ERR = -2324,/**< error writing an .encinfo file */ + RS_RET_EI_INVLD_FILE = -2325,/**< header indicates the file is no .encinfo file */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ diff --git a/runtime/stream.c b/runtime/stream.c index b31520b0..b0df8418 100644 --- a/runtime/stream.c +++ b/runtime/stream.c @@ -45,6 +45,7 @@ #include #include #include +#include #include /* required for HP UX */ #include #include @@ -388,6 +389,7 @@ strmWaitAsyncWriterDone(strm_t *pThis) */ static rsRetVal strmCloseFile(strm_t *pThis) { + off64_t currOffs; DEFiRet; ASSERT(pThis != NULL); @@ -408,11 +410,12 @@ static rsRetVal strmCloseFile(strm_t *pThis) * against this. -- rgerhards, 2010-03-19 */ if(pThis->fd != -1) { + currOffs = lseek64(pThis->fd, 0, SEEK_CUR); close(pThis->fd); pThis->fd = -1; pThis->inode = 0; if(pThis->cryprov != NULL) { - pThis->cryprov->OnFileClose(pThis->cryprovFileData); + pThis->cryprov->OnFileClose(pThis->cryprovFileData, currOffs); pThis->cryprovFileData = NULL; } } -- cgit v1.2.3 From 2679dd4af107290845711c4e265ed1e8b0c051a8 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 08:49:04 +0200 Subject: logenc: rscryutil can now decrypt multi-block logfiles --- runtime/cryprov.h | 2 - runtime/libgcry.c | 6 +- runtime/libgcry.h | 3 + tools/Makefile.am | 2 +- tools/rscryutil.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++------- 5 files changed, 173 insertions(+), 29 deletions(-) diff --git a/runtime/cryprov.h b/runtime/cryprov.h index 5b694f46..8496b745 100644 --- a/runtime/cryprov.h +++ b/runtime/cryprov.h @@ -36,6 +36,4 @@ BEGINinterface(cryprov) /* name must also be changed in ENDinterface macro! */ rsRetVal (*OnFileClose)(void *pFileInstData, off64_t offsLogfile); ENDinterface(cryprov) #define cryprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ -rsRetVal initCrypt(int gcry_mode, char * iniVector); -rsRetVal doCrypt(char *buf, size_t *len); #endif /* #ifndef INCLUDED_CRYPROV_H */ diff --git a/runtime/libgcry.c b/runtime/libgcry.c index 94e087ac..5fd55360 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -14,9 +14,11 @@ * For the current implementation, there must always be an IV record * followed by an END record. Each records is LF-terminated. Record * types can simply be extended in the future by specifying new - * keywords (like "IV") before the colon. + * types (like "IV") before the colon. * To identify a file as rsyslog encryption info file, it must start with * the line "FILETYPE:rsyslog-enrcyption-info" + * There are some size constraints: the recordtype must be 31 bytes at + * most and the actual value (between : and LF) must be 1023 bytes at most. * * This file is part of rsyslog. * @@ -191,7 +193,7 @@ gcryfileConstruct(gcryctx ctx, gcryfile *pgf, uchar *logfn) CHKmalloc(gf = calloc(1, sizeof(struct gcryfile_s))); gf->ctx = ctx; - snprintf(fn, sizeof(fn), "%s.encinfo", logfn); + snprintf(fn, sizeof(fn), "%s%s", logfn, ENCINFO_SUFFIX); fn[MAXFNAME] = '\0'; /* be on save side */ gf->eiName = (uchar*) strdup(fn); *pgf = gf; diff --git a/runtime/libgcry.h b/runtime/libgcry.h index 6e677130..857d2352 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -52,6 +52,9 @@ int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); #define RSGCRYE_EI_OPEN 1 /* error opening .encinfo file */ #define RSGCRYE_OOM 4 /* ran out of memory */ +#define EIF_MAX_RECTYPE_LEN 31 /* max length of record types */ +#define EIF_MAX_VALUE_LEN 1023 /* max length of value types */ #define RSGCRY_FILETYPE_NAME "rsyslog-enrcyption-info" +#define ENCINFO_SUFFIX ".encinfo" #endif /* #ifndef INCLUDED_LIBGCRY_H */ diff --git a/tools/Makefile.am b/tools/Makefile.am index 8957d713..0f2bb57e 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -77,7 +77,7 @@ endif if ENABLE_LIBGCRYPT bin_PROGRAMS += rscryutil rscryutil = rscryutil.c -rscryutil_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) +rscryutil_CPPFLAGS = -I../runtime $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) rscryutil_LDFLAGS = `libgcrypt-config --libs` #rscryutil_LDFLAGS = $(LIBGCRYPT_LIBS) rscryutil.1: rscryutil.rst diff --git a/tools/rscryutil.c b/tools/rscryutil.c index 755371f2..e57eb625 100644 --- a/tools/rscryutil.c +++ b/tools/rscryutil.c @@ -1,5 +1,4 @@ -/* This is a tool for dumpoing the content of GuardTime TLV - * files in a (somewhat) human-readable manner. +/* This is a tool for processing rsyslog encrypted log files. * * Copyright 2013 Adiscon GmbH * @@ -31,6 +30,9 @@ #include #include +#include "rsyslog.h" +#include "libgcry.h" + static enum { MD_DECRYPT } mode = MD_DECRYPT; @@ -38,17 +40,135 @@ static int verbose = 0; static gcry_cipher_hd_t gcry_chd; static size_t blkLength; + +/* rectype/value must be EIF_MAX_*_LEN+1 long! + * returns 0 on success or something else on error/EOF + */ +static int +eiGetRecord(FILE *eifp, char *rectype, char *value) +{ + int r; + unsigned short i, j; + char buf[EIF_MAX_RECTYPE_LEN+EIF_MAX_VALUE_LEN+128]; + /* large enough for any valid record */ + + if(fgets(buf, sizeof(buf), eifp) == NULL) { + r = 1; goto done; + } + + for(i = 0 ; i < EIF_MAX_RECTYPE_LEN && buf[i] != ':' ; ++i) + if(buf[i] == '\0') { + r = 2; goto done; + } else + rectype[i] = buf[i]; + rectype[i] = '\0'; + j = 0; + for(++i ; i < EIF_MAX_VALUE_LEN && buf[i] != '\n' ; ++i, ++j) + if(buf[i] == '\0') { + r = 3; goto done; + } else + value[j] = buf[i]; + value[j] = '\0'; + r = 0; +done: return r; +} + +static int +eiCheckFiletype(FILE *eifp) +{ + char rectype[EIF_MAX_RECTYPE_LEN+1]; + char value[EIF_MAX_VALUE_LEN+1]; + int r; + + if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done; + if(strcmp(rectype, "FILETYPE") || strcmp(value, RSGCRY_FILETYPE_NAME)) { + fprintf(stderr, "invalid filetype \"cookie\" in encryption " + "info file\n"); + fprintf(stderr, "\trectype: '%s', value: '%s'\n", rectype, value); + r = 1; goto done; + } + r = 0; +done: return r; +} + static int -initCrypt(int gcry_mode, char *iv, char *key) +eiGetIV(FILE *eifp, char *iv, size_t leniv) +{ + char rectype[EIF_MAX_RECTYPE_LEN+1]; + char value[EIF_MAX_VALUE_LEN+1]; + size_t valueLen; + unsigned short i, j; + int r; + unsigned char nibble; + + if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done; + if(strcmp(rectype, "IV")) { + fprintf(stderr, "no IV record found when expected, record type " + "seen is '%s'\n", rectype); + r = 1; goto done; + } + valueLen = strlen(value); + if(valueLen/2 != leniv) { + fprintf(stderr, "length of IV is %d, expected %d\n", + valueLen/2, leniv); + r = 1; goto done; + } + + for(i = j = 0 ; i < valueLen ; ++i) { + if(value[i] >= '0' && value[i] <= '9') + nibble = value[i] - '0'; + else if(value[i] >= 'a' && value[i] <= 'f') + nibble = value[i] - 'a' + 10; + else { + fprintf(stderr, "invalid IV '%s'\n", value); + r = 1; goto done; + } + if(i % 2 == 0) + iv[j] = nibble << 4; + else + iv[j++] |= nibble; + } + r = 0; +done: return r; +} + +static int +eiGetEND(FILE *eifp, off64_t *offs) +{ + char rectype[EIF_MAX_RECTYPE_LEN+1]; + char value[EIF_MAX_VALUE_LEN+1]; + int r; + + if((r = eiGetRecord(eifp, rectype, value)) != 0) goto done; + if(strcmp(rectype, "END")) { + fprintf(stderr, "no END record found when expected, record type " + "seen is '%s'\n", rectype); + r = 1; goto done; + } + *offs = atoll(value); + r = 0; +done: return r; +} + +static int +initCrypt(FILE *eifp, int gcry_mode, char *key) { #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable int r = 0; gcry_error_t gcryError; + char iv[4096]; blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); + if(blkLength > sizeof(iv)) { + fprintf(stderr, "internal error[%s:%d]: block length %d too large for " + "iv buffer\n", __FILE__, __LINE__, blkLength); + r = 1; goto done; + } + if((r = eiGetIV(eifp, iv, blkLength)) != 0) goto done; + size_t keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER); if(strlen(key) != keyLength) { - fprintf(stderr, "invalid key lengtjh; key is %u characters, but " + fprintf(stderr, "invalid key length; key is %u characters, but " "exactly %u characters are required\n", strlen(key), keyLength); r = 1; goto done; @@ -87,7 +207,7 @@ removePadding(char *buf, size_t *plen) unsigned iSrc, iDst; char *frstNUL; - frstNUL = strchr(buf, 0x00); + frstNUL = memchr(buf, 0x00, *plen); if(frstNUL == NULL) goto done; iDst = iSrc = frstNUL - buf; @@ -103,20 +223,24 @@ done: return; } static void -doDeCrypt(FILE *fpin, FILE *fpout) +decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) { gcry_error_t gcryError; - char buf[64*1024]; - size_t nRead, nWritten; + size_t nRead, nWritten; + size_t toRead; size_t nPad; + size_t leftTillBlkEnd; + char buf[64*1024]; + leftTillBlkEnd = blkEnd - *pCurrOffs; while(1) { - nRead = fread(buf, 1, sizeof(buf), fpin); + toRead = sizeof(buf) <= leftTillBlkEnd ? sizeof(buf) : leftTillBlkEnd; + toRead = toRead - toRead % blkLength; + nRead = fread(buf, 1, toRead, fpin); if(nRead == 0) break; + leftTillBlkEnd -= nRead, *pCurrOffs += nRead; nPad = (blkLength - nRead % blkLength) % blkLength; - fprintf(stderr, "--->read %d chars, blkLength %d, mod %d, pad %d\n", nRead, blkLength, - nRead % blkLength, nPad); gcryError = gcry_cipher_decrypt( gcry_chd, // gcry_cipher_hd_t buf, // void * @@ -139,13 +263,31 @@ doDeCrypt(FILE *fpin, FILE *fpout) } +static int +doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp, char *key) +{ + off64_t blkEnd; + off64_t currOffs = 0; + int r; + + while(1) { + /* process block */ + if(initCrypt(eifp, GCRY_CIPHER_MODE_CBC, key) != 0) + goto done; + if((r = eiGetEND(eifp, &blkEnd)) != 0) goto done; + decryptBlock(logfp, outfp, blkEnd, &currOffs); + gcry_cipher_close(gcry_chd); + } + r = 0; +done: return r; +} + static void decrypt(char *name, char *key) { - FILE *logfp = NULL; - //, *sigfp = NULL; + FILE *logfp = NULL, *eifp = NULL; int r = 0; - //char sigfname[4096]; + char eifname[4096]; if(!strcmp(name, "-")) { fprintf(stderr, "decrypt mode cannot work on stdin\n"); @@ -155,21 +297,20 @@ decrypt(char *name, char *key) perror(name); goto err; } -#if 0 - snprintf(sigfname, sizeof(sigfname), "%s.gtsig", name); - sigfname[sizeof(sigfname)-1] = '\0'; - if((sigfp = fopen(sigfname, "r")) == NULL) { - perror(sigfname); + snprintf(eifname, sizeof(eifname), "%s%s", name, ENCINFO_SUFFIX); + eifname[sizeof(eifname)-1] = '\0'; + if((eifp = fopen(eifname, "r")) == NULL) { + perror(eifname); goto err; } -#endif + if(eiCheckFiletype(eifp) != 0) + goto err; } - if(initCrypt(GCRY_CIPHER_MODE_CBC, "TODO: init value", key) != 0) - goto err; - doDeCrypt(logfp, stdout); - gcry_cipher_close(gcry_chd); + doDecrypt(logfp, eifp, stdout, key); + fclose(logfp); logfp = NULL; + fclose(eifp); eifp = NULL; return; err: -- cgit v1.2.3 From 815bae1f35c67ff7b8caf7b446a9e4cf1c870aa3 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 11:22:04 +0200 Subject: logenc: add parameters to select cipher algo and mode --- runtime/libgcry.c | 47 ++++++++++++++++++++++++++++-------- runtime/libgcry.h | 40 ++++++++++++++++++++++++++++++- runtime/lmcry_gcry.c | 38 ++++++++++++++++++----------- runtime/rsyslog.h | 2 ++ tools/rscryutil.c | 67 +++++++++++++++++++++++++++++++++------------------- tools/rscryutil.rst | 43 +++++++++++++++++++++++++++++++++ 6 files changed, 188 insertions(+), 49 deletions(-) diff --git a/runtime/libgcry.c b/runtime/libgcry.c index 5fd55360..ef94e8ac 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -49,8 +49,6 @@ #include "rsyslog.h" #include "libgcry.h" -#define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable - static rsRetVal eiWriteRec(gcryfile gf, char *recHdr, size_t lenRecHdr, char *buf, size_t lenBuf) @@ -206,6 +204,8 @@ gcryCtxNew(void) { gcryctx ctx; ctx = calloc(1, sizeof(struct gcryctx_s)); + ctx->algo = GCRY_CIPHER_AES128; + ctx->mode = GCRY_CIPHER_MODE_CBC; return ctx; } @@ -270,9 +270,10 @@ done: return; int rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen) { - uint16_t reqKeyLen = gcry_cipher_get_algo_keylen(GCRY_CIPHER); + uint16_t reqKeyLen; int r; + reqKeyLen = gcry_cipher_get_algo_keylen(ctx->algo); if(keyLen != reqKeyLen) { r = reqKeyLen; goto done; @@ -284,6 +285,36 @@ rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen) done: return r; } +rsRetVal +rsgcrySetMode(gcryctx ctx, uchar *modename) +{ + int mode; + DEFiRet; + + mode = rsgcryModename2Mode((char *)modename); + if(mode == GCRY_CIPHER_MODE_NONE) { + ABORT_FINALIZE(RS_RET_CRY_INVLD_MODE); + } + ctx->mode = mode; +finalize_it: + RETiRet; +} + +rsRetVal +rsgcrySetAlgo(gcryctx ctx, uchar *algoname) +{ + int algo; + DEFiRet; + + algo = rsgcryAlgoname2Algo((char *)algoname); + if(algo == GCRY_CIPHER_NONE) { + ABORT_FINALIZE(RS_RET_CRY_INVLD_ALGO); + } + ctx->algo = algo; +finalize_it: + RETiRet; +} + /* As of some Linux and security expert I spoke to, /dev/urandom * provides very strong random numbers, even if it runs out of * entropy. As far as he knew, this is save for all applications @@ -310,7 +341,7 @@ seedIV(gcryfile gf, uchar **iv) } rsRetVal -rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, uchar *fname) +rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname) { gcry_error_t gcryError; gcryfile gf = NULL; @@ -319,13 +350,9 @@ rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, uchar *fname) CHKiRet(gcryfileConstruct(ctx, &gf, fname)); - gf->blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); + gf->blkLength = gcry_cipher_get_algo_blklen(ctx->algo); - gcryError = gcry_cipher_open( - &gf->chd, // gcry_cipher_hd_t * - GCRY_CIPHER, // int - gcry_mode, // int - 0); // unsigned int + gcryError = gcry_cipher_open(&gf->chd, ctx->algo, ctx->mode, 0); if (gcryError) { dbgprintf("gcry_cipher_open failed: %s/%s\n", gcry_strsource(gcryError), diff --git a/runtime/libgcry.h b/runtime/libgcry.h index 857d2352..d699124d 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -26,6 +26,8 @@ struct gcryctx_s { uchar *key; size_t keyLen; + int algo; + int mode; }; typedef struct gcryctx_s *gcryctx; typedef struct gcryfile_s *gcryfile; @@ -42,10 +44,12 @@ struct gcryfile_s { int rsgcryInit(void); void rsgcryExit(void); int rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen); +rsRetVal rsgcrySetMode(gcryctx ctx, uchar *algoname); +rsRetVal rsgcrySetAlgo(gcryctx ctx, uchar *modename); gcryctx gcryCtxNew(void); void rsgcryCtxDel(gcryctx ctx); int gcryfileDestruct(gcryfile gf, off64_t offsLogfile); -rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, int gcry_mode, uchar *fname); +rsRetVal rsgcryInitCrypt(gcryctx ctx, gcryfile *pgf, uchar *fname); int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); /* error states */ @@ -57,4 +61,38 @@ int rsgcryEncrypt(gcryfile pF, uchar *buf, size_t *len); #define RSGCRY_FILETYPE_NAME "rsyslog-enrcyption-info" #define ENCINFO_SUFFIX ".encinfo" +static inline int +rsgcryAlgoname2Algo(char *algoname) { + if(!strcmp((char*)algoname, "3DES")) return GCRY_CIPHER_3DES; + if(!strcmp((char*)algoname, "CAST5")) return GCRY_CIPHER_CAST5; + if(!strcmp((char*)algoname, "BLOWFISH")) return GCRY_CIPHER_BLOWFISH; + if(!strcmp((char*)algoname, "AES128")) return GCRY_CIPHER_AES128; + if(!strcmp((char*)algoname, "AES192")) return GCRY_CIPHER_AES192; + if(!strcmp((char*)algoname, "AES256")) return GCRY_CIPHER_AES256; + if(!strcmp((char*)algoname, "TWOFISH")) return GCRY_CIPHER_TWOFISH; + if(!strcmp((char*)algoname, "TWOFISH128")) return GCRY_CIPHER_TWOFISH128; + if(!strcmp((char*)algoname, "ARCFOUR")) return GCRY_CIPHER_ARCFOUR; + if(!strcmp((char*)algoname, "DES")) return GCRY_CIPHER_DES; + if(!strcmp((char*)algoname, "SERPENT128")) return GCRY_CIPHER_SERPENT128; + if(!strcmp((char*)algoname, "SERPENT192")) return GCRY_CIPHER_SERPENT192; + if(!strcmp((char*)algoname, "SERPENT256")) return GCRY_CIPHER_SERPENT256; + if(!strcmp((char*)algoname, "RFC2268_40")) return GCRY_CIPHER_RFC2268_40; + if(!strcmp((char*)algoname, "SEED")) return GCRY_CIPHER_SEED; + if(!strcmp((char*)algoname, "CAMELLIA128")) return GCRY_CIPHER_CAMELLIA128; + if(!strcmp((char*)algoname, "CAMELLIA192")) return GCRY_CIPHER_CAMELLIA192; + if(!strcmp((char*)algoname, "CAMELLIA256")) return GCRY_CIPHER_CAMELLIA256; + return GCRY_CIPHER_NONE; +} + +static inline int +rsgcryModename2Mode(char *modename) { + if(!strcmp((char*)modename, "ECB")) return GCRY_CIPHER_MODE_ECB; + if(!strcmp((char*)modename, "CFB")) return GCRY_CIPHER_MODE_CFB; + if(!strcmp((char*)modename, "CBC")) return GCRY_CIPHER_MODE_CBC; + if(!strcmp((char*)modename, "STREAM")) return GCRY_CIPHER_MODE_STREAM; + if(!strcmp((char*)modename, "OFB")) return GCRY_CIPHER_MODE_OFB; + if(!strcmp((char*)modename, "CTR")) return GCRY_CIPHER_MODE_CTR; + if(!strcmp((char*)modename, "AESWRAP")) return GCRY_CIPHER_MODE_AESWRAP; + return GCRY_CIPHER_MODE_NONE; +} #endif /* #ifndef INCLUDED_LIBGCRY_H */ diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index 881d047d..cc65051f 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -89,8 +89,9 @@ SetCnfParam(void *pT, struct nvlst *lst) { lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; int i, r; - uchar *cstr; uchar *key = NULL; + uchar *algo = NULL; + uchar *mode = NULL; struct cnfparamvals *pvals; DEFiRet; @@ -105,23 +106,30 @@ SetCnfParam(void *pT, struct nvlst *lst) continue; if(!strcmp(pblk.descr[i].name, "cry.key")) { key = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); -#if 0 - } else if(!strcmp(pblk.descr[i].name, "sig.timestampservice")) { - cstr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); - gcrySetTimestamper(pThis->ctx, (char*) cstr); - free(cstr); - } else if(!strcmp(pblk.descr[i].name, "sig.block.sizelimit")) { - gcrySetBlockSizeLimit(pThis->ctx, pvals[i].val.d.n); - } else if(!strcmp(pblk.descr[i].name, "sig.keeprecordhashes")) { - gcrySetKeepRecordHashes(pThis->ctx, pvals[i].val.d.n); - } else if(!strcmp(pblk.descr[i].name, "sig.keeptreehashes")) { - gcrySetKeepTreeHashes(pThis->ctx, pvals[i].val.d.n); + } else if(!strcmp(pblk.descr[i].name, "cry.mode")) { + mode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(pblk.descr[i].name, "cry.algo")) { + algo = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else { DBGPRINTF("lmcry_gcry: program error, non-handled " "param '%s'\n", pblk.descr[i].name); -#endif } } + if(algo != NULL) { + iRet = rsgcrySetAlgo(pThis->ctx, algo); + if(iRet != RS_RET_OK) { + errmsg.LogError(0, iRet, "cry.algo '%s' is not know/supported", algo); + FINALIZE; + } + } + if(mode != NULL) { + iRet = rsgcrySetMode(pThis->ctx, mode); + if(iRet != RS_RET_OK) { + errmsg.LogError(0, iRet, "cry.mode '%s' is not know/supported", mode); + FINALIZE; + } + } + /* note: key must be set AFTER algo/mode is set (as it depends on them) */ if(key != NULL) { errmsg.LogError(0, RS_RET_ERR, "Note: specifying an actual key directly from the " "config file is highly insecure - DO NOT USE FOR PRODUCTION"); @@ -138,6 +146,8 @@ SetCnfParam(void *pT, struct nvlst *lst) memset(key, 0, strlen((char*)key)); free(key); } + free(algo); + free(mode); finalize_it: RETiRet; } @@ -151,7 +161,7 @@ OnFileOpen(void *pT, uchar *fn, void *pGF) DEFiRet; dbgprintf("DDDD: cry: onFileOpen: %s\n", fn); - CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, GCRY_CIPHER_MODE_CBC, fn)); + CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, fn)); finalize_it: /* TODO: enable this error message (need to cleanup loop first ;)) errmsg.LogError(0, iRet, "Encryption Provider" diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index ab57eace..4cdd1c1e 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -408,6 +408,8 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_EI_NO_EXISTS = -2323,/**< .encinfo file does not exist (status, not necessarily error!)*/ RS_RET_EI_WR_ERR = -2324,/**< error writing an .encinfo file */ RS_RET_EI_INVLD_FILE = -2325,/**< header indicates the file is no .encinfo file */ + RS_RET_CRY_INVLD_ALGO = -2326,/**< user specified invalid (unkonwn) crypto algorithm */ + RS_RET_CRY_INVLD_MODE = -2327,/**< user specified invalid (unkonwn) crypto mode */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ diff --git a/tools/rscryutil.c b/tools/rscryutil.c index e57eb625..e1e900a7 100644 --- a/tools/rscryutil.c +++ b/tools/rscryutil.c @@ -40,6 +40,9 @@ static int verbose = 0; static gcry_cipher_hd_t gcry_chd; static size_t blkLength; +static char *cry_key = NULL; +static int cry_algo = GCRY_CIPHER_AES128; +static int cry_mode = GCRY_CIPHER_MODE_CBC; /* rectype/value must be EIF_MAX_*_LEN+1 long! * returns 0 on success or something else on error/EOF @@ -151,14 +154,13 @@ done: return r; } static int -initCrypt(FILE *eifp, int gcry_mode, char *key) +initCrypt(FILE *eifp) { - #define GCRY_CIPHER GCRY_CIPHER_3DES // TODO: make configurable int r = 0; - gcry_error_t gcryError; + gcry_error_t gcryError; char iv[4096]; - blkLength = gcry_cipher_get_algo_blklen(GCRY_CIPHER); + blkLength = gcry_cipher_get_algo_blklen(cry_algo); if(blkLength > sizeof(iv)) { fprintf(stderr, "internal error[%s:%d]: block length %d too large for " "iv buffer\n", __FILE__, __LINE__, blkLength); @@ -166,15 +168,15 @@ initCrypt(FILE *eifp, int gcry_mode, char *key) } if((r = eiGetIV(eifp, iv, blkLength)) != 0) goto done; - size_t keyLength = gcry_cipher_get_algo_keylen(GCRY_CIPHER); - if(strlen(key) != keyLength) { + size_t keyLength = gcry_cipher_get_algo_keylen(cry_algo); + if(strlen(cry_key) != keyLength) { fprintf(stderr, "invalid key length; key is %u characters, but " - "exactly %u characters are required\n", strlen(key), + "exactly %u characters are required\n", strlen(cry_key), keyLength); r = 1; goto done; } - gcryError = gcry_cipher_open(&gcry_chd, GCRY_CIPHER, gcry_mode, 0); + gcryError = gcry_cipher_open(&gcry_chd, cry_algo, cry_mode, 0); if (gcryError) { printf("gcry_cipher_open failed: %s/%s\n", gcry_strsource(gcryError), @@ -182,7 +184,7 @@ initCrypt(FILE *eifp, int gcry_mode, char *key) r = 1; goto done; } - gcryError = gcry_cipher_setkey(gcry_chd, key, keyLength); + gcryError = gcry_cipher_setkey(gcry_chd, cry_key, keyLength); if (gcryError) { printf("gcry_cipher_setkey failed: %s/%s\n", gcry_strsource(gcryError), @@ -225,10 +227,9 @@ done: return; static void decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) { - gcry_error_t gcryError; + gcry_error_t gcryError; size_t nRead, nWritten; size_t toRead; - size_t nPad; size_t leftTillBlkEnd; char buf[64*1024]; @@ -240,7 +241,6 @@ decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) if(nRead == 0) break; leftTillBlkEnd -= nRead, *pCurrOffs += nRead; - nPad = (blkLength - nRead % blkLength) % blkLength; gcryError = gcry_cipher_decrypt( gcry_chd, // gcry_cipher_hd_t buf, // void * @@ -248,7 +248,7 @@ decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) NULL, // const void * 0); // size_t if (gcryError) { - fprintf(stderr, "gcry_cipher_encrypt failed: %s/%s\n", + fprintf(stderr, "gcry_cipher_decrypt failed: %s/%s\n", gcry_strsource(gcryError), gcry_strerror(gcryError)); return; @@ -264,7 +264,7 @@ decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs) static int -doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp, char *key) +doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp) { off64_t blkEnd; off64_t currOffs = 0; @@ -272,7 +272,7 @@ doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp, char *key) while(1) { /* process block */ - if(initCrypt(eifp, GCRY_CIPHER_MODE_CBC, key) != 0) + if(initCrypt(eifp) != 0) goto done; if((r = eiGetEND(eifp, &blkEnd)) != 0) goto done; decryptBlock(logfp, outfp, blkEnd, &currOffs); @@ -283,7 +283,7 @@ done: return r; } static void -decrypt(char *name, char *key) +decrypt(char *name) { FILE *logfp = NULL, *eifp = NULL; int r = 0; @@ -307,7 +307,7 @@ decrypt(char *name, char *key) goto err; } - doDecrypt(logfp, eifp, stdout, key); + doDecrypt(logfp, eifp, stdout); fclose(logfp); logfp = NULL; fclose(eifp); eifp = NULL; @@ -326,6 +326,8 @@ static struct option long_options[] = {"version", no_argument, NULL, 'V'}, {"decrypt", no_argument, NULL, 'd'}, {"key", required_argument, NULL, 'k'}, + {"algo", required_argument, NULL, 'a'}, + {"mode", required_argument, NULL, 'm'}, {NULL, 0, NULL, 0} }; @@ -334,10 +336,10 @@ main(int argc, char *argv[]) { int i; int opt; - char *key = ""; + int temp; while(1) { - opt = getopt_long(argc, argv, "dk:vV", long_options, NULL); + opt = getopt_long(argc, argv, "a:dk:m:vV", long_options, NULL); if(opt == -1) break; switch(opt) { @@ -348,7 +350,25 @@ main(int argc, char *argv[]) fprintf(stderr, "WARNING: specifying the actual key " "via the command line is highly insecure\n" "Do NOT use this for PRODUCTION use.\n"); - key = optarg; + cry_key = optarg; + break; + case 'a': + temp = rsgcryAlgoname2Algo(optarg); + if(temp == GCRY_CIPHER_NONE) { + fprintf(stderr, "ERROR: algorithm \"%s\" is not " + "kown/supported\n", optarg); + exit(1); + } + cry_algo = temp; + break; + case 'm': + temp = rsgcryModename2Mode(optarg); + if(temp == GCRY_CIPHER_MODE_NONE) { + fprintf(stderr, "ERROR: cipher mode \"%s\" is not " + "kown/supported\n", optarg); + exit(1); + } + cry_mode = temp; break; case 'v': verbose = 1; @@ -365,13 +385,12 @@ main(int argc, char *argv[]) } if(optind == argc) - decrypt("-", key); + decrypt("-"); else { for(i = optind ; i < argc ; ++i) - decrypt(argv[i], key); /* currently only mode ;) */ + decrypt(argv[i]); /* currently only mode ;) */ } - memset(key, 0, strlen(key)); /* zero-out key store */ + memset(cry_key, 0, strlen(cry_key)); /* zero-out key store */ return 0; } - //char *aesSymKey = "123456789012345678901234"; // TODO: TEST ONLY diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst index 7e3ab5b4..3cc54f57 100644 --- a/tools/rscryutil.rst +++ b/tools/rscryutil.rst @@ -40,6 +40,13 @@ OPTIONS is highly insecure. However, it can be useful for intial testing steps. This option may be removed in the future. +-a, --algo + Sets the encryption algorightm (cipher) to be used. See below + for supported algorithms. The default is "AES128". + +-m, --mode + Sets the ciphermode to be used. See below for supported modes. + The default is "CBC". OPERATION MODES =============== @@ -62,6 +69,42 @@ The command returns an exit code of 0 if everything went fine, and some other code in case of failures. +SUPPORTED ALGORITHMS +==================== + +We basically support what libgcrypt supports. This is: + 3DES + CAST5 + BLOWFISH + AES128 + AES192 + AES256 + TWOFISH + TWOFISH128 + ARCFOUR + DES + SERPENT128 + SERPENT192 + SERPENT256 + RFC2268_40 + SEED + CAMELLIA128 + CAMELLIA192 + CAMELLIA256 + + +SUPPORTED CIPHER MODES +====================== + +We basically support what libgcrypt supports. This is: + ECB + CFB + CBC + STREAM + OFB + CTR + AESWRAP + EXAMPLES ======== -- cgit v1.2.3 From 7a62ef673f3aea7b0ad34e27a4cfaa5ba6e9efd1 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 12:52:59 +0200 Subject: logenc: support keyfiles in rscryutil --- runtime/Makefile.am | 14 ++++----- runtime/libgcry.c | 3 ++ runtime/libgcry.h | 1 + runtime/libgcry_common.c | 77 +++++++++++++++++++++++++++++++++++++++++++++ tools/Makefile.am | 2 +- tools/rscryutil.c | 81 ++++++++++++++++++++++++++++++++++++++++++------ tools/rscryutil.rst | 4 +++ 7 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 runtime/libgcry_common.c diff --git a/runtime/Makefile.am b/runtime/Makefile.am index e1f0673c..ee5a3ef2 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -1,6 +1,6 @@ sbin_PROGRAMS = man_MANS = -noinst_LTLIBRARIES = librsyslog.la librsgt.la +noinst_LTLIBRARIES = librsyslog.la pkglib_LTLIBRARIES = #pkglib_LTLIBRARIES = librsyslog.la @@ -178,15 +178,14 @@ endif # support library for libgcrypt # if ENABLE_LIBGCRYPT -#noinst_LTLIBRARIES += libgcry.la -#libgcry_la_SOURCES = libgcry.c libgcry.h -#libcgry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) + noinst_LTLIBRARIES += libgcry.la + libgcry_la_SOURCES = libgcry.c libgcry_common.c libgcry.h + libgcry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) pkglib_LTLIBRARIES += lmcry_gcry.la - lmcry_gcry_la_SOURCES = lmcry_gcry.c lmcry_gcry.h libgcry.c libgcry.h + lmcry_gcry_la_SOURCES = lmcry_gcry.c lmcry_gcry.h lmcry_gcry_la_CPPFLAGS = $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) lmcry_gcry_la_LDFLAGS = -module -avoid-version `libgcrypt-config --libs` -#lmcry_gcry_la_LIBADD = libgcry.la $(LIBGCRYPT_LIBS) - lmcry_gcry_la_LIBADD = $(LIBGCRYPT_LIBS) + lmcry_gcry_la_LIBADD = libgcry.la $(LIBGCRYPT_LIBS) endif @@ -194,6 +193,7 @@ endif # support library for guardtime # if ENABLE_GUARDTIME + noinst_LTLIBRARIES += librsgt.la librsgt_la_SOURCES = librsgt.c librsgt_read.c librsgt.h pkglib_LTLIBRARIES += lmsig_gt.la lmsig_gt_la_SOURCES = lmsig_gt.c lmsig_gt.h diff --git a/runtime/libgcry.c b/runtime/libgcry.c index ef94e8ac..e57ee8bc 100644 --- a/runtime/libgcry.c +++ b/runtime/libgcry.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include @@ -199,6 +201,7 @@ finalize_it: RETiRet; } + gcryctx gcryCtxNew(void) { diff --git a/runtime/libgcry.h b/runtime/libgcry.h index d699124d..5dde1576 100644 --- a/runtime/libgcry.h +++ b/runtime/libgcry.h @@ -41,6 +41,7 @@ struct gcryfile_s { gcryctx ctx; }; +int gcryGetKeyFromFile(char *fn, char **key, unsigned *keylen); int rsgcryInit(void); void rsgcryExit(void); int rsgcrySetKey(gcryctx ctx, unsigned char *key, uint16_t keyLen); diff --git a/runtime/libgcry_common.c b/runtime/libgcry_common.c new file mode 100644 index 00000000..49a0e669 --- /dev/null +++ b/runtime/libgcry_common.c @@ -0,0 +1,77 @@ +/* libgcry_common.c + * This file hosts functions both being used by the rsyslog runtime as + * well as tools who do not use the runtime (so we can maintain the + * code at a single place). + * + * Copyright 2013 Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rsyslog.h" /* we need data typedefs */ +#include "libgcry.h" + + +/* read a key from a key file + * @param[out] key - key buffer, must be freed by caller + * @param[out] keylen - length of buffer + * @returns 0 if OK, something else otherwise (we do not use + * iRet as this is also called from non-rsyslog w/o runtime) + * The key length is limited to 64KiB to prevent DoS. + * Note well: key is a blob, not a C string (NUL may be present!) + */ +int +gcryGetKeyFromFile(char *fn, char **key, unsigned *keylen) +{ + struct stat sb; + int fd; + int r; + + if(stat(fn, &sb) == -1) { + r = 1; goto done; + } + if((sb.st_mode & S_IFMT) != S_IFREG) { + r = 2; goto done; + } + if(sb.st_size > 64*1024) { + r = 3; goto done; + } + if((*key = malloc(sb.st_size)) == NULL) { + r = -1; goto done; + } + if((fd = open(fn, O_RDONLY)) < 0) { + r = 4; goto done; + } + if(read(fd, *key, sb.st_size) != sb.st_size) { + r = 5; goto done; + } + *keylen = sb.st_size; + close(fd); + r = 0; +done: return r; +} diff --git a/tools/Makefile.am b/tools/Makefile.am index 0f2bb57e..938782f7 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -79,7 +79,7 @@ bin_PROGRAMS += rscryutil rscryutil = rscryutil.c rscryutil_CPPFLAGS = -I../runtime $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS) rscryutil_LDFLAGS = `libgcrypt-config --libs` -#rscryutil_LDFLAGS = $(LIBGCRYPT_LIBS) +rscryutil_LDADD = ../runtime/libgcry.la $(LIBGCRYPT_LIBS) rscryutil.1: rscryutil.rst $(AM_V_GEN) $(RST2MAN) $< $@ man1_MANS = rscryutil.1 diff --git a/tools/rscryutil.c b/tools/rscryutil.c index e1e900a7..9290db4d 100644 --- a/tools/rscryutil.c +++ b/tools/rscryutil.c @@ -34,13 +34,14 @@ #include "libgcry.h" -static enum { MD_DECRYPT +static enum { MD_DECRYPT, MD_WRITE_KEYFILE } mode = MD_DECRYPT; static int verbose = 0; static gcry_cipher_hd_t gcry_chd; static size_t blkLength; static char *cry_key = NULL; +static unsigned cry_keylen = 0; static int cry_algo = GCRY_CIPHER_AES128; static int cry_mode = GCRY_CIPHER_MODE_CBC; @@ -171,7 +172,7 @@ initCrypt(FILE *eifp) size_t keyLength = gcry_cipher_get_algo_keylen(cry_algo); if(strlen(cry_key) != keyLength) { fprintf(stderr, "invalid key length; key is %u characters, but " - "exactly %u characters are required\n", strlen(cry_key), + "exactly %u characters are required\n", cry_keylen, keyLength); r = 1; goto done; } @@ -319,38 +320,82 @@ err: fclose(logfp); } +static void +write_keyfile(char *keyfile) +{ + FILE *fp; + + if(cry_key == NULL) { + fprintf(stderr, "ERROR: key must be set via some method\n"); + exit(1); + } + if(keyfile == NULL) { + fprintf(stderr, "ERROR: keyfile must be set\n"); + exit(1); + } + if((fp = fopen(keyfile, "w")) == NULL) { + perror(keyfile); + exit(1); + } + if(fwrite(cry_key, cry_keylen, 1, fp) != 1) { + perror(keyfile); + exit(1); + } + fclose(fp); +} static struct option long_options[] = { {"verbose", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"decrypt", no_argument, NULL, 'd'}, - {"key", required_argument, NULL, 'k'}, + {"write-keyfile", no_argument, NULL, 'W'}, + {"key", required_argument, NULL, 'K'}, + {"keyfile", required_argument, NULL, 'k'}, {"algo", required_argument, NULL, 'a'}, {"mode", required_argument, NULL, 'm'}, {NULL, 0, NULL, 0} }; +static void +getKeyFromFile(char *fn) +{ + int r; + r = gcryGetKeyFromFile(fn, &cry_key, &cry_keylen); + if(r != 0) { + fprintf(stderr, "Error %d reading key from file '%s'\n", r, fn); + exit(1); + } +} + int main(int argc, char *argv[]) { int i; int opt; int temp; + char *keyfile = NULL; while(1) { - opt = getopt_long(argc, argv, "a:dk:m:vV", long_options, NULL); + opt = getopt_long(argc, argv, "a:dk:K:m:vVW", long_options, NULL); if(opt == -1) break; switch(opt) { case 'd': mode = MD_DECRYPT; break; + case 'W': + mode = MD_WRITE_KEYFILE; + break; case 'k': + keyfile = optarg; + break; + case 'K': fprintf(stderr, "WARNING: specifying the actual key " "via the command line is highly insecure\n" "Do NOT use this for PRODUCTION use.\n"); cry_key = optarg; + cry_keylen = strlen(cry_key); break; case 'a': temp = rsgcryAlgoname2Algo(optarg); @@ -384,13 +429,29 @@ main(int argc, char *argv[]) } } - if(optind == argc) - decrypt("-"); - else { - for(i = optind ; i < argc ; ++i) - decrypt(argv[i]); /* currently only mode ;) */ + if(mode == MD_WRITE_KEYFILE) { + if(optind != argc) { + fprintf(stderr, "ERROR: no file parameters permitted in " + "--write-keyfile mode\n"); + exit(1); + } + write_keyfile(keyfile); + } else { + if(keyfile != NULL) + getKeyFromFile(keyfile); + if(cry_key == NULL) { + fprintf(stderr, "ERROR: key must be set via some method\n"); + exit(1); + } + if(optind == argc) + decrypt("-"); + else { + for(i = optind ; i < argc ; ++i) + decrypt(argv[i]); + } } - memset(cry_key, 0, strlen(cry_key)); /* zero-out key store */ + memset(cry_key, 0, cry_keylen); /* zero-out key store */ + cry_keylen = 0; return 0; } diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst index 3cc54f57..d6381011 100644 --- a/tools/rscryutil.rst +++ b/tools/rscryutil.rst @@ -31,6 +31,10 @@ OPTIONS -d, --decrypt Select decryption mode. This is the default mode. +-W, --write-keyfile + Utility function to write a key to a keyfile. The key can be obtained + via any method (except via a keyfile for obvious reasons). + -v, --verbose Select verbose mode. -- cgit v1.2.3 From 97cbbdac13c0e2a08a8f0cb716b0cdce9a2bb3cf Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 15:24:51 +0200 Subject: logenc: full support for keyfiles including their generation via rscrytool --- ChangeLog | 1 + runtime/lmcry_gcry.c | 30 ++++++++++++-- tools/rscryutil.c | 114 ++++++++++++++++++++++++++++++++++++--------------- tools/rscryutil.rst | 77 +++++++++++++++++++++++++++++++--- 4 files changed, 179 insertions(+), 43 deletions(-) diff --git a/ChangeLog b/ChangeLog index 97114227..a8f6b475 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ Version 7.3.10 [devel] 2013-04-?? - added RainerScript re_extract() function - templates now permit substring extraction relative to end-of-string +- added support for encrypting log files - bugfix: imuxsock aborted under some conditions regression from ratelimiting enhancements --------------------------------------------------------------------------- diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index cc65051f..bcc001fc 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -45,6 +45,7 @@ DEFobjCurrIf(glbl) /* tables for interfacing with the v6 config system */ static struct cnfparamdescr cnfpdescr[] = { { "cry.key", eCmdHdlrGetWord, 0 }, + { "cry.keyfile", eCmdHdlrGetWord, 0 }, { "cry.mode", eCmdHdlrGetWord, 0 }, /* CBC, ECB, etc */ { "cry.algo", eCmdHdlrGetWord, 0 } }; @@ -89,7 +90,9 @@ SetCnfParam(void *pT, struct nvlst *lst) { lmcry_gcry_t *pThis = (lmcry_gcry_t*) pT; int i, r; + unsigned keylen; uchar *key = NULL; + uchar *keyfile = NULL; uchar *algo = NULL; uchar *mode = NULL; struct cnfparamvals *pvals; @@ -106,6 +109,8 @@ SetCnfParam(void *pT, struct nvlst *lst) continue; if(!strcmp(pblk.descr[i].name, "cry.key")) { key = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(pblk.descr[i].name, "cry.keyfile")) { + keyfile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(pblk.descr[i].name, "cry.mode")) { mode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(pblk.descr[i].name, "cry.algo")) { @@ -130,22 +135,39 @@ SetCnfParam(void *pT, struct nvlst *lst) } } /* note: key must be set AFTER algo/mode is set (as it depends on them) */ + if(key != NULL && keyfile != NULL) { + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "only one of the following " + "parameters can be specified: cry.key, cry.keyfile\n"); + ABORT_FINALIZE(RS_RET_INVALID_PARAMS); + } if(key != NULL) { errmsg.LogError(0, RS_RET_ERR, "Note: specifying an actual key directly from the " "config file is highly insecure - DO NOT USE FOR PRODUCTION"); - r = rsgcrySetKey(pThis->ctx, key, strlen((char*)key)); - if(r > 0) { - errmsg.LogError(0, RS_RET_INVALID_PARAMS, "Key length %d expected, but " - "key of length %d given", r, strlen((char*)key)); + keylen = strlen((char*)key); + } + if(keyfile != NULL) { + r = gcryGetKeyFromFile((char*)keyfile, (char**)&key, &keylen); + if(r != 0) { + errmsg.LogError(0, RS_RET_ERR, "error %d reading keyfile %s\n", + r, keyfile); ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } } + /* if we reach this point, we have a valid key */ + r = rsgcrySetKey(pThis->ctx, key, keylen); + if(r > 0) { + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "Key length %d expected, but " + "key of length %d given", r, keylen); + ABORT_FINALIZE(RS_RET_INVALID_PARAMS); + } + cnfparamvalsDestruct(pvals, &pblk); if(key != NULL) { memset(key, 0, strlen((char*)key)); free(key); } + free(keyfile); free(algo); free(mode); finalize_it: diff --git a/tools/rscryutil.c b/tools/rscryutil.c index 9290db4d..be14cde9 100644 --- a/tools/rscryutil.c +++ b/tools/rscryutil.c @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include #include "rsyslog.h" @@ -40,10 +43,13 @@ static int verbose = 0; static gcry_cipher_hd_t gcry_chd; static size_t blkLength; +static char *keyfile = NULL; +static int randomKeyLen = -1; static char *cry_key = NULL; static unsigned cry_keylen = 0; static int cry_algo = GCRY_CIPHER_AES128; static int cry_mode = GCRY_CIPHER_MODE_CBC; +static int optionForce = 0; /* rectype/value must be EIF_MAX_*_LEN+1 long! * returns 0 on success or something else on error/EOF @@ -321,27 +327,70 @@ err: } static void -write_keyfile(char *keyfile) +write_keyfile(char *fn) { - FILE *fp; - - if(cry_key == NULL) { - fprintf(stderr, "ERROR: key must be set via some method\n"); + int fd; + int r; + mode_t fmode; + + fmode = O_WRONLY|O_CREAT; + if(!optionForce) + fmode |= O_EXCL; + if((fd = open(fn, fmode, S_IRUSR)) == -1) { + fprintf(stderr, "error opening keyfile "); + perror(fn); exit(1); } - if(keyfile == NULL) { - fprintf(stderr, "ERROR: keyfile must be set\n"); + if((r = write(fd, cry_key, cry_keylen)) != (ssize_t)cry_keylen) { + fprintf(stderr, "error writing keyfile (ret=%d) ", r); + perror(fn); exit(1); } - if((fp = fopen(keyfile, "w")) == NULL) { - perror(keyfile); + close(fd); +} + +static void +getKeyFromFile(char *fn) +{ + int r; + r = gcryGetKeyFromFile(fn, &cry_key, &cry_keylen); + if(r != 0) { + fprintf(stderr, "Error %d reading key from file '%s'\n", r, fn); exit(1); } - if(fwrite(cry_key, cry_keylen, 1, fp) != 1) { - perror(keyfile); +} + +static void +getRandomKey(void) +{ + int fd; + cry_keylen = randomKeyLen; + cry_key = malloc(randomKeyLen); /* do NOT zero-out! */ + /* if we cannot obtain data from /dev/urandom, we use whatever + * is present at the current memory location as random data. Of + * course, this is very weak and we should consider a different + * option, especially when not running under Linux (for Linux, + * unavailability of /dev/urandom is just a theoretic thing, it + * will always work...). -- TODO -- rgerhards, 2013-03-06 + */ + if((fd = open("/dev/urandom", O_RDONLY)) > 0) { + if(read(fd, cry_key, randomKeyLen)) {}; /* keep compiler happy */ + close(fd); + } +} + + +static void +setKey() +{ + if(randomKeyLen != -1) + getRandomKey(); + else if(keyfile != NULL) + getKeyFromFile(keyfile); + if(cry_key == NULL) { + fprintf(stderr, "ERROR: key must be set via some method\n"); exit(1); } - fclose(fp); } static struct option long_options[] = @@ -349,35 +398,26 @@ static struct option long_options[] = {"verbose", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"decrypt", no_argument, NULL, 'd'}, - {"write-keyfile", no_argument, NULL, 'W'}, + {"force", no_argument, NULL, 'f'}, + {"write-keyfile", required_argument, NULL, 'W'}, {"key", required_argument, NULL, 'K'}, + {"generate-random-key", required_argument, NULL, 'r'}, {"keyfile", required_argument, NULL, 'k'}, {"algo", required_argument, NULL, 'a'}, {"mode", required_argument, NULL, 'm'}, {NULL, 0, NULL, 0} }; -static void -getKeyFromFile(char *fn) -{ - int r; - r = gcryGetKeyFromFile(fn, &cry_key, &cry_keylen); - if(r != 0) { - fprintf(stderr, "Error %d reading key from file '%s'\n", r, fn); - exit(1); - } -} - int main(int argc, char *argv[]) { int i; int opt; int temp; - char *keyfile = NULL; + char *newKeyFile = NULL; while(1) { - opt = getopt_long(argc, argv, "a:dk:K:m:vVW", long_options, NULL); + opt = getopt_long(argc, argv, "a:dfk:K:m:r:vVW:", long_options, NULL); if(opt == -1) break; switch(opt) { @@ -386,10 +426,22 @@ main(int argc, char *argv[]) break; case 'W': mode = MD_WRITE_KEYFILE; + newKeyFile = optarg; break; case 'k': keyfile = optarg; break; + case 'f': + optionForce = 1; + break; + case 'r': + randomKeyLen = atoi(optarg); + if(randomKeyLen > 64*1024) { + fprintf(stderr, "ERROR: keys larger than 64KiB are " + "not supported\n"); + exit(1); + } + break; case 'K': fprintf(stderr, "WARNING: specifying the actual key " "via the command line is highly insecure\n" @@ -429,20 +481,16 @@ main(int argc, char *argv[]) } } + setKey(); + if(mode == MD_WRITE_KEYFILE) { if(optind != argc) { fprintf(stderr, "ERROR: no file parameters permitted in " "--write-keyfile mode\n"); exit(1); } - write_keyfile(keyfile); + write_keyfile(newKeyFile); } else { - if(keyfile != NULL) - getKeyFromFile(keyfile); - if(cry_key == NULL) { - fprintf(stderr, "ERROR: key must be set via some method\n"); - exit(1); - } if(optind == argc) decrypt("-"); else { diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst index d6381011..c546d855 100644 --- a/tools/rscryutil.rst +++ b/tools/rscryutil.rst @@ -7,7 +7,7 @@ Manage Encrypted Log Files -------------------------- :Author: Rainer Gerhards -:Date: 2013-04-08 +:Date: 2013-04-15 :Manual section: 1 SYNOPSIS @@ -31,14 +31,22 @@ OPTIONS -d, --decrypt Select decryption mode. This is the default mode. --W, --write-keyfile +-W, --write-keyfile Utility function to write a key to a keyfile. The key can be obtained - via any method (except via a keyfile for obvious reasons). + via any method. -v, --verbose Select verbose mode. --k, --key +-f, --force + Forces operations that otherwise would fail. + +-k, --keyfile + Reads the key from . File _must_ contain the key, only, no headers + or other meta information. Keyfiles can be generated via the + *--write-keyfile* option. + +-K, --key TESTING AID, NOT FOR PRODUCTION USE. This uses the KEY specified on the command line. This is the actual key, and as such this mode is highly insecure. However, it can be useful for intial testing @@ -52,6 +60,11 @@ OPTIONS Sets the ciphermode to be used. See below for supported modes. The default is "CBC". +-r, --generate-random-key + Generates a random key of length . This option is + meant to be used together with *--write-keyfile* (and it is hard + to envision any other valid use for it). + OPERATION MODES =============== @@ -64,7 +77,25 @@ unpredictable. decrypt ------- -The provided log files are decrypted. +The provided log files are decrypted. Note that the *.encinfo* side files +must exist and be accessible in order for decryption to to work. + +write-keyfile +------------- + +In this mode no log files are processed; thus it is an error to specify +any on the command line. The specified keyfile is written. The key itself +is obtained via the usual key commands. If *--keyfile* is used, that +file is effectively copied. + +For security reasons, existing key files are _not_ overwritten. To permit +this, specify the *--force* option. When doing so, keep in mind that lost +keys cannot be recovered and data encrypted with them may also be considered +lost. + +Keyfiles are always created with 0400 permission, that is read access for only +the user. An exception is when an existing file is overwritten via the +*--force* option, in which case the former permissions still apply. EXIT CODES ========== @@ -77,6 +108,7 @@ SUPPORTED ALGORITHMS ==================== We basically support what libgcrypt supports. This is: + 3DES CAST5 BLOWFISH @@ -101,6 +133,7 @@ SUPPORTED CIPHER MODES ====================== We basically support what libgcrypt supports. This is: + ECB CFB CBC @@ -116,9 +149,41 @@ EXAMPLES Decrypts "logfile" and sends data to stdout. + +**rscryutil --generate-random-key 16 --keyfile /some/secured/path/keyfile** + +Generates random key and stores it in the specified keyfile. + +LOG SIGNATURES +============== + +Encrypted log files can be used together with signing. To verify such a file, +it must be decrypted first, and the verification tool **rsgtutil(1)** must be +run on the decrypted file. + +SECURITY CONSIDERATIONS +======================= + +Specifying keys directly on the command line (*--key* option) is very +insecure and should +not be done, except for testing purposes with test keys. Even then it is +recommended to use keyfiles, which are also easy to handle during testing. +Keep in mind that command history is usally be kept by bash and can also +easily be monitored. + +Local keyfiles are also a security risk. At a minimum, they should be +used with very restrictive file permissions. For this reason, +the *rscryutil* tool creates them with read permissions for the user, +only, no matter what umask is set to. + +When selecting cipher algorithms and modes, care needs to be taken. The +defaults should be reasonable safe to use, but this tends to change over +time. Keep up with the most current crypto recommendations. + + SEE ALSO ======== -**rsyslogd(8)** +**rsgtutil(1)**, **rsyslogd(8)** COPYRIGHT ========= -- cgit v1.2.3 From 64102e8cc352ffc542dca1dfcdd50f5ae776dc1f Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 12 Apr 2013 17:50:08 +0200 Subject: logenc: add capability to use key generation program to rscryutil --- runtime/libgcry_common.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++ tools/rscryutil.c | 9 +++- 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/runtime/libgcry_common.c b/runtime/libgcry_common.c index 49a0e669..63b5e5d5 100644 --- a/runtime/libgcry_common.c +++ b/runtime/libgcry_common.c @@ -75,3 +75,132 @@ gcryGetKeyFromFile(char *fn, char **key, unsigned *keylen) r = 0; done: return r; } + + +/* execute the child process (must be called in child context + * after fork). + */ + +static void +execKeyScript(char *cmd, int pipefd[]) +{ + char *newargv[] = { NULL }; + char *newenviron[] = { NULL }; + + dup2(pipefd[0], STDIN_FILENO); + dup2(pipefd[1], STDOUT_FILENO); + + /* finally exec child */ +fprintf(stderr, "pre execve: %s\n", cmd); + execve(cmd, newargv, newenviron); + /* switch to? + execlp((char*)program, (char*) program, (char*)arg, NULL); + */ + + /* we should never reach this point, but if we do, we terminate */ +done: return; +} + + +static int +openPipe(char *cmd, int *fd) +{ + int pipefd[2]; + pid_t cpid; + int r; + + if(pipe(pipefd) == -1) { + r = 1; goto done; + } + + cpid = fork(); + if(cpid == -1) { + r = 1; goto done; + } + + if(cpid == 0) { + /* we are the child */ + execKeyScript(cmd, pipefd); + exit(1); + } + + close(pipefd[1]); + *fd = pipefd[0]; + r = 0; +done: return r; +} + + +/* Read a character from the program's output. */ +// TODO: highly unoptimized version, should be used in buffered +// mode +static int +readProgChar(int fd, char *c) +{ + int r; + if(read(fd, c, 1) != 1) { + r = 1; goto done; + } + r = 0; +done: return r; +} + +/* Read a line from the script. Line is terminated by LF, which + * is NOT put into the buffer. + * buf must be 64KiB + */ +static int +readProgLine(int fd, char *buf) +{ + char c; + int r; + unsigned i; + + for(i = 0 ; i < 64*1024 ; ++i) { + if((r = readProgChar(fd, &c)) != 0) goto done; + if(c == '\n') + break; + buf[i] = c; + }; + if(i >= 64*1024) { + r = 1; goto done; + } + buf[i] = '\0'; + r = 0; +done: return r; +} +static int +readProgKey(int fd, char *buf, unsigned keylen) +{ + char c; + int r; + unsigned i; + + for(i = 0 ; i < keylen ; ++i) { + if((r = readProgChar(fd, &c)) != 0) goto done; + buf[i] = c; + }; + r = 0; +done: return r; +} + +int +gcryGetKeyFromProg(char *cmd, char **key, unsigned *keylen) +{ + int r; + int fd; + char rcvBuf[64*1024]; + + if((r = openPipe(cmd, &fd)) != 0) goto done; + if((r = readProgLine(fd, rcvBuf)) != 0) goto done; + if(strcmp(rcvBuf, "RSYSLOG-KEY-PROVIDER:0")) { + r = 2; goto done; + } + if((r = readProgLine(fd, rcvBuf)) != 0) goto done; + *keylen = atoi(rcvBuf); + if((*key = malloc(*keylen)) == NULL) { + r = -1; goto done; + } + if((r = readProgKey(fd, *key, *keylen)) != 0) goto done; +done: return r; +} diff --git a/tools/rscryutil.c b/tools/rscryutil.c index be14cde9..2591b2cc 100644 --- a/tools/rscryutil.c +++ b/tools/rscryutil.c @@ -44,6 +44,7 @@ static gcry_cipher_hd_t gcry_chd; static size_t blkLength; static char *keyfile = NULL; +static char *keyprog = NULL; static int randomKeyLen = -1; static char *cry_key = NULL; static unsigned cry_keylen = 0; @@ -387,6 +388,8 @@ setKey() getRandomKey(); else if(keyfile != NULL) getKeyFromFile(keyfile); + else if(keyprog != NULL) + gcryGetKeyFromProg(keyprog, &cry_key, &cry_keylen); if(cry_key == NULL) { fprintf(stderr, "ERROR: key must be set via some method\n"); exit(1); @@ -403,6 +406,7 @@ static struct option long_options[] = {"key", required_argument, NULL, 'K'}, {"generate-random-key", required_argument, NULL, 'r'}, {"keyfile", required_argument, NULL, 'k'}, + {"key-program", required_argument, NULL, 'p'}, {"algo", required_argument, NULL, 'a'}, {"mode", required_argument, NULL, 'm'}, {NULL, 0, NULL, 0} @@ -417,7 +421,7 @@ main(int argc, char *argv[]) char *newKeyFile = NULL; while(1) { - opt = getopt_long(argc, argv, "a:dfk:K:m:r:vVW:", long_options, NULL); + opt = getopt_long(argc, argv, "a:dfk:K:m:p:r:vVW:", long_options, NULL); if(opt == -1) break; switch(opt) { @@ -431,6 +435,9 @@ main(int argc, char *argv[]) case 'k': keyfile = optarg; break; + case 'p': + keyprog = optarg; + break; case 'f': optionForce = 1; break; -- cgit v1.2.3 From fc0babb27d9021c103b05eaae0ccc6caef12137e Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 15 Apr 2013 09:40:34 +0200 Subject: logenc: add key-program support to rsyslog crypto provider --- runtime/lmcry_gcry.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c index bcc001fc..2e4cfff3 100644 --- a/runtime/lmcry_gcry.c +++ b/runtime/lmcry_gcry.c @@ -46,6 +46,7 @@ DEFobjCurrIf(glbl) static struct cnfparamdescr cnfpdescr[] = { { "cry.key", eCmdHdlrGetWord, 0 }, { "cry.keyfile", eCmdHdlrGetWord, 0 }, + { "cry.keyprogram", eCmdHdlrGetWord, 0 }, { "cry.mode", eCmdHdlrGetWord, 0 }, /* CBC, ECB, etc */ { "cry.algo", eCmdHdlrGetWord, 0 } }; @@ -93,11 +94,14 @@ SetCnfParam(void *pT, struct nvlst *lst) unsigned keylen; uchar *key = NULL; uchar *keyfile = NULL; + uchar *keyprogram = NULL; uchar *algo = NULL; uchar *mode = NULL; + int nKeys; /* number of keys (actually methods) specified */ struct cnfparamvals *pvals; DEFiRet; + nKeys = 0; pvals = nvlstGetParams(lst, &pblk, NULL); if(Debug) { dbgprintf("param blk in lmcry_gcry:\n"); @@ -109,8 +113,13 @@ SetCnfParam(void *pT, struct nvlst *lst) continue; if(!strcmp(pblk.descr[i].name, "cry.key")) { key = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + ++nKeys; } else if(!strcmp(pblk.descr[i].name, "cry.keyfile")) { keyfile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + ++nKeys; + } else if(!strcmp(pblk.descr[i].name, "cry.keyprogram")) { + keyprogram = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + ++nKeys; } else if(!strcmp(pblk.descr[i].name, "cry.mode")) { mode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(pblk.descr[i].name, "cry.algo")) { @@ -135,9 +144,9 @@ SetCnfParam(void *pT, struct nvlst *lst) } } /* note: key must be set AFTER algo/mode is set (as it depends on them) */ - if(key != NULL && keyfile != NULL) { - errmsg.LogError(0, RS_RET_INVALID_PARAMS, "only one of the following " - "parameters can be specified: cry.key, cry.keyfile\n"); + if(nKeys != 1) { + errmsg.LogError(0, RS_RET_INVALID_PARAMS, "excactly one of the following " + "parameters can be specified: cry.key, cry.keyfile, cry.keyprogram\n"); ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } if(key != NULL) { @@ -153,6 +162,14 @@ SetCnfParam(void *pT, struct nvlst *lst) ABORT_FINALIZE(RS_RET_INVALID_PARAMS); } } + if(keyprogram != NULL) { + r = gcryGetKeyFromProg((char*)keyprogram, (char**)&key, &keylen); + if(r != 0) { + errmsg.LogError(0, RS_RET_ERR, "error %d obtaining key from program %s\n", + r, keyprogram); + ABORT_FINALIZE(RS_RET_INVALID_PARAMS); + } + } /* if we reach this point, we have a valid key */ r = rsgcrySetKey(pThis->ctx, key, keylen); -- cgit v1.2.3 From 15fc65f35079bdac64c48e9ae37833785a6d379d Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 15 Apr 2013 18:40:06 +0200 Subject: add basic doc for encryption capability --- doc/cryprov_gcry.html | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ doc/omfile.html | 7 ++- doc/sigprov_gt.html | 2 +- tools/rscryutil.rst | 7 +++ 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 doc/cryprov_gcry.html diff --git a/doc/cryprov_gcry.html b/doc/cryprov_gcry.html new file mode 100644 index 00000000..2568add9 --- /dev/null +++ b/doc/cryprov_gcry.html @@ -0,0 +1,121 @@ + + + + +libgcryt Log Crypto Provider (gcry) + + + +back to rsyslog module overview + +

libgcrypt Log Crypto Provider (gcry)

+

Signature Provider Name:    gt

+

Author: Rainer Gerhards <rgerhards@adiscon.com>

+

Supported Since: since 7.3.10 +

Description:

+

Provides encryption support to rsyslog. +

+ +

Configuration Parameters:

+

Crypto providers are loaded by omfile, when the +provider is selected in its "cry.providerName" parameter. +Parameters for the provider are given in the omfile action instance +line. +

This provider creates an encryption information file with the same base name but +the extension ".encinfo" for each log file (both for fixed-name files +as well as dynafiles). Both files together form a set. So you need to +archive both in order to prove integrity. +

    +
  • cry.algo <Encryption Algorithm>
    +The algorithm (cipher) to be used for encryption. +The default algorithm is "AES128". +
    Currently, the following Algorithms are supported: +
      +
    • 3DES +
    • CAST5 +
    • BLOWFISH +
    • AES128 +
    • AES192 +
    • AES256 +
    • TWOFISH +
    • TWOFISH128 +
    • ARCFOUR +
    • DES +
    • SERPENT128 +
    • SERPENT192 +
    • SERPENT256 +
    • RFC2268_40 +
    • SEED +
    • CAMELLIA128 +
    • CAMELLIA192 +
    • CAMELLIA256 +
    +
    + The actual availability of an algorithms depends on which ones + are compiled into libgcrypt. Note that some versions of libgcrypt + simply abort the process (rsyslogd in this case!) if a supported + algorithm is select but not available due to libgcrypt build + settings. There is nothing rsyslog can do against this. So in + order to avoid production downtime, always check carefully when + you change the algorithm. +
  • +
  • cry.mode <Algorithm Mode>
    +The encryption mode to be used. Default ist Cipher Block Chaining (CBC). +Note that not all encryption modes can be used together with all +algorithms. +
    Currently, the following modes are supported: +
      +
    • ECB +
    • CFB +
    • CBC +
    • STREAM +
    • OFB +
    • CTR +
    • AESWRAP +
    +
  • cry.key <encryption key>
    + TESTING AID, NOT FOR PRODUCTION USE. This uses the KEY specified + inside rsyslog.conf. This is the actual key, and as such this mode + is highly insecure. However, it can be useful for intial testing + steps. This option may be removed in the future. +
  • +
  • cry.keyfile <filename>
    + Reads the key from the specified file. The file must contain the key, only, + no headers or other meta information. Keyfiles can be generated via the + rscrytool utility. +
  • +
  • cry.keyprogram <path to program>
    + If given, the key is provided by a so-called "key program". This program + is executed and must return the key to (as well as some meta information) + via stdout. The core idea of key programs is that using this interface the + user can implement as complex (and secure) method to obtain keys as + desired, all without the need to make modifications to rsyslog. +
  • +
+Caveats/Known Bugs: +
    +
  • currently none known +
  • +
+

Samples:

+

This encrypts a log file. Default parameters are used, they key is +provided via a keyfile. +

+ +Note that the keyfile can be generated via the rscrytool utility (see its +documentation for how to actually do that). + + +

[rsyslog.conf overview] +[manual index] [rsyslog site]

+

This documentation is part of the +rsyslog +project.
+Copyright © 2013 by +Rainer Gerhards and +Adiscon. +Released under the GNU GPL version 3 or higher.

+ diff --git a/doc/omfile.html b/doc/omfile.html index a980d37f..06b738bb 100644 --- a/doc/omfile.html +++ b/doc/omfile.html @@ -83,11 +83,16 @@
  • DynaFile
    For each message, the file name is generated based on the given template. Then, this file is opened. As with the ``file'' property, data is appended if the file already exists. If the file does not exist, a new file is created. A cache of recent files is kept. Note that this cache can consume quite some memory (especially if large buffer sizes are used). Files are kept open as long as they stay inside the cache. Currently, files are only evicted from the cache when there is need to do so (due to insufficient cache size). To force-close (and evict) a dynafile from cache, send a HUP signal to rsyslogd.

  • -
  • Sig.Provider [ProviderName]
    +
  • Sig.Provider [ProviderName]
    Selects a signature provider for log signing. Currently, there only is one provider called "gt".

  • +
  • Cry.Provider [ProviderName]
    + Selects a crypto provider for log encryption. Currently, + there only is one provider called + "gcry".

  • +
  • Template [templateName]
    sets a new default template for file actions.

  • diff --git a/doc/sigprov_gt.html b/doc/sigprov_gt.html index 18b0ed10..caeee116 100644 --- a/doc/sigprov_gt.html +++ b/doc/sigprov_gt.html @@ -11,7 +11,7 @@

    GuardTime Log Signature Provider (gt)

    Signature Provider Name:    gt

    Author: Rainer Gerhards <rgerhards@adiscon.com>

    -

    Multi-Ruleset Support: since 7.3.9 +

    Supported Since: since 7.3.9

    Description:

    Provides the ability to sign syslog messages via the GuardTime signature services. diff --git a/tools/rscryutil.rst b/tools/rscryutil.rst index c546d855..dfd447d2 100644 --- a/tools/rscryutil.rst +++ b/tools/rscryutil.rst @@ -46,6 +46,13 @@ OPTIONS or other meta information. Keyfiles can be generated via the *--write-keyfile* option. +-p, --key-program + In this mode, the key is provided by a so-called "key program". This program + is executed and must return the key to (as well as some meta information) + via stdout. The core idea of key programs is that using this interface the + user can implement as complex (and secure) method to obtain keys as + desired, all without the need to make modifications to rsyslog. + -K, --key TESTING AID, NOT FOR PRODUCTION USE. This uses the KEY specified on the command line. This is the actual key, and as such this mode -- cgit v1.2.3 From 89ace1e401dc179a20ec283c2f7c2f80becf4700 Mon Sep 17 00:00:00 2001 From: Louis Bouchard Date: Wed, 17 Apr 2013 08:52:17 +0200 Subject: bugfix: $PreserveFQDN is not properly working closes: http://bugzilla.adiscon.com/show_bug.cgi?id=426 --- ChangeLog | 3 +++ tools/syslogd.c | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/ChangeLog b/ChangeLog index a8f6b475..b8027636 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,9 @@ Version 7.3.10 [devel] 2013-04-?? - added support for encrypting log files - bugfix: imuxsock aborted under some conditions regression from ratelimiting enhancements +- bugfix: $PreserveFQDN is not properly working + Thanks to Louis Bouchard for the patch + closes: http://bugzilla.adiscon.com/show_bug.cgi?id=426 --------------------------------------------------------------------------- Version 7.3.9 [devel] 2013-03-27 - support for signing logs added diff --git a/tools/syslogd.c b/tools/syslogd.c index e291ba47..fe1205dd 100644 --- a/tools/syslogd.c +++ b/tools/syslogd.c @@ -1528,6 +1528,13 @@ queryLocalHostname(void) */ glbl.SetLocalHostName(LocalHostName); glbl.SetLocalDomain(LocalDomain); + + if ( strlen((char*)LocalDomain) ) { + CHKmalloc(LocalFQDNName = (uchar*)malloc(strlen((char*)LocalDomain)+strlen((char*)LocalHostName)+1)); + if ( sprintf((char*)LocalFQDNName,"%s.%s",(char*)LocalHostName,(char*)LocalDomain) ) + glbl.SetLocalFQDNName(LocalFQDNName); + } + glbl.GenerateLocalHostNameProperty(); /* must be redone after conf processing, FQDN setting may have changed */ finalize_it: RETiRet; -- cgit v1.2.3