/* SAMHAIN file system integrity testing                                   */
/* Copyright (C) 2001 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"

#define SH_SYSCALL_CODE

#ifdef HOST_IS_I86LINUX
#define SH_IDT_TABLE
#define SH_PROC_CHECK
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <sys/wait.h>
#include <signal.h>


#ifdef SH_USE_KERN

#undef  FIL__
#define FIL__  _("sh_kern.c")

#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE) 

#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif


#include "samhain.h"
#include "sh_utils.h"
#include "sh_error.h"
#include "sh_modules.h"
#include "sh_kern.h"
#include "sh_ks_xor.h"

#include "sh_unix.h"
#include "sh_hash.h"



sh_rconf sh_kern_table[] = {
  {
    N_("severitykernel"),
    sh_kern_set_severity
  },
  {
    N_("kernelcheckactive"),
    sh_kern_set_activate
  },
  {
    N_("kernelcheckinterval"),
    sh_kern_set_timer
  },
  {
    N_("kernelcheckidt"),
    sh_kern_set_idt
  },
  {
    N_("kernelsystemcall"),
    sh_kern_set_sc_addr
  },
  {
    N_("kernelsyscalltable"),
    sh_kern_set_sct_addr
  },
  {
    N_("kernelprocrootlookup"),
    sh_kern_set_proc_root_lookup
  },
 {
    N_("kernelprocrootiops"),
    sh_kern_set_proc_root_iops
  },
  {
    N_("kernelprocroot"),
    sh_kern_set_proc_root
  },
  {
    NULL,
    NULL
  },
};


static time_t  lastcheck;
static int     ShKernActive   = S_TRUE;
static int     ShKernInterval = 300;
static int     ShKernSeverity = SH_ERR_SEVERE;
static int     ShKernDelay    = 100; /* milliseconds */
static int     ShKernIDT      = S_TRUE;

/* The address of system_call
 */
#ifdef SH_SYS_CALL_ADDR
static unsigned long system_call_addr = SH_SYS_CALL_ADDR;
#else
static unsigned long system_call_addr = 0;
#endif

/* The address of the sys_call_table
 */
#ifdef SH_SYS_CALL_TABLE
static unsigned int  kaddr = SH_SYS_CALL_TABLE;
#else
static unsigned int  kaddr = 0;
#endif

#ifdef PROC_ROOT_LOC
static unsigned long proc_root = PROC_ROOT_LOC;
#else
static unsigned long proc_root = 0;
#endif
#ifdef PROC_ROOT_IOPS_LOC
static unsigned long proc_root_iops = PROC_ROOT_IOPS_LOC;
#else
static unsigned long proc_root_iops = 0;
#endif
#ifdef PROC_ROOT_LOOKUP_LOC
static unsigned long proc_root_lookup = PROC_ROOT_LOOKUP_LOC;
#else
static unsigned long proc_root_lookup = 0;
#endif

int sh_kern_null()
{
  return 0;
}

#ifdef SH_IDT_TABLE

#include <asm/segment.h>

#define SH_MAXIDT   256
unsigned char sh_idt_table[SH_MAXIDT * 8];
char * sh_strseg(unsigned short segment)
{
  switch (segment) {
  case __KERNEL_CS:
    return _("KERNEL_CS");
  case __KERNEL_DS:
    return _("KERNEL_DS");
  case __USER_CS:
    return _("USER_CS");
  case __USER_DS:
    return _("USER_DS");
  default:
    return _("unknown");
  }
}
/* ifdef SH_IDT_TABLE */
#endif

static char * sh_kern_charhex( unsigned char i )
{
  static char i2h[2];
  int j, k;

  j = i / 16;
  k = i - (j*16);

  if (j < 10) i2h[0] = '0'+j;
  else        i2h[0] = 'A'+(j-10);
  
  if (k < 10) i2h[1] = '0'+k;
  else        i2h[1] = 'A'+(k-10);

  return i2h;
}

static void sh_kern_push2db (char * name, unsigned long addr, 
			     unsigned long code1, unsigned long code2,
			     unsigned char * code, int size)
{
  file_type   tmpFile;
  int         i = 0;
  char      * p;

  sl_strlcpy(tmpFile.fullpath, name, PATH_MAX);
  tmpFile.size  = addr;
  tmpFile.mtime = code1;
  tmpFile.ctime = code2;

  tmpFile.atime = 0;
  tmpFile.mode  = 0;
  tmpFile.owner = 0;
  tmpFile.group = 0;
  sl_strlcpy(tmpFile.c_owner, _("root"), 5);
  sl_strlcpy(tmpFile.c_group, _("root"), 5);

  if ((code != NULL) && (size < (PATH_MAX/2)-1))
    {
      tmpFile.c_mode[0] = 'l';  
      tmpFile.c_mode[1] = 'r'; tmpFile.c_mode[2]  = 'w';
      tmpFile.c_mode[3] = 'x'; tmpFile.c_mode[4]  = 'r'; 
      tmpFile.c_mode[5] = 'w'; tmpFile.c_mode[6]  = 'x'; 
      tmpFile.c_mode[7] = 'r'; tmpFile.c_mode[8]  = 'w'; 
      tmpFile.c_mode[9] = 'x'; tmpFile.c_mode[10] = '\0'; 
      for (i = 0; i < size; ++i)
	{
	  p = sh_kern_charhex (code[i]);
	  tmpFile.linkpath[2*i]   = p[0];
	  tmpFile.linkpath[2*i+1] = p[1];
	  tmpFile.linkpath[2*i+2] = '\0';
	}
    }
  else
    {
      tmpFile.c_mode[0] = '-';  
      tmpFile.c_mode[1] = '-'; tmpFile.c_mode[2]  = '-';
      tmpFile.c_mode[3] = '-'; tmpFile.c_mode[4]  = '-'; 
      tmpFile.c_mode[5] = '-'; tmpFile.c_mode[6]  = '-'; 
      tmpFile.c_mode[7] = '-'; tmpFile.c_mode[8]  = '-'; 
      tmpFile.c_mode[9] = '-'; tmpFile.c_mode[10] = '\0'; 
      tmpFile.linkpath[0] = '-'; tmpFile.linkpath[1] = '\0';
    }

  if (sh.flag.checkSum == SH_CHECK_CHECK && sh.flag.update == S_TRUE)
    sh_hash_pushdata_memory (&tmpFile, 
			     _("000000000000000000000000000000000000000000000000"));
  else
    sh_hash_pushdata (&tmpFile, 
		      _("000000000000000000000000000000000000000000000000"));
  return;
}

extern int sh_util_hextobinary (char * binary, char * hex, int bytes);

static char * sh_kern_db2pop (char * name, unsigned long * addr, 
			      unsigned long * code1, unsigned long * code2,
			      int * size)
{
  file_type   tmpFile;
  char      * p;
  int         i;

  if (0 == sh_hash_get_it (name, &tmpFile))
    {
      *addr  = tmpFile.size;
      *code1 = tmpFile.mtime;
      *code2 = tmpFile.ctime;

      if (tmpFile.linkpath[0] != '-')
	{
	  p = SH_ALLOC(PATH_MAX);
	  i = sh_util_hextobinary (p, tmpFile.linkpath, 
				   strlen(tmpFile.linkpath));
	  if (i == 0)
	    {
	      *size = (strlen(tmpFile.linkpath)/2);
	      p[*size] = '\0';
	      return p;
	    }
	  else
	    {
	      SH_FREE(p);
	      *size = 0;
	      return NULL;
	    }
	}
      else
	{
	  *size = 0;
	  return NULL;
	}
    }
  else
    {
      *size = 0;
      *addr = 0;
      return NULL;
    }
}

char * sh_kern_db_syscall (int num, char * prefix,
			   void * in_name, unsigned long * addr,
			   unsigned int * code1, unsigned int * code2,
			   int * size, int direction)
{
  char          path[128];
  char        * p = NULL;
  unsigned long x1, x2;
  char        * name = (char *) in_name;

  sl_snprintf(path, 128, "K_%s_%04d", prefix, num);

  if (direction == 0) 
    {
      x1 = *code1;
      x2 = *code2;

      sh_kern_push2db (path, *addr, x1, x2,
		       name, (name == NULL) ? 0 : (*size));
    }
  else
    {
      p = sh_kern_db2pop (path, addr,  &x1, &x2, size);
      *code1 = (unsigned int) x1;
      *code2 = (unsigned int) x2;
    }
  return p;
}
 
#ifdef HOST_IS_LINUX

int sh_kern_data_init ()
{
  unsigned long store0 = 0;
  unsigned int  store1 = 0, store2 = 0;
  int           datasize, i, j;
  char        * databuf;

#ifdef SH_SYSCALL_CODE
  /* system_call code
   */
  databuf = sh_kern_db_syscall (0, _("system_call"), 
				NULL, &store0, &store1, &store2,
				&datasize, 1);
  if (datasize == sizeof(system_call_code))
    {
      memcpy (system_call_code, databuf, sizeof(system_call_code));
      SH_FREE(databuf);
    }
  else
    {
      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
		      _("system_call_code not found in database"), 
		      _("sh_kern_data_init"));
      return -1;
    }
#endif

  /* syscall address and code
   */ 
  for (i = 0; i < SH_MAXCALLS; ++i) 
    {
      databuf = sh_kern_db_syscall (i, _("syscall"), 
				    NULL, &store0, &store1, &store2,
				    &datasize, 1);
      sh_syscalls[i].addr = store0;
      if (store0 == 0) {
	sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, i, MSG_E_SUBGEN,
			_("syscall address not found in database"), 
			_("sh_kern_data_init"));
	return -1;
      }
#ifdef SH_SYSCALL_CODE
      sh_syscalls[i].code[0] = (unsigned int) store1; 
      sh_syscalls[i].code[1] = (unsigned int) store2;
      if ((store1 == 0) || (store2 == 0)) {
	sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, i, MSG_E_SUBGEN,
			_("syscall code not found in database"), 
			_("sh_kern_data_init"));
	/* fprintf(stderr, "Syscall #%d\n", i); */
	/* return -1; */
      }
#endif 
      if (databuf != NULL) {
	SH_FREE(databuf);
      }
      
    }

#ifdef SH_IDT_TABLE
  if (ShKernIDT == S_TRUE)
    {
      for (j = 0; j < SH_MAXIDT; ++j) 
	{
	  databuf = sh_kern_db_syscall (j, _("idt_table"), 
					NULL, 
					&store0, &store1, &store2,
					&datasize, 1);
	  if (datasize == 8) {
	    memcpy(&idt_table[j*8], databuf, 8);
	    SH_FREE(databuf);
	  } else {
	    sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, j, MSG_E_SUBGEN,
			    _("idt table not found in database"), 
			    _("sh_kern_data_init"));
	    return -1;
	  }
	}
    }
#endif

  return 0;
}

#ifdef SH_PROC_CHECK
struct inode_operations {
  int (*create) (int *,int *,int);
  int * (*lookup) (int *,int *);
  int (*link) (int *,int *,int *);
  int (*unlink) (int *,int *);
  int (*symlink) (int *,int *,const char *);
  int (*mkdir) (int *,int *,int);
  int (*rmdir) (int *,int *);
  int (*mknod) (int *,int *,int,int);
  int (*rename) (int *, int *,
                 int *, int *);
  /* flawfinder: ignore */
  int (*readlink) (int *, char *,int);
  int (*follow_link) (int *, int *);
  void (*truncate) (int *);
  int (*permission) (int *, int);
  int (*revalidate) (int *);
  /*
    int (*setattr) (int *, int *);
    int (*getattr) (int *, int *);
    int (*setxattr) (int *, const char *, void *, size_t, int);
    ssize_t (*getxattr) (int *, const char *, void *, size_t);
    ssize_t (*listxattr) (int *, char *, size_t);
    int (*removexattr) (int *, const char *);
  */
};

struct file_operations {
  int (*create) (int *,int *,int);
};

struct proc_dir_entry {
  unsigned short low_ino;
  unsigned short namelen;
  const char * name;
  mode_t mode;
  nlink_t nlink;
  uid_t uid;
  gid_t gid;
  unsigned long size; 
  struct inode_operations * proc_iops;
  struct file_operations * proc_fops;
  /*
  get_info_t *get_info;
  struct module *owner;
  struct proc_dir_entry *next, *parent, *subdir;
  void *data;
  read_proc_t *read_proc;
  write_proc_t *write_proc;
  atomic_t count;         
  int deleted;  
  */          
};
#endif

int sh_kern_check_internal ()
{
  static int is_init = 0;
  int kd;
  int res;
  pid_t mpid;
  int mpipe[2];
  int i, j, status = 0;
  /* unsigned int kaddr; */
  unsigned long kmem_call_table[512];

#ifdef SH_PROC_CHECK
  struct inode_operations proc_root_inode;
  struct proc_dir_entry proc_root_dir;
#endif

#ifdef SH_SYSCALL_CODE
  unsigned int kmem_code_table[512][2];
#endif
#ifdef SH_IDT_TABLE
  unsigned char  buf[6];
  unsigned short idt_size;
  unsigned long  idt_addr;
  /* int            k, curr_keep = 0; */
  unsigned short idt_offset_lo, idt_offset_hi, idt_selector;
  unsigned char  idt_reserved, idt_flag;
  unsigned short sh_idt_offset_lo, sh_idt_offset_hi, sh_idt_selector;
  unsigned char  sh_idt_reserved, sh_idt_flag;
  int            dpl;
  unsigned long  idt_iaddr;
  int            sh_dpl;
  unsigned long  sh_idt_iaddr;
  char           idt_type, sh_idt_type;
#endif

  unsigned char new_system_call_code[256];

#ifdef SH_USE_LKM
  static int check_getdents      = 0;
  /* #ifdef __NR_getdents64 */
  static int check_getdents64    = 0;
  /* #endif */
  static int copy_if_next        = -1;
  static int copy_if_next_64     = -1;
#endif

  unsigned long store0;
  unsigned int  store1, store2;
  int           datasize;
  int           mod_syscall_addr = 0;
  int           mod_syscall_code = 0;
  UINT64        size_old  = 0, size_new = 0;
  UINT64        mtime_old = 0, mtime_new = 0;
  UINT64        ctime_old = 0, ctime_new = 0;
  char          tmp[128];
  char          msg[2*SH_BUFSIZE];
  char timstr_o[32];
  char timstr_n[32];
  char * p;
  int    k;
  char * linkpath_old;
  char * linkpath_new;

  int           max_system_call = (SYS_CALL_LOC < 128) ? 128 : SYS_CALL_LOC;

  SL_ENTER(_("sh_kern_check_internal"));

  
  if (is_init == 0)
    {
      if (sh.flag.checkSum != SH_CHECK_INIT && sh.flag.update != S_TRUE)
	{
	  if (0 == sh_kern_data_init()) {
	    is_init = 1;
	  } else {
	    sh_error_handle (ShKernSeverity, FIL__, __LINE__, status, 
			     MSG_E_SUBGEN,
			     _("could not initialize - switching off"),
			     _("kern_check_internal") );
	    ShKernActive = S_FALSE;
	    SL_RETURN( (-1), _("sh_kern_check_internal"));
	  }
	}
      else if ((sh.flag.checkSum == SH_CHECK_INIT || 
		sh.flag.checkSum == SH_CHECK_CHECK) && 
	       (sh.flag.update == S_TRUE))
	{
	  if (0 == sh_kern_data_init()) {
	    is_init = 1;
	  } else {
	    sh_error_handle (SH_ERR_WARN, FIL__, __LINE__, status, 
			     MSG_E_SUBGEN,
			     _("no or incomplete data in baseline database"),
			     _("kern_check_internal") );
	  }
	}
    }

  /*
   * kaddr is the address of the sys_call_table
   */

  if (kaddr == (unsigned int) -1)
    {
      sh_error_handle (ShKernSeverity, FIL__, __LINE__, status, MSG_E_SUBGEN,
		       _("no address for sys_call_table - switching off"),
		       _("kern_check_internal") );
      ShKernActive = S_FALSE;
      SL_RETURN( (-1), _("sh_kern_check_internal"));
    }
  
  kd = aud_open(FIL__, __LINE__, SL_YESPRIV, _("/dev/kmem"), O_RDONLY, 0);
  
  if (kd < 0)
    {
      status = errno;
      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
		       _("error opening /dev/kmem"),
		       _("kern_check_internal") );
      SL_RETURN( (-1), _("sh_kern_check_internal"));
    }

  status = aud_pipe(FIL__, __LINE__, mpipe);

  if (status == 0)
    {
      mpid = aud_fork(FIL__, __LINE__);

      switch (mpid) 
	{
	case -1:
	  status = -1;
	  break;
	case 0:                       /* child */
	  status = close(mpipe[0]);
	  setpgid(0, 0);
	  
	  /* Seek to the system call table (at kaddr) and read it into
	   * the kmem_call_table array
	   */
	  if(status == 0)
	    {
	      if(lseek(kd, (off_t)kaddr, SEEK_SET) == -1)
		{
		  status = -2;
		}
	    }
	  if(status == 0)
	    {
	      retry_msleep (0, ShKernDelay); /* milliseconds */
	      if (sizeof(kmem_call_table) != 
		  read(kd, &kmem_call_table, sizeof(kmem_call_table)))
		{
		  status = -3;
		}
	    }

#ifdef SH_SYSCALL_CODE
	  /* 
	   * Seek to the system call address (at sh_syscalls[j].addr) and 
	   * read first 8 bytes into kmem_code_table[j][] (2 * unsigned int)
	   */
	  if(status == 0)
	    {
	      memset(kmem_code_table, 0, sizeof(kmem_code_table));
	      for (j = 0; j < SH_MAXCALLS; ++j) 
		{

		  if (sh_syscalls[j].addr == 0UL) {
		    sh_syscalls[j].addr = kmem_call_table[j];
		  }

		  if (sh_syscalls[j].name == NULL || 
		      sh_syscalls[j].addr == 0UL)
		    break;

		  if ((sh.flag.checkSum == SH_CHECK_INIT || 
		       sh.flag.checkSum == SH_CHECK_CHECK) && 
		      (sh.flag.update == S_TRUE))
		    {
		      lseek (kd, kmem_call_table[j], SEEK_SET);
		    }
		  else
		    {
		      lseek (kd, sh_syscalls[j].addr, SEEK_SET);
		    }
		  read  (kd, &(kmem_code_table[j][0]), 
			 2 * sizeof(unsigned int));
		}
	    }
#endif

#ifdef SH_IDT_TABLE
	  if(status == 0)
	    {
	      /* 
	       * Get the address and size of Interrupt Descriptor Table,
	       * and read the content into 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;

	      if (idt_size > SH_MAXIDT)
		idt_size = SH_MAXIDT;

	      memset(sh_idt_table, '\0', SH_MAXIDT*8);
	      lseek (kd, idt_addr, SEEK_SET);
	      read  (kd, sh_idt_table, idt_size*8);
	    }
#endif

	  /* 
	   * Seek to the system_call address (at system_call_addr) and 
	   * read first 256 bytes into new_system_call_code[]
	   *
	   * system_call_addr is defined in the include file.
	   */
	  if(status == 0)
	    {
	      lseek (kd, system_call_addr, SEEK_SET);
	      read  (kd, new_system_call_code, 256);
	    }


	  /* 
	   * Seek to proc_root and read the structure.
	   * Seek to proc_root_inode_operations and get the structure.
	   */
#ifdef SH_PROC_CHECK
	  if(status == 0)
	    {
	      lseek (kd, proc_root, SEEK_SET);
	      read  (kd, &proc_root_dir, sizeof(proc_root_dir));
	      lseek (kd, proc_root_iops, SEEK_SET);
	      read  (kd, &proc_root_inode, sizeof(proc_root_inode));
	    }
#endif

	  if(status == 0)
	    {
	      status = 
		write(mpipe[1], &kmem_call_table, sizeof(kmem_call_table));
#ifdef SH_SYSCALL_CODE
	      if(status > 0)
		{
		  status = 
		    write(mpipe[1], &kmem_code_table, sizeof(kmem_code_table));
		}
#endif
#ifdef SH_IDT_TABLE
	      if(status > 0)
		{
		  status = 
		    write(mpipe[1], &sh_idt_table, sizeof(sh_idt_table));
		}
#endif
	      if(status > 0)
		{
		  status = 
		    write(mpipe[1], new_system_call_code, 256);
		}
#ifdef SH_PROC_CHECK
	      if(status > 0)
		{
		  status = 
		    write(mpipe[1], &proc_root_dir, sizeof(proc_root_dir));
		}
	      if(status > 0)
		{
		  status = 
		    write(mpipe[1], &proc_root_inode, sizeof(proc_root_inode));
		}
#endif
	    }
	  _exit( (status >= 0) ? 0 : status);
	  break;
	  
	default:
	  close (mpipe[1]);
	  close (kd);
	  retry_msleep (0, ShKernDelay); /* milliseconds */
	  if (sizeof(kmem_call_table) != 
	      read(mpipe[0], &kmem_call_table, sizeof(kmem_call_table)))
	    status = -4;
	  else
	    status = 0;

#ifdef SH_SYSCALL_CODE
	  if(status == 0)
	    {
	      if (sizeof(kmem_code_table) != 
		  read(mpipe[0], &kmem_code_table, sizeof(kmem_code_table)))
		status = -5;
	      else
		status = 0;
	    }
#endif	  

#ifdef SH_IDT_TABLE
	  if(status == 0)
	    {
	      memset(sh_idt_table, '\0', SH_MAXIDT*8);
	      if (sizeof(sh_idt_table) != 
		  read(mpipe[0], &sh_idt_table, sizeof(sh_idt_table)))
		status = -5;
	      else
		status = 0;
	    }
#endif	  

	  if(status == 0)
	    {
	      if (256 != read(mpipe[0], new_system_call_code, 256))
		status = -6;
	      else
		status = 0;
	    }

#ifdef SH_PROC_CHECK
	  if(status == 0)
	    {
	      if (sizeof(proc_root_dir) !=
		  read(mpipe[0], &proc_root_dir, sizeof(proc_root_dir)))
		status = -7;
	      else
		status = 0;
	    }
	  if(status == 0)
	    {
	      if (sizeof(proc_root_inode) !=
		  read(mpipe[0], &proc_root_inode, sizeof(proc_root_inode)))
		status = -8;
	      else
		status = 0;
	    }
#endif

	  if (status < 0)
	    res = waitpid(mpid, NULL,    WNOHANG|WUNTRACED);
	  else 
	    {
	      res = waitpid(mpid, &status, WNOHANG|WUNTRACED);
	      if (res == 0 && 0 != WIFEXITED(status))
		status = WEXITSTATUS(status);
	    }
	  close (mpipe[0]);
	  if (res <= 0)
	    {
	      aud_kill(FIL__, __LINE__, mpid, 9);
	      waitpid(mpid, NULL, 0);
	    }
	  break;
	}
    }

  if ( status < 0)
    {
      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
		       _("error reading from /dev/kmem"),
		       _("kern_check_internal") );
      SL_RETURN( (-1), _("sh_kern_check_internal"));
    }

  /* Check the proc_root inode.
   *
   * This will detect adore-ng.
   */
  if ( (unsigned int) *proc_root_inode.lookup != proc_root_lookup)
    {
      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_KERN_PROC,
		       _("proc_root_inode_operations.lookup != proc_root_lookup"));
    }
  else if ( ( ((unsigned int) * &proc_root_dir.proc_iops) != proc_root_iops) &&
       (proc_root_dir.size != proc_root_iops))
    {
      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_KERN_PROC,
		       _("proc_root.proc_iops != proc_root_inode_operations"));
    }

  /* Check the system_call syscall gate.
   *
   * Stored(old) is system_call_code[]
   */
  if (sh.flag.checkSum == SH_CHECK_INIT || sh.flag.update == S_TRUE)
    {
      store0 = 0; store1 = 0; store2 = 0;
      datasize = sizeof(system_call_code);
      sh_kern_db_syscall (0, _("system_call"), 
			  new_system_call_code, &store0, &store1, &store2,
			  &datasize, 0);
    }

  if ((sh.flag.checkSum != SH_CHECK_INIT) || 
      (sh.flag.update == S_TRUE && is_init == 1))
    {
      for (i = 0; i < (max_system_call + 4); ++i) 
	{
	  if (system_call_code[i] != new_system_call_code[i])
	    {
#ifdef SH_USE_XML
	      sl_snprintf(tmp, 128, "path=\"K_%s_%04d\" ", 
			  _("system_call"), 0);
#else
	      sl_snprintf(tmp, 128, "path=<K_%s_%04d>, ", 
			  _("system_call"), 0);
#endif
	      sl_strlcpy(msg, tmp, SH_BUFSIZE);

	      linkpath_old = SH_ALLOC(520);
	      linkpath_new = SH_ALLOC(520);
	      for (k = 0; k < 256; ++k)
		{
		  p = sh_kern_charhex (system_call_code[k]);
		  linkpath_old[2*k]   = p[0];
		  linkpath_old[2*k+1] = p[1];
		  linkpath_old[2*k+2] = '\0';
		}
	      for (k = 0; k < 256; ++k)
		{
		  p = sh_kern_charhex (new_system_call_code[k]);
		  linkpath_new[2*k]   = p[0];
		  linkpath_new[2*k+1] = p[1];
		  linkpath_new[2*k+2] = '\0';
		}
#ifdef SH_USE_XML
	      sl_strlcat(msg, _("link_old=\""),    2*SH_BUFSIZE);
	      sl_strlcat(msg, linkpath_old,        2*SH_BUFSIZE);
	      sl_strlcat(msg, _("\" link_new=\""), 2*SH_BUFSIZE);
	      sl_strlcat(msg, linkpath_new,        2*SH_BUFSIZE);
	      sl_strlcat(msg, _("\""),             2*SH_BUFSIZE);
#else
	      sl_strlcat(msg, _("link_old=<"),     2*SH_BUFSIZE);
	      sl_strlcat(msg, linkpath_old,        2*SH_BUFSIZE);
	      sl_strlcat(msg, _(">, link_new=<"),   2*SH_BUFSIZE);
	      sl_strlcat(msg, linkpath_new,        2*SH_BUFSIZE);
	      sl_strlcat(msg, _(">"),              2*SH_BUFSIZE);
#endif

	      sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
			       status, MSG_KERN_GATE,
			       new_system_call_code[i], 0,
			       system_call_code[i], 0,
			       0, _("system_call (interrupt handler)"),
			       msg);
	      
	      SH_FREE(linkpath_old);
	      SH_FREE(linkpath_new);

	      for (j = 0; j < (max_system_call + 4); ++j)
		system_call_code[j] = new_system_call_code[j];
	      break;
	    }
	}
    }
  
  /* Check the individual syscalls
   *
   * Stored(old) is sh_syscalls[] array.
   */
  if (sh.flag.checkSum == SH_CHECK_INIT || sh.flag.update == S_TRUE)
    {
      for (i = 0; i < SH_MAXCALLS; ++i) 
	{
	  store0 = kmem_call_table[i]; 
#ifdef SH_SYSCALL_CODE
	  store1 = kmem_code_table[i][0]; store2 = kmem_code_table[i][1];
#else
	  store1 = 0; store2 = 0;
#endif
	  sh_kern_db_syscall (i, _("syscall"), 
			      NULL, &store0, &store1, &store2,
			      0, 0);
	}
    }

  if ((sh.flag.checkSum != SH_CHECK_INIT) || 
      (sh.flag.update == S_TRUE && is_init == 1))
    {
      for (i = 0; i < SH_MAXCALLS; ++i) 
	{
	  if (sh_syscalls[i].name == NULL /* || sh_syscalls[i].addr == 0UL */)
	    break;

#ifdef SH_USE_LKM
	  if (sh_syscalls[i].addr != kmem_call_table[i])
	    {
	      if (check_getdents == 0 && 
		  0 == strcmp(_(sh_syscalls[i].name), _("sys_getdents")))
		{
		  check_getdents = 1;
		  sh_error_handle (SH_ERR_WARN, FIL__, __LINE__, 
				   status, MSG_E_SUBGEN,
				   _("Modified kernel syscall (expected)."),
				   _(sh_syscalls[i].name) );
		  copy_if_next = i;
		  sh_syscalls[i].addr = kmem_call_table[i];
		  continue;
		}
	      /* #ifdef __NR_getdents64 */
	      else if  (check_getdents64 == 0 && 
			0 == strcmp(_(sh_syscalls[i].name), 
				    _("sys_getdents64")))
		{
		  check_getdents64 = 1;
		  sh_error_handle (SH_ERR_WARN, FIL__, __LINE__, 
				   status, MSG_E_SUBGEN,
				   _("Modified kernel syscall (expected)."),
				   _(sh_syscalls[i].name) );
		  copy_if_next_64 = i;
		  sh_syscalls[i].addr = kmem_call_table[i];
		  continue;
		}
	      /* #endif */
	      else
		{
		  size_old = sh_syscalls[i].addr;
		  size_new = kmem_call_table[i];
		  mod_syscall_addr = 1;
		  /*
		  sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
				   status, MSG_KERN_POLICY,
				   kmem_call_table[i],
				   sh_syscalls[i].addr,
				   i, _(sh_syscalls[i].name)
				   );
		  */
		}
	      sh_syscalls[i].addr = kmem_call_table[i];
	    }
#else
	  if (sh_syscalls[i].addr != kmem_call_table[i])
	    {
	      size_old = sh_syscalls[i].addr;
	      size_new = kmem_call_table[i];
	      mod_syscall_addr = 1;
	      /*
	      sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
			       status, MSG_KERN_POLICY,
			       kmem_call_table[i],
			       sh_syscalls[i].addr,
			       i, _(sh_syscalls[i].name)
			       );
	      */
	      sh_syscalls[i].addr = kmem_call_table[i];
	    }
#endif


	  /* Check the code at syscall address
	   *
	   * Stored(old) is sh_syscalls[]
	   */
#ifdef SH_SYSCALL_CODE
	  if ( (mod_syscall_addr == 0) && 
	       ((sh_syscalls[i].code[0] != kmem_code_table[i][0]) || 
		(sh_syscalls[i].code[1] != kmem_code_table[i][1]))
	       )
	    {
	      mtime_old = sh_syscalls[i].code[0];
	      mtime_new = kmem_code_table[i][0];
	      ctime_old = sh_syscalls[i].code[1];
	      ctime_new = kmem_code_table[i][1];
	      mod_syscall_code = 1;

#ifdef SH_USE_LKM
	      if (i == copy_if_next)
		{
		  mod_syscall_code =  0;
		  copy_if_next     = -1;
		}
	      if (i == copy_if_next_64)
		{
		  mod_syscall_code =  0;
		  copy_if_next_64  = -1;
		}
#endif

	      /*
	      sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
			       status, MSG_KERN_POL_CO,
			       kmem_code_table[i][0],  kmem_code_table[i][1],
			       sh_syscalls[i].code[0], sh_syscalls[i].code[1],
			       i, _(sh_syscalls[i].name)
			       );
	      */
	      sh_syscalls[i].code[0] = kmem_code_table[i][0];
	      sh_syscalls[i].code[1] = kmem_code_table[i][1];
	    }
#endif
	  /*
	   * Build the error message, if something has been
	   * detected.
	   */
	  if ((mod_syscall_addr != 0) || (mod_syscall_code != 0))
	    {
#ifdef SH_USE_XML
	      sl_snprintf(tmp, 128, "path=\"K_%s_%04d\" ", 
			  _("syscall"), i);
#else
	      sl_snprintf(tmp, 128, "path=<K_%s_%04d>, ", 
			  _("syscall"), i);
#endif
	      sl_strlcpy(msg, tmp, SH_BUFSIZE);

	      if (mod_syscall_addr != 0)
		{
		  sl_snprintf(tmp, 128, sh_hash_size_format(),
			      size_old, size_new);
		  sl_strlcat(msg, tmp, SH_BUFSIZE); 
		}
	      if (mod_syscall_code != 0)
		{
		  sl_strlcpy (timstr_o, sh_unix_gmttime (ctime_old), 32);
		  sl_strlcpy (timstr_n, sh_unix_gmttime (ctime_new), 32);
#ifdef SH_USE_XML
		  sl_snprintf(tmp, 128, 
			      _("ctime_old=\"%s\" ctime_new=\"%s\" "), 
			      timstr_o, timstr_n);
#else
		  sl_snprintf(tmp, 128, 
			      _("ctime_old=<%s>, ctime_new=<%s>, "), 
			      timstr_o, timstr_n);
#endif
		  sl_strlcat(msg, tmp, SH_BUFSIZE); 
		  sl_strlcpy (timstr_o, sh_unix_gmttime (mtime_old), 32);
		  sl_strlcpy (timstr_n, sh_unix_gmttime (mtime_new), 32);
#ifdef SH_USE_XML
		  sl_snprintf(tmp, 128, 
			      _("mtime_old=\"%s\" mtime_new=\"%s\" "), 
			      timstr_o, timstr_n);
#else
		  sl_snprintf(tmp, 128, 
			      _("mtime_old=<%s>, mtime_new=<%s> "), 
			      timstr_o, timstr_n);
#endif
		  sl_strlcat(msg, tmp, SH_BUFSIZE); 
		}
	      sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
			       status, MSG_KERN_SYSCALL,
			       i, _(sh_syscalls[i].name), msg);
	      mod_syscall_addr = 0;
	      mod_syscall_code = 0;
	    }
	}
    }

#ifdef SH_IDT_TABLE
  if (ShKernIDT == S_TRUE)
    {
      if (sh.flag.checkSum == SH_CHECK_INIT || sh.flag.update == S_TRUE)
	{
	  datasize = 8;
	  for (j = 0; j < SH_MAXIDT; ++j) 
	    {
	      sh_kern_db_syscall (j, _("idt_table"), 
				  &sh_idt_table[j*8], 
				  &store0, &store1, &store2,
				  &datasize, 0);
	    }
	}

      if ((sh.flag.checkSum != SH_CHECK_INIT) || 
	  (sh.flag.update == S_TRUE && is_init == 1))
	{
	  /* Check the Interrupt Descriptor Table
	   *
	   * Stored(old) is idt_table[]
	   */
	  for (j = 0; j < SH_MAXIDT; ++j)
	    {
	      i = j * 8;
	  
	      sh_idt_offset_lo = *((unsigned short *) &sh_idt_table[i]);
	      sh_idt_selector  = *((unsigned short *) &sh_idt_table[i+2]);
	      sh_idt_reserved  = (unsigned char) sh_idt_table[i+4];
	      sh_idt_flag      = (unsigned char) sh_idt_table[i+5];
	      sh_idt_offset_hi = *((unsigned short *) &sh_idt_table[i+6]);
	      sh_idt_iaddr = (unsigned long)(sh_idt_offset_hi << 16) 
		+ sh_idt_offset_lo;
	      
	      if (sh_idt_iaddr == 0)
		{
		  sh_idt_table[i+2] = '\0';
		  sh_idt_table[i+3] = '\0';
		  sh_idt_table[i+5] = '\0';

		  idt_offset_lo = *((unsigned short *) &idt_table[i]);
		  idt_offset_hi = *((unsigned short *) &idt_table[i+6]);
		  idt_iaddr = (unsigned long)(idt_offset_hi << 16) 
		    + idt_offset_lo;
		  if (idt_iaddr == 0)
		    {
		      idt_table[i+2] = '\0';
		      idt_table[i+3] = '\0';
		      idt_table[i+5] = '\0';
		    }
		  
		}
	  
	      if (memcmp(&sh_idt_table[i], &idt_table[i], 8) != 0)
		{
		  
		  idt_offset_lo = *((unsigned short *) &idt_table[i]);
		  idt_selector  = *((unsigned short *) &idt_table[i+2]);
		  idt_reserved  = (unsigned char) idt_table[i+4];
		  idt_flag      = (unsigned char) idt_table[i+5];
		  idt_offset_hi = *((unsigned short *) &idt_table[i+6]);
		  idt_iaddr = (unsigned long)(idt_offset_hi << 16) 
		    + idt_offset_lo;
	      
		  if (idt_iaddr != 0)
		    {
		      if (idt_flag & 64) { dpl = 3; }
		      else               { dpl = 0; }
		      if (idt_flag & 1)  { 
			if (dpl == 3) idt_type = 'S'; 
			else idt_type = 'T'; }
		      else               { idt_type = 'I'; }
		    }
		  else { dpl = -1; idt_type = 'U'; }
		  
		  if (sh_idt_iaddr != 0)
		    {
		      if (sh_idt_flag & 64) { sh_dpl = 3; }
		      else               { sh_dpl = 0; }
		      if (sh_idt_flag & 1)  { 
			if (sh_dpl == 3) sh_idt_type = 'S'; 
			else sh_idt_type = 'T'; }
		      else               { sh_idt_type = 'I'; }
		    }
		  else { sh_dpl = -1; sh_idt_type = 'U'; }
		  
#ifdef SH_USE_XML
		  sl_snprintf(tmp, 128, "path=\"K_%s_%04d\" ", 
			      _("idt_table"), j);
#else
		  sl_snprintf(tmp, 128, "path=<K_%s_%04d> ", 
			      _("idt_table"), j);
#endif
		  sl_strlcpy(msg, tmp, SH_BUFSIZE);

		  linkpath_old = SH_ALLOC(32);
		  linkpath_new = SH_ALLOC(32);
		  for (k = 0; k < 8; ++k)
		    {
		      p = sh_kern_charhex (idt_table[i+k]);
		      linkpath_old[2*k]   = p[0];
		      linkpath_old[2*k+1] = p[1];
		      linkpath_old[2*k+2] = '\0';
		    }
		  for (k = 0; k < 8; ++k)
		    {
		      p = sh_kern_charhex (sh_idt_table[i+k]);
		      linkpath_new[2*k]   = p[0];
		      linkpath_new[2*k+1] = p[1];
		      linkpath_new[2*k+2] = '\0';
		    }
#ifdef SH_USE_XML
		  sl_snprintf(tmp, 128,
			      _("link_old=\"%s\" link_new=\"%s\" "), 
			      linkpath_old, linkpath_new);
#else
		  sl_snprintf(tmp, 128,
			      _("link_old=<%s> link_new=<%s> "), 
			      linkpath_old, linkpath_new);
#endif
		  sl_strlcat(msg, tmp, SH_BUFSIZE);

		  sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
				   status, MSG_KERN_IDT,
				   j, 
				   sh_idt_iaddr, sh_strseg(sh_idt_selector), 
				   (int) sh_dpl, sh_idt_type, 
				   idt_iaddr, sh_strseg(idt_selector),
				   (int) dpl, idt_type, msg);
		  
		  SH_FREE(linkpath_old);
		  SH_FREE(linkpath_new);

		  memcpy(&idt_table[i], &sh_idt_table[i], 8);
		}
	    }
	}
    }
#endif

  SL_RETURN( (0), _("sh_kern_check_internal"));
}
/* ifdef HOST_IS_LINUX */
#else

#include <err.h>
#include <kvm.h>
#include <nlist.h>

/* not OpenBSD */
#if defined(HOST_IS_FREEBSD)
#include <sys/sysent.h>
#endif

#include <sys/syscall.h>
#ifndef  SYS_MAXSYSCALL
#define  SYS_MAXSYSCALL	512
#endif

#ifdef __OpenBSD__
struct proc;
struct sysent {
	short sy_narg;
	short sy_argsize;
	int   (*sy_call)(struct proc *, void *, register_t *);
};
#endif

int sh_kern_data_init ()
{
  unsigned long store0 = 0;
  unsigned int  store1 = 0, store2 = 0;
  int           datasize, i;
  char        * databuf = NULL;

  /* syscall address and code
   */ 
  for (i = 0; i < SH_MAXCALLS; ++i) 
    {
      databuf = sh_kern_db_syscall (i, _("syscall"), 
				    NULL, &store0, &store1, &store2,
				    &datasize, 1);
      sh_syscalls[i].addr = store0;
      if (databuf != NULL) { SH_FREE(databuf); }
      if (store0 == 0) {
	sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
			_("syscall address not found in database"), 
			_("sh_kern_data_init"));
	return -1;
      }

      sh_syscalls[i].code[0] = (unsigned int) store1; 
      sh_syscalls[i].code[1] = (unsigned int) store2;
      if ((store1 == 0) || (store2 == 0)) {
	sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
			_("syscall code not found in database"), 
			_("sh_kern_data_init"));
	return -1;
      }

    }

  return 0;
}

int sh_kern_check_internal ()
{
  struct sysent  sy;
  kvm_t * kd;
  int     i;
  int     status = -1;
  char    errbuf[_POSIX2_LINE_MAX+1];
  struct  nlist * sys_list;
  struct  nlist list[2];

  unsigned long offset = 0L;
  unsigned int  syscall_code[2];  /* 8 bytes */
  unsigned long syscall_addr;

  unsigned long store0 = 0;
  unsigned int  store1 = 0, store2 = 0;

  UINT64        size_old  = 0, size_new = 0;
  UINT64        mtime_old = 0, mtime_new = 0;
  UINT64        ctime_old = 0, ctime_new = 0;
  char          tmp[128];
  char          msg[2*SH_BUFSIZE];
  char timstr_o[32];
  char timstr_n[32];

  static int is_init = 0;

  SL_ENTER(_("sh_kern_check_internal"));

  if (is_init == 0)
    { 
      if (sh.flag.checkSum != SH_CHECK_INIT && sh.flag.update != S_TRUE)
	{
	  if (0 == sh_kern_data_init()) {
	    is_init = 1;
	  } else {
	    sh_error_handle (ShKernSeverity, FIL__, __LINE__, status, 
			     MSG_E_SUBGEN,
			     _("could not initialize - switching off"),
			     _("kern_check_internal") );
	    ShKernActive = S_FALSE;
	    SL_RETURN( (-1), _("sh_kern_check_internal"));
	  }
	}
      else if ((sh.flag.checkSum == SH_CHECK_INIT ||
		sh.flag.checkSum == SH_CHECK_CHECK) && 
	       (sh.flag.update == S_TRUE))
	{	
	  if (0 == sh_kern_data_init()) {
	    is_init = 1;
	  } else {
	    sh_error_handle (ShKernSeverity, FIL__, __LINE__, status, 
			     MSG_E_SUBGEN,
			     _("no or incomplete data in baseline database"),
			     _("kern_check_internal") );
	  }
	}
    }

  /* defined, but not used
   */
  ShKernDelay    = 0;
   
  list[0].n_name = "_sysent";
  list[1].n_name = NULL;

  kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
  if (!kd)
    {
      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
		       errbuf,
		       _("kvm_openfiles") );
      SL_RETURN( (-1), _("sh_kern_check_internal"));
    }

  i = kvm_nlist(kd, list);
  if (i == -1)
    {
      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
		       kvm_geterr(kd),
		       _("kvm_nlist (_sysent)") );
      kvm_close(kd);
      SL_RETURN( (-1), _("sh_kern_check_internal"));
    }

  sys_list = SH_ALLOC((SYS_MAXSYSCALL+1) * sizeof(struct nlist));

  for (i = 0; i < SH_MAXCALLS; ++i)
    sys_list[i].n_name = sh_syscalls[i].name;
  sys_list[SH_MAXCALLS].n_name = NULL;

  i = kvm_nlist(kd, sys_list);
  if (i == -1)
    {
      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
		       kvm_geterr(kd),
		       _("kvm_nlist (syscalls)") );
      kvm_close(kd);
      SH_FREE(sys_list);
      SL_RETURN( (-1), _("sh_kern_check_internal"));
    }
  else if (i > 0)
    {
      sl_snprintf(tmp, 128,
                  _("%d invalid syscalls"), i);
      /*
      for (i = 0; i < SH_MAXCALLS; ++i) {
        if (sys_list[i].n_type == 0 && sys_list[i].n_value == 0)
          fprintf(stderr, "invalid: [%3d] %s\n", i, sh_syscalls[i].name);
      }
      */
      sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN,
                       tmp,
                       _("kvm_nlist (syscalls)") );
    }

  /* Check the individual syscalls
   *
   * Stored(old) is sh_syscalls[] array.
   */
  if (sh.flag.checkSum == SH_CHECK_INIT || sh.flag.update == S_TRUE)
    {
      for (i = 0; i < SH_MAXCALLS; ++i) 
	{
	  if (sh_syscalls[i].name == NULL)
	    {
	      sl_snprintf(tmp, 128, 
			  _("too few entries in sh_syscalls[]: have %d, expect %d"), 
			  i, SH_MAXCALLS);

	      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
			       tmp,
			       _("sh_kern_check_internal") );
	      break;
	    }

	  /* read address of syscall from sysent table
	   */
	  offset = list[0].n_value + (i*sizeof(struct sysent));
	  if (kvm_read(kd, offset, &sy, sizeof(struct sysent)) < 0)
	    {
	      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
			       kvm_geterr(kd),
			       _("kvm_read (syscall table)") );
	      kvm_close(kd);
	      SH_FREE(sys_list);
	      SL_RETURN( (-1), _("sh_kern_check_internal"));
	    }
	  syscall_addr = (unsigned long) sy.sy_call;
	  store0 = syscall_addr;
	  
	  /* read the syscall code
	   */
	  if(kvm_read(kd, (unsigned int) sy.sy_call, &(syscall_code[0]), 
		      2 * sizeof(unsigned int)) < 0)
	    {
	      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
			       kvm_geterr(kd),
			       _("kvm_read (syscall code)") );
	      kvm_close(kd);
	      SH_FREE(sys_list);
	      SL_RETURN( (-1), _("sh_kern_check_internal"));
	    }
	  store1 = syscall_code[0]; store2 = syscall_code[1];
	  
	  sh_kern_db_syscall (i, _("syscall"), 
			      NULL, &store0, &store1, &store2,
			      0, 0);
	}
    }

  if ((sh.flag.checkSum != SH_CHECK_INIT) || 
      (sh.flag.update == S_TRUE && is_init == 1))
    {
      for (i = 0; i < SH_MAXCALLS; ++i)
	{
	  if (sh_syscalls[i].name == NULL)
	    {
	      sl_snprintf(tmp, 128, 
			  _("too few entries in sh_syscalls[]: have %d, expect %d"), 
			  i, SH_MAXCALLS);

	      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
			       tmp,
			       _("sh_kern_check_internal") );
	      break;
	    }
	  
	  /* read address of syscall from sysent table
	   */
	  offset = list[0].n_value + (i*sizeof(struct sysent));
	  if (kvm_read(kd, offset, &sy, sizeof(struct sysent)) < 0)
	    {
	      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
			       kvm_geterr(kd),
			       _("kvm_read (syscall table)") );
	      kvm_close(kd);
	      SH_FREE(sys_list);
	      SL_RETURN( (-1), _("sh_kern_check_internal"));
	    }
	  syscall_addr = (unsigned long) sy.sy_call;
	  
	  if (sh_syscalls[i].addr != syscall_addr)
	    {
#ifdef SH_USE_XML
	      sl_snprintf(tmp, 128, "path=\"K_%s_%04d\" ", 
			  _("syscall"), i);
#else
	      sl_snprintf(tmp, 128, "path=<K_%s_%04d>, ", 
			  _("syscall"), i);
#endif
	      sl_strlcpy(msg, tmp, SH_BUFSIZE);

	      size_old = sh_syscalls[i].addr; 
	      size_new = syscall_addr;
	      sl_snprintf(tmp, 128, sh_hash_size_format(),
			  size_old, size_new);
	      sl_strlcat(msg, tmp, SH_BUFSIZE);
 
	      sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
			       status, MSG_KERN_SYSCALL,
			       /*
			       syscall_addr,
			       sh_syscalls[i].addr,
			       */
			       i, _(sh_syscalls[i].name),
			       msg);
	      sh_syscalls[i].addr = syscall_addr;
	    }
	  else
	    {    
	      /* read the syscall code
	       */
	      if(kvm_read(kd, (unsigned int) sy.sy_call, &(syscall_code[0]), 
			  2 * sizeof(unsigned int)) < 0)
		{
		  sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
				   kvm_geterr(kd),
				   _("kvm_read (syscall code)") );
		  kvm_close(kd);
		  SH_FREE(sys_list);
		  SL_RETURN( (-1), _("sh_kern_check_internal"));
		}
	      
	      if (sh_syscalls[i].code[0] != syscall_code[0] || 
		  sh_syscalls[i].code[1] != syscall_code[1])
		{
		  mtime_old = sh_syscalls[i].code[0];
		  mtime_new = syscall_code[0];
		  ctime_old = sh_syscalls[i].code[1];
		  ctime_new = syscall_code[1];

#ifdef SH_USE_XML
		  sl_snprintf(tmp, 128, "path=\"K_%s_%04d\" ", 
			      _("syscall"), i);
#else
		  sl_snprintf(tmp, 128, "path=<K_%s_%04d>, ", 
			      _("syscall"), i);
#endif
		  sl_strlcpy(msg, tmp, SH_BUFSIZE);

		  sl_strlcpy (timstr_o, sh_unix_gmttime (ctime_old), 32);
		  sl_strlcpy (timstr_n, sh_unix_gmttime (ctime_new), 32);
#ifdef SH_USE_XML
		  sl_snprintf(tmp, 128, 
			      _("ctime_old=\"%s\" ctime_new=\"%s\" "), 
			      timstr_o, timstr_n);
#else
		  sl_snprintf(tmp, 128, 
			      _("ctime_old=<%s>, ctime_new=<%s>, "), 
			      timstr_o, timstr_n);
#endif
		  sl_strlcat(msg, tmp, SH_BUFSIZE); 
		  sl_strlcpy (timstr_o, sh_unix_gmttime (mtime_old), 32);
		  sl_strlcpy (timstr_n, sh_unix_gmttime (mtime_new), 32);
#ifdef SH_USE_XML
		  sl_snprintf(tmp, 128, 
			      _("mtime_old=\"%s\" mtime_new=\"%s\" "), 
			      timstr_o, timstr_n);
#else
		  sl_snprintf(tmp, 128, 
			      _("mtime_old=<%s>, mtime_new=<%s> "), 
			      timstr_o, timstr_n);
#endif
		  sl_strlcat(msg, tmp, SH_BUFSIZE); 

		  sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
				   status, MSG_KERN_SYSCALL,
				   /*
				   syscall_code[0],  syscall_code[1],
				   sh_syscalls[i].code[0], sh_syscalls[i].code[1],
				   */
				   i, _(sh_syscalls[i].name),
				   msg);
		  sh_syscalls[i].code[0] = syscall_code[0];
		  sh_syscalls[i].code[1] = syscall_code[1];
		}
	    }
	}
    }
  SH_FREE(sys_list);
  if(kvm_close(kd) < 0)
    {
      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
                       kvm_geterr(kd),
                       _("kvm_close") );
      exit(EXIT_FAILURE);
    }

  SL_RETURN( (0), _("sh_kern_check_internal"));
}

#endif

/*************
 *
 * module init
 *
 *************/
#if defined(HOST_IS_LINUX)
#include <sys/utsname.h>
#endif

static int AddressReconf = 0;

int sh_kern_init ()
{
#if defined(HOST_IS_LINUX)
  struct utsname buf;
  char         * str;
#endif

  SL_ENTER(_("sh_kern_init"));
  if (ShKernActive == S_FALSE)
    SL_RETURN( (-1), _("sh_kern_init"));

#if defined(HOST_IS_LINUX)
  uname(&buf);

  if ((AddressReconf < 5) && (0 != strcmp(SH_KERNEL_VERSION, buf.release)))
    {
      str = SH_ALLOC(256);
      sl_snprintf(str, 256, 
		  "Compiled for kernel %s, but current kernel is %s, and kernel addresses have not been re-configured",
		  SH_KERNEL_VERSION, buf.release);
      sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, EINVAL, MSG_E_SUBGEN,
		       str,
		       _("kern_check") );
      SH_FREE(str);
      ShKernActive = S_FALSE;
      SL_RETURN( (-1), _("sh_kern_init"));
    }
#endif

  lastcheck  = time (NULL);
  if (sh.flag.checkSum != SH_CHECK_INIT)
    {
      sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, 0, MSG_E_SUBGEN,
		       _("Checking kernel syscalls"),
		       _("kern_check") );
    }
  sh_kern_check_internal ();
  SL_RETURN( (0), _("sh_kern_init"));
}

/*************
 *
 * module cleanup
 *
 *************/
int sh_kern_end ()
{
  return (0);
}


/*************
 *
 * module timer
 *
 *************/
int sh_kern_timer (time_t tcurrent)
{
  if (ShKernActive == S_FALSE)
    return 0;

  if ((int) (tcurrent - lastcheck) >= ShKernInterval)
    {
      lastcheck  = tcurrent;
      return (-1);
    }
  return 0;
}

/*************
 *
 * module check
 *
 *************/
int sh_kern_check ()
{
  sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, EINVAL, MSG_E_SUBGEN,
		   _("Checking kernel syscalls"),
		   _("kern_check") );
  return (sh_kern_check_internal ());
}

/*************
 *
 * module setup
 *
 *************/

int sh_kern_set_severity  (char * c)
{
  char tmp[32];
  tmp[0] = '='; tmp[1] = '\0';
  sl_strlcat (tmp, c, 32);
  sh_error_set_level (tmp, &ShKernSeverity);
  return 0;
}

int sh_kern_set_timer (char * c)
{
  long val;

  SL_ENTER(_("sh_kern_set_timer"));

  val = strtol (c, (char **)NULL, 10);
  if (val <= 0)
    sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
                      _("kern timer"), c);

  val = (val <= 0 ? 60 : val);

  ShKernInterval = (time_t) val;
  SL_RETURN( 0, _("sh_kern_set_timer"));
}

int sh_kern_set_activate (char * c)
{
  int i;
  SL_ENTER(_("sh_kern_set_activate"));
  i = sh_util_flagval(c, &ShKernActive);
  SL_RETURN(i, _("sh_kern_set_activate"));
}

int sh_kern_set_idt (char * c)
{
  int i;
  SL_ENTER(_("sh_kern_set_idt"));
  i = sh_util_flagval(c, &ShKernIDT);
  SL_RETURN(i, _("sh_kern_set_idt"));
}

int sh_kern_set_sc_addr (char * c)
{
  char * endptr;
  unsigned long value;

  SL_ENTER(_("sh_kern_set_sc_addr"));
  errno = 0;
  value = strtoul(c, &endptr, 16);
  if ((ULONG_MAX == value) && (errno == ERANGE))
    {
      SL_RETURN((-1), _("sh_kern_set_sc_addr"));
    }
  if ((*c == '\0') || (*endptr != '\0'))
    {
      SL_RETURN((-1), _("sh_kern_set_sc_addr"));
    }
  system_call_addr = value;
  ++AddressReconf;
  SL_RETURN((0), _("sh_kern_set_sc_addr"));
}

int sh_kern_set_sct_addr (char * c)
{
  char * endptr;
  unsigned long value;

  SL_ENTER(_("sh_kern_set_sct_addr"));
  errno = 0;
  value = strtoul(c, &endptr, 16);
  if ((ULONG_MAX == value) && (errno == ERANGE))
    {
      SL_RETURN((-1), _("sh_kern_set_sct_addr"));
    }
  if ((*c == '\0') || (*endptr != '\0'))
    {
      SL_RETURN((-1), _("sh_kern_set_sct_addr"));
    }
  kaddr = (unsigned int) value;
  ++AddressReconf;
  SL_RETURN((0), _("sh_kern_set_sct_addr"));
}

int sh_kern_set_proc_root (char * c)
{
  char * endptr;
  unsigned long value;

  SL_ENTER(_("sh_kern_set_proc_root"));
  errno = 0;
  value = strtoul(c, &endptr, 16);
  if ((ULONG_MAX == value) && (errno == ERANGE))
    {
      SL_RETURN((-1), _("sh_kern_set_proc_root"));
    }
  if ((*c == '\0') || (*endptr != '\0'))
    {
      SL_RETURN((-1), _("sh_kern_set_proc_root"));
    }
  
  proc_root = value;
  ++AddressReconf;
  SL_RETURN((0), _("sh_kern_set_proc_root"));
}

int sh_kern_set_proc_root_iops (char * c)
{
  char * endptr;
  unsigned long value;

  SL_ENTER(_("sh_kern_set_proc_root_iops"));
  errno = 0;
  value = strtoul(c, &endptr, 16);
  if ((ULONG_MAX == value) && (errno == ERANGE))
    {
      SL_RETURN((-1), _("sh_kern_set_proc_root_iops"));
    }
  if ((*c == '\0') || (*endptr != '\0'))
    {
      SL_RETURN((-1), _("sh_kern_set_proc_root_iops"));
    }
  
  proc_root_iops = value;
  ++AddressReconf;
  SL_RETURN((0), _("sh_kern_set_proc_root_iops"));
}

int sh_kern_set_proc_root_lookup (char * c)
{
  char * endptr;
  unsigned long value;

  SL_ENTER(_("sh_kern_set_proc_root_lookup"));
  errno = 0;
  value = strtoul(c, &endptr, 16);
  if ((ULONG_MAX == value) && (errno == ERANGE))
    {
      SL_RETURN((-1), _("sh_kern_set_proc_root_lookup"));
    }
  if ((*c == '\0') || (*endptr != '\0'))
    {
      SL_RETURN((-1), _("sh_kern_set_proc_root_lookup"));
    }
  proc_root_lookup = value;
  ++AddressReconf;
  SL_RETURN((0), _("sh_kern_set_proc_root_lookup"));
}

#endif

/* #ifdef SH_USE_UTMP */
#endif



