/***
 ***
 *** author   : beavis & butthead 
 ***
 *** nittie.c : checks for a vnode with major/minor similar 
 ***            to /dev/nit. i.e. reports if there is a sunsniffer.
 ***
 *** compile  : cc -o nittie nittie.c -lkvm
 ***
 *** include  : all header files about disclaimers and that kind of rubbish.
 ***
 *** "bugs"   : it checks 'kmem' for such vnodes therefore there is no
 ***            guarantee that it will cue for all sniffers.
 ***            the intruder might have modified 'kmem' already.
 ***
 *** advntges : better than cert's 'cpm' because the sniffer can be
 ***            reading in normal (non promiscuous) mode from /dev/nit
 ***            and nittie.c will sense this.
 ***
 ***/
                   

#include <stdio.h> 
#include <errno.h>
#include <nlist.h>
#include <pwd.h>

#include <sys/time.h>
#include <kvm.h>

#define KERNEL
#include <sys/file.h>
#include <fcntl.h>
#include <sys/proc.h>
#undef KERNEL

/* the following lines are stollen from <sys/vnode.h> */

enum vtype 	{ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VBAD, VFIFO };

struct vnode {
	u_short		v_flag;			/* vnode flags (see below) */
	u_short		v_count;		/* reference count */
	u_short		v_shlockc;		/* count of shared locks */
	u_short		v_exlockc;		/* count of exclusive locks */
	struct vfs	*v_vfsmountedhere; 	/* ptr to vfs mounted here */
	struct vnodeops	*v_op;			/* vnode operations */
	union {
		struct socket	*v_Socket;	/* unix ipc */
		struct stdata	*v_Stream;	/* stream */
		struct page	*v_Pages;	/* vnode pages list */
	} v_s;
	struct vfs	*v_vfsp;		/* ptr to vfs we are in */
	enum vtype	v_type;			/* vnode type */
	dev_t		v_rdev;			/* device (VCHR, VBLK) */
	long		*v_filocks;		/* File/Record locks ... */
	caddr_t		v_data;			/* private data for fs */
};

/* down to here */


#define NIT_MAJ     0x2800
#define MAJ_MASK    0xff00
#define MIN_MASK    0x00ff
#define FALSE       0
#define TRUE        1

#define k_read(x, y, l) \
           if (kvm_read(kd, (unsigned long)x, (char *)y, (unsigned)l) < 0) \
              { perror("kvm_read"); exit(1); }

int    cnt = 0;
kvm_t *kd;

int CheckFile(pUsrFile)
struct file *pUsrFile;
{
   struct vnode vn;

   if ((pUsrFile->f_count == 0) || (pUsrFile->f_data == NULL))
      return FALSE;
   
   k_read(pUsrFile->f_data, &vn, sizeof(struct vnode));

   if ((vn.v_type == VCHR) && ((vn.v_rdev & MAJ_MASK) == NIT_MAJ)) {
      printf("open NIT with major/minor: %d / %d\n",
             ((vn.v_rdev >> 8) & MIN_MASK), (vn.v_rdev & MIN_MASK));
      
      printf("NIT open() flags: %s%s\n", 
             (((pUsrFile->f_flag) & _FREAD) ? "READ " : ""),
             (((pUsrFile->f_flag) & _FWRITE) ? "WRITE" : ""));
      
      return TRUE;
   }
   return FALSE;

}

void userSearch(userProc)
struct proc *userProc;
{

#define MAX_CWD_LEN 1024

   int            flg;
   char         **usrArg;
   struct file  **ppFile, *pCurFile, curFile;
   struct user   *pUsrEnv;
   struct passwd *pUsrInfo;
   struct ucwd    usrCurDir;
   char           szCD[MAX_CWD_LEN];

   
   pUsrEnv = kvm_getu(kd, userProc);
   ppFile = pUsrEnv->u_ofile;

   if (ppFile) do {
      
      k_read(ppFile++, &pCurFile, sizeof(struct file *));

      if (pCurFile == NULL)
         continue;

      k_read(pCurFile, &curFile, sizeof(struct file));
      
      if (flg = CheckFile(&curFile)) {
         cnt++;

         pUsrInfo = getpwuid(userProc->p_uid);
         if (pUsrInfo)
            printf("user: %s (%s)\n", pUsrInfo->pw_name, pUsrInfo->pw_gecos);
         else
            printf("userid: %d\n", userProc->p_uid);
      
         k_read(pUsrEnv->u_cwd, &usrCurDir, sizeof(struct ucwd));
         k_read(usrCurDir.cw_dir, szCD, MAX_CWD_LEN);
         printf("starting dir: %s\n", (szCD[0] ? szCD: "/"));

         kvm_getcmd(kd, userProc, pUsrEnv, &usrArg, NULL);
         if (usrArg) {
            printf("program:");
            while (*usrArg != NULL) printf(" %s", *usrArg++);
            puts("");
         }
         else
            puts("can not find program's args");
            
         printf("pid: %d\n", userProc->p_pid);
         
         puts("---");
      }
   } while (pCurFile && !flg);
}


main(argc, argv)
int    argc;
char **argv;
{
   struct proc *pCurProc;
 
   if (!(kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL))) {
      perror("kvm_open");  
      exit(1);
   }
   
   kvm_setproc(kd);
   while ((pCurProc = kvm_nextproc(kd)) != NULL) 
      userSearch(pCurProc);
   
   if (!cnt)
      puts("can not sense anyone reading from the NIT");
   else
      printf("total: %d process%s using NIT\n", cnt, (cnt == 1 ? "" : "es"));

   kvm_close(kd);
}



