diff options
author | Michael Biebl <biebl@debian.org> | 2013-03-27 12:03:58 +0100 |
---|---|---|
committer | Michael Biebl <biebl@debian.org> | 2013-03-27 12:03:58 +0100 |
commit | d7c2f609d30d5e4d2c69dd2dc7305bd8ec5736ec (patch) | |
tree | ab4cde54583b0e1eb92fdafe0eb7282b7923daa9 /runtime | |
parent | 86831d7a4f485e19befa8cc500d17766798ad07c (diff) | |
download | rsyslog-d7c2f609d30d5e4d2c69dd2dc7305bd8ec5736ec.tar.gz |
Imported Upstream version 7.3.9upstream/7.3.9
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/Makefile.am | 21 | ||||
-rw-r--r-- | runtime/Makefile.in | 195 | ||||
-rw-r--r-- | runtime/cfsysline.c | 28 | ||||
-rw-r--r-- | runtime/debug.c | 2 | ||||
-rw-r--r-- | runtime/librsgt.c | 844 | ||||
-rw-r--r-- | runtime/librsgt.h | 388 | ||||
-rw-r--r-- | runtime/librsgt_read.c | 1092 | ||||
-rw-r--r-- | runtime/lmsig_gt.c | 229 | ||||
-rw-r--r-- | runtime/lmsig_gt.h | 40 | ||||
-rw-r--r-- | runtime/rsyslog.h | 1 | ||||
-rw-r--r-- | runtime/sd-daemon.c | 172 | ||||
-rw-r--r-- | runtime/sd-daemon.h | 63 | ||||
-rw-r--r-- | runtime/sigprov.h | 37 | ||||
-rw-r--r-- | runtime/srUtils.h | 1 | ||||
-rw-r--r-- | runtime/srutils.c | 22 | ||||
-rw-r--r-- | runtime/stream.c | 103 | ||||
-rw-r--r-- | runtime/stream.h | 5 | ||||
-rw-r--r-- | runtime/wtp.c | 2 |
18 files changed, 3096 insertions, 149 deletions
diff --git a/runtime/Makefile.am b/runtime/Makefile.am index fbc92d9..c05cc77 100644 --- a/runtime/Makefile.am +++ b/runtime/Makefile.am @@ -1,6 +1,6 @@ sbin_PROGRAMS = man_MANS = -noinst_LTLIBRARIES = librsyslog.la +noinst_LTLIBRARIES = librsyslog.la librsgt.la pkglib_LTLIBRARIES = #pkglib_LTLIBRARIES = librsyslog.la @@ -17,6 +17,7 @@ librsyslog_la_SOURCES = \ module-template.h \ im-helper.h \ obj-types.h \ + sigprov.h \ nsd.h \ glbl.h \ glbl.c \ @@ -92,6 +93,7 @@ librsyslog_la_SOURCES = \ ../template.h # the files with ../ we need to work on - so that they either become part of the # runtime or will no longer be needed. -- rgerhards, 2008-06-13 +# if WITH_MODDIRS librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools @@ -172,6 +174,19 @@ lmnsd_gtls_la_LDFLAGS = -module -avoid-version lmnsd_gtls_la_LIBADD = $(GNUTLS_LIBS) endif +# +# support library for guardtime +# +if ENABLE_GUARDTIME + 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 + lmsig_gt_la_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) + lmsig_gt_la_LDFLAGS = -module -avoid-version + lmsig_gt_la_LIBADD = librsgt.la $(GUARDTIME_LIBS) +endif + + update-systemd: - curl http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c > sd-daemon.c - curl http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h > sd-daemon.h + curl http://cgit.freedesktop.org/systemd/systemd/plain/src/libsystemd-daemon/sd-daemon.c > sd-daemon.c + curl http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h > sd-daemon.h diff --git a/runtime/Makefile.in b/runtime/Makefile.in index 098f81e..6ed5994 100644 --- a/runtime/Makefile.in +++ b/runtime/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.3 from Makefile.am. +# Makefile.in generated by automake 1.12.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2012 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -17,6 +16,23 @@ VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -59,8 +75,10 @@ sbin_PROGRAMS = # GnuTLS netstream driver # @ENABLE_GNUTLS_TRUE@am__append_4 = lmnsd_gtls.la +@ENABLE_GUARDTIME_TRUE@am__append_5 = lmsig_gt.la subdir = runtime -DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(top_srcdir)/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/atomic_operations.m4 \ $(top_srcdir)/m4/atomic_operations_64bit.m4 \ @@ -102,6 +120,15 @@ am__uninstall_files_from_dir = { \ } am__installdirs = "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(sbindir)" LTLIBRARIES = $(noinst_LTLIBRARIES) $(pkglib_LTLIBRARIES) +librsgt_la_LIBADD = +am__librsgt_la_SOURCES_DIST = librsgt.c librsgt_read.c librsgt.h +@ENABLE_GUARDTIME_TRUE@am_librsgt_la_OBJECTS = librsgt.lo \ +@ENABLE_GUARDTIME_TRUE@ librsgt_read.lo +librsgt_la_OBJECTS = $(am_librsgt_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = am__DEPENDENCIES_1 = librsyslog_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) @@ -124,9 +151,6 @@ am_librsyslog_la_OBJECTS = librsyslog_la-rsyslog.lo \ librsyslog_la-hashtable_itr.lo librsyslog_la-outchannel.lo \ librsyslog_la-template.lo librsyslog_la_OBJECTS = $(am_librsyslog_la_OBJECTS) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent lmnet_la_DEPENDENCIES = am__lmnet_la_SOURCES_DIST = net.c net.h @ENABLE_INET_TRUE@am_lmnet_la_OBJECTS = lmnet_la-net.lo @@ -180,6 +204,16 @@ lmregexp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(lmregexp_la_LDFLAGS) $(LDFLAGS) -o $@ @ENABLE_REGEXP_TRUE@am_lmregexp_la_rpath = -rpath $(pkglibdir) +@ENABLE_GUARDTIME_TRUE@lmsig_gt_la_DEPENDENCIES = librsgt.la \ +@ENABLE_GUARDTIME_TRUE@ $(am__DEPENDENCIES_1) +am__lmsig_gt_la_SOURCES_DIST = lmsig_gt.c lmsig_gt.h +@ENABLE_GUARDTIME_TRUE@am_lmsig_gt_la_OBJECTS = \ +@ENABLE_GUARDTIME_TRUE@ lmsig_gt_la-lmsig_gt.lo +lmsig_gt_la_OBJECTS = $(am_lmsig_gt_la_OBJECTS) +lmsig_gt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(lmsig_gt_la_LDFLAGS) $(LDFLAGS) -o $@ +@ENABLE_GUARDTIME_TRUE@am_lmsig_gt_la_rpath = -rpath $(pkglibdir) lmstrmsrv_la_DEPENDENCIES = am__lmstrmsrv_la_SOURCES_DIST = strmsrv.c strmsrv.h strms_sess.c \ strms_sess.h @@ -199,6 +233,18 @@ lmzlibw_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(lmzlibw_la_LDFLAGS) $(LDFLAGS) -o $@ @ENABLE_ZLIB_TRUE@am_lmzlibw_la_rpath = -rpath $(pkglibdir) PROGRAMS = $(sbin_PROGRAMS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles @@ -211,31 +257,34 @@ LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) -am__v_CC_0 = @echo " CC " $@; -AM_V_at = $(am__v_at_@AM_V@) -am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) -am__v_at_0 = @ +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) -am__v_CCLD_0 = @echo " CCLD " $@; -AM_V_GEN = $(am__v_GEN_@AM_V@) -am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) -am__v_GEN_0 = @echo " GEN " $@; -SOURCES = $(librsyslog_la_SOURCES) $(lmnet_la_SOURCES) \ - $(lmnetstrms_la_SOURCES) $(lmnsd_gtls_la_SOURCES) \ - $(lmnsd_ptcp_la_SOURCES) $(lmregexp_la_SOURCES) \ +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(librsgt_la_SOURCES) $(librsyslog_la_SOURCES) \ + $(lmnet_la_SOURCES) $(lmnetstrms_la_SOURCES) \ + $(lmnsd_gtls_la_SOURCES) $(lmnsd_ptcp_la_SOURCES) \ + $(lmregexp_la_SOURCES) $(lmsig_gt_la_SOURCES) \ $(lmstrmsrv_la_SOURCES) $(lmzlibw_la_SOURCES) -DIST_SOURCES = $(librsyslog_la_SOURCES) $(am__lmnet_la_SOURCES_DIST) \ - $(am__lmnetstrms_la_SOURCES_DIST) \ +DIST_SOURCES = $(am__librsgt_la_SOURCES_DIST) $(librsyslog_la_SOURCES) \ + $(am__lmnet_la_SOURCES_DIST) $(am__lmnetstrms_la_SOURCES_DIST) \ $(am__lmnsd_gtls_la_SOURCES_DIST) \ $(am__lmnsd_ptcp_la_SOURCES_DIST) \ $(am__lmregexp_la_SOURCES_DIST) \ + $(am__lmsig_gt_la_SOURCES_DIST) \ $(am__lmstrmsrv_la_SOURCES_DIST) \ $(am__lmzlibw_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -273,6 +322,8 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@ GNUTLS_LIBS = @GNUTLS_LIBS@ GREP = @GREP@ GSS_LIBS = @GSS_LIBS@ +GUARDTIME_CFLAGS = @GUARDTIME_CFLAGS@ +GUARDTIME_LIBS = @GUARDTIME_LIBS@ HAVE_MYSQL_CONFIG = @HAVE_MYSQL_CONFIG@ HAVE_ORACLE_CONFIG = @HAVE_ORACLE_CONFIG@ HAVE_PGSQL_CONFIG = @HAVE_PGSQL_CONFIG@ @@ -349,6 +400,7 @@ RSRT_CFLAGS = @RSRT_CFLAGS@ RSRT_CFLAGS1 = @RSRT_CFLAGS1@ RSRT_LIBS = @RSRT_LIBS@ RSRT_LIBS1 = @RSRT_LIBS1@ +RST2MAN = @RST2MAN@ RT_LIBS = @RT_LIBS@ SED = @SED@ SET_MAKE = @SET_MAKE@ @@ -419,9 +471,9 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ man_MANS = -noinst_LTLIBRARIES = librsyslog.la +noinst_LTLIBRARIES = librsyslog.la librsgt.la pkglib_LTLIBRARIES = $(am__append_1) $(am__append_2) $(am__append_3) \ - $(am__append_4) + $(am__append_4) $(am__append_5) #pkglib_LTLIBRARIES = librsyslog.la librsyslog_la_SOURCES = \ rsyslog.c \ @@ -436,6 +488,7 @@ librsyslog_la_SOURCES = \ module-template.h \ im-helper.h \ obj-types.h \ + sigprov.h \ nsd.h \ glbl.h \ glbl.c \ @@ -513,6 +566,7 @@ librsyslog_la_SOURCES = \ @WITH_MODDIRS_FALSE@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 # the files with ../ we need to work on - so that they either become part of the # runtime or will no longer be needed. -- rgerhards, 2008-06-13 +# @WITH_MODDIRS_TRUE@librsyslog_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -D_PATH_MODDIR=\"$(pkglibdir)/:$(moddirs)\" $(PTHREADS_CFLAGS) $(LIBEE_CFLAGS) -I\$(top_srcdir)/tools #librsyslog_la_LDFLAGS = -module -avoid-version librsyslog_la_LIBADD = $(DL_LIBS) $(RT_LIBS) $(LIBEE_LIBS) @@ -556,6 +610,15 @@ librsyslog_la_LIBADD = $(DL_LIBS) $(RT_LIBS) $(LIBEE_LIBS) @ENABLE_GNUTLS_TRUE@lmnsd_gtls_la_CPPFLAGS = $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) $(GNUTLS_CFLAGS) @ENABLE_GNUTLS_TRUE@lmnsd_gtls_la_LDFLAGS = -module -avoid-version @ENABLE_GNUTLS_TRUE@lmnsd_gtls_la_LIBADD = $(GNUTLS_LIBS) + +# +# support library for guardtime +# +@ENABLE_GUARDTIME_TRUE@librsgt_la_SOURCES = librsgt.c librsgt_read.c librsgt.h +@ENABLE_GUARDTIME_TRUE@lmsig_gt_la_SOURCES = lmsig_gt.c lmsig_gt.h +@ENABLE_GUARDTIME_TRUE@lmsig_gt_la_CPPFLAGS = $(RSRT_CFLAGS) $(GUARDTIME_CFLAGS) +@ENABLE_GUARDTIME_TRUE@lmsig_gt_la_LDFLAGS = -module -avoid-version +@ENABLE_GUARDTIME_TRUE@lmsig_gt_la_LIBADD = librsgt.la $(GUARDTIME_LIBS) all: all-am .SUFFIXES: @@ -593,15 +656,16 @@ $(am__aclocal_m4_deps): clean-noinstLTLIBRARIES: -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) - @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ - dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ - test "$$dir" != "$$p" || dir=.; \ - echo "rm -f \"$${dir}/so_locations\""; \ - rm -f "$${dir}/so_locations"; \ - done + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) - test -z "$(pkglibdir)" || $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ @@ -609,6 +673,8 @@ install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) else :; fi; \ done; \ test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ } @@ -624,12 +690,16 @@ uninstall-pkglibLTLIBRARIES: clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) - @list='$(pkglib_LTLIBRARIES)'; for p in $$list; do \ - dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ - test "$$dir" != "$$p" || dir=.; \ - echo "rm -f \"$${dir}/so_locations\""; \ - rm -f "$${dir}/so_locations"; \ - done + @list='$(pkglib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } +librsgt.la: $(librsgt_la_OBJECTS) $(librsgt_la_DEPENDENCIES) $(EXTRA_librsgt_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(librsgt_la_OBJECTS) $(librsgt_la_LIBADD) $(LIBS) librsyslog.la: $(librsyslog_la_OBJECTS) $(librsyslog_la_DEPENDENCIES) $(EXTRA_librsyslog_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(librsyslog_la_OBJECTS) $(librsyslog_la_LIBADD) $(LIBS) lmnet.la: $(lmnet_la_OBJECTS) $(lmnet_la_DEPENDENCIES) $(EXTRA_lmnet_la_DEPENDENCIES) @@ -642,14 +712,19 @@ lmnsd_ptcp.la: $(lmnsd_ptcp_la_OBJECTS) $(lmnsd_ptcp_la_DEPENDENCIES) $(EXTRA_lm $(AM_V_CCLD)$(lmnsd_ptcp_la_LINK) $(am_lmnsd_ptcp_la_rpath) $(lmnsd_ptcp_la_OBJECTS) $(lmnsd_ptcp_la_LIBADD) $(LIBS) lmregexp.la: $(lmregexp_la_OBJECTS) $(lmregexp_la_DEPENDENCIES) $(EXTRA_lmregexp_la_DEPENDENCIES) $(AM_V_CCLD)$(lmregexp_la_LINK) $(am_lmregexp_la_rpath) $(lmregexp_la_OBJECTS) $(lmregexp_la_LIBADD) $(LIBS) +lmsig_gt.la: $(lmsig_gt_la_OBJECTS) $(lmsig_gt_la_DEPENDENCIES) $(EXTRA_lmsig_gt_la_DEPENDENCIES) + $(AM_V_CCLD)$(lmsig_gt_la_LINK) $(am_lmsig_gt_la_rpath) $(lmsig_gt_la_OBJECTS) $(lmsig_gt_la_LIBADD) $(LIBS) lmstrmsrv.la: $(lmstrmsrv_la_OBJECTS) $(lmstrmsrv_la_DEPENDENCIES) $(EXTRA_lmstrmsrv_la_DEPENDENCIES) $(AM_V_CCLD)$(lmstrmsrv_la_LINK) $(am_lmstrmsrv_la_rpath) $(lmstrmsrv_la_OBJECTS) $(lmstrmsrv_la_LIBADD) $(LIBS) lmzlibw.la: $(lmzlibw_la_OBJECTS) $(lmzlibw_la_DEPENDENCIES) $(EXTRA_lmzlibw_la_DEPENDENCIES) $(AM_V_CCLD)$(lmzlibw_la_LINK) $(am_lmzlibw_la_rpath) $(lmzlibw_la_OBJECTS) $(lmzlibw_la_LIBADD) $(LIBS) install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) - test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p || test -f $$p1; \ @@ -696,6 +771,8 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librsgt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librsgt_read.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librsyslog_la-action.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librsyslog_la-cfsysline.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librsyslog_la-conf.Plo@am__quote@ @@ -742,6 +819,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmnsd_ptcp_la-nsdpoll_ptcp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmnsd_ptcp_la-nsdsel_ptcp.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmregexp_la-regexp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmsig_gt_la-lmsig_gt.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmstrmsrv_la-strms_sess.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmstrmsrv_la-strmsrv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lmzlibw_la-zlibw.Plo@am__quote@ @@ -1089,6 +1167,13 @@ lmregexp_la-regexp.lo: regexp.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lmregexp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lmregexp_la-regexp.lo `test -f 'regexp.c' || echo '$(srcdir)/'`regexp.c +lmsig_gt_la-lmsig_gt.lo: lmsig_gt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lmsig_gt_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lmsig_gt_la-lmsig_gt.lo -MD -MP -MF $(DEPDIR)/lmsig_gt_la-lmsig_gt.Tpo -c -o lmsig_gt_la-lmsig_gt.lo `test -f 'lmsig_gt.c' || echo '$(srcdir)/'`lmsig_gt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lmsig_gt_la-lmsig_gt.Tpo $(DEPDIR)/lmsig_gt_la-lmsig_gt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='lmsig_gt.c' object='lmsig_gt_la-lmsig_gt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lmsig_gt_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lmsig_gt_la-lmsig_gt.lo `test -f 'lmsig_gt.c' || echo '$(srcdir)/'`lmsig_gt.c + lmstrmsrv_la-strmsrv.lo: strmsrv.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lmstrmsrv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lmstrmsrv_la-strmsrv.lo -MD -MP -MF $(DEPDIR)/lmstrmsrv_la-strmsrv.Tpo -c -o lmstrmsrv_la-strmsrv.lo `test -f 'strmsrv.c' || echo '$(srcdir)/'`strmsrv.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lmstrmsrv_la-strmsrv.Tpo $(DEPDIR)/lmstrmsrv_la-strmsrv.Plo @@ -1165,6 +1250,20 @@ GTAGS: && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags @@ -1310,15 +1409,15 @@ uninstall-am: uninstall-pkglibLTLIBRARIES uninstall-sbinPROGRAMS .PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLTLIBRARIES clean-pkglibLTLIBRARIES \ - clean-sbinPROGRAMS ctags distclean distclean-compile \ - distclean-generic distclean-libtool distclean-tags distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-man install-pdf \ - install-pdf-am install-pkglibLTLIBRARIES install-ps \ - install-ps-am install-sbinPROGRAMS install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ + clean-sbinPROGRAMS cscopelist ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-pkglibLTLIBRARIES \ + install-ps install-ps-am install-sbinPROGRAMS install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags uninstall uninstall-am uninstall-pkglibLTLIBRARIES \ @@ -1326,8 +1425,8 @@ uninstall-am: uninstall-pkglibLTLIBRARIES uninstall-sbinPROGRAMS update-systemd: - curl http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c > sd-daemon.c - curl http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h > sd-daemon.h + curl http://cgit.freedesktop.org/systemd/systemd/plain/src/libsystemd-daemon/sd-daemon.c > sd-daemon.c + curl http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h > sd-daemon.h # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/runtime/cfsysline.c b/runtime/cfsysline.c index 6b06d42..a437b7f 100644 --- a/runtime/cfsysline.c +++ b/runtime/cfsysline.c @@ -350,8 +350,9 @@ static rsRetVal doGetGID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *p struct group gBuf; DEFiRet; uchar szName[256]; - int bufSize = 2048; + int bufSize = 1024; char * stringBuf = NULL; + int err; assert(pp != NULL); assert(*pp != NULL); @@ -361,20 +362,21 @@ static rsRetVal doGetGID(uchar **pp, rsRetVal (*pSetHdlr)(void*, uid_t), void *p ABORT_FINALIZE(RS_RET_NOT_FOUND); } - - CHKmalloc(stringBuf = malloc(bufSize)); - while(pgBuf == NULL) { - errno = 0; - getgrnam_r((char*)szName, &gBuf, stringBuf, bufSize, &pgBuf); - if((pgBuf == NULL) && (errno == ERANGE)) { - /* Increase bufsize and try again.*/ - bufSize *= 2; - CHKmalloc(stringBuf = realloc(stringBuf, bufSize)); - } - } + do { + /* Increase bufsize and try again.*/ + bufSize *= 2; + CHKmalloc(stringBuf = realloc(stringBuf, bufSize)); + err = getgrnam_r((char*)szName, &gBuf, stringBuf, bufSize, &pgBuf); + } while((pgBuf == NULL) && (err == ERANGE)); if(pgBuf == NULL) { - errmsg.LogError(0, RS_RET_NOT_FOUND, "ID for group '%s' could not be found or error", (char*)szName); + if (err != 0) { + rs_strerror_r(err, stringBuf, bufSize); + errmsg.LogError(0, RS_RET_NOT_FOUND, "Query for group '%s' resulted in an error: %s\n", + (char*)szName, stringBuf); + } else { + errmsg.LogError(0, RS_RET_NOT_FOUND, "ID for group '%s' could not be found", (char*)szName); + } iRet = RS_RET_NOT_FOUND; } else { if(pSetHdlr == NULL) { diff --git a/runtime/debug.c b/runtime/debug.c index 3cb2223..876f61d 100644 --- a/runtime/debug.c +++ b/runtime/debug.c @@ -303,7 +303,7 @@ static inline void dbgFuncDBRemoveMutexLock(dbgFuncDB_t *pFuncDB, pthread_mutex_ void dbgOutputTID(char* name) { -# ifdef HAVE_SYSCALL +# if defined(HAVE_SYSCALL) && defined(HAVE_SYS_gettid) if(bOutputTidToStderr) fprintf(stderr, "thread tid %u, name '%s'\n", (unsigned)syscall(SYS_gettid), name); diff --git a/runtime/librsgt.c b/runtime/librsgt.c new file mode 100644 index 0000000..afafe2f --- /dev/null +++ b/runtime/librsgt.c @@ -0,0 +1,844 @@ +/* librsgt.c - rsyslog's guardtime support library + * + * Regarding the online algorithm for Merkle tree signing. Expected + * calling sequence is: + * + * sigblkConstruct + * for each signature block: + * sigblkInit + * for each record: + * sigblkAddRecord + * sigblkFinish + * sigblkDestruct + * + * Obviously, the next call after sigblkFinsh must either be to + * sigblkInit or sigblkDestruct (if no more signature blocks are + * to be emitted, e.g. on file close). sigblkDestruct saves state + * information (most importantly last block hash) and sigblkConstruct + * reads (or initilizes if not present) it. + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <assert.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#define MAXFNAME 1024 + +#include <gt_http.h> + +#include "librsgt.h" + +typedef unsigned char uchar; +#ifndef VERSION +#define VERSION "no-version" +#endif + + +static void +reportErr(gtctx ctx, char *errmsg) +{ + if(ctx->errFunc == NULL) + goto done; + ctx->errFunc(ctx->usrptr, (uchar*)errmsg); +done: return; +} + +static void +reportGTAPIErr(gtctx ctx, gtfile gf, char *apiname, int ecode) +{ + char errbuf[4096]; + snprintf(errbuf, sizeof(errbuf), "%s[%s:%d]: %s", + (gf == NULL) ? (uchar*)"" : gf->sigfilename, + apiname, ecode, GT_getErrorString(ecode)); + errbuf[sizeof(errbuf)-1] = '\0'; + reportErr(ctx, errbuf); +} + +void +rsgtsetErrFunc(gtctx ctx, void (*func)(void*, uchar *), void *usrptr) +{ + ctx->usrptr = usrptr; + ctx->errFunc = func; +} + +imprint_t * +rsgtImprintFromGTDataHash(GTDataHash *hash) +{ + imprint_t *imp; + + if((imp = calloc(1, sizeof(imprint_t))) == NULL) { + goto done; + } + imp->hashID = hashIdentifier(hash->algorithm), + imp->len = hash->digest_length; + if((imp->data = (uint8_t*)malloc(imp->len)) == NULL) { + free(imp); imp = NULL; goto done; + } + memcpy(imp->data, hash->digest, imp->len); +done: return imp; +} + +void +rsgtimprintDel(imprint_t *imp) +{ + if(imp != NULL) { + free(imp->data), + free(imp); + } +} + +int +rsgtInit(char *usragent) +{ + int r = 0; + int ret = GT_OK; + + ret = GT_init(); + if(ret != GT_OK) { + r = 1; + goto done; + } + ret = GTHTTP_init(usragent, 1); + if(ret != GT_OK) { + r = 1; + goto done; + } +done: return r; +} + +void +rsgtExit(void) +{ + GTHTTP_finalize(); + GT_finalize(); +} + + +static inline gtfile +rsgtfileConstruct(gtctx ctx) +{ + gtfile gf; + if((gf = calloc(1, sizeof(struct gtfile_s))) == NULL) + goto done; + gf->ctx = ctx; + gf->hashAlg = ctx->hashAlg; + gf->bKeepRecordHashes = ctx->bKeepRecordHashes; + gf->bKeepTreeHashes = ctx->bKeepTreeHashes; + gf->x_prev = NULL; + +done: return gf; +} + +static inline int +tlvbufPhysWrite(gtfile gf) +{ + ssize_t lenBuf; + ssize_t iTotalWritten; + ssize_t iWritten; + char *pWriteBuf; + int r = 0; + + lenBuf = gf->tlvIdx; + pWriteBuf = gf->tlvBuf; + iTotalWritten = 0; + do { + iWritten = write(gf->fd, pWriteBuf, lenBuf); + if(iWritten < 0) { + iWritten = 0; /* we have written NO bytes! */ + if(errno == EINTR) { + /*NO ERROR, just continue */; + } else { + reportErr(gf->ctx, "signature file write error"); + r = RSGTE_IO; + goto finalize_it; + } + } + /* advance buffer to next write position */ + iTotalWritten += iWritten; + lenBuf -= iWritten; + pWriteBuf += iWritten; + } while(lenBuf > 0); /* Warning: do..while()! */ + +finalize_it: + gf->tlvIdx = 0; + return r; +} + +static inline int +tlvbufChkWrite(gtfile gf) +{ + if(gf->tlvIdx == sizeof(gf->tlvBuf)) { + return tlvbufPhysWrite(gf); + } + return 0; +} + + +/* write to TLV file buffer. If buffer is full, an actual call occurs. Else + * output is written only on flush or close. + */ +static inline int +tlvbufAddOctet(gtfile gf, int8_t octet) +{ + int r; + r = tlvbufChkWrite(gf); + if(r != 0) goto done; + gf->tlvBuf[gf->tlvIdx++] = octet; +done: return r; +} +static inline int +tlvbufAddOctetString(gtfile gf, uint8_t *octet, int size) +{ + int i, r = 0; + for(i = 0 ; i < size ; ++i) { + r = tlvbufAddOctet(gf, octet[i]); + if(r != 0) goto done; + } +done: return r; +} +/* return the actual length in to-be-written octets of an integer */ +static inline uint8_t +tlvbufGetInt64OctetSize(uint64_t val) +{ + if(val >> 56) + return 8; + if((val >> 48) & 0xff) + return 7; + if((val >> 40) & 0xff) + return 6; + if((val >> 32) & 0xff) + return 5; + if((val >> 24) & 0xff) + return 4; + if((val >> 16) & 0xff) + return 3; + if((val >> 8) & 0xff) + return 2; + return 1; +} +static inline int +tlvbufAddInt64(gtfile gf, uint64_t val) +{ + uint8_t doWrite = 0; + int r; + if(val >> 56) { + r = tlvbufAddOctet(gf, (val >> 56) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 48) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 48) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 40) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 40) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 32) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 32) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 24) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 24) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 16) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 16) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + if(doWrite || ((val >> 8) & 0xff)) { + r = tlvbufAddOctet(gf, (val >> 8) & 0xff), doWrite = 1; + if(r != 0) goto done; + } + r = tlvbufAddOctet(gf, val & 0xff); +done: return r; +} + + +int +tlv8Write(gtfile gf, int flags, int tlvtype, int len) +{ + int r; + r = tlvbufAddOctet(gf, (flags << 5)|tlvtype); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, len & 0xff); +done: return r; +} + +int +tlv16Write(gtfile gf, int flags, int tlvtype, uint16_t len) +{ + uint16_t typ; + int r; + typ = ((flags|1) << 13)|tlvtype; + r = tlvbufAddOctet(gf, typ >> 8); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, typ & 0xff); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, (len >> 8) & 0xff); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, len & 0xff); +done: return r; +} + +int +tlvFlush(gtfile gf) +{ + return (gf->tlvIdx == 0) ? 0 : tlvbufPhysWrite(gf); +} + +int +tlvWriteHash(gtfile gf, uint16_t tlvtype, GTDataHash *rec) +{ + unsigned tlvlen; + int r; + tlvlen = 1 + rec->digest_length; + r = tlv16Write(gf, 0x00, tlvtype, tlvlen); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); + if(r != 0) goto done; + r = tlvbufAddOctetString(gf, rec->digest, rec->digest_length); +done: return r; +} + +int +tlvWriteBlockSig(gtfile gf, uchar *der, uint16_t lenDer) +{ + unsigned tlvlen; + uint8_t tlvlenRecords; + int r; + + tlvlenRecords = tlvbufGetInt64OctetSize(gf->nRecords); + tlvlen = 2 + 1 /* hash algo TLV */ + + 2 + hashOutputLengthOctets(gf->hashAlg) /* iv */ + + 2 + 1 + gf->lenBlkStrtHash /* last hash */ + + 2 + tlvlenRecords /* rec-count */ + + 4 + lenDer /* rfc-3161 */; + /* write top-level TLV object (block-sig */ + r = tlv16Write(gf, 0x00, 0x0902, tlvlen); + if(r != 0) goto done; + /* and now write the children */ + //FIXME: flags??? + /* hash-algo */ + r = tlv8Write(gf, 0x00, 0x00, 1); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); + if(r != 0) goto done; + /* block-iv */ + r = tlv8Write(gf, 0x00, 0x01, hashOutputLengthOctets(gf->hashAlg)); + if(r != 0) goto done; + r = tlvbufAddOctetString(gf, gf->IV, hashOutputLengthOctets(gf->hashAlg)); + if(r != 0) goto done; + /* last-hash */ + r = tlv8Write(gf, 0x00, 0x02, gf->lenBlkStrtHash+1); + if(r != 0) goto done; + r = tlvbufAddOctet(gf, hashIdentifier(gf->hashAlg)); + if(r != 0) goto done; + r = tlvbufAddOctetString(gf, gf->blkStrtHash, gf->lenBlkStrtHash); + if(r != 0) goto done; + /* rec-count */ + r = tlv8Write(gf, 0x00, 0x03, tlvlenRecords); + if(r != 0) goto done; + r = tlvbufAddInt64(gf, gf->nRecords); + if(r != 0) goto done; + /* rfc-3161 */ + r = tlv16Write(gf, 0x00, 0x906, lenDer); + if(r != 0) goto done; + r = tlvbufAddOctetString(gf, der, lenDer); +done: return r; +} + +/* support for old platforms - graceful degrade */ +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif +/* read rsyslog log state file; if we cannot access it or the + * contents looks invalid, we flag it as non-present (and thus + * begin a new hash chain). + * The context is initialized accordingly. + */ +static void +readStateFile(gtfile gf) +{ + int fd; + struct rsgtstatefile sf; + + fd = open((char*)gf->statefilename, O_RDONLY|O_NOCTTY|O_CLOEXEC, 0600); + if(fd == -1) goto err; + + if(read(fd, &sf, sizeof(sf)) != sizeof(sf)) goto err; + if(strncmp(sf.hdr, "GTSTAT10", 8)) goto err; + + gf->lenBlkStrtHash = sf.lenHash; + gf->blkStrtHash = calloc(1, gf->lenBlkStrtHash); + if(read(fd, gf->blkStrtHash, gf->lenBlkStrtHash) + != gf->lenBlkStrtHash) { + free(gf->blkStrtHash); + goto err; + } +return; + +err: + gf->lenBlkStrtHash = hashOutputLengthOctets(gf->hashAlg); + gf->blkStrtHash = calloc(1, gf->lenBlkStrtHash); +} + +/* persist all information that we need to re-open and append + * to a log signature file. + */ +static void +writeStateFile(gtfile gf) +{ + int fd; + struct rsgtstatefile sf; + + fd = open((char*)gf->statefilename, + O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0600); + if(fd == -1) + goto done; + + memcpy(sf.hdr, "GTSTAT10", 8); + sf.hashID = hashIdentifier(gf->hashAlg); + sf.lenHash = gf->x_prev->len; + /* if the write fails, we cannot do anything against that. We check + * the condition just to keep the compiler happy. + */ + if(write(fd, &sf, sizeof(sf))){}; + if(write(fd, gf->x_prev->data, gf->x_prev->len)){}; + close(fd); +done: return; +} + + +int +tlvClose(gtfile gf) +{ + int r; + r = tlvFlush(gf); + close(gf->fd); + gf->fd = -1; + writeStateFile(gf); + return r; +} + + +/* note: if file exists, the last hash for chaining must + * be read from file. + */ +int +tlvOpen(gtfile gf, char *hdr, unsigned lenHdr) +{ + int r = 0; + gf->fd = open((char*)gf->sigfilename, + O_WRONLY|O_APPEND|O_NOCTTY|O_CLOEXEC, 0600); + if(gf->fd == -1) { + /* looks like we need to create a new file */ + gf->fd = open((char*)gf->sigfilename, + O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0600); + if(gf->fd == -1) { + r = RSGTE_IO; + goto done; + } + memcpy(gf->tlvBuf, hdr, lenHdr); + gf->tlvIdx = lenHdr; + } else { + gf->tlvIdx = 0; /* header already present! */ + } + /* we now need to obtain the last previous hash, so that + * we can continue the hash chain. We do not check for error + * as a state file error can be recovered by graceful degredation. + */ + readStateFile(gf); +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(gtfile gf) +{ + int hashlen; + int fd; + + hashlen = hashOutputLengthOctets(gf->hashAlg); + gf->IV = malloc(hashlen); /* 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, gf->IV, hashlen)) {}; /* keep compiler happy */ + close(fd); + } +} + +gtctx +rsgtCtxNew(void) +{ + gtctx ctx; + ctx = calloc(1, sizeof(struct gtctx_s)); + ctx->hashAlg = GT_HASHALG_SHA256; + ctx->errFunc = NULL; + ctx->usrptr = NULL; + ctx->timestamper = strdup( + "http://stamper.guardtime.net/gt-signingservice"); + return ctx; +} + +/* either returns gtfile object or NULL if something went wrong */ +gtfile +rsgtCtxOpenFile(gtctx ctx, unsigned char *logfn) +{ + gtfile gf; + char fn[MAXFNAME+1]; + + if((gf = rsgtfileConstruct(ctx)) == NULL) + goto done; + + snprintf(fn, sizeof(fn), "%s.gtsig", logfn); + fn[MAXFNAME] = '\0'; /* be on save side */ + gf->sigfilename = (uchar*) strdup(fn); + snprintf(fn, sizeof(fn), "%s.gtstate", logfn); + fn[MAXFNAME] = '\0'; /* be on save side */ + gf->statefilename = (uchar*) strdup(fn); + if(tlvOpen(gf, LOGSIGHDR, sizeof(LOGSIGHDR)-1) != 0) { + reportErr(ctx, "signature file open failed"); + gf = NULL; + } +done: return gf; +} + + +/* returns 0 on succes, 1 if algo is unknown */ +int +rsgtSetHashFunction(gtctx ctx, char *algName) +{ + int r = 0; + if(!strcmp(algName, "SHA2-256")) + ctx->hashAlg = GT_HASHALG_SHA256; + else if(!strcmp(algName, "SHA2-384")) + ctx->hashAlg = GT_HASHALG_SHA384; + else if(!strcmp(algName, "SHA2-512")) + ctx->hashAlg = GT_HASHALG_SHA512; + else if(!strcmp(algName, "SHA1")) + ctx->hashAlg = GT_HASHALG_SHA1; + else if(!strcmp(algName, "RIPEMD-160")) + ctx->hashAlg = GT_HASHALG_RIPEMD160; + else if(!strcmp(algName, "SHA2-224")) + ctx->hashAlg = GT_HASHALG_SHA224; + else + r = 1; + return r; +} + +int +rsgtfileDestruct(gtfile gf) +{ + int r = 0; + if(gf == NULL) + goto done; + + if(!gf->disabled && gf->bInBlk) { + r = sigblkFinish(gf); + if(r != 0) gf->disabled = 1; + } + if(!gf->disabled) + r = tlvClose(gf); + free(gf->sigfilename); + free(gf->statefilename); + free(gf->IV); + free(gf->blkStrtHash); + rsgtimprintDel(gf->x_prev); + free(gf); +done: return r; +} + +void +rsgtCtxDel(gtctx ctx) +{ + if(ctx != NULL) { + free(ctx->timestamper); + free(ctx); + } +} + +/* new sigblk is initialized, but maybe in existing ctx */ +void +sigblkInit(gtfile gf) +{ + if(gf == NULL) goto done; + seedIV(gf); + memset(gf->roots_valid, 0, sizeof(gf->roots_valid)/sizeof(char)); + gf->nRoots = 0; + gf->nRecords = 0; + gf->bInBlk = 1; +done: return; +} + + +/* concat: add IV to buffer */ +static inline void +bufAddIV(gtfile gf, uchar *buf, size_t *len) +{ + memcpy(buf+*len, gf->IV, hashOutputLengthOctets(gf->hashAlg)); + *len += sizeof(gf->IV); +} + + +/* concat: add imprint to buffer */ +static inline void +bufAddImprint(gtfile gf, uchar *buf, size_t *len, imprint_t *imp) +{ + if(imp == NULL) { + /* TODO: how to get the REAL HASH ID? --> add field? */ + buf[*len] = hashIdentifier(gf->hashAlg); + ++(*len); + memcpy(buf+*len, gf->blkStrtHash, gf->lenBlkStrtHash); + *len += gf->lenBlkStrtHash; + } else { + buf[*len] = imp->hashID; + ++(*len); + memcpy(buf+*len, imp->data, imp->len); + *len += imp->len; + } +} +/* concat: add hash to buffer */ +static inline void +bufAddHash(gtfile gf, uchar *buf, size_t *len, GTDataHash *hash) +{ + buf[*len] = hashIdentifier(gf->hashAlg); + ++(*len); + memcpy(buf+*len, hash->digest, hash->digest_length); + *len += hash->digest_length; +} +/* concat: add tree level to buffer */ +static inline void +bufAddLevel(uchar *buf, size_t *len, uint8_t level) +{ + memcpy(buf+*len, &level, sizeof(level)); + *len += sizeof(level); +} + + +int +hash_m(gtfile gf, GTDataHash **m) +{ + int rgt; + uchar concatBuf[16*1024]; + size_t len = 0; + int r = 0; + + bufAddImprint(gf, concatBuf, &len, gf->x_prev); + bufAddIV(gf, concatBuf, &len); + rgt = GTDataHash_create(gf->hashAlg, concatBuf, len, m); + if(rgt != GT_OK) { + reportGTAPIErr(gf->ctx, gf, "GTDataHash_create", rgt); + r = RSGTE_HASH_CREATE; + goto done; + } +done: return r; +} + +int +hash_r(gtfile gf, GTDataHash **r, const uchar *rec, const size_t len) +{ + int ret = 0, rgt; + rgt = GTDataHash_create(gf->hashAlg, rec, len, r); + if(rgt != GT_OK) { + reportGTAPIErr(gf->ctx, gf, "GTDataHash_create", rgt); + ret = RSGTE_HASH_CREATE; + goto done; + } +done: return ret; +} + + +int +hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *rec, + uint8_t level) +{ + int r = 0, rgt; + uchar concatBuf[16*1024]; + size_t len = 0; + + bufAddHash(gf, concatBuf, &len, m); + bufAddHash(gf, concatBuf, &len, rec); + bufAddLevel(concatBuf, &len, level); + rgt = GTDataHash_create(gf->hashAlg, concatBuf, len, node); + if(rgt != GT_OK) { + reportGTAPIErr(gf->ctx, gf, "GTDataHash_create", rgt); + r = RSGTE_HASH_CREATE; + goto done; + } +done: return r; +} + + +int +sigblkAddRecord(gtfile gf, const uchar *rec, const size_t len) +{ + GTDataHash *x; /* current hash */ + GTDataHash *m, *r, *t, *t_del; + uint8_t j; + int ret = 0; + + if(gf == NULL || gf->disabled) goto done; + if((ret = hash_m(gf, &m)) != 0) goto done; + if((ret = hash_r(gf, &r, rec, len)) != 0) goto done; + if(gf->bKeepRecordHashes) + tlvWriteHash(gf, 0x0900, r); + if((ret = hash_node(gf, &x, m, r, 1)) != 0) goto done; /* hash leaf */ + /* persists x here if Merkle tree needs to be persisted! */ + if(gf->bKeepTreeHashes) + tlvWriteHash(gf, 0x0901, x); + rsgtimprintDel(gf->x_prev); + gf->x_prev = rsgtImprintFromGTDataHash(x); + /* add x to the forest as new leaf, update roots list */ + t = x; + for(j = 0 ; j < gf->nRoots ; ++j) { + if(gf->roots_valid[j] == 0) { + gf->roots_hash[j] = t; + gf->roots_valid[j] = 1; + t = NULL; + break; + } else if(t != NULL) { + /* hash interim node */ + t_del = t; + ret = hash_node(gf, &t, gf->roots_hash[j], t_del, j+2); + gf->roots_valid[j] = 0; + GTDataHash_free(gf->roots_hash[j]); + GTDataHash_free(t_del); + if(ret != 0) goto done; + if(gf->bKeepTreeHashes) + tlvWriteHash(gf, 0x0901, t); + } + } + if(t != NULL) { + /* new level, append "at the top" */ + gf->roots_hash[gf->nRoots] = t; + gf->roots_valid[gf->nRoots] = 1; + ++gf->nRoots; + assert(gf->nRoots < MAX_ROOTS); + t = NULL; + } + ++gf->nRecords; + + /* cleanup (x is cleared as part of the roots array) */ + GTDataHash_free(m); + GTDataHash_free(r); + + if(gf->nRecords == gf->blockSizeLimit) { + ret = sigblkFinish(gf); + if(ret != 0) goto done; + sigblkInit(gf); + } +done: + if(ret != 0) { + gf->disabled = 1; + } + return ret; +} + +static int +timestampIt(gtfile gf, GTDataHash *hash) +{ + unsigned char *der; + size_t lenDer; + int r = GT_OK; + int ret = 0; + GTTimestamp *timestamp = NULL; + + /* Get the timestamp. */ + r = GTHTTP_createTimestampHash(hash, gf->ctx->timestamper, ×tamp); + + if(r != GT_OK) { + reportGTAPIErr(gf->ctx, gf, "GTHTTP_createTimestampHash", r); + ret = 1; + goto done; + } + + /* Encode timestamp. */ + r = GTTimestamp_getDEREncoded(timestamp, &der, &lenDer); + if(r != GT_OK) { + reportGTAPIErr(gf->ctx, gf, "GTTimestamp_getDEREncoded", r); + ret = 1; + goto done; + } + + tlvWriteBlockSig(gf, der, lenDer); + +done: + GT_free(der); + GTTimestamp_free(timestamp); + return ret; +} + + +int +sigblkFinish(gtfile gf) +{ + GTDataHash *root, *rootDel; + int8_t j; + int ret = 0; + + if(gf->nRecords == 0) + goto done; + + root = NULL; + for(j = 0 ; j < gf->nRoots ; ++j) { + if(root == NULL) { + root = gf->roots_valid[j] ? gf->roots_hash[j] : NULL; + gf->roots_valid[j] = 0; + } else if(gf->roots_valid[j]) { + rootDel = root; + ret = hash_node(gf, &root, gf->roots_hash[j], rootDel, j+2); + gf->roots_valid[j] = 0; + GTDataHash_free(gf->roots_hash[j]); + GTDataHash_free(rootDel); + if(ret != 0) goto done; /* checks hash_node() result! */ + } + } + if((ret = timestampIt(gf, root)) != 0) goto done; + + GTDataHash_free(root); + free(gf->blkStrtHash); + gf->lenBlkStrtHash = gf->x_prev->len; + gf->blkStrtHash = malloc(gf->lenBlkStrtHash); + memcpy(gf->blkStrtHash, gf->x_prev->data, gf->x_prev->len); +done: + gf->bInBlk = 0; + return ret; +} diff --git a/runtime/librsgt.h b/runtime/librsgt.h new file mode 100644 index 0000000..bfcc462 --- /dev/null +++ b/runtime/librsgt.h @@ -0,0 +1,388 @@ +/* librsgt.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_LIBRSGT_H +#define INCLUDED_LIBRSGT_H +#include <gt_base.h> + +/* Max number of roots inside the forest. This permits blocks of up to + * 2^MAX_ROOTS records. We assume that 64 is sufficient for all use + * cases ;) [and 64 is not really a waste of memory, so we do not even + * try to work with reallocs and such...] + */ +#define MAX_ROOTS 64 +#define LOGSIGHDR "LOGSIG10" + +/* context for gt calls. This primarily serves as a container for the + * config settings. The actual file-specific data is kept in gtfile. + */ +struct gtctx_s { + enum GTHashAlgorithm hashAlg; + uint8_t bKeepRecordHashes; + uint8_t bKeepTreeHashes; + uint64_t blockSizeLimit; + char *timestamper; + void (*errFunc)(void *, unsigned char*); + void *usrptr; /* for error function */ +}; +typedef struct gtctx_s *gtctx; +typedef struct gtfile_s *gtfile; +typedef struct gterrctx_s gterrctx_t; +typedef struct imprint_s imprint_t; +typedef struct block_sig_s block_sig_t; +typedef struct tlvrecord_s tlvrecord_t; + +/* this describes a file, as far as librsgt is concerned */ +struct gtfile_s { + /* the following data items are mirrored from gtctx to + * increase cache hit ratio (they are frequently accesed). + */ + enum GTHashAlgorithm hashAlg; + uint8_t bKeepRecordHashes; + uint8_t bKeepTreeHashes; + /* end mirrored properties */ + uint8_t disabled; /* permits to disable this file --> set to 1 */ + uint64_t blockSizeLimit; + uint8_t *IV; /* initial value for blinding masks */ + imprint_t *x_prev; /* last leaf hash (maybe of previous block) --> preserve on term */ + unsigned char *sigfilename; + unsigned char *statefilename; + int fd; + unsigned char *blkStrtHash; /* last hash from previous block */ + uint16_t lenBlkStrtHash; + uint64_t nRecords; /* current number of records in current block */ + uint64_t bInBlk; /* are we currently inside a blk --> need to finish on close */ + int8_t nRoots; + /* algo engineering: roots structure is split into two arrays + * in order to improve cache hits. + */ + int8_t roots_valid[MAX_ROOTS]; + GTDataHash *roots_hash[MAX_ROOTS]; + /* data members for the associated TLV file */ + char tlvBuf[4096]; + int tlvIdx; /* current index into tlvBuf */ + gtctx ctx; +}; + +struct tlvrecord_s { + uint16_t tlvtype; + uint16_t tlvlen; + uint8_t hdr[4]; /* the raw header (as persisted to file) */ + uint8_t lenHdr; /* length of raw header */ + uint8_t data[64*1024]; /* the actual data part (of length tlvlen) */ +}; + +/* The following structure describes the "error context" to be used + * for verification and similiar reader functions. While verifying, + * we need some information (like filenames or block numbers) that + * is not readily available from the other objects (or not even known + * to librsgt). In order to provide meaningful error messages, this + * information must be passed in from the external callers. In order + * to centralize information (and make it more manageable), we use + * ths error context here, which contains everything needed to + * generate good error messages. Members of this structure are + * maintained both by library users (the callers) as well as + * the library itself. Who does what simply depends on who has + * the relevant information. + */ +struct gterrctx_s { + FILE *fp; /**< file for error messages */ + char *filename; + uint8_t verbose; + uint64_t recNumInFile; + uint64_t recNum; + uint64_t blkNum; + uint8_t treeLevel; + GTDataHash *computedHash; + GTDataHash *lefthash, *righthash; /* hashes to display if tree hash fails */ + imprint_t *fileHash; + int gtstate; /* status from last relevant GT.*() function call */ + char *errRec; + char *frstRecInBlk; /* This holds the first message seen inside the current block */ +}; + +struct imprint_s { + uint8_t hashID; + int len; + uint8_t *data; +}; + +#define SIGID_RFC3161 0 +struct block_sig_s { + uint8_t hashID; + uint8_t sigID; /* what type of *signature*? */ + uint8_t *iv; + imprint_t lastHash; + uint64_t recCount; + struct { + struct { + uint8_t *data; + size_t len; /* must be size_t due to GT API! */ + } der; + } sig; +}; + + +/* the following defines the gtstate file record. Currently, this record + * is fixed, we may change that over time. + */ +struct rsgtstatefile { + char hdr[8]; /* must be "GTSTAT10" */ + uint8_t hashID; + uint8_t lenHash; + /* after that, the hash value is contained within the file */ +}; + +/* Flags and record types for TLV handling */ +#define RSGT_FLAG_TLV16 0x20 + +/* error states */ +#define RSGTE_IO 1 /* any kind of io error */ +#define RSGTE_FMT 2 /* data fromat error */ +#define RSGTE_INVLTYP 3 /* invalid TLV type record (unexcpected at this point) */ +#define RSGTE_OOM 4 /* ran out of memory */ +#define RSGTE_LEN 5 /* error related to length records */ +#define RSGTE_TS_EXTEND 6/* error extending timestamp */ +#define RSGTE_INVLD_RECCNT 7/* mismatch between actual records and records + given in block-sig record */ +#define RSGTE_INVLHDR 8/* invalid file header */ +#define RSGTE_EOF 9 /* specific EOF */ +#define RSGTE_MISS_REC_HASH 10 /* record hash missing when expected */ +#define RSGTE_MISS_TREE_HASH 11 /* tree hash missing when expected */ +#define RSGTE_INVLD_REC_HASH 12 /* invalid record hash (failed verification) */ +#define RSGTE_INVLD_TREE_HASH 13 /* invalid tree hash (failed verification) */ +#define RSGTE_INVLD_REC_HASHID 14 /* invalid record hash ID (failed verification) */ +#define RSGTE_INVLD_TREE_HASHID 15 /* invalid tree hash ID (failed verification) */ +#define RSGTE_MISS_BLOCKSIG 16 /* block signature record missing when expected */ +#define RSGTE_INVLD_TIMESTAMP 17 /* RFC3161 timestamp is invalid */ +#define RSGTE_TS_DERDECODE 18 /* error DER-Decoding a timestamp */ +#define RSGTE_TS_DERENCODE 19 /* error DER-Encoding a timestamp */ +#define RSGTE_HASH_CREATE 20 /* error creating a hash */ + +/* the following function maps RSGTE_* state to a string - must be updated + * whenever a new state is added. + * Note: it is thread-safe to call this function, as it returns a pointer + * into constant memory pool. + */ +static inline char * +RSGTE2String(int err) +{ + switch(err) { + case 0: + return "success"; + case RSGTE_IO: + return "i/o error"; + case RSGTE_FMT: + return "data format error"; + case RSGTE_INVLTYP: + return "invalid/unexpected tlv record type"; + case RSGTE_OOM: + return "out of memory"; + case RSGTE_LEN: + return "length record problem"; + case RSGTE_TS_EXTEND: + return "error extending timestamp"; + case RSGTE_INVLD_RECCNT: + return "mismatch between actual record count and number in block signature record"; + case RSGTE_INVLHDR: + return "invalid file header"; + case RSGTE_EOF: + return "EOF"; + case RSGTE_MISS_REC_HASH: + return "record hash missing"; + case RSGTE_MISS_TREE_HASH: + return "tree hash missing"; + case RSGTE_INVLD_REC_HASH: + return "record hash mismatch"; + case RSGTE_INVLD_TREE_HASH: + return "tree hash mismatch"; + case RSGTE_INVLD_REC_HASHID: + return "invalid record hash ID"; + case RSGTE_INVLD_TREE_HASHID: + return "invalid tree hash ID"; + case RSGTE_MISS_BLOCKSIG: + return "missing block signature record"; + case RSGTE_INVLD_TIMESTAMP: + return "RFC3161 timestamp invalid"; + case RSGTE_TS_DERDECODE: + return "error DER-decoding RFC3161 timestamp"; + case RSGTE_TS_DERENCODE: + return "error DER-encoding RFC3161 timestamp"; + case RSGTE_HASH_CREATE: + return "error creating hash"; + default: + return "unknown error"; + } +} + + +static inline uint16_t +hashOutputLengthOctets(uint8_t hashID) +{ + switch(hashID) { + case GT_HASHALG_SHA1: /* paper: SHA1 */ + return 20; + case GT_HASHALG_RIPEMD160: /* paper: RIPEMD-160 */ + return 20; + case GT_HASHALG_SHA224: /* paper: SHA2-224 */ + return 28; + case GT_HASHALG_SHA256: /* paper: SHA2-256 */ + return 32; + case GT_HASHALG_SHA384: /* paper: SHA2-384 */ + return 48; + case GT_HASHALG_SHA512: /* paper: SHA2-512 */ + return 64; + default:return 32; + } +} + +static inline uint8_t +hashIdentifier(enum GTHashAlgorithm hashID) +{ + switch(hashID) { + case GT_HASHALG_SHA1: /* paper: SHA1 */ + return 0x00; + case GT_HASHALG_RIPEMD160: /* paper: RIPEMD-160 */ + return 0x02; + case GT_HASHALG_SHA224: /* paper: SHA2-224 */ + return 0x03; + case GT_HASHALG_SHA256: /* paper: SHA2-256 */ + return 0x01; + case GT_HASHALG_SHA384: /* paper: SHA2-384 */ + return 0x04; + case GT_HASHALG_SHA512: /* paper: SHA2-512 */ + return 0x05; + default:return 0xff; + } +} +static inline char * +hashAlgName(uint8_t hashID) +{ + switch(hashID) { + case GT_HASHALG_SHA1: + return "SHA1"; + case GT_HASHALG_RIPEMD160: + return "RIPEMD-160"; + case GT_HASHALG_SHA224: + return "SHA2-224"; + case GT_HASHALG_SHA256: + return "SHA2-256"; + case GT_HASHALG_SHA384: + return "SHA2-384"; + case GT_HASHALG_SHA512: + return "SHA2-512"; + default:return "[unknown]"; + } +} +static inline enum GTHashAlgorithm +hashID2Alg(uint8_t hashID) +{ + switch(hashID) { + case 0x00: + return GT_HASHALG_SHA1; + case 0x02: + return GT_HASHALG_RIPEMD160; + case 0x03: + return GT_HASHALG_SHA224; + case 0x01: + return GT_HASHALG_SHA256; + case 0x04: + return GT_HASHALG_SHA384; + case 0x05: + return GT_HASHALG_SHA512; + default: + return 0xff; + } +} +static inline char * +sigTypeName(uint8_t sigID) +{ + switch(sigID) { + case SIGID_RFC3161: + return "RFC3161"; + default:return "[unknown]"; + } +} +static inline uint16_t +getIVLen(block_sig_t *bs) +{ + return hashOutputLengthOctets(bs->hashID); +} +static inline void +rsgtSetTimestamper(gtctx ctx, char *timestamper) +{ + free(ctx->timestamper); + ctx->timestamper = strdup(timestamper); +} +static inline void +rsgtSetBlockSizeLimit(gtctx ctx, uint64_t limit) +{ + ctx->blockSizeLimit = limit; +} +static inline void +rsgtSetKeepRecordHashes(gtctx ctx, int val) +{ + ctx->bKeepRecordHashes = val; +} +static inline void +rsgtSetKeepTreeHashes(gtctx ctx, int val) +{ + ctx->bKeepTreeHashes = val; +} + +int rsgtSetHashFunction(gtctx ctx, char *algName); +int rsgtInit(char *usragent); +void rsgtExit(void); +gtctx rsgtCtxNew(void); +void rsgtsetErrFunc(gtctx ctx, void (*func)(void*, unsigned char *), void *usrptr); +gtfile rsgtCtxOpenFile(gtctx ctx, unsigned char *logfn); +int rsgtfileDestruct(gtfile gf); +void rsgtCtxDel(gtctx ctx); +void sigblkInit(gtfile gf); +int sigblkAddRecord(gtfile gf, const unsigned char *rec, const size_t len); +int sigblkFinish(gtfile gf); +imprint_t * rsgtImprintFromGTDataHash(GTDataHash *hash); +void rsgtimprintDel(imprint_t *imp); +/* reader functions */ +int rsgt_tlvrdHeader(FILE *fp, unsigned char *hdr); +int rsgt_tlvrd(FILE *fp, tlvrecord_t *rec, void *obj); +void rsgt_tlvprint(FILE *fp, uint16_t tlvtype, void *obj, uint8_t verbose); +void rsgt_printBLOCK_SIG(FILE *fp, block_sig_t *bs, uint8_t verbose); +int rsgt_getBlockParams(FILE *fp, uint8_t bRewind, block_sig_t **bs, uint8_t *bHasRecHashes, uint8_t *bHasIntermedHashes); +int rsgt_chkFileHdr(FILE *fp, char *expect); +gtfile rsgt_vrfyConstruct_gf(void); +void rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHasIntermedHashes); +int rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, FILE *nsigfp, unsigned char *rec, size_t len, gterrctx_t *ectx); +int verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, FILE *nsigfp, uint8_t bExtend, gterrctx_t *ectx); +void rsgt_errctxInit(gterrctx_t *ectx); +void rsgt_errctxExit(gterrctx_t *ectx); +void rsgt_errctxSetErrRec(gterrctx_t *ectx, char *rec); +void rsgt_errctxFrstRecInBlk(gterrctx_t *ectx, char *rec); +void rsgt_objfree(uint16_t tlvtype, void *obj); + + +/* TODO: replace these? */ +int hash_m(gtfile gf, GTDataHash **m); +int hash_r(gtfile gf, GTDataHash **r, const unsigned char *rec, const size_t len); +int hash_node(gtfile gf, GTDataHash **node, GTDataHash *m, GTDataHash *r, uint8_t level); +extern char *rsgt_read_puburl; /**< url of publication server */ +extern uint8_t rsgt_read_showVerified; + +#endif /* #ifndef INCLUDED_LIBRSGT_H */ diff --git a/runtime/librsgt_read.c b/runtime/librsgt_read.c new file mode 100644 index 0000000..25c0db4 --- /dev/null +++ b/runtime/librsgt_read.c @@ -0,0 +1,1092 @@ +/* librsgt_read.c - rsyslog's guardtime support library + * This includes functions used for reading signature (and + * other related) files. Well, actually it also contains + * some writing functionality, but only as far as rsyslog + * itself is not concerned, but "just" the utility programs. + * + * This part of the library uses C stdio and expects that the + * caller will open and close the file to be read itself. + * + * 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <stdio.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdint.h> +#include <assert.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <gt_http.h> + +#include "librsgt.h" + +typedef unsigned char uchar; +#ifndef VERSION +#define VERSION "no-version" +#endif +#define MAXFNAME 1024 + +static int rsgt_read_debug = 0; +char *rsgt_read_puburl = "http://verify.guardtime.com/gt-controlpublications.bin"; +char *rsgt_extend_puburl = "http://verifier.guardtime.net/gt-extendingservice"; +uint8_t rsgt_read_showVerified = 0; + +/* macro to obtain next char from file including error tracking */ +#define NEXTC if((c = fgetc(fp)) == EOF) { \ + r = feof(fp) ? RSGTE_EOF : RSGTE_IO; \ + goto done; \ + } + +/* check return state of operation and abort, if non-OK */ +#define CHKr(code) if((r = code) != 0) goto done + + +/* if verbose==0, only the first and last two octets are shown, + * otherwise everything. + */ +static void +outputHexBlob(FILE *fp, uint8_t *blob, uint16_t len, uint8_t verbose) +{ + unsigned i; + if(verbose || len <= 8) { + for(i = 0 ; i < len ; ++i) + fprintf(fp, "%2.2x", blob[i]); + } else { + fprintf(fp, "%2.2x%2.2x%2.2x[...]%2.2x%2.2x%2.2x", + blob[0], blob[1], blob[2], + blob[len-3], blob[len-2], blob[len-1]); + } +} + +static inline void +outputHash(FILE *fp, char *hdr, uint8_t *data, uint16_t len, uint8_t verbose) +{ + fprintf(fp, "%s", hdr); + outputHexBlob(fp, data, len, verbose); + fputc('\n', fp); +} + +void +rsgt_errctxInit(gterrctx_t *ectx) +{ + ectx->fp = NULL; + ectx->filename = NULL; + ectx->recNum = 0; + ectx->gtstate = 0; + ectx->recNumInFile = 0; + ectx->blkNum = 0; + ectx->verbose = 0; + ectx->errRec = NULL; + ectx->frstRecInBlk = NULL; + ectx->fileHash = NULL; + ectx->lefthash = ectx->righthash = ectx->computedHash = NULL; +} +void +rsgt_errctxExit(gterrctx_t *ectx) +{ + free(ectx->filename); + free(ectx->frstRecInBlk); +} + +/* note: we do not copy the record, so the caller MUST not destruct + * it before processing of the record is completed. To remove the + * current record without setting a new one, call this function + * with rec==NULL. + */ +void +rsgt_errctxSetErrRec(gterrctx_t *ectx, char *rec) +{ + ectx->errRec = strdup(rec); +} +/* This stores the block's first record. Here we copy the data, + * as the caller will usually not preserve it long enough. + */ +void +rsgt_errctxFrstRecInBlk(gterrctx_t *ectx, char *rec) +{ + free(ectx->frstRecInBlk); + ectx->frstRecInBlk = strdup(rec); +} + +static void +reportError(int errcode, gterrctx_t *ectx) +{ + if(ectx->fp != NULL) { + fprintf(ectx->fp, "%s[%llu:%llu:%llu]: error[%u]: %s\n", + ectx->filename, + (long long unsigned) ectx->blkNum, (long long unsigned) ectx->recNum, + (long long unsigned) ectx->recNumInFile, + errcode, RSGTE2String(errcode)); + if(ectx->frstRecInBlk != NULL) + fprintf(ectx->fp, "\tBlock Start Record.: '%s'\n", ectx->frstRecInBlk); + if(ectx->errRec != NULL) + fprintf(ectx->fp, "\tRecord in Question.: '%s'\n", ectx->errRec); + if(ectx->computedHash != NULL) { + outputHash(ectx->fp, "\tComputed Hash......: ", ectx->computedHash->digest, + ectx->computedHash->digest_length, ectx->verbose); + } + if(ectx->fileHash != NULL) { + outputHash(ectx->fp, "\tSignature File Hash: ", ectx->fileHash->data, + ectx->fileHash->len, ectx->verbose); + } + if(errcode == RSGTE_INVLD_TREE_HASH || + errcode == RSGTE_INVLD_TREE_HASHID) { + fprintf(ectx->fp, "\tTree Level.........: %d\n", (int) ectx->treeLevel); + outputHash(ectx->fp, "\tTree Left Hash.....: ", ectx->lefthash->digest, + ectx->lefthash->digest_length, ectx->verbose); + outputHash(ectx->fp, "\tTree Right Hash....: ", ectx->righthash->digest, + ectx->righthash->digest_length, ectx->verbose); + } + if(errcode == RSGTE_INVLD_TIMESTAMP || + errcode == RSGTE_TS_DERDECODE) { + fprintf(ectx->fp, "\tPublication Server.: %s\n", rsgt_read_puburl); + fprintf(ectx->fp, "\tGT Verify Timestamp: [%u]%s\n", + ectx->gtstate, GTHTTP_getErrorString(ectx->gtstate)); + } + if(errcode == RSGTE_TS_EXTEND || + errcode == RSGTE_TS_DERDECODE) { + fprintf(ectx->fp, "\tExtending Server...: %s\n", rsgt_extend_puburl); + fprintf(ectx->fp, "\tGT Extend Timestamp: [%u]%s\n", + ectx->gtstate, GTHTTP_getErrorString(ectx->gtstate)); + } + if(errcode == RSGTE_TS_DERENCODE) { + fprintf(ectx->fp, "\tAPI return state...: [%u]%s\n", + ectx->gtstate, GTHTTP_getErrorString(ectx->gtstate)); + } + } +} + +/* obviously, this is not an error-reporting function. We still use + * ectx, as it has most information we need. + */ +static void +reportVerifySuccess(gterrctx_t *ectx, GTVerificationInfo *vrfyInf) +{ + if(ectx->fp != NULL) { + fprintf(ectx->fp, "%s[%llu:%llu:%llu]: block signature successfully verified\n", + ectx->filename, + (long long unsigned) ectx->blkNum, (long long unsigned) ectx->recNum, + (long long unsigned) ectx->recNumInFile); + if(ectx->frstRecInBlk != NULL) + fprintf(ectx->fp, "\tBlock Start Record.: '%s'\n", ectx->frstRecInBlk); + if(ectx->errRec != NULL) + fprintf(ectx->fp, "\tBlock End Record...: '%s'\n", ectx->errRec); + fprintf(ectx->fp, "\tGT Verify Timestamp: [%u]%s\n", + ectx->gtstate, GTHTTP_getErrorString(ectx->gtstate)); + GTVerificationInfo_print(ectx->fp, 0, vrfyInf); + } +} + +/** + * Write the provided record to the current file position. + * + * @param[in] fp file pointer for writing + * @param[out] rec tlvrecord to write + * + * @returns 0 if ok, something else otherwise + */ +static int +rsgt_tlvwrite(FILE *fp, tlvrecord_t *rec) +{ + int r = RSGTE_IO; + if(fwrite(rec->hdr, (size_t) rec->lenHdr, 1, fp) != 1) goto done; + if(fwrite(rec->data, (size_t) rec->tlvlen, 1, fp) != 1) goto done; + r = 0; +done: return r; +} + +/** + * Read a header from a binary file. + * @param[in] fp file pointer for processing + * @param[in] hdr buffer for the header. Must be 9 bytes + * (8 for header + NUL byte) + * @returns 0 if ok, something else otherwise + */ +int +rsgt_tlvrdHeader(FILE *fp, uchar *hdr) +{ + int r; + if(fread(hdr, 8, 1, fp) != 1) { + r = RSGTE_IO; + goto done; + } + hdr[8] = '\0'; + r = 0; +done: return r; +} + +/* read type a complete tlv record + */ +static int +rsgt_tlvRecRead(FILE *fp, tlvrecord_t *rec) +{ + int r = 1; + int c; + + NEXTC; + rec->hdr[0] = c; + rec->tlvtype = c & 0x1f; + if(c & 0x20) { /* tlv16? */ + rec->lenHdr = 4; + NEXTC; + rec->hdr[1] = c; + rec->tlvtype = (rec->tlvtype << 8) | c; + NEXTC; + rec->hdr[2] = c; + rec->tlvlen = c << 8; + NEXTC; + rec->hdr[3] = c; + rec->tlvlen |= c; + } else { + NEXTC; + rec->lenHdr = 2; + rec->hdr[1] = c; + rec->tlvlen = c; + } + if(fread(rec->data, (size_t) rec->tlvlen, 1, fp) != 1) { + r = RSGTE_IO; + goto done; + } + + if(rsgt_read_debug) + printf("read tlvtype %4.4x, len %u\n", (unsigned) rec->tlvtype, + (unsigned) rec->tlvlen); + r = 0; +done: return r; +} + +/* decode a sub-tlv record from an existing record's memory buffer + */ +static int +rsgt_tlvDecodeSUBREC(tlvrecord_t *rec, uint16_t *stridx, tlvrecord_t *newrec) +{ + int r = 1; + int c; + + if(rec->tlvlen == *stridx) {r=RSGTE_LEN; goto done;} + c = rec->data[(*stridx)++]; + newrec->hdr[0] = c; + newrec->tlvtype = c & 0x1f; + if(c & 0x20) { /* tlv16? */ + newrec->lenHdr = 4; + if(rec->tlvlen == *stridx) {r=RSGTE_LEN; goto done;} + c = rec->data[(*stridx)++]; + newrec->hdr[1] = c; + newrec->tlvtype = (newrec->tlvtype << 8) | c; + if(rec->tlvlen == *stridx) {r=RSGTE_LEN; goto done;} + c = rec->data[(*stridx)++]; + newrec->hdr[2] = c; + newrec->tlvlen = c << 8; + if(rec->tlvlen == *stridx) {r=RSGTE_LEN; goto done;} + c = rec->data[(*stridx)++]; + newrec->hdr[3] = c; + newrec->tlvlen |= c; + } else { + if(rec->tlvlen == *stridx) {r=RSGTE_LEN; goto done;} + c = rec->data[(*stridx)++]; + newrec->lenHdr = 2; + newrec->hdr[1] = c; + newrec->tlvlen = c; + } + if(rec->tlvlen < *stridx + newrec->tlvlen) {r=RSGTE_LEN; goto done;} + memcpy(newrec->data, (rec->data)+(*stridx), newrec->tlvlen); + *stridx += newrec->tlvlen; + + if(rsgt_read_debug) + printf("read sub-tlv: tlvtype %4.4x, len %u\n", + (unsigned) newrec->tlvtype, + (unsigned) newrec->tlvlen); + r = 0; +done: return r; +} + + +static int +rsgt_tlvDecodeIMPRINT(tlvrecord_t *rec, imprint_t **imprint) +{ + int r = 1; + imprint_t *imp; + + if((imp = calloc(1, sizeof(imprint_t))) == NULL) { + r = RSGTE_OOM; + goto done; + } + + imp->hashID = rec->data[0]; + if(rec->tlvlen != 1 + hashOutputLengthOctets(imp->hashID)) { + r = RSGTE_LEN; + goto done; + } + imp->len = rec->tlvlen - 1; + if((imp->data = (uint8_t*)malloc(imp->len)) == NULL) {r=RSGTE_OOM;goto done;} + memcpy(imp->data, rec->data+1, imp->len); + *imprint = imp; + r = 0; +done: return r; +} + +static int +rsgt_tlvDecodeHASH_ALGO(tlvrecord_t *rec, uint16_t *strtidx, uint8_t *hashAlg) +{ + int r = 1; + tlvrecord_t subrec; + + CHKr(rsgt_tlvDecodeSUBREC(rec, strtidx, &subrec)); + if(!(subrec.tlvtype == 0x00 && subrec.tlvlen == 1)) { + r = RSGTE_FMT; + goto done; + } + *hashAlg = subrec.data[0]; + r = 0; +done: return r; +} +static int +rsgt_tlvDecodeBLOCK_IV(tlvrecord_t *rec, uint16_t *strtidx, uint8_t **iv) +{ + int r = 1; + tlvrecord_t subrec; + + CHKr(rsgt_tlvDecodeSUBREC(rec, strtidx, &subrec)); + if(!(subrec.tlvtype == 0x01)) { + r = RSGTE_INVLTYP; + goto done; + } + if((*iv = (uint8_t*)malloc(subrec.tlvlen)) == NULL) {r=RSGTE_OOM;goto done;} + memcpy(*iv, subrec.data, subrec.tlvlen); + r = 0; +done: return r; +} +static int +rsgt_tlvDecodeLAST_HASH(tlvrecord_t *rec, uint16_t *strtidx, imprint_t *imp) +{ + int r = 1; + tlvrecord_t subrec; + + CHKr(rsgt_tlvDecodeSUBREC(rec, strtidx, &subrec)); + if(!(subrec.tlvtype == 0x02)) { r = RSGTE_INVLTYP; goto done; } + imp->hashID = subrec.data[0]; + if(subrec.tlvlen != 1 + hashOutputLengthOctets(imp->hashID)) { + r = RSGTE_LEN; + goto done; + } + imp->len = subrec.tlvlen - 1; + if((imp->data = (uint8_t*)malloc(imp->len)) == NULL) {r=RSGTE_OOM;goto done;} + memcpy(imp->data, subrec.data+1, subrec.tlvlen-1); + r = 0; +done: return r; +} +static int +rsgt_tlvDecodeREC_COUNT(tlvrecord_t *rec, uint16_t *strtidx, uint64_t *cnt) +{ + int r = 1; + int i; + uint64_t val; + tlvrecord_t subrec; + + CHKr(rsgt_tlvDecodeSUBREC(rec, strtidx, &subrec)); + if(!(subrec.tlvtype == 0x03 && subrec.tlvlen <= 8)) { r = RSGTE_INVLTYP; goto done; } + val = 0; + for(i = 0 ; i < subrec.tlvlen ; ++i) { + val = (val << 8) + subrec.data[i]; + } + *cnt = val; + r = 0; +done: return r; +} +static int +rsgt_tlvDecodeSIG(tlvrecord_t *rec, uint16_t *strtidx, block_sig_t *bs) +{ + int r = 1; + tlvrecord_t subrec; + + CHKr(rsgt_tlvDecodeSUBREC(rec, strtidx, &subrec)); + if(!(subrec.tlvtype == 0x0906)) { r = RSGTE_INVLTYP; goto done; } + bs->sig.der.len = subrec.tlvlen; + bs->sigID = SIGID_RFC3161; + if((bs->sig.der.data = (uint8_t*)malloc(bs->sig.der.len)) == NULL) {r=RSGTE_OOM;goto done;} + memcpy(bs->sig.der.data, subrec.data, bs->sig.der.len); + r = 0; +done: return r; +} + +static int +rsgt_tlvDecodeBLOCK_SIG(tlvrecord_t *rec, block_sig_t **blocksig) +{ + int r = 1; + uint16_t strtidx = 0; + block_sig_t *bs; + if((bs = calloc(1, sizeof(block_sig_t))) == NULL) { + r = RSGTE_OOM; + goto done; + } + CHKr(rsgt_tlvDecodeHASH_ALGO(rec, &strtidx, &(bs->hashID))); + CHKr(rsgt_tlvDecodeBLOCK_IV(rec, &strtidx, &(bs->iv))); + CHKr(rsgt_tlvDecodeLAST_HASH(rec, &strtidx, &(bs->lastHash))); + CHKr(rsgt_tlvDecodeREC_COUNT(rec, &strtidx, &(bs->recCount))); + CHKr(rsgt_tlvDecodeSIG(rec, &strtidx, bs)); + if(strtidx != rec->tlvlen) { + r = RSGTE_LEN; + goto done; + } + *blocksig = bs; + r = 0; +done: return r; +} +static int +rsgt_tlvRecDecode(tlvrecord_t *rec, void *obj) +{ + int r = 1; + switch(rec->tlvtype) { + case 0x0900: + case 0x0901: + r = rsgt_tlvDecodeIMPRINT(rec, obj); + if(r != 0) goto done; + break; + case 0x0902: + r = rsgt_tlvDecodeBLOCK_SIG(rec, obj); + if(r != 0) goto done; + break; + } +done: + return r; +} + +static int +rsgt_tlvrdRecHash(FILE *fp, FILE *outfp, imprint_t **imp) +{ + int r; + tlvrecord_t rec; + + if((r = rsgt_tlvrd(fp, &rec, imp)) != 0) goto done; + if(rec.tlvtype != 0x0900) { + r = RSGTE_MISS_REC_HASH; + rsgt_objfree(rec.tlvtype, *imp); + goto done; + } + if(outfp != NULL) + if((r = rsgt_tlvwrite(outfp, &rec)) != 0) goto done; + r = 0; +done: return r; +} + +static int +rsgt_tlvrdTreeHash(FILE *fp, FILE *outfp, imprint_t **imp) +{ + int r; + tlvrecord_t rec; + + if((r = rsgt_tlvrd(fp, &rec, imp)) != 0) goto done; + if(rec.tlvtype != 0x0901) { + r = RSGTE_MISS_TREE_HASH; + rsgt_objfree(rec.tlvtype, *imp); + goto done; + } + if(outfp != NULL) + if((r = rsgt_tlvwrite(outfp, &rec)) != 0) goto done; + r = 0; +done: return r; +} + +/* read BLOCK_SIG during verification phase */ +static int +rsgt_tlvrdVrfyBlockSig(FILE *fp, block_sig_t **bs, tlvrecord_t *rec) +{ + int r; + + if((r = rsgt_tlvrd(fp, rec, bs)) != 0) goto done; + if(rec->tlvtype != 0x0902) { + r = RSGTE_MISS_BLOCKSIG; + rsgt_objfree(rec->tlvtype, *bs); + goto done; + } + r = 0; +done: return r; +} + +/** + * Read the next "object" from file. This usually is + * a single TLV, but may be something larger, for + * example in case of a block-sig TLV record. + * Unknown type records are ignored (or run aborted + * if we are not permitted to skip). + * + * @param[in] fp file pointer for processing + * @param[out] tlvtype type of tlv record (top-level for + * structured objects. + * @param[out] tlvlen length of the tlv record value + * @param[out] obj pointer to object; This is a proper + * tlv record structure, which must be casted + * by the caller according to the reported type. + * The object must be freed by the caller (TODO: better way?) + * + * @returns 0 if ok, something else otherwise + */ +int +rsgt_tlvrd(FILE *fp, tlvrecord_t *rec, void *obj) +{ + int r; + if((r = rsgt_tlvRecRead(fp, rec)) != 0) goto done; + r = rsgt_tlvRecDecode(rec, obj); +done: return r; +} + + +/* return if a blob is all zero */ +static inline int +blobIsZero(uint8_t *blob, uint16_t len) +{ + int i; + for(i = 0 ; i < len ; ++i) + if(blob[i] != 0) + return 0; + return 1; +} + +static void +rsgt_printIMPRINT(FILE *fp, char *name, imprint_t *imp, uint8_t verbose) +{ + fprintf(fp, "%s", name); + outputHexBlob(fp, imp->data, imp->len, verbose); + fputc('\n', fp); +} + +static void +rsgt_printREC_HASH(FILE *fp, imprint_t *imp, uint8_t verbose) +{ + rsgt_printIMPRINT(fp, "[0x0900]Record hash: ", + imp, verbose); +} + +static void +rsgt_printINT_HASH(FILE *fp, imprint_t *imp, uint8_t verbose) +{ + rsgt_printIMPRINT(fp, "[0x0901]Tree hash..: ", + imp, verbose); +} + +/** + * Output a human-readable representation of a block_sig_t + * to proviced file pointer. This function is mainly inteded for + * debugging purposes or dumping tlv files. + * + * @param[in] fp file pointer to send output to + * @param[in] bsig ponter to block_sig_t to output + * @param[in] verbose if 0, abbreviate blob hexdump, else complete + */ +void +rsgt_printBLOCK_SIG(FILE *fp, block_sig_t *bs, uint8_t verbose) +{ + fprintf(fp, "[0x0902]Block Signature Record:\n"); + fprintf(fp, "\tPrevious Block Hash:\n"); + fprintf(fp, "\t Algorithm..: %s\n", hashAlgName(bs->lastHash.hashID)); + fprintf(fp, "\t Hash.......: "); + outputHexBlob(fp, bs->lastHash.data, bs->lastHash.len, verbose); + fputc('\n', fp); + if(blobIsZero(bs->lastHash.data, bs->lastHash.len)) + fprintf(fp, "\t NOTE: New Hash Chain Start!\n"); + fprintf(fp, "\tHash Algorithm: %s\n", hashAlgName(bs->hashID)); + fprintf(fp, "\tIV............: "); + outputHexBlob(fp, bs->iv, getIVLen(bs), verbose); + fputc('\n', fp); + fprintf(fp, "\tRecord Count..: %llu\n", bs->recCount); + fprintf(fp, "\tSignature Type: %s\n", sigTypeName(bs->sigID)); + fprintf(fp, "\tSignature Len.: %u\n", bs->sig.der.len); + fprintf(fp, "\tSignature.....: "); + outputHexBlob(fp, bs->sig.der.data, bs->sig.der.len, verbose); + fputc('\n', fp); +} + + +/** + * Output a human-readable representation of a tlv object. + * + * @param[in] fp file pointer to send output to + * @param[in] tlvtype type of tlv object (record) + * @param[in] verbose if 0, abbreviate blob hexdump, else complete + */ +void +rsgt_tlvprint(FILE *fp, uint16_t tlvtype, void *obj, uint8_t verbose) +{ + switch(tlvtype) { + case 0x0900: + rsgt_printREC_HASH(fp, obj, verbose); + break; + case 0x0901: + rsgt_printINT_HASH(fp, obj, verbose); + break; + case 0x0902: + rsgt_printBLOCK_SIG(fp, obj, verbose); + break; + default:fprintf(fp, "unknown tlv record %4.4x\n", tlvtype); + break; + } +} + +/** + * Free the provided object. + * + * @param[in] tlvtype type of tlv object (record) + * @param[in] obj the object to be destructed + */ +void +rsgt_objfree(uint16_t tlvtype, void *obj) +{ + switch(tlvtype) { + case 0x0900: + case 0x0901: + free(((imprint_t*)obj)->data); + break; + case 0x0902: + free(((block_sig_t*)obj)->iv); + free(((block_sig_t*)obj)->lastHash.data); + free(((block_sig_t*)obj)->sig.der.data); + break; + default:fprintf(stderr, "rsgt_objfree: unknown tlv record %4.4x\n", + tlvtype); + break; + } + free(obj); +} + +/** + * Read block parameters. This detects if the block contains the + * individual log hashes, the intermediate hashes and the overall + * block paramters (from the signature block). As we do not have any + * begin of block record, we do not know e.g. the hash algorithm or IV + * until reading the block signature record. And because the file is + * purely sequential and variable size, we need to read all records up to + * the next signature record. + * If a caller intends to verify a log file based on the parameters, + * he must re-read the file from the begining (we could keep things + * in memory, but this is impractical for large blocks). In order + * to facitate this, the function permits to rewind to the original + * read location when it is done. + * + * @param[in] fp file pointer of tlv file + * @param[in] bRewind 0 - do not rewind at end of procesing, 1 - do so + * @param[out] bs block signature record + * @param[out] bHasRecHashes 0 if record hashes are present, 1 otherwise + * @param[out] bHasIntermedHashes 0 if intermediate hashes are present, + * 1 otherwise + * + * @returns 0 if ok, something else otherwise + */ +int +rsgt_getBlockParams(FILE *fp, uint8_t bRewind, block_sig_t **bs, + uint8_t *bHasRecHashes, uint8_t *bHasIntermedHashes) +{ + int r; + uint64_t nRecs = 0; + uint8_t bDone = 0; + off_t rewindPos = 0; + void *obj; + tlvrecord_t rec; + + if(bRewind) + rewindPos = ftello(fp); + *bHasRecHashes = 0; + *bHasIntermedHashes = 0; + *bs = NULL; + + while(!bDone) { /* we will err out on EOF */ + if((r = rsgt_tlvrd(fp, &rec, &obj)) != 0) goto done; + switch(rec.tlvtype) { + case 0x0900: + ++nRecs; + *bHasRecHashes = 1; + break; + case 0x0901: + *bHasIntermedHashes = 1; + break; + case 0x0902: + *bs = (block_sig_t*) obj; + bDone = 1; + break; + default:fprintf(fp, "unknown tlv record %4.4x\n", rec.tlvtype); + break; + } + if(!bDone) + rsgt_objfree(rec.tlvtype, obj); + } + + if(*bHasRecHashes && (nRecs != (*bs)->recCount)) { + r = RSGTE_INVLD_RECCNT; + goto done; + } + + if(bRewind) { + if(fseeko(fp, rewindPos, SEEK_SET) != 0) { + r = RSGTE_IO; + goto done; + } + } +done: + return r; +} + + +/** + * Read the file header and compare it to the expected value. + * The file pointer is placed right after the header. + * @param[in] fp file pointer of tlv file + * @param[in] excpect expected header (e.g. "LOGSIG10") + * @returns 0 if ok, something else otherwise + */ +int +rsgt_chkFileHdr(FILE *fp, char *expect) +{ + int r; + char hdr[9]; + + if((r = rsgt_tlvrdHeader(fp, (uchar*)hdr)) != 0) goto done; + if(strcmp(hdr, expect)) + r = RSGTE_INVLHDR; + else + r = 0; +done: + return r; +} + +gtfile +rsgt_vrfyConstruct_gf(void) +{ + gtfile gf; + if((gf = calloc(1, sizeof(struct gtfile_s))) == NULL) + goto done; + gf->x_prev = NULL; + +done: return gf; +} + +void +rsgt_vrfyBlkInit(gtfile gf, block_sig_t *bs, uint8_t bHasRecHashes, uint8_t bHasIntermedHashes) +{ + gf->hashAlg = hashID2Alg(bs->hashID); + gf->bKeepRecordHashes = bHasRecHashes; + gf->bKeepTreeHashes = bHasIntermedHashes; + free(gf->IV); + gf->IV = malloc(getIVLen(bs)); + memcpy(gf->IV, bs->iv, getIVLen(bs)); + free(gf->blkStrtHash); + gf->lenBlkStrtHash = bs->lastHash.len; + gf->blkStrtHash = malloc(gf->lenBlkStrtHash); + memcpy(gf->blkStrtHash, bs->lastHash.data, gf->lenBlkStrtHash); +} + +static int +rsgt_vrfy_chkRecHash(gtfile gf, FILE *sigfp, FILE *nsigfp, + GTDataHash *recHash, gterrctx_t *ectx) +{ + int r = 0; + imprint_t *imp = NULL; + + if((r = rsgt_tlvrdRecHash(sigfp, nsigfp, &imp)) != 0) + reportError(r, ectx); + goto done; + if(imp->hashID != hashIdentifier(gf->hashAlg)) { + reportError(r, ectx); + r = RSGTE_INVLD_REC_HASHID; + goto done; + } + if(memcmp(imp->data, recHash->digest, + hashOutputLengthOctets(imp->hashID))) { + r = RSGTE_INVLD_REC_HASH; + ectx->computedHash = recHash; + ectx->fileHash = imp; + reportError(r, ectx); + ectx->computedHash = NULL, ectx->fileHash = NULL; + goto done; + } + r = 0; +done: + if(imp != NULL) + rsgt_objfree(0x0900, imp); + return r; +} + +static int +rsgt_vrfy_chkTreeHash(gtfile gf, FILE *sigfp, FILE *nsigfp, + GTDataHash *hash, gterrctx_t *ectx) +{ + int r = 0; + imprint_t *imp = NULL; + + if((r = rsgt_tlvrdTreeHash(sigfp, nsigfp, &imp)) != 0) { + reportError(r, ectx); + goto done; + } + if(imp->hashID != hashIdentifier(gf->hashAlg)) { + reportError(r, ectx); + r = RSGTE_INVLD_TREE_HASHID; + goto done; + } + if(memcmp(imp->data, hash->digest, + hashOutputLengthOctets(imp->hashID))) { + r = RSGTE_INVLD_TREE_HASH; + ectx->computedHash = hash; + ectx->fileHash = imp; + reportError(r, ectx); + ectx->computedHash = NULL, ectx->fileHash = NULL; + goto done; + } + r = 0; +done: + if(imp != NULL) + rsgt_objfree(0x0901, imp); + return r; +} + +int +rsgt_vrfy_nextRec(block_sig_t *bs, gtfile gf, FILE *sigfp, FILE *nsigfp, + unsigned char *rec, size_t len, gterrctx_t *ectx) +{ + int r = 0; + GTDataHash *x; /* current hash */ + GTDataHash *m, *recHash = NULL, *t, *t_del; + uint8_t j; + + hash_m(gf, &m); + hash_r(gf, &recHash, rec, len); + if(gf->bKeepRecordHashes) { + r = rsgt_vrfy_chkRecHash(gf, sigfp, nsigfp, recHash, ectx); + if(r != 0) goto done; + } + hash_node(gf, &x, m, recHash, 1); /* hash leaf */ + if(gf->bKeepTreeHashes) { + ectx->treeLevel = 0; + ectx->lefthash = m; + ectx->righthash = recHash; + r = rsgt_vrfy_chkTreeHash(gf, sigfp, nsigfp, x, ectx); + if(r != 0) goto done; + } + rsgtimprintDel(gf->x_prev); + gf->x_prev = rsgtImprintFromGTDataHash(x); + /* add x to the forest as new leaf, update roots list */ + t = x; + for(j = 0 ; j < gf->nRoots ; ++j) { + if(gf->roots_valid[j] == 0) { + gf->roots_hash[j] = t; + gf->roots_valid[j] = 1; + t = NULL; + break; + } else if(t != NULL) { + /* hash interim node */ + ectx->treeLevel = j+1; + ectx->righthash = t; + t_del = t; + hash_node(gf, &t, gf->roots_hash[j], t_del, j+2); + gf->roots_valid[j] = 0; + if(gf->bKeepTreeHashes) { + ectx->lefthash = gf->roots_hash[j]; + r = rsgt_vrfy_chkTreeHash(gf, sigfp, nsigfp, t, ectx); + if(r != 0) goto done; /* mem leak ok, we terminate! */ + } + GTDataHash_free(gf->roots_hash[j]); + GTDataHash_free(t_del); + } + } + if(t != NULL) { + /* new level, append "at the top" */ + gf->roots_hash[gf->nRoots] = t; + gf->roots_valid[gf->nRoots] = 1; + ++gf->nRoots; + assert(gf->nRoots < MAX_ROOTS); + t = NULL; + } + ++gf->nRecords; + + /* cleanup */ + GTDataHash_free(m); +done: + if(recHash != NULL) + GTDataHash_free(recHash); + return r; +} + + +/* TODO: think about merging this with the writer. The + * same applies to the other computation algos. + */ +static int +verifySigblkFinish(gtfile gf, GTDataHash **pRoot) +{ + GTDataHash *root, *rootDel; + int8_t j; + int r; + + if(gf->nRecords == 0) + goto done; + + root = NULL; + for(j = 0 ; j < gf->nRoots ; ++j) { + if(root == NULL) { + root = gf->roots_valid[j] ? gf->roots_hash[j] : NULL; + gf->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + } else if(gf->roots_valid[j]) { + rootDel = root; + hash_node(gf, &root, gf->roots_hash[j], root, j+2); + gf->roots_valid[j] = 0; /* guess this is redundant with init, maybe del */ + GTDataHash_free(rootDel); + } + } + + free(gf->blkStrtHash); + gf->blkStrtHash = NULL; + *pRoot = root; + r = 0; +done: + gf->bInBlk = 0; + return r; +} + + +/* helper for rsgt_extendSig: */ +#define COPY_SUBREC_TO_NEWREC \ + memcpy(newrec.data+iWr, subrec.hdr, subrec.lenHdr); \ + iWr += subrec.lenHdr; \ + memcpy(newrec.data+iWr, subrec.data, subrec.tlvlen); \ + iWr += subrec.tlvlen; +static inline int +rsgt_extendSig(GTTimestamp *timestamp, tlvrecord_t *rec, gterrctx_t *ectx) +{ + GTTimestamp *out_timestamp; + uint8_t *der; + size_t lenDer; + int r, rgt; + tlvrecord_t newrec, subrec; + uint16_t iRd, iWr; + + rgt = GTHTTP_extendTimestamp(timestamp, rsgt_extend_puburl, &out_timestamp); + if(rgt != GT_OK) { + ectx->gtstate = rgt; + r = RSGTE_TS_EXTEND; + goto done; + } + r = GTTimestamp_getDEREncoded(out_timestamp, &der, &lenDer); + if(r != GT_OK) { + r = RSGTE_TS_DERENCODE; + ectx->gtstate = rgt; + goto done; + } + /* update block_sig tlv record with new extended timestamp */ + /* we now need to copy all tlv records before the actual der + * encoded part. + */ + iRd = iWr = 0; + // TODO; check tlvtypes at comment places below! + if ((r = rsgt_tlvDecodeSUBREC(rec, &iRd, &subrec)) != 0) goto done; + /* HASH_ALGO */ + COPY_SUBREC_TO_NEWREC + if ((r = rsgt_tlvDecodeSUBREC(rec, &iRd, &subrec)) != 0) goto done; + /* BLOCK_IV */ + COPY_SUBREC_TO_NEWREC + if ((r = rsgt_tlvDecodeSUBREC(rec, &iRd, &subrec)) != 0) goto done; + /* LAST_HASH */ + COPY_SUBREC_TO_NEWREC + if ((r = rsgt_tlvDecodeSUBREC(rec, &iRd, &subrec)) != 0) goto done; + /* REC_COUNT */ + COPY_SUBREC_TO_NEWREC + if ((r = rsgt_tlvDecodeSUBREC(rec, &iRd, &subrec)) != 0) goto done; + /* actual sig! */ + newrec.data[iWr++] = 0x09 | RSGT_FLAG_TLV16; + newrec.data[iWr++] = 0x06; + newrec.data[iWr++] = (lenDer >> 8) & 0xff; + newrec.data[iWr++] = lenDer & 0xff; + /* now we know how large the new main record is */ + newrec.tlvlen = (uint16_t) iWr+lenDer; + newrec.tlvtype = rec->tlvtype; + newrec.hdr[0] = rec->hdr[0]; + newrec.hdr[1] = rec->hdr[1]; + newrec.hdr[2] = (newrec.tlvlen >> 8) & 0xff; + newrec.hdr[3] = newrec.tlvlen & 0xff; + newrec.lenHdr = 4; + memcpy(newrec.data+iWr, der, lenDer); + /* and finally copy back new record to existing one */ + memcpy(rec, &newrec, sizeof(newrec)-sizeof(newrec.data)+newrec.tlvlen+4); + r = 0; +done: + return r; +} + + +/* verify the root hash. This also means we need to compute the + * Merkle tree root for the current block. + */ +int +verifyBLOCK_SIG(block_sig_t *bs, gtfile gf, FILE *sigfp, FILE *nsigfp, + uint8_t bExtend, gterrctx_t *ectx) +{ + int r; + int gtstate; + block_sig_t *file_bs = NULL; + GTTimestamp *timestamp = NULL; + GTVerificationInfo *vrfyInf; + GTDataHash *root = NULL; + tlvrecord_t rec; + + if((r = verifySigblkFinish(gf, &root)) != 0) + goto done; + if((r = rsgt_tlvrdVrfyBlockSig(sigfp, &file_bs, &rec)) != 0) + goto done; + if(ectx->recNum != bs->recCount) { + r = RSGTE_INVLD_RECCNT; + goto done; + } + + gtstate = GTTimestamp_DERDecode(file_bs->sig.der.data, + file_bs->sig.der.len, ×tamp); + if(gtstate != GT_OK) { + r = RSGTE_TS_DERDECODE; + ectx->gtstate = gtstate; + goto done; + } + + gtstate = GTHTTP_verifyTimestampHash(timestamp, root, NULL, + NULL, NULL, rsgt_read_puburl, 0, &vrfyInf); + if(! (gtstate == GT_OK + && vrfyInf->verification_errors == GT_NO_FAILURES) ) { + r = RSGTE_INVLD_TIMESTAMP; + ectx->gtstate = gtstate; + goto done; + } + + if(rsgt_read_showVerified) + reportVerifySuccess(ectx, vrfyInf); + if(bExtend) + if((r = rsgt_extendSig(timestamp, &rec, ectx)) != 0) goto done; + + if(nsigfp != NULL) + if((r = rsgt_tlvwrite(nsigfp, &rec)) != 0) goto done; + r = 0; +done: + if(file_bs != NULL) + rsgt_objfree(0x0902, file_bs); + if(r != 0) + reportError(r, ectx); + if(timestamp != NULL) + GTTimestamp_free(timestamp); + return r; +} diff --git a/runtime/lmsig_gt.c b/runtime/lmsig_gt.c new file mode 100644 index 0000000..0969129 --- /dev/null +++ b/runtime/lmsig_gt.c @@ -0,0 +1,229 @@ +/* lmsig_gt.c + * + * An implementation of the sigprov interface for GuardTime. + * + * 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 "sigprov.h" +#include "lmsig_gt.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[] = { + { "sig.hashfunction", eCmdHdlrGetWord, 0 }, + { "sig.timestampservice", eCmdHdlrGetWord, 0 }, + { "sig.block.sizelimit", eCmdHdlrSize, 0 }, + { "sig.keeprecordhashes", eCmdHdlrBinary, 0 }, + { "sig.keeptreehashes", eCmdHdlrBinary, 0 } +}; +static struct cnfparamblk pblk = + { CNFPARAMBLK_VERSION, + sizeof(cnfpdescr)/sizeof(struct cnfparamdescr), + cnfpdescr + }; + + +static void +errfunc(__attribute__((unused)) void *usrptr, uchar *emsg) +{ + errmsg.LogError(0, RS_RET_SIGPROV_ERR, "Signature Provider" + "Error: %s - disabling signatures", emsg); +} + +/* Standard-Constructor + */ +BEGINobjConstruct(lmsig_gt) + dbgprintf("DDDD: lmsig_gt: called construct\n"); + pThis->ctx = rsgtCtxNew(); + rsgtsetErrFunc(pThis->ctx, errfunc, NULL); +ENDobjConstruct(lmsig_gt) + + +/* destructor for the lmsig_gt object */ +BEGINobjDestruct(lmsig_gt) /* be sure to specify the object type also in END and CODESTART macros! */ +CODESTARTobjDestruct(lmsig_gt) + dbgprintf("DDDD: lmsig_gt: called destruct\n"); + rsgtCtxDel(pThis->ctx); +ENDobjDestruct(lmsig_gt) + + +/* 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) +{ + lmsig_gt_t *pThis = (lmsig_gt_t*) pT; + int i; + uchar *cstr; + struct cnfparamvals *pvals; + pvals = nvlstGetParams(lst, &pblk, NULL); + if(Debug) { + dbgprintf("sig param blk in lmsig_gt:\n"); + cnfparamsPrint(&pblk, pvals); + } + + for(i = 0 ; i < pblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(pblk.descr[i].name, "sig.hashfunction")) { + cstr = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + if(rsgtSetHashFunction(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); + rsgtSetTimestamper(pThis->ctx, (char*) cstr); + free(cstr); + } else if(!strcmp(pblk.descr[i].name, "sig.block.sizelimit")) { + rsgtSetBlockSizeLimit(pThis->ctx, pvals[i].val.d.n); + } else if(!strcmp(pblk.descr[i].name, "sig.keeprecordhashes")) { + rsgtSetKeepRecordHashes(pThis->ctx, pvals[i].val.d.n); + } else if(!strcmp(pblk.descr[i].name, "sig.keeptreehashes")) { + rsgtSetKeepTreeHashes(pThis->ctx, pvals[i].val.d.n); + } else { + DBGPRINTF("lmsig_gt: program error, non-handled " + "param '%s'\n", pblk.descr[i].name); + } + } + cnfparamvalsDestruct(pvals, &pblk); + return RS_RET_OK; +} + + +static rsRetVal +OnFileOpen(void *pT, uchar *fn, void *pGF) +{ + lmsig_gt_t *pThis = (lmsig_gt_t*) pT; + gtfile *pgf = (gtfile*) pGF; + DEFiRet; +dbgprintf("DDDD: onFileOpen: %s\n", fn); + /* note: if *pgf is set to NULL, this auto-disables GT functions */ + *pgf = rsgtCtxOpenFile(pThis->ctx, fn); + sigblkInit(*pgf); + RETiRet; +} + +/* Note: we assume that the record is terminated by a \n. + * As of the GuardTime paper, \n is not part of the signed + * message, so we subtract one from the record size. This + * may cause issues with non-standard formats, but let's + * see how things evolve (the verifier will not work in + * any case when the records are not \n delimited...). + * rgerhards, 2013-03-17 + */ +static rsRetVal +OnRecordWrite(void *pF, uchar *rec, rs_size_t lenRec) +{ + DEFiRet; +dbgprintf("DDDD: onRecordWrite (%d): %s\n", lenRec-1, rec); + sigblkAddRecord(pF, rec, lenRec-1); + + RETiRet; +} + +static rsRetVal +OnFileClose(void *pF) +{ + DEFiRet; +dbgprintf("DDDD: onFileClose\n"); + rsgtfileDestruct(pF); + + RETiRet; +} + +BEGINobjQueryInterface(lmsig_gt) +CODESTARTobjQueryInterface(lmsig_gt) + if(pIf->ifVersion != sigprovCURR_IF_VERSION) {/* check for current version, increment on each change */ + ABORT_FINALIZE(RS_RET_INTERFACE_NOT_SUPPORTED); + } + pIf->Construct = (rsRetVal(*)(void*)) lmsig_gtConstruct; + pIf->SetCnfParam = SetCnfParam; + pIf->Destruct = (rsRetVal(*)(void*)) lmsig_gtDestruct; + pIf->OnFileOpen = OnFileOpen; + pIf->OnRecordWrite = OnRecordWrite; + pIf->OnFileClose = OnFileClose; +finalize_it: +ENDobjQueryInterface(lmsig_gt) + + +BEGINObjClassExit(lmsig_gt, OBJ_IS_LOADABLE_MODULE) /* CHANGE class also in END MACRO! */ +CODESTARTObjClassExit(lmsig_gt) + /* release objects we no longer need */ + objRelease(errmsg, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); + + rsgtExit(); +ENDObjClassExit(lmsig_gt) + + +BEGINObjClassInit(lmsig_gt, 1, OBJ_IS_LOADABLE_MODULE) /* class, version */ + /* request objects we use */ + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(glbl, CORE_COMPONENT)); + + if(rsgtInit("rsyslogd " VERSION) != 0) { + errmsg.LogError(0, RS_RET_SIGPROV_ERR, "error initializing " + "signature provider - cannot sign"); + ABORT_FINALIZE(RS_RET_SIGPROV_ERR); + } +ENDObjClassInit(lmsig_gt) + + +/* --------------- here now comes the plumbing that makes as a library module --------------- */ + + +BEGINmodExit +CODESTARTmodExit + lmsig_gtClassExit(); +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(lmsig_gtClassInit(pModInfo)); /* must be done after tcps_sess, as we use it */ +ENDmodInit diff --git a/runtime/lmsig_gt.h b/runtime/lmsig_gt.h new file mode 100644 index 0000000..665e6a8 --- /dev/null +++ b/runtime/lmsig_gt.h @@ -0,0 +1,40 @@ +/* An implementation of the sigprov interface for GuardTime. + * + * 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_LMSIG_GT_H +#define INCLUDED_LMSIG_GT_H +#include "sigprov.h" +#include "librsgt.h" + +/* interface is defined in sigprov.h, we just implement it! */ +#define lmsig_gtCURR_IF_VERSION sigprovCURR_IF_VERSION +typedef sigprov_if_t lmsig_gt_if_t; + +/* the lmsig_gt object */ +struct lmsig_gt_s { + BEGINobjInstance; /* Data to implement generic object - MUST be the first data element! */ + gtctx ctx; /* librsgt context - contains all we need */ +}; +typedef struct lmsig_gt_s lmsig_gt_t; + +/* prototypes */ +PROTOTYPEObj(lmsig_gt); + +#endif /* #ifndef INCLUDED_LMSIG_GT_H */ diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index e7a5dff..5ba6ede 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -402,6 +402,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_INVLD_MODE = -2311,/**< invalid mode specified in configuration */ 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 */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ diff --git a/runtime/sd-daemon.c b/runtime/sd-daemon.c index 9c23b91..79d8ca3 100644 --- a/runtime/sd-daemon.c +++ b/runtime/sd-daemon.c @@ -25,14 +25,18 @@ ***/ #ifndef _GNU_SOURCE -#define _GNU_SOURCE +# define _GNU_SOURCE #endif #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/un.h> -#include <sys/fcntl.h> +#ifdef __BIONIC__ +# include <linux/fcntl.h> +#else +# include <sys/fcntl.h> +#endif #include <netinet/in.h> #include <stdlib.h> #include <errno.h> @@ -40,10 +44,28 @@ #include <string.h> #include <stdarg.h> #include <stdio.h> +#include <stddef.h> +#include <limits.h> + +#if defined(__linux__) +# include <mqueue.h> +#endif #include "sd-daemon.h" -int sd_listen_fds(int unset_environment) { +#if (__GNUC__ >= 4) +# ifdef SD_EXPORT_SYMBOLS +/* Export symbols */ +# define _sd_export_ __attribute__ ((visibility("default"))) +# else +/* Don't export the symbols */ +# define _sd_export_ __attribute__ ((visibility("hidden"))) +# endif +#else +# define _sd_export_ +#endif + +_sd_export_ int sd_listen_fds(int unset_environment) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; @@ -53,7 +75,8 @@ int sd_listen_fds(int unset_environment) { char *p = NULL; unsigned long l; - if (!(e = getenv("LISTEN_PID"))) { + e = getenv("LISTEN_PID"); + if (!e) { r = 0; goto finish; } @@ -66,7 +89,7 @@ int sd_listen_fds(int unset_environment) { goto finish; } - if (!p || *p || l <= 0) { + if (!p || p == e || *p || l <= 0) { r = -EINVAL; goto finish; } @@ -77,7 +100,8 @@ int sd_listen_fds(int unset_environment) { goto finish; } - if (!(e = getenv("LISTEN_FDS"))) { + e = getenv("LISTEN_FDS"); + if (!e) { r = 0; goto finish; } @@ -90,7 +114,7 @@ int sd_listen_fds(int unset_environment) { goto finish; } - if (!p || *p) { + if (!p || p == e || *p) { r = -EINVAL; goto finish; } @@ -98,7 +122,8 @@ int sd_listen_fds(int unset_environment) { for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) { int flags; - if ((flags = fcntl(fd, F_GETFD)) < 0) { + flags = fcntl(fd, F_GETFD); + if (flags < 0) { r = -errno; goto finish; } @@ -124,13 +149,12 @@ finish: #endif } -int sd_is_fifo(int fd, const char *path) { +_sd_export_ int sd_is_fifo(int fd, const char *path) { struct stat st_fd; if (fd < 0) return -EINVAL; - memset(&st_fd, 0, sizeof(st_fd)); if (fstat(fd, &st_fd) < 0) return -errno; @@ -140,7 +164,6 @@ int sd_is_fifo(int fd, const char *path) { if (path) { struct stat st_path; - memset(&st_path, 0, sizeof(st_path)); if (stat(path, &st_path) < 0) { if (errno == ENOENT || errno == ENOTDIR) @@ -157,6 +180,42 @@ int sd_is_fifo(int fd, const char *path) { return 1; } +_sd_export_ int sd_is_special(int fd, const char *path) { + struct stat st_fd; + + if (fd < 0) + return -EINVAL; + + if (fstat(fd, &st_fd) < 0) + return -errno; + + if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode)) + return 0; + + if (path) { + struct stat st_path; + + if (stat(path, &st_path) < 0) { + + if (errno == ENOENT || errno == ENOTDIR) + return 0; + + return -errno; + } + + if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode)) + return + st_path.st_dev == st_fd.st_dev && + st_path.st_ino == st_fd.st_ino; + else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode)) + return st_path.st_rdev == st_fd.st_rdev; + else + return 0; + } + + return 1; +} + static int sd_is_socket_internal(int fd, int type, int listening) { struct stat st_fd; @@ -208,13 +267,14 @@ union sockaddr_union { struct sockaddr_storage storage; }; -int sd_is_socket(int fd, int family, int type, int listening) { +_sd_export_ int sd_is_socket(int fd, int family, int type, int listening) { int r; if (family < 0) return -EINVAL; - if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) + r = sd_is_socket_internal(fd, type, listening); + if (r <= 0) return r; if (family > 0) { @@ -236,7 +296,7 @@ int sd_is_socket(int fd, int family, int type, int listening) { return 1; } -int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { +_sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) { union sockaddr_union sockaddr; socklen_t l; int r; @@ -244,7 +304,8 @@ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port if (family != 0 && family != AF_INET && family != AF_INET6) return -EINVAL; - if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) + r = sd_is_socket_internal(fd, type, listening); + if (r <= 0) return r; memset(&sockaddr, 0, sizeof(sockaddr)); @@ -281,12 +342,13 @@ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port return 1; } -int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { +_sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) { union sockaddr_union sockaddr; socklen_t l; int r; - if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) + r = sd_is_socket_internal(fd, type, listening); + if (r <= 0) return r; memset(&sockaddr, 0, sizeof(sockaddr)); @@ -302,29 +364,66 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t return 0; if (path) { - if (length <= 0) + if (length == 0) length = strlen(path); - if (length <= 0) + if (length == 0) /* Unnamed socket */ - return l == sizeof(sa_family_t); + return l == offsetof(struct sockaddr_un, sun_path); if (path[0]) /* Normal path socket */ return - (l >= sizeof(sa_family_t) + length + 1) && + (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) && memcmp(path, sockaddr.un.sun_path, length+1) == 0; else /* Abstract namespace socket */ return - (l == sizeof(sa_family_t) + length) && + (l == offsetof(struct sockaddr_un, sun_path) + length) && memcmp(path, sockaddr.un.sun_path, length) == 0; } return 1; } -int sd_notify(int unset_environment, const char *state) { +_sd_export_ int sd_is_mq(int fd, const char *path) { +#if !defined(__linux__) + return 0; +#else + struct mq_attr attr; + + if (fd < 0) + return -EINVAL; + + if (mq_getattr(fd, &attr) < 0) + return -errno; + + if (path) { + char fpath[PATH_MAX]; + struct stat a, b; + + if (path[0] != '/') + return -EINVAL; + + if (fstat(fd, &a) < 0) + return -errno; + + strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12); + fpath[sizeof(fpath)-1] = 0; + + if (stat(fpath, &b) < 0) + return -errno; + + if (a.st_dev != b.st_dev || + a.st_ino != b.st_ino) + return 0; + } + + return 1; +#endif +} + +_sd_export_ int sd_notify(int unset_environment, const char *state) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC) return 0; #else @@ -339,7 +438,8 @@ int sd_notify(int unset_environment, const char *state) { goto finish; } - if (!(e = getenv("NOTIFY_SOCKET"))) + e = getenv("NOTIFY_SOCKET"); + if (!e) return 0; /* Must be an abstract socket, or an absolute path */ @@ -348,7 +448,8 @@ int sd_notify(int unset_environment, const char *state) { goto finish; } - if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) { + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) { r = -errno; goto finish; } @@ -366,7 +467,7 @@ int sd_notify(int unset_environment, const char *state) { memset(&msghdr, 0, sizeof(msghdr)); msghdr.msg_name = &sockaddr; - msghdr.msg_namelen = sizeof(sa_family_t) + strlen(e); + msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e); if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) msghdr.msg_namelen = sizeof(struct sockaddr_un); @@ -392,7 +493,7 @@ finish: #endif } -int sd_notifyf(int unset_environment, const char *format, ...) { +_sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; #else @@ -414,22 +515,19 @@ int sd_notifyf(int unset_environment, const char *format, ...) { #endif } -int sd_booted(void) { +_sd_export_ int sd_booted(void) { #if defined(DISABLE_SYSTEMD) || !defined(__linux__) return 0; #else + struct stat st; - struct stat a, b; - - /* We simply test whether the systemd cgroup hierarchy is - * mounted */ - - if (lstat("/sys/fs/cgroup", &a) < 0) - return 0; + /* We test whether the runtime unit file directory has been + * created. This takes place in mount-setup.c, so is + * guaranteed to happen very early during boot. */ - if (lstat("/sys/fs/cgroup/systemd", &b) < 0) + if (lstat("/run/systemd/system/", &st) < 0) return 0; - return a.st_dev != b.st_dev; + return !!S_ISDIR(st.st_mode); #endif } diff --git a/runtime/sd-daemon.h b/runtime/sd-daemon.h index 45aac8b..fb7456d 100644 --- a/runtime/sd-daemon.h +++ b/runtime/sd-daemon.h @@ -58,25 +58,21 @@ extern "C" { You may find an up-to-date version of these source files online: - http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.h - http://cgit.freedesktop.org/systemd/plain/src/sd-daemon.c + http://cgit.freedesktop.org/systemd/systemd/plain/src/systemd/sd-daemon.h + http://cgit.freedesktop.org/systemd/systemd/plain/src/libsystemd-daemon/sd-daemon.c This should compile on non-Linux systems, too, but with the exception of the sd_is_xxx() calls all functions will become NOPs. - See sd-daemon(7) for more information. + See sd-daemon(3) for more information. */ -#if (__GNUC__ >= 4) +#ifndef _sd_printf_attr_ +#if __GNUC__ >= 4 #define _sd_printf_attr_(a,b) __attribute__ ((format (printf, a, b))) -# if defined(SD_EXPORT_SYMBOLS) -# define _sd_hidden_ -# else -# define _sd_hidden_ __attribute__ ((visibility("hidden"))) -# endif #else #define _sd_printf_attr_(a,b) -#define _sd_hidden_ +#endif #endif /* @@ -113,7 +109,7 @@ extern "C" { See sd_listen_fds(3) for more information. */ -int sd_listen_fds(int unset_environment) _sd_hidden_; +int sd_listen_fds(int unset_environment); /* Helper call for identifying a passed file descriptor. Returns 1 if @@ -125,7 +121,19 @@ int sd_listen_fds(int unset_environment) _sd_hidden_; See sd_is_fifo(3) for more information. */ -int sd_is_fifo(int fd, const char *path) _sd_hidden_; +int sd_is_fifo(int fd, const char *path); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a special character device on the file + system stored under the specified path, 0 otherwise. + If path is NULL a path name check will not be done and the call + only verifies if the file descriptor refers to a special character. + Returns a negative errno style error code on failure. + + See sd_is_special(3) for more information. +*/ +int sd_is_special(int fd, const char *path); /* Helper call for identifying a passed file descriptor. Returns 1 if @@ -141,7 +149,7 @@ int sd_is_fifo(int fd, const char *path) _sd_hidden_; See sd_is_socket(3) for more information. */ -int sd_is_socket(int fd, int family, int type, int listening) _sd_hidden_; +int sd_is_socket(int fd, int family, int type, int listening); /* Helper call for identifying a passed file descriptor. Returns 1 if @@ -155,7 +163,7 @@ int sd_is_socket(int fd, int family, int type, int listening) _sd_hidden_; See sd_is_socket_inet(3) for more information. */ -int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) _sd_hidden_; +int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port); /* Helper call for identifying a passed file descriptor. Returns 1 if @@ -171,7 +179,15 @@ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port See sd_is_socket_unix(3) for more information. */ -int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) _sd_hidden_; +int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length); + +/* + Helper call for identifying a passed file descriptor. Returns 1 if + the file descriptor is a POSIX Message Queue of the specified name, + 0 otherwise. If path is NULL a message queue name check is not + done. Returns a negative errno style error code on failure. +*/ +int sd_is_mq(int fd, const char *path); /* Informs systemd about changed daemon state. This takes a number of @@ -181,7 +197,7 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t READY=1 Tells systemd that daemon startup is finished (only relevant for services of Type=notify). The passed argument is a boolean "1" or "0". Since there is - little value in signalling non-readiness the only + little value in signaling non-readiness the only value daemons should send is "READY=1". STATUS=... Passes a single-line status string back to systemd @@ -201,8 +217,13 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t MAINPID=... The main pid of a daemon, in case systemd did not fork off the process itself. Example: "MAINPID=4711" + WATCHDOG=1 Tells systemd to update the watchdog timestamp. + Services using this feature should do this in + regular intervals. A watchdog framework can use the + timestamps to detect failed services. + Daemons can choose to send additional variables. However, it is - recommened to prefix variable names not listed above with X_. + recommended to prefix variable names not listed above with X_. Returns a negative errno-style error code on failure. Returns > 0 if systemd could be notified, 0 if it couldn't possibly because @@ -217,7 +238,7 @@ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t See sd_notify(3) for more information. */ -int sd_notify(int unset_environment, const char *state) _sd_hidden_; +int sd_notify(int unset_environment, const char *state); /* Similar to sd_notify() but takes a format string. @@ -239,7 +260,7 @@ int sd_notify(int unset_environment, const char *state) _sd_hidden_; See sd_notifyf(3) for more information. */ -int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3) _sd_hidden_; +int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_(2,3); /* Returns > 0 if the system was booted with systemd. Returns < 0 on @@ -248,11 +269,11 @@ int sd_notifyf(int unset_environment, const char *format, ...) _sd_printf_attr_( fine. You should NOT protect them with a call to this function. Also note that this function checks whether the system, not the user session is controlled by systemd. However the functions above work - for both session and system services. + for both user and system services. See sd_booted(3) for more information. */ -int sd_booted(void) _sd_hidden_; +int sd_booted(void); #ifdef __cplusplus } diff --git a/runtime/sigprov.h b/runtime/sigprov.h new file mode 100644 index 0000000..82587b7 --- /dev/null +++ b/runtime/sigprov.h @@ -0,0 +1,37 @@ +/* The interface definition for (file) signature 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_SIGPROV_H +#define INCLUDED_SIGPROV_H + +/* interface */ +BEGINinterface(sigprov) /* 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 (*OnRecordWrite)(void *pFileInstData, uchar *rec, rs_size_t lenRec); + rsRetVal (*OnFileClose)(void *pFileInstData); +ENDinterface(sigprov) +#define sigprovCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */ +#endif /* #ifndef INCLUDED_SIGPROV_H */ diff --git a/runtime/srUtils.h b/runtime/srUtils.h index 3169fd9..8626a4b 100644 --- a/runtime/srUtils.h +++ b/runtime/srUtils.h @@ -91,6 +91,7 @@ char *rs_strerror_r(int errnum, char *buf, size_t buflen); int decodeSyslogName(uchar *name, syslogName_t *codetab); int getSubString(uchar **ppSrc, char *pDst, size_t DstSize, char cSep); rsRetVal getFileSize(uchar *pszName, off_t *pSize); +int containsGlobWildcard(char *str); /* mutex operations */ /* some useful constants */ diff --git a/runtime/srutils.c b/runtime/srutils.c index 7b485b2..6a509b4 100644 --- a/runtime/srutils.c +++ b/runtime/srutils.c @@ -630,6 +630,28 @@ finalize_it: RETiRet; } +/* Returns 1 if the given string contains a non-escaped glob(3) + * wildcard character and 0 otherwise (or if the string is empty). + */ +int +containsGlobWildcard(char *str) +{ + char *p; + if(!str) { + return 0; + } + /* From Linux Programmer's Guide: + * "A string is a wildcard pattern if it contains one of the characters '?', '*' or '['" + * "One can remove the special meaning of '?', '*' and '[' by preceding them by a backslash" + */ + for(p = str; *p != '\0'; p++) { + if((*p == '?' || *p == '*' || *p == '[') && + (p == str || *(p-1) != '\\')) { + return 1; + } + } + return 0; +} /* vim:set ai: */ diff --git a/runtime/stream.c b/runtime/stream.c index 3e890c7..00afcda 100644 --- a/runtime/stream.c +++ b/runtime/stream.c @@ -81,6 +81,7 @@ static void *asyncWriterThread(void *pPtr); static rsRetVal doZipWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf, int bFlush); static rsRetVal doZipFinish(strm_t *pThis); static rsRetVal strmPhysWrite(strm_t *pThis, uchar *pBuf, size_t lenBuf); +static rsRetVal strmSeekCurrOffs(strm_t *pThis); /* methods */ @@ -197,6 +198,7 @@ static rsRetVal doPhysOpen(strm_t *pThis) { int iFlags = 0; + struct stat statOpen; DEFiRet; ISOBJ_TYPE_assert(pThis, strm); @@ -234,15 +236,71 @@ doPhysOpen(strm_t *pThis) ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND); else ABORT_FINALIZE(RS_RET_IO_ERROR); + } + + if(pThis->tOperationsMode == STREAMMODE_READ) { + if(fstat(pThis->fd, &statOpen) == -1) { + DBGPRINTF("Error: cannot obtain inode# for file %s\n", pThis->pszCurrFName); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } + pThis->inode = statOpen.st_ino; + } + + if(!ustrcmp(pThis->pszCurrFName, UCHAR_CONSTANT(_PATH_CONSOLE)) || isatty(pThis->fd)) { + DBGPRINTF("file %d is a tty-type file\n", pThis->fd); + pThis->bIsTTY = 1; } else { - if(!ustrcmp(pThis->pszCurrFName, UCHAR_CONSTANT(_PATH_CONSOLE)) || isatty(pThis->fd)) { - DBGPRINTF("file %d is a tty-type file\n", pThis->fd); - pThis->bIsTTY = 1; + pThis->bIsTTY = 0; + } + +finalize_it: + RETiRet; +} + + +static rsRetVal +strmSetCurrFName(strm_t *pThis) +{ + DEFiRet; + + if(pThis->sType == STREAMTYPE_FILE_CIRCULAR) { + CHKiRet(genFileName(&pThis->pszCurrFName, pThis->pszDir, pThis->lenDir, + pThis->pszFName, pThis->lenFName, pThis->iCurrFNum, pThis->iFileNumDigits)); + } else { + if(pThis->pszDir == NULL) { + if((pThis->pszCurrFName = ustrdup(pThis->pszFName)) == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); } else { - pThis->bIsTTY = 0; + CHKiRet(genFileName(&pThis->pszCurrFName, pThis->pszDir, pThis->lenDir, + pThis->pszFName, pThis->lenFName, -1, 0)); } } +finalize_it: + RETiRet; +} + +/* This function checks if the actual file has changed and, if so, resets the + * offset. This is support for monitoring files. It should be called after + * deserializing the strm object and before doing any other operation on it + * (most importantly not an open or seek!). + */ +static rsRetVal +CheckFileChange(strm_t *pThis) +{ + struct stat statName; + DEFiRet; + CHKiRet(strmSetCurrFName(pThis)); + if(stat((char*) pThis->pszCurrFName, &statName) == -1) + ABORT_FINALIZE(RS_RET_IO_ERROR); + DBGPRINTF("stream/after deserialize checking for file change on '%s', " + "inode %u/%u, size/currOffs %llu/%llu\n", + pThis->pszCurrFName, (unsigned) pThis->inode, + (unsigned) statName.st_ino, statName.st_size, pThis->iCurrOffs); + if(pThis->inode != statName.st_ino || statName.st_size < pThis->iCurrOffs) { + DBGPRINTF("stream: file %s has changed\n", pThis->pszCurrFName); + pThis->iCurrOffs = 0; + } finalize_it: RETiRet; } @@ -265,19 +323,8 @@ static rsRetVal strmOpenFile(strm_t *pThis) if(pThis->pszFName == NULL) ABORT_FINALIZE(RS_RET_FILE_PREFIX_MISSING); - if(pThis->sType == STREAMTYPE_FILE_CIRCULAR) { - CHKiRet(genFileName(&pThis->pszCurrFName, pThis->pszDir, pThis->lenDir, - pThis->pszFName, pThis->lenFName, pThis->iCurrFNum, pThis->iFileNumDigits)); - } else { - if(pThis->pszDir == NULL) { - if((pThis->pszCurrFName = ustrdup(pThis->pszFName)) == NULL) - ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); - } else { - CHKiRet(genFileName(&pThis->pszCurrFName, pThis->pszDir, pThis->lenDir, - pThis->pszFName, pThis->lenFName, -1, 0)); - } - } - + CHKiRet(strmSetCurrFName(pThis)); + CHKiRet(doPhysOpen(pThis)); pThis->iCurrOffs = 0; @@ -357,6 +404,7 @@ static rsRetVal strmCloseFile(strm_t *pThis) if(pThis->fd != -1) { close(pThis->fd); pThis->fd = -1; + pThis->inode = 0; } if(pThis->fdDir != -1) { @@ -432,18 +480,15 @@ static rsRetVal strmHandleEOFMonitor(strm_t *pThis) { DEFiRet; - struct stat statOpen; struct stat statName; ISOBJ_TYPE_assert(pThis, strm); - if(fstat(pThis->fd, &statOpen) == -1) - ABORT_FINALIZE(RS_RET_IO_ERROR); if(stat((char*) pThis->pszCurrFName, &statName) == -1) ABORT_FINALIZE(RS_RET_IO_ERROR); - DBGPRINTF("stream checking for file change on '%s', inode %u/%u", - pThis->pszCurrFName, (unsigned) statOpen.st_ino, + DBGPRINTF("stream checking for file change on '%s', inode %u/%u\n", + pThis->pszCurrFName, (unsigned) pThis->inode, (unsigned) statName.st_ino); - if(statOpen.st_ino == statName.st_ino) { + if(pThis->inode == statName.st_ino) { ABORT_FINALIZE(RS_RET_EOF); } else { /* we had a file change! */ @@ -1343,7 +1388,11 @@ static rsRetVal strmSeek(strm_t *pThis, off64_t offs) } long long i; DBGOPRINT((obj_t*) pThis, "file %d seek, pos %llu\n", pThis->fd, (long long unsigned) offs); - i = lseek64(pThis->fd, offs, SEEK_SET); // TODO: check error! + i = lseek64(pThis->fd, offs, SEEK_SET); + if(i != offs) { + DBGPRINTF("strmSeek: error %lld seeking to offset %lld\n", i, offs); + ABORT_FINALIZE(RS_RET_IO_ERROR); + } pThis->iCurrOffs = offs; /* we are now at *this* offset */ pThis->iBufPtr = 0; /* buffer invalidated */ @@ -1697,6 +1746,9 @@ static rsRetVal strmSerialize(strm_t *pThis, strm_t *pStrm) l = pThis->iCurrOffs; objSerializeSCALAR_VAR(pStrm, iCurrOffs, INT64, l); + l = pThis->inode; + objSerializeSCALAR_VAR(pStrm, inode, INT64, l); + objSerializePTR(pStrm, prevLineSegment, PSZ); CHKiRet(obj.EndSerialize(pStrm)); @@ -1796,6 +1848,8 @@ static rsRetVal strmSetProperty(strm_t *pThis, var_t *pProp) CHKiRet(strmSettOpenMode(pThis, pProp->val.num)); } else if(isProp("iCurrOffs")) { pThis->iCurrOffs = pProp->val.num; + } else if(isProp("inode")) { + pThis->inode = (ino_t) pProp->val.num; } else if(isProp("iMaxFileSize")) { CHKiRet(strmSetiMaxFileSize(pThis, pProp->val.num)); } else if(isProp("iMaxFiles")) { @@ -1865,6 +1919,7 @@ CODESTARTobjQueryInterface(strm) pIf->GetCurrOffset = strmGetCurrOffset; pIf->Dup = strmDup; pIf->SetWCntr = strmSetWCntr; + pIf->CheckFileChange = CheckFileChange; /* set methods */ pIf->SetbDeleteOnClose = strmSetbDeleteOnClose; pIf->SetiMaxFileSize = strmSetiMaxFileSize; diff --git a/runtime/stream.h b/runtime/stream.h index b7e7407..b7cc6d3 100644 --- a/runtime/stream.h +++ b/runtime/stream.h @@ -112,6 +112,7 @@ typedef struct strm_s { int lenDir; int fd; /* the file descriptor, -1 if closed */ int fdDir; /* the directory's descriptor, in case bSync is requested (-1 if closed) */ + ino_t inode; /* current inode for files being monitored (undefined else) */ uchar *pszCurrFName; /* name of current file (if open) */ uchar *pIOBuf; /* the iobuffer currently in use to gather data */ size_t iBufPtrMax; /* current max Ptr in Buffer (if partial read!) */ @@ -187,8 +188,10 @@ BEGINinterface(strm) /* name must also be changed in ENDinterface macro! */ rsRetVal (*ReadLine)(strm_t *pThis, cstr_t **ppCStr, int mode); /* v7 added 2012-09-14 */ INTERFACEpropSetMeth(strm, bVeryReliableZip, int); + /* v8 added 2013-03-21 */ + rsRetVal (*CheckFileChange)(strm_t *pThis); ENDinterface(strm) -#define strmCURR_IF_VERSION 7 /* increment whenever you change the interface structure! */ +#define strmCURR_IF_VERSION 8 /* increment whenever you change the interface structure! */ static inline int strmGetCurrFileNum(strm_t *pStrm) { diff --git a/runtime/wtp.c b/runtime/wtp.c index f8d3588..19151e7 100644 --- a/runtime/wtp.c +++ b/runtime/wtp.c @@ -381,9 +381,9 @@ wtpWorker(void *arg) /* the arg is actually a wti object, even though we are in if(prctl(PR_SET_NAME, thrdName, 0, 0, 0) != 0) { DBGPRINTF("prctl failed, not setting thread name for '%s'\n", wtpGetDbgHdr(pThis)); } + dbgOutputTID((char*)thrdName); # endif - dbgOutputTID((char*)thrdName); pthread_cleanup_push(wtpWrkrExecCancelCleanup, pWti); wtiWorker(pWti); pthread_cleanup_pop(0); |