Index: /trunk/Makefile.in
===================================================================
--- /trunk/Makefile.in	(revision 293)
+++ /trunk/Makefile.in	(revision 294)
@@ -124,5 +124,5 @@
 	sh_processcheck.h sh_portcheck.h sh_pthread.h sh_string.h \
 	sh_log_check.h sh_log_evalrule.h sh_log_correlate.h \
-	sh_log_mark.h sh_log_repeat.h sh_inotify.h
+	sh_log_mark.h sh_log_repeat.h sh_inotify.h sh_registry.h
 
 
@@ -165,4 +165,5 @@
 	$(srcsrc)/sh_log_check.c $(srcsrc)/dnmalloc.c \
 	$(srcsrc)/sh_inotify.c $(srcsrc)/sh_log_repeat.c \
+	$(srcsrc)/sh_audit.c $(srcsrc)/sh_registry.c \
 	$(srcsrc)/t-test1.c
 
@@ -183,5 +184,6 @@
 	sh_log_parse_generic.o \
 	sh_log_correlate.o sh_log_mark.o sh_log_repeat.o \
-	sh_pthread.o sh_string.o sh_inotify.o dnmalloc.o
+	sh_pthread.o sh_string.o sh_inotify.o dnmalloc.o \
+	sh_audit.o sh_registry.o
 
 KERN = kern_head.h kern_head.c
@@ -1703,5 +1705,5 @@
 sh_entropy.o: $(srcsrc)/sh_entropy.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_tiger.h $(srcinc)/sh_calls.h $(srcinc)/sh_pthread.h $(srcinc)/sh_static.h $(srcinc)/sh_pthread.h $(srcinc)/CuTest.h 
 sh_forward.o: $(srcsrc)/sh_forward.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_tiger.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_forward.h $(srcinc)/sh_srp.h $(srcinc)/sh_fifo.h $(srcinc)/sh_tools.h $(srcinc)/sh_entropy.h $(srcinc)/sh_html.h $(srcinc)/sh_nmail.h $(srcinc)/sh_socket.h $(srcinc)/sh_static.h $(srcinc)/rijndael-api-fst.h $(srcinc)/sh_readconf.h $(srcinc)/zAVLTree.h $(srcinc)/sh_extern.h 
-sh_modules.o: $(srcsrc)/sh_modules.c Makefile config_xor.h $(srcinc)/sh_modules.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utmp.h $(srcinc)/sh_mounts.h $(srcinc)/sh_userfiles.h $(srcinc)/sh_kern.h $(srcinc)/sh_suidchk.h $(srcinc)/sh_processcheck.h $(srcinc)/sh_portcheck.h $(srcinc)/sh_logmon.h 
+sh_modules.o: $(srcsrc)/sh_modules.c Makefile config_xor.h $(srcinc)/sh_modules.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utmp.h $(srcinc)/sh_mounts.h $(srcinc)/sh_userfiles.h $(srcinc)/sh_kern.h $(srcinc)/sh_suidchk.h $(srcinc)/sh_processcheck.h $(srcinc)/sh_portcheck.h $(srcinc)/sh_logmon.h $(srcinc)/sh_registry.h 
 sh_utmp.o: $(srcsrc)/sh_utmp.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_error.h $(srcinc)/sh_modules.h $(srcinc)/sh_utmp.h $(srcinc)/sh_pthread.h $(srcinc)/sh_inotify.h 
 sh_kern.o: $(srcsrc)/sh_kern.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_error.h $(srcinc)/sh_modules.h $(srcinc)/sh_kern.h sh_ks_xor.h $(srcinc)/sh_unix.h $(srcinc)/sh_hash.h 
@@ -1751,5 +1753,5 @@
 dnmalloc.o: $(srcsrc)/dnmalloc.c Makefile config.h 
 t-test1.o: $(srcsrc)/t-test1.c Makefile config.h $(srcinc)/malloc.h 
-sh_port2proc.o: $(srcsrc)/sh_port2proc.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_error_min.h $(srcinc)/sh_pthread.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h 
+sh_port2proc.o: $(srcsrc)/sh_port2proc.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_error_min.h $(srcinc)/sh_pthread.h 
 sh_log_parse_syslog.o: $(srcsrc)/sh_log_parse_syslog.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
 sh_log_parse_pacct.o: $(srcsrc)/sh_log_parse_pacct.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
@@ -1766,2 +1768,4 @@
 sh_log_parse_generic.o: $(srcsrc)/sh_log_parse_generic.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_log_check.h $(srcinc)/sh_string.h 
 sh_login_track.o: $(srcsrc)/sh_login_track.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_string.h $(srcinc)/sh_tools.h $(srcinc)/sh_error_min.h $(srcinc)/CuTest.h $(srcinc)/CuTest.h 
+sh_audit.o: $(srcsrc)/sh_audit.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_error.h $(srcinc)/sh_extern.h $(srcinc)/sh_utils.h 
+sh_registry.o: $(srcsrc)/sh_registry.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_modules.h $(srcinc)/sh_hash.h $(srcinc)/sh_tiger.h 
Index: /trunk/configure.ac
===================================================================
--- /trunk/configure.ac	(revision 293)
+++ /trunk/configure.ac	(revision 294)
@@ -12,5 +12,5 @@
 dnl start
 dnl
-AM_INIT_AUTOMAKE(samhain, 2.7.2c)
+AM_INIT_AUTOMAKE(samhain, 2.8.0)
 AC_DEFINE([SAMHAIN], 1, [Application is samhain])
 AC_CANONICAL_HOST
@@ -86,4 +86,5 @@
 	*cygwin*) 
 	AC_DEFINE(HOST_IS_CYGWIN)
+	AC_DEFINE([USE_REGISTRY_CHECK], 1, [Define for registry check])
 	dnmalloc_ok=no
 	AC_MSG_RESULT([no trusted paths and no dnmalloc])
@@ -230,5 +231,5 @@
 	regex.h glob.h \
 	linux/ext2_fs.h linux/fs.h ext2fs/ext2_fs.h asm/segment.h \
-	elf.h linux/elf.h \
+	elf.h linux/elf.h auparse.h \
 	paths.h arpa/nameser.h arpa/nameser_compat.h \
 	rpc/rpcent.h rpc/rpc.h sys/statvfs.h,
@@ -442,4 +443,15 @@
   ]) 
 
+sh_auparse=no
+
+if test "x$ac_cv_header_auparse_h" = "xyes"
+then
+   AC_CHECK_LIB(auparse, auparse_find_field, [
+   			 LIBS="$LIBS -lauparse"
+			 sh_auparse=yes
+			 AC_DEFINE(HAVE_AUPARSE_LIB, 1, [Define if you have the auparse lib])
+			 ])
+fi
+
 dnl arguments for accept
 
@@ -952,4 +964,9 @@
 		then
 		  tmp_LIBS=`echo $LIBS | sed 's%\-lresolv%%' `
+		  LIBS="${tmp_LIBS}"
+		fi
+		if test x"${sh_auparse}" = xyes
+		then
+		  tmp_LIBS=`echo $LIBS | sed 's%\-lauparse%%' `
 		  LIBS="${tmp_LIBS}"
 		fi
Index: /trunk/depend.dep
===================================================================
--- /trunk/depend.dep	(revision 293)
+++ /trunk/depend.dep	(revision 294)
@@ -18,5 +18,5 @@
 sh_entropy.o: $(srcsrc)/sh_entropy.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_tiger.h $(srcinc)/sh_calls.h $(srcinc)/sh_pthread.h $(srcinc)/sh_static.h $(srcinc)/sh_pthread.h $(srcinc)/CuTest.h 
 sh_forward.o: $(srcsrc)/sh_forward.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_tiger.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_forward.h $(srcinc)/sh_srp.h $(srcinc)/sh_fifo.h $(srcinc)/sh_tools.h $(srcinc)/sh_entropy.h $(srcinc)/sh_html.h $(srcinc)/sh_nmail.h $(srcinc)/sh_socket.h $(srcinc)/sh_static.h $(srcinc)/rijndael-api-fst.h $(srcinc)/sh_readconf.h $(srcinc)/zAVLTree.h $(srcinc)/sh_extern.h 
-sh_modules.o: $(srcsrc)/sh_modules.c Makefile config_xor.h $(srcinc)/sh_modules.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utmp.h $(srcinc)/sh_mounts.h $(srcinc)/sh_userfiles.h $(srcinc)/sh_kern.h $(srcinc)/sh_suidchk.h $(srcinc)/sh_processcheck.h $(srcinc)/sh_portcheck.h $(srcinc)/sh_logmon.h 
+sh_modules.o: $(srcsrc)/sh_modules.c Makefile config_xor.h $(srcinc)/sh_modules.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utmp.h $(srcinc)/sh_mounts.h $(srcinc)/sh_userfiles.h $(srcinc)/sh_kern.h $(srcinc)/sh_suidchk.h $(srcinc)/sh_processcheck.h $(srcinc)/sh_portcheck.h $(srcinc)/sh_logmon.h $(srcinc)/sh_registry.h 
 sh_utmp.o: $(srcsrc)/sh_utmp.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_error.h $(srcinc)/sh_modules.h $(srcinc)/sh_utmp.h $(srcinc)/sh_pthread.h $(srcinc)/sh_inotify.h 
 sh_kern.o: $(srcsrc)/sh_kern.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_error.h $(srcinc)/sh_modules.h $(srcinc)/sh_kern.h sh_ks_xor.h $(srcinc)/sh_unix.h $(srcinc)/sh_hash.h 
@@ -68,5 +68,5 @@
 dnmalloc-portable.o: $(srcsrc)/dnmalloc-portable.c Makefile config.h 
 dnmalloc.o: $(srcsrc)/dnmalloc.c Makefile config.h 
-sh_port2proc.o: $(srcsrc)/sh_port2proc.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_error_min.h $(srcinc)/sh_pthread.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h 
+sh_port2proc.o: $(srcsrc)/sh_port2proc.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_utils.h $(srcinc)/sh_error_min.h $(srcinc)/sh_pthread.h 
 sh_log_parse_syslog.o: $(srcsrc)/sh_log_parse_syslog.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
 sh_log_parse_pacct.o: $(srcsrc)/sh_log_parse_pacct.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_log_check.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h 
@@ -83,2 +83,4 @@
 sh_log_parse_generic.o: $(srcsrc)/sh_log_parse_generic.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_log_check.h $(srcinc)/sh_string.h 
 sh_login_track.o: $(srcsrc)/sh_login_track.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_string.h $(srcinc)/sh_tools.h $(srcinc)/sh_error_min.h $(srcinc)/CuTest.h $(srcinc)/CuTest.h 
+sh_audit.o: $(srcsrc)/sh_audit.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_error.h $(srcinc)/sh_extern.h $(srcinc)/sh_utils.h 
+sh_registry.o: $(srcsrc)/sh_registry.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_unix.h $(srcinc)/sh_modules.h $(srcinc)/sh_hash.h $(srcinc)/sh_tiger.h 
Index: /trunk/depend.sum
===================================================================
--- /trunk/depend.sum	(revision 293)
+++ /trunk/depend.sum	(revision 294)
@@ -1,1 +1,1 @@
-2431182974
+2205834486
Index: /trunk/docs/Changelog
===================================================================
--- /trunk/docs/Changelog	(revision 293)
+++ /trunk/docs/Changelog	(revision 294)
@@ -1,4 +1,9 @@
+2.8.0:
+	* Add registry checking
+	* Use auditd records to find out who did it
+
 2.7.2c:
-	* Fix uppercase hostname problem in client/server communication
+        * Fix uppercase hostname problem in client/server communication
+
 
 2.7.2b:
@@ -12,7 +17,7 @@
 	* sh_utils.c: fixed an endianess issue that prevented cross-verification
 	  of email signatures (reported by A. Zangerl)
-        * sh_login_track.c: fix compiler warning (ignored return value 
-          of fwrite)
-	* sh_readconf.c: fix comparison of SeverityUserX string
+	* sh_login_track.c: fix compiler warning (ignored return value 
+	  of fwrite)
+	* sh_readconf.c: fix comparison of SeverityUserX string 
 	  (reported by max__)
 	* sh_processcheck.c: sh_prochk_set_maxpid: set retval on success
Index: /trunk/include/sh_cat.h
===================================================================
--- /trunk/include/sh_cat.h	(revision 293)
+++ /trunk/include/sh_cat.h	(revision 294)
@@ -173,4 +173,10 @@
  MSG_LOGMON_MARK,
  MSG_LOGMON_BURST,
+#endif
+
+#ifdef USE_REGISTRY_CHECK
+ MSG_REG_MISS,
+ MSG_REG_NEW,
+ MSG_REG_CHANGE,
 #endif
 
Index: /trunk/include/sh_extern.h
===================================================================
--- /trunk/include/sh_extern.h	(revision 293)
+++ /trunk/include/sh_extern.h	(revision 294)
@@ -41,4 +41,9 @@
  */
 int sh_ext_popen_init (sh_tas_t * task, char * command);
+
+/*
+ * -- Execute command, return first line of output
+ */
+int sh_ext_system (char * command);
 
 /*
Index: /trunk/include/sh_files.h
===================================================================
--- /trunk/include/sh_files.h	(revision 293)
+++ /trunk/include/sh_files.h	(revision 294)
@@ -21,8 +21,13 @@
 #define SH_FILES_H
 
+void sh_audit_mark (char * file);
+void sh_audit_delete_all ();
+char * sh_audit_fetch (char * file, time_t time, char * result, size_t rsize);
+
 struct sh_dirent {
   char             * sh_d_name;
   struct sh_dirent * next;
 };
+
 
 /* free a directory listing
Index: /trunk/include/sh_hash.h
===================================================================
--- /trunk/include/sh_hash.h	(revision 293)
+++ /trunk/include/sh_hash.h	(revision 294)
@@ -70,9 +70,10 @@
 /* Check whether a file is present in the database.
  */
-int sh_hash_have_it (char * newname);
+int sh_hash_have_it (const char * newname);
 
 /* Get a file if it is present in the database.
+ * If fileHash != NULL also return checksum.
  */
-int sh_hash_get_it (char * newname, file_type * tmpFile);
+int sh_hash_get_it (const char * newname, file_type * tmpFile, char * fileHash);
 
 /* Delete the database from memory.
@@ -113,4 +114,8 @@
 void sh_hash_unvisited (ShErrLevel level);
 
+/* Search for unvisited entries in the database, custom error handler.
+ */
+void sh_hash_unvisited_custom (char prefix, void(*handler)(const char * key));
+
 /* Set a file's status to 'visited'. This is required for
  * files that should be ignored, and may be present in the
@@ -135,15 +140,25 @@
 int hash_full_tree (void); 
 
-/* Insert data
+/* Insert data.
+ * 'key' -> path
+ * 'str' -> binary with size 'size'
  */
-void sh_hash_push2db (char * key, unsigned long val1, 
-		      unsigned long val2, unsigned long val3,
-		      unsigned char * str, int size);
+struct store2db {
+  UINT64 val0;
+  UINT64 val1;
+  UINT64 val2;
+  UINT64 val3;
+  char   checksum[KEY_LEN+1];
+  unsigned char * str;
+  int size;
+};
+
+void sh_hash_push2db (const char * key, struct store2db * save);
+
 
 /* Retrieve data
  */
-char * sh_hash_db2pop (char * key, unsigned long * val1, 
-		       unsigned long * val2, unsigned long * val3,
-		       int * size);
+char * sh_hash_db2pop (const char * key,  struct store2db * get);
+
 
 /* Write out database
Index: /trunk/include/sh_registry.h
===================================================================
--- /trunk/include/sh_registry.h	(revision 294)
+++ /trunk/include/sh_registry.h	(revision 294)
@@ -0,0 +1,13 @@
+
+#ifndef SH_REGISTRY_H
+#define SH_REGISTRY_H
+
+int sh_reg_check_init(struct mod_type * arg);
+int sh_reg_check_timer(time_t tcurrent);
+int sh_reg_check_run(void);
+int sh_reg_check_reconf(void);
+int sh_reg_check_cleanup(void);
+
+extern sh_rconf sh_reg_check_table[];
+
+#endif
Index: /trunk/include/sh_unix.h
===================================================================
--- /trunk/include/sh_unix.h	(revision 293)
+++ /trunk/include/sh_unix.h	(revision 294)
@@ -90,8 +90,13 @@
 /* use prelink     */
 #define MODI_PREL (1 << 13)
+
 /* get content     */
 #define MODI_TXT ((1 << 14)|MODI_CHK)
-
 #define MODI_TXT_ENABLED(a) (((a)&(1 << 14))!=0)
+
+/* get audit record  */
+#define MODI_AUDIT (1 << 15)
+#define MODI_AUDIT_ENABLED(a) (((a)&(1 << 15))!=0)
+
 
 #define SH_TXT_MAX 9200
Index: /trunk/src/samhain.c
===================================================================
--- /trunk/src/samhain.c	(revision 293)
+++ /trunk/src/samhain.c	(revision 294)
@@ -745,4 +745,14 @@
   sh_hash_hashdelete();
   sh_files_hle_reg (NULL);
+  /*
+   * Only flush on exit if running as deamon.
+   * Otherwise we couldn't run another instance
+   * while the deamon is running (would leave the
+   * deamon with flushed ruleset).
+   */
+  if (sh.flag.isdaemon == S_TRUE)
+    {
+      sh_audit_delete_all ();
+    }
 #endif
 #if defined(SH_WITH_SERVER)
@@ -1769,4 +1779,6 @@
 	      (void) sh_ignore_clean ();
 	      (void) hash_full_tree ();
+	      sh_audit_delete_all ();
+
 
 #if defined(SH_WITH_CLIENT)
Index: /trunk/src/sh_audit.c
===================================================================
--- /trunk/src/sh_audit.c	(revision 294)
+++ /trunk/src/sh_audit.c	(revision 294)
@@ -0,0 +1,387 @@
+/* SAMHAIN file system integrity testing                                   */
+/* Copyright (C) 2010 Rainer Wichmann                                      */
+/*                                                                         */
+/*  This program is free software; you can redistribute it                 */
+/*  and/or modify                                                          */
+/*  it under the terms of the GNU General Public License as                */
+/*  published by                                                           */
+/*  the Free Software Foundation; either version 2 of the License, or      */
+/*  (at your option) any later version.                                    */
+/*                                                                         */
+/*  This program is distributed in the hope that it will be useful,        */
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
+/*  GNU General Public License for more details.                           */
+/*                                                                         */
+/*  You should have received a copy of the GNU General Public License      */
+/*  along with this program; if not, write to the Free Software            */
+/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
+
+#include "config_xor.h"
+
+#if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE)
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#if !defined(SH_COMPILE_STATIC) && defined(__linux__) && defined(HAVE_AUPARSE_H) && defined(HAVE_AUPARSE_LIB)
+#include <auparse.h>
+
+#include "samhain.h"
+#include "sh_error.h"
+#include "sh_extern.h"
+#include "sh_utils.h"
+
+#undef  FIL__
+#define FIL__  _("sh_audit.c")
+
+#define REC_SIZE_SYSCALL 32
+#define REC_SIZE_EXE     64
+#define REC_SIZE_SUCCESS 32
+
+struct recordState {
+  char syscall[REC_SIZE_SYSCALL];
+  char exe[REC_SIZE_EXE];
+  char success[REC_SIZE_SUCCESS];
+  unsigned int auid;
+  unsigned int uid;
+  unsigned int gid; 
+  unsigned int euid; 
+  unsigned int egid;
+  unsigned int fsuid; 
+  unsigned int fsgid;
+  time_t time;
+  unsigned int milli;
+};
+
+static int listRecords (auparse_state_t * au, struct recordState * state)
+{
+  if (auparse_first_record(au) != 1)
+    return -1;
+
+  state->time  = auparse_get_time(au);
+  state->milli = auparse_get_milli(au);
+
+  if (auparse_find_field(au, _("syscall")))
+    sl_strlcpy(state->syscall, auparse_interpret_field(au), REC_SIZE_SYSCALL);
+
+  if (auparse_find_field(au, _("success")))
+    strncpy(state->success, auparse_interpret_field(au), REC_SIZE_SUCCESS);
+
+  if (auparse_find_field(au, "uid"))
+    state->uid = auparse_get_field_int(au);
+  if (auparse_find_field(au, "gid"))
+    state->gid = auparse_get_field_int(au);
+
+  if (auparse_find_field(au, _("euid")))
+    state->euid = auparse_get_field_int(au);
+  if (auparse_find_field(au, _("fsuid")))
+    state->fsuid = auparse_get_field_int(au);
+
+  auparse_first_field(au);
+
+  if (auparse_find_field(au, _("auid")))
+    state->auid = auparse_get_field_int(au);
+
+  auparse_first_field(au);
+
+  if (auparse_find_field(au, _("egid")))
+    state->egid = auparse_get_field_int(au);
+  if (auparse_find_field(au, _("fsgid")))
+    state->fsgid = auparse_get_field_int(au);
+
+  auparse_first_field(au);
+
+  if (auparse_find_field(au, "exe"))
+    sl_strlcpy(state->exe, auparse_interpret_field(au), REC_SIZE_EXE);
+
+  return 0;
+}
+    
+static char * doAuparse (char * file, time_t time, char * result, size_t rsize)
+{
+  struct recordState state;
+  struct recordState stateFetched;
+
+  auparse_state_t * au = auparse_init(AUSOURCE_LOGS, NULL);
+
+  if (!au)
+    {
+      char ebuf[SH_ERRBUF_SIZE];
+      int  errnum = errno;
+
+      sl_snprintf(ebuf, sizeof(ebuf), _("Error in auparse_init() - %s\n"), 
+		  strerror(errnum));
+      sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, errnum, MSG_E_SUBGEN,
+		       ebuf,
+		       _("doAuparse") );
+      return NULL;
+    }
+
+  if (ausearch_add_interpreted_item(au, _("name"), "=", file, 
+				    AUSEARCH_RULE_CLEAR) != 0)
+    {
+      goto err;
+    }
+
+  if (time != 0)
+    {
+      ausearch_add_timestamp_item(au, ">=", time-1, 0, AUSEARCH_RULE_AND);
+      ausearch_add_timestamp_item(au, "<=", time+1, 0, AUSEARCH_RULE_AND);
+    }
+
+  if (ausearch_set_stop(au, AUSEARCH_STOP_RECORD) != 0)
+    {
+      sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
+		       _("Error in ausearch_set_stop\n"),
+		       _("doAuparse") );
+      goto err;
+    }
+
+  memset(&state, '\0', sizeof(state));
+
+  while (ausearch_next_event(au) == 1) 
+    {
+      memset(&stateFetched, '\0', sizeof(state));
+      listRecords(au, &stateFetched);
+      if (0 == strcmp(stateFetched.success, "yes"))
+	{
+	  memcpy(&state, &stateFetched, sizeof(state));
+	}
+      auparse_next_event(au);
+    }
+
+  if (0 == strcmp(state.success, "yes"))
+    {
+      char * tmp_exe = sh_util_safe_name(state.exe);
+      sl_snprintf(result, rsize, 
+		  _("time=%lu.%u, syscall=%s, auid=%u, uid=%u, gid=%u, euid=%u, egid=%u, fsuid=%u, fsgid=%u, exe=%s"),
+		  (unsigned long) state.time, state.milli, 
+		  state.syscall,
+		  state.auid, state.uid, state.gid, state.euid, state.egid, 
+		  state.fsuid, state.fsgid, tmp_exe);
+      SH_FREE(tmp_exe);
+      auparse_destroy(au);
+      return result;
+    }
+
+ err:
+  auparse_destroy(au);
+  return NULL;
+}
+
+static int sh_audit_checkdaemon();
+static int  actl_pnum = -1;
+static char * actl_paths[4] = 
+  { 
+    N_("/sbin/auditctl"), 
+    N_("/usr/sbin/auditctl"),
+    N_("/bin/auditctl"), 
+    N_("/usr/bin/auditctl") 
+  };
+
+
+/* Public function to fetch an audit record for path 'file', time 'time'
+ * The 'result' array should be sized ~256 char. 
+ */
+char * sh_audit_fetch (char * file, time_t time, char * result, size_t rsize)
+{
+  char * res = NULL;
+
+  if (sh_audit_checkdaemon() >= 0)
+    {
+      res = doAuparse (file, time, result, rsize);
+
+      if (!res)
+	{
+	  res = doAuparse (file, 0, result, rsize);
+	}
+    }
+  return res;
+}
+
+void sh_audit_delete_all ()
+{
+  int p = sh_audit_checkdaemon();
+
+  if (p >= 0)
+    {
+      char command[64];
+
+      sl_snprintf(command, sizeof(command), _("%s -D -k samhain"),
+		  _(actl_paths[p]));
+      sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 
+		       0, MSG_E_SUBGEN,
+		       _("Deleting audit daemon rules with key samhain"),
+		       _("sh_audit_delete_all") );
+      sh_ext_system(command);
+    }
+  return;
+}
+
+void sh_audit_mark (char * file)
+{
+  static int flushRules = 0;
+
+  int p = sh_audit_checkdaemon();
+
+  /* Flush all rules at startup.
+   */
+  if (flushRules == 0)
+    {
+      sh_audit_delete_all ();
+      flushRules = 1;
+    }
+
+  if (p >= 0)
+    {
+      size_t len = strlen(file) + 64;
+      char * command = SH_ALLOC(len);
+      char * safe;
+
+      sl_snprintf(command, len, _("%s -w %s -p wa -k samhain"),
+		  _(actl_paths[p]),
+		  file);
+
+      safe = sh_util_safe_name_keepspace(command);
+      sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 
+		       0, MSG_E_SUBGEN,
+		       safe,
+		       _("sh_audit_mark") );
+      SH_FREE(safe);
+
+      sh_ext_system(command);
+    }
+  return;
+}
+
+
+static int sh_audit_checkdaemon()
+{
+  int  i;
+  static int flag = 0;
+  char command[64];
+  char * p;
+
+  if (flag != 0)
+    return -1;
+
+  if (actl_pnum >= 0)
+    return actl_pnum;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (0 == access(_(actl_paths[i]), F_OK))
+	{
+	  if (0 == access(_(actl_paths[i]), X_OK))
+	    {
+	      actl_pnum = i;
+	      break;
+	    }
+	  else
+	    {
+	      char ebuf[SH_ERRBUF_SIZE];
+	      int  errnum = errno;
+	      
+	      sl_snprintf(ebuf, sizeof(ebuf), 
+			  _("Cannot execute auditctl - %s\n"), 
+			  strerror(errnum));
+	      sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, 
+			       errnum, MSG_E_SUBGEN,
+			       ebuf,
+			       _("sh_audit_checkdaemon") );
+	      flag = 1;
+	      actl_pnum = -1;
+	      return -1;
+	    }
+	}
+    }
+
+  if (actl_pnum == -1 && flag == 0)
+    {
+      char ebuf[SH_ERRBUF_SIZE];
+      int  errnum = errno;
+      
+      sl_snprintf(ebuf, sizeof(ebuf), 
+		  _("Cannot find auditctl - %s\n"), 
+		  strerror(errnum));
+      sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, 
+		       errnum, MSG_E_SUBGEN,
+		       ebuf,
+		       _("sh_audit_checkdaemon") );
+      flag = 1;
+      actl_pnum = -1;
+      return -1;
+    }
+
+  /* We found an executable auditctl */
+
+  sl_snprintf(command, sizeof(command), _("%s -s"), _(actl_paths[actl_pnum]));
+  sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 
+		   0, MSG_E_SUBGEN,
+		   command,
+		   _("sh_audit_checkdaemon") );
+  p = sh_ext_popen_str (command);
+
+  if (p)
+    {
+      int retval = -1;
+      if (strstr(p, _(" pid=0 ")))
+	{
+	  sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, 
+			   0, MSG_E_SUBGEN,
+			   _("Audit daemon for Linux kernel audit system is not running"),
+			   _("sh_audit_checkdaemon") );
+	  flag = 1;
+	  actl_pnum = -1;
+	}
+      else
+	{
+	  retval = actl_pnum;
+	  sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, 
+			   retval, MSG_E_SUBGEN,
+			   _("Audit daemon is running"),
+			   _("sh_audit_checkdaemon") );
+	}
+      SH_FREE(p);
+      return retval;
+    }
+
+  sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, 
+		   errno, MSG_E_SUBGEN,
+		   _("No output from auditctl -s"),
+		   _("sh_audit_checkdaemon") );
+  flag = 1;
+  actl_pnum = -1;
+  return -1;
+}
+
+/* HAVE_AUPARSE_H */
+#else
+char * sh_audit_fetch (char * file, time_t time, char * result, size_t rsize)
+{
+  (void) file;
+  (void) time;
+  (void) result;
+  (void) rsize;
+
+  return 0;
+}
+void sh_audit_mark (char * file)
+{
+  (void) file;
+  return;
+}
+void sh_audit_delete_all ()
+{
+  return;
+}
+#endif
+
+/* client || standalone */
+#endif
+
Index: /trunk/src/sh_cat.c
===================================================================
--- /trunk/src/sh_cat.c	(revision 293)
+++ /trunk/src/sh_cat.c	(revision 294)
@@ -166,4 +166,10 @@
   { MSG_LOGMON_MARK, SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [Logfile] Event %s missing for %lu seconds\"") },
   { MSG_LOGMON_BURST, SH_ERR_SEVERE, EVENT, N_("msg=\"POLICY [Logfile] Repeated %d times: %s\" host=\"%s\"") },
+#endif
+
+#ifdef USE_REGISTRY_CHECK
+  { MSG_REG_MISS,   SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [RegistryKeyMissing]\" path=\"%s\" %s")},
+  { MSG_REG_NEW,    SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [RegistryKeyNew]\" path=\"%s\" %s")},
+  { MSG_REG_CHANGE, SH_ERR_SEVERE,  EVENT, N_("msg=\"POLICY [RegistryKeyChanged]\" path=\"%s\" %s")},
 #endif
 
@@ -503,4 +509,10 @@
 #endif
 
+#ifdef USE_REGISTRY_CHECK
+  { MSG_REG_MISS,   SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [RegistryKeyMissing] %s>, path=<%s>, %s")},
+  { MSG_REG_NEW,    SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [RegistryKeyNew] %s>, path=<%s>, %s")},
+  { MSG_REG_CHANGE, SH_ERR_SEVERE,  EVENT, N_("msg=<POLICY [RegistryKeyChanged] %s>, path=<%s>, %s")},
+#endif
+
 #if defined(SH_WITH_CLIENT) || defined(SH_STANDALONE)
   
Index: /trunk/src/sh_extern.c
===================================================================
--- /trunk/src/sh_extern.c	(revision 293)
+++ /trunk/src/sh_extern.c	(revision 294)
@@ -839,4 +839,29 @@
 
   return status;
+}
+
+/* Execute a system command */
+
+int sh_ext_system (char * command)
+{
+  sh_tas_t task;
+  int    status;
+
+  SL_ENTER(_("sh_ext_system"));
+
+  status = sh_ext_popen_init (&task, command);
+
+  if (status != 0)
+    {
+      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, status, MSG_E_SUBGEN, 
+                      _("Could not execute command"), _("sh_ext_system"));
+      SL_RETURN ((-1), _("sh_ext_system"));
+    }
+
+  /* close pipe and return exit status
+   */
+  (void) sh_ext_pclose(&task);
+  sh_ext_tas_free (&task);
+  SL_RETURN ((status), _("sh_ext_system"));
 }
 
Index: /trunk/src/sh_files.c
===================================================================
--- /trunk/src/sh_files.c	(revision 293)
+++ /trunk/src/sh_files.c	(revision 294)
@@ -742,5 +742,7 @@
 	if (0 == strncmp(myword, _("TXT"), 3))
 	  sh_files_set_mask (mask, MODI_TXT, act);
-	
+/* get content */
+	if (0 == strncmp(myword, _("AUDIT"), 3))
+	  sh_files_set_mask (mask, MODI_AUDIT, act);
       }
   }
@@ -915,6 +917,11 @@
       SH_FREE(fileName);
       SH_FREE(new_item_ptr);
-    }
-
+      new_item_ptr = NULL;
+    }
+
+  if (new_item_ptr && MODI_AUDIT_ENABLED(new_item_ptr->check_mask))
+    {
+      sh_audit_mark(new_item_ptr->name);
+    }
   SL_RETURN(0, _("sh_files_push_file_int"));
 }
@@ -1472,4 +1479,10 @@
       SH_FREE(dirName);
       SH_FREE(new_item_ptr);
+      new_item_ptr = NULL;
+    }
+
+  if (new_item_ptr && MODI_AUDIT_ENABLED(new_item_ptr->check_mask))
+    {
+      sh_audit_mark(new_item_ptr->name);
     }
 
Index: /trunk/src/sh_getopt.c
===================================================================
--- /trunk/src/sh_getopt.c	(revision 293)
+++ /trunk/src/sh_getopt.c	(revision 294)
@@ -464,4 +464,8 @@
   if (num > 0) fputc ('\n', stdout);
   fputs (_(" optionally store full text for files"), stdout); ++num;
+#endif
+#if !defined(SH_COMPILE_STATIC) && defined(__linux__) && defined(HAVE_AUPARSE_H) && defined(HAVE_AUPARSE_LIB)
+  if (num > 0) fputc ('\n', stdout);
+  fputs (_(" optionally report auditd record of changed file"), stdout); ++num;
 #endif
 #if defined(USE_XATTR)
Index: /trunk/src/sh_hash.c
===================================================================
--- /trunk/src/sh_hash.c	(revision 293)
+++ /trunk/src/sh_hash.c	(revision 294)
@@ -393,5 +393,5 @@
 }
 
-static sh_file_t * hashsearch (char * s);
+static sh_file_t * hashsearch (const char * s);
 
 static sh_file_t * tab[TABSIZE];
@@ -403,5 +403,5 @@
  **************************************************************/
 
-static int hashfunc(char *s) 
+static int hashfunc(const char *s) 
 {
   unsigned int n = 0; 
@@ -467,4 +467,26 @@
  *
  **************************************************************/
+static sh_file_t * delete_db_entry(sh_file_t *p)
+{
+  if (p->fullpath)
+    {
+      SH_FREE(p->fullpath);
+      p->fullpath = NULL;
+    }
+  if (p->linkpath)
+    {
+      if (p->linkpath != notalink)
+	SH_FREE(p->linkpath);
+      p->linkpath = NULL;
+    }
+  if (p->attr_string)
+    {
+      SH_FREE(p->attr_string);
+      p->attr_string = NULL;
+    }
+  SH_FREE(p);
+  return NULL;
+}
+
 static void hash_unvisited (int j, 
 			    sh_file_t *prev, sh_file_t *p, ShErrLevel level)
@@ -560,22 +582,7 @@
 	      else
 		prev->next = p->next;
-	      if (p->fullpath)
-		{
-		  SH_FREE(p->fullpath);
-		  p->fullpath = NULL;
-		}
-	      if (p->linkpath)
-		{
-		  if (p->linkpath != notalink)
-		    SH_FREE(p->linkpath);
-		  p->linkpath = NULL;
-		}
-	      if (p->attr_string)
-		{
-		  SH_FREE(p->attr_string);
-		  p->attr_string = NULL;
-		}
-	      SH_FREE(p);
-	      p = NULL;
+
+	      p = delete_db_entry(p);
+
 	      SL_RET0(_("hash_unvisited"));
 #else
@@ -640,4 +647,73 @@
 }
 
+/*********************************************************************
+ *
+ * Search for unvisited entries in the database, custom error handler.
+ *
+ *********************************************************************/
+void sh_hash_unvisited_custom (char prefix, void(*handler)(const char * key))
+{
+  int i;
+  sh_file_t *p    = NULL;
+  sh_file_t *prev = NULL;
+  sh_file_t *next = NULL;
+
+  SL_ENTER(_("sh_hash_unvisited_custom"));
+
+  SH_MUTEX_LOCK(mutex_hash);
+  for (i = 0; i < TABSIZE; ++i)
+    {
+      if (tab[i] != NULL)
+	{
+	  p = tab[i]; prev = p;
+
+	  do 
+	    {
+	      next = p->next;
+
+	      if (p->fullpath && 
+		  prefix == p->fullpath[0])
+		{
+		  if ((!SH_FFLAG_VISITED_SET(p->fflags)) 
+		      && (!SH_FFLAG_REPORTED_SET(p->fflags)))
+		    {
+		      handler(p->fullpath);
+
+		      if (!SH_FFLAG_CHECKED_SET(p->fflags))
+			{
+			  /* delete */
+			  if (tab[i] == p)
+			    {
+			      tab[i] = p->next;
+			      prev   = tab[i];
+			      next   = prev;
+			    }
+			  else
+			    {
+			      prev->next = p->next;
+			      next       = prev->next;
+			    }
+
+			  p = delete_db_entry(p);
+			}
+		    }
+		  if (p)
+		    {
+		      CLEAR_SH_FFLAG_VISITED(p->fflags);
+		      CLEAR_SH_FFLAG_CHECKED(p->fflags);
+		    }
+		}
+	      if (p)
+		prev = p;
+	      p    = next;
+	    } 
+	  while (p);
+	}
+    }
+  SH_MUTEX_UNLOCK(mutex_hash);
+
+  SL_RET0(_("hash_unvisited_custom"));
+}
+
 
 /**********************************************************************
@@ -683,5 +759,5 @@
  *
  ***********************************************************************/
-static sh_file_t * hashsearch (char * s) 
+static sh_file_t * hashsearch (const char * s) 
 {
   sh_file_t * p;
@@ -1947,5 +2023,5 @@
  *
  *********************************************************************/
-static sh_file_t *  sh_hash_have_it_int (char * newname)
+static sh_file_t *  sh_hash_have_it_int (const char * newname)
 {
   sh_file_t * p;
@@ -1968,5 +2044,5 @@
 }
 
-int sh_hash_have_it (char * newname)
+int sh_hash_have_it (const char * newname)
 {
   sh_file_t * p;
@@ -1993,5 +2069,5 @@
 }
 
-int sh_hash_get_it (char * newname, file_type * tmpFile)
+int sh_hash_get_it (const char * newname, file_type * tmpFile, char * fileHash)
 {
   sh_file_t * p;
@@ -2017,4 +2093,9 @@
       tmpFile->mtime = p->theFile.mtime;
       tmpFile->ctime = p->theFile.ctime;
+      tmpFile->atime = p->theFile.atime;
+
+      if (NULL != fileHash)
+	sl_strlcpy(fileHash, p->theFile.checksum, KEY_LEN+1);
+
       tmpFile->attr_string = NULL;
       retval = 0;
@@ -2144,7 +2225,7 @@
 {
   int i;
-  SL_ENTER(_("sh_hash_set_visited"));
+  SL_ENTER(_("sh_hash_set_missing"));
   i = sh_hash_set_visited_int(newname, SH_FFLAG_CHECKED);
-  SL_RETURN(i, _("sh_hash_set_visited"));
+  SL_RETURN(i, _("sh_hash_set_missing"));
 }
 
@@ -2177,7 +2258,5 @@
  ******************************************************************/
 
-void sh_hash_push2db (char * key, unsigned long val1, 
-		      unsigned long val2, unsigned long val3,
-		      unsigned char * str, int size)
+void sh_hash_push2db (const char * key, struct store2db * save)
 {
   int         i = 0;
@@ -2186,13 +2265,17 @@
   file_type * tmpFile = SH_ALLOC(sizeof(file_type));
 
+  int size            = save->size;
+  unsigned char * str = save->str;
+
+
   tmpFile->attr_string = NULL;
   tmpFile->link_path   = NULL;
 
   sl_strlcpy(tmpFile->fullpath, key, PATH_MAX);
-  tmpFile->size  = val1;
-  tmpFile->mtime = val2;
-  tmpFile->ctime = val3;
-
-  tmpFile->atime = 0;
+  tmpFile->size  = save->val0;
+  tmpFile->mtime = save->val1;
+  tmpFile->ctime = save->val2;
+  tmpFile->atime = save->val3;
+
   tmpFile->mode  = 0;
   tmpFile->owner = 0;
@@ -2226,9 +2309,10 @@
     }
 
-  if (sh.flag.checkSum == SH_CHECK_CHECK && 
-      sh.flag.update == S_TRUE)
-    sh_hash_pushdata_memory (tmpFile, SH_KEY_NULL);
+  if (sh.flag.checkSum == SH_CHECK_INIT)
+    sh_hash_pushdata (tmpFile, 
+		      (save->checksum[0] == '\0') ? SH_KEY_NULL : save->checksum);
   else
-    sh_hash_pushdata (tmpFile, SH_KEY_NULL);
+    sh_hash_pushdata_memory (tmpFile, 
+			     (save->checksum[0] == '\0') ? SH_KEY_NULL : save->checksum);
 
   if (tmpFile->link_path) SH_FREE(tmpFile->link_path);
@@ -2239,7 +2323,5 @@
 extern int sh_util_hextobinary (char * binary, char * hex, int bytes);
 
-char * sh_hash_db2pop (char * key, unsigned long * val1, 
-		       unsigned long * val2, unsigned long * val3,
-		       int * size)
+char * sh_hash_db2pop (const char * key, struct store2db * save)
 {
   size_t      len;
@@ -2247,13 +2329,17 @@
   int         i;
   char      * retval = NULL;
+  char        fileHash[KEY_LEN+1];
   file_type * tmpFile = SH_ALLOC(sizeof(file_type));
   
-  *size = 0;
-
-  if (0 == sh_hash_get_it (key, tmpFile))
-    {
-      *val1 = tmpFile->size;
-      *val2 = tmpFile->mtime;
-      *val3 = tmpFile->ctime;
+  save->size = 0;
+
+  if (0 == sh_hash_get_it (key, tmpFile, fileHash))
+    {
+      save->val0 = tmpFile->size;
+      save->val1 = tmpFile->mtime;
+      save->val2 = tmpFile->ctime;
+      save->val3 = tmpFile->atime;
+
+      sl_strlcpy(save->checksum, fileHash, KEY_LEN+1);
 
       if (tmpFile->link_path && tmpFile->link_path[0] != '-')
@@ -2266,6 +2352,6 @@
 	  if (i == 0)
 	    {
-	      *size = (len/2);
-	      p[*size] = '\0';
+	      save->size = (len/2);
+	      p[save->size] = '\0';
 	      retval = p;
 	    }
@@ -2273,18 +2359,19 @@
 	    {
 	      SH_FREE(p);
-	      *size = 0;
+	      save->size = 0;
 	    }
 	}
       else
 	{
-	  *size = 0;
+	  save->size = 0;
 	}
     }
   else
     {
-      *size = -1;
-      *val1 =  0;
-      *val2 =  0;
-      *val3 =  0;
+      save->size = -1;
+      save->val0 = 0;
+      save->val1 = 0;
+      save->val2 = 0;
+      save->val3 = 0;
     }
   if (tmpFile->link_path) SH_FREE(tmpFile->link_path);
@@ -3459,5 +3546,5 @@
 		      tmp_lnk_old, tmp_lnk);
 #else
-	  sl_snprintf(tmp, SH_MSG_BUF, _("link_old=<%s>, link_new=<%s>"),
+	  sl_snprintf(tmp, SH_MSG_BUF, _("link_old=<%s>, link_new=<%s>, "),
 		      tmp_lnk_old, tmp_lnk);
 #endif
@@ -3479,4 +3566,25 @@
 	}
 
+      if (MODI_AUDIT_ENABLED(theFile->check_mask))
+	{
+	  char result[256];
+
+	  if (NULL != sh_audit_fetch (theFile->fullpath, theFile->mtime, result, sizeof(result)))
+	    {
+#ifdef SH_USE_XML
+	      sl_strlcat(msg, _("obj=\""), SH_MSG_BUF);
+#else
+	      sl_strlcat(msg, _("obj=<"), SH_MSG_BUF);
+#endif
+
+	      sl_strlcat(msg, result, SH_MSG_BUF);
+
+#ifdef SH_USE_XML
+	      sl_strlcat(msg, _("\" "), SH_MSG_BUF);
+#else
+	      sl_strlcat(msg, _(">"), SH_MSG_BUF);
+#endif
+	    } 
+	}
 
       tmp_path = sh_util_safe_name(theFile->fullpath);
Index: /trunk/src/sh_kern.c
===================================================================
--- /trunk/src/sh_kern.c	(revision 293)
+++ /trunk/src/sh_kern.c	(revision 294)
@@ -169,5 +169,5 @@
 
 char * sh_kern_db_syscall (int num, char * prefix,
-		   void * in_name, unsigned long * addr,
+			   void * in_name, unsigned long * addr,
 			   unsigned int * code1, unsigned int * code2,
 			   int * size, int direction)
@@ -177,20 +177,29 @@
   unsigned long   x1 = 0, x2 = 0;
   unsigned char * name = (unsigned char *) in_name;
+  struct store2db save;
 
   sl_snprintf(path, 128, "K_%s_%04d", prefix, num);
 
+  memset(save, '\0', sizeof(struct store2db));
+
   if (direction == SH_KERN_DBPUSH) 
     {
-      x1 = *code1;
-      x2 = *code2;
-
-      sh_hash_push2db (path, *addr, x1, x2,
-		       name, (name == NULL) ? 0 : (*size));
+      save.val0 = *addr;
+      save.val1 = *code1;
+      save.val2 = *code2;
+      save.str  = name;
+      save.size = (name == NULL) ? 0 : (*size);
+
+      sh_hash_push2db (path, &save);
     }
   else
     {
-      p = sh_hash_db2pop (path, addr,  &x1, &x2, size);
-      *code1 = (unsigned int) x1;
-      *code2 = (unsigned int) x2;
+      p = sh_hash_db2pop (path, &save);
+
+      *addr  = (unsigned long) save.val0;
+      *code1 = (unsigned int)  save.val1;
+      *code2 = (unsigned int)  save.val2;
+
+      *size  = (int)           save.size;
     }
   return p;
Index: /trunk/src/sh_modules.c
===================================================================
--- /trunk/src/sh_modules.c	(revision 293)
+++ /trunk/src/sh_modules.c	(revision 294)
@@ -17,4 +17,5 @@
 #include "sh_portcheck.h"
 #include "sh_logmon.h"
+#include "sh_registry.h"
 
 sh_mtype modList[] = {
@@ -155,4 +156,21 @@
 #endif
 
+#ifdef USE_REGISTRY_CHECK
+  {
+    N_("REGISTRY"),
+    -1,
+    0,
+    sh_reg_check_init,
+    sh_reg_check_timer,
+    sh_reg_check_run,
+    sh_reg_check_cleanup,
+    sh_reg_check_reconf,
+
+    N_("[Registry]"),
+    sh_reg_check_table,
+    PTHREAD_MUTEX_INITIALIZER,
+  },
+#endif
+
   {
     NULL,
Index: /trunk/src/sh_registry.c
===================================================================
--- /trunk/src/sh_registry.c	(revision 294)
+++ /trunk/src/sh_registry.c	(revision 294)
@@ -0,0 +1,935 @@
+/* SAMHAIN file system integrity testing                                   */
+/* Copyright (C) 2010       Rainer Wichmann                                */
+/*                                                                         */
+/*  This program is free software; you can redistribute it                 */
+/*  and/or modify                                                          */
+/*  it under the terms of the GNU General Public License as                */
+/*  published by                                                           */
+/*  the Free Software Foundation; either version 2 of the License, or      */
+/*  (at your option) any later version.                                    */
+/*                                                                         */
+/*  This program is distributed in the hope that it will be useful,        */
+/*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
+/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
+/*  GNU General Public License for more details.                           */
+/*                                                                         */
+/*  You should have received a copy of the GNU General Public License      */
+/*  along with this program; if not, write to the Free Software            */
+/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
+
+/***************************************************************************
+ *
+ * This file provides a module for samhain to check the MS Windows registry.
+ *
+ */
+
+#include "config_xor.h"
+
+#ifdef USE_REGISTRY_CHECK
+
+#include <windows.h>
+#include <stdio.h>
+#include <time.h>
+
+#define FIL__  _("sh_registry.c")
+
+/* We don't want to build this into yule 
+ */
+#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
+
+#include <sys/types.h>
+#include <regex.h>
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_utils.h"
+#include "sh_unix.h"
+#include "sh_modules.h"
+#include "sh_hash.h"
+#include "sh_tiger.h"
+
+static int check_key (char * name, int isSingle);
+
+static int sh_reg_set_active  (const char *s);
+static int sh_reg_set_interval (const char * c);
+static int sh_reg_set_severity (const char *s);
+static int sh_reg_add_key (const char *s);
+static int sh_reg_add_hierarchy (const char *s);
+static int sh_reg_add_stop (const char *s);
+static int sh_reg_add_ign (const char *s);
+
+#define STOP_FALSE  0
+#define STOP_CHECK  1
+#define STOP_IGN    2
+
+sh_rconf sh_reg_check_table[] = {
+    {
+        N_("severitychange"),
+        sh_reg_set_severity,
+    },
+    {
+        N_("registrycheckactive"),
+        sh_reg_set_active,
+    },
+    {
+        N_("registrycheckinterval"),
+        sh_reg_set_interval,
+    },
+    {
+        N_("singlekey"),
+        sh_reg_add_key,
+    },
+    {
+        N_("hierarchy"),
+        sh_reg_add_hierarchy,
+    },
+    {
+        N_("stopatkey"),
+        sh_reg_add_stop,
+    },
+    {
+        N_("ignorekey"),
+        sh_reg_add_ign,
+    },
+    {
+        NULL,
+        NULL
+    }
+};
+
+/* Runtime configuration */
+
+#define SH_REGISTRY_INTERVAL 300
+
+static int      ShRegCheckActive      = S_FALSE;
+static time_t   sh_reg_check_interval = SH_REGISTRY_INTERVAL;
+static int      sh_reg_check_severity = SH_ERR_SEVERE;
+
+struct regkeylist {
+  char        * name;
+  int           stop;
+  int           single;
+#ifdef HAVE_REGEX_H
+  regex_t       preg;
+#endif
+
+  struct regkeylist *next;
+};
+
+static struct regkeylist * keylist = NULL;
+
+static int sh_reg_set_active(const char *s) 
+{
+  int value;
+    
+  SL_ENTER(_("sh_reg_set_active"));
+
+  value = sh_util_flagval(s, &ShRegCheckActive);
+
+  SL_RETURN((value), _("sh_reg_set_active"));
+}
+
+static int sh_reg_set_interval (const char * c)
+{
+  int retval = 0;
+  long val;
+
+  SL_ENTER(_("sh_reg_set_interval"));
+  val = strtol (c, (char **)NULL, 10);
+  if (val <= 0)
+    {
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
+		       _("registry check interval"), c);
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+      retval = -1;
+    }
+
+  sh_reg_check_interval = (time_t) val;
+  SL_RETURN(0, _("sh_reg_set_interval"));
+}
+
+static int sh_reg_set_severity (const char *s)
+{
+  char tmp[32];
+  tmp[0] = '='; tmp[1] = '\0';
+  sl_strlcat (tmp, s, 32);
+  return sh_error_set_level (tmp, &sh_reg_check_severity);
+}
+
+static int sh_reg_add_key_int (const char *s, int isSingle, int isStop)
+{
+  struct regkeylist * newkey;
+  size_t len = sl_strlen(s);
+
+  if (len > 0)
+    {
+      newkey = SH_ALLOC(sizeof(struct regkeylist));
+      newkey->single = isSingle;
+      newkey->stop   = isStop;
+      newkey->name = NULL;
+
+      if (STOP_FALSE == isStop)
+	{
+	  newkey->name = SH_ALLOC(len + 1);
+	  sl_strlcpy(newkey->name, s, len+1);
+	}
+      else
+	{
+#ifdef HAVE_REGEX_H
+	  int status = regcomp(&(newkey->preg), s, REG_NOSUB|REG_EXTENDED);
+	  if (status != 0)
+	    {
+	      char  errbuf[256];
+	      regerror(status, &(newkey->preg), errbuf, sizeof(errbuf));
+	      SH_MUTEX_LOCK(mutex_thread_nolog);
+	      sh_error_handle((-1), FIL__, __LINE__, status, MSG_E_SUBGEN, 
+			      errbuf, _("sh_reg_add_key_int"));
+	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+	      SH_FREE(newkey);
+	      return -1;
+	    }
+#else
+	  newkey->name = SH_ALLOC(len + 1);
+	  sl_strlcpy(newkey->name, s, len+1);
+#endif
+	}
+      newkey->next = keylist;
+      keylist      = newkey;
+      return 0;
+    }
+  return -1;
+}
+
+static int sh_reg_add_key (const char *s)
+{
+  return sh_reg_add_key_int (s, S_TRUE, STOP_FALSE);
+}
+static int sh_reg_add_hierarchy (const char *s)
+{
+  return sh_reg_add_key_int (s, S_FALSE, STOP_FALSE);
+}
+static int sh_reg_add_stop (const char *s)
+{
+  return sh_reg_add_key_int (s, S_FALSE, STOP_CHECK);
+}
+static int sh_reg_add_ign (const char *s)
+{
+  return sh_reg_add_key_int (s, S_FALSE, STOP_IGN);
+}
+
+/* Module functions      */
+
+int sh_reg_check_init(struct mod_type * arg)
+{
+#ifndef HAVE_PTHREAD
+  (void) arg;
+#endif
+
+  if (ShRegCheckActive == S_FALSE)
+    return SH_MOD_FAILED;
+#ifdef HAVE_PTHREAD
+  if (arg != NULL && arg->initval < 0 &&
+      (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
+    {
+      if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg))
+	return SH_MOD_THREAD;
+      else
+	return SH_MOD_FAILED;
+    }
+#endif
+  return 0;
+}
+
+int sh_reg_check_timer(time_t tcurrent)
+{
+  static time_t lastcheck = 0;
+
+  SL_ENTER(_("sh_reg_check_timer"));
+  if ((time_t) (tcurrent - lastcheck) >= sh_reg_check_interval)
+    {
+      lastcheck  = tcurrent;
+      SL_RETURN((-1), _("sh_reg_check_timer"));
+    }
+  SL_RETURN(0, _("sh_reg_check_timer"));
+}
+
+#define SH_REGFORM_NEW 1
+#define SH_REGFORM_OLD 2
+
+static char * format_changes(int flag, char * buf, size_t len,
+			     time_t time_old, unsigned long size_old, 
+			     unsigned long keys_old, unsigned long values_old,
+			     char * hash_old,
+			     time_t time_new, unsigned long size_new, 
+			     unsigned long keys_new, unsigned long values_new,
+			     char * hash_new)
+{
+  char timestr1[32];
+  char timestr2[32];
+  char timestr3[32];
+
+  char buf_old[512] = "";
+  char buf_new[512] = "";
+
+  if ((0 != (flag & SH_REGFORM_NEW)) && (NULL != hash_new))
+    {
+      (void) sh_unix_gmttime (time_new,   timestr1,  sizeof(timestr1));
+      (void) sh_unix_gmttime (keys_new,   timestr2,  sizeof(timestr2));
+      (void) sh_unix_gmttime (values_new, timestr3,  sizeof(timestr3));
+
+#ifdef SH_USE_XML
+      sl_snprintf(buf_new, sizeof(buf_new), 
+		  "size_new=\"%lu\" mtime_new=\"%s\" ctime_new=\"%s\" atime_new=\"%s\" chksum_new=\"%s\"",
+		  size_new, timestr1, timestr2, timestr3, hash_new);
+#else
+      sl_snprintf(buf_new, sizeof(buf_new), 
+		  "size_new=<%lu>, mtime_new=<%s>, ctime_new=<%s>, atime_new=<%s>, chksum_new=<%s>",
+		  size_new, timestr1, timestr2, timestr3, hash_new);
+#endif
+    }
+
+  if ((0 != (flag & SH_REGFORM_OLD)) && (NULL != hash_old))
+    {
+      (void) sh_unix_gmttime (time_old,   timestr1,  sizeof(timestr1));
+      (void) sh_unix_gmttime (keys_old,   timestr2,  sizeof(timestr2));
+      (void) sh_unix_gmttime (values_old, timestr3,  sizeof(timestr3));
+
+#ifdef SH_USE_XML
+      sl_snprintf(buf_old, sizeof(buf_old), 
+		  " size_old=\"%lu\" mtime_old=\"%s\" ctime_old=\"%s\" atime_old=\"%s\" chksum_old=\"%s\"",
+		  size_old, timestr1, timestr2, timestr3, hash_old);
+#else
+      sl_snprintf(buf_old, sizeof(buf_old), 
+		  " size_old=<%lu>, mtime_old=<%s>, ctime_old=<%s>, atime_old=<%s>, chksum_old=<%s>",
+		  size_old, timestr1, timestr2, timestr3, hash_old);
+#endif
+    }
+
+  sl_strlcpy(buf, buf_new, len);
+  sl_strlcat(buf, buf_old, len);
+
+  return buf;
+}
+
+static void report_missing_entry(const char * path)
+{
+  char  * infobuf  = SH_ALLOC(1024);
+  char  * errbuf   = SH_ALLOC(1024);
+  char  * tmp      = sh_util_safe_name (path);
+  char timestr[32];
+  struct store2db save;
+
+  memset(&save, '\0', sizeof(struct store2db));
+  sh_hash_db2pop (path, &save);
+    
+  (void) sh_unix_gmttime (save.val1, timestr,  sizeof(timestr));
+  
+  sl_snprintf(infobuf, 1024, _("mtime=%s size=%lu subkeys=%lu values=%lu"),
+	      timestr, 
+	      (unsigned long) save.val0, 
+	      (unsigned long) save.val2, 
+	      (unsigned long) save.val3);
+
+  (void) format_changes (SH_REGFORM_OLD, errbuf, 1024, 
+			 save.val1, save.val0, save.val2, save.val3, save.checksum,
+			 0, 0, 0, 0, NULL);
+  
+  SH_MUTEX_LOCK(mutex_thread_nolog);
+  sh_error_handle(sh_reg_check_severity, FIL__, __LINE__, 0, MSG_REG_MISS, 
+		  infobuf, tmp, errbuf);
+  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+  
+  SH_FREE(tmp);
+  SH_FREE(errbuf);
+  SH_FREE(infobuf);
+  return;
+}
+
+int sh_reg_check_run(void)
+{
+  struct regkeylist *this = keylist;
+
+  if (this)
+    {
+      SH_MUTEX_LOCK(mutex_thread_nolog);
+      sh_error_handle(SH_ERR_INFO, FIL__, __LINE__, 0, MSG_E_SUBGEN, 
+		      _("Checking the registry"),
+		      _("sh_reg_check_run"));
+      SH_MUTEX_UNLOCK(mutex_thread_nolog);
+
+      while (this)
+	{
+	  if (STOP_FALSE == this->stop)
+	    {
+	      /* 
+	       *  -- Check key -- 
+	       */
+	      check_key (this->name, this->single);
+	    }
+	  this = this->next;
+	}
+    }
+  sh_hash_unvisited_custom ('H', report_missing_entry);
+
+  return 0;
+}
+
+int sh_reg_check_reconf(void)
+{
+  struct regkeylist *this;
+
+  while (keylist)
+    {
+      this    = keylist;
+      keylist = keylist->next;
+
+      if (this->name)
+	SH_FREE(this->name);
+#ifdef HAVE_REGEX_H
+      if (STOP_FALSE != this->stop)
+	regfree(&(this->preg));
+#endif
+      SH_FREE(this);
+    }
+
+  sh_reg_check_interval = SH_REGISTRY_INTERVAL;
+
+  return 0;
+}
+
+int sh_reg_check_cleanup(void)
+{
+  sh_reg_check_reconf();
+  return 0;
+}
+
+/* >>>>>>>>>>>> Main check function <<<<<<<<<<<< */
+
+
+#include <windows.h>
+
+#define MAX_KEY_LENGTH (2*256)
+#define MAX_VALUE_NAME (2*16384)
+
+CHAR  achValue[MAX_VALUE_NAME];
+
+unsigned long nKeys = 0;
+unsigned long nVals = 0;
+
+static int CheckThisSubkey (HKEY key, char * subkey, char * path, int isSingle);
+
+static time_t convertTime(FILETIME * ft)
+{
+  time_t result;
+
+  /* Shift high part up by 2^32
+   */
+  UINT64 date = ((UINT64)ft->dwHighDateTime) << 32; 
+
+  /* Add low part 
+   */
+  date |= (UINT64)ft->dwLowDateTime;
+
+  /* Subtract difference between Jan 1, 1601 and Jan 1, 1970
+   */
+  date -= ((UINT64)116444736) * ((UINT64)100) * ((UINT64)10000000);
+
+  /* Divide by number of 100-nanosecond intervals per second
+   */
+  date /= ((UINT64)10000000);
+
+  /* Convert to a time_t 
+   */
+  result = (time_t) date;
+
+  return result;
+}
+
+
+
+#define SH_KEY_NULL _("000000000000000000000000000000000000000000000000")
+
+int QueryKey(HKEY hKey, char * path, size_t pathlen, int isSingle) 
+{ 
+  CHAR     achKey[MAX_KEY_LENGTH];   /* buffer for subkey name */
+  DWORD    cbName;                   /* size of name string */
+  /* CHAR     achClass[MAX_PATH] = "";  *//* buffer for class name */
+  /* DWORD    cchClassName = MAX_PATH/2;*//* size of class string */
+  DWORD    cSubKeys=0;               /* number of subkeys */
+  DWORD    cbMaxSubKey;              /* longest subkey size */
+  DWORD    cchMaxClass;              /* longest class string */
+  DWORD    cValues;              /* number of values for key */
+  DWORD    cchMaxValue;          /* longest value name */
+  DWORD    cbMaxValueData;       /* longest value data */
+  DWORD    cbSecurityDescriptor; /* size of security descriptor */
+  FILETIME ftLastWriteTime;      /* last write time */
+  DWORD    lpType;               /* type of data stored in value */
+  BYTE     lpData[256];          /* buffer for data in value */
+  DWORD    lpcbData;             /* size of lpData buffer */
+  DWORD    i, retCode; 
+  DWORD    cchValue = MAX_VALUE_NAME/2;
+
+  char hashbuf[KEYBUF_SIZE];
+  unsigned long totalSize = 0;
+  time_t fTime = 0;
+
+  char * tPath = NULL;
+  int    doUpdate = S_FALSE;
+
+  retCode = RegQueryInfoKey(
+			    hKey,                    /* key handle */
+			    NULL /* achClass */,     /* buffer for class name */
+			    NULL /* &cchClassName */,/* size of class string */
+			    NULL,                    /* reserved */
+			    &cSubKeys,               /* number of subkeys */
+			    &cbMaxSubKey,            /* longest subkey size */
+			    &cchMaxClass,            /* longest class string */
+			    &cValues,                /* number of values for this key */
+			    &cchMaxValue,            /* longest value name */
+			    &cbMaxValueData,         /* longest value data */
+			    &cbSecurityDescriptor,   /* security descriptor */
+			    &ftLastWriteTime);       /* last write time */
+  
+  if (retCode != ERROR_SUCCESS)
+    {
+      return -1;
+    }
+
+  ++nKeys;
+
+  fTime = convertTime (&ftLastWriteTime);
+
+  /* Enumerate the subkeys, until RegEnumKeyEx fails. */
+  
+  if (cSubKeys)
+    {
+      /*
+       * printf( "\nNumber of subkeys: %lu\n", (unsigned long) cSubKeys);
+       */
+
+      for (i=0; i<cSubKeys; i++) 
+	{ 
+	  cbName = MAX_KEY_LENGTH/2;
+	  retCode = RegEnumKeyEx(hKey, i,
+				 achKey, 
+				 &cbName, 
+				 NULL, 
+				 NULL, 
+				 NULL, 
+				 &ftLastWriteTime);
+ 
+	  if (retCode == ERROR_SUCCESS && S_TRUE != isSingle) 
+	    {
+	      /*
+	       * _tprintf(TEXT("(%lu) %s\\%s\n"), (unsigned long) i+1, 
+	       * path, achKey);
+	       */
+	      CheckThisSubkey (hKey, achKey, path, isSingle); 
+	    }
+	}
+    } 
+  
+  /* Enumerate the key values. */
+
+  if (cValues) 
+    {
+      char hashtmp[3][KEYBUF_SIZE];
+
+      memset(hashbuf, '0', sizeof(hashbuf));
+
+      /* Loop over values and build checksum */
+
+      for (i=0, retCode=ERROR_SUCCESS; i<cValues; i++) 
+	{ 
+	  LPBYTE lpDataAlloc = NULL;
+
+	  cchValue = MAX_VALUE_NAME/2; 
+	  achValue[0] = '\0';
+	  lpcbData = sizeof(lpData);
+	  retCode = RegEnumValue(hKey, i, 
+				 achValue, 
+				 &cchValue, 
+				 NULL, 
+				 &lpType,
+				 lpData,
+				 &lpcbData);
+	  
+	  if (retCode == ERROR_MORE_DATA)
+	    {
+	      lpDataAlloc = SH_ALLOC(lpcbData);
+
+	      retCode = RegEnumValue(hKey, i, 
+				     achValue, 
+				     &cchValue, 
+				     NULL, 
+				     &lpType,
+				     lpDataAlloc,
+				     &lpcbData);
+	    }
+
+	  if (retCode == ERROR_SUCCESS)
+	    {
+	      totalSize += lpcbData;
+
+	      /* checksum(valuename) */
+	      sh_tiger_hash (achValue, TIGER_DATA, cchValue, 
+			     hashtmp[0], KEYBUF_SIZE);
+
+	      /* checksum(valuedata) */
+	      if (NULL == lpDataAlloc)
+		{
+		  sh_tiger_hash ((char*) lpData,      TIGER_DATA, lpcbData, 
+				 hashtmp[1], KEYBUF_SIZE);
+		}
+	      else
+		{
+		  sh_tiger_hash ((char*) lpDataAlloc, TIGER_DATA, lpcbData, 
+				 hashtmp[1], KEYBUF_SIZE);
+		}
+
+	      /* old_checksum */
+	      memcpy(hashtmp[2], hashbuf, KEYBUF_SIZE);
+
+	      /* hash(hash(valuename)+hash(valuedata)+old_hash) */
+	      sh_tiger_hash ((char*) hashtmp, TIGER_DATA, 
+			     sizeof(hashtmp), hashbuf, sizeof(hashbuf));
+
+	      ++nVals;
+	    }
+
+	  if (lpDataAlloc)
+	    {
+	      SH_FREE(lpDataAlloc);
+	    }
+	}
+    }
+  else
+    {
+      /* no values */
+      sl_strlcpy(hashbuf, SH_KEY_NULL, sizeof(hashbuf));
+    }
+
+  /* Here we have:
+   *  hashbuf       [checksum over values], 
+   *  fTime         [last write time], 
+   *  totalSize     [size of all value data],
+   *  cSubKeys      [number of subkeys],
+   *  cValues       [number of values],
+   *  path, pathlen [which may be up to 131072 (256*512) bytes] 
+   */
+
+  if (pathlen > (PATH_MAX-1))
+    {
+      char hashbuf2[KEYBUF_SIZE];
+      char * p = strchr(path, '\\');
+
+      if (p)
+	{
+	  char *q = p;
+
+	  ++p;
+	  
+	  tPath = SH_ALLOC(256 + KEYBUF_SIZE);
+	  *q = '\0';
+	  sl_strlcpy(tPath, path, 256); /* truncates */
+	  *q = '\\';
+	  sl_strlcat(tPath, "\\", 257);
+	  (void) sh_tiger_hash(p, TIGER_DATA, sl_strlen(p), 
+			       hashbuf2, sizeof(hashbuf2));
+	  sl_strlcat(tPath, hashbuf2, 256 + KEYBUF_SIZE);
+	}
+    }
+ 
+  if (sh.flag.checkSum == SH_CHECK_CHECK || sh.flag.update == S_TRUE)
+    {
+      struct store2db save;
+
+      memset(&save, '\0', sizeof(struct store2db));
+
+      if (tPath)
+	{
+	  sh_hash_db2pop (tPath, &save);
+	}
+      else
+	{
+	  sh_hash_db2pop (path, &save);
+	}
+
+      if (save.size == -1)
+	{
+	  /* Not in database */
+
+	  char  * infobuf  = SH_ALLOC(1024);
+	  char  * errbuf   = SH_ALLOC(1024);
+	  char  * tmp      = sh_util_safe_name ((tPath == NULL) ? path : tPath);
+	  char timestr[32];
+      
+	  (void) sh_unix_gmttime (fTime, timestr,  sizeof(timestr));
+
+	  sl_snprintf(infobuf, 1024, 
+		      _("mtime=%s size=%lu subkeys=%lu values=%lu"), 
+		      timestr, 
+		      (unsigned long) totalSize, 
+		      (unsigned long) cSubKeys, 
+		      (unsigned long) cValues);
+
+	  (void) format_changes (SH_REGFORM_NEW, errbuf, 1024, 
+				 0, 0, 0, 0, NULL,
+				 fTime, totalSize, cSubKeys, cValues, hashbuf);
+      
+	  SH_MUTEX_LOCK(mutex_thread_nolog);
+	  sh_error_handle(sh_reg_check_severity, FIL__, __LINE__, 
+			  0, MSG_REG_NEW, 
+			  infobuf, tmp, errbuf);
+	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+	  
+	  SH_FREE(tmp);
+	  SH_FREE(errbuf);
+	  SH_FREE(infobuf);
+
+	  doUpdate = S_TRUE;
+	}
+      else if (save.val0 != totalSize || 
+	       ((time_t) save.val1) != fTime || 
+	       save.val2 != cSubKeys ||
+	       save.val3 != cValues ||
+	       0 != strcmp(save.checksum, hashbuf))
+	{
+	  /* Change detected */
+	  char  * infobuf  = SH_ALLOC(1024);
+	  char  * errbuf   = SH_ALLOC(1024);
+	  char  * tmp      = sh_util_safe_name ((tPath == NULL) ? path : tPath);
+ 	  char timestr_new[32];
+      
+	  (void) sh_unix_gmttime (fTime,     timestr_new,  sizeof(timestr_new));
+
+	  sl_snprintf(infobuf, 1024, 
+		      _("mtime=%s size %lu->%lu subkeys %lu->%lu values %lu->%lu checksum %s"), 
+		      timestr_new, 
+		      (unsigned long) save.val0, (unsigned long) totalSize, 
+		      (unsigned long) save.val2, (unsigned long) cSubKeys, 
+		      (unsigned long) save.val3, (unsigned long) cValues, 
+		      (0 == strcmp(save.checksum, hashbuf)) ? _("good") : _("bad"));
+
+	  (void) format_changes (SH_REGFORM_OLD|SH_REGFORM_NEW, errbuf, 1024, 
+				 save.val1, save.val0, 
+				 save.val2, save.val3, save.checksum,
+				 fTime, totalSize, 
+				 cSubKeys, cValues, hashbuf);
+      
+	  SH_MUTEX_LOCK(mutex_thread_nolog);
+	  sh_error_handle(sh_reg_check_severity, FIL__, __LINE__, 
+			  0, MSG_REG_CHANGE, 
+			  infobuf, tmp, errbuf);
+	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+	  
+	  SH_FREE(tmp);
+	  SH_FREE(errbuf);
+	  SH_FREE(infobuf);
+
+	  doUpdate = S_TRUE;
+	}
+    }
+ 
+  if ( sh.flag.checkSum == SH_CHECK_INIT || doUpdate == S_TRUE )
+    {
+      struct store2db save;
+
+      memset(&save, '\0', sizeof(struct store2db));
+      
+      save.val0 = totalSize;
+      save.val1 = fTime;
+      save.val2 = cSubKeys;
+      save.val3 = cValues;
+      sl_strlcpy(save.checksum, hashbuf, KEY_LEN+1);
+
+      if (tPath)
+	{
+	  sh_hash_push2db (tPath, &save);
+	}
+      else
+	{
+	  sh_hash_push2db (path, &save);
+	}
+    }
+
+  if (tPath)
+    sh_hash_set_visited (tPath);
+  else
+    sh_hash_set_visited (path);
+
+  if (tPath)
+    {
+      SH_FREE(tPath);
+    }
+
+  return 0;
+}
+
+static int check_for_stop (char * name)
+{
+  struct regkeylist *this = keylist;
+
+  while (this)
+    {
+      if (STOP_FALSE != this->stop)
+	{
+#ifdef HAVE_REGEX_H
+	  if (0 == regexec(&(this->preg), name, 0, NULL, 0))
+	    return this->stop;
+#else
+	  if (0 == strcmp(this->name, name))
+	    return this->stop;
+#endif
+	}
+      this = this->next;
+    }
+  return STOP_FALSE;
+}
+
+
+int CheckThisSubkey (HKEY key, char * subkey, char * path, int isSingle)
+{
+  HKEY hTestKey;
+  char * newpath;
+  size_t len;
+  int    retval = -1;
+  
+  len = strlen(path) + 1 + strlen(subkey) + 1;
+  newpath = SH_ALLOC(len);
+  snprintf(newpath, len, "%s\\%s", path, subkey);
+  
+  /* Check for stop condition, if not single key. 
+   * Set flag to isSingle = S_TRUE if we should stop here. 
+   */
+  if (S_TRUE != isSingle)
+    {
+      int isStop = check_for_stop(newpath);
+
+      if (STOP_CHECK == isStop)
+	{
+	  isSingle = S_TRUE;
+	}
+      else if (STOP_IGN == isStop)
+	{
+	  SH_FREE(newpath);
+	  return 0;
+	}
+    }
+
+  len = strlen(path) + 1 + strlen(subkey) + 1;
+  newpath = SH_ALLOC(len);
+  snprintf(newpath, len, "%s\\%s", path, subkey);
+  
+  if( RegOpenKeyEx( key,
+		    subkey,
+		    0,
+		    KEY_READ,
+		    &hTestKey) == ERROR_SUCCESS
+      )
+    {
+      QueryKey(hTestKey, newpath, len-1, isSingle);
+      RegCloseKey(hTestKey);
+      retval = 0;
+    }
+  else
+    {
+      /* Error message */
+      char  * tmp    = sh_util_safe_name (newpath);
+      size_t  tlen   = sl_strlen(tmp);
+      
+      if (SL_TRUE == sl_ok_adds(64, tlen))
+	{
+	  char * errbuf = SH_ALLOC(64 + tlen);
+	  sl_snprintf(errbuf, 64+tlen, _("Failed to open key %s"), tmp);
+	  
+	  SH_MUTEX_LOCK(mutex_thread_nolog);
+	  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
+			  errbuf, _("CheckThisSubkey"));
+	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+	  
+	  SH_FREE(errbuf);
+	}
+      sh_reg_add_ign (tmp);
+      SH_FREE(tmp);
+    }
+  
+  SH_FREE(newpath);
+  return retval;
+}
+
+
+int check_key (char * key, int isSingle)
+{
+  HKEY topKey;
+  char * subkey;
+  char path[20] = "";
+  int pos = 0;
+  
+  if      (0 == strncmp(key, _("HKEY_CLASSES_ROOT"), 17))
+    {
+      topKey = HKEY_CLASSES_ROOT;
+      pos = 17;
+      strncpy(path, _("HKEY_CLASSES_ROOT"), sizeof(path));
+    }
+  else if (0 == strncmp(key, _("HKEY_CURRENT_USER"), 17))
+    {
+      topKey = HKEY_CURRENT_USER;
+      pos = 17;
+      strncpy(path, _("HKEY_CURRENT_USER"), sizeof(path));
+    }
+  else if (0 == strncmp(key, _("HKEY_LOCAL_MACHINE"), 18))
+    {
+      topKey = HKEY_LOCAL_MACHINE;
+      pos = 18;
+      strncpy(path, _("HKEY_LOCAL_MACHINE"), sizeof(path));
+    }
+  else if (0 == strncmp(key, _("HKEY_USERS"), 10))
+    {
+      topKey = HKEY_USERS;
+      pos = 10;
+      strncpy(path, _("HKEY_USERS"), sizeof(path));
+    }
+
+
+  if (pos > 0)
+    {
+      if (key[pos] == '\\')
+	{
+	  ++pos;
+	  subkey = &key[pos];
+	}
+    }
+  else
+    {
+
+      char * tmp = sh_util_safe_name_keepspace(key);
+      size_t tlen = sl_strlen(tmp);
+
+      if (SL_TRUE == sl_ok_adds(64, tlen))
+	{
+	  char * errbuf = SH_ALLOC(64 + tlen);
+	  
+	  sl_snprintf(errbuf, 64+tlen, _("Invalid key %s"), tmp);
+	  
+	  SH_MUTEX_LOCK(mutex_thread_nolog);
+	  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
+			  errbuf, _("check_key"));
+	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+	  
+	  SH_FREE(errbuf);
+	}
+      SH_FREE(tmp);
+      return -1;
+    }
+  
+  return CheckThisSubkey (topKey, subkey, path, isSingle);
+}
+
+/* #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) */
+#endif
+
+/* #ifdef USE_REGISTRY_CHECK */
+#endif
+
Index: /trunk/src/sh_unix.c
===================================================================
--- /trunk/src/sh_unix.c	(revision 293)
+++ /trunk/src/sh_unix.c	(revision 294)
@@ -3353,5 +3353,5 @@
     {
       /* lookup file in database */
-      status = sh_hash_get_it (filename, tmpFile);
+      status = sh_hash_get_it (filename, tmpFile, NULL);
       if (status != 0) {
 	goto out;
Index: /trunk/src/sh_utils.c
===================================================================
--- /trunk/src/sh_utils.c	(revision 293)
+++ /trunk/src/sh_utils.c	(revision 294)
@@ -796,5 +796,4 @@
   SL_RETURN( 0, _("sh_util_sigtype"));
 }
-
 
 char * sh_util_siggen (char * hexkey,  
