home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1998 July & August / Pcwk78a98.iso / Wtestowe / Clico / UNIX / SAMBA / SOURCE / SAMBA.TAR / samba-1.9.17 / source / chgpasswd.c < prev    next >
C/C++ Source or Header  |  1997-07-18  |  11KB  |  401 lines

  1. /* 
  2.    Unix SMB/Netbios implementation.
  3.    Version 1.9.
  4.    Samba utility functions
  5.    Copyright (C) Andrew Tridgell 1992-1997
  6.    
  7.    This program is free software; you can redistribute it and/or modify
  8.    it under the terms of the GNU General Public License as published by
  9.    the Free Software Foundation; either version 2 of the License, or
  10.    (at your option) any later version.
  11.    
  12.    This program is distributed in the hope that it will be useful,
  13.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.    GNU General Public License for more details.
  16.    
  17.    You should have received a copy of the GNU General Public License
  18.    along with this program; if not, write to the Free Software
  19.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. */
  21.  
  22. /* fork a child process to exec passwd and write to its
  23. * tty to change a users password. This is running as the
  24. * user who is attempting to change the password.
  25. */
  26.  
  27. /* 
  28.  * This code was copied/borrowed and stolen from various sources.
  29.  * The primary source was the poppasswd.c from the authors of POPMail. This software
  30.  * was included as a client to change passwords using the 'passwd' program
  31.  * on the remote machine.
  32.  *
  33.  * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE
  34.  * is defined in the compiler directives located in the Makefile.
  35.  *
  36.  * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
  37.  * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
  38.  * and rights to modify, distribute or incorporate this change to the CAP suite or
  39.  * using it for any other reason are granted, so long as this disclaimer is left intact.
  40.  */
  41.  
  42. /*
  43.    This code was hacked considerably for inclusion in Samba, primarily
  44.    by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
  45.    of the "password chat" option, which allows the easy runtime
  46.    specification of the expected sequence of events to change a
  47.    password.
  48.    */
  49.  
  50. #include "includes.h"
  51.  
  52. extern int DEBUGLEVEL;
  53.  
  54. #ifdef ALLOW_CHANGE_PASSWORD
  55.  
  56. #define MINPASSWDLENGTH 5
  57. #define BUFSIZE 512
  58.  
  59. static int findpty(char **slave)
  60. {
  61.   int master;
  62. #if defined(SVR4) || defined(SUNOS5)
  63.   extern char *ptsname();
  64. #else /* defined(SVR4) || defined(SUNOS5) */
  65.   static char line[12];
  66.   void *dirp;
  67.   char *dpname;
  68. #endif /* defined(SVR4) || defined(SUNOS5) */
  69.   
  70. #if defined(SVR4) || defined(SUNOS5)
  71.   if ((master = open("/dev/ptmx", O_RDWR)) >= 1) {
  72.     grantpt(master);
  73.     unlockpt(master);
  74.     *slave = ptsname(master);
  75.     return (master);
  76.   }
  77. #else /* defined(SVR4) || defined(SUNOS5) */
  78.   strcpy( line, "/dev/ptyXX" );
  79.  
  80.   dirp = OpenDir(-1, "/dev", True);
  81.   if (!dirp) return(-1);
  82.   while ((dpname = ReadDirName(dirp)) != NULL) {
  83.     if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
  84.       DEBUG(3,("pty: try to open %s, line was %s\n", dpname, line ) );
  85.       line[8] = dpname[3];
  86.       line[9] = dpname[4];
  87.       if ((master = open(line, O_RDWR)) >= 0) {
  88.         DEBUG(3,("pty: opened %s\n", line ) );
  89.     line[5] = 't';
  90.     *slave = line;
  91.     CloseDir(dirp);
  92.     return (master);
  93.       }
  94.     }
  95.   }
  96.   CloseDir(dirp);
  97. #endif /* defined(SVR4) || defined(SUNOS5) */
  98.   return (-1);
  99. }
  100.  
  101. static int dochild(int master,char *slavedev, char *name, char *passwordprogram)
  102. {
  103.   int slave;
  104.   struct termios stermios;
  105.   struct passwd *pass = Get_Pwnam(name,True);
  106.   int gid = pass->pw_gid;
  107.   int uid = pass->pw_uid;
  108.  
  109. #ifdef USE_SETRES
  110.   setresuid(0,0,0);
  111. #else /* USE_SETRES */
  112.   setuid(0);
  113. #endif /* USE_SETRES */
  114.  
  115.   /* Start new session - gets rid of controlling terminal. */
  116.   if (setsid() < 0) {
  117.     DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n"));
  118.     return(False);
  119.   }
  120.  
  121.   /* Open slave pty and acquire as new controlling terminal. */
  122.   if ((slave = open(slavedev, O_RDWR)) < 0) {
  123.     DEBUG(3,("More weirdness, could not open %s\n", 
  124.          slavedev));
  125.     return(False);
  126.   }
  127. #if defined(SVR4) || defined(SUNOS5)
  128.   ioctl(slave, I_PUSH, "ptem");
  129.   ioctl(slave, I_PUSH, "ldterm");
  130. #else /* defined(SVR4) || defined(SUNOS5) */
  131.   if (ioctl(slave,TIOCSCTTY,0) <0) {
  132.      DEBUG(3,("Error in ioctl call for slave pty\n"));
  133.      /* return(False); */
  134.   }
  135. #endif /* defined(SVR4) || defined(SUNOS5) */
  136.  
  137.   /* Close master. */
  138.   close(master);
  139.  
  140.   /* Make slave stdin/out/err of child. */
  141.  
  142.   if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) {
  143.     DEBUG(3,("Could not re-direct stdin\n"));
  144.     return(False);
  145.   }
  146.   if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) {
  147.     DEBUG(3,("Could not re-direct stdout\n"));
  148.     return(False);
  149.   }
  150.   if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) {
  151.     DEBUG(3,("Could not re-direct stderr\n"));
  152.     return(False);
  153.   }
  154.   if (slave > 2) close(slave);
  155.  
  156.   /* Set proper terminal attributes - no echo, canonical input processing,
  157.      no map NL to CR/NL on output. */
  158.  
  159.   if (tcgetattr(0, &stermios) < 0) {
  160.     DEBUG(3,("could not read default terminal attributes on pty\n"));
  161.     return(False);
  162.   }
  163.   stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
  164.   stermios.c_lflag |= ICANON;
  165.   stermios.c_oflag &= ~(ONLCR);
  166.   if (tcsetattr(0, TCSANOW, &stermios) < 0) {
  167.     DEBUG(3,("could not set attributes of pty\n"));
  168.     return(False);
  169.   }
  170.  
  171.   /* make us completely into the right uid */
  172. #ifdef USE_SETRES
  173.   setresgid(0,0,0);
  174.   setresuid(0,0,0);
  175.   setresgid(gid,gid,gid);
  176.   setresuid(uid,uid,uid);      
  177. #else      
  178.   setuid(0);
  179.   seteuid(0);
  180.   setgid(gid);
  181.   setegid(gid);
  182.   setuid(uid);
  183.   seteuid(uid);
  184. #endif
  185.  
  186.   /* execl() password-change application */
  187.   if (execl("/bin/sh","sh","-c",passwordprogram,NULL) < 0) {
  188.     DEBUG(3,("Bad status returned from %s\n",passwordprogram));
  189.     return(False);
  190.   }
  191.   return(True);
  192. }
  193.  
  194. static int expect(int master,char *expected,char *buf)
  195. {
  196.   int n, m;
  197.  
  198.   n = 0;
  199.   buf[0] = 0;
  200.   while (1) {
  201.     if (n >= BUFSIZE-1) {
  202.       return False;
  203.     }
  204.  
  205.     /* allow 4 seconds for some output to appear */
  206.     m = read_with_timeout(master, buf+n, 1, BUFSIZE-1-n, 4000);
  207.     if (m < 0) 
  208.       return False;
  209.  
  210.     n += m;
  211.     buf[n] = 0;
  212.  
  213.     {
  214.       pstring s1,s2;
  215.       strcpy(s1,buf);
  216.       strcpy(s2,expected);
  217.       if (do_match(s1, s2, False))
  218.     return(True);
  219.     }
  220.   }
  221. }
  222.  
  223. static void pwd_sub(char *buf)
  224. {
  225.   string_sub(buf,"\\n","\n");
  226.   string_sub(buf,"\\r","\r");
  227.   string_sub(buf,"\\s"," ");
  228.   string_sub(buf,"\\t","\t");
  229. }
  230.  
  231. static void writestring(int fd,char *s)
  232. {
  233.   int l;
  234.   
  235.   l = strlen (s);
  236.   write (fd, s, l);
  237. }
  238.  
  239.  
  240. static int talktochild(int master, char *chatsequence)
  241. {
  242.   char buf[BUFSIZE];
  243.   int count=0;
  244.   char *ptr=chatsequence;
  245.   fstring chatbuf;
  246.  
  247.   *buf = 0;
  248.   sleep(1);
  249.  
  250.   while (next_token(&ptr,chatbuf,NULL)) {
  251.     BOOL ok=True;
  252.     count++;
  253.     pwd_sub(chatbuf);
  254.     if (!strequal(chatbuf,"."))
  255.       ok = expect(master,chatbuf,buf);
  256.  
  257. #if DEBUG_PASSWORD
  258.       DEBUG(100,("chatbuf=[%s] responsebuf=[%s]\n",chatbuf,buf));
  259. #endif      
  260.  
  261.     if (!ok) {
  262.       DEBUG(3,("response %d incorrect\n",count));
  263.       return(False);
  264.     }
  265.  
  266.     if (!next_token(&ptr,chatbuf,NULL)) break;
  267.     pwd_sub(chatbuf);
  268.     if (!strequal(chatbuf,"."))
  269.       writestring(master,chatbuf);
  270.  
  271. #if DEBUG_PASSWORD
  272.     DEBUG(100,("sendbuf=[%s]\n",chatbuf));
  273. #endif      
  274.   }
  275.  
  276.   if (count<1) return(False);
  277.  
  278.   return (True);
  279. }
  280.  
  281.  
  282. BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence)
  283. {
  284.   char *slavedev;
  285.   int master;
  286.   pid_t pid, wpid;
  287.   int wstat;
  288.   BOOL chstat;    
  289.  
  290.   /* allocate a pseudo-terminal device */
  291.   if ((master = findpty (&slavedev)) < 0) {
  292.     DEBUG(3,("Cannot Allocate pty for password change: %s",name));
  293.     return(False);
  294.   }
  295.  
  296.   if ((pid = fork()) < 0) {
  297.     DEBUG(3,("Cannot fork() child for password change: %s",name));
  298.     return(False);
  299.   }
  300.  
  301.   /* we now have a pty */
  302.   if (pid > 0){            /* This is the parent process */
  303.     if ((chstat = talktochild(master, chatsequence)) == False) {
  304.       DEBUG(3,("Child failed to change password: %s\n",name));
  305.       kill(pid, SIGKILL); /* be sure to end this process */
  306.       return(False);
  307.     }
  308.     if ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
  309.       DEBUG(3,("The process is no longer waiting!\n\n"));
  310.       return(False);
  311.     }
  312.     if (pid != wpid) {
  313.       DEBUG(3,("We were waiting for the wrong process ID\n"));    
  314.       return(False);
  315.     }
  316.     if (WIFEXITED(wstat) == 0) {
  317.       DEBUG(3,("The process exited while we were waiting\n"));
  318.       return(False);
  319.     }
  320.     if (WEXITSTATUS(wstat) != 0) {
  321.       DEBUG(3,("The status of the process exiting was %d\n", wstat));
  322.       return(False);
  323.     }
  324.     
  325.   } else {
  326.     /* CHILD */
  327.  
  328.     /* make sure it doesn't freeze */
  329.     alarm(20);
  330.  
  331.     DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,getuid(),getgid()));
  332.     chstat = dochild(master, slavedev, name, passwordprogram);
  333.   }
  334.   DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name));
  335.   return (chstat);
  336. }
  337.  
  338.  
  339. BOOL chgpasswd(char *name,char *oldpass,char *newpass)
  340. {
  341.   pstring passwordprogram;
  342.   pstring chatsequence;
  343.  
  344.   strlower(name); 
  345.   DEBUG(3,("Password change for user: %s\n",name));
  346.  
  347. #if DEBUG_PASSWORD
  348.   DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass)); 
  349. #endif
  350.  
  351.   /* Take the passed information and test it for minimum criteria */
  352.   /* Minimum password length */
  353.   if (strlen(newpass) < MINPASSWDLENGTH) /* too short, must be at least MINPASSWDLENGTH */ 
  354.     {
  355.       DEBUG(2,("Password Change: %s, New password is shorter than MINPASSWDLENGTH\n",name));
  356.       return (False);        /* inform the user */
  357.     }
  358.   
  359.   /* Password is same as old password */
  360.   if (strcmp(oldpass,newpass) == 0) /* don't allow same password */
  361.     {
  362.       DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */
  363.       return (False);        /* inform the user */
  364.     }
  365.  
  366. #if (defined(PASSWD_PROGRAM) && defined(PASSWD_CHAT))
  367.   strcpy(passwordprogram,PASSWD_PROGRAM);
  368.   strcpy(chatsequence,PASSWD_CHAT);
  369. #else
  370.   strcpy(passwordprogram,lp_passwd_program());
  371.   strcpy(chatsequence,lp_passwd_chat());
  372. #endif
  373.  
  374.   if (!*chatsequence) {
  375.     DEBUG(2,("Null chat sequence - no password changing\n"));
  376.     return(False);
  377.   }
  378.  
  379.   if (!*passwordprogram) {
  380.     DEBUG(2,("Null password program - no password changing\n"));
  381.     return(False);
  382.   }
  383.  
  384.   string_sub(passwordprogram,"%u",name);
  385.   string_sub(passwordprogram,"%o",oldpass);
  386.   string_sub(passwordprogram,"%n",newpass);
  387.  
  388.   string_sub(chatsequence,"%u",name);
  389.   string_sub(chatsequence,"%o",oldpass);
  390.   string_sub(chatsequence,"%n",newpass);
  391.   return(chat_with_program(passwordprogram,name,chatsequence));
  392. }
  393.  
  394. #else
  395. BOOL chgpasswd(char *name,char *oldpass,char *newpass)
  396. {
  397.   DEBUG(0,("Password changing not compiled in (user=%s)\n",name));
  398.   return(False);
  399. }
  400. #endif
  401.