home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Divers / samba-1.9.18p7.tar.gz / samba-1.9.18p7.tar / samba-1.9.18p7 / source / smbpasswd.c < prev    next >
C/C++ Source or Header  |  1998-05-12  |  20KB  |  730 lines

  1. /*
  2.  * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
  3.  * (C) Jeremy Allison 1995-1998
  4.  * 
  5.  * This program is free software; you can redistribute it and/or modify it under
  6.  * the terms of the GNU General Public License as published by the Free
  7.  * Software Foundation; either version 2 of the License, or (at your option)
  8.  * any later version.
  9.  * 
  10.  * This program is distributed in the hope that it will be useful, but WITHOUT
  11.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13.  * more details.
  14.  * 
  15.  * You should have received a copy of the GNU General Public License along with
  16.  * this program; if not, write to the Free Software Foundation, Inc., 675
  17.  * Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19.  
  20. #include "includes.h"
  21.  
  22. /* 
  23.  * Password changing error codes.
  24.  */
  25.  
  26. struct
  27. {
  28.   int err;
  29.   char *message;
  30. } pw_change_errmap[] =
  31. {
  32.   {5,    "User has insufficient privilege" },
  33.   {86,   "The specified password is invalid" },
  34.   {2226, "Operation only permitted on a Primary Domain Controller"  },
  35.   {2242, "The password of this user has expired." },
  36.   {2243, "The password of this user cannot change." },
  37.   {2244, "This password cannot be used now (password history conflict)." },
  38.   {2245, "The password is shorter than required." },
  39.   {2246, "The password of this user is too recent to change."},
  40.   {0, NULL}
  41. };
  42.  
  43. char *get_error_message(struct cli_state *cli)
  44. {
  45.   static fstring error_message;
  46.   int errclass;
  47.   int errnum;
  48.   int i;
  49.  
  50.   /* 
  51.    * Errors are of two kinds - smb errors,
  52.    * dealt with by cli_errstr, and rap
  53.    * errors, whose error code is in cli.error.
  54.    */
  55.  
  56.   cli_error(cli, &errclass, &errnum);
  57.   if(errclass != 0) 
  58.     return cli_errstr(cli);
  59.  
  60.   slprintf(error_message, sizeof(fstring) - 1, "code %d", cli->error);
  61.       
  62.   for(i = 0; pw_change_errmap[i].message != NULL; i++) {
  63.     if (pw_change_errmap[i].err == cli->error) {
  64.       fstrcpy( error_message, pw_change_errmap[i].message);
  65.       break;
  66.     }
  67.   }
  68.  
  69.   return error_message;
  70. }
  71.  
  72. static int gethexpwd(char *p, char *pwd)
  73. {
  74.     int i;
  75.     unsigned char   lonybble, hinybble;
  76.     char           *hexchars = "0123456789ABCDEF";
  77.     char           *p1, *p2;
  78.     for (i = 0; i < 32; i += 2) {
  79.         hinybble = toupper(p[i]);
  80.         lonybble = toupper(p[i + 1]);
  81.  
  82.         p1 = strchr(hexchars, hinybble);
  83.         p2 = strchr(hexchars, lonybble);
  84.         if (!p1 || !p2)
  85.             return (False);
  86.  
  87.         hinybble = PTR_DIFF(p1, hexchars);
  88.         lonybble = PTR_DIFF(p2, hexchars);
  89.  
  90.         pwd[i / 2] = (hinybble << 4) | lonybble;
  91.     }
  92.     return (True);
  93. }
  94.  
  95. static struct smb_passwd *
  96. _my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd, 
  97.         BOOL *got_valid_nt_entry, long *pwd_seekpos)
  98. {
  99.     /* Static buffers we will return. */
  100.     static struct smb_passwd pw_buf;
  101.     static pstring  user_name;
  102.     static unsigned char smbpwd[16];
  103.     static unsigned char smbntpwd[16];
  104.  
  105.     char            linebuf[256];
  106.     unsigned char   c;
  107.     unsigned char  *p;
  108.     long            uidval;
  109.     long            linebuf_len;
  110.  
  111.     /*
  112.      * Scan the file, a line at a time and check if the name matches.
  113.      */
  114.     while (!feof(fp)) {
  115.         linebuf[0] = '\0';
  116.         *pwd_seekpos = ftell(fp);
  117.  
  118.         fgets(linebuf, 256, fp);
  119.         if (ferror(fp))
  120.             return NULL;
  121.  
  122.         /*
  123.          * Check if the string is terminated with a newline - if not
  124.          * then we must keep reading and discard until we get one.
  125.          */
  126.         linebuf_len = strlen(linebuf);
  127.         if (linebuf[linebuf_len - 1] != '\n') {
  128.             c = '\0';
  129.             while (!ferror(fp) && !feof(fp)) {
  130.                 c = fgetc(fp);
  131.                 if (c == '\n')
  132.                     break;
  133.             }
  134.         } else
  135.             linebuf[linebuf_len - 1] = '\0';
  136.  
  137.         if ((linebuf[0] == 0) && feof(fp))
  138.             break;
  139.         /*
  140.          * The line we have should be of the form :-
  141.          * 
  142.          * username:uid:[32hex bytes]:....other flags presently
  143.          * ignored....
  144.          * 
  145.          * or,
  146.          * 
  147.          * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
  148.          * 
  149.          * if Windows NT compatible passwords are also present.
  150.          */
  151.  
  152.         if (linebuf[0] == '#' || linebuf[0] == '\0')
  153.             continue;
  154.         p = (unsigned char *) strchr(linebuf, ':');
  155.         if (p == NULL)
  156.             continue;
  157.         /*
  158.          * As 256 is shorter than a pstring we don't need to check
  159.          * length here - if this ever changes....
  160.          */
  161.         strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
  162.         user_name[PTR_DIFF(p, linebuf)] = '\0';
  163.         if (!strequal(user_name, name))
  164.             continue;
  165.  
  166.         /* User name matches - get uid and password */
  167.         p++;        /* Go past ':' */
  168.         if (!isdigit(*p))
  169.             return (False);
  170.  
  171.         uidval = atoi((char *) p);
  172.         while (*p && isdigit(*p))
  173.             p++;
  174.  
  175.         if (*p != ':')
  176.             return (False);
  177.  
  178.         /*
  179.          * Now get the password value - this should be 32 hex digits
  180.          * which are the ascii representations of a 16 byte string.
  181.          * Get two at a time and put them into the password.
  182.          */
  183.         p++;
  184.         *pwd_seekpos += PTR_DIFF(p, linebuf);    /* Save exact position
  185.                              * of passwd in file -
  186.                              * this is used by
  187.                              * smbpasswd.c */
  188.         if (*p == '*' || *p == 'X') {
  189.             /* Password deliberately invalid - end here. */
  190.             *valid_old_pwd = False;
  191.             *got_valid_nt_entry = False;
  192.             pw_buf.smb_nt_passwd = NULL;    /* No NT password (yet)*/
  193.  
  194.             /* Now check if the NT compatible password is
  195.                available. */
  196.             p += 33; /* Move to the first character of the line after 
  197.                         the lanman password. */
  198.             if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
  199.                 /* NT Entry was valid - even if 'X' or '*', can be overwritten */
  200.                 *got_valid_nt_entry = True;
  201.                 if (*p != '*' && *p != 'X') {
  202.                   if (gethexpwd((char *)p,(char *)smbntpwd))
  203.                     pw_buf.smb_nt_passwd = smbntpwd;
  204.                 }
  205.             }
  206.             pw_buf.smb_name = user_name;
  207.             pw_buf.smb_userid = uidval;
  208.             pw_buf.smb_passwd = NULL;    /* No password */
  209.             return (&pw_buf);
  210.         }
  211.         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33))
  212.             return (False);
  213.  
  214.         if (p[32] != ':')
  215.             return (False);
  216.  
  217.         if (!strncasecmp((char *)p, "NO PASSWORD", 11)) {
  218.           pw_buf.smb_passwd = NULL;    /* No password */
  219.         } else {
  220.           if(!gethexpwd((char *)p,(char *)smbpwd))
  221.             return False;
  222.           pw_buf.smb_passwd = smbpwd;
  223.         }
  224.  
  225.         pw_buf.smb_name = user_name;
  226.         pw_buf.smb_userid = uidval;
  227.         pw_buf.smb_nt_passwd = NULL;
  228.         *got_valid_nt_entry = False;
  229.         *valid_old_pwd = True;
  230.  
  231.         /* Now check if the NT compatible password is
  232.            available. */
  233.         p += 33; /* Move to the first character of the line after 
  234.                     the lanman password. */
  235.         if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
  236.             /* NT Entry was valid - even if 'X' or '*', can be overwritten */
  237.             *got_valid_nt_entry = True;
  238.             if (*p != '*' && *p != 'X') {
  239.               if (gethexpwd((char *)p,(char *)smbntpwd))
  240.                 pw_buf.smb_nt_passwd = smbntpwd;
  241.             }
  242.         }
  243.         return &pw_buf;
  244.     }
  245.     return NULL;
  246. }
  247.  
  248. /*
  249.  * Print command usage on stderr and die.
  250.  */
  251. static void usage(char *name, BOOL is_root)
  252. {
  253.     if(is_root)
  254.         fprintf(stderr, "Usage is : %s [-a] [-D DEBUGLEVEL] [username] [password]\n\
  255. %s: [-r machine] [-D DEBUGLEVEL] [username] [password]\n%s: [-h]\n", name, name, name);
  256.     else
  257.         fprintf(stderr, "Usage is : %s [-h] [-D DEBUGLEVEL] [-r machine] [password]\n", name);
  258.     exit(1);
  259. }
  260.  
  261. int main(int argc, char **argv)
  262. {
  263.   extern char *optarg;
  264.   extern int optind;
  265.   extern int DEBUGLEVEL;
  266.   char *prog_name;
  267.   int             real_uid;
  268.   struct passwd  *pwd;
  269.   fstring         old_passwd;
  270.   uchar           old_p16[16];
  271.   uchar           old_nt_p16[16];
  272.   fstring         new_passwd;
  273.   uchar           new_p16[16];
  274.   uchar           new_nt_p16[16];
  275.   char           *p;
  276.   struct smb_passwd *smb_pwent;
  277.   FILE           *fp;
  278.   BOOL            valid_old_pwd = False;
  279.   BOOL         got_valid_nt_entry = False;
  280.   long            seekpos;
  281.   int             pwfd;
  282.   char            ascii_p16[66];
  283.   char            c;
  284.   int             ch;
  285.   int             ret, i, err, writelen;
  286.   int             lockfd = -1;
  287.   char           *pfile = SMB_PASSWD_FILE;
  288.   char            readbuf[16 * 1024];
  289.   BOOL is_root = False;
  290.   pstring  user_name;
  291.   char *remote_machine = NULL;
  292.   BOOL         add_user = False;
  293.   BOOL         got_new_pass = False;
  294.   pstring servicesf = CONFIGFILE;
  295.  
  296.   new_passwd[0] = '\0';
  297.   user_name[0] = '\0';
  298.  
  299.   memset(old_passwd, '\0', sizeof(old_passwd));
  300.   memset(new_passwd, '\0', sizeof(new_passwd));
  301.  
  302.   prog_name = argv[0];
  303.  
  304.   TimeInit();
  305.  
  306.   setup_logging(prog_name,True);
  307.   
  308.   charset_initialise();
  309.  
  310.   if (!lp_load(servicesf,True)) {
  311.     fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n", prog_name, servicesf);
  312.   }
  313.     
  314.   codepage_initialise(lp_client_code_page());
  315.  
  316.   /* Get the real uid */
  317.   real_uid = getuid();
  318.   
  319.   /* Check the effective uid */
  320.   if ((geteuid() == 0) && (real_uid != 0)) {
  321.     fprintf(stderr, "%s: Must *NOT* be setuid root.\n", prog_name);
  322.     exit(1);
  323.   }
  324.  
  325.   is_root = (real_uid == 0);
  326.  
  327.   while ((ch = getopt(argc, argv, "ahr:D:")) != EOF) {
  328.     switch(ch) {
  329.     case 'a':
  330.       add_user = True;
  331.       break;
  332.     case 'r':
  333.       remote_machine = optarg;
  334.       break;
  335.     case 'D':
  336.       DEBUGLEVEL = atoi(optarg);
  337.       break;
  338.     case 'h':
  339.     default:
  340.       usage(prog_name, is_root);
  341.     }
  342.   }
  343.  
  344.   argc -= optind;
  345.   argv += optind;
  346.  
  347.   /*
  348.    * Ensure add_user and remote machine are
  349.    * not both set.
  350.    */
  351.   if(add_user && (remote_machine != NULL))
  352.     usage(prog_name, True);
  353.  
  354.   if( is_root ) {
  355.  
  356.     /*
  357.      * Deal with root - can add a user, but only locally.
  358.      */
  359.  
  360.     switch(argc) {
  361.       case 0:
  362.         break;
  363.       case 1:
  364.         /* If we are root we can change another's password. */
  365.         pstrcpy(user_name, argv[0]);
  366.         break;
  367.       case 2:
  368.         pstrcpy(user_name, argv[0]);
  369.         fstrcpy(new_passwd, argv[1]);
  370.         got_new_pass = True;
  371.         break;
  372.       default:
  373.         usage(prog_name, True);
  374.     }
  375.  
  376.     if(*user_name) {
  377.       if((pwd = getpwnam(user_name)) == NULL) {
  378.         fprintf(stderr, "%s: User \"%s\" was not found in system password file.\n", 
  379.                     prog_name, user_name);
  380.         exit(1);
  381.       }
  382.     } else {
  383.       if((pwd = getpwuid(real_uid)) != NULL)
  384.         pstrcpy( user_name, pwd->pw_name);
  385.     }
  386.  
  387.   } else {
  388.  
  389.     if(add_user) {
  390.       fprintf(stderr, "%s: Only root can set anothers password.\n", prog_name);
  391.       usage(prog_name, False);
  392.     }
  393.  
  394.     if(argc > 1)
  395.       usage(prog_name, False);
  396.  
  397.     if(argc == 1) {
  398.       fstrcpy(new_passwd, argv[0]);
  399.       got_new_pass = True;
  400.     }
  401.  
  402.     if((pwd = getpwuid(real_uid)) != NULL)
  403.       pstrcpy( user_name, pwd->pw_name);
  404.  
  405.     /*
  406.      * A non-root user is always setting a password
  407.      * via a remote machine (even if that machine is
  408.      * localhost).
  409.      */
  410.  
  411.     if(remote_machine == NULL)
  412.       remote_machine = "127.0.0.1";
  413.   }    
  414.     
  415.   if (*user_name == '\0') {
  416.     fprintf(stderr, "%s: Unable to get a user name for password change.\n", prog_name);
  417.     exit(1);
  418.   }
  419.  
  420.   /* 
  421.    * If we are root we don't ask for the old password (unless it's on a
  422.    * remote machine.
  423.    */
  424.  
  425.   if (remote_machine != NULL) {
  426.     p = getpass("Old SMB password:");
  427.     fstrcpy(old_passwd, p);
  428.   }
  429.  
  430.   if (!got_new_pass) {
  431.     new_passwd[0] = '\0';
  432.  
  433.     p = getpass("New SMB password:");
  434.  
  435.     strncpy(new_passwd, p, sizeof(fstring));
  436.     new_passwd[sizeof(fstring)-1] = '\0';
  437.  
  438.     p = getpass("Retype new SMB password:");
  439.  
  440.     if (strncmp(p, new_passwd, sizeof(fstring)-1))
  441.     {
  442.       fprintf(stderr, "%s: Mismatch - password unchanged.\n", prog_name);
  443.       exit(1);
  444.     }
  445.   }
  446.   
  447.   if (new_passwd[0] == '\0') {
  448.     printf("Password not set\n");
  449.     exit(0);
  450.   }
  451.  
  452.   /* 
  453.    * Now do things differently depending on if we're changing the
  454.    * password on a remote machine. Remember - a normal user is
  455.    * always using this code, looping back to the local smbd.
  456.    */
  457.  
  458.   if(remote_machine != NULL) {
  459.     struct cli_state cli;
  460.     struct in_addr ip;
  461.     fstring myname;
  462.  
  463.     if(get_myname(myname,NULL) == False) {
  464.       fprintf(stderr, "%s: unable to get my hostname.\n", prog_name );
  465.       exit(1);
  466.     }
  467.  
  468.     if(!resolve_name( remote_machine, &ip)) {
  469.       fprintf(stderr, "%s: unable to find an IP address for machine %s.\n",
  470.               prog_name, remote_machine );
  471.       exit(1);
  472.     }
  473.  
  474.     memset(&cli, '\0', sizeof(struct cli_state));
  475.  
  476.     if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
  477.       fprintf(stderr, "%s: unable to connect to SMB server on machine %s. Error was : %s.\n",
  478.               prog_name, remote_machine, get_error_message(&cli) );
  479.       exit(1);
  480.     }
  481.   
  482.     if (!cli_session_request(&cli, remote_machine, 0x20, myname)) {
  483.       fprintf(stderr, "%s: machine %s rejected the session request. Error was : %s.\n",
  484.               prog_name, remote_machine, get_error_message(&cli) );
  485.       cli_shutdown(&cli);
  486.       exit(1);
  487.     }
  488.   
  489.     cli.protocol = PROTOCOL_NT1;
  490.  
  491.     if (!cli_negprot(&cli)) {
  492.       fprintf(stderr, "%s: machine %s rejected the negotiate protocol. Error was : %s.\n",        
  493.               prog_name, remote_machine, get_error_message(&cli) );
  494.       cli_shutdown(&cli);
  495.       exit(1);
  496.     }
  497.   
  498.     if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd),
  499.                            "", 0, "")) {
  500.       fprintf(stderr, "%s: machine %s rejected the session setup. Error was : %s.\n",        
  501.               prog_name, remote_machine, get_error_message(&cli) );
  502.       cli_shutdown(&cli);
  503.       exit(1);
  504.     }               
  505.  
  506.     if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
  507.       fprintf(stderr, "%s: machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
  508.               prog_name, remote_machine, get_error_message(&cli) );
  509.       cli_shutdown(&cli);
  510.       exit(1);
  511.     }
  512.  
  513.     if(!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
  514.       fprintf(stderr, "%s: machine %s rejected the password change: Error was : %s.\n",
  515.               prog_name, remote_machine, get_error_message(&cli) );
  516.       cli_shutdown(&cli);
  517.       exit(1);
  518.     }
  519.  
  520.     cli_shutdown(&cli);
  521.     exit(0);
  522.   }
  523.  
  524.   /* Calculate the MD4 hash (NT compatible) of the old and new passwords */
  525.   memset(old_nt_p16, '\0', 16);
  526.   E_md4hash((uchar *)old_passwd, old_nt_p16);
  527.   
  528.   memset(new_nt_p16, '\0', 16);
  529.   E_md4hash((uchar *) new_passwd, new_nt_p16);
  530.   
  531.   /* Mangle the passwords into Lanman format */
  532.   old_passwd[14] = '\0';
  533.   strupper(old_passwd);
  534.   new_passwd[14] = '\0';
  535.   strupper(new_passwd);
  536.   
  537.   /*
  538.    * Calculate the SMB (lanman) hash functions of both old and new passwords.
  539.    */
  540.   
  541.   memset(old_p16, '\0', 16);
  542.   E_P16((uchar *) old_passwd, old_p16);
  543.   
  544.   memset(new_p16, '\0', 16);
  545.   E_P16((uchar *) new_passwd, new_p16);
  546.   
  547.   /*
  548.    * Open the smbpaswd file XXXX - we need to parse smb.conf to get the
  549.    * filename
  550.    */
  551.   fp = fopen(pfile, "r+");
  552.   if (!fp && errno == ENOENT) {
  553.       fp = fopen(pfile, "w");
  554.       if (fp) {
  555.           fprintf(fp, "# Samba SMB password file\n");
  556.           fclose(fp);
  557.           fp = fopen(pfile, "r+");
  558.       }
  559.   }
  560.   if (!fp) {
  561.       err = errno;
  562.       fprintf(stderr, "%s: Failed to open password file %s.\n",
  563.           prog_name, pfile);
  564.       errno = err;
  565.       perror(prog_name);
  566.       exit(err);
  567.   }
  568.   
  569.   /* Set read buffer to 16k for effiecient reads */
  570.   setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
  571.   
  572.   /* make sure it is only rw by the owner */
  573.   chmod(pfile, 0600);
  574.  
  575.   /* Lock the smbpasswd file for write. */
  576.   if ((lockfd = pw_file_lock(pfile, F_WRLCK, 5)) < 0) {
  577.     err = errno;
  578.     fprintf(stderr, "%s: Failed to lock password file %s.\n",
  579.         prog_name, pfile);
  580.     fclose(fp);
  581.     errno = err;
  582.     perror(prog_name);
  583.     exit(err);
  584.   }
  585.   /* Get the smb passwd entry for this user */
  586.   smb_pwent = _my_get_smbpwnam(fp, user_name, &valid_old_pwd, 
  587.                    &got_valid_nt_entry, &seekpos);
  588.   if (smb_pwent == NULL) {
  589.     if(add_user == False) {
  590.       fprintf(stderr, "%s: Failed to find entry for user %s in file %s.\n",
  591.             prog_name, pwd->pw_name, pfile);
  592.       fclose(fp);
  593.       pw_file_unlock(lockfd);
  594.       exit(1);
  595.     }
  596.  
  597.     /* Create a new smb passwd entry and set it to the given password. */
  598.     {
  599.       int fd;
  600.       int new_entry_length;
  601.       char *new_entry;
  602.       long offpos;
  603.  
  604.       /* The add user write needs to be atomic - so get the fd from 
  605.          the fp and do a raw write() call.
  606.        */
  607.       fd = fileno(fp);
  608.  
  609.       if((offpos = lseek(fd, 0, SEEK_END)) == -1) {
  610.         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
  611. Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
  612.         fclose(fp);
  613.         pw_file_unlock(lockfd);
  614.         exit(1);
  615.       }
  616.  
  617.       new_entry_length = strlen(pwd->pw_name) + 1 + 15 + 1 + 
  618.                          32 + 1 + 32 + 1 + strlen(pwd->pw_gecos) + 
  619.                          1 + strlen(pwd->pw_dir) + 1 + 
  620.                          strlen(pwd->pw_shell) + 1;
  621.       if((new_entry = (char *)malloc( new_entry_length )) == 0) {
  622.         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
  623. Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
  624.         fclose(fp);
  625.         pw_file_unlock(lockfd);
  626.         exit(1);
  627.       }
  628.  
  629.       slprintf(new_entry, new_entry_length - 1, "%s:%u:", pwd->pw_name, (unsigned)pwd->pw_uid);
  630.       p = &new_entry[strlen(new_entry)];
  631.       for( i = 0; i < 16; i++)
  632.         slprintf(&p[i*2], new_entry_length - (p - new_entry) - (i*2) - 1, "%02X", new_p16[i]);
  633.       p += 32;
  634.       *p++ = ':';
  635.       for( i = 0; i < 16; i++)
  636.         slprintf(&p[i*2], new_entry_length - (p - new_entry) - (i*2) - 1,"%02X", new_nt_p16[i]);
  637.       p += 32;
  638.       *p++ = ':';
  639.       slprintf(p, new_entry_length - (p - new_entry) - 1, "%s:%s:%s\n", pwd->pw_gecos, 
  640.               pwd->pw_dir, pwd->pw_shell);
  641.       if(write(fd, new_entry, strlen(new_entry)) != strlen(new_entry)) {
  642.         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
  643. Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
  644.         /* Remove the entry we just wrote. */
  645.         if(ftruncate(fd, offpos) == -1) {
  646.           fprintf(stderr, "%s: ERROR failed to ftruncate file %s. \
  647. Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
  648.                    prog_name, pwd->pw_name, strerror(errno));
  649.         }
  650.         fclose(fp);
  651.         pw_file_unlock(lockfd);
  652.         exit(1);
  653.       }
  654.       
  655.       fclose(fp);  
  656.       pw_file_unlock(lockfd);  
  657.       exit(0);
  658.     }
  659.   } else {
  660.       /* the entry already existed */
  661.       add_user = False;
  662.   }
  663.  
  664.   /*
  665.    * We are root - just write the new password.
  666.    */
  667.  
  668.   /* Create the 32 byte representation of the new p16 */
  669.   for (i = 0; i < 16; i++) {
  670.     slprintf(&ascii_p16[i * 2], sizeof(ascii_p16) - (i*2) - 1, "%02X", (uchar) new_p16[i]);
  671.   }
  672.   if(got_valid_nt_entry) {
  673.     /* Add on the NT md4 hash */
  674.     ascii_p16[32] = ':';
  675.     for (i = 0; i < 16; i++) {
  676.       slprintf(&ascii_p16[(i * 2)+33], sizeof(ascii_p16) - (i*2) - 32, "%02X", (uchar) new_nt_p16[i]);
  677.     }
  678.   }
  679.   /*
  680.    * Do an atomic write into the file at the position defined by
  681.    * seekpos.
  682.    */
  683.   pwfd = fileno(fp);
  684.   ret = lseek(pwfd, seekpos - 1, SEEK_SET);
  685.   if (ret != seekpos - 1) {
  686.     err = errno;
  687.     fprintf(stderr, "%s: seek fail on file %s.\n",
  688.         prog_name, pfile);
  689.     fclose(fp);
  690.     errno = err;
  691.     perror(prog_name);
  692.     pw_file_unlock(lockfd);
  693.     exit(1);
  694.   }
  695.   /* Sanity check - ensure the character is a ':' */
  696.   if (read(pwfd, &c, 1) != 1) {
  697.     err = errno;
  698.     fprintf(stderr, "%s: read fail on file %s.\n",
  699.         prog_name, pfile);
  700.     fclose(fp);
  701.     errno = err;
  702.     perror(prog_name);
  703.     pw_file_unlock(lockfd);
  704.     exit(1);
  705.   }
  706.   if (c != ':') {
  707.     fprintf(stderr, "%s: sanity check on passwd file %s failed.\n",
  708.         prog_name, pfile);
  709.     fclose(fp);
  710.     pw_file_unlock(lockfd);
  711.     exit(1);
  712.   }
  713.   writelen = (got_valid_nt_entry) ? 65 : 32;
  714.   if (write(pwfd, ascii_p16, writelen) != writelen) {
  715.     err = errno;
  716.     fprintf(stderr, "%s: write fail in file %s.\n",
  717.         prog_name, pfile);
  718.     fclose(fp);
  719.     errno = err;
  720.     perror(prog_name);
  721.     pw_file_unlock(lockfd);
  722.     exit(err);
  723.   }
  724.   fclose(fp);
  725.   pw_file_unlock(lockfd);
  726.   printf("Password changed\n");
  727.   return 0;
  728. }
  729.  
  730.