diff options
author | Andrew Bartlett <abartlet@samba.org> | 2004-01-26 09:03:09 +0000 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2004-01-26 09:03:09 +0000 |
commit | 36abff1609ad7f537494d08c321c0bf853d6ca9e (patch) | |
tree | d3b04478049885ba710565eaa71600b76b3e3408 /source3/smbd | |
parent | 784b05c4895fa8d7f5215d4444bc74e91a918114 (diff) | |
download | samba-36abff1609ad7f537494d08c321c0bf853d6ca9e.tar.gz samba-36abff1609ad7f537494d08c321c0bf853d6ca9e.tar.bz2 samba-36abff1609ad7f537494d08c321c0bf853d6ca9e.zip |
Revise our server-side password change code to cope with the various
different feilds that different clients send. (For example, not all clients
send both password types).
This also cleans up the code to make it clearer what is really going on,
and to make better use of common functions.
Andrew Bartlett
(This used to be commit 934e9f3bd0e8a938263978ffcd1fef51e42fa5e4)
Diffstat (limited to 'source3/smbd')
-rw-r--r-- | source3/smbd/chgpasswd.c | 267 |
1 files changed, 158 insertions, 109 deletions
diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c index e6117245e7..a452575af3 100644 --- a/source3/smbd/chgpasswd.c +++ b/source3/smbd/chgpasswd.c @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. Samba utility functions Copyright (C) Andrew Tridgell 1992-1998 - Copyright (C) Andrew Bartlett 2001-2002 + Copyright (C) Andrew Bartlett 2001-2004 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* These comments regard the code to change the user's unix password: */ + /* fork a child process to exec passwd and write to its * tty to change a users password. This is running as the * user who is attempting to change the password. @@ -30,9 +32,6 @@ * was included as a client to change passwords using the 'passwd' program * on the remote machine. * - * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE - * is defined in the compiler directives located in the Makefile. - * * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences * and rights to modify, distribute or incorporate this change to the CAP suite or @@ -52,10 +51,12 @@ extern struct passdb_ops pdb_ops; static NTSTATUS check_oem_password(const char *user, - uchar * lmdata, const uchar * lmhash, - const uchar * ntdata, const uchar * nthash, - SAM_ACCOUNT **hnd, char *new_passwd, - int new_passwd_size); + uchar password_encrypted_with_lm_hash[516], + const uchar old_lm_hash_encrypted[16], + uchar password_encrypted_with_nt_hash[516], + const uchar old_nt_hash_encrypted[16], + SAM_ACCOUNT **hnd, char *new_passwd, + int new_passwd_size); #if ALLOW_CHANGE_PASSWORD @@ -572,7 +573,7 @@ the string %%u, and the given string %s does not.\n", passwordprogram )); BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root) { - DEBUG(0, ("chgpasswd: Password changing not compiled in (user=%s)\n", name)); + DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name)); return (False); } #endif /* ALLOW_CHANGE_PASSWORD */ @@ -712,14 +713,19 @@ BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2) ************************************************************/ NTSTATUS pass_oem_change(char *user, - uchar * lmdata, uchar * lmhash, - uchar * ntdata, uchar * nthash) + uchar password_encrypted_with_lm_hash[516], + const uchar old_lm_hash_encrypted[16], + uchar password_encrypted_with_nt_hash[516], + const uchar old_nt_hash_encrypted[16]) { - fstring new_passwd; + pstring new_passwd; SAM_ACCOUNT *sampass = NULL; - NTSTATUS nt_status = check_oem_password(user, lmdata, lmhash, ntdata, nthash, - &sampass, new_passwd, sizeof(new_passwd)); - + NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash, + old_lm_hash_encrypted, + password_encrypted_with_nt_hash, + old_nt_hash_encrypted, + &sampass, new_passwd, sizeof(new_passwd)); + if (!NT_STATUS_IS_OK(nt_status)) return nt_status; @@ -736,33 +742,42 @@ NTSTATUS pass_oem_change(char *user, } /*********************************************************** - Code to check the OEM hashed password. + Decrypt and verify a user password change. - this function ignores the 516 byte nt OEM hashed password - but does use the lm OEM password to check the nt hashed-hash. + The 516 byte long buffers are encrypted with the old NT and + old LM passwords, and if the NT passwords are present, both + buffers contain a unicode string. + After decrypting the buffers, check the password is correct by + matching the old hashed passwords with the passwords in the passdb. + ************************************************************/ static NTSTATUS check_oem_password(const char *user, - uchar * lmdata, const uchar * lmhash, - const uchar * ntdata, const uchar * nthash, - SAM_ACCOUNT **hnd, char *new_passwd, - int new_passwd_size) + uchar password_encrypted_with_lm_hash[516], + const uchar old_lm_hash_encrypted[16], + uchar password_encrypted_with_nt_hash[516], + const uchar old_nt_hash_encrypted[16], + SAM_ACCOUNT **hnd, char *new_passwd, + int new_passwd_size) { static uchar null_pw[16]; static uchar null_ntpw[16]; SAM_ACCOUNT *sampass = NULL; + char *password_encrypted; + const char *encryption_key; const uint8 *lanman_pw, *nt_pw; uint16 acct_ctrl; - int new_pw_len; - uchar new_ntp16[16]; - uchar unenc_old_ntpw[16]; - uchar new_p16[16]; - uchar unenc_old_pw[16]; + uint32 new_pw_len; + uchar new_nt_hash[16]; + uchar old_nt_hash_plain[16]; + uchar new_lm_hash[16]; + uchar old_lm_hash_plain[16]; char no_pw[2]; BOOL ret; - BOOL nt_pass_set = (ntdata != NULL && nthash != NULL); + BOOL nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted); + BOOL lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted); *hnd = NULL; @@ -775,97 +790,152 @@ static NTSTATUS check_oem_password(const char *user, if (ret == False) { DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n")); pdb_free_sam(&sampass); - return NT_STATUS_WRONG_PASSWORD; - /* - TODO: check what Win2k returns for this: - return NT_STATUS_NO_SUCH_USER; - */ + return NT_STATUS_NO_SUCH_USER; } acct_ctrl = pdb_get_acct_ctrl(sampass); if (acct_ctrl & ACB_DISABLED) { - DEBUG(0,("check_lanman_password: account %s disabled.\n", user)); + DEBUG(2,("check_lanman_password: account %s disabled.\n", user)); pdb_free_sam(&sampass); return NT_STATUS_ACCOUNT_DISABLED; } - /* construct a null password (in case one is needed */ - no_pw[0] = 0; - no_pw[1] = 0; - nt_lm_owf_gen(no_pw, null_ntpw, null_pw); - - /* save pointers to passwords so we don't have to keep looking them up */ - lanman_pw = pdb_get_lanman_passwd(sampass); - nt_pw = pdb_get_nt_passwd(sampass); + if (acct_ctrl & ACB_PWNOTREQ && lp_null_passwords()) { + /* construct a null password (in case one is needed */ + no_pw[0] = 0; + no_pw[1] = 0; + nt_lm_owf_gen(no_pw, null_ntpw, null_pw); + lanman_pw = null_pw; + nt_pw = null_pw; - /* check for null passwords */ - if (lanman_pw == NULL) { - if (!(acct_ctrl & ACB_PWNOTREQ)) { - DEBUG(0,("check_oem_password: no lanman password !\n")); - pdb_free_sam(&sampass); - return NT_STATUS_WRONG_PASSWORD; + } else { + /* save pointers to passwords so we don't have to keep looking them up */ + if (lp_lanman_auth()) { + lanman_pw = pdb_get_lanman_passwd(sampass); + } else { + lanman_pw = NULL; } + nt_pw = pdb_get_nt_passwd(sampass); } - - if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) { - if (!(acct_ctrl & ACB_PWNOTREQ)) { - DEBUG(0,("check_oem_password: no ntlm password !\n")); - pdb_free_sam(&sampass); - return NT_STATUS_WRONG_PASSWORD; - } + + if (nt_pw && nt_pass_set) { + /* IDEAL Case: passwords are in unicode, and we can + * read use the password encrypted with the NT hash + */ + password_encrypted = password_encrypted_with_nt_hash; + encryption_key = nt_pw; + } else if (lanman_pw && lm_pass_set) { + /* password may still be in unicode, but use LM hash version */ + password_encrypted = password_encrypted_with_lm_hash; + encryption_key = lanman_pw; + } else if (nt_pass_set) { + DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n", + user)); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } else if (lm_pass_set) { + DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n", + user)); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } else { + DEBUG(1, ("password change requested for user %s, but no password supplied!\n", + user)); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; } - - /* - * Call the hash function to get the new password. - */ - SamOEMhash( lmdata, lanman_pw, 516); /* - * The length of the new password is in the last 4 bytes of - * the data buffer. + * Decrypt the password with the key */ + SamOEMhash( password_encrypted, encryption_key, 516); - new_pw_len = IVAL(lmdata, 512); - - if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) { - DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len)); + if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len, + nt_pass_set ? STR_UNICODE : STR_ASCII)) { pdb_free_sam(&sampass); return NT_STATUS_WRONG_PASSWORD; } - if (nt_pass_set) { - /* - * nt passwords are in unicode - */ - pull_ucs2(NULL, new_passwd, - (const smb_ucs2_t *)&lmdata[512 - new_pw_len], - new_passwd_size, new_pw_len, 0); - } else { - memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len); - new_passwd[new_pw_len] = 0; - } - /* * To ensure we got the correct new password, hash it and * use it as a key to test the passed old password. */ - nt_lm_owf_gen(new_passwd, new_ntp16, new_p16); + if (nt_pass_set) { + /* NT passwords, verify the NT hash. */ + + /* Calculate the MD4 hash (NT compatible) of the password */ + memset(new_nt_hash, '\0', 16); + E_md4hash(new_passwd, new_nt_hash); + + if (nt_pw) { + /* + * Now use new_nt_hash as the key to see if the old + * password matches. + */ + D_P16(new_nt_hash, old_nt_hash_encrypted, old_nt_hash_plain); + + if (memcmp(nt_pw, old_nt_hash_plain, 16)) { + DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } + + /* We could check the LM password here, but there is + * little point, we already know the password is + * correct, and the LM password might not even be + * present. */ + + /* Further, LM hash generation algorithms + * differ with charset, so we could + * incorrectly fail a perfectly valid password + * change */ +#ifdef DEBUG_PASSWORD + DEBUG(100, + ("check_oem_password: password %s ok\n", new_passwd)); +#endif + *hnd = sampass; + return NT_STATUS_OK; + } + + if (lanman_pw) { + /* + * Now use new_nt_hash as the key to see if the old + * LM password matches. + */ + D_P16(new_nt_hash, old_lm_hash_encrypted, old_lm_hash_plain); + + if (memcmp(lanman_pw, old_lm_hash_plain, 16)) { + DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } +#ifdef DEBUG_PASSWORD + DEBUG(100, + ("check_oem_password: password %s ok\n", new_passwd)); +#endif + *hnd = sampass; + return NT_STATUS_OK; + } + } + + if (lanman_pw && lm_pass_set) { + + E_deshash(new_passwd, new_lm_hash); - if (!nt_pass_set) { /* - * Now use new_p16 as the key to see if the old + * Now use new_lm_hash as the key to see if the old * password matches. */ - D_P16(new_p16, lmhash, unenc_old_pw); - - if (memcmp(lanman_pw, unenc_old_pw, 16)) { + D_P16(new_lm_hash, old_lm_hash_encrypted, old_lm_hash_plain); + + if (memcmp(lanman_pw, old_lm_hash_plain, 16)) { DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); pdb_free_sam(&sampass); return NT_STATUS_WRONG_PASSWORD; } - + #ifdef DEBUG_PASSWORD DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd)); @@ -874,30 +944,9 @@ static NTSTATUS check_oem_password(const char *user, return NT_STATUS_OK; } - /* - * Now use new_p16 as the key to see if the old - * password matches. - */ - D_P16(new_ntp16, lmhash, unenc_old_pw); - D_P16(new_ntp16, nthash, unenc_old_ntpw); - - if (memcmp(lanman_pw, unenc_old_pw, 16)) { - DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); - pdb_free_sam(&sampass); - return NT_STATUS_WRONG_PASSWORD; - } - - if (memcmp(nt_pw, unenc_old_ntpw, 16)) { - DEBUG(0,("check_oem_password: old nt password doesn't match.\n")); - pdb_free_sam(&sampass); - return NT_STATUS_WRONG_PASSWORD; - } -#ifdef DEBUG_PASSWORD - DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd)); -#endif - - *hnd = sampass; - return NT_STATUS_OK; + /* should not be reached */ + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; } /*********************************************************** |