--- poppassd.c.orig	Mon Jul  7 15:15:03 2003
+++ poppassd.c	Mon Jul  7 15:17:46 2003
@@ -13,11 +13,11 @@
  * 
  * Doesn't actually change any passwords itself.  It simply listens for
  * incoming requests, gathers the required information (user name, old
- * password, new password) and executes /bin/passwd, talking to it over
+ * password, new password) and executes /usr/bin/passwd, talking to it over
  * a pseudo-terminal pair.  The advantage of this is that we don't need
  * to have any knowledge of either the password file format (which may
  * include dbx files that need to be rebuilt) or of any file locking
- * protocol /bin/passwd and cohorts may use (and which isn't documented).
+ * protocol /usr/bin/passwd and cohorts may use (and which isn't documented).
  *
  * The current version has been tested at NU under SunOS release 4.1.2 
  * and 4.1.3, and under HP-UX 8.02 and 9.01. We have tested the server 
@@ -29,7 +29,7 @@
  * Note that unencrypted passwords are transmitted over the network.  If
  * this bothers you, think hard about whether you want to implement the
  * password changing feature.  On the other hand, it's no worse than what
- * happens when you run /bin/passwd while connected via telnet or rlogin.
+ * happens when you run /usr/bin/passwd while connected via telnet or rlogin.
  * Well, maybe it is, since the use of a dedicated port makes it slightly
  * easier for a network snooper to snarf passwords off the wire.
  *
@@ -47,7 +47,7 @@
  * (which talks to /bin/password) is directly descended from Smith's
  * version, with changes for SunOS and HP-UX by Norstad (with help from
  * sample code in "Advanced Programming in the UNIX Environment"
- * by W. Richard Stevens). The code to report /bin/passwd error messages
+ * by W. Richard Stevens). The code to report /usr/bin/passwd error messages
  * back to the client in the final 500 response, and a new version of the
  * code to find the next free pty, is by Norstad.
  *        
@@ -145,8 +145,11 @@
 static char *P1[] =
    {"Old password:",
     "Changing password for *.\nOld password:",
+    "Changing local password for *.\nOld password:",
+    "Changing local password for *\nOld Password:",
     "Changing password for * on *.\nOld password:",
     "Changing NIS password for * on *.\nOld password:",
+    "Changing NIS password for *\nOld Password:",
     "Changing password for *\n*'s Old password:",
     ""};
 
@@ -165,7 +168,10 @@
     
 static char *P4[] =
    {"\n",
+    "\npasswd: rebuilding the database...\npasswd: done\n",
+    "\npasswd: updating the database...\npasswd: done\n",
     "NIS entry changed on *\n",
+    "\n\nNIS password has been changed on *.\n",
     ""};
 
 
@@ -180,17 +186,14 @@
      char emess[BUFSIZE];
      char *slavedev;
      struct passwd *pw, *getpwnam();
+     struct termios stermios;
      int c, master;
      pid_t pid, wpid;
      int wstat;
      
      *user = *oldpass = *newpass = 0;
      
-     if (openlog ("poppassd", LOG_PID, LOG_LOCAL2) < 0)
-     {
-	  WriteToClient ("500 Can't open syslog.");
-	       exit (1);
-     }
+     openlog ("poppassd", LOG_PID, LOG_LOCAL2);
      
      WriteToClient ("200 poppassd v%s hello, who are you?", VERSION);
      ReadFromClient (line);
@@ -212,12 +215,16 @@
      
      if ((pw = getpwnam (user)) == NULL)
      {
-	  WriteToClient ("500 Unknown user, %s.", user);
+	  syslog (LOG_ERR, "Unknown user, %s", user);
+	  sleep (5);
+	  WriteToClient ("500 Old password is incorrect.");
 	  exit(1);
      }
 
      if (chkPass (user, oldpass, pw) == FAILURE)
      {
+	  syslog (LOG_ERR, "Incorrect password from %s", user);
+	  sleep (5);
 	  WriteToClient ("500 Old password is incorrect.");
 	  exit(1);
      }
@@ -232,99 +239,96 @@
 	  WriteToClient ("500 New password required.");
 	  exit(1);
      }
-     /* get pty to talk to password program */
-     if ((master = findpty (&slavedev)) < 0)
-     {
-	  syslog (LOG_ERR, "can't find pty");
-          WriteToClient("500 Server busy - try again later.");
-	  exit (1);
-     }
-	 
-     /* fork child process to talk to password program */
-     if ((pid = fork()) < 0)     /* Error, can't fork */
-     {
-	  syslog (LOG_ERR, "can't fork for passwd: %m");
-	  WriteToClient ("500 Server error (can't fork passwd), get help!");
-	  exit (1);
-     }
 
-     if (pid)   /* Parent */
-     {
-	  sleep (1);    /* Make sure child is ready.  Is this really needed? */
-	  if (talktochild (master, user, oldpass, newpass, emess) == FAILURE)
-	  {
-	       syslog (LOG_ERR, "failed attempt by %s", user);
-	       if (*emess == '\0') {
-	          WriteToClient ("500 Unable to change password." );
-               } else {
-		  WriteToClient ("500 %s", emess);
-               }
-	       exit(1);
-	  }
-
-	  if ((wpid = waitpid (pid, &wstat, 0)) < 0)
-	  {
-	       syslog (LOG_ERR, "wait for /bin/passwd child failed: %m");
-	       WriteToClient ("500 Server error (wait failed), get help!");
-	       exit (1);
-	  }
-
-	  if (pid != wpid)
-	  {
-	       syslog (LOG_ERR, "wrong child (/bin/passwd waited for!");
-	       WriteToClient ("500 Server error (wrong child), get help!");
-	       exit (1);
-	  }
-
-	  if (WIFEXITED (wstat) == 0)
-	  {
-	       syslog (LOG_ERR, "child (/bin/passwd) killed?");
-	       WriteToClient ("500 Server error (funny wstat), get help!");
-	       exit (1);
-	  }
-
-	  if (WEXITSTATUS (wstat) != 0)
-	  {
-	       syslog (LOG_ERR, "child (/bin/passwd) exited abnormally");
-	       WriteToClient ("500 Server error (abnormal exit), get help!");
-	       exit (1);
-	  }
-
-	  syslog (LOG_ERR, "password changed for %s", user);
-	  WriteToClient ("200 Password changed, thank-you.");
-
-          ReadFromClient (line);
-	  if (strncmp(line, "quit", 4) != 0) {
-	  	WriteToClient("500 Quit required.");
+	/* we need a pty to run passwd on but we have to make sure it is set up
+	   as we like it - no echo, canonical input processing, no map NL to CR/NL
+	   on outputs - otherwise our expect function will be confused */
+	(void)memset((void *)&stermios, 0, sizeof(stermios));
+	stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+	stermios.c_lflag |= ICANON;
+	stermios.c_oflag &= ~(ONLCR);
+
+	/* get a pty and fork */
+	switch(pid = forkpty(&master, NULL, &stermios, NULL)) {
+	case -1:
+		/* failure - can't get pty, can't fork etc */
+		WriteToClient("500 Server Error - Contact Your Administrator");
+		exit(1);
+		break;
+	case 0:
+		/* slave/child */
+		/* Set login name */
+		if (setlogin(user) < 0) {
+			syslog(LOG_ERR, "setlogin failed: %m");
+			WriteToClient("500 Server Error - Contact Your Administrator");
+			return(0);
+		}
+		setuid (pw->pw_uid);
+		setgid (pw->pw_gid);
+		dochild (master, NULL, user);
+		break;
+	default:
+		/* master/parent */
+		sleep (1);    /* Make sure child is ready.  Is this really needed? */
+		if (talktochild (master, user, oldpass, newpass, emess) == FAILURE)
+		{
+			syslog (LOG_ERR, "failed attempt by %s", user);
+			if (*emess == '\0') {
+				WriteToClient ("500 Unable to change password." );
+			} else {
+				WriteToClient ("500 %s", emess);
+			}
+			exit(1);
+		}
+		
+		break;
+	}
+
+	if ((wpid = waitpid (pid, &wstat, 0)) < 0)
+	{
+		syslog (LOG_ERR, "wait for /usr/bin/passwd child failed: %m");
+		WriteToClient ("500 Server error (wait failed), get help!");
+		exit (1);
+	}
+
+	if (pid != wpid)
+	{
+	     syslog (LOG_ERR, "wrong child (/usr/bin/passwd) waited for!");
+	     WriteToClient ("500 Server error (wrong child), get help!");
+	     exit (1);
+	}
+
+	if (WIFEXITED (wstat) == 0)
+	{
+	     syslog (LOG_ERR, "child (/usr/bin/passwd) killed?");
+	     WriteToClient ("500 Server error (funny wstat), get help!");
+	     exit (1);
+	}
+
+	if (WEXITSTATUS (wstat) != 0)
+	{
+	     syslog (LOG_ERR, "child (/usr/bin/passwd) exited abnormally");
+	     WriteToClient ("500 Server error (abnormal exit), get help!");
+	     exit (1);
+	}
+
+	syslog (LOG_ERR, "password changed for %s", user);
+	WriteToClient ("200 Password changed, thank-you.");
+
+    ReadFromClient (line);
+	if (strncmp(line, "quit", 4) != 0) {
+		WriteToClient("500 Quit required.");
 		exit (1);
-	  }
+	}
 	  
-	  WriteToClient("200 Bye.");
-	  exit (0);
-     }
-     else      /* Child */
-     {
-	  /*
-	   * Become the user trying who's password is being changed.  We're
-	   * about to exec /bin/passwd with is setuid root anyway, but this
-	   * way it looks to the child completely like it's being run by
-	   * the normal user, which makes it do its own password verification
-	   * before doing any thing.  In theory, we've already verified the
-	   * password, but this extra level of checking doesn't hurt.  Besides,
-	   * the way I do it here, if somebody manages to change somebody
-	   * else's password, you can complain to your vendor about security
-	   * holes, not to me!
-	   */
-	  setuid (pw->pw_uid);
-	  setgid (pw->pw_gid);
-	  dochild (master, slavedev, user);
-     }
+	WriteToClient("200 Bye.");
+	exit (0);
 }
 
 /*
  * dochild
  *
- * Do child stuff - set up slave pty and execl /bin/passwd.
+ * Do child stuff - set up slave pty and execl /usr/bin/passwd.
  *
  * Code adapted from "Advanced Programming in the UNIX Environment"
  * by W. Richard Stevens.
@@ -335,105 +339,14 @@
 int master;
 char *slavedev, *user;
 {
-   int slave;
-   struct termios stermios;
+   /* Fork /usr/bin/passwd. */
 
-   /* Start new session - gets rid of controlling terminal. */
-   
-   if (setsid() < 0) {
-      syslog(LOG_ERR, "setsid failed: %m");
-      return(0);
-   }
-
-   /* Open slave pty and acquire as new controlling terminal. */
-
-   if ((slave = open(slavedev, O_RDWR)) < 0) {
-      syslog(LOG_ERR, "can't open slave pty: %m");
-      return(0);
-   }
-
-   /* Close master. */
-
-   close(master);
-
-   /* Make slave stdin/out/err of child. */
-
-   if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) {
-      syslog(LOG_ERR, "dup2 error to stdin: %m");
-      return(0);
-   }
-   if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
-      syslog(LOG_ERR, "dup2 error to stdout: %m");
-      return(0);
-   }
-   if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
-      syslog(LOG_ERR, "dup2 error to stderr: %m");
-      return(0);
-   }
-   if (slave > 2) close(slave);
-
-   /* Set proper terminal attributes - no echo, canonical input processing,
-      no map NL to CR/NL on output. */
-
-   if (tcgetattr(0, &stermios) < 0) {
-      syslog(LOG_ERR, "tcgetattr error: %m");
-      return(0);
-   }
-   stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
-   stermios.c_lflag |= ICANON;
-   stermios.c_oflag &= ~(ONLCR);
-   if (tcsetattr(0, TCSANOW, &stermios) < 0) {
-      syslog(LOG_ERR, "tcsetattr error: %m");
-      return(0);
-   }
-
-   /* Fork /bin/passwd. */
-
-   if (execl("/bin/passwd", "passwd", user, (char*)0) < 0) {
-      syslog(LOG_ERR, "can't exec /bin/passwd: %m");
+   if (execl("/usr/bin/passwd", "passwd", user, (char*)0) < 0) {
+      syslog(LOG_ERR, "can't exec /usr/bin/passwd: %m");
       return(0);
    }
 }
 
-
-/*
- * findpty()
- *
- * Finds the first available pseudo-terminal master/slave pair.  The master
- * side is opened and a fd returned as the function value.  A pointer to the
- * name of the slave side (i.e. "/dev/ttyp0") is returned in the argument,
- * which should be a char**.  The name itself is stored in a static buffer.
- *
- * A negative value is returned on any sort of error.
- *
- * Modified by Norstad to remove assumptions about number of pty's allocated
- * on this UNIX box.
- */
-findpty (slave)
-char **slave;
-{
-   int master;
-   static char *line = "/dev/ptyXX";
-   DIR *dirp;
-   struct dirent *dp;
-
-   dirp = opendir("/dev");
-   while ((dp = readdir(dirp)) != NULL) {
-      if (strncmp(dp->d_name, "pty", 3) == 0 && strlen(dp->d_name) == 5) {
-         line[8] = dp->d_name[3];
-         line[9] = dp->d_name[4];
-         if ((master = open(line, O_RDWR)) >= 0) {
-            line[5] = 't';
-            *slave = line;
-            closedir(dirp);
-            return (master);
-         }
-      }
-   }
-   closedir(dirp);
-   return (-1);
-}
-
 /*
  * writestring()
  *
@@ -485,8 +398,10 @@
      }
 
      writestring(master, pswd);
-
+     sleep(2);
      if (!expect(master, P4, buf)) return FAILURE;
+
+     close(master);
 
      return SUCCESS;
 }
