home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / poppassd.c < prev    next >
C/C++ Source or Header  |  2000-06-19  |  19KB  |  674 lines

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