summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--configure.ac33
-rw-r--r--doc/cryprov_gcry.html121
-rw-r--r--doc/omfile.html7
-rw-r--r--doc/sigprov_gt.html2
-rw-r--r--runtime/Makefile.am24
-rw-r--r--runtime/cryprov.h39
-rw-r--r--runtime/libgcry.c426
-rw-r--r--runtime/libgcry.h99
-rw-r--r--runtime/libgcry_common.c206
-rw-r--r--runtime/lmcry_gcry.c290
-rw-r--r--runtime/lmcry_gcry.h39
-rw-r--r--runtime/rsyslog.h7
-rw-r--r--runtime/stream.c29
-rw-r--r--runtime/stream.h11
-rw-r--r--tools/Makefile.am19
-rw-r--r--tools/omfile.c73
-rw-r--r--tools/rscryutil.c512
-rw-r--r--tools/rscryutil.rst199
-rw-r--r--tools/syslogd.c7
20 files changed, 2134 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 1d04020e..4c0698de 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,14 @@
---------------------------------------------------------------------------
Version 7.3.11 [devel] 2013-04-??
+- added support for encrypting log files
+- bugfix: $PreserveFQDN is not properly working
+ Thanks to Louis Bouchard for the patch
+ closes: http://bugzilla.adiscon.com/show_bug.cgi?id=426
- bugfix: imuxsock aborted due to problem in ratelimiting code
Thanks to Tomas Heinrich for the patch.
+- bugfix: imuxsock aborted under some conditions
+ regression from ratelimiting enhancements - this was a different one
+ to the one Tomas Heinrich patched.
---------------------------------------------------------------------------
Version 7.3.10 [devel] 2013-04-10
- added RainerScript re_extract() function
diff --git a/configure.ac b/configure.ac
index c0712a5e..3c5a1002 100644
--- a/configure.ac
+++ b/configure.ac
@@ -759,6 +759,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_LIBGCRYPT, test x$enable_libgcrypt = xyes)
+
# support for building the rsyslogd runtime
AC_ARG_ENABLE(rsyslogrt,
@@ -1410,6 +1442,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 }---"
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 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="Content-Language" content="en">
+<title>libgcryt Log Crypto Provider (gcry)</title>
+</head>
+
+<body>
+<a href="rsyslog_conf_modules.html">back to rsyslog module overview</a>
+
+<h1>libgcrypt Log Crypto Provider (gcry)</h1>
+<p><b>Signature Provider Name:&nbsp;&nbsp;&nbsp; gt</b></p>
+<p><b>Author: </b>Rainer Gerhards &lt;rgerhards@adiscon.com&gt;</p>
+<p><b>Supported Since: </b>since 7.3.10
+<p><b>Description</b>:</p>
+<p>Provides encryption support to rsyslog.
+</p>
+
+<p><b>Configuration Parameters</b>:</p>
+<p>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.
+<p>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.
+<ul>
+<li><b>cry.algo</b> &lt;Encryption Algorithm&gt;<br>
+The algorithm (cipher) to be used for encryption.
+The default algorithm is "AES128".
+<br>Currently, the following Algorithms are supported:
+ <ul>
+ <li>3DES
+ <li>CAST5
+ <li>BLOWFISH
+ <li>AES128
+ <li>AES192
+ <li>AES256
+ <li>TWOFISH
+ <li>TWOFISH128
+ <li>ARCFOUR
+ <li>DES
+ <li>SERPENT128
+ <li>SERPENT192
+ <li>SERPENT256
+ <li>RFC2268_40
+ <li>SEED
+ <li>CAMELLIA128
+ <li>CAMELLIA192
+ <li>CAMELLIA256
+ </ul>
+ <br>
+ 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.
+</li>
+<li><b>cry.mode</b> &lt;Algorithm Mode&gt;<br>
+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.
+<br>Currently, the following modes are supported:
+ <ul>
+ <li>ECB
+ <li>CFB
+ <li>CBC
+ <li>STREAM
+ <li>OFB
+ <li>CTR
+ <li>AESWRAP
+ </ul>
+<li><b>cry.key</b> &lt;encryption key&gt;<br>
+ 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.
+</li>
+<li><b>cry.keyfile</b> &lt;filename&gt;<br>
+ 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.
+</li>
+<li><b>cry.keyprogram</b> &lt;path to program&gt;<br>
+ 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.
+</li>
+</ul>
+<b>Caveats/Known Bugs:</b>
+<ul>
+<li>currently none known
+</li>
+</ul>
+<p><b>Samples:</b></p>
+<p>This encrypts a log file. Default parameters are used, they key is
+provided via a keyfile.
+</p>
+<textarea rows="3" cols="60">
+action(type="omfile" file="/var/log/somelog"
+ cry.provider="gcry" keyfile="/secured/path/to/keyfile")
+</textarea>
+Note that the keyfile can be generated via the rscrytool utility (see its
+documentation for how to actually do that).
+
+
+<p>[<a href="rsyslog_conf.html">rsyslog.conf overview</a>]
+[<a href="manual.html">manual index</a>] [<a href="http://www.rsyslog.com/">rsyslog site</a>]</p>
+<p><font size="2">This documentation is part of the
+<a href="http://www.rsyslog.com/">rsyslog</a>
+project.<br>
+Copyright &copy; 2013 by
+<a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and
+<a href="http://www.adiscon.com/">Adiscon</a>.
+Released under the GNU GPL version 3 or higher.</font></p>
+</body></html>
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 @@
<li><strong>DynaFile </strong><br>
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. <br></li><br>
- <li><strong>Sig.Provider </strong>[ProviderName]<br>
+ <li><b>Sig.Provider </b>[ProviderName]<br>
Selects a signature provider for log signing. Currently,
there only is one provider called
"<a href="sigprov_gt.html">gt</a>".<br></li><br>
+ <li><b>Cry.Provider </b>[ProviderName]<br>
+ Selects a crypto provider for log encryption. Currently,
+ there only is one provider called
+ "<a href="cryprov_gcry.html">gcry</a>".<br></li><br>
+
<li><strong>Template </strong>[templateName]<br>
sets a new default template for file actions.<br></li><br>
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 @@
<h1>GuardTime Log Signature Provider (gt)</h1>
<p><b>Signature Provider Name:&nbsp;&nbsp;&nbsp; gt</b></p>
<p><b>Author: </b>Rainer Gerhards &lt;rgerhards@adiscon.com&gt;</p>
-<p><b>Multi-Ruleset Support: </b>since 7.3.9
+<p><b>Supported Since: </b>since 7.3.9
<p><b>Description</b>:</p>
<p>Provides the ability to sign syslog messages via the
GuardTime signature services.
diff --git a/runtime/Makefile.am b/runtime/Makefile.am
index c05cc773..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
@@ -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
@@ -175,9 +175,25 @@ lmnsd_gtls_la_LIBADD = $(GNUTLS_LIBS)
endif
#
+# support library for libgcrypt
+#
+if ENABLE_LIBGCRYPT
+ 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
+ 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)
+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/cryprov.h b/runtime/cryprov.h
new file mode 100644
index 00000000..8496b745
--- /dev/null
+++ b/runtime/cryprov.h
@@ -0,0 +1,39 @@
+/* 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 <gcrypt.h>
+
+/* 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, off64_t offsLogfile);
+ENDinterface(cryprov)
+#define cryprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
+#endif /* #ifndef INCLUDED_CRYPROV_H */
diff --git a/runtime/libgcry.c b/runtime/libgcry.c
new file mode 100644
index 00000000..e57ee8bc
--- /dev/null
+++ b/runtime/libgcry.c
@@ -0,0 +1,426 @@
+/* gcry.c - rsyslog's libgcrypt based crypto provider
+ *
+ * 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:<hex> The initial vector used at block start. Also indicates start
+ * start of block.
+ * END:<int> 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
+ * 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.
+ *
+ * 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 <stdio.h>
+#include <gcrypt.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "rsyslog.h"
+#include "libgcry.h"
+
+
+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;
+ DEFiRet;
+
+ CHKmalloc(gf = calloc(1, sizeof(struct gcryfile_s)));
+ gf->ctx = ctx;
+ snprintf(fn, sizeof(fn), "%s%s", logfn, ENCINFO_SUFFIX);
+ fn[MAXFNAME] = '\0'; /* be on save side */
+ gf->eiName = (uchar*) strdup(fn);
+ *pgf = gf;
+finalize_it:
+ RETiRet;
+}
+
+
+gcryctx
+gcryCtxNew(void)
+{
+ gcryctx ctx;
+ ctx = calloc(1, sizeof(struct gcryctx_s));
+ ctx->algo = GCRY_CIPHER_AES128;
+ ctx->mode = GCRY_CIPHER_MODE_CBC;
+ return ctx;
+}
+
+int
+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;
+}
+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;
+}
+
+/* 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;
+ int r;
+
+ reqKeyLen = gcry_cipher_get_algo_keylen(ctx->algo);
+ if(keyLen != reqKeyLen) {
+ r = reqKeyLen;
+ goto done;
+ }
+ ctx->keyLen = keyLen;
+ ctx->key = malloc(keyLen);
+ memcpy(ctx->key, key, keyLen);
+ r = 0;
+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
+ * (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, uchar *fname)
+{
+ gcry_error_t gcryError;
+ gcryfile gf = NULL;
+ uchar *iv = NULL;
+ DEFiRet;
+
+ CHKiRet(gcryfileConstruct(ctx, &gf, fname));
+
+ gf->blkLength = gcry_cipher_get_algo_blklen(ctx->algo);
+
+ gcryError = gcry_cipher_open(&gf->chd, ctx->algo, ctx->mode, 0);
+ 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, gf->ctx->key, gf->ctx->keyLen);
+ if (gcryError) {
+ dbgprintf("gcry_cipher_setkey failed: %s/%s\n",
+ gcry_strsource(gcryError),
+ gcry_strerror(gcryError));
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ 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, -1);
+ 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;
+}
diff --git a/runtime/libgcry.h b/runtime/libgcry.h
new file mode 100644
index 00000000..5dde1576
--- /dev/null
+++ b/runtime/libgcry.h
@@ -0,0 +1,99 @@
+/* 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 <gt_base.h>
+
+
+struct gcryctx_s {
+ uchar *key;
+ size_t keyLen;
+ int algo;
+ int mode;
+};
+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 */
+ uchar *eiName; /* name of .encinfo file */
+ int fd; /* descriptor of .encinfo file (-1 if not open) */
+ 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);
+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, 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 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"
+
+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/libgcry_common.c b/runtime/libgcry_common.c
new file mode 100644
index 00000000..63b5e5d5
--- /dev/null
+++ b/runtime/libgcry_common.c
@@ -0,0 +1,206 @@
+/* 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 <stdio.h>
+#include <gcrypt.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#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;
+}
+
+
+/* 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/runtime/lmcry_gcry.c b/runtime/lmcry_gcry.c
new file mode 100644
index 00000000..2e4cfff3
--- /dev/null
+++ b/runtime/lmcry_gcry.c
@@ -0,0 +1,290 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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.key", eCmdHdlrGetWord, 0 },
+ { "cry.keyfile", eCmdHdlrGetWord, 0 },
+ { "cry.keyprogram", eCmdHdlrGetWord, 0 },
+ { "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.
+ */
+static rsRetVal
+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 *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");
+ cnfparamsPrint(&pblk, pvals);
+ }
+
+ 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);
+ ++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")) {
+ 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);
+ }
+ }
+ 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(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) {
+ errmsg.LogError(0, RS_RET_ERR, "Note: specifying an actual key directly from the "
+ "config file is highly insecure - DO NOT USE FOR PRODUCTION");
+ 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(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);
+ 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:
+ RETiRet;
+}
+
+
+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);
+
+ CHKiRet(rsgcryInitCrypt(pThis->ctx, pgf, 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;
+}
+
+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, off64_t offsLogfile)
+{
+ DEFiRet;
+dbgprintf("DDDD: onFileClose\n");
+ gcryfileDestruct(pF, offsLogfile);
+
+ 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 b1f7d632..9fdf2b0f 100644
--- a/runtime/rsyslog.h
+++ b/runtime/rsyslog.h
@@ -405,6 +405,13 @@ 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 */
+ 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 */
+ 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/runtime/stream.c b/runtime/stream.c
index 00afcdaa..b0df8418 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.
*
@@ -45,6 +45,7 @@
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
+#include <sys/types.h>
#include <sys/stat.h> /* required for HP UX */
#include <errno.h>
#include <pthread.h>
@@ -56,6 +57,7 @@
#include "stream.h"
#include "unicode-helper.h"
#include "module-template.h"
+#include "cryprov.h"
#if HAVE_SYS_PRCTL_H
# include <sys/prctl.h>
#endif
@@ -253,6 +255,11 @@ doPhysOpen(strm_t *pThis)
pThis->bIsTTY = 0;
}
+dbgprintf("DDDD: cryprov %p\n", pThis->cryprov);
+ if(pThis->cryprov != NULL) {
+ CHKiRet(pThis->cryprov->OnFileOpen(pThis->cryprovData,
+ pThis->pszCurrFName, &pThis->cryprovFileData));
+ }
finalize_it:
RETiRet;
}
@@ -382,6 +389,7 @@ strmWaitAsyncWriterDone(strm_t *pThis)
*/
static rsRetVal strmCloseFile(strm_t *pThis)
{
+ off64_t currOffs;
DEFiRet;
ASSERT(pThis != NULL);
@@ -402,9 +410,14 @@ 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, currOffs);
+ pThis->cryprovFileData = NULL;
+ }
}
if(pThis->fdDir != -1) {
@@ -1200,10 +1213,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 +1621,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 +1958,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..938782f7 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -41,7 +41,8 @@ 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`
+#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 = -I../runtime $(RSRT_CFLAGS) $(LIBGCRYPT_CFLAGS)
+rscryutil_LDFLAGS = `libgcrypt-config --libs`
+rscryutil_LDADD = ../runtime/libgcry.la $(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 faf3c24f..46d882bf 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,50 @@ initSigprov(instanceData *pData, struct nvlst *lst)
done: return;
}
+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);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ 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);
+ ABORT_FINALIZE(RS_RET_CRYPROV_ERR);
+ }
+
+ if(pData->cryprov.Construct(&pData->cryprovData) != RS_RET_OK) {
+ errmsg.LogError(0, RS_RET_CRYPROV_ERR, "omfile: error constructing "
+ "crypto provider %s dataset - encryption disabled",
+ szDrvrName);
+ ABORT_FINALIZE(RS_RET_CRYPROV_ERR);
+ }
+ CHKiRet(pData->cryprov.SetCnfParam(pData->cryprovData, lst));
+
+ dbgprintf("loaded crypto provider %s, data instance at %p\n",
+ szDrvrName, pData->cryprovData);
+ pData->useCryprov = 1;
+finalize_it:
+ RETiRet;
+}
+
BEGINnewActInst
struct cnfparamvals *pvals;
uchar *tplToUse;
@@ -1102,6 +1167,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 +1185,10 @@ CODESTARTnewActInst
initSigprov(pData, lst);
}
+ if(pData->cryprovName != NULL) {
+ CHKiRet(initCryprov(pData, lst));
+ }
+
tplToUse = ustrdup((pData->tplName == NULL) ? getDfltTpl() : pData->tplName);
CHKiRet(OMSRsetEntry(*ppOMSR, 0, tplToUse, OMSR_NO_RQD_TPL_OPTS));
diff --git a/tools/rscryutil.c b/tools/rscryutil.c
new file mode 100644
index 00000000..2591b2cc
--- /dev/null
+++ b/tools/rscryutil.c
@@ -0,0 +1,512 @@
+/* This is a tool for processing rsyslog encrypted log files.
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <gcrypt.h>
+
+#include "rsyslog.h"
+#include "libgcry.h"
+
+
+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 *keyfile = NULL;
+static char *keyprog = 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
+ */
+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
+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 r = 0;
+ gcry_error_t gcryError;
+ char iv[4096];
+
+ 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);
+ r = 1; goto done;
+ }
+ if((r = eiGetIV(eifp, iv, blkLength)) != 0) goto done;
+
+ 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", cry_keylen,
+ keyLength);
+ r = 1; goto done;
+ }
+
+ gcryError = gcry_cipher_open(&gcry_chd, cry_algo, cry_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, cry_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 = memchr(buf, 0x00, *plen);
+ 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
+decryptBlock(FILE *fpin, FILE *fpout, off64_t blkEnd, off64_t *pCurrOffs)
+{
+ gcry_error_t gcryError;
+ size_t nRead, nWritten;
+ size_t toRead;
+ size_t leftTillBlkEnd;
+ char buf[64*1024];
+
+ leftTillBlkEnd = blkEnd - *pCurrOffs;
+ while(1) {
+ 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;
+ 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_decrypt 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 int
+doDecrypt(FILE *logfp, FILE *eifp, FILE *outfp)
+{
+ off64_t blkEnd;
+ off64_t currOffs = 0;
+ int r;
+
+ while(1) {
+ /* process block */
+ if(initCrypt(eifp) != 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)
+{
+ FILE *logfp = NULL, *eifp = NULL;
+ int r = 0;
+ char eifname[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;
+ }
+ snprintf(eifname, sizeof(eifname), "%s%s", name, ENCINFO_SUFFIX);
+ eifname[sizeof(eifname)-1] = '\0';
+ if((eifp = fopen(eifname, "r")) == NULL) {
+ perror(eifname);
+ goto err;
+ }
+ if(eiCheckFiletype(eifp) != 0)
+ goto err;
+ }
+
+ doDecrypt(logfp, eifp, stdout);
+
+ fclose(logfp); logfp = NULL;
+ fclose(eifp); eifp = NULL;
+ return;
+
+err:
+ fprintf(stderr, "error %d processing file %s\n", r, name);
+ if(logfp != NULL)
+ fclose(logfp);
+}
+
+static void
+write_keyfile(char *fn)
+{
+ 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((r = write(fd, cry_key, cry_keylen)) != (ssize_t)cry_keylen) {
+ fprintf(stderr, "error writing keyfile (ret=%d) ", r);
+ perror(fn);
+ exit(1);
+ }
+ 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);
+ }
+}
+
+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);
+ 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);
+ }
+}
+
+static struct option long_options[] =
+{
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"decrypt", no_argument, NULL, 'd'},
+ {"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'},
+ {"key-program", required_argument, NULL, 'p'},
+ {"algo", required_argument, NULL, 'a'},
+ {"mode", required_argument, NULL, 'm'},
+ {NULL, 0, NULL, 0}
+};
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ int opt;
+ int temp;
+ char *newKeyFile = NULL;
+
+ while(1) {
+ opt = getopt_long(argc, argv, "a:dfk:K:m:p:r:vVW:", long_options, NULL);
+ if(opt == -1)
+ break;
+ switch(opt) {
+ case 'd':
+ mode = MD_DECRYPT;
+ break;
+ case 'W':
+ mode = MD_WRITE_KEYFILE;
+ newKeyFile = optarg;
+ break;
+ case 'k':
+ keyfile = optarg;
+ break;
+ case 'p':
+ keyprog = 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"
+ "Do NOT use this for PRODUCTION use.\n");
+ cry_key = optarg;
+ cry_keylen = strlen(cry_key);
+ 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;
+ 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;
+ }
+ }
+
+ 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(newKeyFile);
+ } else {
+ if(optind == argc)
+ decrypt("-");
+ else {
+ for(i = optind ; i < argc ; ++i)
+ decrypt(argv[i]);
+ }
+ }
+
+ 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
new file mode 100644
index 00000000..dfd447d2
--- /dev/null
+++ b/tools/rscryutil.rst
@@ -0,0 +1,199 @@
+=========
+rscryutil
+=========
+
+--------------------------
+Manage Encrypted Log Files
+--------------------------
+
+:Author: Rainer Gerhards <rgerhards@adiscon.com>
+:Date: 2013-04-15
+: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.
+
+-W, --write-keyfile <file>
+ Utility function to write a key to a keyfile. The key can be obtained
+ via any method.
+
+-v, --verbose
+ Select verbose mode.
+
+-f, --force
+ Forces operations that otherwise would fail.
+
+-k, --keyfile <file>
+ Reads the key from <file>. File _must_ contain the key, only, no headers
+ or other meta information. Keyfiles can be generated via the
+ *--write-keyfile* option.
+
+-p, --key-program <path-to-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 <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.
+
+-a, --algo <algo>
+ Sets the encryption algorightm (cipher) to be used. See below
+ for supported algorithms. The default is "AES128".
+
+-m, --mode <mode>
+ Sets the ciphermode to be used. See below for supported modes.
+ The default is "CBC".
+
+-r, --generate-random-key <bytes>
+ Generates a random key of length <bytes>. 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
+===============
+
+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. 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
+==========
+
+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
+========
+
+**rscryutil logfile**
+
+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
+========
+**rsgtutil(1)**, **rsyslogd(8)**
+
+COPYRIGHT
+=========
+
+This page is part of the *rsyslog* project, and is available under
+LGPLv2.
diff --git a/tools/syslogd.c b/tools/syslogd.c
index 03fb2bd9..77adb2cb 100644
--- a/tools/syslogd.c
+++ b/tools/syslogd.c
@@ -1529,6 +1529,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;