/*
     etherscan - A real time network security monitor
     Copyright (C) 1993 Douglas Lee Schales, David K. Hess, David R. Safford

     Please see the file `RESTRICTIONS' for the complete copyright notice.

rsh.c - 05/23/93

*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <ctype.h>
#include <sys/time.h>
#include <math.h>

extern float microsec(struct timeval);
extern struct timeval subtime(struct timeval, struct timeval);
extern struct timeval addtime(struct timeval, struct timeval);

struct ipaddr {
     unsigned int addr:32;
};

struct rsh_sess {
     struct rsh_sess *next;
     struct rsh_sess *prev;
     struct in_addr srcaddr;
     struct in_addr dstaddr;
     struct timeval tlp;
     unsigned short srcport;
     char buffer[1024];
     int bufptr;
     char remusername[80];
     char locusername[80];
     char cmdname[1024];
     int state;
     int linecnt;
#define RSH_INIT 0
#define RSH_REMUSER 1
#define RSH_LOCUSER 2
#define RSH_CMD 3
#define RSH_ACTIVE 4
};

static void
checksession(struct rsh_sess *sp)
{
#include "lognames.h"
     int i;

     /*
       To enable logging of root -> root rsh's, delete
       the strcmp() against the remusername.
     */

     if((strcmp(sp->locusername, "root") == 0 &&
	 strcmp(sp->remusername, "root") != 0)){
	  outtime(sp->tlp);
	  printf(" [rsh] ");
	  outhost(sp->srcaddr);
	  printf(".%d", sp->srcport);
	  outhost(sp->dstaddr);
	  printf(" '%s' (from %s):\n", sp->locusername, sp->remusername);
	  printf("\t%s\n", sp->cmdname);
	  fflush(stdout);
     }
     else {
	  char *cmd, *cp;
	  int logit = 0;
	  cmd = sp->cmdname;
	  for(cp = sp->cmdname;*cp && !isspace(*cp);cp++)
	       if(*cp == '/')
		    cmd=cp+1;
	  if(strcmp(cmd, "csh") == 0 ||
	     strcmp(cmd, "tcsh") == 0 ||
	     strcmp(cmd, "sh") == 0 ||
	     strcmp(cmd, "ksh") == 0 ||
	     strcmp(cmd, "bash") == 0){
	       logit = 1;
	  }
	  else {
	       if(strncmp(cmd, "csh ", 4) == 0 ||
		  strncmp(cmd, "tcsh ", 5) == 0){
		    for(;;){
			 for(;*cp && isspace(*cp);cp++)
			      ;
			 if(*cp == '-'){
			      for(cp++;*cp && !isspace(*cp);cp++)
				   switch(*cp){
				   case 's':
				   case 'i':
				   case 't':
					logit = 1;
					break;
				   }
			 }
			 else
			      break;
		    }
	       }
	       if(strncmp(cmd, "sh ", 3) == 0 ||
		  strncmp(cmd, "ksh ", 4) == 0 ||
		  strncmp(cmd, "bash ", 5) == 0){
		    for(;;){
			 for(;*cp && isspace(*cp);cp++)
			      ;
			 if(*cp == '-'){
			      for(cp++;*cp && !isspace(*cp);cp++)
				   switch(*cp){
				   case 's':
				   case 'i':
					logit = 1;
					break;
				   }
			 }
			 else
			      break;
		    }
	       }
	  }
	  if(logit){
	       outtime(sp->tlp);
	       printf(" [rsh] ");
	       outhost(sp->srcaddr);
	       printf(".%d", sp->srcport);
	       outhost(sp->dstaddr);
	       printf(" '%s' active shell (%s):\n",
		      sp->locusername, sp->remusername);
	       printf("\t%s\n", sp->cmdname);
	       fflush(stdout);
	  }
     }
     
     for(i=0;names[i];i++)
	  if(strcasecmp(sp->locusername, names[i]) == 0)
	       break;
     if(names[i]){
	  outtime(sp->tlp);
	  printf(" [rsh] ");
	  outhost(sp->srcaddr);
	  printf(".%d", sp->srcport);
	  outhost(sp->dstaddr);
	  printf(" as `%s' (from `%s'):\n",
		 sp->locusername, sp->remusername);
	  printf("\t%s\n", sp->cmdname);
	  fflush(stdout);
     }
}

static void
checkbuffer(struct rsh_sess *sp)
{
     switch(sp->state){
     case RSH_REMUSER:
	  strncpy(sp->remusername, sp->buffer, 16);
	  sp->state = RSH_LOCUSER;
	  break;
     case RSH_LOCUSER:
	  strncpy(sp->locusername, sp->buffer, 16);
	  sp->state = RSH_CMD;
	  break;
     case RSH_CMD:
	  strncpy(sp->cmdname, sp->buffer, 1023);
	  checksession(sp);
	  sp->state = RSH_ACTIVE;
	  break;
     case RSH_ACTIVE:
	  sp->linecnt++;
	  break;
     default:
	  /* ehh?? */
	  break;
     }
}

static struct rsh_sess *head = (struct rsh_sess *)0;

rsh_dump(struct ip *ip, struct tcphdr *tcp, struct timeval timebuf)
{
     struct rsh_sess *next;
     struct rsh_sess *rove;
     struct timeval delta;

     for(rove = head;rove;rove=rove->next){
	  if(memcmp((char *)&ip->ip_dst.s_addr,(char *)&rove->dstaddr,4)==0 &&
	     memcmp((char *)&ip->ip_src.s_addr,(char *)&rove->srcaddr,4)==0 &&
	     memcmp((char *)&tcp->th_sport,(char *)&rove->srcport, 2)==0){
	       break;
	  }
     }
     if(!rove && (tcp->th_flags & (TH_RST|TH_FIN)))
	  return;
     if(!rove && ((tcp->th_flags & (TH_SYN|TH_ACK)) == TH_SYN)){
	  rove = (struct rsh_sess *)malloc(sizeof(struct rsh_sess));
	  memcpy((char *)&rove->dstaddr.s_addr,(char *)&ip->ip_dst, 4);
	  memcpy((char *)&rove->srcaddr.s_addr,(char *)&ip->ip_src, 4);
	  memcpy((char *)&rove->srcport,(char *)&tcp->th_sport, 2);
	  rove->bufptr = 0;
	  rove->state = RSH_INIT;
	  rove->linecnt = rove->bufptr = 0;
	  rove->remusername[0] = rove->locusername[0] = 0;
	  rove->next = head;
	  rove->prev = (struct rsh_sess *)0;
	  if(head)
	       head->prev = rove;
	  head = rove;
     }

     if(!rove)
	  return;
     rove->tlp = timebuf;

     if(!(tcp->th_flags & ~(TH_PUSH|TH_ACK))){
	  unsigned char offset;
	  int len;
	  char *bp;
	  int i;

	  char *offp;
	  char off;

	  offp = ((char *)&tcp->th_ack)+4;
	  memcpy(&off, offp, 1);
          offset = (off & 0xf0) >> 2;

	  len = ip->ip_len - offset - sizeof(struct ip);
	  bp = ((char *)tcp)+offset;

	  for(i=0;i<len;i++){
	       if(rove->state == RSH_INIT){
		    if(bp[i] == 0)
			 rove->state = RSH_REMUSER;
	       }
	       else if(rove->state != RSH_ACTIVE){
		    if(!bp[i]){
			 rove->buffer[rove->bufptr] = 0;
			 rove->tlp = timebuf;
			 checkbuffer(rove);
			 rove->bufptr = 0;
		    }
		    else if(rove->bufptr < 1023)
			 rove->buffer[rove->bufptr++] = bp[i];
	       }
	       else if(bp[i] == '\n' || bp[i] == '\r'){
		    if(i+1 != len && bp[i+1] == '\n' || bp[i+1] == '\r')
			 i++;
		    rove->buffer[rove->bufptr] = 0;
		    rove->tlp = timebuf;
		    checkbuffer(rove);
		    rove->bufptr = 0;
	       }
	       else if((rove->bufptr < 1023) && bp[i])
		    rove->buffer[rove->bufptr++] = bp[i];
	  }
     }
     else if(tcp->th_flags & TH_RST ||
	     tcp->th_flags & TH_FIN){

	  if(rove == head)
	       head = rove->next;
	  if(rove->prev)
	       rove->prev->next = rove->next;
	  if(rove->next)
	       rove->next->prev = rove->prev;
	  free(rove);
     }

     next = head;
     while(next){
	  if(timebuf.tv_sec - next->tlp.tv_sec > 60){
	       struct rsh_sess *nextnext = next->next;

	       if(next == head)
		    head = next->next;
	       if(next->prev)
		    next->prev->next = next->next;
	       if(next->next)
		    next->next->prev = next->prev;
	       free(next);
	       next = nextnext;
	  }
	  else
	       next = next->next;
     }
}
