home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.qualcomm.com
/
2014.06.ftp.qualcomm.com.tar
/
ftp.qualcomm.com
/
eudora
/
servers
/
unix
/
password
/
pwserve-4
< prev
next >
Wrap
Text File
|
1997-03-26
|
22KB
|
768 lines
/*
* poppassd.c
*
* John Norstad
* Northwestern University
* j-norstad@nwu.edu
*
* IMPORTANT NOTE:
*
* Please do not write to me asking for help getting this program running
* on your system. You are on your own. If you send me email about this
* program, I will not read it, and I will not reply to it. Sorry, but the
* only alternative is to not distribute it at all.
*
* Based on earlier versions by Roy Smith <roy@nyu.edu> and Daniel
* L. Leavitt <dll.mitre.org>.
#!/bin/sh
# This is a shell archive (shar 3.32)
# made 01/28/1994 20:35 UTC by sdorner@ux1.cso.uiuc.edu
# Source directory /cso/staff/sdorner
#
# existing files WILL be overwritten
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 345 -rw-r--r-- poppassd/Makefile
# 138 -rw-r--r-- poppassd/README
# 18598 -rw-r--r-- poppassd/poppassd.c
#
if touch 2>&1 | fgrep 'amc' > /dev/null
then TOUCH=touch
else TOUCH=true
fi
# ============= poppassd/Makefile ==============
if test ! -d 'poppassd'; then
echo "x - creating directory poppassd"
mkdir 'poppassd'
fi
echo "x - extracting poppassd/Makefile (Text)"
sed 's/^X//' << 'SHAR_EOF' > poppassd/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
Xpoppassd.o: poppassd.c
X cc -c $(CFLAGS) poppassd.c
SHAR_EOF
$TOUCH -am 0128143394 poppassd/Makefile &&
chmod 0644 poppassd/Makefile ||
echo "restore of poppassd/Makefile failed"
set `wc -c poppassd/Makefile`;Wc_c=$1
if test "$Wc_c" != "345"; then
echo original size 345, current size $Wc_c
fi
# ============= poppassd/README ==============
echo "x - extracting poppassd/README (Text)"
sed 's/^X//' << 'SHAR_EOF' > poppassd/README &&
Xpoppassd is a password change server for Eudora and NUPOP.
XSee the poppassd.c source file for more details and
Xinstallation instructions.
SHAR_EOF
$TOUCH -am 0128143394 poppassd/README &&
chmod 0644 poppassd/README ||
echo "restore of poppassd/README failed"
set `wc -c poppassd/README`;Wc_c=$1
if test "$Wc_c" != "138"; then
echo original size 138, current size $Wc_c
fi
# ============= poppassd/poppassd.c ==============
echo "x - extracting poppassd/poppassd.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > poppassd/poppassd.c &&
X/*
X * poppassd.c
X *
X * A Eudora and NUPOP change password server.
X *
X * John Norstad
X * Academic Computing and Network Services
X * Northwestern University
X * j-norstad@nwu.edu
X *
X * Based on earlier versions by Roy Smith <roy@nyu.edu> and Daniel
X * L. Leavitt <dll.mitre.org>.
X *
X * Doesn't actually change any passwords itself. It simply listens for
X * incoming 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 * The current version has been tested at NU under SunOS release 4.1.2
X * and 4.1.3, and under HP-UX 8.02 and 9.01. We have tested the server
X * with both Eudora 1.3.1 and NUPOP 2.0.
X *
X * Other sites report that this version also works under AIX and NIS,
X * and with PC Eudora.
X *
X * Note that unencrypted passwords are transmitted over the network. If
X * this bothers you, think hard about whether you want to implement the
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 * The front-end code (which talks to the client) is directly
X * descended from Leavitt's original version. The back-end pseudo-tty stuff
X * (which talks to /bin/password) is directly descended from Smith's
X * version, with changes for SunOS and HP-UX by Norstad (with help from
X * sample code in "Advanced Programming in the UNIX Environment"
X * by W. Richard Stevens). The code to report /bin/passwd error messages
X * back to the client in the final 500 response, and a new version of the
X * code to find the next free pty, is by Norstad.
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 root /usr/local/bin/poppassd poppassd
X *
X * and in /etc/services:
X *
X * poppassd 106/tcp
X *
X * Logs to the local2 facility. Should have an entry in /etc/syslog.conf
X * like the following:
X *
X * local2.err /var/adm/poppassd-log
X */
X
X/* Modification history.
X *
X * 06/09/93. Version 1.0.
X *
X * 06/29/93. Version 1.1.
X * Include program name 'poppassd' and version number in initial
X * hello message.
X * Case insensitive command keywords (user, pass, newpass, quit).
X * Fixes problem reported by Raoul Schaffner with PC Eudora.
X * Read 'quit' command from client instead of just terminating after
X * password change.
X * Add new code for NIS support (contributed by Max Caines).
X *
X * 08/31/93. Version 1.2.
X * Generalized the expected string matching to solve several problems
X * with NIS and AIX. The new "*" character in pattern strings
X * matches any sequence of 0 or more characters.
X * Fix an error in the "getemess" function which could cause the
X * program to hang if more than one string was defined in the
X * P2 array.
X */
X
X/* Steve Dorner's description of the simple protocol:
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 VERSION "1.2"
X
X#define SUCCESS 1
X#define FAILURE 0
X#define BUFSIZE 512
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/wait.h>
X#include <unistd.h>
X#include <fcntl.h>
X#include <syslog.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#include <string.h>
X#include <termios.h>
X#include <dirent.h>
X
X
X/* Prompt strings expected from the "passwd" command. If you want
X * to port this program to yet another flavor of UNIX, you may need to add
X * more prompt strings here.
X *
X * Each prompt is defined as an array of pointers to alternate
X * strings, terminated by an empty string. In the strings, '*'
X * matches any sequence of 0 or more characters. Pattern matching
X * is case-insensitive.
X */
X
Xstatic char *P1[] =
X {"Old password:",
X "Changing password for *.\nOld password:",
X "Changing password for * on *.\nOld password:",
X "Changing NIS password for * on *.\nOld password:",
X "Changing password for *\n*'s Old password:",
X ""};
X
Xstatic char *P2[] =
X {"\nNew password:",
X "\n*'s New password:",
X ""};
X
Xstatic char *P3[] =
X {"\nRe-enter new password:",
X "\nRetype new password:",
X "\nEnter the new password again:",
X "\n*Re-enter *'s new password:",
X "\nVerify:",
X ""};
X
Xstatic char *P4[] =
X {"\n",
X "NIS entry changed on *\n",
X ""};
X
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 emess[BUFSIZE];
X char *slavedev;
X struct passwd *pw, *getpwnam();
X int c, master;
X pid_t pid, wpid;
X int wstat;
X
X *user = *oldpass = *newpass = 0;
X
X if (openlog ("poppassd", LOG_PID, LOG_LOCAL2) < 0)
X {
X WriteToClient ("500 Can't open syslog.");
X exit (1);
X }
X
X WriteToClient ("200 poppassd v%s hello, who are you?", VERSION);
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 Old password is incorrect.");
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 /* get pty to talk to password program */
X if ((master = findpty (&slavedev)) < 0)
X {
X syslog (LOG_ERR, "can't find pty");
X WriteToClient("500 Server busy - try again later.");
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 exit (1);
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, emess) == FAILURE)
X {
X syslog (LOG_ERR, "failed attempt by %s", user);
X if (*emess == '\0') {
X WriteToClient ("500 Unable to change password." );
X } else {
X WriteToClient ("500 %s", emess);
X }
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 exit (1);
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 syslog (LOG_ERR, "password changed for %s", user);
X WriteToClient ("200 Password changed, thank-you.");
X
X ReadFromClient (line);
X if (strncmp(line, "quit", 4) != 0) {
X WriteToClient("500 Quit required.");
X exit (1);
X }
X
X WriteToClient("200 Bye.");
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 (master, slavedev, user);
X }
X}
X
X/*
X * dochild
X *
X * Do child stuff - set up slave pty and execl /bin/passwd.
X *
X * Code adapted from "Advanced Programming in the UNIX Environment"
X * by W. Richard Stevens.
X *
X */
X
Xdochild (master, slavedev, user)
Xint master;
Xchar *slavedev, *user;
X{
X int slave;
X struct termios stermios;
X
X /* Start new session - gets rid of controlling terminal. */
X
X if (setsid() < 0) {
X syslog(LOG_ERR, "setsid failed: %m");
X return(0);
X }
X
X /* Open slave pty and acquire as new controlling terminal. */
X
X if ((slave = open(slavedev, O_RDWR)) < 0) {
X syslog(LOG_ERR, "can't open slave pty: %m");
X return(0);
X }
X
X /* Close master. */
X
X close(master);
X
X /* Make slave stdin/out/err of child. */
X
X if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) {
X syslog(LOG_ERR, "dup2 error to stdin: %m");
X return(0);
X }
X if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
X syslog(LOG_ERR, "dup2 error to stdout: %m");
X return(0);
X }
X if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
X syslog(LOG_ERR, "dup2 error to stderr: %m");
X return(0);
X }
X if (slave > 2) close(slave);
X
X /* Set proper terminal attributes - no echo, canonical input processing,
X no map NL to CR/NL on output. */
X
X if (tcgetattr(0, &stermios) < 0) {
X syslog(LOG_ERR, "tcgetattr error: %m");
X return(0);
X }
X stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
X stermios.c_lflag |= ICANON;
X stermios.c_oflag &= ~(ONLCR);
X if (tcsetattr(0, TCSANOW, &stermios) < 0) {
X syslog(LOG_ERR, "tcsetattr error: %m");
X return(0);
X }
X
X /* Fork /bin/passwd. */
X
X if (execl("/bin/passwd", "passwd", user, (char*)0) < 0) {
X syslog(LOG_ERR, "can't exec /bin/passwd: %m");
X return(0);
X }
X}
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 *
X * Modified by Norstad to remove assumptions about number of pty's allocated
X * on this UNIX box.
X */
Xfindpty (slave)
Xchar **slave;
X{
X int master;
X static char *line = "/dev/ptyXX";
X DIR *dirp;
X struct dirent *dp;
X
X dirp = opendir("/dev");
X while ((dp = readdir(dirp)) != NULL) {
X if (strncmp(dp->d_name, "pty", 3) == 0 && strlen(dp->d_name) == 5) {
X line[8] = dp->d_name[3];
X line[9] = dp->d_name[4];
X if ((master = open(line, O_RDWR)) >= 0) {
X line[5] = 't';
X *slave = line;
X closedir(dirp);
X return (master);
X }
X }
X }
X closedir(dirp);
X return (-1);
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.
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, emess)
Xint master;
Xchar *user, *oldpass, *newpass, *emess;
X{
X char buf[BUFSIZE];
X char pswd[BUFSIZE+1];
X int m, n;
X
X *emess = 0;
X
X if (!expect(master, P1, buf)) return FAILURE;
X
X sprintf(pswd, "%s\n", oldpass);
X writestring(master, pswd);
X
X if (!expect(master, P2, buf)) return FAILURE;
X
X sprintf(pswd, "%s\n", newpass);
X writestring(master, pswd);
X
X if (!expect(master, P3, buf)) {
X getemess(master, P2, buf);
X strcpy(emess, buf);
X return FAILURE;
X }
X
X writestring(master, pswd);
X
X if (!expect(master, P4, buf)) return FAILURE;
X
X return SUCCESS;
X}
X
X/*
X * match ()
X *
X * Matches a string against a pattern. Wild-card characters '*' in
X * the pattern match any sequence of 0 or more characters in the string.
X * The match is case-insensitive.
X *
X * Entry: str = string.
X * pat = pattern.
X *
X * Exit: function result =
X * 0 if no match.
X * 1 if the string matches some initial segment of
X * the pattern.
X * 2 if the string matches the full pattern.
X */
Xmatch (str, pat)
Xchar *str;
Xchar *pat;
X{
X int result;
X
X for (; *str && *pat && *pat != '*'; str++, pat++)
X if (tolower(*str) != tolower(*pat)) return 0;
X if (*str == 0) return *pat == 0 ? 2 : 1;
X if (*pat == 0) return 0;
X for (; *str; str++) if ((result = match(str, pat+1)) != 0) return result;
X return 0;
X}
X
X/*
X * expect ()
X *
X * Reads 'passwd' command output and compares it to expected output.
X *
X * Entry: master = fid of master pty.
X * expected = pointer to array of pointers to alternate expected
X * strings, terminated by an empty string.
X * buf = pointer to buffer.
X *
X * Exit: function result = SUCCESS if output matched, FAILURE if not.
X * buf = the text read from the slave.
X *
X * Text is read from the slave and accumulated in buf. As long as
X * the text accumulated so far is an initial segment of at least
X * one of the expected strings, the function continues the read.
X * As soon as one of full expected strings has been read, the
X * function returns SUCCESS. As soon as the text accumulated so far
X * is not an initial segment of or exact match for at least one of
X * the expected strings, the function returns FAILURE.
X */
Xexpect (master, expected, buf)
Xint master;
Xchar **expected;
Xchar *buf;
X{
X int n, m;
X char **s;
X int initialSegment;
X int result;
X
X n = 0;
X buf[0] = 0;
X while (1) {
X if (n >= BUFSIZE-1) {
X syslog(LOG_ERR, "buffer overflow on read from child");
X return FAILURE;
X }
X m = read(master, buf+n, BUFSIZE-1-n);
X if (m < 0) {
X syslog(LOG_ERR, "read error from child: %m");
X return FAILURE;
X }
X n += m;
X buf[n] = 0;
X initialSegment = 0;
X for (s = expected; **s != 0; s++) {
X result = match(buf, *s);
X if (result == 2) return SUCCESS;
X initialSegment = initialSegment || result == 1;
X }
X if (!initialSegment) return FAILURE;
X }
X}
X
X/*
X * getemess()
X *
X * This function accumulates a 'passwd' command error message issued
X * after the first copy of the password has been sent.
X *
X * Entry: master = fid of master pty.
X * expected = pointer to array of pointers to alternate expected
X * strings for first password prompt, terminated by an
X * empty string.
X * buf = pointer to buffer containing text read so far.
X *
X * Exit: buf = the error message read from the slave.
X *
X * Text is read from the slave and accumulated in buf until the text
X * at the end of the buffer is an exact match for one of the expected
X * prompt strings. The expected prompt string is removed from the buffer,
X * returning just the error message text. Newlines in the error message
X * text are replaced by spaces.
X */
Xgetemess (master, expected, buf)
Xint master;
Xchar **expected;
Xchar *buf;
X{
X int n, m;
X char **s;
X char *p, *q;
X
X n = strlen(buf);
X while (1) {
X for (s = expected; **s != 0; s++) {
X for (p = buf; *p; p++) {
X if (match(p, *s) == 2) {
X *p = 0;
X for (q = buf; *q; q++) if (*q == '\n') *q = ' ';
X return;
X }
X }
X }
X if (n >= BUFSIZE-1) {
X syslog(LOG_ERR, "buffer overflow on read from child");
X return;
X }
X m = read(master, buf+n, BUFSIZE+1-n);
X if (m < 0) {
X syslog(LOG_ERR, "read error from child: %m");
X return;
X }
X n += m;
X buf[n] = 0;
X }
X}
X
XWriteToClient (fmt, va_alist)
Xchar *fmt;
Xva_dcl
X{
X va_list ap;
X
X va_start (ap);
X vfprintf (stdout, fmt, ap);
X fputs ("\r\n", stdout );
X fflush (stdout);
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 /* convert initial keyword on line to lower case. */
X
X for (sp = line; isalpha(*sp); sp++) *sp = tolower(*sp);
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}
SHAR_EOF
$TOUCH -am 0128143394 poppassd/poppassd.c &&
chmod 0644 poppassd/poppassd.c ||
echo "restore of poppassd/poppassd.c failed"
set `wc -c poppassd/poppassd.c`;Wc_c=$1
if test "$Wc_c" != "18598"; then
echo original size 18598, current size $Wc_c
fi
exit 0