/* * feather.c - preserve a program's atime and mtime after executing * Written by Kairi Nakatsuki * * usage: feather command [args ...] * * I wrote this little ditty after a session of pondering--Do people think * about the fact that the access times of commands they execute are modified * on a read(), mknod(), chmod(), or utimes(), when trying to cover one's * tracks? Some breakin attempts I've seen suggest otherwise, which prompted * me to write this program to sate my curiousity. This utility is mainly * useful when one wants to cover their tracks further after gaining a root * shell. Note that if you really wanted to be thorough about being covert, * make sure you use this utility in conjunction with touch(1) to set the * atime and mtime of the shell and compiler to pre-intrusion values. * Sure, one could do the very same with touch(1) after each execution, but * that would be tedious, no? * * On most UNIX workalikes, all that is needed to compile this program is: * * $ cc feather.c -o feather * * The functionality of most of the code is explained thoroughly for those new * to the scene. You may notice that I "over-code" things by rewriting the * functionality of functions that may already exist; note that this is for * portability reasons, as I tested this bit of code on my NEXTSTEP 3.3 * machine. Also tested under GNU/Linux (glibc 2.2), NetBSD 1.6.1, and QNX. * * If you find a legitimate use for this snippet of code, e-mail me, as I would * be very impressed to know. Don't let this program be an open invitation to * "own" somebody's machine simply to test it out. This is intended for * educational purposes only. Allow me to remind you to be responsible. * */ #include #include #include #include #include #include #include /* added */ #include char *appname; extern int errno; static void usage () { fprintf (stderr, "usage: %s command [args ...]\n", appname); exit (1); } char * which (const char *command) { char *path = NULL, *tmp = NULL, *buf = NULL; int len, cmdlen = strlen (command); /* Test to see if we need to find the absolute path name of the command by * seeing if the string passed to which() is the filename of an executable. * If so, duplicate the contents of command, so the pointer returned by * which() can be handled consistiently. */ if (access (command, X_OK) == 0) { if ((buf = (char *) malloc (cmdlen + 1)) == NULL) { fprintf (stderr, "%s: %s: %s\n", appname, "malloc()", strerror (errno)); exit (errno); } strcpy (buf, command); return (buf); /* free() me */ } /* If getenv() doesn't work, there's obviously a serious memory issue going * on. (If someone doesn't code responsibly, who will? */ if ((path = getenv ("PATH")) == NULL) { fprintf (stderr, "%s: %s: %s\n", appname, "getenv()", strerror (errno)); exit (errno); } /* For each element of the PATH environment variable, test to see if the * element, with a slash (/) and contents of command appended, is a valid * path to an executable. If this is the case, return the contents of * buf. buf needs to be free()d, or else we'll have memory leaking all over * the place. I am a neat freak. */ for (tmp = strtok (path, ":"); tmp; tmp = strtok (NULL, ":")) { len = strlen (tmp) + cmdlen + 2; if ((buf = (char *) malloc (len)) == NULL) { fprintf (stderr, "%s: %s: %s\n", appname, "malloc()", strerror (errno)); exit (errno); } strcpy (buf, tmp); strcat (buf, "/"); strcat (buf, command); if (access (buf, X_OK) == 0) { return (buf); /* free() me */ } /* Get rid of buf if we didn't find our executable yet. */ free (buf); } /* Obviously, we haven't found the full pathname of our command, so let's * return NULL. */ return (NULL); } int main (int argc, char **argv) { char *filename = NULL, **args = argv + 1; /* self-explanatory. */ struct stat sb; pid_t pid; /* used when fork()ing. Technically not necessary, but here for * correctness. */ /* Used in error and usage messages. */ appname = argv[0]; if (argc == 1) { /* Print usage and die. */ usage (); } if ((filename = which (args[0])) == NULL) { /* Since which() returned NULL, assume that the command name given is not * that of a valid, existing executable. Complain and die. */ fprintf (stderr, "%s: %s: %s\n", appname, args[0], strerror (errno)); exit (1); } if (stat (filename, &sb) < 0) { /* If something happened to make stat() unhappy, complain and die. */ fprintf (stderr, "%s: %s: %s\n", appname, filename, strerror (errno)); free (filename); exit (errno); } /* fork() time */ pid = fork (); /* UNIX mitosis */ if (pid == 0) { /* executed by the new child process */ if (execv (filename, args) < 0) { fprintf (stderr, "%s: %s: %s\n", appname, filename, strerror (errno)); exit (errno); } } else if (pid > 0) { /* executed by the original parent process */ struct timeval times[2]; /* wait for execution of the child process to end before setting the atime * and mtime back to their original values. */ wait (0); /* The first element of the struct timeval array declared above always * corresponds to the access time of an inode. The second one is always * the modification time. */ times[0].tv_sec = sb.st_atime; times[0].tv_usec = 0; times[1].tv_sec = sb.st_mtime; times[1].tv_usec = 0; if (utimes (filename, times) < 0) { /* This happens because the program did not have the permissions to * modify the inode's time stamps. Pity. */ fprintf (stderr, "%s: utimes(): %s: %s\n", appname, filename, strerror (errno)); } } else if (pid < 0) { /* We were unable to fork() altogether. */ fprintf (stderr, "%s: %s: %s\n", appname, "fork()", strerror (errno)); exit (errno); } /* Save the environment! */ free (filename); return (0); }