source: trunk/src/sh_kern.c @ 362

Last change on this file since 362 was 362, checked in by katerina, 10 years ago

Fix for ticket #267 (Multiple compiler warnings with gcc 4.6.1).

File size: 50.8 KB
Line 
1/* SAMHAIN file system integrity testing                                   */
2/* Copyright (C) 2001 Rainer Wichmann                                      */
3/*                                                                         */
4/*  This program is free software; you can redistribute it                 */
5/*  and/or modify                                                          */
6/*  it under the terms of the GNU General Public License as                */
7/*  published by                                                           */
8/*  the Free Software Foundation; either version 2 of the License, or      */
9/*  (at your option) any later version.                                    */
10/*                                                                         */
11/*  This program is distributed in the hope that it will be useful,        */
12/*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
13/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
14/*  GNU General Public License for more details.                           */
15/*                                                                         */
16/*  You should have received a copy of the GNU General Public License      */
17/*  along with this program; if not, write to the Free Software            */
18/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
19
20
21#include "config_xor.h"
22
23#define SH_SYSCALL_CODE
24
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <fcntl.h>
32#include <unistd.h>
33#include <errno.h>
34#include <limits.h>
35#include <sys/wait.h>
36#include <signal.h>
37#include <sys/mman.h>
38
39
40#ifdef SH_USE_KERN
41
42#undef  FIL__
43#define FIL__  _("sh_kern.c")
44
45#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
46
47#if TIME_WITH_SYS_TIME
48#include <sys/time.h>
49#include <time.h>
50#else
51#if HAVE_SYS_TIME_H
52#include <sys/time.h>
53#else
54#include <time.h>
55#endif
56#endif
57
58
59#include "samhain.h"
60#include "sh_pthread.h"
61#include "sh_utils.h"
62#include "sh_error.h"
63#include "sh_modules.h"
64#include "sh_kern.h"
65#include "sh_ks_xor.h"
66
67#include "sh_unix.h"
68#include "sh_hash.h"
69
70
71
72sh_rconf sh_kern_table[] = {
73  {
74    N_("severitykernel"),
75    sh_kern_set_severity
76  },
77  {
78    N_("kernelcheckactive"),
79    sh_kern_set_activate
80  },
81  {
82    N_("kernelcheckinterval"),
83    sh_kern_set_timer
84  },
85  {
86    N_("kernelcheckidt"),
87    sh_kern_set_idt
88  },
89  {
90    N_("kernelcheckpci"),
91    sh_kern_set_pci
92  },
93  {
94    N_("kernelsystemcall"),
95    sh_kern_set_sc_addr
96  },
97  {
98    N_("kernelsyscalltable"),
99    sh_kern_set_sct_addr
100  },
101  {
102    N_("kernelprocrootlookup"),
103    sh_kern_set_proc_root_lookup
104  },
105 {
106    N_("kernelprocrootiops"),
107    sh_kern_set_proc_root_iops
108  },
109  {
110    N_("kernelprocroot"),
111    sh_kern_set_proc_root
112  },
113  {
114    NULL,
115    NULL
116  },
117};
118
119
120static time_t  lastcheck;
121static int     ShKernActive   = S_TRUE;
122static int     ShKernInterval = 300;
123static int     ShKernSeverity = SH_ERR_SEVERE;
124static int     ShKernDelay    = 100; /* milliseconds */
125static int     ShKernIDT      = S_TRUE;
126static int     ShKernPCI      = S_TRUE;
127
128/* The address of system_call
129 */
130#ifdef SH_SYS_CALL_ADDR
131static unsigned long system_call_addr = SH_SYS_CALL_ADDR;
132#else
133static unsigned long system_call_addr = 0;
134#endif
135
136/* The address of the sys_call_table
137 */
138#ifdef SH_SYS_CALL_TABLE
139static unsigned long  kaddr = SH_SYS_CALL_TABLE;
140#else
141static unsigned long  kaddr = 0;
142#endif
143
144#ifdef PROC_ROOT_LOC
145static unsigned long proc_root = PROC_ROOT_LOC;
146#else
147static unsigned long proc_root = 0;
148#endif
149#ifdef PROC_ROOT_IOPS_LOC
150static unsigned long proc_root_iops = PROC_ROOT_IOPS_LOC;
151#else
152static unsigned long proc_root_iops = 0;
153#endif
154#ifdef PROC_ROOT_LOOKUP_LOC
155static unsigned long proc_root_lookup = PROC_ROOT_LOOKUP_LOC;
156#else
157static unsigned long proc_root_lookup = 0;
158#endif
159
160/* This is the module 'reconfigure' function, which is a no-op.
161 */
162int sh_kern_null()
163{
164  return 0;
165}
166
167#define SH_KERN_DBPUSH 0
168#define SH_KERN_DBPOP  1
169
170char * sh_kern_db_syscall (int num, char * prefix,
171                           void * in_name, unsigned long * addr,
172                           unsigned int * code1, unsigned int * code2,
173                           int * size, int direction)
174{
175  char            path[128];
176  char          * p = NULL;
177
178  unsigned char * name = (unsigned char *) in_name;
179  struct store2db save;
180
181  sl_snprintf(path, 128, "K_%s_%04d", prefix, num);
182
183  memset(&save, '\0', sizeof(struct store2db));
184
185  if (direction == SH_KERN_DBPUSH) 
186    {
187      save.val0 = *addr;
188      save.val1 = *code1;
189      save.val2 = *code2;
190      save.str  = name;
191      save.size = (name == NULL) ? 0 : (*size);
192
193      sh_hash_push2db (path, &save);
194    }
195  else
196    {
197      p = sh_hash_db2pop (path, &save);
198
199      *addr  = (unsigned long) save.val0;
200      *code1 = (unsigned int)  save.val1;
201      *code2 = (unsigned int)  save.val2;
202
203      *size  = (int)           save.size;
204    }
205  return p;
206}
207
208static char * sh_kern_pathmsg (char * msg, size_t msg_len,
209                               int num, char * prefix,
210                               unsigned char * old, size_t old_len,
211                               unsigned char * new, size_t new_len)
212{
213  size_t k;
214  char   tmp[128];
215  char  *p;
216  char  *linkpath_old;
217  char  *linkpath_new;
218  char   i2h[2];
219
220#ifdef SH_USE_XML
221  sl_snprintf(tmp, sizeof(tmp), _("path=\"K_%s_%04d\" "), 
222              prefix, num);
223#else
224  sl_snprintf(tmp, sizeof(tmp), _("path=<K_%s_%04d> "), 
225              prefix, num);
226#endif
227  sl_strlcpy(msg, tmp, msg_len);
228
229  if (SL_TRUE == sl_ok_muls(old_len, 2) &&
230      SL_TRUE == sl_ok_adds(old_len * 2, 1))
231    linkpath_old = SH_ALLOC(old_len * 2 + 1);
232  else
233    return msg;
234
235  if (SL_TRUE == sl_ok_muls(new_len, 2) &&
236      SL_TRUE == sl_ok_adds(new_len * 2, 1))
237    linkpath_new = SH_ALLOC(new_len * 2 + 1);
238  else
239    return msg;
240
241  for (k = 0; k < old_len; ++k)
242    {
243      p = sh_util_charhex (old[k], i2h);
244      linkpath_old[2*k]   = p[0];
245      linkpath_old[2*k+1] = p[1];
246      linkpath_old[2*k+2] = '\0';
247    }
248
249  for (k = 0; k < new_len; ++k)
250    {
251      p = sh_util_charhex (new[k], i2h);
252      linkpath_new[2*k]   = p[0];
253      linkpath_new[2*k+1] = p[1];
254      linkpath_new[2*k+2] = '\0';
255   
256}
257#ifdef SH_USE_XML
258  sl_strlcat(msg, _("link_old=\""),    msg_len);
259  sl_strlcat(msg, linkpath_old,        msg_len);
260  sl_strlcat(msg, _("\" link_new=\""), msg_len);
261  sl_strlcat(msg, linkpath_new,        msg_len);
262  sl_strlcat(msg, _("\""),             msg_len);
263#else
264  sl_strlcat(msg, _("link_old=<"),     msg_len);
265  sl_strlcat(msg, linkpath_old,        msg_len);
266  sl_strlcat(msg, _(">, link_new=<"),  msg_len);
267  sl_strlcat(msg, linkpath_new,        msg_len);
268  sl_strlcat(msg, _(">"),              msg_len);
269#endif
270
271  SH_FREE(linkpath_old);
272  SH_FREE(linkpath_new);
273
274  return msg;
275}
276 
277#ifdef HOST_IS_LINUX
278
279#ifndef KERNEL_VERSION
280#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
281#endif
282
283/*
284 * Interrupt Descriptor Table
285 */
286#ifdef HAVE_ASM_SEGMENT_H
287#include <asm/segment.h>
288#endif
289
290#define SH_MAXIDT   256
291
292static unsigned char sh_idt_table[SH_MAXIDT * 8];
293
294static char * sh_strseg(unsigned short segment)
295{
296  static int flip = 0;
297  static char one[32];
298  static char two[32];
299
300  switch (segment) {
301#ifdef __KERNEL_CS
302  case __KERNEL_CS:
303    return _("KERNEL_CS");
304#endif
305#ifdef __KERNEL_DS
306  case __KERNEL_DS:
307    return _("KERNEL_DS");
308#endif
309#ifdef __USER_CS
310  case __USER_CS:
311    return _("USER_CS");
312#endif
313#ifdef __USER_DS
314  case __USER_DS:
315    return _("USER_DS");
316#endif
317  default:
318    if (flip == 0)
319      {
320        snprintf(one, sizeof(one), "%hX", segment);
321        flip = 1;
322        return one;
323      }
324    else
325      {
326        snprintf(two, sizeof(two), "%hX", segment);
327        flip = 0;
328        return two;
329      }
330  }
331}
332
333
334static int sh_kern_data_init ()
335{
336  unsigned long store0 = 0;
337  unsigned int  store1 = 0, store2 = 0;
338  int           datasize, i, j;
339  char        * databuf;
340
341  /* system_call code
342   */
343  databuf = sh_kern_db_syscall (0, _("system_call"), 
344                                NULL, &store0, &store1, &store2,
345                                &datasize, SH_KERN_DBPOP);
346  if (datasize == sizeof(system_call_code))
347    {
348      memcpy (system_call_code, databuf, sizeof(system_call_code));
349      SH_FREE(databuf);
350    }
351  else
352    {
353      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
354                      _("system_call_code not found in database"), 
355                      _("sh_kern_data_init"));
356      return -1;
357    }
358
359  /* syscall address and code
360   */ 
361  for (i = 0; i < SH_MAXCALLS; ++i) 
362    {
363      databuf = sh_kern_db_syscall (i, _("syscall"), 
364                                    NULL, &store0, &store1, &store2,
365                                    &datasize, SH_KERN_DBPOP);
366      sh_syscalls[i].addr = store0;
367      if (store0 == 0) {
368        sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, i, MSG_E_SUBGEN,
369                        _("syscall address not found in database"), 
370                        _("sh_kern_data_init"));
371        return -1;
372      }
373
374      sh_syscalls[i].code[0] = (unsigned int) store1; 
375      sh_syscalls[i].code[1] = (unsigned int) store2;
376      if ((store1 == 0) || (store2 == 0)) {
377        sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, i, MSG_E_SUBGEN,
378                        _("syscall code not found in database"), 
379                        _("sh_kern_data_init"));
380      }
381
382      if (databuf != NULL) {
383        SH_FREE(databuf);
384      }
385     
386    }
387
388  if (ShKernIDT == S_TRUE)
389    {
390      for (j = 0; j < SH_MAXIDT; ++j) 
391        {
392          databuf = sh_kern_db_syscall (j, _("idt_table"), 
393                                        NULL, 
394                                        &store0, &store1, &store2,
395                                        &datasize, SH_KERN_DBPOP);
396          if (datasize == 8) {
397            memcpy(&idt_table[j*8], databuf, 8);
398            SH_FREE(databuf);
399          } else {
400            sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, j, MSG_E_SUBGEN,
401                            _("idt table not found in database"), 
402                            _("sh_kern_data_init"));
403            return -1;
404          }
405        }
406    }
407
408  return 0;
409}
410
411
412/*
413 * Defined in include/linux/fs.h
414 */
415
416/* Here. we are only interested in 'lookup'. I.e. the struct
417 * must be <= the real one, and 'lookup' must be at the
418 * correct position.
419 */
420struct inode_operations {
421  int (*create) (int *,int *,int);
422  int * (*lookup) (int *,int *);
423  int (*link) (int *,int *,int *);
424  int (*unlink) (int *,int *);
425  int (*symlink) (int *,int *,const char *);
426  int (*mkdir) (int *,int *,int);
427  int (*rmdir) (int *,int *);
428  int (*mknod) (int *,int *,int,int);
429  int (*rename) (int *, int *,
430                 int *, int *);
431  /* flawfinder: ignore */
432  int (*readlink) (int *, char *,int);
433  int (*follow_link) (int *, int *);
434  void (*truncate) (int *);
435  int (*permission) (int *, int);
436  int (*revalidate) (int *);
437  /*
438    int (*setattr) (int *, int *);
439    int (*getattr) (int *, int *);
440    int (*setxattr) (int *, const char *, void *, size_t, int);
441    ssize_t (*getxattr) (int *, const char *, void *, size_t);
442    ssize_t (*listxattr) (int *, char *, size_t);
443    int (*removexattr) (int *, const char *);
444  */
445};
446
447/*
448 * this one is just for dummy purposes
449 */
450struct file_operations {
451  int (*create) (int *,int *,int);
452};
453
454/* Defined in include/linux/proc_fs.h
455 * Here we are interested in the 'proc_iops' member.
456 */
457struct proc_dir_entry {
458  unsigned short low_ino;
459  unsigned short namelen;
460  const char * name;
461  mode_t mode;
462  nlink_t nlink;
463  uid_t uid;
464  gid_t gid;
465#if defined  TWO_SIX_SEVENTEEN_PLUS
466  /* size is loff_t in 2.6.17+ kernels */
467  unsigned long dummy; 
468#endif
469  unsigned long size;
470  struct inode_operations * proc_iops;
471  struct file_operations * proc_fops;
472  /*
473  get_info_t *get_info;
474  struct module *owner;
475  struct proc_dir_entry *next, *parent, *subdir;
476  void *data;
477  read_proc_t *read_proc;
478  write_proc_t *write_proc;
479  atomic_t count;         
480  int deleted; 
481  */         
482};
483
484
485static int sh_kern_kmem_read (int fd, unsigned long addr, 
486                              unsigned char * buf, int len)
487{
488  if (lseek(fd, addr, SEEK_SET) == (off_t) (-1))
489    {
490      return -1;
491    }
492  if (read(fd, buf, len) < 0)
493    {
494      return -1;
495    }
496  return 0;
497}
498
499static int sh_kern_read_data (int fd, unsigned long addr, 
500                              unsigned char * buf, size_t len)
501{
502  size_t    moff, roff;
503  size_t    sz;
504  char    * kmap;
505
506  /* next, try mmap()
507   */
508  sz = getpagesize(); /* unistd.h */
509
510  moff = ((size_t)(addr/sz)) * sz;                 /* lower page boundary */
511  roff = addr - moff;    /* off relative to lower address of mmapped area */
512
513  kmap = mmap(0, len+sz, PROT_READ, MAP_PRIVATE, fd, moff);/* sys/mman.h */
514
515  if (kmap == MAP_FAILED)
516    {
517      /* then, try read()
518       */
519      if (0 == sh_kern_kmem_read (fd, addr, buf, len))
520        return 0;
521
522      memset(buf, '\0', len);
523      return -1;
524    }
525
526  memcpy (buf, &kmap[roff], len);
527  return munmap(kmap, len+sz);
528}
529
530
531static int check_init (int * init_retval)
532{
533  static int is_init = 0;
534
535  SL_ENTER(_("check_init"));
536
537  if (is_init == 0)
538    {
539      if (sh.flag.checkSum != SH_CHECK_INIT && sh.flag.update != S_TRUE)
540        {
541          if (0 == sh_kern_data_init()) {
542            is_init = 1;
543          } else {
544            sh_error_handle (ShKernSeverity, FIL__, __LINE__, 1, 
545                             MSG_E_SUBGEN,
546                             _("could not initialize kernel check - switching off"),
547                             _("check_init") );
548            ShKernActive = S_FALSE;
549            *init_retval = is_init;
550            SL_RETURN( (-1), _("check_init"));
551          }
552        }
553      else if ((sh.flag.checkSum == SH_CHECK_INIT || 
554                sh.flag.checkSum == SH_CHECK_CHECK) && 
555               (sh.flag.update == S_TRUE))
556        {
557          if (0 == sh_kern_data_init()) {
558            is_init = 1;
559          } else {
560            sh_error_handle (SH_ERR_WARN, FIL__, __LINE__, 0, 
561                             MSG_E_SUBGEN,
562                             _("no or incomplete data in baseline database for kernel check"),
563                             _("check_init") );
564          }
565        }
566    }
567  *init_retval = is_init;
568  SL_RETURN( (0), _("check_init"));
569}
570
571#define SH_KERN_SIZ 512
572#define SH_KERN_SCC 256
573
574static void run_child(int kd, int mpipe[2])
575{
576  int j;
577
578  unsigned long kmem_call_table[SH_KERN_SIZ];
579  unsigned int  kmem_code_table[SH_KERN_SIZ][2];
580
581  unsigned char new_system_call_code[SH_KERN_SCC];
582
583  struct inode_operations proc_root_inode;
584  struct proc_dir_entry   proc_root_dir;
585
586  int status = sl_close_fd(FIL__, __LINE__, mpipe[0]);
587
588  setpgid(0, 0);
589         
590  /* Seek to the system call table (at kaddr) and read it into
591   * the kmem_call_table array
592   */
593  if(status == 0)
594    {
595      retry_msleep (0, ShKernDelay); /* milliseconds */
596     
597      if (sh_kern_read_data (kd, kaddr, 
598                             (unsigned char *) &kmem_call_table, 
599                             sizeof(kmem_call_table)))
600        {
601          status = -2;
602        }
603    }
604
605  /*
606   * Seek to the system call address (at sh_syscalls[j].addr) and
607   * read first 8 bytes into the array kmem_code_table[j][] (2 * unsigned int)
608   */
609  if(status == 0)
610    {
611      memset(kmem_code_table, 0, sizeof(kmem_code_table));
612      for (j = 0; j < SH_MAXCALLS; ++j) 
613        {
614          if (sh_syscalls[j].addr == 0UL) {
615            sh_syscalls[j].addr = kmem_call_table[j];
616          }
617
618          if (sh_syscalls[j].name == NULL || 
619              sh_syscalls[j].addr == 0UL)
620            break;
621
622          if ((sh.flag.checkSum == SH_CHECK_INIT || 
623               sh.flag.checkSum == SH_CHECK_CHECK) && 
624              (sh.flag.update == S_TRUE))
625            {
626              if (sh_kern_read_data (kd, kmem_call_table[j], 
627                                     (unsigned char *) &(kmem_code_table[j][0]),
628                                     2 * sizeof(unsigned int)))
629                status = -3;
630            }
631          else
632            {
633              if (sh_kern_read_data (kd, sh_syscalls[j].addr, 
634                                     (unsigned char *) &(kmem_code_table[j][0]),
635                                     2 * sizeof(unsigned int)))
636                status = -4;
637            }
638        }
639    }
640
641  if(status == 0)
642    {
643      /*
644       * Get the address and size of Interrupt Descriptor Table,
645       * and read the content into the global array sh_idt_table[]
646       */
647      struct {
648        char pad[6];
649        unsigned short size;
650        unsigned long  addr;
651      } idt;
652
653      __asm__ volatile ("sidt %0": "=m" (idt.size));
654
655      idt.size = (idt.size + 1)/8;
656     
657      if (idt.size > SH_MAXIDT)
658        idt.size = SH_MAXIDT;
659     
660      memset(sh_idt_table, '\0', SH_MAXIDT*8);
661      if (sh_kern_read_data (kd, idt.addr, 
662                             (unsigned char *) sh_idt_table, idt.size*8))
663        status = -5;
664    }
665
666  /*
667   * Seek to the system_call address (at system_call_addr) and
668   * read first 256 bytes into new_system_call_code[]
669   *
670   * system_call_addr is defined in the include file.
671   */
672  if(status == 0)
673    {
674      if (sh_kern_read_data (kd, system_call_addr, 
675                             (unsigned char *) new_system_call_code, 
676                             SH_KERN_SCC))
677        status = -6;
678    }
679 
680  /*
681   * Seek to proc_root and read the structure.
682   * Seek to proc_root_inode_operations and get the structure.
683   */
684  if(status == 0)
685    {
686      if (sh_kern_read_data (kd, proc_root, 
687                             (unsigned char *) &proc_root_dir, 
688                             sizeof(proc_root_dir)))
689        status = -7;
690    }
691/* 2.6.21 (((2) << 16) + ((6) << 8) + (21)) */
692#if SH_KERNEL_NUMBER < KERNEL_VERSION(2,6,21)
693  if(status == 0)
694    {
695      if (sh_kern_read_data (kd, proc_root_iops, 
696                             (unsigned char *) &proc_root_inode, 
697                             sizeof(proc_root_inode)))
698        status = -8;
699    }
700#else
701    memset(&proc_root_inode, '\0', sizeof(proc_root_inode));
702#endif
703 
704  /*
705   * Write out data to the pipe
706   */
707  status = write(mpipe[1], &status, sizeof(int));
708
709  if (status > 0)
710    status = write(mpipe[1], &kmem_call_table, sizeof(kmem_call_table));
711 
712  if(status > 0)
713    status = write(mpipe[1], &kmem_code_table, sizeof(kmem_code_table));
714 
715  if(status > 0)
716    status = write(mpipe[1], &sh_idt_table, sizeof(sh_idt_table));
717 
718  if(status > 0)
719    status = write(mpipe[1], new_system_call_code, SH_KERN_SCC);
720 
721  if(status > 0)
722    status = write(mpipe[1], &proc_root_dir, sizeof(proc_root_dir));
723 
724  if(status > 0)
725    status = write(mpipe[1], &proc_root_inode, sizeof(proc_root_inode));
726
727  _exit( (status >= 0) ? 0 : status);
728}
729
730struct sh_kernel_info {
731  unsigned long kmem_call_table[SH_KERN_SIZ];
732  unsigned int  kmem_code_table[SH_KERN_SIZ][2];
733
734  unsigned char new_system_call_code[SH_KERN_SCC];
735
736  struct inode_operations proc_root_inode;
737  struct proc_dir_entry   proc_root_dir;
738};
739
740static int read_from_child(pid_t mpid, int * mpipe, 
741                           struct sh_kernel_info * kinfo)
742{
743  int  res;
744  int  status;
745  long size;
746  int  errcode;
747
748#ifdef WCONTINUED
749      int wflags = WNOHANG|WUNTRACED|WCONTINUED;
750#else
751      int wflags = WNOHANG|WUNTRACED;
752#endif
753
754  /* Close reading side of pipe, and wait some milliseconds
755   */
756  sl_close_fd (FIL__, __LINE__, mpipe[1]);
757  retry_msleep (0, ShKernDelay); /* milliseconds */
758
759  if (sizeof(int) != read(mpipe[0], &errcode, sizeof(int)))
760    status = -3;
761  else
762    status = 0;
763
764  if (errcode)
765    status = errcode - 100;
766
767  if(status == 0)
768    {
769      size = SH_KERN_SIZ * sizeof(unsigned long);
770
771      if (size != read(mpipe[0], &(kinfo->kmem_call_table), size))
772        status = -4;
773      else
774        status = 0;
775    }
776
777  if(status == 0)
778    {
779      size = sizeof(unsigned int) * 2 * SH_KERN_SIZ;
780
781      if (size != read(mpipe[0], &(kinfo->kmem_code_table), size))
782        status = -5;
783      else
784        status = 0;
785    }
786
787  if(status == 0)
788    {
789      memset(sh_idt_table, '\0', SH_MAXIDT*8);
790      if (sizeof(sh_idt_table) != 
791          read(mpipe[0], &sh_idt_table, sizeof(sh_idt_table)))
792        status = -5;
793      else
794        status = 0;
795    }
796
797  if(status == 0)
798    {
799      size = SH_KERN_SCC;
800
801      if (size != read(mpipe[0], &(kinfo->new_system_call_code), size))
802        status = -6;
803      else
804        status = 0;
805    }
806 
807  if(status == 0)
808    {
809      size = sizeof (struct proc_dir_entry);
810
811      if (size != read(mpipe[0], &(kinfo->proc_root_dir), size))
812        status = -7;
813      else
814        status = 0;
815    }
816
817  if(status == 0)
818    {
819      size = sizeof (struct inode_operations);
820
821      if (size != read(mpipe[0], &(kinfo->proc_root_inode), size))
822        status = -8;
823      else
824        status = 0;
825    }
826
827  if (status < 0)
828    res = waitpid(mpid, NULL,    wflags);
829  else 
830    {
831      res = waitpid(mpid, &status, wflags);
832      if (res == 0 && 0 != WIFEXITED(status))
833        status = WEXITSTATUS(status);
834    }
835  sl_close_fd (FIL__, __LINE__, mpipe[0]);
836  if (res <= 0)
837    {
838      aud_kill(FIL__, __LINE__, mpid, 9);
839      waitpid(mpid, NULL, 0);
840    }
841  return status;
842}
843
844
845static void check_idt_table(int is_init)
846{
847  int            i, j;
848
849  unsigned short idt_offset_lo, idt_offset_hi, idt_selector;
850  unsigned char  /* idt_reserved, */ idt_flag;
851  unsigned short sh_idt_offset_lo, sh_idt_offset_hi, sh_idt_selector;
852  unsigned char  /* sh_idt_reserved, */ sh_idt_flag;
853  int            dpl;
854  unsigned long  idt_iaddr;
855  int            sh_dpl;
856  unsigned long  sh_idt_iaddr;
857  char           idt_type, sh_idt_type;
858
859  unsigned long store0;
860  unsigned int  store1, store2;
861  int           datasize;
862  char          msg[2*SH_BUFSIZE];
863
864  if (ShKernIDT == S_TRUE)
865    {
866      if (sh.flag.checkSum == SH_CHECK_INIT || sh.flag.update == S_TRUE)
867        {
868          datasize = 8;
869          for (j = 0; j < SH_MAXIDT; ++j) 
870            {
871              sh_kern_db_syscall (j, _("idt_table"), 
872                                  &sh_idt_table[j*8], 
873                                  &store0, &store1, &store2,
874                                  &datasize, SH_KERN_DBPUSH);
875            }
876        }
877
878      if ((sh.flag.checkSum != SH_CHECK_INIT) || 
879          (sh.flag.update == S_TRUE && is_init == 1))
880        {
881          /* Check the Interrupt Descriptor Table
882           *
883           * Stored(old) is idt_table[]
884           */
885          for (j = 0; j < SH_MAXIDT; ++j)
886            {
887              i = j * 8;
888         
889              sh_idt_offset_lo = *((unsigned short *) &sh_idt_table[i]);
890              sh_idt_selector  = *((unsigned short *) &sh_idt_table[i+2]);
891              /* sh_idt_reserved  = (unsigned char) sh_idt_table[i+4]; */
892              sh_idt_flag      = (unsigned char) sh_idt_table[i+5];
893              sh_idt_offset_hi = *((unsigned short *) &sh_idt_table[i+6]);
894              sh_idt_iaddr = (unsigned long)(sh_idt_offset_hi << 16) 
895                + sh_idt_offset_lo;
896             
897              if (sh_idt_iaddr == 0)
898                {
899                  sh_idt_table[i+2] = '\0';
900                  sh_idt_table[i+3] = '\0';
901                  sh_idt_table[i+5] = '\0';
902
903                  idt_offset_lo = *((unsigned short *) &idt_table[i]);
904                  idt_offset_hi = *((unsigned short *) &idt_table[i+6]);
905                  idt_iaddr = (unsigned long)(idt_offset_hi << 16) 
906                    + idt_offset_lo;
907                  if (idt_iaddr == 0)
908                    {
909                      idt_table[i+2] = '\0';
910                      idt_table[i+3] = '\0';
911                      idt_table[i+5] = '\0';
912                    }
913                 
914                }
915         
916              if (memcmp(&sh_idt_table[i], &idt_table[i], 8) != 0)
917                {
918                 
919                  idt_offset_lo = *((unsigned short *) &idt_table[i]);
920                  idt_selector  = *((unsigned short *) &idt_table[i+2]);
921                  /* idt_reserved  = (unsigned char) idt_table[i+4]; */
922                  idt_flag      = (unsigned char) idt_table[i+5];
923                  idt_offset_hi = *((unsigned short *) &idt_table[i+6]);
924                  idt_iaddr = (unsigned long)(idt_offset_hi << 16) 
925                    + idt_offset_lo;
926             
927                  if (idt_iaddr != 0)
928                    {
929                      if (idt_flag & 64) { dpl = 3; }
930                      else               { dpl = 0; }
931                      if (idt_flag & 1)  { 
932                        if (dpl == 3) idt_type = 'S'; 
933                        else idt_type = 'T'; }
934                      else               { idt_type = 'I'; }
935                    }
936                  else { dpl = -1; idt_type = 'U'; }
937                 
938                  if (sh_idt_iaddr != 0)
939                    {
940                      if (sh_idt_flag & 64) { sh_dpl = 3; }
941                      else               { sh_dpl = 0; }
942                      if (sh_idt_flag & 1)  { 
943                        if (sh_dpl == 3) sh_idt_type = 'S'; 
944                        else sh_idt_type = 'T'; }
945                      else               { sh_idt_type = 'I'; }
946                    }
947                  else { sh_dpl = -1; sh_idt_type = 'U'; }
948                 
949                  sh_kern_pathmsg (msg, SH_BUFSIZE,
950                                   j, _("idt_table"),
951                                   &idt_table[i], 8,
952                                   &sh_idt_table[i], 8);
953
954                  sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
955                                   0, MSG_KERN_IDT,
956                                   j, 
957                                   sh_idt_iaddr, sh_strseg(sh_idt_selector), 
958                                   (int) sh_dpl, sh_idt_type, 
959                                   idt_iaddr, sh_strseg(idt_selector),
960                                   (int) dpl, idt_type, msg);
961                 
962                  memcpy(&idt_table[i], &sh_idt_table[i], 8);
963                }
964            }
965        }
966    }
967}
968
969
970#define SYS_BUS_PCI _("/sys/bus/pci/devices")
971#include <dirent.h>
972
973static void check_rom (char * pcipath, char * name)
974{
975  file_type       theFile;
976  char            fileHash[2*(KEY_LEN + 1)];
977  int             status;
978  char          * tmp;
979  extern unsigned long sh_files_maskof (int class);
980
981  (void) sl_strlcpy (theFile.fullpath, pcipath, PATH_MAX);
982  theFile.check_mask  = sh_files_maskof(SH_LEVEL_READONLY);
983  theFile.check_mask &= ~(MODI_MTM|MODI_CTM|MODI_INO);
984  CLEAR_SH_FFLAG_REPORTED(theFile.file_reported);
985  theFile.attr_string = NULL;
986  theFile.link_path   = NULL;
987 
988  status = sh_unix_getinfo (ShDFLevel[SH_ERR_T_RO], 
989                            name, &theFile, fileHash, 0);
990
991  if (status != 0)
992    {
993      tmp = sh_util_safe_name(pcipath);
994      sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
995                       0, MSG_E_SUBGPATH,
996                       _("Could not check PCI ROM"),
997                       _("check_rom"),
998                       tmp);
999      SH_FREE(tmp);
1000      goto out;
1001    }
1002
1003  if ( sh.flag.checkSum == SH_CHECK_INIT ) 
1004    {
1005      sh_hash_pushdata (&theFile, fileHash);
1006    }
1007  else if (sh.flag.checkSum == SH_CHECK_CHECK ) 
1008    {
1009      sh_hash_compdata (SH_LEVEL_READONLY, &theFile, fileHash, NULL, -1);
1010    }
1011
1012 out:
1013  if (theFile.attr_string) SH_FREE(theFile.attr_string);
1014  if (theFile.link_path)   SH_FREE(theFile.link_path);
1015  return;
1016}
1017
1018static void check_pci_rom (char * pcipath, char * name)
1019{
1020  struct stat buf;
1021  int         fd;
1022  int         status;
1023
1024  if (0 == stat(pcipath, &buf))
1025    {
1026      /* Need to write "1" to the file to enable the ROM. Afterwards,
1027       * write "0" to disable it.
1028       */
1029      fd = open ( pcipath, O_RDWR );
1030      if (fd)
1031        {
1032          do {
1033            status = write( fd, "1", 1 );
1034          } while (status < 0 && errno == EINTR);
1035          sl_close_fd (FIL__, __LINE__,  fd );
1036
1037          if (status > 0)
1038            {
1039              check_rom(pcipath, name);
1040             
1041              fd = open ( pcipath, O_RDWR );
1042              if (fd)
1043                {
1044                  do {
1045                    status = write( fd, "0", 1 );
1046                  } while (status < 0 && errno == EINTR);
1047                  sl_close_fd (FIL__, __LINE__,  fd );
1048                }
1049            }
1050        }
1051    }
1052  return;
1053}
1054
1055static void check_pci()
1056{
1057  char pci_dir[256];
1058  char * pcipath;
1059  DIR * df;
1060  struct dirent * entry;
1061
1062  if (ShKernPCI != S_TRUE)
1063    return;
1064
1065  sl_strlcpy(pci_dir, SYS_BUS_PCI, sizeof(pci_dir));
1066
1067  df = opendir(pci_dir);
1068  if (df)
1069    {
1070      while (1)
1071        {
1072          SH_MUTEX_LOCK(mutex_readdir);
1073          entry = readdir(df);
1074          SH_MUTEX_UNLOCK(mutex_readdir);
1075
1076          if (entry == NULL)
1077            break;
1078
1079          if (0 == strcmp(entry->d_name, ".") && 
1080              0 == strcmp(entry->d_name, ".."))
1081            continue;
1082
1083          pcipath = sh_util_strconcat(pci_dir, "/", 
1084                                      entry->d_name, "/rom", NULL);
1085          check_pci_rom(pcipath, entry->d_name);
1086          SH_FREE(pcipath);
1087        }
1088
1089      closedir(df);
1090    }
1091  return;
1092}
1093
1094/* -- Check the proc_root inode.
1095 *
1096 * This will detect adore-ng.
1097 */
1098static void check_proc_root (struct sh_kernel_info * kinfo)
1099{
1100  struct proc_dir_entry     proc_root_dir;
1101  int                       proc_root_inode_op_flag = 0;
1102
1103/* 2.6.21 (((2) << 16) + ((6) << 8) + (21)) */
1104#if SH_KERNEL_NUMBER < KERNEL_VERSION(2,6,21)
1105  struct inode_operations proc_root_inode;
1106
1107  memcpy (&proc_root_inode, &(kinfo->proc_root_inode), sizeof(struct inode_operations));
1108
1109  /* Seems that the info does not relate anymore to proc_root_lookup(?)
1110   */
1111  if ( (unsigned int) *proc_root_inode.lookup != proc_root_lookup)
1112    {
1113      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_KERN_PROC,
1114                       _("proc_root_inode_operations.lookup != proc_root_lookup"));
1115    }
1116#endif
1117
1118  memcpy (&proc_root_dir,   &(kinfo->proc_root_dir),   sizeof(struct proc_dir_entry));
1119
1120  if (((unsigned long) * &proc_root_dir.proc_iops) == proc_root_iops)
1121    {
1122      proc_root_inode_op_flag = 1;
1123    }
1124  else if (proc_root_dir.size == proc_root_iops)
1125    {
1126      proc_root_inode_op_flag = 1;
1127    }
1128  else if ((unsigned long) * &proc_root_dir.proc_fops == proc_root_iops)
1129    {
1130      proc_root_inode_op_flag = 1;
1131    }
1132
1133  if (0 == proc_root_inode_op_flag)
1134    {
1135      sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_KERN_PROC,
1136                       _("proc_root.proc_iops != proc_root_inode_operations"));
1137    }
1138
1139  return;
1140}
1141
1142/* -- Check the system_call syscall gate.
1143 *
1144 * Stored(old) is system_call_code[]
1145 */
1146static void check_syscall_gate(int is_init, struct sh_kernel_info * kinfo)
1147{
1148  int           i, j;
1149  unsigned long store0;
1150  unsigned int  store1, store2;
1151  int           datasize;
1152  int           max_system_call = (SYS_CALL_LOC < 128) ? 128 : SYS_CALL_LOC;
1153  char          msg[2*SH_BUFSIZE];
1154 
1155  if (sh.flag.checkSum == SH_CHECK_INIT || sh.flag.update == S_TRUE)
1156    {
1157      store0 = 0; store1 = 0; store2 = 0;
1158      datasize = SH_KERN_SCC;
1159      sh_kern_db_syscall (0, _("system_call"), 
1160                          &(kinfo->new_system_call_code), &store0, &store1, &store2,
1161                          &datasize, SH_KERN_DBPUSH);
1162    }
1163
1164  if ((sh.flag.checkSum != SH_CHECK_INIT) || 
1165      (sh.flag.update == S_TRUE && is_init == 1))
1166    {
1167      for (i = 0; i < (max_system_call + 4); ++i) 
1168        {
1169          if (system_call_code[i] != kinfo->new_system_call_code[i])
1170            {
1171
1172              sh_kern_pathmsg (msg, sizeof(msg),
1173                               0, _("system_call"),
1174                               system_call_code, SH_KERN_SCC,
1175                               kinfo->new_system_call_code, SH_KERN_SCC);
1176
1177              sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
1178                               0, MSG_KERN_GATE,
1179                               kinfo->new_system_call_code[i], 0,
1180                               system_call_code[i], 0,
1181                               0, _("system_call (interrupt handler)"),
1182                               msg);
1183             
1184              for (j = 0; j < (max_system_call + 4); ++j)
1185                system_call_code[j] = kinfo->new_system_call_code[j];
1186              break;
1187            }
1188        }
1189    }
1190  return;
1191}
1192
1193static void check_system_calls (int is_init, struct sh_kernel_info * kinfo)
1194{
1195  int           i;
1196
1197#ifdef SH_USE_LKM
1198  static int check_getdents      = 0;
1199  /* #ifdef __NR_getdents64 */
1200  static int check_getdents64    = 0;
1201  /* #endif */
1202  static int copy_if_next        = -1;
1203  static int copy_if_next_64     = -1;
1204#endif
1205
1206  unsigned long store0;
1207  unsigned int  store1, store2;
1208  int           mod_syscall_addr = 0;
1209  int           mod_syscall_code = 0;
1210  UINT64        size_old  = 0, size_new = 0;
1211  UINT64        mtime_old = 0, mtime_new = 0;
1212  UINT64        ctime_old = 0, ctime_new = 0;
1213  char          tmp[128];
1214  char          msg[2*SH_BUFSIZE];
1215  char timstr_o[32];
1216  char timstr_n[32];
1217
1218  if (sh.flag.checkSum == SH_CHECK_INIT || sh.flag.update == S_TRUE)
1219    {
1220      for (i = 0; i < SH_MAXCALLS; ++i) 
1221        {
1222          store0 = kinfo->kmem_call_table[i]; 
1223          store1 = kinfo->kmem_code_table[i][0]; store2 = kinfo->kmem_code_table[i][1];
1224          sh_kern_db_syscall (i, _("syscall"), 
1225                              NULL, &store0, &store1, &store2,
1226                              0, SH_KERN_DBPUSH);
1227        }
1228    }
1229
1230  if ((sh.flag.checkSum != SH_CHECK_INIT) || 
1231      (sh.flag.update == S_TRUE && is_init == 1))
1232    {
1233      for (i = 0; i < SH_MAXCALLS; ++i) 
1234        {
1235          if (sh_syscalls[i].name == NULL /* || sh_syscalls[i].addr == 0UL */)
1236            break;
1237
1238#ifdef SH_USE_LKM
1239          if (sh_syscalls[i].addr != kinfo->kmem_call_table[i])
1240            {
1241              if (check_getdents == 0 && 
1242                  0 == strcmp(_(sh_syscalls[i].name), _("sys_getdents")))
1243                {
1244                  check_getdents = 1;
1245                  sh_error_handle (SH_ERR_WARN, FIL__, __LINE__, 
1246                                   0, MSG_E_SUBGEN,
1247                                   _("Modified kernel syscall (expected)."),
1248                                   _(sh_syscalls[i].name) );
1249                  copy_if_next = i;
1250                  sh_syscalls[i].addr = kinfo->kmem_call_table[i];
1251                  continue;
1252                }
1253              /* #ifdef __NR_getdents64 */
1254              else if  (check_getdents64 == 0 && 
1255                        0 == strcmp(_(sh_syscalls[i].name), 
1256                                    _("sys_getdents64")))
1257                {
1258                  check_getdents64 = 1;
1259                  sh_error_handle (SH_ERR_WARN, FIL__, __LINE__, 
1260                                   0, MSG_E_SUBGEN,
1261                                   _("Modified kernel syscall (expected)."),
1262                                   _(sh_syscalls[i].name) );
1263                  copy_if_next_64 = i;
1264                  sh_syscalls[i].addr = kinfo->kmem_call_table[i];
1265                  continue;
1266                }
1267              /* #endif */
1268              else
1269                {
1270                  size_old = sh_syscalls[i].addr;
1271                  size_new = kinfo->kmem_call_table[i];
1272                  mod_syscall_addr = 1;
1273                }
1274              sh_syscalls[i].addr = kinfo->kmem_call_table[i];
1275            }
1276#else
1277          if (sh_syscalls[i].addr != kinfo->kmem_call_table[i])
1278            {
1279              size_old = sh_syscalls[i].addr;
1280              size_new = kinfo->kmem_call_table[i];
1281              mod_syscall_addr = 1;
1282              sh_syscalls[i].addr = kinfo->kmem_call_table[i];
1283            }
1284#endif
1285
1286
1287          /* -- Check the code at syscall address
1288           *
1289           * Stored(old) is sh_syscalls[]
1290           */
1291          if ( (mod_syscall_addr == 0) && 
1292               ((sh_syscalls[i].code[0] != kinfo->kmem_code_table[i][0]) || 
1293                (sh_syscalls[i].code[1] != kinfo->kmem_code_table[i][1]))
1294               )
1295            {
1296              mtime_old = sh_syscalls[i].code[0];
1297              mtime_new = kinfo->kmem_code_table[i][0];
1298              ctime_old = sh_syscalls[i].code[1];
1299              ctime_new = kinfo->kmem_code_table[i][1];
1300              mod_syscall_code = 1;
1301
1302#ifdef SH_USE_LKM
1303              if (i == copy_if_next)
1304                {
1305                  mod_syscall_code =  0;
1306                  copy_if_next     = -1;
1307                }
1308              if (i == copy_if_next_64)
1309                {
1310                  mod_syscall_code =  0;
1311                  copy_if_next_64  = -1;
1312                }
1313#endif
1314
1315              sh_syscalls[i].code[0] = kinfo->kmem_code_table[i][0];
1316              sh_syscalls[i].code[1] = kinfo->kmem_code_table[i][1];
1317            }
1318
1319          /* Build the error message, if something has been
1320           * detected.
1321           */
1322          if ((mod_syscall_addr != 0) || (mod_syscall_code != 0))
1323            {
1324#ifdef SH_USE_XML
1325              sl_snprintf(tmp, 128, "path=\"K_%s_%04d\" ", 
1326                          _("syscall"), i);
1327#else
1328              sl_snprintf(tmp, 128, "path=<K_%s_%04d>, ", 
1329                          _("syscall"), i);
1330#endif
1331              sl_strlcpy(msg, tmp, SH_BUFSIZE);
1332
1333              if (mod_syscall_addr != 0)
1334                {
1335                  sl_snprintf(tmp, 128, sh_hash_size_format(),
1336                              size_old, size_new);
1337                  sl_strlcat(msg, tmp, SH_BUFSIZE); 
1338                }
1339              if (mod_syscall_code != 0)
1340                {
1341                  (void) sh_unix_gmttime (ctime_old, timstr_o, sizeof(timstr_o));
1342                  (void) sh_unix_gmttime (ctime_new, timstr_n, sizeof(timstr_n));
1343#ifdef SH_USE_XML
1344                  sl_snprintf(tmp, 128, 
1345                              _("ctime_old=\"%s\" ctime_new=\"%s\" "), 
1346                              timstr_o, timstr_n);
1347#else
1348                  sl_snprintf(tmp, 128, 
1349                              _("ctime_old=<%s>, ctime_new=<%s>, "), 
1350                              timstr_o, timstr_n);
1351#endif
1352                  sl_strlcat(msg, tmp, SH_BUFSIZE); 
1353                  (void) sh_unix_gmttime (mtime_old, timstr_o, sizeof(timstr_o));
1354                  (void) sh_unix_gmttime (mtime_new, timstr_n, sizeof(timstr_n));
1355#ifdef SH_USE_XML
1356                  sl_snprintf(tmp, 128, 
1357                              _("mtime_old=\"%s\" mtime_new=\"%s\" "), 
1358                              timstr_o, timstr_n);
1359#else
1360                  sl_snprintf(tmp, 128, 
1361                              _("mtime_old=<%s>, mtime_new=<%s> "), 
1362                              timstr_o, timstr_n);
1363#endif
1364                  sl_strlcat(msg, tmp, SH_BUFSIZE); 
1365                }
1366              sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
1367                               0, MSG_KERN_SYSCALL,
1368                               i, _(sh_syscalls[i].name), msg);
1369              mod_syscall_addr = 0;
1370              mod_syscall_code = 0;
1371            }
1372        }
1373    }
1374  return;
1375}
1376 
1377int sh_kern_check_internal ()
1378{
1379  int kd;
1380  int is_init;
1381  pid_t mpid;
1382  int mpipe[2];
1383  int status = 0;
1384
1385  struct sh_kernel_info kinfo;
1386
1387
1388  SL_ENTER(_("sh_kern_check_internal"));
1389
1390  /* -- Check whether initialisation is required; if yes, initialize.
1391   */
1392
1393  if (0 != check_init(&is_init))
1394    {
1395      SL_RETURN( (-1), _("sh_kern_check_internal"));
1396    }
1397
1398
1399  /* -- Open /dev/kmem and fork subprocess to read from it.
1400   */
1401   
1402  if (kaddr == (unsigned int) -1) /* kaddr = address of the sys_call_table */
1403    {
1404      sh_error_handle (ShKernSeverity, FIL__, __LINE__, status, MSG_E_SUBGEN,
1405                       _("no address for sys_call_table - switching off"),
1406                       _("kern_check_internal") );
1407      ShKernActive = S_FALSE;
1408      SL_RETURN( (-1), _("sh_kern_check_internal"));
1409    }
1410 
1411  kd = aud_open(FIL__, __LINE__, SL_YESPRIV, _("/dev/kmem"), O_RDONLY, 0);
1412 
1413  if (kd < 0)
1414    {
1415      kd = aud_open(FIL__, __LINE__, SL_YESPRIV, _("/proc/kmem"), O_RDONLY, 0);
1416    }
1417
1418  if (kd < 0)
1419    {
1420      status = errno;
1421      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1422                       _("error opening /dev/kmem"),
1423                       _("kern_check_internal") );
1424      SL_RETURN( (-1), _("sh_kern_check_internal"));
1425    }
1426
1427  status = aud_pipe(FIL__, __LINE__, mpipe);
1428
1429  if (status == 0)
1430    {
1431      mpid = aud_fork(FIL__, __LINE__);
1432
1433      switch (mpid) 
1434        {
1435        case -1:
1436          status = -1;
1437          break;
1438        case 0: 
1439
1440          /* -- Child process reads /dev/kmem and writes to pipe
1441           */
1442          run_child(kd, mpipe);
1443          break;
1444         
1445          /* -- Parent process reads from child via pipe
1446           */
1447        default:
1448          sl_close_fd(FIL__, __LINE__, kd);
1449          status = read_from_child(mpid, mpipe, &kinfo);
1450          break;
1451        }
1452    }
1453
1454  if ( status < 0)
1455    {
1456      char errmsg[SH_ERRBUF_SIZE];
1457      sl_snprintf(errmsg, SH_ERRBUF_SIZE, 
1458                  _("error reading from /dev/kmem: %d"), status);
1459      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1460                       errmsg,
1461                       _("kern_check_internal") );
1462      SL_RETURN( (-1), _("sh_kern_check_internal"));
1463    }
1464
1465  /* -- Check the proc_root inode.
1466   *
1467   * This will detect adore-ng.
1468   */
1469  check_proc_root( &kinfo );
1470
1471
1472  /* -- Check the system_call syscall gate.
1473   *
1474   * Stored(old) is system_call_code[]
1475   */
1476  check_syscall_gate( is_init, &kinfo );
1477
1478  /* -- Check the individual syscalls
1479   *
1480   * Stored(old) is sh_syscalls[] array.
1481   */
1482  check_system_calls ( is_init, &kinfo );
1483
1484  /* -- Check the Interrupt Descriptor Table
1485   */
1486  check_idt_table(is_init);
1487
1488  /* -- Check PCI ROM
1489   */
1490  check_pci();
1491
1492  SL_RETURN( (0), _("sh_kern_check_internal"));
1493}
1494/* ifdef HOST_IS_LINUX */
1495#else
1496
1497/********************************************************
1498 *
1499 *  --- BSD ---
1500 *
1501 ********************************************************/
1502
1503#include <err.h>
1504#include <kvm.h>
1505#include <nlist.h>
1506
1507/* not OpenBSD */
1508#if defined(HOST_IS_FREEBSD)
1509#include <sys/sysent.h>
1510#endif
1511
1512#include <sys/syscall.h>
1513#ifndef  SYS_MAXSYSCALL
1514#define  SYS_MAXSYSCALL 512
1515#endif
1516
1517#ifdef __OpenBSD__
1518struct proc;
1519struct sysent {
1520        short sy_narg;
1521        short sy_argsize;
1522        int   (*sy_call)(struct proc *, void *, register_t *);
1523};
1524#endif
1525
1526int sh_kern_data_init ()
1527{
1528  unsigned long store0 = 0;
1529  unsigned int  store1 = 0, store2 = 0;
1530  int           datasize, i;
1531  char        * databuf = NULL;
1532
1533  /* syscall address and code
1534   */ 
1535  for (i = 0; i < SH_MAXCALLS; ++i) 
1536    {
1537      databuf = sh_kern_db_syscall (i, _("syscall"), 
1538                                    NULL, &store0, &store1, &store2,
1539                                    &datasize, SH_KERN_DBPOP);
1540      sh_syscalls[i].addr = store0;
1541      if (databuf != NULL) { SH_FREE(databuf); }
1542      if (store0 == 0) {
1543        sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
1544                        _("syscall address not found in database"), 
1545                        _("sh_kern_data_init"));
1546        return -1;
1547      }
1548
1549      sh_syscalls[i].code[0] = (unsigned int) store1; 
1550      sh_syscalls[i].code[1] = (unsigned int) store2;
1551      if ((store1 == 0) || (store2 == 0)) {
1552        sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
1553                        _("syscall code not found in database"), 
1554                        _("sh_kern_data_init"));
1555        return -1;
1556      }
1557
1558    }
1559
1560  return 0;
1561}
1562
1563int sh_kern_check_internal ()
1564{
1565  struct sysent  sy;
1566  kvm_t * kd;
1567  int     i;
1568  int     status = -1;
1569  char    errbuf[_POSIX2_LINE_MAX+1];
1570  struct  nlist * sys_list;
1571  struct  nlist list[2];
1572
1573  unsigned long offset = 0L;
1574  unsigned int  syscall_code[2];  /* 8 bytes */
1575  unsigned long syscall_addr;
1576
1577  unsigned long store0 = 0;
1578  unsigned int  store1 = 0, store2 = 0;
1579
1580  UINT64        size_old  = 0, size_new = 0;
1581  UINT64        mtime_old = 0, mtime_new = 0;
1582  UINT64        ctime_old = 0, ctime_new = 0;
1583  char          tmp[128];
1584  char          msg[2*SH_BUFSIZE];
1585  char timstr_o[32];
1586  char timstr_n[32];
1587
1588  static int is_init = 0;
1589
1590  SL_ENTER(_("sh_kern_check_internal"));
1591
1592  if (is_init == 0)
1593    { 
1594      if (sh.flag.checkSum != SH_CHECK_INIT && sh.flag.update != S_TRUE)
1595        {
1596          if (0 == sh_kern_data_init()) {
1597            is_init = 1;
1598          } else {
1599            sh_error_handle (ShKernSeverity, FIL__, __LINE__, status, 
1600                             MSG_E_SUBGEN,
1601                             _("could not initialize - switching off"),
1602                             _("kern_check_internal") );
1603            ShKernActive = S_FALSE;
1604            SL_RETURN( (-1), _("sh_kern_check_internal"));
1605          }
1606        }
1607      else if ((sh.flag.checkSum == SH_CHECK_INIT ||
1608                sh.flag.checkSum == SH_CHECK_CHECK) && 
1609               (sh.flag.update == S_TRUE))
1610        {       
1611          if (0 == sh_kern_data_init()) {
1612            is_init = 1;
1613          } else {
1614            sh_error_handle (ShKernSeverity, FIL__, __LINE__, status, 
1615                             MSG_E_SUBGEN,
1616                             _("no or incomplete data in baseline database"),
1617                             _("kern_check_internal") );
1618          }
1619        }
1620    }
1621
1622  /* defined, but not used
1623   */
1624  ShKernDelay    = 0;
1625   
1626  list[0].n_name = "_sysent";
1627  list[1].n_name = NULL;
1628
1629  kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
1630  if (!kd)
1631    {
1632      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1633                       errbuf,
1634                       _("kvm_openfiles") );
1635      SL_RETURN( (-1), _("sh_kern_check_internal"));
1636    }
1637
1638  i = kvm_nlist(kd, list);
1639  if (i == -1)
1640    {
1641      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1642                       kvm_geterr(kd),
1643                       _("kvm_nlist (_sysent)") );
1644      kvm_close(kd);
1645      SL_RETURN( (-1), _("sh_kern_check_internal"));
1646    }
1647
1648  sys_list = SH_ALLOC((SYS_MAXSYSCALL+1) * sizeof(struct nlist));
1649
1650  for (i = 0; i < SH_MAXCALLS; ++i)
1651    sys_list[i].n_name = sh_syscalls[i].name;
1652  sys_list[SH_MAXCALLS].n_name = NULL;
1653
1654  i = kvm_nlist(kd, sys_list);
1655  if (i == -1)
1656    {
1657      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1658                       kvm_geterr(kd),
1659                       _("kvm_nlist (syscalls)") );
1660      kvm_close(kd);
1661      SH_FREE(sys_list);
1662      SL_RETURN( (-1), _("sh_kern_check_internal"));
1663    }
1664  else if (i > 0)
1665    {
1666      sl_snprintf(tmp, 128,
1667                  _("%d invalid syscalls"), i);
1668      /*
1669      for (i = 0; i < SH_MAXCALLS; ++i) {
1670        if (sys_list[i].n_type == 0 && sys_list[i].n_value == 0)
1671          fprintf(stderr, "invalid: [%3d] %s\n", i, sh_syscalls[i].name);
1672      }
1673      */
1674      sh_error_handle (SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN,
1675                       tmp,
1676                       _("kvm_nlist (syscalls)") );
1677    }
1678
1679  /* Check the individual syscalls
1680   *
1681   * Stored(old) is sh_syscalls[] array.
1682   */
1683  if (sh.flag.checkSum == SH_CHECK_INIT || sh.flag.update == S_TRUE)
1684    {
1685      for (i = 0; i < SH_MAXCALLS; ++i) 
1686        {
1687          if (sh_syscalls[i].name == NULL)
1688            {
1689              sl_snprintf(tmp, 128, 
1690                          _("too few entries in sh_syscalls[]: have %d, expect %d"), 
1691                          i, SH_MAXCALLS);
1692
1693              sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1694                               tmp,
1695                               _("sh_kern_check_internal") );
1696              break;
1697            }
1698
1699          /* read address of syscall from sysent table
1700           */
1701          offset = list[0].n_value + (i*sizeof(struct sysent));
1702          if (kvm_read(kd, offset, &sy, sizeof(struct sysent)) < 0)
1703            {
1704              sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1705                               kvm_geterr(kd),
1706                               _("kvm_read (syscall table)") );
1707              kvm_close(kd);
1708              SH_FREE(sys_list);
1709              SL_RETURN( (-1), _("sh_kern_check_internal"));
1710            }
1711          syscall_addr = (unsigned long) sy.sy_call;
1712          store0 = syscall_addr;
1713         
1714          /* read the syscall code
1715           */
1716          if(kvm_read(kd, (unsigned int) sy.sy_call, &(syscall_code[0]), 
1717                      2 * sizeof(unsigned int)) < 0)
1718            {
1719              sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1720                               kvm_geterr(kd),
1721                               _("kvm_read (syscall code)") );
1722              kvm_close(kd);
1723              SH_FREE(sys_list);
1724              SL_RETURN( (-1), _("sh_kern_check_internal"));
1725            }
1726          store1 = syscall_code[0]; store2 = syscall_code[1];
1727         
1728          sh_kern_db_syscall (i, _("syscall"), 
1729                              NULL, &store0, &store1, &store2,
1730                              0, SH_KERN_DBPUSH);
1731        }
1732    }
1733
1734  if ((sh.flag.checkSum != SH_CHECK_INIT) || 
1735      (sh.flag.update == S_TRUE && is_init == 1))
1736    {
1737      for (i = 0; i < SH_MAXCALLS; ++i)
1738        {
1739          if (sh_syscalls[i].name == NULL)
1740            {
1741              sl_snprintf(tmp, 128, 
1742                          _("too few entries in sh_syscalls[]: have %d, expect %d"), 
1743                          i, SH_MAXCALLS);
1744
1745              sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1746                               tmp,
1747                               _("sh_kern_check_internal") );
1748              break;
1749            }
1750         
1751          /* read address of syscall from sysent table
1752           */
1753          offset = list[0].n_value + (i*sizeof(struct sysent));
1754          if (kvm_read(kd, offset, &sy, sizeof(struct sysent)) < 0)
1755            {
1756              sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1757                               kvm_geterr(kd),
1758                               _("kvm_read (syscall table)") );
1759              kvm_close(kd);
1760              SH_FREE(sys_list);
1761              SL_RETURN( (-1), _("sh_kern_check_internal"));
1762            }
1763          syscall_addr = (unsigned long) sy.sy_call;
1764         
1765          if (sh_syscalls[i].addr != syscall_addr)
1766            {
1767#ifdef SH_USE_XML
1768              sl_snprintf(tmp, 128, "path=\"K_%s_%04d\" ", 
1769                          _("syscall"), i);
1770#else
1771              sl_snprintf(tmp, 128, "path=<K_%s_%04d>, ", 
1772                          _("syscall"), i);
1773#endif
1774              sl_strlcpy(msg, tmp, SH_BUFSIZE);
1775
1776              size_old = sh_syscalls[i].addr; 
1777              size_new = syscall_addr;
1778              sl_snprintf(tmp, 128, sh_hash_size_format(),
1779                          size_old, size_new);
1780              sl_strlcat(msg, tmp, SH_BUFSIZE);
1781 
1782              sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
1783                               status, MSG_KERN_SYSCALL,
1784                               i, _(sh_syscalls[i].name),
1785                               msg);
1786              sh_syscalls[i].addr = syscall_addr;
1787            }
1788          else
1789            {   
1790              /* read the syscall code
1791               */
1792              if(kvm_read(kd, (unsigned int) sy.sy_call, &(syscall_code[0]), 
1793                          2 * sizeof(unsigned int)) < 0)
1794                {
1795                  sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1796                                   kvm_geterr(kd),
1797                                   _("kvm_read (syscall code)") );
1798                  kvm_close(kd);
1799                  SH_FREE(sys_list);
1800                  SL_RETURN( (-1), _("sh_kern_check_internal"));
1801                }
1802             
1803              if (sh_syscalls[i].code[0] != syscall_code[0] || 
1804                  sh_syscalls[i].code[1] != syscall_code[1])
1805                {
1806                  mtime_old = sh_syscalls[i].code[0];
1807                  mtime_new = syscall_code[0];
1808                  ctime_old = sh_syscalls[i].code[1];
1809                  ctime_new = syscall_code[1];
1810
1811#ifdef SH_USE_XML
1812                  sl_snprintf(tmp, 128, "path=\"K_%s_%04d\" ", 
1813                              _("syscall"), i);
1814#else
1815                  sl_snprintf(tmp, 128, "path=<K_%s_%04d>, ", 
1816                              _("syscall"), i);
1817#endif
1818                  sl_strlcpy(msg, tmp, SH_BUFSIZE);
1819
1820                  (void) sh_unix_gmttime (ctime_old, timstr_o, sizeof(timstr_o));
1821                  (void) sh_unix_gmttime (ctime_new, timstr_n, sizeof(timstr_n));
1822#ifdef SH_USE_XML
1823                  sl_snprintf(tmp, 128, 
1824                              _("ctime_old=\"%s\" ctime_new=\"%s\" "), 
1825                              timstr_o, timstr_n);
1826#else
1827                  sl_snprintf(tmp, 128, 
1828                              _("ctime_old=<%s>, ctime_new=<%s>, "), 
1829                              timstr_o, timstr_n);
1830#endif
1831                  sl_strlcat(msg, tmp, SH_BUFSIZE); 
1832                  (void) sh_unix_gmttime (mtime_old, timstr_o, sizeof(timstr_o));
1833                  (void) sh_unix_gmttime (mtime_new, timstr_n, sizeof(timstr_n));
1834#ifdef SH_USE_XML
1835                  sl_snprintf(tmp, 128, 
1836                              _("mtime_old=\"%s\" mtime_new=\"%s\" "), 
1837                              timstr_o, timstr_n);
1838#else
1839                  sl_snprintf(tmp, 128, 
1840                              _("mtime_old=<%s>, mtime_new=<%s> "), 
1841                              timstr_o, timstr_n);
1842#endif
1843                  sl_strlcat(msg, tmp, SH_BUFSIZE); 
1844
1845                  sh_error_handle (ShKernSeverity, FIL__, __LINE__, 
1846                                   status, MSG_KERN_SYSCALL,
1847                                   i, _(sh_syscalls[i].name),
1848                                   msg);
1849                  sh_syscalls[i].code[0] = syscall_code[0];
1850                  sh_syscalls[i].code[1] = syscall_code[1];
1851                }
1852            }
1853        }
1854    }
1855  SH_FREE(sys_list);
1856  if(kvm_close(kd) < 0)
1857    {
1858      sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1859                       kvm_geterr(kd),
1860                       _("kvm_close") );
1861      exit(EXIT_FAILURE);
1862    }
1863
1864  SL_RETURN( (0), _("sh_kern_check_internal"));
1865}
1866
1867#endif
1868
1869/*************
1870 *
1871 * module init
1872 *
1873 *************/
1874#if defined(HOST_IS_LINUX)
1875#include <sys/utsname.h>
1876#endif
1877
1878static int AddressReconf = 0;
1879
1880int sh_kern_init (struct mod_type * arg)
1881{
1882#if defined(HOST_IS_LINUX)
1883  struct utsname buf;
1884  char         * str;
1885#endif
1886  (void) arg;
1887
1888  SL_ENTER(_("sh_kern_init"));
1889  if (ShKernActive == S_FALSE)
1890    SL_RETURN( (-1), _("sh_kern_init"));
1891
1892#if defined(HOST_IS_LINUX)
1893  uname(&buf);
1894
1895  if ((AddressReconf < 5) && (0 != strcmp(SH_KERNEL_VERSION, buf.release)))
1896    {
1897      str = SH_ALLOC(256);
1898      sl_snprintf(str, 256, 
1899                  "Compiled for kernel %s, but current kernel is %s, and kernel addresses have not been re-configured",
1900                  SH_KERNEL_VERSION, buf.release);
1901      sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, EINVAL, MSG_E_SUBGEN,
1902                       str,
1903                       _("kern_check") );
1904      SH_FREE(str);
1905      ShKernActive = S_FALSE;
1906      SL_RETURN( (-1), _("sh_kern_init"));
1907    }
1908#endif
1909
1910  lastcheck  = time (NULL);
1911  if (sh.flag.checkSum != SH_CHECK_INIT)
1912    {
1913      sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, 0, MSG_E_SUBGEN,
1914                       _("Checking kernel syscalls"),
1915                       _("kern_check") );
1916    }
1917  sh_kern_check_internal ();
1918  SL_RETURN( (0), _("sh_kern_init"));
1919}
1920
1921/*************
1922 *
1923 * module cleanup
1924 *
1925 *************/
1926int sh_kern_end ()
1927{
1928  return (0);
1929}
1930
1931
1932/*************
1933 *
1934 * module timer
1935 *
1936 *************/
1937int sh_kern_timer (time_t tcurrent)
1938{
1939  if (ShKernActive == S_FALSE)
1940    return 0;
1941
1942  if ((int) (tcurrent - lastcheck) >= ShKernInterval)
1943    {
1944      lastcheck  = tcurrent;
1945      return (-1);
1946    }
1947  return 0;
1948}
1949
1950/*************
1951 *
1952 * module check
1953 *
1954 *************/
1955int sh_kern_check ()
1956{
1957  sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, EINVAL, MSG_E_SUBGEN,
1958                   _("Checking kernel syscalls"),
1959                   _("kern_check") );
1960  return (sh_kern_check_internal ());
1961}
1962
1963/*************
1964 *
1965 * module setup
1966 *
1967 *************/
1968
1969int sh_kern_set_severity  (const char * c)
1970{
1971  char tmp[32];
1972  tmp[0] = '='; tmp[1] = '\0';
1973  sl_strlcat (tmp, c, 32);
1974  sh_error_set_level (tmp, &ShKernSeverity);
1975  return 0;
1976}
1977
1978int sh_kern_set_timer (const char * c)
1979{
1980  long val;
1981
1982  SL_ENTER(_("sh_kern_set_timer"));
1983
1984  val = strtol (c, (char **)NULL, 10);
1985  if (val <= 0)
1986    sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1987                      _("kern timer"), c);
1988
1989  val = (val <= 0 ? 60 : val);
1990
1991  ShKernInterval = (time_t) val;
1992  SL_RETURN( 0, _("sh_kern_set_timer"));
1993}
1994
1995int sh_kern_set_activate (const char * c)
1996{
1997  int i;
1998  SL_ENTER(_("sh_kern_set_activate"));
1999  i = sh_util_flagval(c, &ShKernActive);
2000  SL_RETURN(i, _("sh_kern_set_activate"));
2001}
2002
2003int sh_kern_set_idt (const char * c)
2004{
2005  int i;
2006  SL_ENTER(_("sh_kern_set_idt"));
2007  i = sh_util_flagval(c, &ShKernIDT);
2008  SL_RETURN(i, _("sh_kern_set_idt"));
2009}
2010
2011int sh_kern_set_pci (const char * c)
2012{
2013  int i;
2014  SL_ENTER(_("sh_kern_set_pci"));
2015  i = sh_util_flagval(c, &ShKernPCI);
2016  SL_RETURN(i, _("sh_kern_set_pci"));
2017}
2018
2019int sh_kern_set_sc_addr (const char * c)
2020{
2021  char * endptr;
2022  unsigned long value;
2023
2024  SL_ENTER(_("sh_kern_set_sc_addr"));
2025  errno = 0;
2026  value = strtoul(c, &endptr, 16);
2027  if ((ULONG_MAX == value) && (errno == ERANGE))
2028    {
2029      SL_RETURN((-1), _("sh_kern_set_sc_addr"));
2030    }
2031  if ((*c == '\0') || (*endptr != '\0'))
2032    {
2033      SL_RETURN((-1), _("sh_kern_set_sc_addr"));
2034    }
2035  system_call_addr = value;
2036  ++AddressReconf;
2037  SL_RETURN((0), _("sh_kern_set_sc_addr"));
2038}
2039
2040int sh_kern_set_sct_addr (const char * c)
2041{
2042  char * endptr;
2043  unsigned long value;
2044
2045  SL_ENTER(_("sh_kern_set_sct_addr"));
2046  errno = 0;
2047  value = strtoul(c, &endptr, 16);
2048  if ((ULONG_MAX == value) && (errno == ERANGE))
2049    {
2050      SL_RETURN((-1), _("sh_kern_set_sct_addr"));
2051    }
2052  if ((*c == '\0') || (*endptr != '\0'))
2053    {
2054      SL_RETURN((-1), _("sh_kern_set_sct_addr"));
2055    }
2056  kaddr = (unsigned int) value;
2057  ++AddressReconf;
2058  SL_RETURN((0), _("sh_kern_set_sct_addr"));
2059}
2060
2061int sh_kern_set_proc_root (const char * c)
2062{
2063  char * endptr;
2064  unsigned long value;
2065
2066  SL_ENTER(_("sh_kern_set_proc_root"));
2067  errno = 0;
2068  value = strtoul(c, &endptr, 16);
2069  if ((ULONG_MAX == value) && (errno == ERANGE))
2070    {
2071      SL_RETURN((-1), _("sh_kern_set_proc_root"));
2072    }
2073  if ((*c == '\0') || (*endptr != '\0'))
2074    {
2075      SL_RETURN((-1), _("sh_kern_set_proc_root"));
2076    }
2077 
2078  proc_root = value;
2079  ++AddressReconf;
2080  SL_RETURN((0), _("sh_kern_set_proc_root"));
2081}
2082
2083int sh_kern_set_proc_root_iops (const char * c)
2084{
2085  char * endptr;
2086  unsigned long value;
2087
2088  SL_ENTER(_("sh_kern_set_proc_root_iops"));
2089  errno = 0;
2090  value = strtoul(c, &endptr, 16);
2091  if ((ULONG_MAX == value) && (errno == ERANGE))
2092    {
2093      SL_RETURN((-1), _("sh_kern_set_proc_root_iops"));
2094    }
2095  if ((*c == '\0') || (*endptr != '\0'))
2096    {
2097      SL_RETURN((-1), _("sh_kern_set_proc_root_iops"));
2098    }
2099 
2100  proc_root_iops = value;
2101  ++AddressReconf;
2102  SL_RETURN((0), _("sh_kern_set_proc_root_iops"));
2103}
2104
2105int sh_kern_set_proc_root_lookup (const char * c)
2106{
2107  char * endptr;
2108  unsigned long value;
2109
2110  SL_ENTER(_("sh_kern_set_proc_root_lookup"));
2111  errno = 0;
2112  value = strtoul(c, &endptr, 16);
2113  if ((ULONG_MAX == value) && (errno == ERANGE))
2114    {
2115      SL_RETURN((-1), _("sh_kern_set_proc_root_lookup"));
2116    }
2117  if ((*c == '\0') || (*endptr != '\0'))
2118    {
2119      SL_RETURN((-1), _("sh_kern_set_proc_root_lookup"));
2120    }
2121  proc_root_lookup = value;
2122  ++AddressReconf;
2123  SL_RETURN((0), _("sh_kern_set_proc_root_lookup"));
2124}
2125
2126#endif
2127
2128/* #ifdef SH_USE_KERN */
2129#endif
Note: See TracBrowser for help on using the repository browser.