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

  1. /*
  2.  * poppassd.c
  3.  * 
  4.  * Daemon to service Eudora "Change Password" requests.  This program
  5.  * doesn't actually change any passwords itself.  It simply listens for
  6.  * incomming requests, gathers the required information (user name, old
  7.  * password, new password) and executes /bin/passwd, talking to it over
  8.  * a pseudo-terminal pair.  The advantage of this is that we don't need
  9.  * to have any knowledge of either the password file format (which may
  10.  * include dbx files that need to be rebuilt) or of any file locking
  11.  * protocol /bin/passwd and cohorts may use (and which isn't documented).
  12.  *
  13.  * Note that unencrypted passwords are transmitted over the network.  If
  14.  * this bothers you, think hard about whether you want to implement Eudora's
  15.  * password changing feature.  On the other hand, it's no worse than what
  16.  * happens when you run /bin/passwd while connected via telnet or rlogin.
  17.  * Well, maybe it is, since the use of a dedicated port makes it slightly
  18.  * easier for a network snooper to snarf passwords off the wire.
  19.  *
  20.  * NOTE: In addition to the security issue outlined in the above paragraph,
  21.  * you should be aware that this program is going to be run as root by
  22.  * ordinary users and it mucks around with the password file.  This should
  23.  * set alarms off in your head.  I think I've devised a pretty foolproof
  24.  * way to ensure that security is maintained, but I'm no security expert and
  25.  * you would be a fool to install this without first reading the code and
  26.  * ensuring yourself that what I consider safe is good enough for you.  If
  27.  * something goes wrong, it's your fault, not mine.
  28.  *
  29.  * This is an adaptation of a server by the same name from dll@mitre.org
  30.  * (Daniel L. Leavitt of The MITRE Corporation).  The front-end code (which
  31.  * talks to the Eudora client) is directly descended from his original
  32.  * version.  The back-end stuff (which talks to /bin/password) is mine.
  33.  *        
  34.  * Should be owned by root, and executable only by root.  It can be started
  35.  * with an entry in /etc/inetd.conf such as the following:
  36.  *
  37.  * poppassd stream tcp nowait /usr/etc/poppassd poppassd
  38.  * 
  39.  * and in /etc/services:
  40.  * 
  41.  * ppassd        106/tcp
  42.  *
  43.  * Roy Smith <roy@nyu.edu>
  44.  * Department of Microbiology,
  45.  * NYU School of Medicine
  46.  * 550 First Avenue
  47.  * New York, NY 10016
  48.  */
  49.  
  50. /* The server's responses should be like an FTP server's responses; 
  51.  * 1xx for in progress, 2xx for success, 3xx for more information
  52.  * needed, 4xx for temporary failure, and 5xx for permanent failure.  
  53.  * Putting it all together, here's a sample conversation:
  54.  *
  55.  *   S: 200 hello\r\n
  56.  *   E: user yourloginname\r\n
  57.  *   S: 300 please send your password now\r\n
  58.  *   E: pass yourcurrentpassword\r\n
  59.  *   S: 200 My, that was tasty\r\n
  60.  *   E: newpass yournewpassword\r\n
  61.  *   S: 200 Happy to oblige\r\n
  62.  *   E: quit\r\n
  63.  *   S: 200 Bye-bye\r\n
  64.  *   S: <closes connection>
  65.  *   E: <closes connection>
  66.  */
  67.  
  68. #define SUCCESS 1
  69. #define FAILURE 0
  70. #define BUFSIZE 512
  71. #define MAXARGS 32
  72. #define SUPPRESS 1
  73.  
  74. #include <sys/types.h>
  75. #include <sys/stat.h>
  76. #include <sys/ioctl.h>
  77. #include <sys/wait.h>
  78. #include <unistd.h>
  79. #include <fcntl.h>
  80. #include <syslog.h>
  81. #include <sgtty.h>
  82. #include <stdlib.h>
  83. #include <stdio.h>
  84. #include <ctype.h>
  85. #include <strings.h>
  86. #include <errno.h>
  87. #include <varargs.h>
  88. #include <pwd.h>
  89.  
  90. main (argc, argv)
  91. int argc;
  92. char *argv[];
  93. {
  94.      char line[BUFSIZE];
  95.      char user[BUFSIZE];
  96.      char oldpass[BUFSIZE];
  97.      char newpass[BUFSIZE];
  98.      char logFile[BUFSIZE];
  99.      char *slavedev;
  100.      struct passwd *pw, *getpwnam();
  101.      int c, master;
  102.      pid_t pid, wpid;
  103.      union wait wstat;
  104.      
  105.      chdir ("/etc" );
  106.      strcpy (user, "" );
  107.      strcpy (oldpass, "" );
  108.      strcpy (newpass, "" );
  109.      
  110.      if (openlog ("poppassd", LOG_PID) < 0)
  111.      {
  112.       WriteToClient ("500 Can't open syslog.");
  113.            exit (1);
  114.      }
  115.      
  116.      WriteToClient ("200 hello, who are you?");
  117.      ReadFromClient (line);
  118.      sscanf (line, "user %s", user) ;
  119.      if (strlen (user) == 0)
  120.      {
  121.       WriteToClient ("500 username required.");
  122.       exit(1);
  123.      }
  124.  
  125.      WriteToClient ("200 your password please.");
  126.      ReadFromClient (line);
  127.      sscanf (line, "pass %s", oldpass) ;
  128.      if (strlen (oldpass) == 0)
  129.      {
  130.       WriteToClient ("500 password required.");
  131.       exit(1);
  132.      }
  133.      
  134.      if ((pw = getpwnam (user)) == NULL)
  135.      {
  136.       WriteToClient ("500 Unknown user, %s.", user);
  137.       exit(1);
  138.      }
  139.  
  140.      if (chkPass (user, oldpass, pw) == FAILURE)
  141.      {
  142.       WriteToClient ("500 Authentication failure.");
  143.       exit(1);
  144.      }
  145.  
  146.      WriteToClient ("200 your new password please.");
  147.      ReadFromClient (line);
  148.      sscanf (line, "newpass %s", newpass);
  149.      
  150.      /* new pass required */
  151.      if (strlen (newpass) == 0)
  152.      {
  153.       WriteToClient ("500 new password required.");
  154.       exit(1);
  155.      }
  156.      
  157.      /* new pass must be 6 char or longer */
  158.      if (strlen (newpass) < 6 )
  159.      {
  160.       WriteToClient ("500 New password too short");
  161.       exit(1);
  162.      }
  163.      
  164.      /* new pass must be different from old pass */
  165.      if (!strcmp (oldpass, newpass))
  166.      {
  167.       WriteToClient ("500 New password must be different.");
  168.       exit(1);
  169.      }
  170.  
  171.      /* get pty to talk to password program */
  172.      if ((master = findpty (&slavedev)) < 0)
  173.      {
  174.       syslog (LOG_ERR, "can't find pty");
  175.       exit (1);
  176.      }
  177.      
  178.      /* fork child process to talk to password program */
  179.      if ((pid = fork()) < 0)     /* Error, can't fork */
  180.      {
  181.       syslog (LOG_ERR, "can't fork for passwd: %m");
  182.       WriteToClient ("500 Server error (can't fork passwd), get help!");
  183.       return (0);
  184.      }
  185.  
  186.      if (pid)   /* Parent */
  187.      {
  188.       sleep (1);    /* Make sure child is ready.  Is this really needed? */
  189.       if (talktochild (master, user, oldpass, newpass) == FAILURE)
  190.       {
  191.            syslog (LOG_ERR, "failed attempt by %s", user);
  192.            WriteToClient ("500 Unable to change password." );
  193.            exit(1);
  194.       }
  195.  
  196.       if ((wpid = waitpid (pid, &wstat, 0)) < 0)
  197.       {
  198.            syslog (LOG_ERR, "wait for /bin/passwd child failed: %m");
  199.            WriteToClient ("500 Server error (wait failed), get help!");
  200.            exit (1);
  201.       }
  202.  
  203.       if (pid != wpid)
  204.       {
  205.            syslog (LOG_ERR, "wrong child (/bin/passwd waited for!");
  206.            WriteToClient ("500 Server error (wrong child), get help!");
  207.            return (0);
  208.       }
  209.  
  210.       if (WIFEXITED (wstat) == 0)
  211.       {
  212.            syslog (LOG_ERR, "child (/bin/passwd) killed?");
  213.            WriteToClient ("500 Server error (funny wstat), get help!");
  214.            exit (1);
  215.       }
  216.  
  217.       if (WEXITSTATUS (wstat) != 0)
  218.       {
  219.            syslog (LOG_ERR, "child (/bin/passwd) exited abnormally");
  220.            WriteToClient ("500 Server error (abnormal exit), get help!");
  221.            exit (1);
  222.       }
  223.  
  224.       if (hashpass() == 0)
  225.            exit (1);
  226.  
  227.       syslog (LOG_ERR, "password changed for %s", user);
  228.       WriteToClient ("200 Password changed, thank-you.");
  229.       exit (0);
  230.      }
  231.      else      /* Child */
  232.      {
  233.       /*
  234.        * Become the user trying who's password is being changed.  We're
  235.        * about to exec /bin/passwd with is setuid root anyway, but this
  236.        * way it looks to the child completely like it's being run by
  237.        * the normal user, which makes it do its own password verification
  238.        * before doing any thing.  In theory, we've already verified the
  239.        * password, but this extra level of checking doesn't hurt.  Besides,
  240.        * the way I do it here, if somebody manages to change somebody
  241.        * else's password, you can complain to your vendor about security
  242.        * holes, not to me!
  243.        */
  244.       setuid (pw->pw_uid);
  245.       setgid (pw->pw_gid);
  246.       dochild (slavedev, user);
  247.      }
  248. }
  249.  
  250. dochild (slavedev, user)
  251. char *slavedev, *user;
  252. {
  253.      int fd, fdmax, slave;
  254.      struct sgttyb sgbuf;
  255.  
  256.      /*
  257.       * Get rid of control terminal and
  258.       * close all open file descriptors.
  259.       */
  260.      fd = open ("/dev/tty", O_RDWR, 0);
  261.      ioctl (fd, TIOCNOTTY, 0);
  262.       
  263.      fdmax = sysconf (_SC_OPEN_MAX);
  264.      for (fd = 0; fd < fdmax; fd++)
  265.       close (fd);
  266.  
  267.      /*
  268.       * Open slave side of pty (which now becomes the control terminal).
  269.       * Set appropriate tty modes and make sure stdin, stdout, and stderr
  270.       * are all connected to it.
  271.       */
  272.      if ((slave = open (slavedev, O_RDWR)) < 0)
  273.      {
  274.       syslog (LOG_ERR, "Can't open slave pty %s: %m", slavedev);
  275.       return (0);
  276.      }
  277.      
  278.      ioctl (slave, TIOCGETP, &sgbuf);
  279.      sgbuf.sg_flags = ANYP;
  280.      ioctl (slave, TIOCSETP, &sgbuf);
  281.  
  282.      dup2 (slave, 0);
  283.      dup2 (slave, 1);
  284.      dup2 (slave, 2);
  285.  
  286.      /*
  287.       * This is probably just paranoia; if all fds are closed, slave
  288.       * should be fd 0.  In that case, the dup2 (slave, 0) above is 
  289.       * also just being paranoid.
  290.       */
  291.      if (slave > 2)
  292.       (void) close(slave);
  293.  
  294.      /*
  295.       * Execute the "real" password program, which should now think
  296.       * it is talking to a normal user on a normal tty port.
  297.       */
  298.      execl ("/bin/passwd", "passwd", user, (char *) 0);
  299.  
  300.      /*
  301.       * We only reach here if execl fails!  It's not 100% clear what
  302.       * to do at this point.  We can no longer talk to the client since
  303.       * we no longer have the client socket open.  Just log a syslog
  304.       * message (and hope its gets logged, since it's not clear what
  305.       * evil things closing the syslog fd has done!) and count on the
  306.       * parent process to realize we've died.
  307.       */
  308.      openlog ("poppassd (child)", LOG_PID);
  309.      syslog (LOG_ERR, "can't execl /bin/passwd: %m");
  310.      exit (1);
  311. }
  312.  
  313. /*
  314.  * findpty()
  315.  *
  316.  * Finds the first available pseudo-terminal master/slave pair.  The master
  317.  * side is opened and a fd returned as the function value.  A pointer to the
  318.  * name of the slave side (i.e. "/dev/ttyp0") is returned in the argument,
  319.  * which should be a char**.  The name itself is stored in a static buffer.
  320.  *
  321.  * A negative value is returned on any sort of error.
  322.  */
  323. findpty (slave)
  324. char **slave;
  325. {
  326.      int c, i, master;
  327.      static char *line;
  328.      struct stat statbuf;
  329.      
  330.      for (c = 'p'; c <= 's'; c++)
  331.      {
  332.       line = "/dev/ptyXX";
  333.       line[sizeof("/dev/pty")-1] = c;
  334.       line[sizeof("/dev/ptyp")-1] = '0';
  335.       if (stat (line, &statbuf) < 0)
  336.            break;
  337.       
  338.       for (i = 0; i < 16; i++)
  339.       {
  340.            line[sizeof("/dev/ptyp")-1] = "0123456789abcdef"[i];
  341.            if ((master = open(line, O_RDWR)) >= 0)
  342.             goto found_pty;
  343.       }
  344.      }
  345.  
  346.      syslog(LOG_ERR, "All network ports in use");
  347.      return (-1);
  348.  
  349.    found_pty:
  350.      line[sizeof("/dev/")-1] = 't';        /* "/dev/ptyp0" --> "/dev/ttyp0" */
  351.      *slave = line;
  352.      return (master);
  353. }
  354.  
  355. /*
  356.  * writestring()
  357.  *
  358.  * Write a string in a single write() system call.
  359.  */
  360. writestring (fd, s)
  361. char *s;
  362. {
  363.      int l;
  364.  
  365.      l = strlen (s);
  366.      write (fd, s, l);
  367. }
  368.  
  369. /*
  370.  * talktochild()
  371.  *
  372.  * Handles the conversation between the parent and child (password program)
  373.  * processes.  This is where you want to customize the conversation script.
  374.  *
  375.  * Returns SUCCESS is the conversation is completed without any problems,
  376.  * FAILURE if any errors are encountered (in which case, it can be assumed
  377.  * that the password wasn't changed).
  378.  */
  379. talktochild (master, user, oldpass, newpass)
  380. int master;
  381. char *user, *oldpass, *newpass;
  382. {
  383.      int n;
  384.      char buf[500];
  385.  
  386.      sprintf (buf, "Changing password for %s\n", user);
  387.      if (!expect (master, buf))
  388.       return (FAILURE);
  389.  
  390.      if (!expect (master, "Old password: "))
  391.       return (FAILURE);
  392.      
  393.      sprintf (buf, "%s\n", oldpass);
  394.      writestring (master, buf);
  395.  
  396.      if (!expect (master, "\n") || !expect (master, "Enter new password: "))
  397.       return (FAILURE);
  398.  
  399.      sprintf (buf, "%s\n", newpass);
  400.      writestring (master, buf);
  401.  
  402.      if (!expect (master, "\n") || !expect (master, "Verify: "))
  403.       return (FAILURE);
  404.  
  405.      writestring (master, buf);
  406.      if (!expect (master, "\n") || !expect (master, ""))
  407.       return (FAILURE);
  408.  
  409.      return (SUCCESS);
  410. }
  411.  
  412. /*
  413.  * expect ()
  414.  *
  415.  * Read the next line and check to make sure it is an exact match for
  416.  * the string expected, including newlines.  If the expected string is
  417.  * empty, it's considered to match either EOF, or getting an EWOULDBLOCK
  418.  * error, which would typically occur if the child has already exited.
  419.  *
  420.  * A syslog error message is generated and expect returns 0 on any error
  421.  * or mismatch.  If the expected string is found, expect() returns 1.
  422.  */
  423. expect (fd, s)
  424. int fd;
  425. char *s;
  426. {
  427.      int n;
  428.      char buf[1000];
  429.      extern int errno;
  430.  
  431.      if ((n = read (fd, buf, 999)) < 0)
  432.      {
  433.       if (errno == EWOULDBLOCK && *s == NULL)
  434.            return (1);
  435.       
  436.       syslog (LOG_ERR, "read error from child: %m");
  437.       return (0);
  438.      }
  439.  
  440.      buf[n] = 0;
  441.      if (strcmp (s, buf) != 0)
  442.      {
  443.       syslog (LOG_ERR, "wrong response from child --");
  444.       syslog (LOG_ERR, "expected %s", s);
  445.       syslog (LOG_ERR, "got %s", buf);
  446.       return (0);
  447.      }
  448.      
  449.      return (1);
  450. }
  451.  
  452. WriteToClient (va_alist)
  453. va_dcl
  454. {
  455.     va_list ap;
  456.     char *fmt;      
  457.     char *args[MAXARGS];
  458.     int argno = 0;
  459.     char string[BUFSIZE];
  460.     
  461.     va_start (ap);
  462.     fmt = va_arg (ap, char *);
  463.     while ((args[argno++] = va_arg(ap, char *)) != (char *)0)
  464.             ;
  465.  
  466.     vfprintf (stdout, fmt, args);
  467.     fputs ("\r\n", stdout );
  468.     fflush (stdout);
  469.  
  470.     vsprintf (string, fmt, args);
  471.  
  472.     va_end (ap);
  473. }
  474.  
  475. ReadFromClient (line)
  476. char *line;
  477. {
  478.     char *sp;
  479.     int i;
  480.  
  481.     strcpy (line, "");
  482.     fgets (line, BUFSIZE, stdin);
  483.     if ((sp = strchr(line, '\n')) != NULL) *sp = '\0'; 
  484.     if ((sp = strchr(line, '\r')) != NULL) *sp = '\0'; 
  485. }
  486.  
  487. int chkPass (user, pass, pw)
  488. char *user;
  489. char *pass;
  490. struct passwd *pw;
  491. {
  492.      /*  Compare the supplied password with the password file entry */
  493.      if (strcmp (crypt (pass, pw->pw_passwd), pw->pw_passwd) != 0)
  494.       return (FAILURE);
  495.      else 
  496.       return (SUCCESS);
  497. }
  498.  
  499. /*
  500.  * hashpass()
  501.  *
  502.  * Execute mkpasswd to build the hashed (dbx) passwd files.
  503.  */
  504. hashpass()
  505. {
  506.      pid_t pid, wpid;
  507.      union wait wstat;
  508.      int fd;
  509.  
  510.      /* fork child process to talk to run mkpasswd */
  511.      if ((pid = fork()) < 0)     /* Error, can't fork */
  512.      {
  513.       syslog (LOG_ERR, "can't fork for mkpassd: %m");
  514.       WriteToClient ("500 Server error (can't fork mkpasswd), get help!");
  515.       return (0);
  516.      }
  517.  
  518.      if (pid)   /* Parent */
  519.      {
  520.       if ((wpid = waitpid (pid, &wstat, 0)) < 0)
  521.       {
  522.            syslog (LOG_ERR, "wait for mkpasswd child failed: %m");
  523.            WriteToClient ("500 Server error (wait failed), get help!");
  524.            return (0);
  525.       }
  526.  
  527.       if (pid != wpid)
  528.       {
  529.            syslog (LOG_ERR, "wrong child waited for in hashpass()!");
  530.            WriteToClient ("500 Server error (wrong child), get help!");
  531.            return (0);
  532.       }
  533.  
  534.       if (WIFEXITED (wstat) == 0)
  535.       {
  536.            syslog (LOG_ERR, "child (mkpasswd) killed?");
  537.            WriteToClient ("500 Server error (funny wstat), get help!");
  538.            return (0);
  539.       }
  540.  
  541.       if (WEXITSTATUS (wstat) != 0)
  542.       {
  543.            syslog (LOG_ERR, "child (mkpasswd) exited abnormally");
  544.            WriteToClient ("500 Server error (abnormal exit), get help!");
  545.            return (0);
  546.       }
  547.  
  548.       return (1);    /* mkpasswd executed normally */
  549.      }
  550.      else      /* Child */
  551.      {
  552.       /*
  553.        * Making stdout and stderr /dev/null keeps mkpasswd messages (such
  554.        * as the one that says "669 password entries, maximum length 125")
  555.        * from being visible to the client.  If you are doing debugging,
  556.        * just change /dev/null to /tmp/mkpasswd.std{out,err}, or whatever.
  557.        */
  558.       close (1);
  559.       fd = open ("/dev/null", O_RDWR, 0);
  560.       if (fd != 1)
  561.       {
  562.            dup2 (fd, 1);
  563.            close (fd);
  564.       }
  565.       close (2);
  566.       fd = open ("/dev/null", O_RDWR, 0);
  567.       if (fd != 2)
  568.       {
  569.            dup2 (fd, 2);
  570.            close (fd);
  571.       }
  572.  
  573.       execl ("/etc/mkpasswd", "mkpasswd", "/etc/passwd", (char *) 0);
  574.  
  575.       /*
  576.        * We only reach here if execl fails!
  577.        */
  578.       syslog (LOG_ERR, "can't execl /etc/mkpasswd: %m");
  579.       WriteToClient ("500 Server error (can't exec mkpasswd), get help!");
  580.       exit (1);
  581.      }
  582. }
  583.