Integration of Anders Blomdell <anders.blomdell@control.lth.se>'s
authorJeremy Allison <jra@samba.org>
Thu, 24 Sep 1998 22:33:13 +0000 (22:33 +0000)
committerJeremy Allison <jra@samba.org>
Thu, 24 Sep 1998 22:33:13 +0000 (22:33 +0000)
smbpasswd changes. Not exactly the same as his code - several
changes.
Jeremy.
(This used to be commit e96747a8e3b9ea5a79c4258e55d7e8f3bf0bf193)

source3/include/proto.h
source3/include/smb.h
source3/passdb/ldap.c
source3/passdb/nispass.c
source3/passdb/passdb.c
source3/passdb/smbpass.c
source3/script/addtosmbpass
source3/script/mksmbpasswd.sh
source3/smbd/oplock.c
source3/utils/smbpasswd.c

index cbd4f07b2f3949940c01a00153f2f0ab47f398f1..fd77c45ee35d1771e9aff98f2d8f0aac554c813c 100644 (file)
@@ -1101,7 +1101,7 @@ void pdb_init_smb(struct smb_passwd *user);
 void pdb_init_sam(struct sam_passwd *user);
 struct sam_disp_info *pdb_sam_to_dispinfo(struct sam_passwd *user);
 struct smb_passwd *pdb_sam_to_smb(struct sam_passwd *user);
-char *pdb_encode_acct_ctrl(uint16 acct_ctrl);
+char *pdb_encode_acct_ctrl(uint16 acct_ctrl, size_t length);
 uint16 pdb_decode_acct_ctrl(char *p);
 BOOL pdb_gethexpwd(char *p, char *pwd);
 BOOL pdb_name_to_rid(char *user_name, uint32 *u_rid, uint32 *g_rid);
index 842aeb57be0ec9c5b57c11c39eb8f6de07b5dca2..bec14ddd13739949a2c532852ed688a55bcee14e 100644 (file)
@@ -1601,6 +1601,12 @@ extern int unix_ERR_code;
                 __FILE__, __LINE__)), smb_panic("assert failed")))
 #define SMB_ASSERT_ARRAY(a,n) SMB_ASSERT((sizeof(a)/sizeof((a)[0])) >= (n))
 
+/*
+ * Size of new password account encoding string. DO NOT CHANGE.
+ */
+
+#define NEW_PW_FORMAT_SPACE_PADDED_LEN 14
+
 #endif /* _SMB_H */
 
 #include "ntdomain.h"
index e27b4fbea7be35061b7dfdcb3916473245c6d26e..80ba6be3a79ae33970c9596ca98501541baaa146 100644 (file)
@@ -576,7 +576,7 @@ static BOOL modadd_ldappwd_entry(struct smb_passwd *newpwd, int flag)
        
        make_a_mod(&mods, ldap_state, "rid", rid);
        make_a_mod(&mods, ldap_state, "pwdLastSet", lst);
-       make_a_mod(&mods, ldap_state, "userAccountControl", pdb_encode_acct_ctrl(newpwd->acct_ctrl));
+       make_a_mod(&mods, ldap_state, "userAccountControl", pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
        
        switch(flag)
        {
index 04c1c23b2e6a62263179d49525841b666592bbde..4a2a7723d643e4756956d23418c4e3f07d14e3b8 100644 (file)
@@ -376,7 +376,7 @@ static BOOL add_nisp21pwd_entry(struct sam_passwd *newpwd)
        slprintf(smb_grpid, sizeof(smb_grpid), "%u", newpwd->smb_grpid);
        slprintf(group_rid, sizeof(group_rid), "0x%x", newpwd->group_rid);
 
-       safe_strcpy(acb, pdb_encode_acct_ctrl(newpwd->acct_ctrl), sizeof(acb)); 
+       safe_strcpy(acb, pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN), sizeof(acb)); 
 
        set_single_attribute(&new_obj, NPF_NAME          , newpwd->smb_name     , strlen(newpwd->smb_name)     , 0);
        set_single_attribute(&new_obj, NPF_UID           , uid                  , strlen(uid)                  , 0);
index 02f30b24c87a38331ecc6b8225f95245d7d17029..6633088245aff4f95504f875a716013167dd2470 100644 (file)
@@ -491,26 +491,35 @@ struct smb_passwd *pdb_sam_to_smb(struct sam_passwd *user)
 
 /**********************************************************
  Encode the account control bits into a string.
+ length = length of string to encode into (including terminating
+ null). length *MUST BE MORE THAN 2* !
  **********************************************************/
-char *pdb_encode_acct_ctrl(uint16 acct_ctrl)
+
+char *pdb_encode_acct_ctrl(uint16 acct_ctrl, size_t length)
 {
   static fstring acct_str;
-  char *p = acct_str;
-  *p++ = '[';
-
-  if (acct_ctrl & ACB_HOMDIRREQ) *p++ = 'H';
-  if (acct_ctrl & ACB_TEMPDUP  ) *p++ = 'T'; 
-  if (acct_ctrl & ACB_NORMAL   ) *p++ = 'U';
-  if (acct_ctrl & ACB_MNS      ) *p++ = 'M';
-  if (acct_ctrl & ACB_WSTRUST  ) *p++ = 'W';
-  if (acct_ctrl & ACB_SVRTRUST ) *p++ = 'S';
-  if (acct_ctrl & ACB_AUTOLOCK ) *p++ = 'L';
-  if (acct_ctrl & ACB_PWNOEXP  ) *p++ = 'X';
-  if (acct_ctrl & ACB_DOMTRUST ) *p++ = 'I';
-      
-  *p++ = ']';
-  *p = '\0';
+  size_t i = 0;
+
+  acct_str[i++] = '[';
+
+  if (acct_ctrl & ACB_PWNOTREQ ) acct_str[i++] = 'N';
+  if (acct_ctrl & ACB_DISABLED ) acct_str[i++] = 'D';
+  if (acct_ctrl & ACB_HOMDIRREQ) acct_str[i++] = 'H';
+  if (acct_ctrl & ACB_TEMPDUP  ) acct_str[i++] = 'T'; 
+  if (acct_ctrl & ACB_NORMAL   ) acct_str[i++] = 'U';
+  if (acct_ctrl & ACB_MNS      ) acct_str[i++] = 'M';
+  if (acct_ctrl & ACB_WSTRUST  ) acct_str[i++] = 'W';
+  if (acct_ctrl & ACB_SVRTRUST ) acct_str[i++] = 'S';
+  if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L';
+  if (acct_ctrl & ACB_PWNOEXP  ) acct_str[i++] = 'X';
+  if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I';
+
+  for ( ; i < length - 2 ; i++ ) { acct_str[i] = ' '; }
+
+  i = length - 2;
+  acct_str[i++] = ']';
+  acct_str[i++] = '\0';
+
   return acct_str;
 }     
 
@@ -538,15 +547,8 @@ uint16 pdb_decode_acct_ctrl(char *p)
        {
                switch (*p)
                {
-#if 0
-                       /*
-                        * Hmmm. Don't allow these to be set/read independently
-                        * of the actual password fields. We don't want a mismatch.
-                        * JRA.
-                        */
                        case 'N': { acct_ctrl |= ACB_PWNOTREQ ; break; /* 'N'o password. */ }
                        case 'D': { acct_ctrl |= ACB_DISABLED ; break; /* 'D'isabled. */ }
-#endif 
                        case 'H': { acct_ctrl |= ACB_HOMDIRREQ; break; /* 'H'omedir required. */ }
                        case 'T': { acct_ctrl |= ACB_TEMPDUP  ; break; /* 'T'emp account. */ } 
                        case 'U': { acct_ctrl |= ACB_NORMAL   ; break; /* 'U'ser account (normal). */ } 
@@ -556,7 +558,7 @@ uint16 pdb_decode_acct_ctrl(char *p)
                        case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ } 
                        case 'X': { acct_ctrl |= ACB_PWNOEXP  ; break; /* No 'X'piry on password */ } 
                        case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
-
+            case ' ':
                        case ':':
                        case '\n':
                        case '\0': 
index a2b648b2fd239d47eabc1fdbaef7e6c8e2aed486..cff049dcd5f3fd32d04d7f4ffcaef535560d1db4 100644 (file)
@@ -264,7 +264,7 @@ static struct smb_passwd *getsmbfilepwent(void *vp)
         p++;
       if(*p == ':') {
         p++;
-        if(*p && StrnCaseCmp((char *)p, "LCT-", 4)) {
+        if(*p && (StrnCaseCmp((char *)p, "LCT-", 4)==0)) {
           int i;
           p += 4;
           for(i = 0; i < 8; i++) {
@@ -473,7 +473,7 @@ Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
     return False;
   }
 
-  new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + 5 + 1 + 13 + 2;
+  new_entry_length = strlen(newpwd->smb_name) + 1 + 15 + 1 + 32 + 1 + 32 + 1 + NEW_PW_FORMAT_SPACE_PADDED_LEN + 1 + 13 + 2;
 
   if((new_entry = (char *)malloc( new_entry_length )) == NULL) {
     DEBUG(0, ("add_smbfilepwd_entry(malloc): Failed to add entry for user %s to file %s. \
@@ -518,8 +518,7 @@ Error was %s\n", newpwd->smb_name, pfile, strerror(errno)));
 
   /* Add the account encoding and the last change time. */
   slprintf((char *)p, new_entry_length - 1 - (p - new_entry),  "%s:LCT-%08X:\n",
-                                  pdb_encode_acct_ctrl(newpwd->acct_ctrl),
-                     (uint32)time(NULL));
+           pdb_encode_acct_ctrl(newpwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN), (uint32)time(NULL));
 
 #ifdef DEBUG_PASSWORD
   DEBUG(100, ("add_smbfilepwd_entry(%d): new_entry_len %d entry_len %d made line |%s|", 
@@ -538,9 +537,11 @@ Error was %s. Password file may be corrupt ! Please examine by hand !\n",
     }
 
     endsmbfilepwent(fp);
+    free(new_entry);
     return False;
   }
 
+  free(new_entry);
   endsmbfilepwent(fp);
   return True;
 }
@@ -654,7 +655,7 @@ static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
      * 
      * or,
      *
-     * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
+     * username:uid:[32hex bytes]:[32hex bytes]:[attributes]:LCT-XXXXXXXX:...ignored.
      *
      * if Windows NT compatible passwords are also present.
      */
@@ -772,11 +773,31 @@ static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
   if (*p == '[') {
 
     i = 0;
-    p++;
+    encode_bits[i++] = *p++;
     while((linebuf_len > PTR_DIFF(p, linebuf)) && (*p != ']'))
       encode_bits[i++] = *p++;
 
-    encode_bits[i] = '\0';
+    encode_bits[i++] = ']';
+    encode_bits[i++] = '\0';
+
+    if(i == NEW_PW_FORMAT_SPACE_PADDED_LEN) {
+      /*
+       * We are using a new format, space padded
+       * acct ctrl field. Encode the given acct ctrl
+       * bits into it.
+       */
+      fstrcpy(encode_bits, pdb_encode_acct_ctrl(pwd->acct_ctrl, NEW_PW_FORMAT_SPACE_PADDED_LEN));
+    } else {
+      /*
+       * If using the old format and the ACB_DISABLED or
+       * ACB_PWNOTREQ are set then set the lanman and NT passwords to NULL
+       * here as we have no space to encode the change.
+       */
+      if(pwd->acct_ctrl & (ACB_DISABLED|ACB_PWNOTREQ)) {
+        pwd->smb_passwd = NULL;
+        pwd->smb_nt_passwd = NULL;
+      }
+    }
 
     /* Go past the ']' */
     if(linebuf_len > PTR_DIFF(p, linebuf))
@@ -785,8 +806,8 @@ static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
     if((linebuf_len > PTR_DIFF(p, linebuf)) && (*p == ':')) {
       p++;
 
-      /* We should be pointing at the TLC entry. */
-      if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && StrnCaseCmp((char *)p, "LCT-", 4)) {
+      /* We should be pointing at the LCT entry. */
+      if((linebuf_len > (PTR_DIFF(p, linebuf) + 13)) && (StrnCaseCmp((char *)p, "LCT-", 4) == 0)) {
 
         p += 4;
         for(i = 0; i < 8; i++) {
@@ -807,39 +828,6 @@ static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
 
   /* Entry is correctly formed. */
 
-  /*
-   * Do an atomic write into the file at the position defined by
-   * seekpos.
-   */
-
-  /* The mod user write needs to be atomic - so get the fd from 
-     the fp and do a raw write() call.
-   */
-
-  fd = fileno(fp);
-
-  if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
-    DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
-    pw_file_unlock(lockfd,&pw_file_lock_depth);
-    fclose(fp);
-    return False;
-  }
-
-  /* Sanity check - ensure the character is a ':' */
-  if (read(fd, &c, 1) != 1) {
-    DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
-    pw_file_unlock(lockfd,&pw_file_lock_depth);
-    fclose(fp);
-    return False;
-  }
-
-  if (c != ':')        {
-    DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
-    pw_file_unlock(lockfd,&pw_file_lock_depth);
-    fclose(fp);
-    return False;
-  }
   /* Create the 32 byte representation of the new p16 */
   if(pwd->smb_passwd != NULL) {
     for (i = 0; i < 16; i++) {
@@ -854,7 +842,7 @@ static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
 
   /* Add on the NT md4 hash */
   ascii_p16[32] = ':';
-  wr_len = 65;
+  wr_len = 66;
   if (pwd->smb_nt_passwd != NULL) {
     for (i = 0; i < 16; i++) {
       slprintf(&ascii_p16[(i*2)+33], sizeof(fstring) - 1, "%02X", (uchar) pwd->smb_nt_passwd[i]);
@@ -865,6 +853,7 @@ static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
     else
       fstrcpy(&ascii_p16[33], "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
   }
+  ascii_p16[65] = ':';
 
   /* Add on the account info bits and the time of last
      password change. */
@@ -874,7 +863,7 @@ static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
   if(got_pass_last_set_time) {
     slprintf(&ascii_p16[strlen(ascii_p16)], 
             sizeof(ascii_p16)-(strlen(ascii_p16)+1),
-            ":[%s]:TLC-%08X:", 
+            "%s:LCT-%08X:", 
                      encode_bits, (uint32)pwd->pass_last_set_time );
     wr_len = strlen(ascii_p16);
   }
@@ -884,6 +873,53 @@ static BOOL mod_smbfilepwd_entry(struct smb_passwd* pwd, BOOL override)
   dump_data(100, ascii_p16, wr_len);
 #endif
 
+  if(wr_len > sizeof(linebuf)) {
+    DEBUG(0, ("mod_smbfilepwd_entry: line to write (%d) is too long.\n", wr_len+1));
+    pw_file_unlock(lockfd,&pw_file_lock_depth);
+    fclose(fp);
+    return (False);
+  }
+
+  /*
+   * Do an atomic write into the file at the position defined by
+   * seekpos.
+   */
+
+  /* The mod user write needs to be atomic - so get the fd from 
+     the fp and do a raw write() call.
+   */
+
+  fd = fileno(fp);
+
+  if (sys_lseek(fd, pwd_seekpos - 1, SEEK_SET) != pwd_seekpos - 1) {
+    DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
+    pw_file_unlock(lockfd,&pw_file_lock_depth);
+    fclose(fp);
+    return False;
+  }
+
+  /* Sanity check - ensure the areas we are writing are framed by ':' */
+  if (read(fd, linebuf, wr_len+1) != wr_len+1) {
+    DEBUG(0, ("mod_smbfilepwd_entry: read fail on file %s.\n", pfile));
+    pw_file_unlock(lockfd,&pw_file_lock_depth);
+    fclose(fp);
+    return False;
+  }
+
+  if ((linebuf[0] != ':') || (linebuf[wr_len] != ':')) {
+    DEBUG(0, ("mod_smbfilepwd_entry: check on passwd file %s failed.\n", pfile));
+    pw_file_unlock(lockfd,&pw_file_lock_depth);
+    fclose(fp);
+    return False;
+  }
+  if (sys_lseek(fd, pwd_seekpos, SEEK_SET) != pwd_seekpos) {
+    DEBUG(0, ("mod_smbfilepwd_entry: seek fail on file %s.\n", pfile));
+    pw_file_unlock(lockfd,&pw_file_lock_depth);
+    fclose(fp);
+    return False;
+  }
+
   if (write(fd, ascii_p16, wr_len) != wr_len) {
     DEBUG(0, ("mod_smbfilepwd_entry: write failed in passwd file %s\n", pfile));
     pw_file_unlock(lockfd,&pw_file_lock_depth);
index 42af518397c0584d7e07cb1c79bd0fe4eb30254c..5a41022a270c08f8124043725f221bcd581766c6 100644 (file)
@@ -48,11 +48,11 @@ BEGIN {
 }
 END {
   fmt = "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:";
-  fmt = fmt   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:%s:%s:%s\n";
+  fmt = fmt   "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[U]:LCT-00000000:%s:\n";
   for(name in names) {
     while ((getline < pwdf) > 0) {
       if ($1 == name) {
-       printf(fmt, $1, $3, $5, $6, $7);
+       printf(fmt, $1, $3, $5);
        close(pwdf);
        notfound = "";
        break;
@@ -65,7 +65,7 @@ END {
       command = ypmatch " " name " passwd";
       command | getline;
       if (NF > 0) {
-       printf(fmt, $1, $3, $5, $6, $7);
+       printf(fmt, $1, $3, $5);
       }
       close(command);
     }
index 6e592acd6525bd0d9e3680d10ad800162106abb0..10acc1257d7106b4c7fcbcd4d4c0d6b605aa6457 100755 (executable)
@@ -2,5 +2,5 @@
 awk 'BEGIN {FS=":"
        printf("#\n# SMB password file.\n#\n")
        }
-{ printf( "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:%s:%s:%s\n", $1, $3, $5, $6, $7) }
+{ printf( "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:[U]:LCT-00000000:%s\n", $1, $3, $5) }
 '
index d73fcda117df3bed2b45879ac0403c1da803a95a..b418e7cd5495adce13f5ad944deff08f18c831bd 100644 (file)
@@ -240,16 +240,47 @@ BOOL set_file_oplock(files_struct *fsp)
 {
 #if defined(HAVE_KERNEL_OPLOCKS)
   if(lp_kernel_oplocks()) {
-    if(fcntl(fsp->fd_ptr->fd, F_OPLKREG, oplock_pipe_write) < 0) {
-      if(errno != EAGAIN) {
+#if 0 /* for now. */
+    extern struct current_user current_user;
+#endif
+    int saved_errno;
+    int fcntl_ret;
+
+#if 0 /* for now. */
+    /*
+     * Go back to being root.
+     */
+
+    unbecome_user();
+#endif
+
+    fcntl_ret = fcntl(fsp->fd_ptr->fd, F_OPLKREG, oplock_pipe_write);
+    saved_errno = errno;
+
+#if 0 /* for now. */
+    /*
+     * Go back to being the correct user.
+     */
+    if(!become_user(fsp->conn, current_user.vuid))
+    {
+      DEBUG( 0, ( "set_file_oplock: unable to re-become user!" ) );
+      DEBUGADD( 0, ( "Shutting down server\n" ) );
+      close_sockets();
+      close(oplock_sock);
+      exit_server("unable to re-become user");
+    }
+#endif
+
+    if(fcntl_ret < 0) {
+      if(saved_errno != EAGAIN) {
         DEBUG(0,("set_file_oplock: Unable to get kernel oplock on file %s, dev = %x, \
 inode = %.0f. Error was %s\n", 
               fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode,
                strerror(errno) ));
       } else {
-        DEBUG(5,("set_file_oplock: Refused oplock on file %s, dev = %x, \
+        DEBUG(5,("set_file_oplock: Refused oplock on file %s, fd = %d, dev = %x, \
 inode = %.0f. Another process had the file open.\n",
-              fsp->fsp_name, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode ));
+              fsp->fsp_name, fsp->fd_ptr->fd, (unsigned int)fsp->fd_ptr->dev, (double)fsp->fd_ptr->inode ));
       }
       return False;
     }
index 03553b0ea964ea3067661056e4a8864271771592..b97b0c765fd0c56e36ce6abf2eb4c4308a5d76d5 100644 (file)
@@ -32,7 +32,7 @@ static char *prog_name;
 static void usage(char *name, BOOL is_root)
 {
        if(is_root)
-               fprintf(stderr, "Usage is : %s [-D DEBUGLEVEL] [-a] [-d] [-m] [-n] [username] [password]\n\
+               fprintf(stderr, "Usage is : %s [-D DEBUGLEVEL] [-a] [-d] [-e] [-m] [-n] [username] [password]\n\
 %s: [-R <name resolve order>] [-D DEBUGLEVEL] [-j DOMAINNAME] [-r machine] [-U remote_username] [username] [password]\n%s: [-h]\n", name, name, name);
        else
                fprintf(stderr, "Usage is : %s [-h] [-D DEBUGLEVEL] [-r machine] [-U remote_username] [password]\n", name);
@@ -112,6 +112,56 @@ unable to join domain.\n", prog_name);
   return (int)ret;
 }
 
+/*************************************************************
+ Utility function to create password hashes.
+*************************************************************/
+
+static void create_new_hashes( char *new_passwd, uchar *new_p16, uchar *new_nt_p16)
+{
+  memset(new_nt_p16, '\0', 16);
+  E_md4hash((uchar *) new_passwd, new_nt_p16);
+  
+  /* Mangle the password into Lanman format */
+  new_passwd[14] = '\0';
+  strupper(new_passwd);
+  
+  /*
+   * Calculate the SMB (lanman) hash functions of the new password.
+   */
+  
+  memset(new_p16, '\0', 16);
+  E_P16((uchar *) new_passwd, new_p16);
+}
+
+/*************************************************************
+ Utility function to prompt for new password.
+*************************************************************/
+
+static void prompt_for_new_password(char *new_passwd)
+{
+  char *p;
+
+  new_passwd[0] = '\0';
+  
+  p = getpass("New SMB password:");
+
+  strncpy(new_passwd, p, sizeof(fstring));
+  new_passwd[sizeof(fstring)-1] = '\0';
+
+  p = getpass("Retype new SMB password:");
+
+  if (strncmp(p, new_passwd, sizeof(fstring)-1))
+  {
+    fprintf(stderr, "%s: Mismatch - password unchanged.\n", prog_name);
+    exit(1);
+  }
+
+  if (new_passwd[0] == '\0') {
+    printf("Password not set\n");
+    exit(0);
+  }
+}
+
 /*********************************************************
  Start here.
 **********************************************************/
@@ -140,6 +190,7 @@ int main(int argc, char **argv)
   BOOL got_new_pass = False;
   BOOL trust_account = False;
   BOOL disable_user = False;
+  BOOL enable_user = False;
   BOOL set_no_password = False;
   BOOL joining_domain = False;
   char *new_domain = NULL;
@@ -202,7 +253,7 @@ int main(int argc, char **argv)
 
   is_root = (real_uid == 0);
 
-  while ((ch = getopt(argc, argv, "adhmnj:r:R:D:U:")) != EOF) {
+  while ((ch = getopt(argc, argv, "adehmnj:r:R:D:U:")) != EOF) {
     switch(ch) {
     case 'a':
       if(is_root)
@@ -218,6 +269,14 @@ int main(int argc, char **argv)
       } else
         usage(prog_name, is_root);
       break;
+    case 'e':
+      if(is_root) {
+        enable_user = True;
+        got_new_pass = True;
+        fstrcpy(new_passwd, "XXXXXX");
+      } else
+        usage(prog_name, is_root);
+      break;
     case 'D':
       DEBUGLEVEL = atoi(optarg);
       break;
@@ -337,7 +396,17 @@ int main(int argc, char **argv)
   } else {
 
     if(add_user) {
-      fprintf(stderr, "%s: Only root can set anothers password.\n", prog_name);
+      fprintf(stderr, "%s: Only root can add a user.\n", prog_name);
+      usage(prog_name, False);
+    }
+
+    if(disable_user) {
+      fprintf(stderr, "%s: Only root can disable a user.\n", prog_name);
+      usage(prog_name, False);
+    }
+
+    if(enable_user) {
+      fprintf(stderr, "%s: Only root can enable a user.\n", prog_name);
       usage(prog_name, False);
     }
 
@@ -392,22 +461,8 @@ int main(int argc, char **argv)
     fstrcpy(old_passwd, p);
   }
 
-  if (!got_new_pass) {
-    new_passwd[0] = '\0';
-
-    p = getpass("New SMB password:");
-
-    strncpy(new_passwd, p, sizeof(fstring));
-    new_passwd[sizeof(fstring)-1] = '\0';
-
-    p = getpass("Retype new SMB password:");
-
-    if (strncmp(p, new_passwd, sizeof(fstring)-1))
-    {
-      fprintf(stderr, "%s: Mismatch - password unchanged.\n", prog_name);
-      exit(1);
-    }
-  }
+  if (!got_new_pass)
+    prompt_for_new_password(new_passwd);
   
   if (new_passwd[0] == '\0') {
     printf("Password not set\n");
@@ -493,25 +548,14 @@ int main(int argc, char **argv)
 
   /* Calculate the MD4 hash (NT compatible) of the new password. */
   
-  memset(new_nt_p16, '\0', 16);
-  E_md4hash((uchar *) new_passwd, new_nt_p16);
-  
-  /* Mangle the password into Lanman format */
-  new_passwd[14] = '\0';
-  strupper(new_passwd);
-  
-  /*
-   * Calculate the SMB (lanman) hash functions of the new password.
-   */
-  
-  memset(new_p16, '\0', 16);
-  E_P16((uchar *) new_passwd, new_p16);
-  
+  create_new_hashes( new_passwd, new_p16, new_nt_p16);
+
   /*
    * Open the smbpaswd file.
    */
   vp = startsmbpwent(True);
   if (!vp && errno == ENOENT) {
+      fprintf(stderr,"%s: smbpasswd file did not exist - attempting to create it.\n", prog_name);
          fp = fopen(lp_smb_passwd_file(), "w");
          if (fp) {
                  fprintf(fp, "# Samba SMB password file\n");
@@ -578,23 +622,19 @@ int main(int argc, char **argv)
    * and the valid last change time.
    */
 
-  if(disable_user) {
-    /*
-     * This currently won't work as it means changing
-     * the length of the record. JRA.
-     */
+  if(disable_user)
     smb_pwent->acct_ctrl |= ACB_DISABLED;
-    smb_pwent->smb_passwd = NULL;
-    smb_pwent->smb_nt_passwd = NULL;
-  } else if (set_no_password) {
-    /*
-     * This currently won't work as it means changing
-     * the length of the record. JRA.
-     */
+  else if (enable_user) {
+    if(smb_pwent->smb_passwd == NULL) {
+      prompt_for_new_password(new_passwd);
+      create_new_hashes( new_passwd, new_p16, new_nt_p16);
+      smb_pwent->smb_passwd = new_p16;
+      smb_pwent->smb_nt_passwd = new_nt_p16;
+    }
+    smb_pwent->acct_ctrl &= ~ACB_DISABLED;
+  } else if (set_no_password)
     smb_pwent->acct_ctrl |= ACB_PWNOTREQ;
-    smb_pwent->smb_passwd = NULL;
-    smb_pwent->smb_nt_passwd = NULL; 
-  } else {
+  else {
     smb_pwent->smb_passwd = new_p16;
     smb_pwent->smb_nt_passwd = new_nt_p16;
   }
@@ -609,6 +649,8 @@ int main(int argc, char **argv)
   endsmbpwent(vp);
   if(disable_user)
     printf("User %s disabled.\n", user_name);
+  else if(enable_user)
+    printf("User %s enabled.\n", user_name);
   else if (set_no_password)
     printf("User %s - set to no password.\n", user_name);
   else