aboutsummaryrefslogtreecommitdiff
path: root/mail/poppassd/files/patch-poppassd.c
diff options
context:
space:
mode:
Diffstat (limited to 'mail/poppassd/files/patch-poppassd.c')
-rw-r--r--mail/poppassd/files/patch-poppassd.c552
1 files changed, 552 insertions, 0 deletions
diff --git a/mail/poppassd/files/patch-poppassd.c b/mail/poppassd/files/patch-poppassd.c
new file mode 100644
index 000000000000..e4169d6097b8
--- /dev/null
+++ b/mail/poppassd/files/patch-poppassd.c
@@ -0,0 +1,552 @@
+--- poppassd.c.orig Thu Jan 4 23:19:36 2007
++++ poppassd.c Thu Jan 4 23:24:26 2007
+@@ -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.
+ *
+@@ -44,10 +44,10 @@
+ *
+ * The front-end code (which talks to the client) is directly
+ * descended from Leavitt's original version. The back-end pseudo-tty stuff
+- * (which talks to /bin/password) is directly descended from Smith's
++ * (which talks to /usr/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.
+ *
+@@ -112,7 +112,7 @@
+
+ #define SUCCESS 1
+ #define FAILURE 0
+-#define BUFSIZE 512
++#define BUFSIZE 768
+
+ #include <sys/types.h>
+ #include <sys/stat.h>
+@@ -125,12 +125,23 @@
+ #include <ctype.h>
+ #include <strings.h>
+ #include <errno.h>
+-#include <varargs.h>
++#include <stdarg.h>
+ #include <pwd.h>
+ #include <string.h>
+ #include <termios.h>
+ #include <dirent.h>
++#include <libutil.h>
+
++/* prototypes to keep modern compilers happy */
++void ReadFromClient (char *line);
++void WriteToClient (char *fmt, ...);
++int chkPass (char *user, char *pass, struct passwd *pw);
++int dochild (int master, char *slavedev, char *user);
++int expect (int master, char **expected, char *buf);
++void getemess (int master, char **expected, char *buf);
++int match (char *str, char *pat);
++int talktochild (int master, char *user, char *oldpass, char *newpass, char *emess);
++void writestring (int fd, char *s);
+
+ /* Prompt strings expected from the "passwd" command. If you want
+ * to port this program to yet another flavor of UNIX, you may need to add
+@@ -145,19 +156,24 @@
+ 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:",
+ ""};
+
+ static char *P2[] =
+ {"\nNew password:",
++ "\n*\n*\n*\n*\n*\n*\n*\n*\n*\n*\n*\nEnter new password: ",
+ "\n*'s New password:",
+ ""};
+
+ static char *P3[] =
+ {"\nRe-enter new password:",
+ "\nRetype new password:",
++ "\nRe-type new password: ",
+ "\nEnter the new password again:",
+ "\n*Re-enter *'s new password:",
+ "\nVerify:",
+@@ -165,13 +181,14 @@
+
+ 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",
+ ""};
+
+
+-main (argc, argv)
+-int argc;
+-char *argv[];
++int main (int argc, char *argv[])
+ {
+ char line[BUFSIZE];
+ char user[BUFSIZE];
+@@ -180,17 +197,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 +226,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,215 +250,118 @@
+ 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 Password change unsuccessful, try a different 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.
+ *
+ */
+
+-dochild (master, slavedev, user)
+-int master;
+-char *slavedev, *user;
++int dochild (int master, char *slavedev, char *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()
+ *
+ * Write a string in a single write() system call.
+ */
+-writestring (fd, s)
+-char *s;
++void writestring (int fd, char *s)
+ {
+ int l;
+
+@@ -458,9 +379,7 @@
+ * FAILURE if any errors are encountered (in which case, it can be assumed
+ * that the password wasn't changed).
+ */
+-talktochild (master, user, oldpass, newpass, emess)
+-int master;
+-char *user, *oldpass, *newpass, *emess;
++int talktochild (int master, char *user, char *oldpass, char *newpass, char *emess)
+ {
+ char buf[BUFSIZE];
+ char pswd[BUFSIZE+1];
+@@ -485,9 +404,11 @@
+ }
+
+ writestring(master, pswd);
+-
++ sleep(2);
+ if (!expect(master, P4, buf)) return FAILURE;
+
++ close(master);
++
+ return SUCCESS;
+ }
+
+@@ -507,9 +428,7 @@
+ * the pattern.
+ * 2 if the string matches the full pattern.
+ */
+-match (str, pat)
+-char *str;
+-char *pat;
++int match (char *str, char *pat)
+ {
+ int result;
+
+@@ -542,10 +461,7 @@
+ * is not an initial segment of or exact match for at least one of
+ * the expected strings, the function returns FAILURE.
+ */
+-expect (master, expected, buf)
+-int master;
+-char **expected;
+-char *buf;
++int expect (int master, char **expected, char *buf)
+ {
+ int n, m;
+ char **s;
+@@ -596,10 +512,7 @@
+ * returning just the error message text. Newlines in the error message
+ * text are replaced by spaces.
+ */
+-getemess (master, expected, buf)
+-int master;
+-char **expected;
+-char *buf;
++void getemess (int master, char **expected, char *buf)
+ {
+ int n, m;
+ char **s;
+@@ -630,21 +543,18 @@
+ }
+ }
+
+-WriteToClient (fmt, va_alist)
+-char *fmt;
+-va_dcl
++void WriteToClient (char *fmt, ...)
+ {
+ va_list ap;
+
+- va_start (ap);
++ va_start (ap, fmt);
+ vfprintf (stdout, fmt, ap);
+ fputs ("\r\n", stdout );
+ fflush (stdout);
+ va_end (ap);
+ }
+
+-ReadFromClient (line)
+-char *line;
++void ReadFromClient (char *line)
+ {
+ char *sp;
+ int i;
+@@ -659,10 +569,7 @@
+ for (sp = line; isalpha(*sp); sp++) *sp = tolower(*sp);
+ }
+
+-int chkPass (user, pass, pw)
+-char *user;
+-char *pass;
+-struct passwd *pw;
++int chkPass (char *user, char *pass, struct passwd *pw)
+ {
+ /* Compare the supplied password with the password file entry */
+ if (strcmp (crypt (pass, pw->pw_passwd), pw->pw_passwd) != 0)