home *** CD-ROM | disk | FTP | other *** search
/ ftp.qualcomm.com / 2014.06.ftp.qualcomm.com.tar / ftp.qualcomm.com / eudora / servers / unix / password / pwserve-5-osf1 / poppassd.c < prev   
C/C++ Source or Header  |  1997-03-26  |  19KB  |  697 lines

  1. /*
  2.  * poppassd.c
  3.  *
  4.  * A Eudora and NUPOP change password server.
  5.  *
  6.  * John Norstad
  7.  * Academic Computing and Network Services
  8.  * Northwestern University
  9.  * j-norstad@nwu.edu
  10.  *
  11.  * Based on earlier versions by Roy Smith <roy@nyu.edu> and Daniel
  12.  * L. Leavitt <dll.mitre.org>.
  13.  * 
  14.  * Doesn't actually change any passwords itself.  It simply listens for
  15.  * incoming requests, gathers the required information (user name, old
  16.  * password, new password) and executes /bin/passwd, talking to it over
  17.  * a pseudo-terminal pair.  The advantage of this is that we don't need
  18.  * to have any knowledge of either the password file format (which may
  19.  * include dbx files that need to be rebuilt) or of any file locking
  20.  * protocol /bin/passwd and cohorts may use (and which isn't documented).
  21.  *
  22.  * The current version has been tested at NU under SunOS release 4.1.2 
  23.  * and 4.1.3, and under HP-UX 8.02 and 9.01. We have tested the server 
  24.  * with both Eudora 1.3.1 and NUPOP 2.0.
  25.  *
  26.  * Other sites report that this version also works under AIX and NIS,
  27.  * and with PC Eudora.
  28.  *
  29.  * Note that unencrypted passwords are transmitted over the network.  If
  30.  * this bothers you, think hard about whether you want to implement the
  31.  * password changing feature.  On the other hand, it's no worse than what
  32.  * happens when you run /bin/passwd while connected via telnet or rlogin.
  33.  * Well, maybe it is, since the use of a dedicated port makes it slightly
  34.  * easier for a network snooper to snarf passwords off the wire.
  35.  *
  36.  * NOTE: In addition to the security issue outlined in the above paragraph,
  37.  * you should be aware that this program is going to be run as root by
  38.  * ordinary users and it mucks around with the password file.  This should
  39.  * set alarms off in your head.  I think I've devised a pretty foolproof
  40.  * way to ensure that security is maintained, but I'm no security expert and
  41.  * you would be a fool to install this without first reading the code and
  42.  * ensuring yourself that what I consider safe is good enough for you.  If
  43.  * something goes wrong, it's your fault, not mine.
  44.  *
  45.  * The front-end code (which talks to the client) is directly 
  46.  * descended from Leavitt's original version.  The back-end pseudo-tty stuff 
  47.  * (which talks to /bin/password) is directly descended from Smith's
  48.  * version, with changes for SunOS and HP-UX by Norstad (with help from
  49.  * sample code in "Advanced Programming in the UNIX Environment"
  50.  * by W. Richard Stevens). The code to report /bin/passwd error messages
  51.  * back to the client in the final 500 response, and a new version of the
  52.  * code to find the next free pty, is by Norstad.
  53.  *        
  54.  * Should be owned by root, and executable only by root.  It can be started
  55.  * with an entry in /etc/inetd.conf such as the following:
  56.  *
  57.  * poppassd stream tcp nowait root /usr/local/bin/poppassd poppassd
  58.  * 
  59.  * and in /etc/services:
  60.  * 
  61.  * poppassd    106/tcp
  62.  *
  63.  * Logs to the local2 facility. Should have an entry in /etc/syslog.conf
  64.  * like the following:
  65.  *
  66.  * local2.err    /var/adm/poppassd-log
  67.  */
  68.  
  69. /* Modification history.
  70.  *
  71.  * 06/09/93. Version 1.0.
  72.  *
  73.  * 06/29/93. Version 1.1.
  74.  * Include program name 'poppassd' and version number in initial 
  75.  *    hello message.
  76.  * Case insensitive command keywords (user, pass, newpass, quit).
  77.  *    Fixes problem reported by Raoul Schaffner with PC Eudora.
  78.  * Read 'quit' command from client instead of just terminating after 
  79.  *    password change.
  80.  * Add new code for NIS support (contributed by Max Caines).
  81.  *
  82.  * 08/31/93. Version 1.2.
  83.  * Generalized the expected string matching to solve several problems
  84.  *    with NIS and AIX. The new "*" character in pattern strings
  85.  *    matches any sequence of 0 or more characters.
  86.  * Fix an error in the "getemess" function which could cause the
  87.  *    program to hang if more than one string was defined in the
  88.  *    P2 array.
  89.  */
  90.  
  91. /* Steve Dorner's description of the simple protocol:
  92.  *
  93.  * The server's responses should be like an FTP server's responses; 
  94.  * 1xx for in progress, 2xx for success, 3xx for more information
  95.  * needed, 4xx for temporary failure, and 5xx for permanent failure.  
  96.  * Putting it all together, here's a sample conversation:
  97.  *
  98.  *   S: 200 hello\r\n
  99.  *   E: user yourloginname\r\n
  100.  *   S: 300 please send your password now\r\n
  101.  *   E: pass yourcurrentpassword\r\n
  102.  *   S: 200 My, that was tasty\r\n
  103.  *   E: newpass yournewpassword\r\n
  104.  *   S: 200 Happy to oblige\r\n
  105.  *   E: quit\r\n
  106.  *   S: 200 Bye-bye\r\n
  107.  *   S: <closes connection>
  108.  *   E: <closes connection>
  109.  */
  110.  
  111. #define VERSION "1.2"
  112.  
  113. #define SUCCESS 1
  114. #define FAILURE 0
  115. #define BUFSIZE 512
  116.  
  117. #include <sys/types.h>
  118. #include <sys/stat.h>
  119. #include <sys/wait.h>
  120. #include <unistd.h>
  121. #include <fcntl.h>
  122. #include <syslog.h>
  123. #include <stdlib.h>
  124. #include <stdio.h>
  125. #include <ctype.h>
  126. #include <strings.h>
  127. #include <errno.h>
  128. #include <varargs.h>
  129. #include <pwd.h>
  130. #include <string.h>
  131. #include <termios.h>
  132. #include <dirent.h>
  133.  
  134. #ifdef OSF1
  135. #   include <sys/ioctl.h>
  136. #endif 
  137.  
  138.  
  139. /* Prompt strings expected from the "passwd" command. If you want
  140.  * to port this program to yet another flavor of UNIX, you may need to add
  141.  * more prompt strings here.
  142.  *
  143.  * Each prompt is defined as an array of pointers to alternate 
  144.  * strings, terminated by an empty string. In the strings, '*'
  145.  * matches any sequence of 0 or more characters. Pattern matching
  146.  * is case-insensitive.
  147.  */
  148.  
  149. static char *P1[] =
  150.    {"Old password:",
  151.     "Changing password for *.\nOld password:",
  152.     "Changing password for * on *.\nOld password:",
  153.     "Changing NIS password for * on *.\nOld password:",
  154.     "Changing password for *\n*'s Old password:",
  155.     "Changing password for *\nOld password: ",        /* for Ultrix */
  156.     "Changing password for *.\n\nOld password:",    /* for OSF/1 */
  157.     ""};
  158.  
  159. static char *P2[] =
  160.    {"\nNew password:",
  161.     "\n*'s New password:",
  162.     "\nEnter new password: ",                /* for Ultrix */
  163.     ""};
  164.  
  165. static char *P3[] =
  166.    {"\nRe-enter new password:",
  167.     "\nRetype new password:",
  168.     "\nEnter the new password again:",
  169.     "\n*Re-enter *'s new password:",
  170.     "\nVerify:",
  171.     "\nVerify: ",                    /* for Ultrix */
  172.     "\nWarning, only the first 8 characters of the password are significant.\nVerify: ",                        /* for Ultrix */
  173.     "\nRetype new password:",                /* for OSF1 */
  174.     ""};
  175.     
  176. static char *P4[] =
  177.    {"\n",
  178.     "NIS entry changed on *\n",
  179.     ""};
  180.  
  181.  
  182. main (argc, argv)
  183. int argc;
  184. char *argv[];
  185. {
  186.      char line[BUFSIZE];
  187.      char user[BUFSIZE];
  188.      char oldpass[BUFSIZE];
  189.      char newpass[BUFSIZE];
  190.      char emess[BUFSIZE];
  191.      char *slavedev;
  192.      struct passwd *pw, *getpwnam();
  193.      int c, master;
  194.      pid_t pid, wpid;
  195.      int wstat;
  196.      
  197.      *user = *oldpass = *newpass = 0;
  198.      
  199. #ifdef ULTRIX
  200.      if (openlog ("poppassd", LOG_PID) < 0)
  201. #else
  202.      if (openlog ("poppassd", LOG_PID, LOG_LOCAL2) < 0)
  203. #endif
  204.      {
  205.       WriteToClient ("500 Can't open syslog.");
  206.            exit (1);
  207.      }
  208.      
  209.      WriteToClient ("200 poppassd v%s hello, who are you?", VERSION);
  210.      ReadFromClient (line);
  211.      sscanf (line, "user %s", user) ;
  212.      if (strlen (user) == 0)
  213.      {
  214.       WriteToClient ("500 Username required.");
  215.       exit(1);
  216.      }
  217.  
  218.      WriteToClient ("200 your password please.");
  219.      ReadFromClient (line);
  220.      sscanf (line, "pass %s", oldpass) ;
  221.      if (strlen (oldpass) == 0)
  222.      {
  223.       WriteToClient ("500 Password required.");
  224.       exit(1);
  225.      }
  226.      
  227.      if ((pw = getpwnam (user)) == NULL)
  228.      {
  229.       WriteToClient ("500 Unknown user, %s.", user);
  230.       exit(1);
  231.      }
  232.  
  233.      if (chkPass (user, oldpass, pw) == FAILURE)
  234.      {
  235.       WriteToClient ("500 Old password is incorrect.");
  236.       exit(1);
  237.      }
  238.  
  239.      WriteToClient ("200 your new password please.");
  240.      ReadFromClient (line);
  241.      sscanf (line, "newpass %s", newpass);
  242.      
  243.      /* new pass required */
  244.      if (strlen (newpass) == 0)
  245.      {
  246.       WriteToClient ("500 New password required.");
  247.       exit(1);
  248.      }
  249.      /* get pty to talk to password program */
  250.      if ((master = findpty (&slavedev)) < 0)
  251.      {
  252.       syslog (LOG_ERR, "can't find pty");
  253.           WriteToClient("500 Server busy - try again later.");
  254.       exit (1);
  255.      }
  256.      
  257.      /* fork child process to talk to password program */
  258.      if ((pid = fork()) < 0)     /* Error, can't fork */
  259.      {
  260.       syslog (LOG_ERR, "can't fork for passwd: %m");
  261.       WriteToClient ("500 Server error (can't fork passwd), get help!");
  262.       exit (1);
  263.      }
  264.  
  265.      if (pid)   /* Parent */
  266.      {
  267.       sleep (1);    /* Make sure child is ready.  Is this really needed? */
  268.       if (talktochild (master, user, oldpass, newpass, emess) == FAILURE)
  269.       {
  270.            syslog (LOG_ERR, "failed attempt by %s", user);
  271.            if (*emess == '\0') {
  272.               WriteToClient ("500 Unable to change password." );
  273.                } else {
  274.           WriteToClient ("500 %s", emess);
  275.                }
  276.            exit(1);
  277.       }
  278.  
  279.       if ((wpid = waitpid (pid, &wstat, 0)) < 0)
  280.       {
  281.            syslog (LOG_ERR, "wait for /bin/passwd child failed: %m");
  282.            WriteToClient ("500 Server error (wait failed), get help!");
  283.            exit (1);
  284.       }
  285.  
  286.       if (pid != wpid)
  287.       {
  288.            syslog (LOG_ERR, "wrong child (/bin/passwd waited for!");
  289.            WriteToClient ("500 Server error (wrong child), get help!");
  290.            exit (1);
  291.       }
  292.  
  293.       if (WIFEXITED (wstat) == 0)
  294.       {
  295.            syslog (LOG_ERR, "child (/bin/passwd) killed?");
  296.            WriteToClient ("500 Server error (funny wstat), get help!");
  297.            exit (1);
  298.       }
  299.  
  300.       if (WEXITSTATUS (wstat) != 0)
  301.       {
  302.            syslog (LOG_ERR, "child (/bin/passwd) exited abnormally");
  303.            WriteToClient ("500 Server error (abnormal exit), get help!");
  304.            exit (1);
  305.       }
  306.  
  307.       syslog (LOG_ERR, "password changed for %s", user);
  308.       WriteToClient ("200 Password changed, thank-you.");
  309.  
  310.           ReadFromClient (line);
  311.       if (strncmp(line, "quit", 4) != 0) {
  312.           WriteToClient("500 Quit required.");
  313.         exit (1);
  314.       }
  315.       
  316.       WriteToClient("200 Bye.");
  317.       exit (0);
  318.      }
  319.      else      /* Child */
  320.      {
  321.       /*
  322.        * Become the user trying who's password is being changed.  We're
  323.        * about to exec /bin/passwd with is setuid root anyway, but this
  324.        * way it looks to the child completely like it's being run by
  325.        * the normal user, which makes it do its own password verification
  326.        * before doing any thing.  In theory, we've already verified the
  327.        * password, but this extra level of checking doesn't hurt.  Besides,
  328.        * the way I do it here, if somebody manages to change somebody
  329.        * else's password, you can complain to your vendor about security
  330.        * holes, not to me!
  331.        */
  332.       setuid (pw->pw_uid);
  333.       setgid (pw->pw_gid);
  334.       dochild (master, slavedev, user);
  335.      }
  336. }
  337.  
  338. /*
  339.  * dochild
  340.  *
  341.  * Do child stuff - set up slave pty and execl /bin/passwd.
  342.  *
  343.  * Code adapted from "Advanced Programming in the UNIX Environment"
  344.  * by W. Richard Stevens.
  345.  *
  346.  */
  347.  
  348. dochild (master, slavedev, user)
  349. int master;
  350. char *slavedev, *user;
  351. {
  352.    int slave;
  353.    struct termios stermios;
  354.  
  355.    /* Start new session - gets rid of controlling terminal. */
  356.    
  357.    if (setsid() < 0) {
  358.       syslog(LOG_ERR, "setsid failed: %m");
  359.       return(0);
  360.    }
  361.  
  362.    /* Open slave pty and acquire as new controlling terminal. */
  363.  
  364.    if ((slave = open(slavedev, O_RDWR)) < 0) {
  365.       syslog(LOG_ERR, "can't open slave pty: %m");
  366.       return(0);
  367.    }
  368.  
  369. #ifdef OSF1
  370. /*
  371.  * under OSF/1, there is no implicit assignment of control terminal with
  372.  * open(). The call to ioctl below opens the slave device and attaches
  373.  * it to the current session as the controlling terminal. Refer to
  374.  * tty(7) under OSF/1 for details.
  375.  */
  376.    ioctl(slave,TIOCSCTTY,0);
  377. #endif
  378.  
  379.    /* Close master. */
  380.  
  381.    close(master);
  382.  
  383.    /* Make slave stdin/out/err of child. */
  384.  
  385.    if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) {
  386.       syslog(LOG_ERR, "dup2 error to stdin: %m");
  387.       return(0);
  388.    }
  389.    if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
  390.       syslog(LOG_ERR, "dup2 error to stdout: %m");
  391.       return(0);
  392.    }
  393.    if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
  394.       syslog(LOG_ERR, "dup2 error to stderr: %m");
  395.       return(0);
  396.    }
  397.    if (slave > 2) close(slave);
  398.  
  399.    /* Set proper terminal attributes - no echo, canonical input processing,
  400.       no map NL to CR/NL on output. */
  401.  
  402.    if (tcgetattr(0, &stermios) < 0) {
  403.       syslog(LOG_ERR, "tcgetattr error: %m");
  404.       return(0);
  405.    }
  406.    stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
  407.    stermios.c_lflag |= ICANON;
  408.    stermios.c_oflag &= ~(ONLCR);
  409.    if (tcsetattr(0, TCSANOW, &stermios) < 0) {
  410.       syslog(LOG_ERR, "tcsetattr error: %m");
  411.       return(0);
  412.    }
  413.  
  414.    /* Fork /bin/passwd. */
  415.  
  416.    if (execl("/bin/passwd", "passwd", user, (char*)0) < 0) {
  417.       syslog(LOG_ERR, "can't exec /bin/passwd: %m");
  418.       return(0);
  419.    }
  420. }
  421.  
  422.  
  423. /*
  424.  * findpty()
  425.  *
  426.  * Finds the first available pseudo-terminal master/slave pair.  The master
  427.  * side is opened and a fd returned as the function value.  A pointer to the
  428.  * name of the slave side (i.e. "/dev/ttyp0") is returned in the argument,
  429.  * which should be a char**.  The name itself is stored in a static buffer.
  430.  *
  431.  * A negative value is returned on any sort of error.
  432.  *
  433.  * Modified by Norstad to remove assumptions about number of pty's allocated
  434.  * on this UNIX box.
  435.  */
  436. findpty (slave)
  437. char **slave;
  438. {
  439.    int master;
  440.    static char *line = "/dev/ptyXX";
  441.    DIR *dirp;
  442.    struct dirent *dp;
  443.  
  444.    dirp = opendir("/dev");
  445.    while ((dp = readdir(dirp)) != NULL) {
  446.       if (strncmp(dp->d_name, "pty", 3) == 0 && strlen(dp->d_name) == 5) {
  447.          line[8] = dp->d_name[3];
  448.          line[9] = dp->d_name[4];
  449.          if ((master = open(line, O_RDWR)) >= 0) {
  450.             line[5] = 't';
  451.             *slave = line;
  452.             closedir(dirp);
  453.             return (master);
  454.          }
  455.       }
  456.    }
  457.    closedir(dirp);
  458.    return (-1);
  459. }
  460.  
  461. /*
  462.  * writestring()
  463.  *
  464.  * Write a string in a single write() system call.
  465.  */
  466. writestring (fd, s)
  467. char *s;
  468. {
  469.      int l;
  470.  
  471.      l = strlen (s);
  472.      write (fd, s, l);
  473. }
  474.  
  475. /*
  476.  * talktochild()
  477.  *
  478.  * Handles the conversation between the parent and child (password program)
  479.  * processes.
  480.  *
  481.  * Returns SUCCESS is the conversation is completed without any problems,
  482.  * FAILURE if any errors are encountered (in which case, it can be assumed
  483.  * that the password wasn't changed).
  484.  */
  485. talktochild (master, user, oldpass, newpass, emess)
  486. int master;
  487. char *user, *oldpass, *newpass, *emess;
  488. {
  489.      char buf[BUFSIZE];
  490.      char pswd[BUFSIZE+1];
  491.      int m, n;
  492.  
  493.      *emess = 0;
  494.  
  495.      if (!expect(master, P1, buf)) return FAILURE;
  496.  
  497.      sprintf(pswd, "%s\n", oldpass);
  498.      writestring(master, pswd);
  499.  
  500.      if (!expect(master, P2, buf)) return FAILURE;
  501.  
  502.      sprintf(pswd, "%s\n", newpass);
  503.      writestring(master, pswd);
  504.  
  505.      if (!expect(master, P3, buf)) {
  506.         getemess(master, P2, buf);
  507.     strcpy(emess, buf);
  508.     return FAILURE;
  509.      }
  510.  
  511.      writestring(master, pswd);
  512.  
  513.      if (!expect(master, P4, buf)) return FAILURE;
  514.  
  515.      return SUCCESS;
  516. }
  517.  
  518. /*
  519.  * match ()
  520.  *
  521.  * Matches a string against a pattern. Wild-card characters '*' in
  522.  * the pattern match any sequence of 0 or more characters in the string.
  523.  * The match is case-insensitive.
  524.  *
  525.  * Entry: str = string.
  526.  *        pat = pattern.
  527.  *
  528.  * Exit:  function result =
  529.  *        0 if no match.
  530.  *        1 if the string matches some initial segment of
  531.  *          the pattern.
  532.  *        2 if the string matches the full pattern.
  533.  */
  534. match (str, pat)
  535. char *str;
  536. char *pat;
  537. {
  538.    int result;
  539.    
  540.    for (; *str && *pat && *pat != '*'; str++, pat++) 
  541.       if (tolower(*str) != tolower(*pat)) return 0;
  542.    if (*str == 0) return *pat == 0 ? 2 : 1;
  543.    if (*pat == 0) return 0;
  544.    for (; *str; str++) if ((result = match(str, pat+1)) != 0) return result;
  545.    return 0; 
  546. }
  547.  
  548. /*
  549.  * expect ()
  550.  *
  551.  * Reads 'passwd' command output and compares it to expected output.
  552.  *
  553.  * Entry: master = fid of master pty.
  554.  *      expected = pointer to array of pointers to alternate expected
  555.  *            strings, terminated by an empty string.
  556.  *        buf = pointer to buffer.
  557.  *
  558.  * Exit:  function result = SUCCESS if output matched, FAILURE if not.
  559.  *        buf = the text read from the slave.
  560.  *
  561.  * Text is read from the slave and accumulated in buf. As long as
  562.  * the text accumulated so far is an initial segment of at least 
  563.  * one of the expected strings, the function continues the read.
  564.  * As soon as one of full expected strings has been read, the
  565.  * function returns SUCCESS. As soon as the text accumulated so far
  566.  * is not an initial segment of or exact match for at least one of 
  567.  * the expected strings, the function returns FAILURE.
  568.  */
  569. expect (master, expected, buf)
  570. int master;
  571. char **expected;
  572. char *buf;
  573. {
  574.      int n, m;
  575.      char **s;
  576.      int initialSegment;
  577.      int result;
  578.      
  579.      n = 0;
  580.      buf[0] = 0;
  581.      while (1) {
  582.          if (n >= BUFSIZE-1) {
  583.        syslog(LOG_ERR, "buffer overflow on read from child");
  584.        return FAILURE;
  585.     }
  586.          m = read(master, buf+n, BUFSIZE-1-n);
  587.     if (m < 0) {
  588.        syslog(LOG_ERR, "read error from child: %m");
  589.        return FAILURE;
  590.     }
  591.     n += m;
  592.     buf[n] = 0;
  593.     initialSegment = 0;
  594.         for (s = expected; **s != 0; s++) {
  595.            result = match(buf, *s);
  596.        if (result == 2) return SUCCESS;
  597.        initialSegment = initialSegment || result == 1; 
  598.     }
  599.     if (!initialSegment) return FAILURE;
  600.      }
  601. }
  602.  
  603. /*
  604.  * getemess()
  605.  *
  606.  * This function accumulates a 'passwd' command error message issued
  607.  * after the first copy of the password has been sent.
  608.  *
  609.  * Entry: master = fid of master pty.
  610.  *      expected = pointer to array of pointers to alternate expected
  611.  *            strings for first password prompt, terminated by an 
  612.  *            empty string.
  613.  *        buf = pointer to buffer containing text read so far.
  614.  *
  615.  * Exit:  buf = the error message read from the slave.
  616.  *
  617.  * Text is read from the slave and accumulated in buf until the text
  618.  * at the end of the buffer is an exact match for one of the expected
  619.  * prompt strings. The expected prompt string is removed from the buffer,
  620.  * returning just the error message text. Newlines in the error message
  621.  * text are replaced by spaces.
  622.  */
  623. getemess (master, expected, buf)
  624. int master;
  625. char **expected;
  626. char *buf;
  627. {
  628.    int n, m;
  629.    char **s;
  630.    char *p, *q;
  631.  
  632.    n = strlen(buf);
  633.    while (1) {
  634.       for (s = expected; **s != 0; s++) {
  635.          for (p = buf; *p; p++) {
  636.             if (match(p, *s) == 2) {
  637.                *p = 0;
  638.                for (q = buf; *q; q++) if (*q == '\n') *q = ' ';
  639.                return;
  640.             }
  641.          }
  642.       }
  643.       if (n >= BUFSIZE-1) {
  644.      syslog(LOG_ERR, "buffer overflow on read from child");
  645.      return;
  646.       }
  647.       m = read(master, buf+n, BUFSIZE+1-n);
  648.       if (m < 0) {
  649.      syslog(LOG_ERR, "read error from child: %m");
  650.      return;
  651.       }
  652.       n += m;
  653.       buf[n] = 0;
  654.    }
  655. }
  656.  
  657. WriteToClient (fmt, va_alist)
  658. char *fmt;
  659. va_dcl
  660. {
  661.     va_list ap;
  662.     
  663.     va_start (ap);
  664.     vfprintf (stdout, fmt, ap);
  665.     fputs ("\r\n", stdout );
  666.     fflush (stdout);
  667.     va_end (ap);
  668. }
  669.  
  670. ReadFromClient (line)
  671. char *line;
  672. {
  673.     char *sp;
  674.     int i;
  675.  
  676.     strcpy (line, "");
  677.     fgets (line, BUFSIZE, stdin);
  678.     if ((sp = strchr(line, '\n')) != NULL) *sp = '\0'; 
  679.     if ((sp = strchr(line, '\r')) != NULL) *sp = '\0'; 
  680.     
  681.     /* convert initial keyword on line to lower case. */
  682.     
  683.     for (sp = line; isalpha(*sp); sp++) *sp = tolower(*sp);
  684. }
  685.  
  686. int chkPass (user, pass, pw)
  687. char *user;
  688. char *pass;
  689. struct passwd *pw;
  690. {
  691.      /*  Compare the supplied password with the password file entry */
  692.      if (strcmp (crypt (pass, pw->pw_passwd), pw->pw_passwd) != 0)
  693.       return (FAILURE);
  694.      else 
  695.       return (SUCCESS);
  696. }
  697.