#!/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 <roy@nyu.edu>
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: <closes connection>
X *   E: <closes connection>
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 <sys/types.h>
X#include <sys/stat.h>
X#include <sys/ioctl.h>
X#include <sys/wait.h>
X#include <unistd.h>
X#include <fcntl.h>
X#include <syslog.h>
X#include <sgtty.h>
X#include <stdlib.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <strings.h>
X#include <errno.h>
X#include <varargs.h>
X#include <pwd.h>
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
