#!/bin/sh # This is a shell archive (shar 3.32) # made 02/11/1993 21:23 UTC by dorner@ux1.cso.uiuc.edu # Source directory /cso/staff/dorner # # existing files WILL be overwritten # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 15723 -rw-r--r-- poppassd.c # 2030 -rw-r--r-- Makefile # if touch 2>&1 | fgrep 'amc' > /dev/null then TOUCH=touch else TOUCH=true fi # ============= poppassd.c ============== echo "x - extracting poppassd.c (Text)" sed 's/^X//' << 'SHAR_EOF' > poppassd.c && X/* X * poppassd.c X * X * Daemon to service Eudora "Change Password" requests. This program X * doesn't actually change any passwords itself. It simply listens for X * incomming requests, gathers the required information (user name, old X * password, new password) and executes /bin/passwd, talking to it over X * a pseudo-terminal pair. The advantage of this is that we don't need X * to have any knowledge of either the password file format (which may X * include dbx files that need to be rebuilt) or of any file locking X * protocol /bin/passwd and cohorts may use (and which isn't documented). X * X * Note that unencrypted passwords are transmitted over the network. If X * this bothers you, think hard about whether you want to implement Eudora's X * password changing feature. On the other hand, it's no worse than what X * happens when you run /bin/passwd while connected via telnet or rlogin. X * Well, maybe it is, since the use of a dedicated port makes it slightly X * easier for a network snooper to snarf passwords off the wire. X * X * NOTE: In addition to the security issue outlined in the above paragraph, X * you should be aware that this program is going to be run as root by X * ordinary users and it mucks around with the password file. This should X * set alarms off in your head. I think I've devised a pretty foolproof X * way to ensure that security is maintained, but I'm no security expert and X * you would be a fool to install this without first reading the code and X * ensuring yourself that what I consider safe is good enough for you. If X * something goes wrong, it's your fault, not mine. X * X * This is an adaptation of a server by the same name from dll@mitre.org X * (Daniel L. Leavitt of The MITRE Corporation). The front-end code (which X * talks to the Eudora client) is directly descended from his original X * version. The back-end stuff (which talks to /bin/password) is mine. X * X * Should be owned by root, and executable only by root. It can be started X * with an entry in /etc/inetd.conf such as the following: X * X * poppassd stream tcp nowait /usr/etc/poppassd poppassd X * X * and in /etc/services: X * X * ppassd 106/tcp X * X * Roy Smith X * Department of Microbiology, X * NYU School of Medicine X * 550 First Avenue X * New York, NY 10016 X */ X X/* The server's responses should be like an FTP server's responses; X * 1xx for in progress, 2xx for success, 3xx for more information X * needed, 4xx for temporary failure, and 5xx for permanent failure. X * Putting it all together, here's a sample conversation: X * X * S: 200 hello\r\n X * E: user yourloginname\r\n X * S: 300 please send your password now\r\n X * E: pass yourcurrentpassword\r\n X * S: 200 My, that was tasty\r\n X * E: newpass yournewpassword\r\n X * S: 200 Happy to oblige\r\n X * E: quit\r\n X * S: 200 Bye-bye\r\n X * S: X * E: X */ X X#define SUCCESS 1 X#define FAILURE 0 X#define BUFSIZE 512 X#define MAXARGS 32 X#define SUPPRESS 1 X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X Xmain (argc, argv) Xint argc; Xchar *argv[]; X{ X char line[BUFSIZE]; X char user[BUFSIZE]; X char oldpass[BUFSIZE]; X char newpass[BUFSIZE]; X char logFile[BUFSIZE]; X char *slavedev; X struct passwd *pw, *getpwnam(); X int c, master; X pid_t pid, wpid; X union wait wstat; X X chdir ("/etc" ); X strcpy (user, "" ); X strcpy (oldpass, "" ); X strcpy (newpass, "" ); X X if (openlog ("poppassd", LOG_PID) < 0) X { X WriteToClient ("500 Can't open syslog."); X exit (1); X } X X WriteToClient ("200 hello, who are you?"); X ReadFromClient (line); X sscanf (line, "user %s", user) ; X if (strlen (user) == 0) X { X WriteToClient ("500 username required."); X exit(1); X } X X WriteToClient ("200 your password please."); X ReadFromClient (line); X sscanf (line, "pass %s", oldpass) ; X if (strlen (oldpass) == 0) X { X WriteToClient ("500 password required."); X exit(1); X } X X if ((pw = getpwnam (user)) == NULL) X { X WriteToClient ("500 Unknown user, %s.", user); X exit(1); X } X X if (chkPass (user, oldpass, pw) == FAILURE) X { X WriteToClient ("500 Authentication failure."); X exit(1); X } X X WriteToClient ("200 your new password please."); X ReadFromClient (line); X sscanf (line, "newpass %s", newpass); X X /* new pass required */ X if (strlen (newpass) == 0) X { X WriteToClient ("500 new password required."); X exit(1); X } X X /* new pass must be 6 char or longer */ X if (strlen (newpass) < 6 ) X { X WriteToClient ("500 New password too short"); X exit(1); X } X X /* new pass must be different from old pass */ X if (!strcmp (oldpass, newpass)) X { X WriteToClient ("500 New password must be different."); X exit(1); X } X X /* get pty to talk to password program */ X if ((master = findpty (&slavedev)) < 0) X { X syslog (LOG_ERR, "can't find pty"); X exit (1); X } X X /* fork child process to talk to password program */ X if ((pid = fork()) < 0) /* Error, can't fork */ X { X syslog (LOG_ERR, "can't fork for passwd: %m"); X WriteToClient ("500 Server error (can't fork passwd), get help!"); X return (0); X } X X if (pid) /* Parent */ X { X sleep (1); /* Make sure child is ready. Is this really needed? */ X if (talktochild (master, user, oldpass, newpass) == FAILURE) X { X syslog (LOG_ERR, "failed attempt by %s", user); X WriteToClient ("500 Unable to change password." ); X exit(1); X } X X if ((wpid = waitpid (pid, &wstat, 0)) < 0) X { X syslog (LOG_ERR, "wait for /bin/passwd child failed: %m"); X WriteToClient ("500 Server error (wait failed), get help!"); X exit (1); X } X X if (pid != wpid) X { X syslog (LOG_ERR, "wrong child (/bin/passwd waited for!"); X WriteToClient ("500 Server error (wrong child), get help!"); X return (0); X } X X if (WIFEXITED (wstat) == 0) X { X syslog (LOG_ERR, "child (/bin/passwd) killed?"); X WriteToClient ("500 Server error (funny wstat), get help!"); X exit (1); X } X X if (WEXITSTATUS (wstat) != 0) X { X syslog (LOG_ERR, "child (/bin/passwd) exited abnormally"); X WriteToClient ("500 Server error (abnormal exit), get help!"); X exit (1); X } X X if (hashpass() == 0) X exit (1); X X syslog (LOG_ERR, "password changed for %s", user); X WriteToClient ("200 Password changed, thank-you."); X exit (0); X } X else /* Child */ X { X /* X * Become the user trying who's password is being changed. We're X * about to exec /bin/passwd with is setuid root anyway, but this X * way it looks to the child completely like it's being run by X * the normal user, which makes it do its own password verification X * before doing any thing. In theory, we've already verified the X * password, but this extra level of checking doesn't hurt. Besides, X * the way I do it here, if somebody manages to change somebody X * else's password, you can complain to your vendor about security X * holes, not to me! X */ X setuid (pw->pw_uid); X setgid (pw->pw_gid); X dochild (slavedev, user); X } X} X Xdochild (slavedev, user) Xchar *slavedev, *user; X{ X int fd, fdmax, slave; X struct sgttyb sgbuf; X X /* X * Get rid of control terminal and X * close all open file descriptors. X */ X fd = open ("/dev/tty", O_RDWR, 0); X ioctl (fd, TIOCNOTTY, 0); X X fdmax = sysconf (_SC_OPEN_MAX); X for (fd = 0; fd < fdmax; fd++) X close (fd); X X /* X * Open slave side of pty (which now becomes the control terminal). X * Set appropriate tty modes and make sure stdin, stdout, and stderr X * are all connected to it. X */ X if ((slave = open (slavedev, O_RDWR)) < 0) X { X syslog (LOG_ERR, "Can't open slave pty %s: %m", slavedev); X return (0); X } X X ioctl (slave, TIOCGETP, &sgbuf); X sgbuf.sg_flags = ANYP; X ioctl (slave, TIOCSETP, &sgbuf); X X dup2 (slave, 0); X dup2 (slave, 1); X dup2 (slave, 2); X X /* X * This is probably just paranoia; if all fds are closed, slave X * should be fd 0. In that case, the dup2 (slave, 0) above is X * also just being paranoid. X */ X if (slave > 2) X (void) close(slave); X X /* X * Execute the "real" password program, which should now think X * it is talking to a normal user on a normal tty port. X */ X execl ("/bin/passwd", "passwd", user, (char *) 0); X X /* X * We only reach here if execl fails! It's not 100% clear what X * to do at this point. We can no longer talk to the client since X * we no longer have the client socket open. Just log a syslog X * message (and hope its gets logged, since it's not clear what X * evil things closing the syslog fd has done!) and count on the X * parent process to realize we've died. X */ X openlog ("poppassd (child)", LOG_PID); X syslog (LOG_ERR, "can't execl /bin/passwd: %m"); X exit (1); X} X X/* X * findpty() X * X * Finds the first available pseudo-terminal master/slave pair. The master X * side is opened and a fd returned as the function value. A pointer to the X * name of the slave side (i.e. "/dev/ttyp0") is returned in the argument, X * which should be a char**. The name itself is stored in a static buffer. X * X * A negative value is returned on any sort of error. X */ Xfindpty (slave) Xchar **slave; X{ X int c, i, master; X static char *line; X struct stat statbuf; X X for (c = 'p'; c <= 's'; c++) X { X line = "/dev/ptyXX"; X line[sizeof("/dev/pty")-1] = c; X line[sizeof("/dev/ptyp")-1] = '0'; X if (stat (line, &statbuf) < 0) X break; X X for (i = 0; i < 16; i++) X { X line[sizeof("/dev/ptyp")-1] = "0123456789abcdef"[i]; X if ((master = open(line, O_RDWR)) >= 0) X goto found_pty; X } X } X X syslog(LOG_ERR, "All network ports in use"); X return (-1); X X found_pty: X line[sizeof("/dev/")-1] = 't'; /* "/dev/ptyp0" --> "/dev/ttyp0" */ X *slave = line; X return (master); X} X X/* X * writestring() X * X * Write a string in a single write() system call. X */ Xwritestring (fd, s) Xchar *s; X{ X int l; X X l = strlen (s); X write (fd, s, l); X} X X/* X * talktochild() X * X * Handles the conversation between the parent and child (password program) X * processes. This is where you want to customize the conversation script. X * X * Returns SUCCESS is the conversation is completed without any problems, X * FAILURE if any errors are encountered (in which case, it can be assumed X * that the password wasn't changed). X */ Xtalktochild (master, user, oldpass, newpass) Xint master; Xchar *user, *oldpass, *newpass; X{ X int n; X char buf[500]; X X sprintf (buf, "Changing password for %s\n", user); X if (!expect (master, buf)) X return (FAILURE); X X if (!expect (master, "Old password: ")) X return (FAILURE); X X sprintf (buf, "%s\n", oldpass); X writestring (master, buf); X X if (!expect (master, "\n") || !expect (master, "Enter new password: ")) X return (FAILURE); X X sprintf (buf, "%s\n", newpass); X writestring (master, buf); X X if (!expect (master, "\n") || !expect (master, "Verify: ")) X return (FAILURE); X X writestring (master, buf); X if (!expect (master, "\n") || !expect (master, "")) X return (FAILURE); X X return (SUCCESS); X} X X/* X * expect () X * X * Read the next line and check to make sure it is an exact match for X * the string expected, including newlines. If the expected string is X * empty, it's considered to match either EOF, or getting an EWOULDBLOCK X * error, which would typically occur if the child has already exited. X * X * A syslog error message is generated and expect returns 0 on any error X * or mismatch. If the expected string is found, expect() returns 1. X */ Xexpect (fd, s) Xint fd; Xchar *s; X{ X int n; X char buf[1000]; X extern int errno; X X if ((n = read (fd, buf, 999)) < 0) X { X if (errno == EWOULDBLOCK && *s == NULL) X return (1); X X syslog (LOG_ERR, "read error from child: %m"); X return (0); X } X X buf[n] = 0; X if (strcmp (s, buf) != 0) X { X syslog (LOG_ERR, "wrong response from child --"); X syslog (LOG_ERR, "expected %s", s); X syslog (LOG_ERR, "got %s", buf); X return (0); X } X X return (1); X} X XWriteToClient (va_alist) Xva_dcl X{ X va_list ap; X char *fmt; X char *args[MAXARGS]; X int argno = 0; X char string[BUFSIZE]; X X va_start (ap); X fmt = va_arg (ap, char *); X while ((args[argno++] = va_arg(ap, char *)) != (char *)0) X ; X X vfprintf (stdout, fmt, args); X fputs ("\r\n", stdout ); X fflush (stdout); X X vsprintf (string, fmt, args); X X va_end (ap); X} X XReadFromClient (line) Xchar *line; X{ X char *sp; X int i; X X strcpy (line, ""); X fgets (line, BUFSIZE, stdin); X if ((sp = strchr(line, '\n')) != NULL) *sp = '\0'; X if ((sp = strchr(line, '\r')) != NULL) *sp = '\0'; X} X Xint chkPass (user, pass, pw) Xchar *user; Xchar *pass; Xstruct passwd *pw; X{ X /* Compare the supplied password with the password file entry */ X if (strcmp (crypt (pass, pw->pw_passwd), pw->pw_passwd) != 0) X return (FAILURE); X else X return (SUCCESS); X} X X/* X * hashpass() X * X * Execute mkpasswd to build the hashed (dbx) passwd files. X */ Xhashpass() X{ X pid_t pid, wpid; X union wait wstat; X int fd; X X /* fork child process to talk to run mkpasswd */ X if ((pid = fork()) < 0) /* Error, can't fork */ X { X syslog (LOG_ERR, "can't fork for mkpassd: %m"); X WriteToClient ("500 Server error (can't fork mkpasswd), get help!"); X return (0); X } X X if (pid) /* Parent */ X { X if ((wpid = waitpid (pid, &wstat, 0)) < 0) X { X syslog (LOG_ERR, "wait for mkpasswd child failed: %m"); X WriteToClient ("500 Server error (wait failed), get help!"); X return (0); X } X X if (pid != wpid) X { X syslog (LOG_ERR, "wrong child waited for in hashpass()!"); X WriteToClient ("500 Server error (wrong child), get help!"); X return (0); X } X X if (WIFEXITED (wstat) == 0) X { X syslog (LOG_ERR, "child (mkpasswd) killed?"); X WriteToClient ("500 Server error (funny wstat), get help!"); X return (0); X } X X if (WEXITSTATUS (wstat) != 0) X { X syslog (LOG_ERR, "child (mkpasswd) exited abnormally"); X WriteToClient ("500 Server error (abnormal exit), get help!"); X return (0); X } X X return (1); /* mkpasswd executed normally */ X } X else /* Child */ X { X /* X * Making stdout and stderr /dev/null keeps mkpasswd messages (such X * as the one that says "669 password entries, maximum length 125") X * from being visible to the client. If you are doing debugging, X * just change /dev/null to /tmp/mkpasswd.std{out,err}, or whatever. X */ X close (1); X fd = open ("/dev/null", O_RDWR, 0); X if (fd != 1) X { X dup2 (fd, 1); X close (fd); X } X close (2); X fd = open ("/dev/null", O_RDWR, 0); X if (fd != 2) X { X dup2 (fd, 2); X close (fd); X } X X execl ("/etc/mkpasswd", "mkpasswd", "/etc/passwd", (char *) 0); X X /* X * We only reach here if execl fails! X */ X syslog (LOG_ERR, "can't execl /etc/mkpasswd: %m"); X WriteToClient ("500 Server error (can't exec mkpasswd), get help!"); X exit (1); X } X} SHAR_EOF $TOUCH -am 0211152293 poppassd.c && chmod 0644 poppassd.c || echo "restore of poppassd.c failed" set `wc -c poppassd.c`;Wc_c=$1 if test "$Wc_c" != "15723"; then echo original size 15723, current size $Wc_c fi # ============= Makefile ============== echo "x - extracting Makefile (Text)" sed 's/^X//' << 'SHAR_EOF' > Makefile && XBINDIR = /usr/etc XLIBDIR = XCFLAGS = -g XLFLAGS = -g XCCM = cc -Em X XOBJECTS = poppassd.o XLIBS = X Xpoppassd: $(OBJECTS) X cc -o poppassd $(LFLAGS) $(OBJECTS) $(LIBS) X Xinstall: poppassd X install -g bin -o root -m 500 poppassd $(BINDIR) X Xclean: X rm -f *.o *~* core Makefile.new Makefile.bak poppassd X Xdepend: X cp Makefile Makefile.old X sed -n -e "/^BINDIR =/s|=.*|= $(BINDIR)|" \ X -e "/^LIBDIR =/s|=.*|= $(LIBDIR)|" \ X -e "/^CFLAGS =/s|=.*|= $(CFLAGS)|" \ X -e "/^LFLAGS =/s|=.*|= $(LFLAGS)|" \ X -e "/^CCM =/s|=.*|= $(CCM)|" \ X -e "1,/^# DO NOT DELETE THIS LINE/p" \ X Makefile > Makefile.new X for i in $(OBJECTS); do \ X echo; \ X cfile=`basename $$i .o`.c; \ X $(CCM) $$cfile; \ X echo ' cc -c' '$$(CFLAGS)' "$$cfile"; \ X done >> Makefile.new X mv Makefile.new Makefile X X# DO NOT DELETE THIS LINE -- make depend uses it X Xpoppassd.o: poppassd.c Xpoppassd.o: /usr/include/sys/types.h Xpoppassd.o: /usr/include/ansi_compat.h Xpoppassd.o: /usr/include/sys/stat.h Xpoppassd.o: /usr/include/ansi_compat.h Xpoppassd.o: /usr/include/sys/ioctl.h Xpoppassd.o: /usr/include/ansi_compat.h Xpoppassd.o: /usr/include/sys/ttychars.h Xpoppassd.o: /usr/include/sys/ttydev.h Xpoppassd.o: /usr/include/sys/ttyio.h Xpoppassd.o: /usr/include/sgtty.h Xpoppassd.o: /usr/include/ansi_compat.h Xpoppassd.o: /usr/include/unistd.h Xpoppassd.o: /usr/include/fcntl.h Xpoppassd.o: /usr/include/sys/file.h Xpoppassd.o: /usr/include/ansi_compat.h Xpoppassd.o: /usr/include/sys/types.h Xpoppassd.o: /usr/include/syslog.h Xpoppassd.o: /usr/include/sgtty.h Xpoppassd.o: /usr/include/stdlib.h Xpoppassd.o: /usr/include/ansi_compat.h Xpoppassd.o: /usr/include/stdio.h Xpoppassd.o: /usr/include/ansi_compat.h Xpoppassd.o: /usr/include/ctype.h Xpoppassd.o: /usr/include/strings.h Xpoppassd.o: /usr/include/errno.h Xpoppassd.o: /usr/include/varargs.h Xpoppassd.o: /usr/include/ansi_compat.h Xpoppassd.o: /usr/include/stamp.h Xpoppassd.o: /usr/include/ansi_compat.h Xpoppassd.o: /usr/include/pwd.h Xpoppassd.o: /usr/include/ansi_compat.h Xpoppassd.o: /usr/include/sys/types.h X cc -c $(CFLAGS) poppassd.c SHAR_EOF $TOUCH -am 0211152393 Makefile && chmod 0644 Makefile || echo "restore of Makefile failed" set `wc -c Makefile`;Wc_c=$1 if test "$Wc_c" != "2030"; then echo original size 2030, current size $Wc_c fi exit 0 .