From 20770b6f1c25288e90d3e0d215afa7f0809ce124 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 19 Mar 1998 20:06:47 +0000 Subject: [PATCH] Adding the same changes to HEAD as were added to BRANCH_1_9_18. 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. --- docs/manpages/smbpasswd.8 | 44 +++- docs/textdocs/ENCRYPTION.txt | 57 +++--- source/client/client.c | 25 --- source/client/smbmount.c | 25 --- source/include/proto.h | 5 +- source/libsmb/clientgen.c | 100 ++++++++- source/libsmb/smbdes.c | 10 +- source/passdb/smbpass.c | 6 - source/smbd/chgpasswd.c | 29 ++- source/utils/smbpasswd.c | 379 +++++++++++++++++++++++------------ 10 files changed, 447 insertions(+), 233 deletions(-) diff --git a/docs/manpages/smbpasswd.8 b/docs/manpages/smbpasswd.8 index 1cd2c54c434..24b37135984 100644 --- a/docs/manpages/smbpasswd.8 +++ b/docs/manpages/smbpasswd.8 @@ -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 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 \ +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 \ +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. diff --git a/docs/textdocs/ENCRYPTION.txt b/docs/textdocs/ENCRYPTION.txt index 315e7de53f7..352f3457b47 100644 --- a/docs/textdocs/ENCRYPTION.txt +++ b/docs/textdocs/ENCRYPTION.txt @@ -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 -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: +Old SMB 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" diff --git a/source/client/client.c b/source/client/client.c index 679114fa2bb..3ce635fbeb2 100644 --- a/source/client/client.c +++ b/source/client/client.c @@ -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)) diff --git a/source/client/smbmount.c b/source/client/smbmount.c index fdafaec9faf..bab74308592 100644 --- a/source/client/smbmount.c +++ b/source/client/smbmount.c @@ -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)) diff --git a/source/include/proto.h b/source/include/proto.h index e980bcacc9a..209004e90ad 100644 --- a/source/include/proto.h +++ b/source/include/proto.h @@ -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 */ diff --git a/source/libsmb/clientgen.c b/source/libsmb/clientgen.c index 4585c8a5441..dcebf704554 100644 --- a/source/libsmb/clientgen.c +++ b/source/libsmb/clientgen.c @@ -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 diff --git a/source/libsmb/smbdes.c b/source/libsmb/smbdes.c index 8f95a5a297b..e5d8f4a1e0b 100644 --- a/source/libsmb/smbdes.c +++ b/source/libsmb/smbdes.c @@ -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; diff --git a/source/passdb/smbpass.c b/source/passdb/smbpass.c index b51f6753065..a1913c4959e 100644 --- a/source/passdb/smbpass.c +++ b/source/passdb/smbpass.c @@ -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 diff --git a/source/smbd/chgpasswd.c b/source/smbd/chgpasswd.c index 57d81ad756d..779845d37ae 100644 --- a/source/smbd/chgpasswd.c +++ b/source/smbd/chgpasswd.c @@ -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); diff --git a/source/utils/smbpasswd.c b/source/utils/smbpasswd.c index 7ab8d8a0dbf..5b5f86c5e1c 100644 --- a/source/utils/smbpasswd.c +++ b/source/utils/smbpasswd.c @@ -19,11 +19,23 @@ #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); } -- 2.34.1