diff options
Diffstat (limited to 'source3/smbd/chgpasswd.c')
-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 692e82680d..e3df8a11d0 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 @@ -62,10 +61,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 @@ -565,7 +566,7 @@ the string %%u, and the given string %s does not.\n", passwordprogram )); BOOL chgpasswd(const char *name, const struct passwd *pass, 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 */ @@ -705,14 +706,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; @@ -729,33 +735,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; @@ -768,97 +783,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)); @@ -867,30 +937,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; } /*********************************************************** |