Adding the same changes to HEAD as were added to BRANCH_1_9_18.
authorJeremy Allison <jra@samba.org>
Thu, 19 Mar 1998 20:06:47 +0000 (20:06 +0000)
committerJeremy Allison <jra@samba.org>
Thu, 19 Mar 1998 20:06:47 +0000 (20:06 +0000)
Changed smbpasswd to be client-server for a normal user, rather
than accessing the private/smbpasswd file directly (it still accesses
this file directly when run as root, so root can add users/change a
users password without knowing the old password).

A shakeout of this change is that smbpasswd can now be used to
change a users password on a remote NT machine (yep - you heard
that one right - we can now change a NT password from UNIX !!!!!).

Jeremy.
(This used to be commit 20770b6f1c25288e90d3e0d215afa7f0809ce124)

docs/manpages/smbpasswd.8
docs/textdocs/ENCRYPTION.txt
source3/client/client.c
source3/client/smbmount.c
source3/include/proto.h
source3/libsmb/clientgen.c
source3/libsmb/smbdes.c
source3/passdb/smbpass.c
source3/smbd/chgpasswd.c
source3/utils/smbpasswd.c

index 1cd2c54c43432fec67ac06e793f31704d0d65e05..24b37135984859f950dbe4d5e5e3f48c670fa3c4 100644 (file)
@@ -1,10 +1,13 @@
-.TH SMBPASSWD 8 "08 Jan 1998" "smbpasswd 1.9.18"
+.TH SMBPASSWD 8 "19 Feb 1998" "smbpasswd 1.9.18p3"
 .SH NAME
 smbpasswd \- change a users smb password in the smbpasswd file.
 .SH SYNOPSIS
 .B smbpasswd
 [
-.B \-add
+.B \-a
+] [
+.B \-r
+remote_machine
 ] [
 .B username
 ]
@@ -27,14 +30,38 @@ smb password (specified by the string "NO PASSWORD" in the
 smbpasswd file) then just press the <Enter> key when asked
 for your old password.
 
+.B New for 1.9.18p4.
+smbpasswd will now allow a user to change their password
+on a Windows NT server. To use this add the 
+.I \-r
+.I \<remote_machine\>
+paramter to the smbpasswd command. The machine name is looked
+up using the "name resolve order" parameter defined in the
+smb.conf [global] section. Note that when changing a Windows
+NT password for a domain user,
+.I \<remote machine\>
+must be the name of the Primary domain controller.
+
+To allow users to change their passwords from "NO PASSWORD"
+in the smbpasswd file to a valid password the administrator
+must set the following parameter in the [global] section of
+the smb.conf :
+
+null passwords = true
+
+This is 
+.B NOT
+recommended as a general policy, it is recommended that
+new users be assigned a default password instead.
+
 The 
-.I \-add
+.I \-a
 and 
 .I username
 options can only be used by a user running as root.
 
 .SH OPTIONS
-.I \-add
+.I \-a
 
 .RS 3
 Specifies that the username following should be added to
@@ -70,13 +97,12 @@ It is recommended that the
 program be installed in the /usr/local/samba/bin directory. This should be
 a directory readable by all, writeable only by root. The program should be
 executable by all. The program 
-.B must
-be setuid root. This means the permissions should
-look like -r-sr-xr-x and the program must be owned by root.
+.B must not 
+be setuid root.
 
 .SH VERSION
 
-This man page is correct for version 1.9.17 of the Samba suite.
+This man page is correct for version 1.9.18p4 of the Samba suite.
 These notes will necessarily lag behind 
 development of the software, so it is possible that your version of 
 the program has extensions or parameter semantics that differ from or are not 
@@ -93,7 +119,7 @@ The
 .B smbpasswd
 command is only useful if
 .I Samba
-has been compiled with encrypted passwords. See the file
+has been set up to use encrypted passwords. See the file
 .I ENCRYPTION.txt
 in the docs directory for details on how to do this.
 
index 315e7de53f7fe0f898a5baebc4491fe4a1d6e904..352f3457b47450947148e04f604416d5bc6c8cdf 100644 (file)
@@ -1,8 +1,8 @@
 !==
-!== ENCRYPTION.txt for Samba release 1.9.18 08 Jan 1998
+!== ENCRYPTION.txt for Samba release 1.9.18p3 19 Feb 1998
 !==
 Contributor:   Jeremy Allison <samba-bugs@samba.anu.edu.au>
-Updated:       June 27, 1997
+Updated:       March 19, 1998
 Note:          Please refer to WinNT.txt also
 
 Subject:       LanManager / Samba Password Encryption.
@@ -207,7 +207,16 @@ bob:100:NO PASSWORDXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:Bob's
 If you are allowing users to use the smbpasswd command to set their own
 passwords, you may want to give users NO PASSWORD initially so they do
 not have to enter a previous password when changing to their new
-password (not recommended).
+password (not recommended). In order for you to allow this the
+smbpasswd program must be able to connect to the smbd daemon as
+that user with no password. Enable this by adding the line :
+
+null passwords = true
+
+to the [global] section of the smb.conf file (this is why the
+above scenario is not recommended). Preferebly, allocate your
+users a default password to begin with, so you do not have
+to enable this on your server.
 
 Note : This file should be protected very carefully. Anyone with
 access to this file can (with enough knowledge of the protocols) gain
@@ -220,22 +229,28 @@ The smbpasswd Command.
 The smbpasswd command maintains the two 32 byte password fields in
 the smbpasswd file. If you wish to make it similar to the unix passwd
 or yppasswd programs, install it in /usr/local/samba/bin (or your main
-Samba binary directory) and make it setuid root.
+Samba binary directory).
 
-Note that if you do not do this then the root user will have to set all
-users passwords.
+Note that as of Samba 1.9.18p4 this program MUST NOT BE INSTALLED
+setuid root (the new smbpasswd code enforces this restriction so
+it cannot be run this way by accident).
 
-To set up smbpasswd as setuid root, change to the Samba binary install
-directory and then type (as root) :
+smbpasswd now works in a client-server mode where it contacts
+the local smbd to change the users password on its behalf. This
+has enormous benefits - as follows.
 
-chown root smbpasswd
-chmod 4555 smbpasswd
+1). smbpasswd no longer has to be setuid root - an enourmous
+range of potential security problems is eliminated.
 
-If smbpasswd is installed as setuid root then you would use it as
-follows.
+2). smbpasswd now has the capability to change passwords
+on Windows NT servers (this only works when the request is
+sent to the NT Primary Domain Controller if you are changing 
+an NT Domain users password).
+
+To run smbpasswd as a normal user just type :
 
 smbpasswd
-Old SMB password: <type old alue here - just hit return if there is NO PASSWORD>
+Old SMB password: <type old value here - or hit return if there was no old password >
 New SMB Password: < type new value >
 Repeat New SMB Password: < re-type new value >
 
@@ -255,15 +270,8 @@ forgotten their passwords.
 smbpasswd is designed to work in the same way and be familiar to UNIX
 users who use the passwd or yppasswd commands.
 
-NOTE. As smbpasswd is designed to be installed as setuid root I would
-appreciate it if everyone examined the source code to look for
-potential security flaws. A setuid program, if not written properly can
-be an open door to a system cracker. Please help make this program
-secure by reporting all problems to me (the author, Jeremy Allison).
-
-My email address is :-
-
-jallison@whistle.com
+For more details on using smbpasswd refer to the man page which
+will always be the definitive reference.
 
 Setting up Samba to support LanManager Encryption.
 --------------------------------------------------
@@ -310,11 +318,6 @@ tridge:148:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:And
 note that the uid and username fields must be right. Also, you must get
 the number of X's right (there should be 32).
 
-If you wish, install the smbpasswd program as suid root.
-
-chown root /usr/local/samba/bin/smbpasswd
-chmod 4555 /usr/local/samba/bin/smbpasswd
-
 5) set the passwords for users using the smbpasswd command. For
 example, as root you could do "smbpasswd tridge"
 
index 679114fa2bb44f149d38099698de32b0e2543db9..3ce635fbeb2fab8a68d4d19cff1e1ce62f12134a 100644 (file)
@@ -3845,31 +3845,6 @@ static void usage(char *pname)
       return(ret);
     }
 
-#ifdef NTDOMAIN
-
-       if (nt_domain_logon)
-       {
-               int ret = 0;
-               sprintf(service,"\\\\%s\\IPC$",query_host);
-               strupper(service);
-               connect_as_ipc = True;
-
-               DEBUG(5,("NT Domain Logon.  Service: %s\n", service));
-
-               if (cli_open_sockets(port))
-               {
-                       if (!cli_send_login(NULL,NULL,True,True,NULL)) return(1);
-
-                       do_nt_login(desthost, myhostname, Client, cnum);
-
-                       cli_send_logout();
-                       close_sockets();
-               }
-
-               return(ret);
-       }
-#endif 
-
   if (cli_open_sockets(port))
     {
       if (!process(base_directory))
index fdafaec9faff60af6776e845ad9912e84884ff5f..bab7430859212658aca4dc8b1347acb55cfec98a 100644 (file)
@@ -883,31 +883,6 @@ static void usage(char *pname)
   get_myname((*myname)?NULL:myname,NULL);  
   strupper(myname);
 
-#ifdef NTDOMAIN
-
-       if (nt_domain_logon)
-       {
-               int ret = 0;
-               sprintf(service,"\\\\%s\\IPC$",query_host);
-               strupper(service);
-               connect_as_ipc = True;
-
-               DEBUG(5,("NT Domain Logon.  Service: %s\n", service));
-
-               if (cli_open_sockets(port))
-               {
-                       if (!cli_send_login(NULL,NULL,True,True,NULL)) return(1);
-
-                       do_nt_login(desthost, myhostname, Client, cnum);
-
-                       cli_send_logout();
-                       close_sockets();
-               }
-
-               return(ret);
-       }
-#endif 
-
   if (cli_open_sockets(port))
     {
       if (!process(base_directory))
index e980bcacc9a517e3d6c2e892b93b2253b8ddcee8..209004e90adee05b5d6e3b6a8d36929122975d8b 100644 (file)
@@ -82,6 +82,8 @@ BOOL cli_qpathinfo2(struct cli_state *cli, char *fname,
                    time_t *w_time, uint32 *size);
 BOOL cli_qfileinfo(struct cli_state *cli, int fnum, 
                   time_t *c_time, time_t *a_time, time_t *m_time, uint32 *size);
+BOOL cli_oem_change_password(struct cli_state *cli, char *user, char *new_password,
+                             char *old_password);
 BOOL cli_negprot(struct cli_state *cli);
 BOOL cli_session_request(struct cli_state *cli, char *host, int name_type,
                         char *myname);
@@ -1666,9 +1668,10 @@ struct shmem_ops *sysv_shm_open(int ronly);
 void E_P16(unsigned char *p14,unsigned char *p16);
 void E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24);
 void D_P16(unsigned char *p14, unsigned char *in, unsigned char *out);
+void E_old_pw_hash( unsigned char *p14, unsigned char *in, unsigned char *out);
 void cred_hash1(unsigned char *out,unsigned char *in,unsigned char *key);
 void cred_hash2(unsigned char *out,unsigned char *in,unsigned char *key);
-void SamOEMhash( unsigned char *data, unsigned char *key);
+void SamOEMhash( unsigned char *data, unsigned char *key, int val);
 
 /*The following definitions come from  smbencrypt.c  */
 
index 4585c8a544148944a767705b37f73b8ade29afc3..dcebf704554c5e4f03bb9d01f443e11d49306581 100644 (file)
@@ -426,7 +426,7 @@ BOOL cli_session_setup(struct cli_state *cli,
                return False;
        }
 
-       if ((cli->sec_mode & 2) && *pass && passlen != 24) {
+       if ((cli->sec_mode & 2) && passlen != 24) {
                passlen = 24;
                SMBencrypt((uchar *)pass,(uchar *)cli->cryptkey,(uchar *)pword);
        } else {
@@ -1207,6 +1207,104 @@ BOOL cli_qfileinfo(struct cli_state *cli, int fnum,
        return True;
 }
 
+/****************************************************************************
+Send a SamOEMChangePassword command
+****************************************************************************/
+
+BOOL cli_oem_change_password(struct cli_state *cli, char *user, char *new_password,
+                             char *old_password)
+{
+  char param[16+sizeof(fstring)];
+  char data[532];
+  char *p = param;
+  fstring upper_case_old_pw;
+  fstring upper_case_new_pw;
+  unsigned char old_pw_hash[16];
+  unsigned char new_pw_hash[16];
+  int data_len;
+  int param_len = 0;
+  int new_pw_len = strlen(new_password);
+  char *rparam = NULL;
+  char *rdata = NULL;
+  int rprcnt, rdrcnt;
+
+  cli->error = -1;
+
+  if(strlen(user) >= sizeof(fstring)-1) {
+    DEBUG(0,("cli_oem_change_password: user name %s is too long.\n", user));
+    return False;
+  }
+
+  if(new_pw_len > 512) {
+    DEBUG(0,("cli_oem_change_password: new password for user %s is too long.\n", user));
+    return False;
+  }
+
+  SSVAL(p,0,214); /* SamOEMChangePassword command. */
+  p += 2;
+  strcpy(p, "zsT");
+  p = skip_string(p,1);
+  strcpy(p, "B516B16");
+  p = skip_string(p,1);
+  fstrcpy(p,user);
+  p = skip_string(p,1);
+  SSVAL(p,0,532);
+  p += 2;
+
+  param_len = PTR_DIFF(p,param);
+
+  /*
+   * Now setup the data area.
+   */
+  memset(data, '\0', sizeof(data));
+  fstrcpy( &data[512 - new_pw_len], new_password);
+  SIVAL(data, 512, new_pw_len);
+
+  /*
+   * Get the Lanman hash of the old password, we
+   * use this as the key to SamOEMHash().
+   */
+  memset(upper_case_old_pw, '\0', sizeof(upper_case_old_pw));
+  fstrcpy(upper_case_old_pw, old_password);
+  strupper(upper_case_old_pw);
+  E_P16((uchar *)upper_case_old_pw, old_pw_hash);
+
+  SamOEMhash( (unsigned char *)data, (unsigned char *)old_pw_hash, True);
+
+  /* 
+   * Now place the old password hash in the data.
+   */
+  memset(upper_case_new_pw, '\0', sizeof(upper_case_new_pw));
+  fstrcpy(upper_case_new_pw, new_password);
+  strupper(upper_case_new_pw);
+
+  E_P16((uchar *)upper_case_new_pw, new_pw_hash);
+
+  E_old_pw_hash( new_pw_hash, old_pw_hash, &data[516]);
+
+  data_len = 532;
+    
+  if(cli_send_trans(cli,SMBtrans,PIPE_LANMAN,0,0,
+                 data,param,NULL,
+                 data_len , param_len,0,
+                 0,2,0) == False) {
+    DEBUG(0,("cli_oem_change_password: Failed to send password change for user %s\n",
+              user ));
+    return False;
+  }
+
+  if(cli_receive_trans(cli,SMBtrans, &rdrcnt, &rprcnt, &rdata, &rparam)) {
+    if(rparam)
+      cli->error = SVAL(rparam,0);
+  }
+
+  if (rparam)
+    free(rparam);
+  if (rdata)
+    free(rdata);
+
+  return (cli->error == 0);
+}
 
 /****************************************************************************
 send a negprot command
index 8f95a5a297b51593c43317b8cd1c5a03447bbc8b..e5d8f4a1e0b7bd12ed8373752934f449328dabb9 100644 (file)
@@ -323,6 +323,12 @@ void D_P16(unsigned char *p14, unsigned char *in, unsigned char *out)
         smbhash(out+8, in+8, p14+7, 0);
 }
 
+void E_old_pw_hash( unsigned char *p14, unsigned char *in, unsigned char *out)
+{
+        smbhash(out, in, p14, 1);
+        smbhash(out+8, in+8, p14+7, 1);
+}
+
 void cred_hash1(unsigned char *out,unsigned char *in,unsigned char *key)
 {
        unsigned char buf[8];
@@ -341,7 +347,7 @@ void cred_hash2(unsigned char *out,unsigned char *in,unsigned char *key)
        smbhash(out, buf, key2, 1);
 }
 
-void SamOEMhash( unsigned char *data, unsigned char *key)
+void SamOEMhash( unsigned char *data, unsigned char *key, int val)
 {
   unsigned char s_box[256];
   unsigned char index_i = 0;
@@ -365,7 +371,7 @@ void SamOEMhash( unsigned char *data, unsigned char *key)
      s_box[j] = tc;
   }
 
-  for( ind = 0; ind < 516; ind++)
+  for( ind = 0; ind < (val ? 516 : 16); ind++)
   {
     unsigned char tc;
     unsigned char t;
index b51f675306536f3383b68d37a5aa1b347fe047f5..a1913c4959ee5e91afe55e06525adfe6d8fe78f2 100644 (file)
@@ -768,12 +768,6 @@ BOOL mod_smbpwd_entry(struct smb_passwd* pwd)
     return False;
   }
 
-  if (!strncasecmp((char *) p, "NO PASSWORD", 11)) {
-    fclose(fp);
-    pw_file_unlock(lockfd);
-    return False;
-  }
-
   /* Now check if the NT compatible password is
      available. */
   p += 33; /* Move to the first character of the line after
index 57d81ad756dd5ca3743a6e2900c6cb4127ed15ee..779845d37ae17526430a3ac6aa2a4870588bcdf9 100644 (file)
@@ -408,6 +408,7 @@ BOOL check_lanman_password(char *user, unsigned char *pass1,
 {
   unsigned char unenc_new_pw[16];
   unsigned char unenc_old_pw[16];
+  unsigned char null_pw[16];
   struct smb_passwd *smbpw;
 
   *psmbpw = NULL;
@@ -428,8 +429,13 @@ BOOL check_lanman_password(char *user, unsigned char *pass1,
     return False;
   }
 
-  if(smbpw->smb_passwd == NULL)
+  if((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
   {
+    unsigned char no_pw[14];
+    memset(no_pw, '\0', 14);
+    E_P16((uchar *)no_pw, (uchar *)null_pw);
+    smbpw->smb_passwd = null_pw;
+  } else if (smbpw->smb_passwd == NULL) {
     DEBUG(0,("check_lanman_password: no lanman password !\n"));
     return False;
   }
@@ -460,6 +466,7 @@ BOOL check_lanman_password(char *user, unsigned char *pass1,
 BOOL change_lanman_password(struct smb_passwd *smbpw, unsigned char *pass1, unsigned char *pass2)
 {
   unsigned char unenc_new_pw[16];
+  unsigned char null_pw[16];
   BOOL ret;
 
   if(smbpw == NULL)
@@ -474,8 +481,13 @@ BOOL change_lanman_password(struct smb_passwd *smbpw, unsigned char *pass1, unsi
     return False;
   }
 
-  if(smbpw->smb_passwd == NULL)
+  if((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
   {
+    unsigned char no_pw[14];
+    memset(no_pw, '\0', 14);
+    E_P16((uchar *)no_pw, (uchar *)null_pw);
+    smbpw->smb_passwd = null_pw;
+  } else if (smbpw->smb_passwd == NULL) {
     DEBUG(0,("change_lanman_password: no lanman password !\n"));
     return False;
   }
@@ -507,6 +519,7 @@ BOOL check_oem_password(char *user, unsigned char *data,
   fstring upper_case_new_passwd;
   unsigned char new_p16[16];
   unsigned char unenc_old_pw[16];
+  unsigned char null_pw[16];
 
   become_root(0);
   *psmbpw = smbpw = get_smbpwd_entry(user, 0);
@@ -524,8 +537,13 @@ BOOL check_oem_password(char *user, unsigned char *data,
     return False;
   }
 
-  if(smbpw->smb_passwd == NULL)
+  if((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
   {
+    unsigned char no_pw[14];
+    memset(no_pw, '\0', 14);
+    E_P16((uchar *)no_pw, (uchar *)null_pw);
+    smbpw->smb_passwd = null_pw;
+  } else if (smbpw->smb_passwd == NULL) {
     DEBUG(0,("check_oem_password: no lanman password !\n"));
     return False;
   }
@@ -533,7 +551,7 @@ BOOL check_oem_password(char *user, unsigned char *data,
   /* 
    * Call the hash function to get the new password.
    */
-  SamOEMhash( (unsigned char *)data, (unsigned char *)smbpw->smb_passwd);
+  SamOEMhash( (unsigned char *)data, (unsigned char *)smbpw->smb_passwd, True);
 
   /* 
    * The length of the new password is in the last 4 bytes of
@@ -541,7 +559,7 @@ BOOL check_oem_password(char *user, unsigned char *data,
    */
   new_pw_len = IVAL(data,512);
   if(new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
-    DEBUG(0,("check_oem_password: incorrect password length.\n"));
+    DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
     return False;
   }
 
@@ -587,6 +605,7 @@ BOOL change_oem_password(struct smb_passwd *smbpw, char *new_passwd)
   unsigned char new_nt_p16[16];
   unsigned char new_p16[16];
 
+  memset(upper_case_new_passwd, '\0', sizeof(upper_case_new_passwd));
   fstrcpy(upper_case_new_passwd, new_passwd);
   strupper(upper_case_new_passwd);
 
index 7ab8d8a0dbfa7be7ac98541513a9c10e1cceda73..5b5f86c5e1c510f72819175f86d3e48c721d153d 100644 (file)
 
 #include "includes.h"
 
-/* Static buffers we will return. */
-static struct smb_passwd pw_buf;
-static pstring  user_name;
-static unsigned char smbpwd[16];
-static unsigned char smbntpwd[16];
+/* 
+ * Password changing error codes.
+ */
+
+struct
+{
+  int err;
+  char *message;
+} pw_change_errmap[] =
+{
+  {5,    "User has insufficient privilege" },
+  {86,   "The specified password is invalid" },
+  {2226, "Operation only permitted on a Primary Domain Controller"  },
+  {2243, "The password cannot be changed" },
+  {2246, "The password is too short" },
+  {0, NULL}
+};
 
 static int gethexpwd(char *p, char *pwd)
 {
@@ -52,6 +64,12 @@ static struct smb_passwd *
 _my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd, 
                BOOL *got_valid_nt_entry, long *pwd_seekpos)
 {
+       /* Static buffers we will return. */
+       static struct smb_passwd pw_buf;
+       static pstring  user_name;
+       static unsigned char smbpwd[16];
+       static unsigned char smbntpwd[16];
+
        char            linebuf[256];
        unsigned char   c;
        unsigned char  *p;
@@ -198,14 +216,21 @@ _my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd,
 /*
  * Print command usage on stderr and die.
  */
-static void usage(char *name)
+static void usage(char *name, BOOL is_root)
 {
-       fprintf(stderr, "Usage is : %s [-add] [username] [password]\n", name);
+       if(is_root)
+               fprintf(stderr, "Usage is : %s [-a] [username] [password]\n\
+%s: [-r machine] [username] [password]\n%s: [-h]", name, name, name);
+       else
+               fprintf(stderr, "Usage is : %s [-h] [-r machine] [password]\n", name);
        exit(1);
 }
 
- int main(int argc, char **argv)
+int main(int argc, char **argv)
 {
+  extern char *optarg;
+  extern int optind;
+  char *prog_name;
   int             real_uid;
   struct passwd  *pwd;
   fstring         old_passwd;
@@ -218,9 +243,7 @@ static void usage(char *name)
   struct smb_passwd *smb_pwent;
   FILE           *fp;
   BOOL            valid_old_pwd = False;
-  BOOL                         got_valid_nt_entry = False;
-  BOOL            add_user = False;
-  int             add_pass = 0;
+  BOOL          got_valid_nt_entry = False;
   long            seekpos;
   int             pwfd;
   char            ascii_p16[66];
@@ -229,103 +252,141 @@ static void usage(char *name)
   int             lockfd = -1;
   char           *pfile = SMB_PASSWD_FILE;
   char            readbuf[16 * 1024];
-  
+  BOOL is_root = False;
+  pstring  user_name;
+  char *remote_machine = NULL;
+  BOOL          add_user = False;
+  BOOL          got_new_pass = False;
+  pstring servicesf = CONFIGFILE;
+
+  new_passwd[0] = '\0';
+  user_name[0] = '\0';
+
+  memset(old_passwd, '\0', sizeof(old_passwd));
+  memset(new_passwd, '\0', sizeof(old_passwd));
+
+  prog_name = argv[0];
+
   TimeInit();
 
-  setup_logging(argv[0],True);
+  setup_logging(prog_name,True);
   
   charset_initialise();
-  
-#ifndef DEBUG_PASSWORD
-  /* Check the effective uid */
-  if (geteuid() != 0) {
-    fprintf(stderr, "%s: Must be setuid root.\n", argv[0]);
-    exit(1);
+
+  if (!lp_load(servicesf,True,False,False)) {
+    fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n", prog_name, servicesf);
   }
-#endif
-  
+    
+  codepage_initialise(lp_client_code_page());
+
   /* Get the real uid */
   real_uid = getuid();
   
-  /* Deal with usage problems */
-  if (real_uid == 0)
-  {
-    /* As root we can change anothers password and add a user. */
-    if (argc > 4 )
-      usage(argv[0]);
-  }
-  else if (argc == 2 || argc > 3)
-  {
-    fprintf(stderr, "%s: Only root can set anothers password.\n", argv[0]);
-    usage(argv[0]);
+  /* Check the effective uid */
+  if ((geteuid() == 0) && (real_uid != 0)) {
+    fprintf(stderr, "%s: Must *NOT* be setuid root.\n", prog_name);
+    exit(1);
   }
-  
-  if (real_uid == 0 && (argc > 1))
-  {
-    /* We are root - check if we should add the user */
-    if ((argv[1][0] == '-') && (argv[1][1] == 'a'))
+
+  is_root = (real_uid == 0);
+
+  while ((c = getopt(argc, argv, "ahr:")) != EOF) {
+    switch(c) {
+    case 'a':
       add_user = True;
+      break;
+    case 'r':
+      remote_machine = optarg;
+      break;
+    case 'h':
+    default:
+      usage(prog_name, is_root);
+    }
+  }
 
-    if(add_user && (argc <= 2 || argc > 4))
-      usage(argv[0]);
+  argc -= optind;
+  argv += optind;
 
-    /* root can specify password on command-line */
-    if (argc == (add_user ? 4 : 3))
-    {
-      /* -a argument (add_user): new password is 3rd argument. */
-      /* no -a argument (add_user): new password is 2nd argument */
+  /*
+   * Ensure add_user and remote machine are
+   * not both set.
+   */
+  if(add_user && (remote_machine != NULL))
+    usage(prog_name, True);
+
+  if( is_root ) {
+
+    /*
+     * Deal with root - can add a user, but only locally.
+     */
+
+    switch(argc) {
+      case 0:
+        break;
+      case 1:
+        /* If we are root we can change another's password. */
+        pstrcpy(user_name, argv[0]);
+        break;
+      case 2:
+        pstrcpy(user_name, argv[0]);
+        fstrcpy(new_passwd, argv[1]);
+        got_new_pass = True;
+        break;
+      default:
+        usage(prog_name, True);
+    }
 
-      add_pass = add_user ? 3 : 2;
+    if(*user_name)
+      pwd = getpwnam(user_name);
+    else {
+      if((pwd = getpwuid(real_uid)) != NULL)
+        pstrcpy( user_name, pwd->pw_name);
     }
 
-    /* If we are root we can change another's password. */
-    strncpy(user_name, add_user ? argv[2] : argv[1], sizeof(user_name) - 1);
-    user_name[sizeof(user_name) - 1] = '\0';
+  } else {
 
-    pwd = getpwnam(user_name);
-  }
-  else
-  {
-    /* non-root can specify old pass / new pass on command-line */
-    if (argc == 3)
-    {
-       /* non-root specifies new password as 2nd argument */
-       add_pass = 2;
+    if(add_user) {
+      fprintf(stderr, "%s: Only root can set anothers password.\n", prog_name);
+      usage(prog_name, False);
     }
 
-    pwd = getpwuid(real_uid);
-  }
-  
-  if (pwd == 0) {
-    fprintf(stderr, "%s: Unable to get UNIX password entry for user.\n", argv[0]);
-    exit(1);
-  }
+    if(argc > 1)
+      usage(prog_name, False);
 
-  /* If we are root we don't ask for the old password. */
-  old_passwd[0] = '\0';
-  if (real_uid != 0)
-  {
-    if (add_pass)
-    {
-      /* old password, as non-root, is 1st argument */
-      strncpy(old_passwd, argv[1], sizeof(fstring));
+    if(argc == 1) {
+      fstrcpy(new_passwd, argv[0]);
+      got_new_pass = True;
     }
-    else
-    {
-      p = getpass("Old SMB password:");
-      strncpy(old_passwd, p, sizeof(fstring));
-    }
-    old_passwd[sizeof(fstring)-1] = '\0';
+
+    if((pwd = getpwuid(real_uid)) != NULL)
+      pstrcpy( user_name, pwd->pw_name);
+
+    /*
+     * A non-root user is always setting a password
+     * via a remote machine (even if that machine is
+     * localhost).
+     */
+
+    if(remote_machine == NULL)
+      remote_machine = "127.0.0.1";
+  }    
+    
+  if (*user_name == '\0') {
+    fprintf(stderr, "%s: Unable to get a user name for password change.\n", prog_name);
+    exit(1);
   }
 
-  if (add_pass)
-  {
-    /* new password is specified on the command line */
-    strncpy(new_passwd, argv[add_user ? 3 : 2], sizeof(new_passwd) - 1);
-    new_passwd[sizeof(new_passwd) - 1] = '\0';
+  /* 
+   * If we are root we don't ask for the old password (unless it's on a
+   * remote machine.
+   */
+
+  if (remote_machine != NULL) {
+    p = getpass("Old SMB password:");
+    fstrcpy(old_passwd, p);
   }
-  else
-  {
+
+  if (!got_new_pass) {
     new_passwd[0] = '\0';
 
     p = getpass("New SMB password:");
@@ -337,17 +398,96 @@ static void usage(char *name)
 
     if (strncmp(p, new_passwd, sizeof(fstring)-1))
     {
-      fprintf(stderr, "%s: Mismatch - password unchanged.\n", argv[0]);
+      fprintf(stderr, "%s: Mismatch - password unchanged.\n", prog_name);
       exit(1);
     }
   }
   
-  if (new_passwd[0] == '\0')
-  {
+  if (new_passwd[0] == '\0') {
     printf("Password not set\n");
     exit(0);
   }
+  /* 
+   * Now do things differently depending on if we're changing the
+   * password on a remote machine. Remember - a normal user is
+   * always using this code, looping back to the local smbd.
+   */
+
+  if(remote_machine != NULL) {
+    struct cli_state cli;
+    struct in_addr ip;
+    fstring myname;
+
+    if(get_myname(myname,NULL) == False) {
+      fprintf(stderr, "%s: unable to get my hostname.\n", prog_name );
+      exit(1);
+    }
+
+    if(!resolve_name( remote_machine, &ip)) {
+      fprintf(stderr, "%s: unable to find an IP address for machine %s.\n",
+              prog_name, remote_machine );
+      exit(1);
+    }
+  
+    if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
+      fprintf(stderr, "%s: unable to connect to SMB server on machine %s.\n",
+              prog_name, remote_machine );
+      exit(1);
+    }
+  
+    if (!cli_session_request(&cli, remote_machine, 0x20, myname)) {
+      fprintf(stderr, "%s: machine %s rejected the session setup.\n",
+              prog_name, remote_machine );
+      cli_shutdown(&cli);
+      exit(1);
+    }
+  
+    cli.protocol = PROTOCOL_NT1;
+
+    if (!cli_negprot(&cli)) {
+      fprintf(stderr, "%s: machine %s rejected the negotiate protocol.\n",        
+              prog_name, remote_machine );
+      cli_shutdown(&cli);
+      exit(1);
+    }
   
+    if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd),
+                           "", 0, "")) {
+      fprintf(stderr, "%s: machine %s rejected the session setup.\n",        
+              prog_name, remote_machine );
+      cli_shutdown(&cli);
+      exit(1);
+    }               
+
+    if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
+      fprintf(stderr, "%s: machine %s rejected the tconX on the IPC$ share.\n",
+              prog_name, remote_machine );
+      cli_shutdown(&cli);
+      exit(1);
+    }
+
+    if(!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
+      fstring error_message;
+
+      sprintf(error_message, " with code %d", cli.error);
+      
+      for(i = 0; pw_change_errmap[i].message != NULL; i++) {
+        if (pw_change_errmap[i].err == cli.error) {
+          fstrcpy( error_message, pw_change_errmap[i].message);
+          break;
+        }
+      }
+      fprintf(stderr, "%s: machine %s rejected the password change: %s.\n",
+              prog_name, remote_machine, error_message );
+      cli_shutdown(&cli);
+      exit(1);
+    }
+
+    cli_shutdown(&cli);
+    exit(0);
+  }
+
   /* Calculate the MD4 hash (NT compatible) of the old and new passwords */
   memset(old_nt_p16, '\0', 16);
   E_md4hash((uchar *)old_passwd, old_nt_p16);
@@ -387,9 +527,9 @@ static void usage(char *name)
   if (!fp) {
          err = errno;
          fprintf(stderr, "%s: Failed to open password file %s.\n",
-                 argv[0], pfile);
+                 prog_name, pfile);
          errno = err;
-         perror(argv[0]);
+         perror(prog_name);
          exit(err);
   }
   
@@ -403,19 +543,19 @@ static void usage(char *name)
   if ((lockfd = pw_file_lock(fileno(fp), F_WRLCK, 5)) < 0) {
     err = errno;
     fprintf(stderr, "%s: Failed to lock password file %s.\n",
-           argv[0], pfile);
+           prog_name, pfile);
     fclose(fp);
     errno = err;
-    perror(argv[0]);
+    perror(prog_name);
     exit(err);
   }
   /* Get the smb passwd entry for this user */
-  smb_pwent = _my_get_smbpwnam(fp, pwd->pw_name, &valid_old_pwd, 
+  smb_pwent = _my_get_smbpwnam(fp, user_name, &valid_old_pwd, 
                               &got_valid_nt_entry, &seekpos);
   if (smb_pwent == NULL) {
     if(add_user == False) {
       fprintf(stderr, "%s: Failed to find entry for user %s in file %s.\n",
-             argv[0], pwd->pw_name, pfile);
+             prog_name, pwd->pw_name, pfile);
       fclose(fp);
       pw_file_unlock(lockfd);
       exit(1);
@@ -435,7 +575,7 @@ static void usage(char *name)
 
       if((offpos = lseek(fd, 0, SEEK_END)) == -1) {
         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
-Error was %s\n", argv[0], pwd->pw_name, pfile, strerror(errno));
+Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
         fclose(fp);
         pw_file_unlock(lockfd);
         exit(1);
@@ -447,7 +587,7 @@ Error was %s\n", argv[0], pwd->pw_name, pfile, strerror(errno));
                          strlen(pwd->pw_shell) + 1;
       if((new_entry = (char *)malloc( new_entry_length )) == 0) {
         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
-Error was %s\n", argv[0], pwd->pw_name, pfile, strerror(errno));
+Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
         fclose(fp);
         pw_file_unlock(lockfd);
         exit(1);
@@ -467,12 +607,12 @@ Error was %s\n", argv[0], pwd->pw_name, pfile, strerror(errno));
               pwd->pw_dir, pwd->pw_shell);
       if(write(fd, new_entry, strlen(new_entry)) != strlen(new_entry)) {
         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
-Error was %s\n", argv[0], pwd->pw_name, pfile, strerror(errno));
+Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
         /* Remove the entry we just wrote. */
         if(ftruncate(fd, offpos) == -1) {
           fprintf(stderr, "%s: ERROR failed to ftruncate file %s. \
 Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
-                   argv[0], pwd->pw_name, strerror(errno));
+                   prog_name, pwd->pw_name, strerror(errno));
         }
         fclose(fp);
         pw_file_unlock(lockfd);
@@ -488,35 +628,10 @@ Error was %s. Password file may be corrupt ! Please examine by hand !\n",
          add_user = False;
   }
 
-  /* If we are root or the password is 'NO PASSWORD' then
-     we don't need to check the old password. */
-  if (real_uid != 0) {
-    if (valid_old_pwd == False) {
-      fprintf(stderr, "%s: User %s has no old SMB password.\n", argv[0], pwd->pw_name);
-    }
-    /* Check the old Lanman password - NULL means 'NO PASSWORD' */
-    if (smb_pwent->smb_passwd != NULL) {
-      if (memcmp(old_p16, smb_pwent->smb_passwd, 16)) {
-        fprintf(stderr, "%s: Couldn't change password.\n", argv[0]);
-        fclose(fp);
-        pw_file_unlock(lockfd);
-        exit(1);
-      }
-    }
-    /* Check the NT password if it exists */
-    if (smb_pwent->smb_nt_passwd != NULL) {
-      if (memcmp(old_nt_p16, smb_pwent->smb_nt_passwd, 16)) {
-       fprintf(stderr, "%s: Couldn't change password.\n", argv[0]);
-       fclose(fp);
-       pw_file_unlock(lockfd);
-       exit(1);
-      }
-    }
-  }
   /*
-   * If we get here either we were root or the old password checked out
-   * ok.
+   * We are root - just write the new password.
    */
+
   /* Create the 32 byte representation of the new p16 */
   for (i = 0; i < 16; i++) {
     sprintf(&ascii_p16[i * 2], "%02X", (uchar) new_p16[i]);
@@ -537,10 +652,10 @@ Error was %s. Password file may be corrupt ! Please examine by hand !\n",
   if (ret != seekpos - 1) {
     err = errno;
     fprintf(stderr, "%s: seek fail on file %s.\n",
-           argv[0], pfile);
+           prog_name, pfile);
     fclose(fp);
     errno = err;
-    perror(argv[0]);
+    perror(prog_name);
     pw_file_unlock(lockfd);
     exit(1);
   }
@@ -548,16 +663,16 @@ Error was %s. Password file may be corrupt ! Please examine by hand !\n",
   if (read(pwfd, &c, 1) != 1) {
     err = errno;
     fprintf(stderr, "%s: read fail on file %s.\n",
-           argv[0], pfile);
+           prog_name, pfile);
     fclose(fp);
     errno = err;
-    perror(argv[0]);
+    perror(prog_name);
     pw_file_unlock(lockfd);
     exit(1);
   }
   if (c != ':') {
     fprintf(stderr, "%s: sanity check on passwd file %s failed.\n",
-           argv[0], pfile);
+           prog_name, pfile);
     fclose(fp);
     pw_file_unlock(lockfd);
     exit(1);
@@ -566,10 +681,10 @@ Error was %s. Password file may be corrupt ! Please examine by hand !\n",
   if (write(pwfd, ascii_p16, writelen) != writelen) {
     err = errno;
     fprintf(stderr, "%s: write fail in file %s.\n",
-           argv[0], pfile);
+           prog_name, pfile);
     fclose(fp);
     errno = err;
-    perror(argv[0]);
+    perror(prog_name);
     pw_file_unlock(lockfd);
     exit(err);
   }