Index: /trunk/Makefile.in
===================================================================
--- /trunk/Makefile.in	(revision 278)
+++ /trunk/Makefile.in	(revision 279)
@@ -136,5 +136,5 @@
 	$(srcsrc)/sh_mem.c $(srcsrc)/sh_entropy.c \
 	$(srcsrc)/sh_forward.c $(srcsrc)/sh_modules.c \
-	$(srcsrc)/sh_utmp.c $(srcsrc)/sh_kern.c \
+	$(srcsrc)/sh_utmp.c $(srcsrc)/sh_login_track.c $(srcsrc)/sh_kern.c \
 	$(srcsrc)/sh_suidchk.c $(srcsrc)/sh_srp.c \
 	$(srcsrc)/sh_fifo.c $(srcsrc)/sh_tools.c \
@@ -170,5 +170,5 @@
 	samhain.o sh_unix.o sh_utils.o sh_error.o \
 	sh_getopt.o sh_readconf.o sh_filter.o \
-	sh_hash.o sh_mail.o sh_nmail.o sh_mem.o \
+	sh_hash.o sh_mail.o sh_nmail.o sh_mem.o sh_login_track.o \
 	sh_entropy.o sh_forward.o sh_modules.o sh_utmp.o sh_kern.o \
 	sh_suidchk.o sh_srp.o sh_fifo.o sh_tools.o sh_html.o sh_gpg.o \
@@ -1233,4 +1233,26 @@
 	  exit 1; \
 	fi
+
+# -- NEW --
+samhain_kmem.ko: $(srcsrc)/samhain_kmem.c
+	@test -d m_comp || mkdir m_comp; \
+	echo "KVERSION := \$$(shell uname -r)" > m_comp/Makefile;\
+	echo "KSOURCE ?= /lib/modules/\$$(KVERSION)/build" >> m_comp/Makefile;\
+	echo "obj-m   := samhain_kmem.o"                   >> m_comp/Makefile;\
+	echo ".PHONY: modules install clean modules_add"   >> m_comp/Makefile;\
+	echo "install : modules_add"                       >> m_comp/Makefile;\
+	echo "modules modules_install clean:"              >> m_comp/Makefile;\
+	echo "T\$$(MAKE) -C \$$(KSOURCE) \$$@ SUBDIRS=\$$(CURDIR) KBUILD_VERBOSE=2" |  tr T '\t' >> m_comp/Makefile;\
+	cp config.h m_comp/; \
+	cp $(srcsrc)/samhain_kmem.c m_comp/; \
+	cd m_comp && $(MAKE) modules
+	@if test -f m_comp/samhain_kmem.ko; then \
+	  cp -p m_comp/samhain_kmem.ko samhain_kmem.ko; \
+	  rm -rf m_comp/; \
+	else \
+	  echo "Kernel module samhain_kmem.ko not build"; \
+	  exit 1; \
+	fi
+
 
 # -- NEW --
@@ -1743,2 +1765,3 @@
 sh_log_repeat.o: $(srcsrc)/sh_log_repeat.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h 
 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 
Index: /trunk/configure.ac
===================================================================
--- /trunk/configure.ac	(revision 278)
+++ /trunk/configure.ac	(revision 279)
@@ -12,5 +12,5 @@
 dnl start
 dnl
-AM_INIT_AUTOMAKE(samhain, 2.6.4)
+AM_INIT_AUTOMAKE(samhain, 2.7.0)
 AC_DEFINE([SAMHAIN], 1, [Application is samhain])
 AC_CANONICAL_HOST
@@ -63,4 +63,7 @@
 	   i*86*)
 	   AC_DEFINE(HOST_IS_I86LINUX)
+	   ;;
+	   x86_64)
+	   AC_DEFINE([HOST_IS_64LINUX], 1, [Define if host OS is 64bit Linux])
 	   ;;
 	   *)
@@ -2020,7 +2023,5 @@
 	]
 )
-AC_SUBST(lkm_inc)
-AC_SUBST(sh_lkm)
-AC_SUBST(sh_insmod_cmd)
+
 AC_SUBST(install_name)
 AC_SUBST(INSTALL_NAME)
@@ -2064,7 +2065,9 @@
 		kernelversion=`uname -r`
 		AC_DEFINE_UNQUOTED(SH_KERNEL_VERSION, _("${kernelversion}"), [Define the kernel version])
+
 		if test "x${withval}" != "xyes"; then
 			systemmap="${withval}"
 		fi
+
 		if test "x${cross_compiling}" = xyes; then
 			:
@@ -2072,6 +2075,46 @@
 			LIBS="$LIBS -lkvm"
 			sh_libkvm="-lkvm"
-		elif test -f "${systemmap}"; then 
-			:
+		elif test -f "${systemmap}"; then
+		     	if test -f /dev/kmem; then 
+			   :
+			else
+			   # need kernel module
+
+	   		   if test -f /lib/modules/${kernelversion}/build/include/linux/kernel.h; then
+			      lkm_inc="-I/lib/modules/${kernelversion}/build/include"
+	   		   else
+			       AC_MSG_WARN([--enable-khide: /lib/modules/${kernelversion}/build/include/linux not found])
+			       AC_MSG_WARN([--enable-khide: You may need to install the kernel-source])
+			       AC_MSG_WARN([--enable-khide: headers for the currently-running kernel.])
+	   		   fi
+
+			   AC_MSG_CHECKING([for vmlist_lock])
+			   sh_vmlist_lock=`egrep ['[bdBD] vmlist_lock$'] ${systemmap} | awk '{print $1}'` 
+			   if test x"$sh_vmlist_lock" = x; then 
+			       AC_MSG_RESULT(no)
+			   else
+			       sh_vmlist_lock="0x${sh_vmlist_lock}"
+			       AC_MSG_RESULT([${sh_vmlist_lock}])
+			       AC_DEFINE_UNQUOTED(SH_VMLIST_LOCK, ${sh_vmlist_lock}, [The address of the vmlist spinlock])
+			   fi
+
+			   AC_MSG_CHECKING([for vmlist])
+			   sh_vmlist_lock=`egrep ['[bdBD] vmlist$'] ${systemmap} | awk '{print $1}'` 
+			   if test x"$sh_vmlist" = x; then 
+			       AC_MSG_RESULT(no)
+			   else
+			       sh_vmlist="0x${sh_vmlist}"
+			       AC_MSG_RESULT([${sh_vmlist}])
+			       AC_DEFINE_UNQUOTED(SH_VMLIST, ${sh_vmlist}, [The address of the vmlist])
+			   fi
+
+			   sh_lkm="${sh_lkm} samhain_kmem.ko"
+			   echo "${sh_insmod_cmd}" | grep 'no kernel module' >/dev/null
+			   if [ $? -eq 0 ]; then
+			      sh_insmod_cmd="modprobe ${install_name}_kmem"
+			   else
+			      sh_insmod_cmd="modprobe ${install_name}_kmem; ${sh_insmod_cmd}"
+			   fi
+			fi
 		else
 			AC_MSG_ERROR([Option --with-kcheck=systemmap cannot be used, because system map ${systemmap} does not exist.])
@@ -2080,4 +2123,9 @@
 	]
 )
+
+AC_SUBST(lkm_inc)
+AC_SUBST(sh_lkm)
+AC_SUBST(sh_insmod_cmd)
+
 AC_SUBST(systemmap)
 AC_SUBST(sh_libkvm)
Index: /trunk/depend.dep
===================================================================
--- /trunk/depend.dep	(revision 278)
+++ /trunk/depend.dep	(revision 279)
@@ -82,2 +82,3 @@
 sh_log_repeat.o: $(srcsrc)/sh_log_repeat.c Makefile config_xor.h $(srcinc)/samhain.h $(srcinc)/sh_pthread.h $(srcinc)/sh_utils.h $(srcinc)/sh_string.h $(srcinc)/sh_log_check.h $(srcinc)/sh_log_evalrule.h 
 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 
Index: /trunk/depend.sum
===================================================================
--- /trunk/depend.sum	(revision 278)
+++ /trunk/depend.sum	(revision 279)
@@ -1,1 +1,1 @@
-315325077
+3825914950
Index: /trunk/docs/Changelog
===================================================================
--- /trunk/docs/Changelog	(revision 278)
+++ /trunk/docs/Changelog	(revision 279)
@@ -1,8 +1,18 @@
+2.7.0:
+	* sh_utmp.c, sh_login_track.c: additional login checks
+	* sh_unix.c: use SIGTTIN as alternative for SIGABRT
+	  (SIGABRT seems not to work on AIX, reported by Peter)
+	* sh_utmp.c: fix compile error without pthreads (inotify_watch used)
+	* sh_kern.c, kern_head.c: fix some 64bit issues
+	* dnmalloc.c: fix compiler warning (ignored ret value)
+	* Fix LSB init script for kernel module
+	* samhain_kmem kernel module for /proc/kmem added
+
 2.6.4:
-	* Don't read proc_root_iops in sh_kern.c (Problem report
-	  by H. R.)
-	* Logfile check can check output of shell commands
-	* Use data directory as default for logfile checkpoints
-	* Fix broken checkpoint save/restore for logfiles
+        * Don't read proc_root_iops in sh_kern.c (Problem report
+          by H. R.)
+        * Logfile check can check output of shell commands
+        * Use data directory as default for logfile checkpoints
+        * Fix broken checkpoint save/restore for logfiles
 
 2.6.3:
Index: /trunk/include/kern_head.h
===================================================================
--- /trunk/include/kern_head.h	(revision 278)
+++ /trunk/include/kern_head.h	(revision 279)
@@ -1,8 +1,320 @@
 
+
+/* x86_64 sys_call_table for kernel 2.4.x
+ * generated from 2.6.33 unistd_64.h
+ * grep ^__SYSCALL unistd_64.h | \
+ *   sed s/.*,[[:blank:]]// | sed 's/)//' | \
+ *   awk 'BEGIN{n = 0;}{ printf "    %-32s /%c %03d %c/\n", \
+ *                              sprintf("\"%s\",", $1), "*", n, "*"; ++n; }'
+ *
+ */
+char * syscalls_64[] = {
+    "sys_read",                      /* 000 */
+    "sys_write",                     /* 001 */
+    "sys_open",                      /* 002 */
+    "sys_close",                     /* 003 */
+    "sys_newstat",                   /* 004 */
+    "sys_newfstat",                  /* 005 */
+    "sys_newlstat",                  /* 006 */
+    "sys_poll",                      /* 007 */
+    "sys_lseek",                     /* 008 */
+    "sys_mmap",                      /* 009 */
+    "sys_mprotect",                  /* 010 */
+    "sys_munmap",                    /* 011 */
+    "sys_brk",                       /* 012 */
+    "sys_rt_sigaction",              /* 013 */
+    "sys_rt_sigprocmask",            /* 014 */
+    "stub_rt_sigreturn",             /* 015 */
+    "sys_ioctl",                     /* 016 */
+    "sys_pread64",                   /* 017 */
+    "sys_pwrite64",                  /* 018 */
+    "sys_readv",                     /* 019 */
+    "sys_writev",                    /* 020 */
+    "sys_access",                    /* 021 */
+    "sys_pipe",                      /* 022 */
+    "sys_select",                    /* 023 */
+    "sys_sched_yield",               /* 024 */
+    "sys_mremap",                    /* 025 */
+    "sys_msync",                     /* 026 */
+    "sys_mincore",                   /* 027 */
+    "sys_madvise",                   /* 028 */
+    "sys_shmget",                    /* 029 */
+    "sys_shmat",                     /* 030 */
+    "sys_shmctl",                    /* 031 */
+    "sys_dup",                       /* 032 */
+    "sys_dup2",                      /* 033 */
+    "sys_pause",                     /* 034 */
+    "sys_nanosleep",                 /* 035 */
+    "sys_getitimer",                 /* 036 */
+    "sys_alarm",                     /* 037 */
+    "sys_setitimer",                 /* 038 */
+    "sys_getpid",                    /* 039 */
+    "sys_sendfile64",                /* 040 */
+    "sys_socket",                    /* 041 */
+    "sys_connect",                   /* 042 */
+    "sys_accept",                    /* 043 */
+    "sys_sendto",                    /* 044 */
+    "sys_recvfrom",                  /* 045 */
+    "sys_sendmsg",                   /* 046 */
+    "sys_recvmsg",                   /* 047 */
+    "sys_shutdown",                  /* 048 */
+    "sys_bind",                      /* 049 */
+    "sys_listen",                    /* 050 */
+    "sys_getsockname",               /* 051 */
+    "sys_getpeername",               /* 052 */
+    "sys_socketpair",                /* 053 */
+    "sys_setsockopt",                /* 054 */
+    "sys_getsockopt",                /* 055 */
+    "stub_clone",                    /* 056 */
+    "stub_fork",                     /* 057 */
+    "stub_vfork",                    /* 058 */
+    "stub_execve",                   /* 059 */
+    "sys_exit",                      /* 060 */
+    "sys_wait4",                     /* 061 */
+    "sys_kill",                      /* 062 */
+    "sys_uname",                     /* 063 */
+    "sys_semget",                    /* 064 */
+    "sys_semop",                     /* 065 */
+    "sys_semctl",                    /* 066 */
+    "sys_shmdt",                     /* 067 */
+    "sys_msgget",                    /* 068 */
+    "sys_msgsnd",                    /* 069 */
+    "sys_msgrcv",                    /* 070 */
+    "sys_msgctl",                    /* 071 */
+    "sys_fcntl",                     /* 072 */
+    "sys_flock",                     /* 073 */
+    "sys_fsync",                     /* 074 */
+    "sys_fdatasync",                 /* 075 */
+    "sys_truncate",                  /* 076 */
+    "sys_ftruncate",                 /* 077 */
+    "sys_getdents",                  /* 078 */
+    "sys_getcwd",                    /* 079 */
+    "sys_chdir",                     /* 080 */
+    "sys_fchdir",                    /* 081 */
+    "sys_rename",                    /* 082 */
+    "sys_mkdir",                     /* 083 */
+    "sys_rmdir",                     /* 084 */
+    "sys_creat",                     /* 085 */
+    "sys_link",                      /* 086 */
+    "sys_unlink",                    /* 087 */
+    "sys_symlink",                   /* 088 */
+    "sys_readlink",                  /* 089 */
+    "sys_chmod",                     /* 090 */
+    "sys_fchmod",                    /* 091 */
+    "sys_chown",                     /* 092 */
+    "sys_fchown",                    /* 093 */
+    "sys_lchown",                    /* 094 */
+    "sys_umask",                     /* 095 */
+    "sys_gettimeofday",              /* 096 */
+    "sys_getrlimit",                 /* 097 */
+    "sys_getrusage",                 /* 098 */
+    "sys_sysinfo",                   /* 099 */
+    "sys_times",                     /* 100 */
+    "sys_ptrace",                    /* 101 */
+    "sys_getuid",                    /* 102 */
+    "sys_syslog",                    /* 103 */
+    "sys_getgid",                    /* 104 */
+    "sys_setuid",                    /* 105 */
+    "sys_setgid",                    /* 106 */
+    "sys_geteuid",                   /* 107 */
+    "sys_getegid",                   /* 108 */
+    "sys_setpgid",                   /* 109 */
+    "sys_getppid",                   /* 110 */
+    "sys_getpgrp",                   /* 111 */
+    "sys_setsid",                    /* 112 */
+    "sys_setreuid",                  /* 113 */
+    "sys_setregid",                  /* 114 */
+    "sys_getgroups",                 /* 115 */
+    "sys_setgroups",                 /* 116 */
+    "sys_setresuid",                 /* 117 */
+    "sys_getresuid",                 /* 118 */
+    "sys_setresgid",                 /* 119 */
+    "sys_getresgid",                 /* 120 */
+    "sys_getpgid",                   /* 121 */
+    "sys_setfsuid",                  /* 122 */
+    "sys_setfsgid",                  /* 123 */
+    "sys_getsid",                    /* 124 */
+    "sys_capget",                    /* 125 */
+    "sys_capset",                    /* 126 */
+    "sys_rt_sigpending",             /* 127 */
+    "sys_rt_sigtimedwait",           /* 128 */
+    "sys_rt_sigqueueinfo",           /* 129 */
+    "sys_rt_sigsuspend",             /* 130 */
+    "stub_sigaltstack",              /* 131 */
+    "sys_utime",                     /* 132 */
+    "sys_mknod",                     /* 133 */
+    "sys_ni_syscall",                /* 134 */
+    "sys_personality",               /* 135 */
+    "sys_ustat",                     /* 136 */
+    "sys_statfs",                    /* 137 */
+    "sys_fstatfs",                   /* 138 */
+    "sys_sysfs",                     /* 139 */
+    "sys_getpriority",               /* 140 */
+    "sys_setpriority",               /* 141 */
+    "sys_sched_setparam",            /* 142 */
+    "sys_sched_getparam",            /* 143 */
+    "sys_sched_setscheduler",        /* 144 */
+    "sys_sched_getscheduler",        /* 145 */
+    "sys_sched_get_priority_max",    /* 146 */
+    "sys_sched_get_priority_min",    /* 147 */
+    "sys_sched_rr_get_interval",     /* 148 */
+    "sys_mlock",                     /* 149 */
+    "sys_munlock",                   /* 150 */
+    "sys_mlockall",                  /* 151 */
+    "sys_munlockall",                /* 152 */
+    "sys_vhangup",                   /* 153 */
+    "sys_modify_ldt",                /* 154 */
+    "sys_pivot_root",                /* 155 */
+    "sys_sysctl",                    /* 156 */
+    "sys_prctl",                     /* 157 */
+    "sys_arch_prctl",                /* 158 */
+    "sys_adjtimex",                  /* 159 */
+    "sys_setrlimit",                 /* 160 */
+    "sys_chroot",                    /* 161 */
+    "sys_sync",                      /* 162 */
+    "sys_acct",                      /* 163 */
+    "sys_settimeofday",              /* 164 */
+    "sys_mount",                     /* 165 */
+    "sys_umount",                    /* 166 */
+    "sys_swapon",                    /* 167 */
+    "sys_swapoff",                   /* 168 */
+    "sys_reboot",                    /* 169 */
+    "sys_sethostname",               /* 170 */
+    "sys_setdomainname",             /* 171 */
+    "stub_iopl",                     /* 172 */
+    "sys_ioperm",                    /* 173 */
+    "sys_ni_syscall",                /* 174 */
+    "sys_init_module",               /* 175 */
+    "sys_delete_module",             /* 176 */
+    "sys_ni_syscall",                /* 177 */
+    "sys_ni_syscall",                /* 178 */
+    "sys_quotactl",                  /* 179 */
+    "sys_nfsservctl",                /* 180 */
+    "sys_ni_syscall",                /* 181 */
+    "sys_ni_syscall",                /* 182 */
+    "sys_ni_syscall",                /* 183 */
+    "sys_ni_syscall",                /* 184 */
+    "sys_ni_syscall",                /* 185 */
+    "sys_gettid",                    /* 186 */
+    "sys_readahead",                 /* 187 */
+    "sys_setxattr",                  /* 188 */
+    "sys_lsetxattr",                 /* 189 */
+    "sys_fsetxattr",                 /* 190 */
+    "sys_getxattr",                  /* 191 */
+    "sys_lgetxattr",                 /* 192 */
+    "sys_fgetxattr",                 /* 193 */
+    "sys_listxattr",                 /* 194 */
+    "sys_llistxattr",                /* 195 */
+    "sys_flistxattr",                /* 196 */
+    "sys_removexattr",               /* 197 */
+    "sys_lremovexattr",              /* 198 */
+    "sys_fremovexattr",              /* 199 */
+    "sys_tkill",                     /* 200 */
+    "sys_time",                      /* 201 */
+    "sys_futex",                     /* 202 */
+    "sys_sched_setaffinity",         /* 203 */
+    "sys_sched_getaffinity",         /* 204 */
+    "sys_ni_syscall",                /* 205 */
+    "sys_io_setup",                  /* 206 */
+    "sys_io_destroy",                /* 207 */
+    "sys_io_getevents",              /* 208 */
+    "sys_io_submit",                 /* 209 */
+    "sys_io_cancel",                 /* 210 */
+    "sys_ni_syscall",                /* 211 */
+    "sys_lookup_dcookie",            /* 212 */
+    "sys_epoll_create",              /* 213 */
+    "sys_ni_syscall",                /* 214 */
+    "sys_ni_syscall",                /* 215 */
+    "sys_remap_file_pages",          /* 216 */
+    "sys_getdents64",                /* 217 */
+    "sys_set_tid_address",           /* 218 */
+    "sys_restart_syscall",           /* 219 */
+    "sys_semtimedop",                /* 220 */
+    "sys_fadvise64",                 /* 221 */
+    "sys_timer_create",              /* 222 */
+    "sys_timer_settime",             /* 223 */
+    "sys_timer_gettime",             /* 224 */
+    "sys_timer_getoverrun",          /* 225 */
+    "sys_timer_delete",              /* 226 */
+    "sys_clock_settime",             /* 227 */
+    "sys_clock_gettime",             /* 228 */
+    "sys_clock_getres",              /* 229 */
+    "sys_clock_nanosleep",           /* 230 */
+    "sys_exit_group",                /* 231 */
+    "sys_epoll_wait",                /* 232 */
+    "sys_epoll_ctl",                 /* 233 */
+    "sys_tgkill",                    /* 234 */
+    "sys_utimes",                    /* 235 */
+    "sys_ni_syscall",                /* 236 */
+    "sys_mbind",                     /* 237 */
+    "sys_set_mempolicy",             /* 238 */
+    "sys_get_mempolicy",             /* 239 */
+    "sys_mq_open",                   /* 240 */
+    "sys_mq_unlink",                 /* 241 */
+    "sys_mq_timedsend",              /* 242 */
+    "sys_mq_timedreceive",           /* 243 */
+    "sys_mq_notify",                 /* 244 */
+    "sys_mq_getsetattr",             /* 245 */
+    "sys_kexec_load",                /* 246 */
+    "sys_waitid",                    /* 247 */
+    "sys_add_key",                   /* 248 */
+    "sys_request_key",               /* 249 */
+    "sys_keyctl",                    /* 250 */
+    "sys_ioprio_set",                /* 251 */
+    "sys_ioprio_get",                /* 252 */
+    "sys_inotify_init",              /* 253 */
+    "sys_inotify_add_watch",         /* 254 */
+    "sys_inotify_rm_watch",          /* 255 */
+    "sys_migrate_pages",             /* 256 */
+    "sys_openat",                    /* 257 */
+    "sys_mkdirat",                   /* 258 */
+    "sys_mknodat",                   /* 259 */
+    "sys_fchownat",                  /* 260 */
+    "sys_futimesat",                 /* 261 */
+    "sys_newfstatat",                /* 262 */
+    "sys_unlinkat",                  /* 263 */
+    "sys_renameat",                  /* 264 */
+    "sys_linkat",                    /* 265 */
+    "sys_symlinkat",                 /* 266 */
+    "sys_readlinkat",                /* 267 */
+    "sys_fchmodat",                  /* 268 */
+    "sys_faccessat",                 /* 269 */
+    "sys_pselect6",                  /* 270 */
+    "sys_ppoll",                     /* 271 */
+    "sys_unshare",                   /* 272 */
+    "sys_set_robust_list",           /* 273 */
+    "sys_get_robust_list",           /* 274 */
+    "sys_splice",                    /* 275 */
+    "sys_tee",                       /* 276 */
+    "sys_sync_file_range",           /* 277 */
+    "sys_vmsplice",                  /* 278 */
+    "sys_move_pages",                /* 279 */
+    "sys_utimensat",                 /* 280 */
+    "sys_epoll_pwait",               /* 281 */
+    "sys_signalfd",                  /* 282 */
+    "sys_timerfd_create",            /* 283 */
+    "sys_eventfd",                   /* 284 */
+    "sys_fallocate",                 /* 285 */
+    "sys_timerfd_settime",           /* 286 */
+    "sys_timerfd_gettime",           /* 287 */
+    "sys_accept4",                   /* 288 */
+    "sys_signalfd4",                 /* 289 */
+    "sys_eventfd2",                  /* 290 */
+    "sys_epoll_create1",             /* 291 */
+    "sys_dup3",                      /* 292 */
+    "sys_pipe2",                     /* 293 */
+    "sys_inotify_init1",             /* 294 */
+    "sys_preadv",                    /* 295 */
+    "sys_pwritev",                   /* 296 */
+    "sys_rt_tgsigqueueinfo",         /* 297 */
+    "sys_perf_event_open",           /* 298 */
+    "sys_recvmmsg",                  /* 299 */
+    NULL
+};
 
 /* i386 sys_call_table for kernel 2.4.x
  */
-char * callz_2p4[] = {
-    "sys_ni_syscall",    /* 0 - old setup() system call*/
+char * syscalls_32[] = {
+    "sys_restart_syscall",    /* 0 - old setup() system call*/
     "sys_exit",
     "sys_fork",
@@ -21,5 +333,5 @@
     "sys_chmod",        /* 15 */
     "sys_lchown16",
-    "sys_ni_syscall",                /* old break syscall holder */
+    "sys_break",
     "sys_stat",
     "sys_lseek",
@@ -35,9 +347,9 @@
     "sys_pause",
     "sys_utime",        /* 30 */
-    "sys_ni_syscall",                /* old stty syscall holder */
-    "sys_ni_syscall",                /* old gtty syscall holder */
+    "sys_stty",
+    "sys_gtty",
     "sys_access",
     "sys_nice",
-    "sys_ni_syscall",    /* 35 */        /* old ftime syscall holder */
+    "sys_ftime",        /* 35 */
     "sys_sync",
     "sys_kill",
@@ -48,5 +360,5 @@
     "sys_pipe",
     "sys_times",
-    "sys_ni_syscall",                /* old prof syscall holder */
+    "sys_prof",
     "sys_brk",        /* 45 */
     "sys_setgid16",
@@ -56,11 +368,11 @@
     "sys_getegid16",    /* 50 */
     "sys_acct",
-    "sys_umount",                    /* recycled never used  phys() */
-    "sys_ni_syscall",                /* old lock syscall holder */
+    "sys_umount2",
+    "sys_lock",
     "sys_ioctl",
     "sys_fcntl",        /* 55 */
-    "sys_ni_syscall",                /* old mpx syscall holder */
+    "sys_mpx",
     "sys_setpgid",
-    "sys_ni_syscall",                /* old ulimit syscall holder */
+    "sys_ulimit",
     "sys_olduname",
     "sys_umask",        /* 60 */
@@ -80,5 +392,5 @@
     "sys_sethostname",
     "sys_setrlimit",    /* 75 */
-    "sys_old_getrlimit",
+    "sys_getrlimit",
     "sys_getrusage",
     "sys_gettimeofday",
@@ -102,5 +414,5 @@
     "sys_getpriority",
     "sys_setpriority",
-    "sys_ni_syscall",                /* old profil syscall holder */
+    "sys_profil",
     "sys_statfs",
     "sys_fstatfs",        /* 100 */
@@ -113,8 +425,8 @@
     "sys_newlstat",
     "sys_newfstat",
-    "sys_uname",
+    "sys_olduname",
     "sys_iopl",        /* 110 */
     "sys_vhangup",
-    "sys_ni_syscall",    /* old idle system call */
+    "sys_idle",
     "sys_vm86old",
     "sys_wait4",
@@ -141,5 +453,5 @@
     "sys_sysfs",        /* 135 */
     "sys_personality",
-    "sys_ni_syscall",    /* for afs_syscall */
+    "sys_afs_syscall",
     "sys_setfsuid16",
     "sys_setfsgid16",
@@ -153,5 +465,5 @@
     "sys_getsid",
     "sys_fdatasync",
-    "sys_sysctl",
+    "sys__sysctl",
     "sys_mlock",        /* 150 */
     "sys_munlock",
@@ -168,12 +480,12 @@
     "sys_nanosleep",
     "sys_mremap",
-    "sys_setresuid16",
-    "sys_getresuid16",    /* 165 */
+    "sys_setresuid",
+    "sys_getresuid",    /* 165 */
     "sys_vm86",
     "sys_query_module",
     "sys_poll",
     "sys_nfsservctl",
-    "sys_setresgid16",    /* 170 */
-    "sys_getresgid16",
+    "sys_setresgid",    /* 170 */
+    "sys_getresgid",
     "sys_prctl",
     "sys_rt_sigreturn",
@@ -195,5 +507,5 @@
     "sys_putpmsg",        /* streams2 */
     "sys_vfork",      /* 190 */
-    "sys_getrlimit",
+    "sys_ugetrlimit",
     "sys_mmap2",
     "sys_truncate64",
@@ -202,23 +514,23 @@
     "sys_lstat64",
     "sys_fstat64",
-    "sys_lchown",
-    "sys_getuid",
-    "sys_getgid",        /* 200 */
-    "sys_geteuid",
-    "sys_getegid",
-    "sys_setreuid",
-    "sys_setregid",
-    "sys_getgroups",    /* 205 */
-    "sys_setgroups",
-    "sys_fchown",
-    "sys_setresuid",
-    "sys_getresuid",
-    "sys_setresgid",    /* 210 */
-    "sys_getresgid",
-    "sys_chown",
-    "sys_setuid",
-    "sys_setgid",
-    "sys_setfsuid",        /* 215 */
-    "sys_setfsgid",
+    "sys_lchown32",
+    "sys_getuid32",
+    "sys_getgid32",        /* 200 */
+    "sys_geteuid32",
+    "sys_getegid32",
+    "sys_setreuid32",
+    "sys_setregid32",
+    "sys_getgroups32",    /* 205 */
+    "sys_setgroups32",
+    "sys_fchown32",
+    "sys_setresuid32",
+    "sys_getresuid32",
+    "sys_setresgid32",    /* 210 */
+    "sys_getresgid32",
+    "sys_chown32",
+    "sys_setuid32",
+    "sys_setgid32",
+    "sys_setfsuid32",        /* 215 */
+    "sys_setfsgid32",
     "sys_pivot_root",
     "sys_mincore",
@@ -226,5 +538,5 @@
     "sys_getdents64",    /* 220 */
     "sys_fcntl64",
-    "sys_tux",     /* reserved for TUX */
+    "sys_tux",     /* reserved for TUX, unused */
     "sys_security",
     "sys_gettid",
@@ -277,206 +589,73 @@
     "sys_utimes",
     "sys_fadvise64_64",
-    "sys_vserver",
+    "sys_vserver",            /* last 2.4 */
+    "sys_mbind",
+    "sys_get_mempolicy",      /* 275 */
+    "sys_set_mempolicy",
+    "sys_mq_open",
+    "sys_mq_unlink",
+    "sys_mq_timedsend",
+    "sys_mq_timedreceive",    /* 280 */
+    "sys_mq_notify",
+    "sys_mq_getsetattr",
+    "sys_kexec_load",
+    "sys_waitid",
+    "sys_sys_setaltroot",     /* 285 */
+    "sys_add_key",
+    "sys_request_key",
+    "sys_keyctl",
+    "sys_ioprio_set",
+    "sys_ioprio_get",         /* 290 */
+    "sys_inotify_init",
+    "sys_inotify_add_watch",
+    "sys_inotify_rm_watch",
+    "sys_migrate_pages",
+    "sys_openat",             /* 295 */
+    "sys_mkdirat",
+    "sys_mknodat",
+    "sys_fchownat",
+    "sys_futimesat",
+    "sys_fstatat64",          /* 300 */
+    "sys_unlinkat",
+    "sys_renameat",
+    "sys_linkat",
+    "sys_symlinkat",
+    "sys_readlinkat",         /* 305 */
+    "sys_fchmodat",
+    "sys_faccessat",
+    "sys_pselect6",
+    "sys_ppoll",
+    "sys_unshare",            /* 310 */
+    "sys_set_robust_list",
+    "sys_get_robust_list",
+    "sys_splice",
+    "sys_sync_file_range",
+    "sys_tee",                /* 315 */
+    "sys_vmsplice",
+    "sys_move_pages",
+    "sys_getcpu",
+    "sys_epoll_pwait",
+    "sys_utimensat",          /* 320 */
+    "sys_signalfd",
+    "sys_timerfd_create",
+    "sys_eventfd",
+    "sys_fallocate",          /* last 2.6.24 */
+    "sys_timerfd_settime",    /* 325 */
+    "sys_timerfd_gettime",
+    "sys_signalfd4",
+    "sys_eventfd2",
+    "sys_epoll_create1",
+    "sys_dup3",               /* 330 */
+    "sys_pipe2",
+    "sys_inotify_init1",      /* end 2.6.27 */
+    "sys_preadv",
+    "sys_pwritev",            /* end 2.6.30 */
+    "sys_rt_tgsigqueueinfo",  /* 335 */
+    "sys_perf_event_open",    /* end 2.6.31 */
+    "sys_recvmmsg",
     NULL
 };
 
 
-
-/* i386 sys_call_table for kernel 2.2.x
- */
-char * callz_2p2[]={
-  "sys_ni_syscall",        /* 0 */
-  "sys_exit",
-  "sys_fork",
-  "sys_read",
-  "sys_write",
-  "sys_open",              /* 5 */
-  "sys_close",
-  "sys_waitpid", 
-  "sys_creat",
-  "sys_link",
-  "sys_unlink",              /* 10 */
-  "sys_execve",
-  "sys_chdir",
-  "sys_time",
-  "sys_mknod",
-  "sys_chmod",              /* 15 */
-  "sys_lchown",
-  "sys_ni_syscall",
-  "sys_stat",
-  "sys_lseek",
-  "sys_getpid",              /* 20 */
-  "sys_mount",
-  "sys_oldumount", 
-  "sys_setuid",
-  "sys_getuid",
-  "sys_stime",              /* 25 */
-  "sys_ptrace",
-  "sys_alarm",
-  "sys_fstat",
-  "sys_pause",
-  "sys_utime",              /* 30 */
-  "sys_ni_syscall",
-  "sys_ni_syscall",
-  "sys_access",
-  "sys_nice",
-  "sys_ni_syscall",              /* 35 */
-  "sys_sync",
-  "sys_kill",
-  "sys_rename",
-  "sys_mkdir",
-  "sys_rmdir",              /* 40 */
-  "sys_dup",
-  "sys_pipe",
-  "sys_times",
-  "sys_ni_syscall",
-  "sys_brk",              /* 45 */
-  "sys_setgid",
-  "sys_getgid",
-  "sys_signal",
-  "sys_geteuid",
-  "sys_getegid",              /* 50 */
-  "sys_acct",
-  "sys_umount",
-  "sys_ni_syscall",
-  "sys_ioctl",
-  "sys_fcntl",              /* 55 */
-  "sys_ni_syscall",
-  "sys_setpgid",
-  "sys_ni_syscall",
-  "sys_olduname",
-  "sys_umask",              /* 60 */
-  "sys_chroot",
-  "sys_ustat",
-  "sys_dup2",
-  "sys_getppid",
-  "sys_getpgrp",              /* 65 */
-  "sys_setsid",
-  "sys_sigaction",
-  "sys_sgetmask",
-  "sys_ssetmask",
-  "sys_setreuid",              /* 70 */
-  "sys_setregid",
-  "sys_sigsuspend",
-  "sys_sigpending",
-  "sys_sethostname",
-  "sys_setrlimit",              /* 75 */
-  "sys_getrlimit",
-  "sys_getrusage",
-  "sys_gettimeofday",
-  "sys_settimeofday",
-  "sys_getgroups",              /* 80 */
-  "sys_setgroups",
-  "old_select",
-  "sys_symlink",
-  "sys_lstat",
-  "sys_readlink",              /* 85 */
-  "sys_uselib",
-  "sys_swapon",
-  "sys_reboot",
-  "old_readdir",
-  "old_mmap",              /* 90 */
-  "sys_munmap",
-  "sys_truncate",
-  "sys_ftruncate",
-  "sys_fchmod",
-  "sys_fchown",              /* 95 */
-  "sys_getpriority",
-  "sys_setpriority",
-  "sys_ni_syscall",
-  "sys_statfs",
-  "sys_fstatfs",              /* 100 */
-  "sys_ioperm",
-  "sys_socketcall",
-  "sys_syslog",
-  "sys_setitimer",
-  "sys_getitimer",              /* 105 */
-  "sys_newstat",
-  "sys_newlstat",
-  "sys_newfstat",
-  "sys_uname",
-  "sys_iopl",              /* 110 */
-  "sys_vhangup",
-  "sys_idle",
-  "sys_vm86old",
-  "sys_wait4",
-  "sys_swapoff",              /* 115 */
-  "sys_sysinfo",
-  "sys_ipc",
-  "sys_fsync",
-  "sys_sigreturn",
-  "sys_clone",              /* 120 */
-  "sys_setdomainname",
-  "sys_newuname",
-  "sys_modify_ldt",
-  "sys_adjtimex",
-  "sys_mprotect",              /* 125 */
-  "sys_sigprocmask",
-  "sys_create_module",
-  "sys_init_module",
-  "sys_delete_module",
-  "sys_get_kernel_syms", /* 130 */
-  "sys_quotactl",
-  "sys_getpgid",
-  "sys_fchdir",
-  "sys_bdflush",
-  "sys_sysfs",              /* 135 */
-  "sys_personality",
-  "sys_ni_syscall",
-  "sys_setfsuid",
-  "sys_setfsgid",
-  "sys_llseek",              /* 140 */
-  "sys_getdents",
-  "sys_select",
-  "sys_flock",
-  "sys_msync",
-  "sys_readv",              /* 145 */
-  "sys_writev",
-  "sys_getsid",
-  "sys_fdatasync",
-  "sys_sysctl",
-  "sys_mlock",              /* 150 */
-  "sys_munlock",
-  "sys_mlockall",
-  "sys_munlockall",
-  "sys_sched_setparam", 
-  "sys_sched_getparam",  /* 155 */
-  "sys_sched_setscheduler",
-  "sys_sched_getscheduler",
-  "sys_sched_yield",
-  "sys_sched_get_priority_max",
-  "sys_sched_get_priority_min", /* 160 */
-  "sys_sched_rr_get_interval",
-  "sys_nanosleep",
-  "sys_mremap",
-  "sys_setresuid",
-  "sys_getresuid",              /* 165 */
-  "sys_vm86",
-  "sys_query_module",
-  "sys_poll",
-  "sys_nfsservctl", 
-  "sys_setresgid",              /* 170 */
-  "sys_getresgid",
-  "sys_prctl",
-  "sys_rt_sigreturn",
-  "sys_rt_sigaction",
-  "sys_rt_sigprocmask", /* 175 */
-  "sys_rt_sigpending",
-  "sys_rt_sigtimedwait",
-  "sys_rt_sigqueueinfo",
-  "sys_rt_sigsuspend",
-  "sys_pread",              /* 180 */
-  "sys_pwrite",
-  "sys_chown",
-  "sys_getcwd",
-  "sys_capget",
-  "sys_capset",              /* 185 */
-  "sys_sigaltstack",
-  "sys_sendfile",
-  "sys_ni_syscall",
-  "sys_ni_syscall",
-  "sys_vfork",              /* 190 */
-  NULL
-};
 
 /* i386 sys_call_table for openbsd
Index: /trunk/include/sh_cat.h
===================================================================
--- /trunk/include/sh_cat.h	(revision 278)
+++ /trunk/include/sh_cat.h	(revision 279)
@@ -130,4 +130,7 @@
  MSG_UT_ROT,      
 
+ MSG_UT_BAD,
+ MSG_UT_FIRST,
+ MSG_UT_OUTLIER,
 #endif
 
Index: /trunk/include/sh_error.h
===================================================================
--- /trunk/include/sh_error.h	(revision 278)
+++ /trunk/include/sh_error.h	(revision 279)
@@ -121,4 +121,8 @@
 int  sh_log_set_facility (const char * c);
 
+/* map heartbeat messages 
+ */
+int sh_log_set_stamp_priority (const char * c);
+
 /* define message header
  */
Index: /trunk/include/sh_utmp.h
===================================================================
--- /trunk/include/sh_utmp.h	(revision 278)
+++ /trunk/include/sh_utmp.h	(revision 279)
@@ -19,4 +19,22 @@
 
 extern sh_rconf sh_utmp_table[];
+
+/* >>           Login tracking             << */
+
+/* 'yes', 'no', 'paranoid'                    */
+int sh_login_set_siglevel      (const char * c);
+
+/* 'yes', 'no', 'domain'                      */
+int sh_login_set_checklevel    (const char * c);
+
+/* 'always' 'never' workdays(..) sunday(..)   */
+int sh_login_set_def_allow     (const char * c);
+
+/* user:'always' 'never' workdays(..)         */
+int sh_login_set_user_allow    (const char * c);
+
+/* Reset everything to defaults.              */
+void sh_login_reset (void);
+
 #endif
 
Index: /trunk/init/samhain.startLSB.in
===================================================================
--- /trunk/init/samhain.startLSB.in	(revision 278)
+++ /trunk/init/samhain.startLSB.in	(revision 279)
@@ -89,4 +89,11 @@
 	${DAEMON} start
 	ERRNUM=$?
+        #
+        # The hiding kernel module
+        #
+        if [ $ERRNUM -eq 0 ]; then
+                @sh_insmod_cmd@
+        fi
+	#
 	SH_ACT="started"
 	;;
Index: /trunk/scripts/redhat_i386.client.spec.in
===================================================================
--- /trunk/scripts/redhat_i386.client.spec.in	(revision 278)
+++ /trunk/scripts/redhat_i386.client.spec.in	(revision 279)
@@ -78,6 +78,6 @@
 # after the package is installed
 install -m 700 samhain-install.sh init/samhain.startLinux init/samhain.startLSB ${RPM_BUILD_ROOT}/etc
-install -m 640 -o 0 -g 0 samhain_hide.o		${RPM_BUILD_ROOT}/lib/modules/`uname -r`/samhain_hide.o
-install -m 640 -o 0 -g 0 samhain_erase.o	${RPM_BUILD_ROOT}/lib/modules/`uname -r`/samhain_erase.o
+install -m 640 -o 0 -g 0 samhain_kmem.ko	${RPM_BUILD_ROOT}/lib/modules/`uname -r`/samhain_kmem.ko
+install -m 640 -o 0 -g 0 samhain_hide.ko        ${RPM_BUILD_ROOT}/lib/modules/`uname -r`/samhain_hide.ko
 install -m 700 -o 0 -g 0 samhain_setpwd		${RPM_BUILD_ROOT}/usr/local/sbin/samhain_setpwd
 
Index: /trunk/src/dnmalloc.c
===================================================================
--- /trunk/src/dnmalloc.c	(revision 278)
+++ /trunk/src/dnmalloc.c	(revision 279)
@@ -309,4 +309,5 @@
   char * i3 = "): ";
   char * i5 = "\n";
+  int   res = 0;
 
   iov[0].iov_base = i1;               iov[0].iov_len = strlen(i1); 
@@ -314,6 +315,8 @@
   iov[2].iov_base = i3;               iov[2].iov_len = strlen(i3); 
   iov[3].iov_base = (char*) error;    iov[3].iov_len = strlen(error); 
-  iov[4].iov_base = i5;               iov[4].iov_len = strlen(i5); 
-  writev(STDERR_FILENO, iov, 5);
+  iov[4].iov_base = i5;               iov[4].iov_len = strlen(i5);
+  do {
+    res = writev(STDERR_FILENO, iov, 5);
+  } while (res < 0 && errno == EINTR);  
 #else
   fputs("assertion failed (", stderr);
Index: /trunk/src/kern_head.c
===================================================================
--- /trunk/src/kern_head.c	(revision 278)
+++ /trunk/src/kern_head.c	(revision 279)
@@ -7,5 +7,5 @@
 #include "config.h"
 
-#ifdef HOST_IS_I86LINUX
+#if defined(HOST_IS_I86LINUX) || defined(HOST_IS_64LINUX)
 #define SH_IDT_TABLE
 #endif
@@ -73,4 +73,6 @@
 static unsigned char system_call_code[SYS_CODE_SIZE];
 
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
 static int kmem_read (int fd, unsigned long addr, unsigned char * buf, int len)
 {
@@ -105,6 +107,5 @@
 
   if (verbose)
-    fprintf(stderr, 
-	    "kmem_mmap: read() from /dev/kmem failed, now trying mmap()\n");
+    fprintf(stderr, "kmem_mmap: read() failed, now trying mmap()\n");
 
   sz = getpagesize(); /* unistd.h */
@@ -141,9 +142,29 @@
 
   fd = open ("/dev/kmem", O_RDONLY);
+
   if (fd < 0)
     {
-      perror("read_kcode: open /dev/kmem");
-      return -1;
-    }
+      if (0 != access("/proc/kmem", R_OK)) 
+	{
+	  perror("read_kcode: access /proc/kmem");
+
+	  fprintf(stderr, "\n");
+      
+	  fprintf(stderr, "NOTE:  kern_head: apparently you have no /dev/kmem, and the samhain_kmem module is not loaded\n");
+	  fprintf(stderr, "       If you get this message, then proceed as follows:\n");
+	  fprintf(stderr, "       $ make samhain_kmem.ko\n");
+	  fprintf(stderr, "       $ sudo insmod samhain_kmem.ko; sudo ./kern_head > sh_ks.h; sudo rmmod samhain_kmem\n");
+	  fprintf(stderr, "       $ make\n\n");
+	  exit (EXIT_FAILURE);
+	}
+      fd = open ("/proc/kmem", O_RDONLY);
+    }
+
+  if (fd < 0)
+    {
+      perror("read_kcode: open /dev/kmem and /proc/kmem");
+      return -1;
+    }
+
   if (kmem_mmap(fd, addr, buf, len) < 0)
     {
@@ -151,5 +172,7 @@
       return -1;
     }
+
   close (fd);
+
   return 0;
 }
@@ -203,6 +226,11 @@
 {
   FILE * fp;
-  char buf[512], addr[16], * p;
+  char buf[512], addr[32], * p;
   unsigned long retval = 0;
+#if defined(__x86_64__) || defined(__amd64__)
+  int off = 8;
+#else
+  int off = 0;
+#endif
 
   fp = fopen (systemmap, "r");
@@ -216,5 +244,5 @@
   while (fgets(buf, 512, fp) != NULL)
     {
-      if (buf[9] != flag)
+      if (buf[9+off] != flag)
         continue;
 
@@ -223,9 +251,9 @@
         *p = '\0';
 
-      if (0 != strcmp(&buf[11], symbol))
+      if (0 != strcmp(&buf[11+off], symbol))
         continue;
 
       addr[0] = '0'; addr[1] = 'x'; addr[2] = '\0';
-      strncat(&addr[2], buf, 8);
+      strncat(&addr[2], buf, 8+off);
 
       retval = strtoul(addr, NULL, 0);
@@ -247,6 +275,11 @@
 {
   FILE * fp;
-  char buf[512], addr[16], name[128];
+  char buf[512], addr[32], name[128];
   int  i, j, count = 0, maxcall = 0;
+#if defined(__x86_64__) || defined(__amd64__)
+  int off = 8;
+#else
+  int off = 0;
+#endif
 
   fp = fopen (SYSTEMMAP, "r");
@@ -266,7 +299,7 @@
     {
       
-      if ( ( (buf[9] == 'D') || (buf[9] == 'd') || 
-	     (buf[9] == 'R') || (buf[9] == 'r')) && 
-	   0 == strncmp("sys_call_table", &buf[11], 14))
+      if ( ( (buf[9+off] == 'D') || (buf[9+off] == 'd') || 
+	     (buf[9+off] == 'R') || (buf[9+off] == 'r')) && 
+	   0 == strncmp("sys_call_table", &buf[11+off], 14))
 	{
 	  printf("/* found sys_call_table */\n");
@@ -274,6 +307,7 @@
 	   */
 	  addr[0] = '0'; addr[1] = 'x'; addr[2] = '\0';
-	  strncat(&addr[2], buf, 8);
-	  addr[10] = '\0';
+	  strncat(&addr[2], buf, 8+off);
+	  addr[10+off] = '\0';
+
 	  sh_sys_call.addr_sys_call_table = strtoul(addr, NULL, 0);
 	  if (sh_sys_call.addr_sys_call_table == ULONG_MAX)
@@ -288,8 +322,8 @@
 	}
 
-      if (buf[9] != 'T')
+      if (buf[9+off] != 'T')
 	continue;
 
-      if (0 == strncmp("system_call", &buf[11], 11))
+      if (0 == strncmp("system_call", &buf[11+off], 11))
 	{
 	  printf("/* found system_call */\n");
@@ -297,6 +331,6 @@
 	   */
 	  addr[0] = '0'; addr[1] = 'x'; addr[2] = '\0';
-	  strncat(&addr[2], buf, 8);
-	  addr[10] = '\0';
+	  strncat(&addr[2], buf, 8+off);
+	  addr[10+off] = '\0';
 	  addr_system_call = strtoul(addr, NULL, 0);
 	  if (addr_system_call == ULONG_MAX)
@@ -308,18 +342,20 @@
 
 
-      if ( (buf[11]!='s' || buf[12]!='y' || buf[13]!='s' || buf[14]!='_') &&
-	   (buf[11]!='o' || buf[12]!='l' || buf[13]!='d' || buf[14]!='_'))
+      if ( (buf[11+off]!='s' || buf[12+off]!='y' || 
+	    buf[13+off]!='s' || buf[14+off]!='_') &&
+	   (buf[11+off]!='o' || buf[12+off]!='l' || 
+	    buf[13+off]!='d' || buf[14+off]!='_'))
 	continue;
 
       for (i = 0; i < num; ++i)
 	{
-	  for (j = 0; j < 128; ++j)
+	  for (j = 0; j < 127; ++j)
 	    {
-	      if (buf[11+j] == '\n' || buf[11+j] == '\0')
+	      if (buf[11+off+j] == '\n' || buf[11+off+j] == '\0')
 		{
 		  name[j] = '\0';
 		  break;
 		}
-	      name[j] = buf[11+j];
+	      name[j] = buf[11+off+j];
 	    }
 
@@ -331,6 +367,6 @@
 	       */
 	      addr[0] = '0'; addr[1] = 'x'; addr[2] = '\0';
-	      strncat(&addr[2], buf, 8);
-	      addr[10] = '\0';
+	      strncat(&addr[2], buf, 8+off);
+	      addr[10+off] = '\0';
 	      sh_smap[i].addr = strtoul(addr, NULL, 0);
 	      if (sh_smap[i].addr == ULONG_MAX)
@@ -347,4 +383,5 @@
     }
   fclose(fp);
+
   if ((count > 0) && (maxcall > 0))
     return maxcall+1;
@@ -357,6 +394,4 @@
 {
   int i, count, maxcall, qq;
-  int which = 4;
-  int two_six_seventeen_plus = 0;
   smap_entry sh_smap[SH_MAXCALLS];
   struct utsname utbuf;
@@ -368,4 +403,6 @@
 
   unsigned long addr_ni_syscall = 0;
+
+  int major, minor, micro, is64 = 0;
 
   if (argc > 1)
@@ -396,17 +433,11 @@
     }
 
-  if      (strncmp(p, "2.2", 3) == 0)
-    which = 2;
-  else if (strncmp(p, "2.4", 3) == 0)
-    which = 4;
-  else if (strncmp(p, "2.6", 3) == 0)
-    {
-      which = 6;
-      if (17 >= atoi (&p[4]))
-	{
-	  two_six_seventeen_plus = 1;
-	}
-    }
-  else
+  if (3 != sscanf(p, "%d.%d.%d", &major, &minor, &micro))
+    {
+      perror("kern_head: sscanf");
+      exit (EXIT_FAILURE);
+    }
+
+  if (minor != 4 && minor != 6)
     {
       fprintf(stderr, "kern_head: kernel %s not supported\n", p);
@@ -418,6 +449,13 @@
       utbuf.machine[3] != '6')
     {
-      fprintf(stderr, "kern_head: machine %s not supported\n", utbuf.machine);
-      exit (EXIT_FAILURE);
+      if (0 != strcmp(utbuf.machine, "x86_64"))
+	{
+	  fprintf(stderr, "kern_head: machine %s not supported\n", utbuf.machine);
+	  exit (EXIT_FAILURE);
+	}
+      else
+	{
+	  is64 = 1;
+	}
     }
 
@@ -428,7 +466,5 @@
       fprintf(stderr, "NOTE:  kern_head: must run as 'root' (need to read from /dev/kmem)\n");
       fprintf(stderr, "       If you get this message, then proceed as follows:\n");
-      fprintf(stderr, "       $ su\n");
-      fprintf(stderr, "       $ ./kern_head > sh_ks.h\n");
-      fprintf(stderr, "       $ exit\n");
+      fprintf(stderr, "       $ sudo ./kern_head > sh_ks.h\n");
       fprintf(stderr, "       $ make\n\n");
       exit (EXIT_FAILURE);
@@ -438,37 +474,41 @@
   printf("#define SH_KERN_CALLS_H\n\n");
 
-  printf("\n/* Kernel %s, machine %s -- use table %s */\n\n", 
+  printf("\n/* Kernel %s, machine %s, %d bit -- use table callz_2p4 */\n\n", 
 	 p, utbuf.machine,
-	 (which == 2) ? "callz_2p2" : "callz_2p4");
-      
+	 (is64 == 0) ? 32 : 64,
+	 (is64 == 0) ? "syscalls_32" : "syscalls_64");
 
   /* initiate the system call table 
    */
-  for (i = 0; i < SH_MAXCALLS; ++i)
-    {
-      if (which == 2)
-	{
-	  if (callz_2p2[i] == NULL)
+  if (is64 == 0)
+    {
+      for (i = 0; i < SH_MAXCALLS; ++i)
+	{
+	  if (syscalls_32[i] == NULL)
 	    break;
-	  strcpy(sh_smap[i].name, callz_2p2[i]);
-	}
-      else
-	{
-	  if (callz_2p4[i] == NULL)
+	  strcpy(sh_smap[i].name, syscalls_32[i]);
+	  sh_smap[i].addr    = 0UL;
+	}
+      if (minor == 6) /* fix syscall map for 2.6 */
+	{
+	  strcpy(sh_smap[0].name,   "sys_restart_syscall");
+	  strcpy(sh_smap[180].name, "sys_pread64");
+	  strcpy(sh_smap[181].name, "sys_pwrite64");
+	}
+    }
+  else /* x86_64 */
+    {
+      for (i = 0; i < SH_MAXCALLS; ++i)
+	{
+	  if (syscalls_64[i] == NULL)
 	    break;
-	  strcpy(sh_smap[i].name, callz_2p4[i]);
-	}
-      sh_smap[i].addr    = 0UL;
-    }
-
-  if (which == 6) /* fix syscall map for 2.6 */
-    {
-      strcpy(sh_smap[0].name,   "sys_restart_syscall");
-      strcpy(sh_smap[180].name, "sys_pread64");
-      strcpy(sh_smap[181].name, "sys_pwrite64");
-    }
+	  strcpy(sh_smap[i].name, syscalls_64[i]);
+	  sh_smap[i].addr    = 0UL;
+	}
+    }
+
   count = i;
 
-  /* get the actual number of the highest syscalls and use no more.
+  /* get the actual number of the highest syscall and use no more.
    * get sys_call_table and system_call
    */
@@ -480,4 +520,5 @@
       exit (EXIT_FAILURE);
     }
+
   if (addr_system_call == 0L) 
     {
@@ -502,5 +543,6 @@
         }
     }
-  if (which < 6)
+
+  if (minor < 6)
     {
       maxcall = (maxcall > 256) ? 256 : maxcall;
@@ -622,7 +664,8 @@
   }
 
-  if (two_six_seventeen_plus == 1) {
-    printf("#define TWO_SIX_SEVENTEEN_PLUS 1\n\n");
-  }
+  if (KERNEL_VERSION(major,minor,micro) >= KERNEL_VERSION(2,6,17)) 
+    {
+      printf("#define TWO_SIX_SEVENTEEN_PLUS 1\n\n");
+    }
 
   printf("#endif\n");
Index: /trunk/src/samhain_kmem.c
===================================================================
--- /trunk/src/samhain_kmem.c	(revision 279)
+++ /trunk/src/samhain_kmem.c	(revision 279)
@@ -0,0 +1,602 @@
+/* Most of this code is ripped from the Linux kernel:
+ *
+ *  linux/drivers/char/mem.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Added devfs support. 
+ *    Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu>
+ *  Shared /dev/zero mmaping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
+ */
+
+#include "config.h" 
+
+#undef _
+#define _(string) string
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mman.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/raw.h>
+#include <linux/tty.h>
+#include <linux/capability.h>
+#include <linux/ptrace.h>
+#include <linux/device.h>
+#include <linux/highmem.h>
+#include <linux/crash_dump.h>
+#include <linux/backing-dev.h>
+#include <linux/bootmem.h>
+#include <linux/splice.h>
+#include <linux/pfn.h>
+#include <linux/version.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+
+#ifdef CONFIG_IA64
+# include <linux/efi.h>
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("samhain_kmem Kernel Module");
+MODULE_AUTHOR("Rainer Wichmann");
+
+static int debug = 0;
+#ifdef MODULE_PARM 
+MODULE_PARM (debug, "i");
+#else
+module_param(debug, int, 0444);
+#endif
+
+#ifdef MODULE_PARM_DESC
+MODULE_PARM_DESC(debug, "Set to a non-zero value for debugging.");
+#endif
+
+/* struct task_struct
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+#define TASK_EUID euid
+#else
+#define TASK_EUID cred->euid
+#endif
+
+static struct proc_dir_entry *proc_entry;
+
+/*
+ * Architectures vary in how they handle caching for addresses
+ * outside of main memory.
+ *
+ */
+static inline int uncached_access(struct file *file, unsigned long addr)
+{
+#if defined(__i386__) && !defined(__arch_um__)
+  /*
+   * On the PPro and successors, the MTRRs are used to set
+   * memory types for physical addresses outside main memory,
+   * so blindly setting PCD or PWT on those pages is wrong.
+   * For Pentiums and earlier, the surround logic should disable
+   * caching for the high addresses through the KEN pin, but
+   * we maintain the tradition of paranoia in this code.
+   */
+  if (file->f_flags & O_SYNC)
+    return 1;
+  return !( test_bit(X86_FEATURE_MTRR, (const void *) boot_cpu_data.x86_capability) ||
+	    test_bit(X86_FEATURE_K6_MTRR, (const void *) boot_cpu_data.x86_capability) ||
+	    test_bit(X86_FEATURE_CYRIX_ARR, (const void *) boot_cpu_data.x86_capability) ||
+	    test_bit(X86_FEATURE_CENTAUR_MCR, (const void *) boot_cpu_data.x86_capability) )
+    && addr >= __pa(high_memory);
+#elif defined(__x86_64__) && !defined(__arch_um__)
+  /* 
+   * This is broken because it can generate memory type aliases,
+   * which can cause cache corruptions
+   * But it is only available for root and we have to be bug-to-bug
+   * compatible with i386.
+   */
+  if (file->f_flags & O_SYNC)
+    return 1;
+  /* same behaviour as i386. PAT always set to cached and MTRRs control the
+     caching behaviour. 
+     Hopefully a full PAT implementation will fix that soon. */      
+  return 0;
+#elif defined(CONFIG_IA64)
+  /*
+   * On ia64, we ignore O_SYNC because we cannot tolerate 
+   * memory attribute aliases.
+   */
+  return !(efi_mem_attributes(addr) & EFI_MEMORY_WB);
+#elif defined(CONFIG_MIPS)
+  {
+    extern int __uncached_access(struct file *file,
+				 unsigned long addr);
+    
+    return __uncached_access(file, addr);
+  }
+#else
+  /*
+   * Accessing memory above the top the kernel knows about 
+   * or through a file pointer
+   * that was marked O_SYNC will be done non-cached.
+   */
+  if (file->f_flags & O_SYNC)
+    return 1;
+  return addr >= __pa(high_memory);
+#endif
+}
+
+#ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE
+static inline int valid_phys_addr_range(unsigned long addr, size_t count)
+{
+  if (addr + count > __pa(high_memory))
+    return 0;
+  
+  return 1;
+}
+
+static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
+{
+  return 1;
+}
+#endif
+
+
+/* #ifndef __HAVE_PHYS_MEM_ACCESS_PROT */
+static pgprot_t my_phys_mem_access_prot(struct file *file, unsigned long pfn,
+                                     unsigned long size, pgprot_t vma_prot)
+{
+#ifdef pgprot_noncached
+  unsigned long offset = pfn << PAGE_SHIFT;
+  
+  if (uncached_access(file, offset))
+    return pgprot_noncached(vma_prot);
+#else
+#error pgtable
+#endif
+  return vma_prot;
+}
+/* #endif */
+
+
+#ifndef CONFIG_MMU
+static unsigned long get_unmapped_area_mem(struct file *file,
+                                           unsigned long addr,
+                                           unsigned long len,
+                                           unsigned long pgoff,
+                                           unsigned long flags)
+{
+  if (!valid_mmap_phys_addr_range(pgoff, len))
+    return (unsigned long) -EINVAL;
+  return pgoff << PAGE_SHIFT;
+}
+
+/* can't do an in-place private mapping if there's no MMU */
+static inline int private_mapping_ok(struct vm_area_struct *vma)
+{
+  return vma->vm_flags & VM_MAYSHARE;
+}
+#else
+#define get_unmapped_area_mem   NULL
+
+static inline int private_mapping_ok(struct vm_area_struct *vma)
+{
+  return 1;
+}
+#endif
+
+static int mmap_mem(struct file * file, struct vm_area_struct * vma)
+{
+  size_t size = vma->vm_end - vma->vm_start;
+  
+  if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
+    return -EINVAL;
+  
+  if (!private_mapping_ok(vma))
+    return -ENOSYS;
+  
+  vma->vm_page_prot = my_phys_mem_access_prot(file, vma->vm_pgoff,
+					      size,
+					      vma->vm_page_prot);
+  
+  /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
+  if (remap_pfn_range(vma,
+		      vma->vm_start,
+		      vma->vm_pgoff,
+		      size,
+		      vma->vm_page_prot))
+    return -EAGAIN;
+  return 0;
+}
+
+static int mmap_kmem(struct file * file, struct vm_area_struct * vma)
+{
+  unsigned long pfn;
+  
+  /* Turn a kernel-virtual address into a physical page frame */
+  pfn = __pa((u64)vma->vm_pgoff << PAGE_SHIFT) >> PAGE_SHIFT;
+  
+  /*
+   * RED-PEN: on some architectures there is more mapped memory
+   * than available in mem_map which pfn_valid checks
+   * for. Perhaps should add a new macro here.
+   *
+   * RED-PEN: vmalloc is not supported right now.
+   */
+  if (!pfn_valid(pfn))
+    return -EIO;
+  
+  vma->vm_pgoff = pfn;
+  return mmap_mem(file, vma);
+}
+
+static int my_permission(struct inode *inode, int op)
+{
+  /* 
+   * only root (uid 0) may read from it 
+   */
+  if (debug)
+    {
+      printk(KERN_INFO "samhain_kmem: permission op = %d, current->euid = %d\n", 
+	     op, (int)current->TASK_EUID );
+    }
+
+  if ((op & 4) != 0 && (op & 2) == 0 && current->TASK_EUID == 0)
+    {
+      if (debug)
+	{
+	  printk(KERN_INFO "samhain_kmem: access granted\n" );
+	}
+      return 0;
+    }
+  
+  /* 
+   * If it's anything else, access is denied 
+   */
+  if ((op & 2) != 0)
+    {
+      printk(KERN_INFO "/proc/kmem: access denied, "
+	     "permission op = %d, current->euid = %d\n", 
+	     op, (int)current->TASK_EUID );
+    }
+  else if (debug)
+    {
+      printk(KERN_INFO "samhain_kmem: access denied\n" );
+    }
+  return -EACCES;
+}
+
+static struct inode_operations Inode_Ops_Kmem = {
+  .permission = my_permission,	/* check for permissions */
+};
+
+static int open_kmem(struct inode * inode, struct file * filp)
+{
+  int ret = capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
+
+  if (debug)
+    {
+      printk(KERN_INFO "samhain_kmem: open_kmem retval = %d\n", ret);
+    }
+
+  if (ret == 0)
+    try_module_get(THIS_MODULE);
+
+  if (debug)
+    {
+      printk(KERN_INFO "samhain_kmem: open_kmem return\n");
+    }
+
+  return ret;
+}
+
+static int close_kmem(struct inode *inode, struct file *file)
+{
+  if (debug)
+    {
+      printk(KERN_INFO "samhain_kmem: close_kmem enter\n");
+    }
+
+  module_put(THIS_MODULE);
+
+  if (debug)
+    {
+      printk(KERN_INFO "samhain_kmem: close_kmem return\n");
+    }
+
+  return 0;		/* success */
+}
+
+/*********************************************************************
+ *
+ *   >>>  Required info from System.map: vmlist_lock, vmlist  <<<
+ */
+static rwlock_t * sh_vmlist_lock_ptr = (rwlock_t *) SH_VMLIST_LOCK;
+
+static struct vm_struct * sh_vmlist   = (struct vm_struct *) SH_VMLIST_LOCK;
+/*
+ *
+ *********************************************************************/
+
+static long my_vread(char *buf, char *addr, unsigned long count)
+{
+        struct vm_struct *tmp;
+        char *vaddr, *buf_start = buf;
+        unsigned long n;
+
+        /* Don't allow overflow */
+        if ((unsigned long) addr + count < count)
+                count = -(unsigned long) addr;
+
+        read_lock(sh_vmlist_lock_ptr);
+        for (tmp = sh_vmlist; tmp; tmp = tmp->next) {
+                vaddr = (char *) tmp->addr;
+                if (addr >= vaddr + tmp->size - PAGE_SIZE)
+                        continue;
+                while (addr < vaddr) {
+                        if (count == 0)
+                                goto finished;
+                        *buf = '\0';
+                        buf++;
+                        addr++;
+                        count--;
+                }
+                n = vaddr + tmp->size - PAGE_SIZE - addr;
+                do {
+                        if (count == 0)
+                                goto finished;
+                        *buf = *addr;
+                        buf++;
+                        addr++;
+                        count--;
+                } while (--n > 0);
+        }
+finished:
+        read_unlock(sh_vmlist_lock_ptr);
+	if (debug)
+	  {
+	    printk(KERN_INFO "samhain_kmem:  start %lu\n", (unsigned long) buf_start);
+	    printk(KERN_INFO "samhain_kmem:  end   %lu\n", (unsigned long) buf);
+	    printk(KERN_INFO "samhain_kmem:  size  %lu\n", (unsigned long) (buf - buf_start));
+	  }
+        return buf - buf_start;
+}
+
+static ssize_t read_kmem(struct file *file, char __user *buf, 
+                         size_t count, loff_t *ppos)
+{
+  unsigned long p = *ppos;
+  ssize_t low_count, read, sz;
+  char * kbuf; /* k-addr because vread() takes vmlist_lock rwlock */
+  
+  if (debug) {
+    printk(KERN_INFO "samhain_kmem: read_kmem entry\n");
+    printk(KERN_INFO "samhain_kmem:  p    %lu\n", (unsigned long) p);
+    printk(KERN_INFO "samhain_kmem:  high %lu\n", (unsigned long) high_memory);
+  }
+ 
+  read = 0;
+  if (p < (unsigned long) high_memory) {
+    low_count = count;
+
+    if (debug) {
+      printk(KERN_INFO "samhain_kmem:  low_count(1)  %ld\n", (long) low_count);
+    }
+
+    if (count > (unsigned long) high_memory - p)
+      low_count = (unsigned long) high_memory - p;
+    
+    if (debug) {
+      printk(KERN_INFO "samhain_kmem:  low_count(2)  %ld\n", (long) low_count);
+    }
+
+#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
+    /* we don't have page 0 mapped on sparc and m68k.. */
+    if (p < PAGE_SIZE && low_count > 0) {
+      size_t tmp = PAGE_SIZE - p;
+      if (tmp > low_count) tmp = low_count;
+      if (clear_user(buf, tmp))
+	{
+	  if (debug) {
+	    printk(KERN_INFO "samhain_kmem: Bad address, line %d\n", __LINE__);
+	  }
+	  return -EFAULT;
+	}
+      buf += tmp;
+      p += tmp;
+      read += tmp;
+      low_count -= tmp;
+      count -= tmp;
+    }
+#endif
+
+    if (debug) {
+      printk(KERN_INFO "samhain_kmem:  low_count(3)  %ld\n", (long) low_count);
+    }
+
+    while (low_count > 0) {
+      /*
+       * Handle first page in case it's not aligned
+       */
+      if (-p & (PAGE_SIZE - 1))
+	sz = -p & (PAGE_SIZE - 1);
+      else
+	sz = PAGE_SIZE;
+      
+      sz = min_t(unsigned long, sz, low_count);
+      
+      /*
+       * On ia64 if a page has been mapped somewhere as
+       * uncached, then it must also be accessed uncached
+       * by the kernel or data corruption may occur
+       */
+      kbuf = xlate_dev_kmem_ptr((char *)p);
+      
+      if (copy_to_user(buf, kbuf, sz))
+	{
+	  if (debug) {
+	    printk(KERN_INFO "samhain_kmem: Bad address, line %d\n", __LINE__);
+	    printk(KERN_INFO "samhain_kmem:  size %ld\n", (long) sz);
+	    printk(KERN_INFO "samhain_kmem:  kbuf %p\n", kbuf);
+	    printk(KERN_INFO "samhain_kmem:  buf  %p\n", buf);
+	    printk(KERN_INFO "samhain_kmem:  high %lu\n", (unsigned long) high_memory);
+	  }
+	  return -EFAULT;
+	}
+      buf += sz;
+      p += sz;
+      read += sz;
+      low_count -= sz;
+      count -= sz;
+      if (debug) {
+	printk(KERN_INFO "samhain_kmem:  low_count(4)  %ld\n", (long) low_count);
+      }
+    }
+  }
+
+  if (debug) {
+    printk(KERN_INFO "samhain_kmem: read_kmem mid\n");
+    printk(KERN_INFO "samhain_kmem:  count  %lu\n", (unsigned long) count);
+  }
+
+  if (count > 0) {
+    kbuf = (char *)__get_free_page(GFP_KERNEL);
+    if (!kbuf)
+      {
+	if (debug) {
+	  printk(KERN_INFO "samhain_kmem: out of memory\n");
+	}
+	return -ENOMEM;
+      }
+    while (count > 0) {
+      int len = count;
+      
+      if (len > PAGE_SIZE)
+	len = PAGE_SIZE;
+      len = my_vread(kbuf, (char *)p, len);
+      if (!len)
+	break;
+      if (copy_to_user(buf, kbuf, len)) {
+	if (debug) {
+	  printk(KERN_INFO "samhain_kmem: Bad address, line %d\n", __LINE__);
+	  printk(KERN_INFO "samhain_kmem:  size %ld\n", (long) len);
+	  printk(KERN_INFO "samhain_kmem:  kbuf %p\n", kbuf);
+	  printk(KERN_INFO "samhain_kmem:  buf  %p\n", buf);
+	  printk(KERN_INFO "samhain_kmem:  high %lu\n", (unsigned long) high_memory);
+	}
+	free_page((unsigned long)kbuf);
+	return -EFAULT;
+      }
+      count -= len;
+      buf += len;
+      read += len;
+      p += len;
+    }
+    free_page((unsigned long)kbuf);
+  }
+  *ppos = p;
+  if (debug) {
+    printk(KERN_INFO "samhain_kmem: read_kmem end\n");
+    printk(KERN_INFO "samhain_kmem:  read  %ld\n", (long) read);
+  }
+  return read;
+}
+
+
+static loff_t memory_lseek(struct file * file, loff_t offset, int orig)
+{
+  loff_t ret;
+  
+  mutex_lock(&file->f_path.dentry->d_inode->i_mutex);
+  switch (orig) {
+  case 0:
+    file->f_pos = offset;
+    ret = file->f_pos;
+    force_successful_syscall_return();
+    break;
+  case 1:
+    file->f_pos += offset;
+    ret = file->f_pos;
+    force_successful_syscall_return();
+    break;
+  default:
+    if (debug) {
+      printk(KERN_INFO "samhain_kmem: invalid input %d\n", orig);
+    }
+    ret = -EINVAL;
+  }
+  mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
+  return ret;
+}
+
+static const struct file_operations File_Ops_Kmem = {
+  .llseek            = memory_lseek,
+  .read              = read_kmem,
+  .mmap              = mmap_kmem,
+  .open              = open_kmem,
+  .release           = close_kmem,
+  .get_unmapped_area = get_unmapped_area_mem,
+};
+
+
+/* Init function called on module entry 
+ */
+static int my_module_init( void )
+{
+  int ret = 0;
+
+  proc_entry = create_proc_entry( "kmem", 0400, NULL ); 
+
+  if (proc_entry == NULL) {
+    
+    ret = -ENOMEM;
+    
+    printk(KERN_INFO "samhain_kmem: Couldn't create proc entry\n");
+    
+  } else {
+    
+/* 2.6.30 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
+    proc_entry->owner     = THIS_MODULE;
+#endif
+    proc_entry->proc_iops = &Inode_Ops_Kmem;
+    proc_entry->proc_fops = &File_Ops_Kmem;
+    
+    proc_entry->uid       = 0;
+    proc_entry->gid       = 0;
+    proc_entry->mode      = S_IFREG | S_IRUSR;
+    
+    if (debug) {
+      printk(KERN_INFO "samhain_kmem: module is now loaded.\n");
+    }
+  }
+
+  return ret;
+}
+
+/* Cleanup function called on module exit */
+
+static void my_module_cleanup( void )
+{
+  remove_proc_entry("kmem", NULL);
+
+  if (debug) {
+    printk(KERN_INFO "samhain_kmem: module is now unloaded.\n");
+  }
+  return;
+}
+
+
+
+/* Declare entry and exit functions */
+
+module_init( my_module_init );
+
+module_exit( my_module_cleanup );
Index: /trunk/src/sh_cat.c
===================================================================
--- /trunk/src/sh_cat.c	(revision 278)
+++ /trunk/src/sh_cat.c	(revision 279)
@@ -121,4 +121,8 @@
   { MSG_UT_LG3C,     SH_ERR_INFO,    EVENT, N_("msg=\"Logout\" tty=\"%s\" time=\"%s\" status=\"%d\"")},
   { MSG_UT_ROT,      SH_ERR_WARN,    RUN,   N_("msg=\"Logfile size decreased\" path=\"%s\"")},
+
+  { MSG_UT_BAD,      SH_ERR_SEVERE,  EVENT, N_("msg=\"Login at disallowed time\" userid=\"%s\" host=\"%s\" time=\"%s\"")},
+  { MSG_UT_FIRST,    SH_ERR_SEVERE,  EVENT, N_("msg=\"First login from this host\" userid=\"%s\" host=\"%s\" time=\"%s\"")},
+  { MSG_UT_OUTLIER,  SH_ERR_SEVERE,  EVENT, N_("msg=\"Login time outlier\" userid=\"%s\" host=\"%s\" time=\"%s\"")},
 
 #endif
@@ -454,4 +458,7 @@
   { MSG_UT_ROT,      SH_ERR_WARN,    RUN,   N_("msg=<Logfile size decreased>, path=<%s>")},
 
+  { MSG_UT_BAD,      SH_ERR_SEVERE,  EVENT, N_("msg=<Login at disallowed time> userid=<%s> host=<%s> time=<%s>")},
+  { MSG_UT_FIRST,    SH_ERR_SEVERE,  EVENT, N_("msg=<First login from this host> userid=<%s> host=<%s> time=<%s>")},
+  { MSG_UT_OUTLIER,  SH_ERR_SEVERE,  EVENT, N_("msg=<Login time outlier> userid=<%s> host=<%s> time=<%s>")},
 #endif
 
Index: /trunk/src/sh_err_log.c
===================================================================
--- /trunk/src/sh_err_log.c	(revision 278)
+++ /trunk/src/sh_err_log.c	(revision 279)
@@ -377,5 +377,5 @@
 	      key[0] = '\0';
 	      
-	      while (sl_strlen(key) < KEY_LEN ) 
+	      while (strlen(key) < KEY_LEN ) 
 		{ 
 		  if (key[0] != '\n' && key[0] != '\0')
Index: /trunk/src/sh_err_syslog.c
===================================================================
--- /trunk/src/sh_err_syslog.c	(revision 278)
+++ /trunk/src/sh_err_syslog.c	(revision 279)
@@ -131,5 +131,26 @@
 }
   
-  
+static int sh_stamp_priority = LOG_ERR;
+
+/* set priority for heartbeat messages
+ */
+int  sh_log_set_stamp_priority (const char * c)
+{
+  int retval = 0;
+
+  if      (0 == strcmp(c, _("LOG_DEBUG")))   { sh_stamp_priority = LOG_DEBUG; }
+  else if (0 == strcmp(c, _("LOG_INFO")))    { sh_stamp_priority = LOG_INFO;  }
+  else if (0 == strcmp(c, _("LOG_NOTICE")))  { sh_stamp_priority = LOG_NOTICE;}
+  else if (0 == strcmp(c, _("LOG_WARNING"))) { sh_stamp_priority = LOG_WARNING;}
+  else if (0 == strcmp(c, _("LOG_ERR")))     { sh_stamp_priority = LOG_ERR;   }
+  else if (0 == strcmp(c, _("LOG_CRIT")))    { sh_stamp_priority = LOG_CRIT;  }
+  else if (0 == strcmp(c, _("LOG_ALERT")))   { sh_stamp_priority = LOG_ALERT; }
+#ifdef LOG_EMERG
+  else if (0 == strcmp(c, _("LOG_EMERG")))   { sh_stamp_priority = LOG_EMERG; }
+#endif
+  else { retval = -1; }
+
+  return retval;
+}  
 
 /* syslog error message
@@ -154,5 +175,5 @@
   else if (severity == SH_ERR_NOTICE) priority = LOG_NOTICE;
   else if (severity == SH_ERR_WARN)   priority = LOG_WARNING;
-  else if (severity == SH_ERR_STAMP)  priority = LOG_ERR;
+  else if (severity == SH_ERR_STAMP)  priority = sh_stamp_priority;
   else if (severity == SH_ERR_ERR)    priority = LOG_ERR;
   else if (severity == SH_ERR_SEVERE) priority = LOG_CRIT;
Index: /trunk/src/sh_hash.c
===================================================================
--- /trunk/src/sh_hash.c	(revision 278)
+++ /trunk/src/sh_hash.c	(revision 279)
@@ -1805,5 +1805,5 @@
 		  sl_write (pushdata_fd, _(" Date "), 6);
 		  (void) sh_unix_time(0, timestring, sizeof(timestring));
-		  sl_write (pushdata_fd, timestring, sl_strlen(timestring));
+		  sl_write (pushdata_fd, timestring, strlen(timestring));
 		  sl_write (pushdata_fd,        "\n", 1);
 		} else {
Index: /trunk/src/sh_kern.c
===================================================================
--- /trunk/src/sh_kern.c	(revision 278)
+++ /trunk/src/sh_kern.c	(revision 279)
@@ -137,7 +137,7 @@
  */
 #ifdef SH_SYS_CALL_TABLE
-static unsigned int  kaddr = SH_SYS_CALL_TABLE;
+static unsigned long  kaddr = SH_SYS_CALL_TABLE;
 #else
-static unsigned int  kaddr = 0;
+static unsigned long  kaddr = 0;
 #endif
 
@@ -268,4 +268,8 @@
 #ifdef HOST_IS_LINUX
 
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
 /*
  * Interrupt Descriptor Table
@@ -281,4 +285,8 @@
 static char * sh_strseg(unsigned short segment)
 {
+  static int flip = 0;
+  static char one[32];
+  static char two[32];
+
   switch (segment) {
 #ifdef __KERNEL_CS
@@ -299,5 +307,16 @@
 #endif
   default:
-    return _("unknown");
+    if (flip == 0)
+      {
+	snprintf(one, sizeof(one), "%hX", segment);
+	flip = 1;
+	return one;
+      }
+    else
+      {
+	snprintf(two, sizeof(two), "%hX", segment);
+	flip = 0;
+	return two;
+      }
   }
 }
@@ -550,8 +569,4 @@
   unsigned int  kmem_code_table[SH_KERN_SIZ][2];
 
-  unsigned char  buf[6];
-  unsigned short idt_size;
-  unsigned long  idt_addr;
-
   unsigned char new_system_call_code[SH_KERN_SCC];
 
@@ -620,15 +635,20 @@
        * and read the content into the global array sh_idt_table[]
        */
-      __asm__ volatile ("sidt %0": "=m" (buf));
-      idt_size = *((unsigned short *) &buf[0]);
-      idt_addr = *((unsigned long *)  &buf[2]);
-      idt_size = (idt_size + 1)/8;
+      struct {
+	char pad[6];
+	unsigned short size;
+	unsigned long  addr;
+      } idt;
+
+      __asm__ volatile ("sidt %0": "=m" (idt.size));
+
+      idt.size = (idt.size + 1)/8;
       
-      if (idt_size > SH_MAXIDT)
-	idt_size = SH_MAXIDT;
+      if (idt.size > SH_MAXIDT)
+	idt.size = SH_MAXIDT;
       
       memset(sh_idt_table, '\0', SH_MAXIDT*8);
-      if (sh_kern_read_data (kd, idt_addr, 
-			     (unsigned char *) sh_idt_table, idt_size*8))
+      if (sh_kern_read_data (kd, idt.addr, 
+			     (unsigned char *) sh_idt_table, idt.size*8))
 	status = -5;
     }
@@ -660,5 +680,5 @@
     }
 /* 2.6.21 (((2) << 16) + ((6) << 8) + (21)) */
-#if SH_KERNEL_NUMBER < 132629
+#if SH_KERNEL_NUMBER < KERNEL_VERSION(2,6,21)
   if(status == 0)
     {
@@ -669,5 +689,5 @@
     }
 #else
-  memset(&proc_root_inode, '\0', sizeof(proc_root_inode));
+    memset(&proc_root_inode, '\0', sizeof(proc_root_inode));
 #endif
   
@@ -1062,8 +1082,9 @@
 static void check_proc_root (struct sh_kernel_info * kinfo)
 {
-  struct proc_dir_entry   proc_root_dir;
+  struct proc_dir_entry     proc_root_dir;
+  struct inode_operations * proc_root_inode_op = NULL;
 
 /* 2.6.21 (((2) << 16) + ((6) << 8) + (21)) */
-#if SH_KERNEL_NUMBER < 132629
+#if SH_KERNEL_NUMBER < KERNEL_VERSION(2,6,21)
   struct inode_operations proc_root_inode;
 
@@ -1080,8 +1101,19 @@
 
   memcpy (&proc_root_dir,   &(kinfo->proc_root_dir),   sizeof(struct proc_dir_entry));
-  if (    (((unsigned int) * &proc_root_dir.proc_iops) != proc_root_iops)
-	    && (proc_root_dir.size != proc_root_iops)
-	    && (((unsigned int) * &proc_root_dir.proc_fops) != proc_root_iops)
-	    )
+
+  if (((unsigned long) * &proc_root_dir.proc_iops) == proc_root_iops)
+    {
+      proc_root_inode_op = (struct inode_operations *) &(proc_root_dir.proc_iops);
+    }
+  else if (proc_root_dir.size == proc_root_iops)
+    {
+      proc_root_inode_op = (struct inode_operations *) &(proc_root_dir.size);
+    }
+  else if ((unsigned long) * &proc_root_dir.proc_fops == proc_root_iops)
+    {
+      proc_root_inode_op = (struct inode_operations *) &(proc_root_dir.proc_fops);
+    }
+
+  if (!proc_root_inode_op)
     {
       sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_KERN_PROC,
@@ -1365,4 +1397,9 @@
   if (kd < 0)
     {
+      kd = aud_open(FIL__, __LINE__, SL_YESPRIV, _("/proc/kmem"), O_RDONLY, 0);
+    }
+
+  if (kd < 0)
+    {
       status = errno;
       sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
Index: /trunk/src/sh_login_track.c
===================================================================
--- /trunk/src/sh_login_track.c	(revision 279)
+++ /trunk/src/sh_login_track.c	(revision 279)
@@ -0,0 +1,1411 @@
+/* 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"
+
+#undef  FIL__
+#define FIL__  _("sh_login_track.c")
+
+#if defined(SH_USE_UTMP) && (defined(SH_WITH_CLIENT) || defined (SH_STANDALONE)) 
+
+#include <string.h>
+
+#include "samhain.h"
+#include "sh_pthread.h"
+#include "sh_utils.h"
+#include "sh_unix.h"
+#include "sh_string.h"
+#include "sh_tools.h"
+#include "sh_error_min.h"
+
+#ifdef HAVE_UTMPX_H
+
+#include <utmpx.h>
+#define SH_UTMP_S utmpx
+#undef  ut_name
+#define ut_name ut_user
+#ifdef HAVE_UTXTIME
+#undef  ut_time
+#define ut_time        ut_xtime
+#else
+#undef  ut_time
+#define ut_time        ut_tv.tv_sec
+#endif
+
+#else
+
+#include <utmp.h>
+#define SH_UTMP_S utmp
+
+#endif
+
+
+#define SH_LTRACK_VERSION 1
+
+#define SH_LTRACK_USIZE  32
+#define SH_LTRACK_HSIZE 256
+/* One hour    (15 deg)  */
+#define SH_LTRACK_HTRES  24
+/* Ten minutes (2.5 deg) */
+#define SH_LTRACK_GTRES 144
+
+/* Avoid compiling against lmath by including result tables for sin, cos
+ */
+const double sintab_htres[SH_LTRACK_HTRES] = {
+ 0.13052619222005157340,  0.38268343236508978178,  0.60876142900872065589,  0.79335334029123516508,  0.92387953251128673848,  0.99144486137381038215, 
+ 0.99144486137381038215,  0.92387953251128673848,  0.79335334029123516508,  0.60876142900872087793,  0.38268343236508989280,  0.13052619222005157340, 
+-0.13052619222005132360, -0.38268343236508967076, -0.60876142900872065589, -0.79335334029123494304, -0.92387953251128651644, -0.99144486137381038215, 
+-0.99144486137381049318, -0.92387953251128662746, -0.79335334029123516508, -0.60876142900872087793, -0.38268343236509039240, -0.13052619222005168442, 
+};
+const double costab_htres[SH_LTRACK_HTRES] = {
+ 0.99144486137381038215,  0.92387953251128673848,  0.79335334029123516508,  0.60876142900872065589,  0.38268343236508983729,  0.13052619222005171218, 
+-0.13052619222005160116, -0.38268343236508972627, -0.60876142900872065589, -0.79335334029123505406, -0.92387953251128673848, -0.99144486137381038215, 
+-0.99144486137381049318, -0.92387953251128684951, -0.79335334029123516508, -0.60876142900872087793, -0.38268343236509033689, -0.13052619222005162891, 
+ 0.13052619222005126809,  0.38268343236509000382,  0.60876142900872054486,  0.79335334029123494304,  0.92387953251128651644,  0.99144486137381038215, 
+};
+const double sintab_gtres[SH_LTRACK_GTRES] = {
+ 0.02181488503456112046,  0.06540312923014306168,  0.10886687485196457070,  0.15212338618991669281,  0.19509032201612824808,  0.23768589232617309825, 
+ 0.27982901403099208482,  0.32143946530316158672,  0.36243803828370163567,  0.40274668985873718352,  0.44228869021900124592,  0.48098876891938763256, 
+ 0.51877325816052144436,  0.55557023301960217765,  0.59130964836358235193,  0.62592347218405908205,  0.65934581510006884386,  0.69151305578226940352, 
+ 0.72236396205975550444,  0.75183980747897738439,  0.77988448309288171956,  0.80644460426748254545,  0.83146961230254523567,  0.85491187067294649449, 
+ 0.87672675570750768781,  0.89687274153268836674,  0.91531147911944710227,  0.93200786928279844012,  0.94693012949510557696,  0.96004985438592871372, 
+ 0.97134206981326143282,  0.98078528040323043058,  0.98836151046776066220,  0.99405633822231964647,  0.99785892323860347908,  0.99976202707990913243, 
+ 0.99976202707990913243,  0.99785892323860347908,  0.99405633822231964647,  0.98836151046776066220,  0.98078528040323043058,  0.97134206981326143282, 
+ 0.96004985438592871372,  0.94693012949510568799,  0.93200786928279855115,  0.91531147911944721329,  0.89687274153268836674,  0.87672675570750779883, 
+ 0.85491187067294671653,  0.83146961230254545772,  0.80644460426748254545,  0.77988448309288183058,  0.75183980747897738439,  0.72236396205975561546, 
+ 0.69151305578226951454,  0.65934581510006895488,  0.62592347218405919307,  0.59130964836358257397,  0.55557023301960217765,  0.51877325816052133334, 
+ 0.48098876891938763256,  0.44228869021900130143,  0.40274668985873729454,  0.36243803828370174669,  0.32143946530316175325,  0.27982901403099230686, 
+ 0.23768589232617337581,  0.19509032201612860891,  0.15212338618991663730,  0.10886687485196457070,  0.06540312923014311719,  0.02181488503456121761, 
+-0.02181488503456097475, -0.06540312923014286739, -0.10886687485196432090, -0.15212338618991641526, -0.19509032201612835911, -0.23768589232617312601, 
+-0.27982901403099202930, -0.32143946530316153121, -0.36243803828370152464, -0.40274668985873707250, -0.44228869021900107938, -0.48098876891938741052, 
+-0.51877325816052122232, -0.55557023301960195560, -0.59130964836358235193, -0.62592347218405908205, -0.65934581510006884386, -0.69151305578226929249, 
+-0.72236396205975550444, -0.75183980747897727337, -0.77988448309288194160, -0.80644460426748265647, -0.83146961230254523567, -0.85491187067294660551, 
+-0.87672675570750768781, -0.89687274153268825572, -0.91531147911944710227, -0.93200786928279844012, -0.94693012949510557696, -0.96004985438592860270, 
+-0.97134206981326132180, -0.98078528040323031956, -0.98836151046776066220, -0.99405633822231953545, -0.99785892323860347908, -0.99976202707990913243, 
+-0.99976202707990913243, -0.99785892323860347908, -0.99405633822231964647, -0.98836151046776066220, -0.98078528040323043058, -0.97134206981326143282, 
+-0.96004985438592871372, -0.94693012949510568799, -0.93200786928279855115, -0.91531147911944721329, -0.89687274153268847776, -0.87672675570750790985, 
+-0.85491187067294682755, -0.83146961230254545772, -0.80644460426748287851, -0.77988448309288216365, -0.75183980747897782848, -0.72236396205975605955, 
+-0.69151305578226918147, -0.65934581510006873284, -0.62592347218405897102, -0.59130964836358235193, -0.55557023301960217765, -0.51877325816052144436, 
+-0.48098876891938774358, -0.44228869021900141245, -0.40274668985873740557, -0.36243803828370185771, -0.32143946530316186427, -0.27982901403099241788, 
+-0.23768589232617348683, -0.19509032201612871993, -0.15212338618991719241, -0.10886687485196513969, -0.06540312923014367230, -0.02181488503456178660
+};
+const double costab_gtres[SH_LTRACK_GTRES] = {
+ 0.99976202707990913243,  0.99785892323860347908,  0.99405633822231964647,  0.98836151046776066220,  0.98078528040323043058,  0.97134206981326143282, 
+ 0.96004985438592871372,  0.94693012949510568799,  0.93200786928279855115,  0.91531147911944721329,  0.89687274153268836674,  0.87672675570750768781, 
+ 0.85491187067294660551,  0.83146961230254523567,  0.80644460426748265647,  0.77988448309288183058,  0.75183980747897738439,  0.72236396205975561546, 
+ 0.69151305578226940352,  0.65934581510006884386,  0.62592347218405908205,  0.59130964836358235193,  0.55557023301960228867,  0.51877325816052155538, 
+ 0.48098876891938774358,  0.44228869021900124592,  0.40274668985873723903,  0.36243803828370169118,  0.32143946530316169774,  0.27982901403099202930, 
+ 0.23768589232617309825,  0.19509032201612833135,  0.15212338618991680383,  0.10886687485196473724,  0.06540312923014304780,  0.02181488503456115863, 
+-0.02181488503456103373, -0.06540312923014292290, -0.10886687485196461234, -0.15212338618991669281, -0.19509032201612819257, -0.23768589232617298723, 
+-0.27982901403099191828, -0.32143946530316158672, -0.36243803828370158016, -0.40274668985873712801, -0.44228869021900113490, -0.48098876891938746603, 
+-0.51877325816052122232, -0.55557023301960195560, -0.59130964836358246295, -0.62592347218405908205, -0.65934581510006884386, -0.69151305578226929249, 
+-0.72236396205975550444, -0.75183980747897727337, -0.77988448309288160853, -0.80644460426748243442, -0.83146961230254534669, -0.85491187067294660551, 
+-0.87672675570750768781, -0.89687274153268825572, -0.91531147911944710227, -0.93200786928279844012, -0.94693012949510557696, -0.96004985438592860270, 
+-0.97134206981326132180, -0.98078528040323043058, -0.98836151046776066220, -0.99405633822231964647, -0.99785892323860347908, -0.99976202707990913243, 
+-0.99976202707990913243, -0.99785892323860347908, -0.99405633822231964647, -0.98836151046776077322, -0.98078528040323043058, -0.97134206981326143282, 
+-0.96004985438592871372, -0.94693012949510568799, -0.93200786928279855115, -0.91531147911944721329, -0.89687274153268836674, -0.87672675570750779883, 
+-0.85491187067294671653, -0.83146961230254545772, -0.80644460426748254545, -0.77988448309288183058, -0.75183980747897749541, -0.72236396205975561546, 
+-0.69151305578226951454, -0.65934581510006906591, -0.62592347218405897102, -0.59130964836358224090, -0.55557023301960217765, -0.51877325816052144436, 
+-0.48098876891938768807, -0.44228869021900135694, -0.40274668985873735005, -0.36243803828370180220, -0.32143946530316180876, -0.27982901403099236237, 
+-0.23768589232617343132, -0.19509032201612866442, -0.15212338618991713690, -0.10886687485196507030, -0.06540312923014361679, -0.02181488503456172415, 
+ 0.02181488503456135639,  0.06540312923014325597,  0.10886687485196470948,  0.15212338618991677608,  0.19509032201612830359,  0.23768589232617307050, 
+ 0.27982901403099197379,  0.32143946530316147570,  0.36243803828370146913,  0.40274668985873701699,  0.44228869021900102387,  0.48098876891938735501, 
+ 0.51877325816052111129,  0.55557023301960184458,  0.59130964836358201886,  0.62592347218405863796,  0.65934581510006839977,  0.69151305578226895943, 
+ 0.72236396205975572649,  0.75183980747897749541,  0.77988448309288183058,  0.80644460426748254545,  0.83146961230254523567,  0.85491187067294660551, 
+ 0.87672675570750768781,  0.89687274153268825572,  0.91531147911944710227,  0.93200786928279844012,  0.94693012949510557696,  0.96004985438592860270, 
+ 0.97134206981326132180,  0.98078528040323031956,  0.98836151046776066220,  0.99405633822231953545,  0.99785892323860347908,  0.99976202707990913243
+};
+
+struct sh_track_entry_data {
+  UINT64      last_login;
+  UINT32      array[SH_LTRACK_HTRES]; /* 1 h resolution */
+  char        hostname[SH_LTRACK_HSIZE];
+};
+
+struct sh_track_entry {
+  struct sh_track_entry_data data;
+  struct sh_track_entry * next;
+};
+
+struct sh_track_head {
+  UINT32 version;
+  UINT32 n_entries;
+  UINT64 last_login;
+  char   hostname[SH_LTRACK_HSIZE];
+  UINT32 array[SH_LTRACK_GTRES]; /* 10 min resolution */
+};
+
+struct sh_track {
+  struct sh_track_head head;
+  struct sh_track_entry * list;
+};
+
+
+/* Returns zero/nonzero
+ */
+static int get_bool(char *bitarray, unsigned int index)
+{
+  int bool;
+
+  bitarray += index / 8; /* skip to char */
+  bool = (*bitarray & (1 << (index % 8)));
+
+  return bool;
+}
+
+static void set_bool(char *bitarray, unsigned int index, int bool)
+{
+  bitarray += index / 8; /* skip to char */
+  if (bool)
+    *bitarray |= 1 << (index % 8);
+  else    
+    *bitarray &= ~(1 << (index % 8));
+  return;
+}
+
+
+static char * build_path (const char * user)
+{
+  char * ui;
+
+  if (0 != sh_util_base64_enc_alloc (&ui, user, sl_strlen(user)))
+    {
+      char * path = sh_util_strconcat(DEFAULT_DATAROOT, "/", ui, NULL);
+
+      SH_FREE(ui);
+      return path;
+    }
+  return NULL;
+}
+
+static void destroy_loaded(struct sh_track * urecord)
+{
+  if (urecord)
+    {
+      struct sh_track_entry * entry = urecord->list;
+      struct sh_track_entry * entry_old;
+
+      while(entry)
+	{
+	  entry_old = entry;
+	  entry = entry->next;
+	  SH_FREE(entry_old);
+	}
+      SH_FREE(urecord);
+    }
+  return;
+}
+
+static struct sh_track * load_data_int (char * path)
+{
+  struct sh_track_head * uhead;
+  struct sh_track * urecord;
+
+  urecord = SH_ALLOC(sizeof(struct sh_track));
+  memset(urecord, '\0', sizeof(struct sh_track));
+
+  uhead = &(urecord->head);
+  uhead->version = SH_LTRACK_VERSION;
+
+  if (path)
+    {
+      FILE * fp = fopen(path, "rb");
+      
+      if (fp)
+	{
+	  size_t n;
+	  
+	  n = fread(uhead, sizeof(struct sh_track_head), 1, fp);
+	  
+	  if (n == 1)
+	    {
+	      struct sh_track_entry_data entry_data;
+	      struct sh_track_entry * entry;
+	      
+	      while (1 == fread(&entry_data, sizeof(entry_data), 1, fp))
+		{
+		  entry = SH_ALLOC(sizeof(struct sh_track_entry));
+		  memcpy(&(entry->data), &entry_data, sizeof(entry_data));
+		  entry->next   = urecord->list;
+		  urecord->list = entry;
+		}
+	    }
+	  fclose(fp);
+	}
+    }
+
+  return urecord;
+}
+
+static struct sh_track * load_data (const char * user)
+{
+  char * path = build_path (user);
+  struct sh_track * res = load_data_int (path);
+
+  if (path)
+    SH_FREE(path);
+  return res;
+}
+
+static void save_data_int (struct sh_track * urecord, char * path)
+{
+  mode_t mask;
+  FILE * fp;
+  
+  mask = umask(S_IWGRP | S_IWOTH);
+  fp = fopen(path, "wb");
+  (void) umask(mask);
+  
+  if (fp)
+    {
+      size_t n;
+      
+      n = fwrite(&(urecord->head), sizeof(struct sh_track_head), 1, fp);
+      
+      if (n == 1)
+	{
+	  struct sh_track_entry * entry = urecord->list;
+	  
+	  while (entry)
+	    {
+	      fwrite(&(entry->data), sizeof(struct sh_track_entry_data), 1, fp);
+	      entry = entry->next;
+	    }
+	}
+      fclose(fp);
+    }
+  return;
+}
+
+static void save_data (struct sh_track * urecord, const char * user)
+{
+  char * path = build_path (user);
+
+  if (path)
+    {
+      save_data_int (urecord, path);
+      SH_FREE(path);
+    }
+  return;
+}
+
+/**************
+ *
+ * Configurable
+ *
+ **************/
+
+enum significance { SIG00, SIG01, SIG05 };
+enum checklevel   { CHECK_NONE, CHECK_HOST, CHECK_DOMAIN };
+enum days         { WORKDAYS = 0, SATURDAY, SUNDAY };
+#define LTRACK_NDAYS 3
+
+static int sig_level    = SIG00;
+static int check_level  = CHECK_NONE;
+static int check_date   = S_FALSE;
+
+/* We use a bit array of SH_LTRACK_GTRES bits for allowed times 
+ * (10 min resolution)
+ */
+#define BITARRSIZ(a) ((a + 7) / 8)
+
+static int global_init  = S_FALSE;
+static char global_dates[LTRACK_NDAYS][BITARRSIZ(SH_LTRACK_GTRES)];
+
+struct sh_track_dates {
+  char user[SH_LTRACK_USIZE];
+  char dates[LTRACK_NDAYS][BITARRSIZ(SH_LTRACK_GTRES)];
+  struct sh_track_dates * next;
+};
+struct sh_track_dates * user_dates = NULL;
+
+static int set_dates (char bitarray[][BITARRSIZ(SH_LTRACK_GTRES)], 
+		      unsigned int size, const char * defstr);
+
+void sh_login_reset (void)
+{
+  int i, j;
+  struct sh_track_dates *u_old, *u;
+
+  u          = user_dates;
+  user_dates = NULL;
+
+  while(u)
+    {
+      u_old = u;
+      u     = u->next;
+      SH_FREE(u_old);
+    }
+
+  for (j = 0; j < LTRACK_NDAYS; ++j)
+    {
+      for (i = 0; i < SH_LTRACK_GTRES; ++i)
+	{ 
+	  set_bool(global_dates[j], i, 0);
+	}
+    }
+  global_init = S_FALSE;
+
+  sig_level    = SIG00;
+  check_level  = CHECK_NONE;
+  check_date   = S_FALSE;
+
+  return;
+}
+
+int sh_login_set_def_allow(const char * c)
+{
+  int res = set_dates(global_dates, SH_LTRACK_GTRES, c);
+
+  if (res == 0)
+    {
+      check_date   = S_TRUE;
+      global_init  = S_TRUE;
+    }
+  return res;
+}
+
+static struct sh_track_dates * find_user(const char * user)
+{
+  struct sh_track_dates * u = user_dates;
+
+  while(u)
+    {
+      if (0 == strcmp(user, u->user))
+	{
+	  return u;
+	}
+      u = u->next;
+    }
+  return NULL;
+}
+
+int sh_login_set_user_allow(const char * c)
+{
+  unsigned int i = 0;
+  const char *p = c;
+  char user[SH_LTRACK_USIZE];
+  
+  struct sh_track_dates * u;
+
+  while (p && *p && *p != ':' && *p != ' ' && *p != '\t')
+    {
+      user[i] = *p; ++p; ++i;
+
+      if (i == SH_LTRACK_USIZE)
+	return -1;
+    }
+
+  while (*p && (*p == ' ' || *p == '\t')) ++p;
+
+  if (*p && (i < SH_LTRACK_USIZE) && (*p == ':'))
+    {
+      user[i] = '\0';
+
+      ++p; while (*p && (*p == ' ' || *p == '\t')) ++p;
+
+      if (*p)
+	{
+	  int res;
+	  int flag = 0;
+
+	  u = find_user(user);
+
+	  if (!u)
+	    {
+	      u = SH_ALLOC(sizeof(struct sh_track_dates));
+	      memset(u, '\0', sizeof(struct sh_track_dates));
+	      sl_strlcpy(u->user, user, SH_LTRACK_USIZE);
+	      flag = 1;
+	    }
+
+	  res = set_dates(u->dates, SH_LTRACK_GTRES, p);
+	  if (res != 0)
+	    {
+	      if (flag == 1)
+		SH_FREE(u);
+	      return -1;
+	    }
+
+	  if (flag == 1)
+	    {
+	      u->next    = user_dates;
+	      user_dates = u;
+	    }
+
+	  check_date = S_TRUE;
+	  return 0;
+	}
+    }
+  return -1;
+}
+
+int sh_login_set_siglevel(const char * c)
+{
+  int ret = sh_util_flagval(c, &sig_level);
+
+  if (ret == 0)
+    {
+      sig_level = (sig_level == S_FALSE) ? SIG00 : SIG01;
+      return 0;
+    }
+  else
+    {
+      if (0 == strcmp(c, _("paranoid")))
+	{
+	  sig_level = SIG05;
+	  return 0;
+	}
+    }
+  sig_level = SIG00;
+  return -1;
+}
+
+int sh_login_set_checklevel(const char * c)
+{
+  int ret = sh_util_flagval(c, &check_level);
+
+  if (ret == 0)
+    {
+      check_level = (check_level == S_FALSE) ? CHECK_NONE : CHECK_HOST;
+      return 0;
+    }
+  else
+    {
+      if (0 == strcmp(c, _("domain")))
+	{
+	  check_level = CHECK_DOMAIN;
+	  return 0;
+	}
+    }
+  check_level = CHECK_NONE;
+  return -1;
+}
+
+static int eval_range(char * bitarray, unsigned int size, char * def)
+{
+  unsigned int h1, m1, h2, m2;
+
+  int res = sscanf(def, "%d:%d - %d:%d", &h1, &m1, &h2, &m2);
+
+  if (res == 4)
+    {
+      unsigned int t1 = 3600*h1 + 60*m1;
+      unsigned int t2 = 3600*h2 + 60*m2;
+      int hres        = (60*60*24)/size;
+      unsigned int i;
+
+      if (t1 > t2 || t1 > 86340 || t2 > 86340)
+	return -1;
+
+      t1  = t1 / hres;
+      t2  = t2 / hres;
+      t1  = (t1 < size) ? t1 : (size-1);
+      t2  = (t2 < size) ? t2 : (size-1);
+
+      for (i = t1; i <= t2; ++i)
+	{
+	  set_bool(bitarray, i, 1);
+	}
+      return 0;
+    }
+  return -1;
+}
+
+static int set_ranges(char * bitarray, unsigned int size, 
+		      char ** splits, unsigned int nfields)
+{
+  unsigned int i;
+  int retval = 0;
+
+  for (i = 0; i < nfields; ++i)
+    {
+      char * range = &(splits[i][0]);
+
+      if (0 != eval_range(bitarray, size, range))
+	retval = -1;
+    }
+  return retval;
+}
+
+/* 'always', 'never', workdays(list of ranges), (sun|satur)day(list of ranges)
+ */
+static int set_dates (char bitarray[][BITARRSIZ(SH_LTRACK_GTRES)], 
+		      unsigned int size, 
+		      const char * defstr)
+{
+  unsigned int i, j;
+  int retval = -1;
+
+  if (0 == strcmp(_("always"), defstr))
+    {
+      for (j = 0; j < LTRACK_NDAYS; ++j)
+	for (i = 0; i < size; ++i)
+	  set_bool(bitarray[j], i, 1);
+      retval = 0;
+    }
+  else if (0 == strcmp(_("never"), defstr))
+    {
+      for (j = 0; j < LTRACK_NDAYS; ++j)
+	for (i = 0; i < size; ++i)
+	  set_bool(bitarray[j], i, 0);
+      retval = 0;
+    }
+  else
+    {
+      unsigned int nfields = 24; /* list of ranges */
+      size_t       lengths[24];
+      char *       new    = NULL;
+      char **      splits = NULL;
+
+      if      (0 == strncmp(_("workdays"), defstr, 7))
+	{
+	  new    = sh_util_strdup(defstr);
+	  splits = split_array_braced(new, _("workdays"), 
+				      &nfields, lengths);
+	  j = WORKDAYS;
+	}
+      else if (0 == strncmp(_("saturday"), defstr, 8))
+	{
+	  new    = sh_util_strdup(defstr);
+	  splits = split_array_braced(new, _("saturday"), 
+				      &nfields, lengths);
+	  j = SATURDAY;
+	}
+      else if (0 == strncmp(_("sunday"), defstr, 6))
+	{
+	  new    = sh_util_strdup(defstr);
+	  splits = split_array_braced(new, _("sunday"), 
+				      &nfields, lengths);
+	  j = SUNDAY;
+	}
+      else
+	{
+	  return -1;
+	}
+
+      if (new && splits && nfields > 0)
+	{
+	  retval = set_ranges(bitarray[j], size, splits, nfields);
+	}
+
+      if (new) SH_FREE(new);
+    }
+  return retval;
+}
+
+
+
+/**************
+ *
+ * Report
+ *
+ **************/
+
+void report_generic(char * file, int line, 
+		    const char * user, time_t time, const char * host, int what)
+{
+  char   ttt[TIM_MAX];
+
+  SH_MUTEX_LOCK(mutex_thread_nolog);
+  (void) sh_unix_time (time, ttt, TIM_MAX);
+  sh_error_handle ((-1), file, line, 0, what,
+		   user, host, ttt);
+  SH_MUTEX_UNLOCK(mutex_thread_nolog);
+  return;
+}
+
+void report_bad_date(char * file, int line, 
+		     const char *user, time_t time, const char * host)
+{
+  report_generic(file, line, user, time, host, MSG_UT_BAD);
+}
+
+void report_first(char * file, int line, 
+		  const char *user, time_t time, const char * host)
+{
+  report_generic(file, line, user, time, host, MSG_UT_FIRST);
+}
+
+void report_outlier(char * file, int line, 
+		    const char *user, time_t time, const char * host)
+{
+  report_generic(file, line, user, time, host, MSG_UT_OUTLIER);
+}
+
+/**************
+ *
+ * Dates
+ *
+ **************/
+
+static int check_login_date(const char * user, unsigned int index, int wday)
+{
+  unsigned int i, j;
+  struct sh_track_dates * allowed = NULL;
+  int day;
+
+  /* Use an intermediate array 'char* b[m]' to cast 'char a[m][n]' to 'char** c' */
+  char * aux[LTRACK_NDAYS];
+  char **good = (char **) aux;
+
+  for (i = 0; i < LTRACK_NDAYS; ++i)
+    {
+      aux[i] = (char *) &global_dates[i][0];
+      /* + i * BITARRSIZ(SH_LTRACK_GTRES); */
+    }
+
+  if (wday > 0 && wday < 6)
+    day = WORKDAYS;
+  else if (wday == 6)
+    day = SATURDAY;
+  else
+    day = SUNDAY;
+
+  if (check_date != S_FALSE)
+    {
+      if (S_FALSE == global_init)
+	{
+	  for (j = 0; j < LTRACK_NDAYS; ++j)
+	    {
+	      for (i = 0; i < SH_LTRACK_GTRES; ++i) 
+		set_bool(global_dates[j], i, 1);
+	    }
+	  global_init = S_TRUE;
+	}
+
+      if (user) {
+	allowed = find_user(user);
+      }
+
+      if (allowed)
+	{
+	  for (i = 0; i < LTRACK_NDAYS; ++i)
+	    {
+	      aux[i] = (char *)&(allowed->dates)[i][0]; 
+	      /* + i*BITARRSIZ(SH_LTRACK_GTRES); */
+	    }
+	}
+      
+      if (0 == get_bool(good[day], index))
+	{
+	  return -1;
+	}
+    }
+  return 0;
+} 
+
+/**************
+ *
+ * Statistics
+ *
+ **************/
+
+/* Compute sqrt(s) using the babylonian algorithm
+ * (to avoid linking with -lm).
+ */ 
+static double sh_sqrt(double s)
+{
+  double eps = 1.0e-6;
+  double x0  = 1.0;
+  double xs  = s;
+
+  double diff = xs - x0;
+  diff = (diff > 0.0) ? diff : -diff;
+
+  while (diff > eps)
+    {
+      xs = x0;
+      x0 = 0.5 * (x0 + (s/x0));
+      diff = xs - x0;
+      diff = (diff > 0.0) ? diff : -diff;
+    }
+  return x0;
+}
+
+static double M_crit(int n, int flag)
+{
+#define SH_MCSIZE 10
+  const double M_05[SH_MCSIZE] = { 0.975, 0.918, 0.855, 0.794, 0.739, 0.690, 0.647, 0.577, 0.497, 0.406 };
+  const double M_01[SH_MCSIZE] = { 0.995, 0.970, 0.934, 0.891, 0.845, 0.799, 0.760, 0.688, 0.603, 0.498 };
+  const int    M_nn[SH_MCSIZE] = {     4,     5,     6,     7,     8,     9,    10,    12,    15,    20 };
+
+  if (n > M_nn[SH_MCSIZE-1])
+    {
+      return ((flag == SIG05) ? M_05[SH_MCSIZE-1] : M_01[SH_MCSIZE-1]);
+    }
+  else
+    {
+      unsigned int i;
+
+      for (i = 1; i < SH_MCSIZE; ++i)
+	{
+	  if (n < M_nn[i])
+	    {
+	      return ((flag == SIG05) ? M_05[i-1] : M_01[i-1]);
+	    }
+	}
+    }
+
+  return ((flag == SIG05) ? M_05[SH_MCSIZE-1] : M_01[SH_MCSIZE-1]);
+}
+
+static int check_statistics (unsigned int index, UINT32 * array, unsigned int size,
+			     const double * costab, const double * sintab)
+{
+  double C = 0.0;
+  double S = 0.0;
+  double R, Rk, M;
+
+  unsigned int i, n = 0;
+
+  if (sig_level != SIG00)
+    {
+      for (i = 0; i < size; ++i)
+	{
+	  n += array[i];
+	  C += (array[i] * costab[i]);
+	  S += (array[i] * sintab[i]);
+	}
+      
+      if (n > 2) /* current is at least 4th datapoint */
+	{
+	  R = sh_sqrt(S*S + C*C);
+	  
+	  C += array[index] * costab[index];
+	  S += array[index] * sintab[index];
+	  Rk = sh_sqrt(S*S + C*C);
+	  ++n;
+	  
+	  M  = (Rk - R + 1.0)/((double)n - R);
+	  
+	  if (M > M_crit(n, sig_level))
+	    {
+	      return -1;
+	    }
+	}
+    }
+  return 0;
+}
+
+static char * stripped_hostname (const char * host)
+{
+  char *p, *q;
+  
+  if (is_numeric(host))
+    {
+      p = sh_util_strdup(host);
+      q = strrchr(p, '.');
+      if (q) 
+	{
+	  *q = '\0';
+	  q = strrchr(p, '.');
+	  if (q)
+	    {
+	      *q = '\0';
+	    }
+	}
+    }
+  else
+    {
+      q = strchr(host, '.'); 
+      if (q && *q)
+	{
+	  ++q;
+	  p = sh_util_strdup(q);
+	}
+      else
+	{
+	  p = sh_util_strdup(host);
+	}
+    }
+  return p;
+}
+
+static unsigned int time_to_index(struct tm * tp, int nbin)
+{
+  int hres  = (60*60*24)/nbin;
+  int index = tp->tm_hour * 3600 + tp->tm_min * 60 + tp->tm_sec;
+  index  = index / hres;
+  index  = (index < nbin) ? index : (nbin-1);
+
+  return index;
+}
+
+static struct sh_track_entry * check_host(struct sh_track_entry * list, 
+					  const char * user, time_t time, const char * host,
+					  struct tm * tp)
+{
+  unsigned int    index = time_to_index(tp, SH_LTRACK_HTRES);
+  struct sh_track_entry * entry = list;
+
+  char * p = NULL;
+  const char * q;
+
+  if (check_level == CHECK_DOMAIN)
+    {
+      p = stripped_hostname(host);
+      q = p;
+    }
+  else
+    {
+      q = host;
+    }
+
+  while (entry)
+    {
+      if (0 == strncmp(q, (entry->data).hostname, SH_LTRACK_HSIZE))
+	break;
+      entry = entry->next;
+    }
+
+  if (entry)
+    {
+      int isAlert;
+
+      (entry->data).last_login    = time;
+
+      /* Check host statistics here 
+       */
+      isAlert = check_statistics (index, (entry->data).array, SH_LTRACK_HTRES, 
+				  costab_htres, sintab_htres); 
+
+      if (isAlert != 0) 
+	{
+	  report_outlier(FIL__, __LINE__, user, time, host);
+	}
+
+      /* Update array afterwards
+       */
+      (entry->data).array[index] += 1;
+    }
+  else
+    {
+      entry = SH_ALLOC(sizeof(struct sh_track_entry));
+      memset(entry, '\0', sizeof(struct sh_track_entry));
+      (entry->data).last_login    = time;
+      (entry->data).array[index]  = 1;
+      sl_strlcpy((entry->data).hostname, q, SH_LTRACK_HSIZE);
+
+      /* Report first login from this host 
+       */
+      if (check_level != CHECK_NONE) 
+	{ 
+	  report_first (FIL__, __LINE__, user, time, host);
+	}
+      
+      if (p)
+	SH_FREE(p);
+      return entry;
+    }
+
+  if (p)
+    SH_FREE(p);
+  return NULL;
+}
+
+/********************************************************
+ *
+ * Public Function
+ *
+ ********************************************************/
+
+void sh_ltrack_check(struct SH_UTMP_S * ut)
+{
+  int gres;
+  const char * user;
+  time_t time;
+#if defined(HAVE_UTHOST)
+  const char * host;
+#else
+  const char * host;
+#endif
+  struct sh_track * urecord;
+  time_t last_login;
+
+  /* Just return if we are not supposed to do anything
+   */
+  if (sig_level == SIG00 && check_level == CHECK_NONE && check_date == S_FALSE)
+    return;
+
+
+#if defined(HAVE_UTHOST)
+  host = ut->ut_host;
+#else
+  host = sh_util_strdup(_("unknown"));
+#endif
+  time = ut->ut_time;
+  user = ut->ut_name;
+
+  gres  = (60*60*24)/SH_LTRACK_GTRES;
+
+  urecord    = load_data(user);
+  last_login = (urecord->head).last_login;
+
+  urecord = load_data(user);
+  last_login = (urecord->head).last_login;
+
+  if (   last_login < time &&
+	 ( (time - last_login) >= gres || 
+	   0 != strcmp(host, (urecord->head).hostname)
+	   )
+	 )
+    {
+      struct tm ts;
+      unsigned int  index;
+      int isAlert;
+      struct sh_track_entry * entry;
+
+      (urecord->head).last_login = time;
+      sl_strlcpy((urecord->head).hostname, host, SH_LTRACK_HSIZE);
+      (urecord->head).n_entries += 1;
+
+      memcpy(&ts, localtime(&time), sizeof(struct tm));
+      index = time_to_index(&ts, SH_LTRACK_GTRES);
+      
+      /* Check global statistics here 
+       */
+      isAlert = check_statistics (index, (urecord->head).array, 
+				  SH_LTRACK_GTRES, 
+				  costab_gtres, sintab_gtres);
+      
+      if (isAlert != 0) 
+	{
+	  report_outlier(FIL__, __LINE__, user, time, host);
+	}
+      
+
+      if (check_date != S_FALSE)
+	{
+	  int isBad = check_login_date(user, index, ts.tm_wday);
+
+	  if (isBad != 0)
+	    {
+	      report_bad_date(FIL__, __LINE__, user, time, host);
+	    }
+	}
+
+      /* Update array afterwards 
+       */
+      (urecord->head).array[index] += 1;
+
+      entry = check_host(urecord->list, user, time, host, &ts);
+      if (entry)
+	{
+	  entry->next   = urecord->list;
+	  urecord->list = entry;
+	}
+
+      save_data(urecord, user);
+    } 
+
+  destroy_loaded(urecord);
+
+#if !defined(HAVE_UTHOST)
+  SH_FREE(host);
+#endif
+  return;
+}
+
+#ifdef SH_CUTEST
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "CuTest.h"
+
+void Test_login (CuTest *tc) {
+  char bitarr[10] = { 0,0,0,0,0,0,0,0,0,128 };
+  unsigned int i;
+  int j, k;
+  char buf[1024];
+  char *p, *q;
+  size_t l1, l2;
+
+  /* Check bitarray */
+
+  for (i = 0; i < 72; ++i)
+    {
+      set_bool(bitarr, i, 1);
+    }
+  for (i = 72; i < 80; ++i)
+    {
+      set_bool(bitarr, i, 0);
+    }
+  for (i = 0; i < 80; ++i)
+    {
+      j = get_bool(bitarr, i);
+      if (i < 72)
+	CuAssertTrue(tc, j > 0);
+      else
+	CuAssertIntEquals(tc, 0, j);
+    }
+
+  /* check build_path */
+
+  j = sl_strlcpy(buf, DEFAULT_DATAROOT, sizeof(buf));
+  CuAssertIntEquals(tc, 0, j);
+
+  p = build_path("rainer");
+  q = sh_util_dirname(p);
+  j = strncmp(buf, q, strlen(buf));
+  l1 = strlen(buf); l2 = strlen(q);
+  CuAssertTrue(tc, l2 >= l1);
+  CuAssertIntEquals(tc, 0, j);
+
+  q = sh_util_basename(p);
+  CuAssertStrEquals(tc, q, "cmFpbmVy");
+
+  { /* Check load/save of user data */
+    struct sh_track urecord, *precord;
+    struct sh_track_entry uentry0, *pentry;
+    struct sh_track_entry uentry1;
+
+    urecord.head.version   = 40;
+    urecord.head.n_entries = 41;
+    urecord.head.last_login = 42;
+    for (i = 0; i < SH_LTRACK_GTRES; ++i)
+      urecord.head.array[i] = 0;
+    urecord.head.array[30] = 30;
+
+    urecord.list = &uentry0;
+    uentry0.next = &uentry1;
+    uentry1.next = NULL;
+
+    uentry0.data.last_login = 52;
+    strcpy(uentry0.data.hostname, "host0");
+    for (i = 0; i < SH_LTRACK_HTRES; ++i)
+      uentry0.data.array[i] = 0;
+    uentry0.data.array[5] = 50;
+
+    uentry1.data.last_login = 62;
+    strcpy(uentry1.data.hostname, "host1");
+    for (i = 0; i < SH_LTRACK_HTRES; ++i)
+      uentry1.data.array[i] = 0;
+    uentry1.data.array[6] = 60;
+
+    snprintf(buf, sizeof(buf), "cutest_%06d", (int) getpid());
+
+    save_data_int(&urecord, buf);
+
+    precord = load_data_int(buf);
+
+    CuAssertIntEquals(tc, urecord.head.version, (precord->head).version);
+    CuAssertIntEquals(tc, urecord.head.n_entries, (precord->head).n_entries);
+    CuAssertIntEquals(tc, urecord.head.last_login, (precord->head).last_login);
+    for (i = 0; i < SH_LTRACK_GTRES; ++i)
+      CuAssertIntEquals(tc, urecord.head.array[i], (precord->head).array[i]);
+
+    CuAssertPtrNotNull(tc, precord->list);
+    pentry = precord->list;
+    CuAssertIntEquals(tc, uentry1.data.last_login, (pentry->data).last_login);
+    CuAssertStrEquals(tc, uentry1.data.hostname, (pentry->data).hostname);
+    for (i = 0; i < SH_LTRACK_HTRES; ++i)
+      CuAssertIntEquals(tc, uentry1.data.array[i], (pentry->data).array[i]);
+
+    CuAssertPtrNotNull(tc, pentry->next);
+    pentry = pentry->next;
+    CuAssertIntEquals(tc, uentry0.data.last_login, (pentry->data).last_login);
+    CuAssertStrEquals(tc, uentry0.data.hostname, (pentry->data).hostname);
+    for (i = 0; i < SH_LTRACK_HTRES; ++i)
+      CuAssertIntEquals(tc, uentry0.data.array[i], (pentry->data).array[i]);
+
+    CuAssertPtrEquals(tc, pentry->next, NULL);
+    destroy_loaded(precord);
+    unlink(buf);
+
+    precord = load_data_int("supacalifragilistic");
+    CuAssertPtrNotNull(tc, precord);
+    CuAssertPtrEquals(tc, precord->list, NULL);
+    CuAssertIntEquals(tc, SH_LTRACK_VERSION, (precord->head).version);
+    CuAssertIntEquals(tc, 0, (precord->head).n_entries);
+    CuAssertIntEquals(tc, 0, (precord->head).last_login);
+    for (i = 0; i < SH_LTRACK_GTRES; ++i)
+      CuAssertIntEquals(tc, 0, (precord->head).array[i]);
+    destroy_loaded(precord);
+
+    precord = load_data_int(NULL);
+    CuAssertPtrNotNull(tc, precord);
+    CuAssertPtrEquals(tc, precord->list, NULL);
+    CuAssertIntEquals(tc, SH_LTRACK_VERSION, (precord->head).version);
+    CuAssertIntEquals(tc, 0, (precord->head).n_entries);
+    CuAssertIntEquals(tc, 0, (precord->head).last_login);
+    for (i = 0; i < SH_LTRACK_GTRES; ++i)
+      CuAssertIntEquals(tc, 0, (precord->head).array[i]);
+    destroy_loaded(precord);
+  }
+
+  /* check configuration */
+
+  j = sh_login_set_siglevel("duh");
+  CuAssertIntEquals(tc, -1, j);
+  CuAssertIntEquals(tc, SIG00, sig_level);
+
+  j = sh_login_set_siglevel("yes");
+  CuAssertIntEquals(tc, 0, j);
+  CuAssertIntEquals(tc, SIG01, sig_level);
+  j = sh_login_set_siglevel("no");
+  CuAssertIntEquals(tc, 0, j);
+  CuAssertIntEquals(tc, SIG00, sig_level);
+  j = sh_login_set_siglevel("paranoid");
+  CuAssertIntEquals(tc, 0, j);
+  CuAssertIntEquals(tc, SIG05, sig_level);
+
+  j = sh_login_set_checklevel("duh");
+  CuAssertIntEquals(tc, -1, j);
+  CuAssertIntEquals(tc, CHECK_NONE, check_level);
+
+  j = sh_login_set_checklevel("yes");
+  CuAssertIntEquals(tc, 0, j);
+  CuAssertIntEquals(tc, CHECK_HOST, check_level);
+  j = sh_login_set_checklevel("no");
+  CuAssertIntEquals(tc, 0, j);
+  CuAssertIntEquals(tc, CHECK_NONE, check_level);
+  j = sh_login_set_checklevel("domain");
+  CuAssertIntEquals(tc, 0, j);
+  CuAssertIntEquals(tc, CHECK_DOMAIN, check_level);
+
+  j = sh_login_set_def_allow("always");
+  CuAssertIntEquals(tc, 0, j);
+  for (j = 0; j < LTRACK_NDAYS; ++j)
+    {
+      for (i = 0; i < SH_LTRACK_GTRES; ++i)
+	{
+	  k = get_bool(global_dates[j], i);
+	  CuAssertTrue(tc, k > 0);
+	}
+    }
+
+  j = sh_login_set_def_allow("never");
+  CuAssertIntEquals(tc, 0, j);
+  for (j = 0; j < LTRACK_NDAYS; ++j)
+    {
+      for (i = 0; i < SH_LTRACK_GTRES; ++i)
+	{
+	  k = get_bool(global_dates[j], i);
+	  CuAssertIntEquals(tc, 0, k);
+	}
+    }
+
+  j = sh_login_set_def_allow("workdays( 0:12-1:30, 07:30-18:29,23:30-23:59)");
+  CuAssertIntEquals(tc, 0, j);
+  for (j = 0; j < LTRACK_NDAYS; ++j)
+    {
+      for (i = 0; i < SH_LTRACK_GTRES; ++i)
+	{
+	  k = get_bool(global_dates[j], i);
+	  // fprintf(stderr, "%d: %d: %d\n", j, i, k);
+	  if (j == WORKDAYS)
+	    {
+	      if ( (i>=1 && i<=9) || (i>=45 && i <=110) || (i>=141 && i<=143))
+		CuAssertTrue(tc, k > 0);
+	      else
+		CuAssertIntEquals(tc, 0, k);
+	    }
+	  else
+	    {
+	      CuAssertIntEquals(tc, 0, k);
+	    }
+	}
+    }
+
+  j = sh_login_set_user_allow("rainer :workdays( 0:12-1:30, 07:30-18:29,23:30-23:59)");
+  CuAssertIntEquals(tc, 0, j);
+  j = sh_login_set_user_allow("rainer :saturday( 0:0-23:59)");
+  CuAssertIntEquals(tc, 0, j);
+  j = sh_login_set_user_allow("rain : workdays(0:12-1:30, 07:30-18:29,23:30-23:59)");
+  CuAssertIntEquals(tc, 0, j);
+  j = sh_login_set_user_allow("cat: workdays( 0:12-1:30, 07:30-18:29,23:30-23:59 )");
+  CuAssertIntEquals(tc, 0, j);
+  j = sh_login_set_user_allow("cat: sunday(0:00-23:59)");
+  CuAssertIntEquals(tc, 0, j);
+
+  {
+    int count = 0;
+    struct sh_track_dates * u = user_dates;
+    
+    CuAssertPtrNotNull(tc, u);
+
+    do {
+
+      if (count == 0) {
+	CuAssertStrEquals(tc, u->user, "cat");
+	CuAssertPtrNotNull(tc, u->next);
+      }
+      else if (count == 1) {
+	CuAssertStrEquals(tc, u->user, "rain");
+	CuAssertPtrNotNull(tc, u->next);
+      }
+      else if (count == 2) {
+	CuAssertStrEquals(tc, u->user, "rainer");
+	CuAssertPtrEquals(tc, u->next, NULL);
+      }
+
+      for (j = 0; j < LTRACK_NDAYS; ++j)
+	{
+	  for (i = 0; i < SH_LTRACK_GTRES; ++i)
+	    {
+	      k = get_bool(u->dates[j], i);
+	      // fprintf(stderr, "%d: %d: %d\n", j, i, k);
+	      if (j == WORKDAYS)
+		{
+		  if ( (i>=1 && i<=9) || (i>=45 && i <=110) || 
+		       (i>=141 && i<=143) )
+		    {
+		      CuAssertTrue(tc, k > 0);
+		    }
+		  else
+		    {
+		      CuAssertIntEquals(tc, 0, k);
+		    }
+		}
+	      else
+		{
+		  if ((count == 0 && j == SUNDAY) || 
+		      (count == 2 && j == SATURDAY))
+		    CuAssertTrue(tc, k > 0);
+		  else
+		    CuAssertIntEquals(tc, 0, k);
+		}
+	    }
+	}
+
+      if (u->next == NULL)
+	break;
+
+      u = u->next; ++count;
+
+    } while (1 == 1);
+  }
+
+  sh_login_reset();
+  CuAssertIntEquals(tc, SIG00, sig_level);
+  CuAssertIntEquals(tc, CHECK_NONE, check_level);
+
+  /* check dates */
+
+  j = sh_login_set_def_allow("workdays( 0:12-1:30, 07:30-18:29,23:30-23:59)");
+  CuAssertIntEquals(tc, 0, j);
+
+  j = check_login_date("rainer", 0, 2);
+  CuAssertIntEquals(tc, -1, j);
+  j = check_login_date("rainer", 1, 2);
+  CuAssertIntEquals(tc,  0, j);
+  j = check_login_date("rainer",50, 3);
+  CuAssertIntEquals(tc,  0, j);
+  j = check_login_date("rainer",142, 5);
+  CuAssertIntEquals(tc,  0, j);
+  j = check_login_date("rainer", 1, 0);
+  CuAssertIntEquals(tc, -1, j);
+  j = check_login_date("rainer", 1, 6);
+  CuAssertIntEquals(tc, -1, j);
+  j = sh_login_set_user_allow("rainer :saturday( 0:0-23:59)");
+  CuAssertIntEquals(tc, 0, j);
+  j = check_login_date("rainer", 1, 6);
+  CuAssertIntEquals(tc,  0, j);
+  j = sh_login_set_user_allow("mouse :sunday( 0:0-23:59)");
+  CuAssertIntEquals(tc, 0, j);
+  j = sh_login_set_user_allow("cat :saturday(0:0-23:59)");
+  CuAssertIntEquals(tc, 0, j);
+  j = check_login_date("rainer", 1, 6);
+  CuAssertIntEquals(tc,  0, j);
+  j = check_login_date("mouse", 1, 6);
+  CuAssertIntEquals(tc, -1, j);
+  j = check_login_date("mouse", 1, 0);
+  CuAssertIntEquals(tc,  0, j);
+  j = check_login_date("cat", 1, 6);
+  CuAssertIntEquals(tc,  0, j);
+  j = check_login_date("dog", 1, 6);
+  CuAssertIntEquals(tc, -1, j);
+
+  sh_login_reset();
+
+  /* statistics, critical values */
+  {
+    double f;
+
+    f = M_crit(1, SIG05);
+    CuAssertTrue(tc, f > 0.974 && f < 0.976);
+    f = M_crit(13, SIG05);
+    CuAssertTrue(tc, f > 0.576 && f < 0.578);
+    f = M_crit(22, SIG05);
+    CuAssertTrue(tc, f > 0.405 && f < 0.407);
+    f = M_crit(10, SIG05);
+    CuAssertTrue(tc, f > 0.646 && f < 0.648);
+    f = M_crit(10, SIG01);
+    CuAssertTrue(tc, f > 0.759 && f < 0.761);
+  }
+
+  /* stripped hostname */
+  p = stripped_hostname("127.20.120.100");
+  CuAssertStrEquals(tc, "127.20", p);
+
+  p = stripped_hostname("foo.www.example.com");
+  CuAssertStrEquals(tc, p, "www.example.com");
+
+  p = stripped_hostname("www.example.com");
+  CuAssertStrEquals(tc, p, "example.com");
+
+  p = stripped_hostname("localhost");
+  CuAssertStrEquals(tc, p, "localhost");
+
+  {
+    struct tm tt;
+
+    tt.tm_hour =  0;
+    tt.tm_min  = 30;
+    tt.tm_sec  =  0;
+
+    for (i = 0; i < 24; ++i)
+      {
+	tt.tm_hour =  i;
+	j = time_to_index(&tt, SH_LTRACK_HTRES);
+	CuAssertIntEquals(tc, j, i);
+      }
+
+    tt.tm_min  = 10;
+
+    for (i = 0; i < 24; ++i)
+      {
+	tt.tm_hour =  i;
+	j = time_to_index(&tt, SH_LTRACK_GTRES);
+	CuAssertIntEquals(tc, 1+i*6, j);
+      }
+  } 
+}
+/* #ifdef SH_CUTEST */
+#endif
+
+#else
+
+#ifdef SH_CUTEST
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "CuTest.h"
+
+void Test_login (CuTest *tc) {
+  (void) tc;
+}
+
+/* #ifdef SH_CUTEST */
+#endif
+
+#endif
Index: /trunk/src/sh_port2proc.c
===================================================================
--- /trunk/src/sh_port2proc.c	(revision 278)
+++ /trunk/src/sh_port2proc.c	(revision 279)
@@ -322,5 +322,6 @@
 /* returns the command and fills the 'user' array 
  */
-static char * port2proc_query(char * file, int proto, struct in_addr * saddr, int sport, 
+static char * port2proc_query(char * file, int proto, 
+			      struct in_addr * saddr, int sport, 
 			      unsigned long * pid, char * user, size_t userlen)
 {
Index: /trunk/src/sh_readconf.c
===================================================================
--- /trunk/src/sh_readconf.c	(revision 278)
+++ /trunk/src/sh_readconf.c	(revision 279)
@@ -255,5 +255,5 @@
 		  sh.host.release, sh.host.machine);
       
-      if  (sl_strncmp (p,  myident, sl_strlen(myident)) == 0
+      if  (sl_strncmp (p,  myident, strlen(myident)) == 0
 #ifdef HAVE_REGEX_H
 	   || sh_util_regcmp (p, myident) == 0
@@ -1202,4 +1202,7 @@
     sh_log_set_facility },
 
+  { N_("syslogmapstampto"),    SH_SECTION_LOG,   SH_SECTION_MISC, 
+    sh_log_set_stamp_priority },
+
   { N_("mactype"),     SH_SECTION_MISC,  SH_SECTION_NONE, 
     sh_util_sigtype },
Index: /trunk/src/sh_unix.c
===================================================================
--- /trunk/src/sh_unix.c	(revision 278)
+++ /trunk/src/sh_unix.c	(revision 279)
@@ -670,4 +670,8 @@
     sig_force_check = 1;
 #endif
+#ifdef SIGTTIN
+  if (mysignal == SIGTTIN)
+    sig_fresh_trail = 1;
+#endif
 #ifdef SIGABRT
   if (mysignal == SIGABRT)
@@ -837,7 +841,5 @@
   retry_sigaction(FIL__, __LINE__, SIGTSTP,   &ignact, &oldact);
 #endif
-#ifdef SIGTTIN
-  retry_sigaction(FIL__, __LINE__, SIGTTIN,   &ignact, &oldact);
-#endif
+
 #if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
 #ifdef SIGTTOU
@@ -847,7 +849,16 @@
     retry_sigaction(FIL__, __LINE__, SIGTTOU,   &ignact, &oldact);
 #endif
+#ifdef SIGTTIN
+  if (goDaemon == 1)
+    retry_sigaction(FIL__, __LINE__, SIGTTIN,     &act2, &oldact);
+  else
+    retry_sigaction(FIL__, __LINE__, SIGTTIN,   &ignact, &oldact);
+#endif
 #else
 #ifdef SIGTTOU
   retry_sigaction(FIL__, __LINE__, SIGTTOU,   &ignact, &oldact);
+#endif
+#ifdef SIGTTIN
+  retry_sigaction(FIL__, __LINE__, SIGTTIN,   &ignact, &oldact);
 #endif
 #endif
@@ -3128,13 +3139,4 @@
   SL_RETURN(0, _("sh_unix_getinfo_attr"));
 }
-#else
-static 
-int sh_unix_getinfo_attr (char * name, 
-			  unsigned long * flags, 
-			  char * c_attr,
-			  int fd, struct stat * buf)
-{
-  return 0;
-}
 
 /* defined(__linux__) || defined(HAVE_STAT_FLAGS) */
@@ -3905,4 +3907,5 @@
   theFile->attributes      =    0;
 
+#if (defined(__linux__) && (defined(HAVE_LINUX_EXT2_FS_H) || defined(HAVE_EXT2FS_EXT2_FS_H))) || defined(HAVE_STAT_FLAGS)
   if (theFile->c_mode[0] != 'c' && theFile->c_mode[0] != 'b' &&
       theFile->c_mode[0] != 'l' )
@@ -3910,4 +3913,5 @@
 			 &theFile->attributes, theFile->c_attributes, 
 			 fd, &buf);
+#endif
 #endif
 
Index: /trunk/src/sh_utmp.c
===================================================================
--- /trunk/src/sh_utmp.c	(revision 278)
+++ /trunk/src/sh_utmp.c	(revision 279)
@@ -225,4 +225,20 @@
   },
   {
+    N_("logincheckfirst"),
+    sh_login_set_checklevel
+  },
+  {
+    N_("logincheckoutlier"),
+    sh_login_set_siglevel
+  },
+  {
+    N_("logincheckdate"),
+    sh_login_set_def_allow
+  },
+  {
+    N_("logincheckuserdate"),
+    sh_login_set_user_allow
+  },
+  {
     NULL,
     NULL
@@ -237,4 +253,6 @@
   ShUtmpActive       = S_TRUE;
   ShUtmpInterval     = 300;
+
+  sh_login_reset();
   return;
 }
@@ -498,4 +516,7 @@
 int sh_utmp_init (struct mod_type * arg)
 {
+#if !defined(HAVE_PTHREAD)
+  (void) arg;
+#endif
   if (ShUtmpActive == BAD)
     return SH_MOD_FAILED;
@@ -548,5 +569,7 @@
   init_done          = 0;
 
+#if defined(HAVE_PTHREAD)
   sh_inotify_remove(&inotify_watch);
+#endif
 
   SL_RETURN( (0), _("sh_utmp_end"));
@@ -557,5 +580,7 @@
 {
   set_defaults();
+#if defined(HAVE_PTHREAD)
   sh_inotify_remove(&inotify_watch);
+#endif
   return 0;
 }
@@ -1206,9 +1231,9 @@
 }
 
+extern void sh_ltrack_check(struct SH_UTMP_S * ut);
 
 static void sh_utmp_login_morechecks(struct SH_UTMP_S * ut)
 {
-  if (ut)
-    return;
+  sh_ltrack_check(ut);
   return;
 }
@@ -1216,6 +1241,5 @@
 static void sh_utmp_logout_morechecks(struct log_user * user)
 {
-  if (user)
-    return;
+  (void) user;
   return;
 }
Index: /trunk/src/slib.c
===================================================================
--- /trunk/src/slib.c	(revision 278)
+++ /trunk/src/slib.c	(revision 279)
@@ -16,7 +16,7 @@
 #endif
 
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
 #include <fcntl.h>
 #include <signal.h>
@@ -259,5 +259,5 @@
   else
     sl_strlcpy(tmp, fmt, 256);
-  retval = sl_strlen(tmp);
+  retval = strlen(tmp);
   if (retval > 0 && tmp[retval-1] == '\n')
     tmp[retval-1] = '\0';
@@ -274,5 +274,5 @@
       sprintf      (val, _("[%2d] "), trace_level);
       sl_strlcat   (msg,     val,   256);
-      sl_vsnprintf (&msg[sl_strlen(msg)], 255, tmp, ap);
+      sl_vsnprintf (&msg[strlen(msg)], 255, tmp, ap);
       sl_snprintf  (tmp, 255, _(" \t - File %c%s%c at line %d"), 
 		    0x22, file, 0x22, line);
Index: /trunk/test/testcompile.sh
===================================================================
--- /trunk/test/testcompile.sh	(revision 278)
+++ /trunk/test/testcompile.sh	(revision 279)
@@ -378,4 +378,6 @@
 	fi
 	#
+	[ -z "${SMATCH}" ] || { CC="${SAVE_CC}"; export CC; SMATCH=""; export SMATCH; }
+	#
 	${TOP_SRCDIR}/configure --quiet  --prefix=$PW_DIR --localstatedir=$PW_DIR --with-config-file=$PW_DIR/samhainrc.test  --enable-process-check --enable-port-check --enable-static > /dev/null 2>> test_log
 	#
@@ -386,5 +388,6 @@
 	let "num = num + 1" >/dev/null
 	run_uno $? $num || let "numfail = numfail + 1"  >/dev/null
-
+	#
+	[ -z "${SMATCH_CC}" ] || { CC="${SMATCH_CC}"; export CC; SMATCH="${SAVE_SMATCH}"; export SMATCH; }
 	#
 	# test standalone compilation
