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