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 / smbpasswd.c < prev    next >
C/C++ Source or Header  |  1997-08-19  |  16KB  |  585 lines

  1. #ifdef SMB_PASSWD
  2.  
  3. /*
  4.  * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
  5.  * (C) Jeremy Allison 1995-1997.
  6.  * 
  7.  * This program is free software; you can redistribute it and/or modify it under
  8.  * the terms of the GNU General Public License as published by the Free
  9.  * Software Foundation; either version 2 of the License, or (at your option)
  10.  * any later version.
  11.  * 
  12.  * This program is distributed in the hope that it will be useful, but WITHOUT
  13.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15.  * more details.
  16.  * 
  17.  * You should have received a copy of the GNU General Public License along with
  18.  * this program; if not, write to the Free Software Foundation, Inc., 675
  19.  * Mass Ave, Cambridge, MA 02139, USA.
  20.  */
  21.  
  22. #include "includes.h"
  23. #include "des.h"
  24.  
  25. /* Static buffers we will return. */
  26. static struct smb_passwd pw_buf;
  27. static pstring  user_name;
  28. static unsigned char smbpwd[16];
  29. static unsigned char smbntpwd[16];
  30.  
  31. static int gethexpwd(char *p, char *pwd)
  32. {
  33.     int i;
  34.     unsigned char   lonybble, hinybble;
  35.     char           *hexchars = "0123456789ABCDEF";
  36.     char           *p1, *p2;
  37.     for (i = 0; i < 32; i += 2) {
  38.         hinybble = toupper(p[i]);
  39.         lonybble = toupper(p[i + 1]);
  40.  
  41.         p1 = strchr(hexchars, hinybble);
  42.         p2 = strchr(hexchars, lonybble);
  43.         if (!p1 || !p2)
  44.             return (False);
  45.  
  46.         hinybble = PTR_DIFF(p1, hexchars);
  47.         lonybble = PTR_DIFF(p2, hexchars);
  48.  
  49.         pwd[i / 2] = (hinybble << 4) | lonybble;
  50.     }
  51.     return (True);
  52. }
  53.  
  54. struct smb_passwd *
  55. _my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd, 
  56.         BOOL *got_valid_nt_entry, long *pwd_seekpos)
  57. {
  58.     char            linebuf[256];
  59.     unsigned char   c;
  60.     unsigned char  *p;
  61.     long            uidval;
  62.     long            linebuf_len;
  63.  
  64.     /*
  65.      * Scan the file, a line at a time and check if the name matches.
  66.      */
  67.     while (!feof(fp)) {
  68.         linebuf[0] = '\0';
  69.         *pwd_seekpos = ftell(fp);
  70.  
  71.         fgets(linebuf, 256, fp);
  72.         if (ferror(fp))
  73.             return NULL;
  74.  
  75.         /*
  76.          * Check if the string is terminated with a newline - if not
  77.          * then we must keep reading and discard until we get one.
  78.          */
  79.         linebuf_len = strlen(linebuf);
  80.         if (linebuf[linebuf_len - 1] != '\n') {
  81.             c = '\0';
  82.             while (!ferror(fp) && !feof(fp)) {
  83.                 c = fgetc(fp);
  84.                 if (c == '\n')
  85.                     break;
  86.             }
  87.         } else
  88.             linebuf[linebuf_len - 1] = '\0';
  89.  
  90.         if ((linebuf[0] == 0) && feof(fp))
  91.             break;
  92.         /*
  93.          * The line we have should be of the form :-
  94.          * 
  95.          * username:uid:[32hex bytes]:....other flags presently
  96.          * ignored....
  97.          * 
  98.          * or,
  99.          * 
  100.          * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
  101.          * 
  102.          * if Windows NT compatible passwords are also present.
  103.          */
  104.  
  105.         if (linebuf[0] == '#' || linebuf[0] == '\0')
  106.             continue;
  107.         p = (unsigned char *) strchr(linebuf, ':');
  108.         if (p == NULL)
  109.             continue;
  110.         /*
  111.          * As 256 is shorter than a pstring we don't need to check
  112.          * length here - if this ever changes....
  113.          */
  114.         strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
  115.         user_name[PTR_DIFF(p, linebuf)] = '\0';
  116.         if (!strequal(user_name, name))
  117.             continue;
  118.  
  119.         /* User name matches - get uid and password */
  120.         p++;        /* Go past ':' */
  121.         if (!isdigit(*p))
  122.             return (False);
  123.  
  124.         uidval = atoi((char *) p);
  125.         while (*p && isdigit(*p))
  126.             p++;
  127.  
  128.         if (*p != ':')
  129.             return (False);
  130.  
  131.         /*
  132.          * Now get the password value - this should be 32 hex digits
  133.          * which are the ascii representations of a 16 byte string.
  134.          * Get two at a time and put them into the password.
  135.          */
  136.         p++;
  137.         *pwd_seekpos += PTR_DIFF(p, linebuf);    /* Save exact position
  138.                              * of passwd in file -
  139.                              * this is used by
  140.                              * smbpasswd.c */
  141.         if (*p == '*' || *p == 'X') {
  142.             /* Password deliberately invalid - end here. */
  143.             *valid_old_pwd = False;
  144.             *got_valid_nt_entry = False;
  145.             pw_buf.smb_nt_passwd = NULL;    /* No NT password (yet)*/
  146.  
  147.             /* Now check if the NT compatible password is
  148.                available. */
  149.             p += 33; /* Move to the first character of the line after 
  150.                         the lanman password. */
  151.             if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
  152.                 /* NT Entry was valid - even if 'X' or '*', can be overwritten */
  153.                 *got_valid_nt_entry = True;
  154.                 if (*p != '*' && *p != 'X') {
  155.                   if (gethexpwd((char *)p,(char *)smbntpwd))
  156.                     pw_buf.smb_nt_passwd = smbntpwd;
  157.                 }
  158.             }
  159.             pw_buf.smb_name = user_name;
  160.             pw_buf.smb_userid = uidval;
  161.             pw_buf.smb_passwd = NULL;    /* No password */
  162.             return (&pw_buf);
  163.         }
  164.         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33))
  165.             return (False);
  166.  
  167.         if (p[32] != ':')
  168.             return (False);
  169.  
  170.         if (!strncasecmp((char *)p, "NO PASSWORD", 11)) {
  171.           pw_buf.smb_passwd = NULL;    /* No password */
  172.         } else {
  173.           if(!gethexpwd((char *)p,(char *)smbpwd))
  174.             return False;
  175.           pw_buf.smb_passwd = smbpwd;
  176.         }
  177.  
  178.         pw_buf.smb_name = user_name;
  179.         pw_buf.smb_userid = uidval;
  180.         pw_buf.smb_nt_passwd = NULL;
  181.         *got_valid_nt_entry = False;
  182.         *valid_old_pwd = True;
  183.  
  184.         /* Now check if the NT compatible password is
  185.            available. */
  186.         p += 33; /* Move to the first character of the line after 
  187.                     the lanman password. */
  188.         if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
  189.             /* NT Entry was valid - even if 'X' or '*', can be overwritten */
  190.             *got_valid_nt_entry = True;
  191.             if (*p != '*' && *p != 'X') {
  192.               if (gethexpwd((char *)p,(char *)smbntpwd))
  193.                 pw_buf.smb_nt_passwd = smbntpwd;
  194.             }
  195.         }
  196.         return &pw_buf;
  197.     }
  198.     return NULL;
  199. }
  200.  
  201. /*
  202.  * Print command usage on stderr and die.
  203.  */
  204. static void usage(char *name)
  205. {
  206.     fprintf(stderr, "Usage is : %s [-add] [username] [password]\n", name);
  207.     exit(1);
  208. }
  209.  
  210.  int main(int argc, char **argv)
  211. {
  212.   int             real_uid;
  213.   struct passwd  *pwd;
  214.   fstring         old_passwd;
  215.   uchar           old_p16[16];
  216.   uchar           old_nt_p16[16];
  217.   fstring         new_passwd;
  218.   uchar           new_p16[16];
  219.   uchar           new_nt_p16[16];
  220.   char           *p;
  221.   struct smb_passwd *smb_pwent;
  222.   FILE           *fp;
  223.   BOOL            valid_old_pwd = False;
  224.   BOOL             got_valid_nt_entry = False;
  225.   BOOL            add_user = False;
  226.   int             add_pass = 0;
  227.   long            seekpos;
  228.   int             pwfd;
  229.   char            ascii_p16[66];
  230.   char            c;
  231.   int             ret, i, err, writelen;
  232.   int             lockfd = -1;
  233.   char           *pfile = SMB_PASSWD_FILE;
  234.   char            readbuf[16 * 1024];
  235.   
  236.   TimeInit();
  237.  
  238.   setup_logging(argv[0],True);
  239.   
  240.   charset_initialise();
  241.   
  242. #ifndef DEBUG_PASSWORD
  243.   /* Check the effective uid */
  244.   if (geteuid() != 0) {
  245.     fprintf(stderr, "%s: Must be setuid root.\n", argv[0]);
  246.     exit(1);
  247.   }
  248. #endif
  249.   
  250.   /* Get the real uid */
  251.   real_uid = getuid();
  252.   
  253.   /* Deal with usage problems */
  254.   if (real_uid == 0)
  255.   {
  256.     /* As root we can change anothers password and add a user. */
  257.     if (argc > 4 )
  258.       usage(argv[0]);
  259.   }
  260.   else if (argc == 2 || argc > 3)
  261.   {
  262.     fprintf(stderr, "%s: Only root can set anothers password.\n", argv[0]);
  263.     usage(argv[0]);
  264.   }
  265.   
  266.   if (real_uid == 0 && (argc > 1))
  267.   {
  268.     /* We are root - check if we should add the user */
  269.     if ((argv[1][0] == '-') && (argv[1][1] == 'a'))
  270.       add_user = True;
  271.  
  272.     if(add_user && (argc <= 2 || argc > 4))
  273.       usage(argv[0]);
  274.  
  275.     /* root can specify password on command-line */
  276.     if (argc == (add_user ? 4 : 3))
  277.     {
  278.       /* -a argument (add_user): new password is 3rd argument. */
  279.       /* no -a argument (add_user): new password is 2nd argument */
  280.  
  281.       add_pass = add_user ? 3 : 2;
  282.     }
  283.  
  284.     /* If we are root we can change another's password. */
  285.     strncpy(user_name, add_user ? argv[2] : argv[1], sizeof(user_name) - 1);
  286.     user_name[sizeof(user_name) - 1] = '\0';
  287.  
  288.     pwd = getpwnam(user_name);
  289.   }
  290.   else
  291.   {
  292.     /* non-root can specify old pass / new pass on command-line */
  293.     if (argc == 3)
  294.     {
  295.        /* non-root specifies new password as 2nd argument */
  296.        add_pass = 2;
  297.     }
  298.  
  299.     pwd = getpwuid(real_uid);
  300.   }
  301.   
  302.   if (pwd == 0) {
  303.     fprintf(stderr, "%s: Unable to get UNIX password entry for user.\n", argv[0]);
  304.     exit(1);
  305.   }
  306.  
  307.   /* If we are root we don't ask for the old password. */
  308.   old_passwd[0] = '\0';
  309.   if (real_uid != 0)
  310.   {
  311.     if (add_pass)
  312.     {
  313.       /* old password, as non-root, is 1st argument */
  314.       strncpy(old_passwd, argv[1], sizeof(fstring));
  315.     }
  316.     else
  317.     {
  318.       p = getpass("Old SMB password:");
  319.       strncpy(old_passwd, p, sizeof(fstring));
  320.     }
  321.     old_passwd[sizeof(fstring)-1] = '\0';
  322.   }
  323.  
  324.   if (add_pass)
  325.   {
  326.     /* new password is specified on the command line */
  327.     strncpy(new_passwd, argv[add_user ? 3 : 2], sizeof(new_passwd) - 1);
  328.     new_passwd[sizeof(new_passwd) - 1] = '\0';
  329.   }
  330.   else
  331.   {
  332.     new_passwd[0] = '\0';
  333.  
  334.     p = getpass("New SMB password:");
  335.  
  336.     strncpy(new_passwd, p, sizeof(fstring));
  337.     new_passwd[sizeof(fstring)-1] = '\0';
  338.  
  339.     p = getpass("Retype new SMB password:");
  340.  
  341.     if (strncmp(p, new_passwd, sizeof(fstring)-1))
  342.     {
  343.       fprintf(stderr, "%s: Mismatch - password unchanged.\n", argv[0]);
  344.       exit(1);
  345.     }
  346.   }
  347.   
  348.   if (new_passwd[0] == '\0')
  349.   {
  350.     printf("Password not set\n");
  351.     exit(0);
  352.   }
  353.   
  354.   /* Calculate the MD4 hash (NT compatible) of the old and new passwords */
  355.   memset(old_nt_p16, '\0', 16);
  356.   E_md4hash((uchar *)old_passwd, old_nt_p16);
  357.   
  358.   memset(new_nt_p16, '\0', 16);
  359.   E_md4hash((uchar *) new_passwd, new_nt_p16);
  360.   
  361.   /* Mangle the passwords into Lanman format */
  362.   old_passwd[14] = '\0';
  363.   strupper(old_passwd);
  364.   new_passwd[14] = '\0';
  365.   strupper(new_passwd);
  366.   
  367.   /*
  368.    * Calculate the SMB (lanman) hash functions of both old and new passwords.
  369.    */
  370.   
  371.   memset(old_p16, '\0', 16);
  372.   E_P16((uchar *) old_passwd, old_p16);
  373.   
  374.   memset(new_p16, '\0', 16);
  375.   E_P16((uchar *) new_passwd, new_p16);
  376.   
  377.   /*
  378.    * Open the smbpaswd file XXXX - we need to parse smb.conf to get the
  379.    * filename
  380.    */
  381.   if ((fp = fopen(pfile, "r+")) == NULL) {
  382.     err = errno;
  383.     fprintf(stderr, "%s: Failed to open password file %s.\n",
  384.         argv[0], pfile);
  385.     errno = err;
  386.     perror(argv[0]);
  387.     exit(err);
  388.   }
  389.   /* Set read buffer to 16k for effiecient reads */
  390.   setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
  391.   
  392.   /* make sure it is only rw by the owner */
  393.   chmod(pfile, 0600);
  394.   
  395.   /* Lock the smbpasswd file for write. */
  396.   if ((lockfd = pw_file_lock(pfile, F_WRLCK, 5)) < 0) {
  397.     err = errno;
  398.     fprintf(stderr, "%s: Failed to lock password file %s.\n",
  399.         argv[0], pfile);
  400.     fclose(fp);
  401.     errno = err;
  402.     perror(argv[0]);
  403.     exit(err);
  404.   }
  405.   /* Get the smb passwd entry for this user */
  406.   smb_pwent = _my_get_smbpwnam(fp, pwd->pw_name, &valid_old_pwd, 
  407.                    &got_valid_nt_entry, &seekpos);
  408.   if (smb_pwent == NULL) {
  409.     if(add_user == False) {
  410.       fprintf(stderr, "%s: Failed to find entry for user %s in file %s.\n",
  411.             argv[0], pwd->pw_name, pfile);
  412.       fclose(fp);
  413.       pw_file_unlock(lockfd);
  414.       exit(1);
  415.     }
  416.  
  417.     /* Create a new smb passwd entry and set it to the given password. */
  418.     {
  419.       int fd;
  420.       int i;
  421.       int new_entry_length;
  422.       char *new_entry;
  423.       char *p;
  424.       long offpos;
  425.  
  426.       /* The add user write needs to be atomic - so get the fd from 
  427.          the fp and do a raw write() call.
  428.        */
  429.       fd = fileno(fp);
  430.  
  431.       if((offpos = lseek(fd, 0, SEEK_END)) == -1) {
  432.         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
  433. Error was %d\n", argv[0], pwd->pw_name, pfile, errno);
  434.         fclose(fp);
  435.         pw_file_unlock(lockfd);
  436.         exit(1);
  437.       }
  438.  
  439.       new_entry_length = strlen(pwd->pw_name) + 1 + 15 + 1 + 
  440.                          32 + 1 + 32 + 1 + strlen(pwd->pw_gecos) + 
  441.                          1 + strlen(pwd->pw_dir) + 1 + 
  442.                          strlen(pwd->pw_shell) + 1;
  443.       if((new_entry = (char *)malloc( new_entry_length )) == 0) {
  444.         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
  445. Error was %d\n", argv[0], pwd->pw_name, pfile, errno);
  446.         fclose(fp);
  447.         pw_file_unlock(lockfd);
  448.         exit(1);
  449.       }
  450.  
  451.       sprintf(new_entry, "%s:%u:", pwd->pw_name, pwd->pw_uid);
  452.       p = &new_entry[strlen(new_entry)];
  453.       for( i = 0; i < 16; i++)
  454.         sprintf(&p[i*2], "%02X", new_p16[i]);
  455.       p += 32;
  456.       *p++ = ':';
  457.       for( i = 0; i < 16; i++)
  458.         sprintf(&p[i*2], "%02X", new_nt_p16[i]);
  459.       p += 32;
  460.       *p++ = ':';
  461.       sprintf(p, "%s:%s:%s\n", pwd->pw_gecos, 
  462.               pwd->pw_dir, pwd->pw_shell);
  463.       if(write(fd, new_entry, strlen(new_entry)) != strlen(new_entry)) {
  464.         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
  465. Error was %d\n", argv[0], pwd->pw_name, pfile, errno);
  466.         /* Remove the entry we just wrote. */
  467.         if(ftruncate(fd, offpos) == -1) {
  468.           fprintf(stderr, "%s: ERROR failed to ftruncate file %s. \
  469. Error was %d. Password file may be corrupt ! Please examine by hand !\n", 
  470.                    argv[0], pwd->pw_name, errno);
  471.         }
  472.         fclose(fp);
  473.         pw_file_unlock(lockfd);
  474.         exit(1);
  475.       }
  476.       
  477.       fclose(fp);  
  478.       pw_file_unlock(lockfd);  
  479.       exit(0);
  480.     }
  481.   }
  482.  
  483.   /* If we are root or the password is 'NO PASSWORD' then
  484.      we don't need to check the old password. */
  485.   if (real_uid != 0) {
  486.     if (valid_old_pwd == False) {
  487.       fprintf(stderr, "%s: User %s has no old SMB password.\n", argv[0], pwd->pw_name);
  488.     }
  489.     /* Check the old Lanman password - NULL means 'NO PASSWORD' */
  490.     if (smb_pwent->smb_passwd != NULL) {
  491.       if (memcmp(old_p16, smb_pwent->smb_passwd, 16)) {
  492.         fprintf(stderr, "%s: Couldn't change password.\n", argv[0]);
  493.         fclose(fp);
  494.         pw_file_unlock(lockfd);
  495.         exit(1);
  496.       }
  497.     }
  498.     /* Check the NT password if it exists */
  499.     if (smb_pwent->smb_nt_passwd != NULL) {
  500.       if (memcmp(old_nt_p16, smb_pwent->smb_nt_passwd, 16)) {
  501.     fprintf(stderr, "%s: Couldn't change password.\n", argv[0]);
  502.     fclose(fp);
  503.     pw_file_unlock(lockfd);
  504.     exit(1);
  505.       }
  506.     }
  507.   }
  508.   /*
  509.    * If we get here either we were root or the old password checked out
  510.    * ok.
  511.    */
  512.   /* Create the 32 byte representation of the new p16 */
  513.   for (i = 0; i < 16; i++) {
  514.     sprintf(&ascii_p16[i * 2], "%02X", (uchar) new_p16[i]);
  515.   }
  516.   if(got_valid_nt_entry) {
  517.     /* Add on the NT md4 hash */
  518.     ascii_p16[32] = ':';
  519.     for (i = 0; i < 16; i++) {
  520.       sprintf(&ascii_p16[(i * 2)+33], "%02X", (uchar) new_nt_p16[i]);
  521.     }
  522.   }
  523.   /*
  524.    * Do an atomic write into the file at the position defined by
  525.    * seekpos.
  526.    */
  527.   pwfd = fileno(fp);
  528.   ret = lseek(pwfd, seekpos - 1, SEEK_SET);
  529.   if (ret != seekpos - 1) {
  530.     err = errno;
  531.     fprintf(stderr, "%s: seek fail on file %s.\n",
  532.         argv[0], pfile);
  533.     fclose(fp);
  534.     errno = err;
  535.     perror(argv[0]);
  536.     pw_file_unlock(lockfd);
  537.     exit(1);
  538.   }
  539.   /* Sanity check - ensure the character is a ':' */
  540.   if (read(pwfd, &c, 1) != 1) {
  541.     err = errno;
  542.     fprintf(stderr, "%s: read fail on file %s.\n",
  543.         argv[0], pfile);
  544.     fclose(fp);
  545.     errno = err;
  546.     perror(argv[0]);
  547.     pw_file_unlock(lockfd);
  548.     exit(1);
  549.   }
  550.   if (c != ':') {
  551.     fprintf(stderr, "%s: sanity check on passwd file %s failed.\n",
  552.         argv[0], pfile);
  553.     fclose(fp);
  554.     pw_file_unlock(lockfd);
  555.     exit(1);
  556.   }
  557.   writelen = (got_valid_nt_entry) ? 65 : 32;
  558.   if (write(pwfd, ascii_p16, writelen) != writelen) {
  559.     err = errno;
  560.     fprintf(stderr, "%s: write fail in file %s.\n",
  561.         argv[0], pfile);
  562.     fclose(fp);
  563.     errno = err;
  564.     perror(argv[0]);
  565.     pw_file_unlock(lockfd);
  566.     exit(err);
  567.   }
  568.   fclose(fp);
  569.   pw_file_unlock(lockfd);
  570.   printf("Password changed\n");
  571.   return 0;
  572. }
  573.  
  574. #else
  575.  
  576. #include "includes.h"
  577.  
  578. int 
  579. main(int argc, char **argv)
  580. {
  581.   printf("smb password encryption not selected in Makefile\n");
  582.   return 0;
  583. }
  584. #endif
  585.