2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) Andrew Bartlett 2001-2004
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 /* These comments regard the code to change the user's unix password: */
23 /* fork a child process to exec passwd and write to its
24 * tty to change a users password. This is running as the
25 * user who is attempting to change the password.
29 * This code was copied/borrowed and stolen from various sources.
30 * The primary source was the poppasswd.c from the authors of POPMail. This software
31 * was included as a client to change passwords using the 'passwd' program
32 * on the remote machine.
34 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
35 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
36 * and rights to modify, distribute or incorporate this change to the CAP suite or
37 * using it for any other reason are granted, so long as this disclaimer is left intact.
41 This code was hacked considerably for inclusion in Samba, primarily
42 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
43 of the "password chat" option, which allows the easy runtime
44 specification of the expected sequence of events to change a
50 extern struct passdb_ops pdb_ops;
52 static NTSTATUS check_oem_password(const char *user,
53 uchar password_encrypted_with_lm_hash[516],
54 const uchar old_lm_hash_encrypted[16],
55 uchar password_encrypted_with_nt_hash[516],
56 const uchar old_nt_hash_encrypted[16],
57 struct samu **hnd, char *new_passwd,
60 #if ALLOW_CHANGE_PASSWORD
62 static int findpty(char **slave)
69 #if defined(HAVE_GRANTPT)
70 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
71 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
75 *slave = (char *)ptsname(master);
79 ("findpty: Unable to create master/slave pty pair.\n"));
80 /* Stop fd leak on error. */
87 ("findpty: Allocated slave pty %s\n", *slave));
91 #endif /* HAVE_GRANTPT */
93 fstrcpy(line, "/dev/ptyXX");
95 dirp = sys_opendir("/dev");
98 while ((dpname = readdirname(dirp)) != NULL)
100 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
103 ("pty: try to open %s, line was %s\n", dpname,
107 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
109 DEBUG(3, ("pty: opened %s\n", line));
121 static int dochild(int master, const char *slavedev, const struct passwd *pass,
122 const char *passwordprogram, bool as_root)
125 struct termios stermios;
132 ("dochild: user doesn't exist in the UNIX password database.\n"));
139 gain_root_privilege();
141 /* Start new session - gets rid of controlling terminal. */
145 ("Weirdness, couldn't let go of controlling terminal\n"));
149 /* Open slave pty and acquire as new controlling terminal. */
150 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
152 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
155 #if defined(I_PUSH) && defined(I_FIND)
156 if (ioctl(slave, I_FIND, "ptem") == 0) {
157 ioctl(slave, I_PUSH, "ptem");
159 if (ioctl(slave, I_FIND, "ldterm") == 0) {
160 ioctl(slave, I_PUSH, "ldterm");
162 #elif defined(TIOCSCTTY)
163 if (ioctl(slave, TIOCSCTTY, 0) < 0)
165 DEBUG(3, ("Error in ioctl call for slave pty\n"));
173 /* Make slave stdin/out/err of child. */
175 if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
177 DEBUG(3, ("Could not re-direct stdin\n"));
180 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
182 DEBUG(3, ("Could not re-direct stdout\n"));
185 if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
187 DEBUG(3, ("Could not re-direct stderr\n"));
193 /* Set proper terminal attributes - no echo, canonical input processing,
194 no map NL to CR/NL on output. */
196 if (tcgetattr(0, &stermios) < 0)
199 ("could not read default terminal attributes on pty\n"));
202 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
203 stermios.c_lflag |= ICANON;
205 stermios.c_oflag &= ~(ONLCR);
207 if (tcsetattr(0, TCSANOW, &stermios) < 0)
209 DEBUG(3, ("could not set attributes of pty\n"));
213 /* make us completely into the right uid */
216 become_user_permanently(uid, gid);
220 ("Invoking '%s' as password change program.\n",
223 /* execl() password-change application */
224 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
226 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
232 static int expect(int master, char *issue, char *expected)
235 int attempts, timeout, nread, len;
238 for (attempts = 0; attempts < 2; attempts++) {
239 if (!strequal(issue, ".")) {
240 if (lp_passwd_chat_debug())
241 DEBUG(100, ("expect: sending [%s]\n", issue));
243 if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
244 DEBUG(2,("expect: (short) write returned %d\n", len ));
249 if (strequal(expected, "."))
252 /* Initial timeout. */
253 timeout = lp_passwd_chat_timeout() * 1000;
257 while ((len = read_socket_with_timeout(master, buffer + nread, 1,
258 sizeof(buffer) - nread - 1,
264 /* Eat leading/trailing whitespace before match. */
266 pstrcpy( str, buffer);
267 trim_char( str, ' ', ' ');
269 if ((match = unix_wild_match(expected, str)) == True) {
270 /* Now data has started to return, lower timeout. */
271 timeout = lp_passwd_chat_timeout() * 100;
276 if (lp_passwd_chat_debug())
277 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
278 expected, buffer, match ? "yes" : "no" ));
284 DEBUG(2, ("expect: %s\n", strerror(errno)));
289 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
293 static void pwd_sub(char *buf)
295 all_string_sub(buf, "\\n", "\n", 0);
296 all_string_sub(buf, "\\r", "\r", 0);
297 all_string_sub(buf, "\\s", " ", 0);
298 all_string_sub(buf, "\\t", "\t", 0);
301 static int talktochild(int master, const char *seq)
304 fstring issue, expected;
308 while (next_token(&seq, expected, NULL, sizeof(expected)))
313 if (!expect(master, issue, expected))
315 DEBUG(3, ("Response %d incorrect\n", count));
319 if (!next_token(&seq, issue, NULL, sizeof(issue)))
324 if (!strequal(issue, ".")) {
325 /* we have one final issue to send */
326 fstrcpy(expected, ".");
327 if (!expect(master, issue, expected))
334 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
335 char *chatsequence, bool as_root)
344 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
348 /* allocate a pseudo-terminal device */
349 if ((master = findpty(&slavedev)) < 0) {
350 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
355 * We need to temporarily stop CatchChild from eating
356 * SIGCLD signals as it also eats the exit status code. JRA.
359 CatchChildLeaveStatus();
361 if ((pid = sys_fork()) < 0) {
362 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
368 /* we now have a pty */
369 if (pid > 0) { /* This is the parent process */
370 if ((chstat = talktochild(master, chatsequence)) == False) {
371 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
372 kill(pid, SIGKILL); /* be sure to end this process */
375 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
376 if (errno == EINTR) {
384 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
391 * Go back to ignoring children.
398 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
401 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
402 DEBUG(3, ("chat_with_program: The process exited with status %d \
403 while we were waiting\n", WEXITSTATUS(wstat)));
406 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
407 else if (WIFSIGNALLED(wstat)) {
408 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
409 while we were waiting\n", WTERMSIG(wstat)));
417 * Lose any elevated privileges.
419 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
420 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
422 /* make sure it doesn't freeze */
428 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
429 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
430 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
436 * The child should never return from dochild() ....
439 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
444 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
445 (chstat ? "" : "un"), pass->pw_name));
449 bool chgpasswd(const char *name, const struct passwd *pass,
450 const char *oldpass, const char *newpass, bool as_root)
452 pstring passwordprogram;
453 pstring chatsequence;
461 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
463 #ifdef DEBUG_PASSWORD
464 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
467 /* Take the passed information and test it for minimum criteria */
469 /* Password is same as old password */
470 if (strcmp(oldpass, newpass) == 0) {
471 /* don't allow same password */
472 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
473 return (False); /* inform the user */
477 * Check the old and new passwords don't contain any control
481 len = strlen(oldpass);
482 for (i = 0; i < len; i++) {
483 if (iscntrl((int)oldpass[i])) {
484 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
489 len = strlen(newpass);
490 for (i = 0; i < len; i++) {
491 if (iscntrl((int)newpass[i])) {
492 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
498 if (lp_pam_password_change()) {
505 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
507 ret = smb_pam_passchange(name, oldpass, newpass);
517 /* A non-PAM password change just doen't make sense without a valid local user */
520 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
524 pstrcpy(passwordprogram, lp_passwd_program());
525 pstrcpy(chatsequence, lp_passwd_chat());
527 if (!*chatsequence) {
528 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
532 if (!*passwordprogram) {
533 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
538 /* The password program *must* contain the user name to work. Fail if not. */
539 if (strstr_m(passwordprogram, "%u") == NULL) {
540 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
541 the string %%u, and the given string %s does not.\n", passwordprogram ));
546 pstring_sub(passwordprogram, "%u", name);
547 /* note that we do NOT substitute the %o and %n in the password program
548 as this would open up a security hole where the user could use
549 a new password containing shell escape characters */
551 pstring_sub(chatsequence, "%u", name);
552 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
553 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
554 return (chat_with_program
555 (passwordprogram, pass, chatsequence, as_root));
558 #else /* ALLOW_CHANGE_PASSWORD */
560 bool chgpasswd(const char *name, const struct passwd *pass,
561 const char *oldpass, const char *newpass, bool as_root)
563 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
566 #endif /* ALLOW_CHANGE_PASSWORD */
568 /***********************************************************
569 Code to check the lanman hashed password.
570 ************************************************************/
572 bool check_lanman_password(char *user, uchar * pass1,
573 uchar * pass2, struct samu **hnd)
575 uchar unenc_new_pw[16];
576 uchar unenc_old_pw[16];
577 struct samu *sampass = NULL;
579 const uint8 *lanman_pw;
582 if ( !(sampass = samu_new(NULL)) ) {
583 DEBUG(0, ("samu_new() failed!\n"));
588 ret = pdb_getsampwnam(sampass, user);
592 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
593 TALLOC_FREE(sampass);
597 acct_ctrl = pdb_get_acct_ctrl (sampass);
598 lanman_pw = pdb_get_lanman_passwd (sampass);
600 if (acct_ctrl & ACB_DISABLED) {
601 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
602 TALLOC_FREE(sampass);
606 if (lanman_pw == NULL) {
607 if (acct_ctrl & ACB_PWNOTREQ) {
608 /* this saves the pointer for the caller */
612 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
613 TALLOC_FREE(sampass);
618 /* Get the new lanman hash. */
619 D_P16(lanman_pw, pass2, unenc_new_pw);
621 /* Use this to get the old lanman hash. */
622 D_P16(unenc_new_pw, pass1, unenc_old_pw);
624 /* Check that the two old passwords match. */
625 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
626 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
627 TALLOC_FREE(sampass);
631 /* this saves the pointer for the caller */
636 /***********************************************************
637 Code to change the lanman hashed password.
638 It nulls out the NT hashed password as it will
640 NOTE this function is designed to be called as root. Check the old password
641 is correct before calling. JRA.
642 ************************************************************/
644 bool change_lanman_password(struct samu *sampass, uchar *pass2)
646 static uchar null_pw[16];
647 uchar unenc_new_pw[16];
652 if (sampass == NULL) {
653 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
657 acct_ctrl = pdb_get_acct_ctrl(sampass);
658 pwd = pdb_get_lanman_passwd(sampass);
660 if (acct_ctrl & ACB_DISABLED) {
661 DEBUG(0,("change_lanman_password: account %s disabled.\n",
662 pdb_get_username(sampass)));
667 if (acct_ctrl & ACB_PWNOTREQ) {
669 memset(no_pw, '\0', 14);
670 E_P16(no_pw, null_pw);
672 /* Get the new lanman hash. */
673 D_P16(null_pw, pass2, unenc_new_pw);
675 DEBUG(0,("change_lanman_password: no lanman password !\n"));
679 /* Get the new lanman hash. */
680 D_P16(pwd, pass2, unenc_new_pw);
683 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
687 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
688 return False; /* We lose the NT hash. Sorry. */
691 if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED)) {
692 TALLOC_FREE(sampass);
693 /* Not quite sure what this one qualifies as, but this will do */
697 /* Now flush the sam_passwd struct to persistent storage */
698 ret = NT_STATUS_IS_OK(pdb_update_sam_account (sampass));
703 /***********************************************************
704 Code to check and change the OEM hashed password.
705 ************************************************************/
707 NTSTATUS pass_oem_change(char *user,
708 uchar password_encrypted_with_lm_hash[516],
709 const uchar old_lm_hash_encrypted[16],
710 uchar password_encrypted_with_nt_hash[516],
711 const uchar old_nt_hash_encrypted[16],
712 uint32 *reject_reason)
715 struct samu *sampass = NULL;
716 NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash,
717 old_lm_hash_encrypted,
718 password_encrypted_with_nt_hash,
719 old_nt_hash_encrypted,
720 &sampass, new_passwd, sizeof(new_passwd));
722 if (!NT_STATUS_IS_OK(nt_status))
725 /* We've already checked the old password here.... */
727 nt_status = change_oem_password(sampass, NULL, new_passwd, True, reject_reason);
730 memset(new_passwd, 0, sizeof(new_passwd));
732 TALLOC_FREE(sampass);
737 /***********************************************************
738 Decrypt and verify a user password change.
740 The 516 byte long buffers are encrypted with the old NT and
741 old LM passwords, and if the NT passwords are present, both
742 buffers contain a unicode string.
744 After decrypting the buffers, check the password is correct by
745 matching the old hashed passwords with the passwords in the passdb.
747 ************************************************************/
749 static NTSTATUS check_oem_password(const char *user,
750 uchar password_encrypted_with_lm_hash[516],
751 const uchar old_lm_hash_encrypted[16],
752 uchar password_encrypted_with_nt_hash[516],
753 const uchar old_nt_hash_encrypted[16],
754 struct samu **hnd, char *new_passwd,
757 static uchar null_pw[16];
758 static uchar null_ntpw[16];
759 struct samu *sampass = NULL;
760 uint8 *password_encrypted;
761 const uint8 *encryption_key;
762 const uint8 *lanman_pw, *nt_pw;
765 uchar new_nt_hash[16];
766 uchar new_lm_hash[16];
771 bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
772 bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
776 if ( !(sampass = samu_new( NULL )) ) {
777 return NT_STATUS_NO_MEMORY;
781 ret = pdb_getsampwnam(sampass, user);
785 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
786 TALLOC_FREE(sampass);
787 return NT_STATUS_NO_SUCH_USER;
790 acct_ctrl = pdb_get_acct_ctrl(sampass);
792 if (acct_ctrl & ACB_DISABLED) {
793 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
794 TALLOC_FREE(sampass);
795 return NT_STATUS_ACCOUNT_DISABLED;
798 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
799 /* construct a null password (in case one is needed */
802 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
807 /* save pointers to passwords so we don't have to keep looking them up */
808 if (lp_lanman_auth()) {
809 lanman_pw = pdb_get_lanman_passwd(sampass);
813 nt_pw = pdb_get_nt_passwd(sampass);
816 if (nt_pw && nt_pass_set) {
817 /* IDEAL Case: passwords are in unicode, and we can
818 * read use the password encrypted with the NT hash
820 password_encrypted = password_encrypted_with_nt_hash;
821 encryption_key = nt_pw;
822 } else if (lanman_pw && lm_pass_set) {
823 /* password may still be in unicode, but use LM hash version */
824 password_encrypted = password_encrypted_with_lm_hash;
825 encryption_key = lanman_pw;
826 } else if (nt_pass_set) {
827 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
829 TALLOC_FREE(sampass);
830 return NT_STATUS_WRONG_PASSWORD;
831 } else if (lm_pass_set) {
832 if (lp_lanman_auth()) {
833 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
836 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
839 TALLOC_FREE(sampass);
840 return NT_STATUS_WRONG_PASSWORD;
842 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
844 TALLOC_FREE(sampass);
845 return NT_STATUS_WRONG_PASSWORD;
849 * Decrypt the password with the key
851 SamOEMhash( password_encrypted, encryption_key, 516);
853 if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len,
854 nt_pass_set ? STR_UNICODE : STR_ASCII)) {
855 TALLOC_FREE(sampass);
856 return NT_STATUS_WRONG_PASSWORD;
860 * To ensure we got the correct new password, hash it and
861 * use it as a key to test the passed old password.
865 /* NT passwords, verify the NT hash. */
867 /* Calculate the MD4 hash (NT compatible) of the password */
868 memset(new_nt_hash, '\0', 16);
869 E_md4hash(new_passwd, new_nt_hash);
873 * check the NT verifier
875 E_old_pw_hash(new_nt_hash, nt_pw, verifier);
876 if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
877 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
878 TALLOC_FREE(sampass);
879 return NT_STATUS_WRONG_PASSWORD;
882 /* We could check the LM password here, but there is
883 * little point, we already know the password is
884 * correct, and the LM password might not even be
887 /* Further, LM hash generation algorithms
888 * differ with charset, so we could
889 * incorrectly fail a perfectly valid password
891 #ifdef DEBUG_PASSWORD
893 ("check_oem_password: password %s ok\n", new_passwd));
901 * check the lm verifier
903 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
904 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
905 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
906 TALLOC_FREE(sampass);
907 return NT_STATUS_WRONG_PASSWORD;
909 #ifdef DEBUG_PASSWORD
911 ("check_oem_password: password %s ok\n", new_passwd));
918 if (lanman_pw && lm_pass_set) {
920 E_deshash(new_passwd, new_lm_hash);
923 * check the lm verifier
925 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
926 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
927 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
928 TALLOC_FREE(sampass);
929 return NT_STATUS_WRONG_PASSWORD;
932 #ifdef DEBUG_PASSWORD
934 ("check_oem_password: password %s ok\n", new_passwd));
940 /* should not be reached */
941 TALLOC_FREE(sampass);
942 return NT_STATUS_WRONG_PASSWORD;
945 /***********************************************************
946 This routine takes the given password and checks it against
947 the password history. Returns True if this password has been
948 found in the history list.
949 ************************************************************/
951 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
953 uchar new_nt_p16[NT_HASH_LEN];
954 uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
956 const uint8 *pwhistory;
959 uint32 pwHisLen, curr_pwHisLen;
961 pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHisLen);
966 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
967 if (!pwhistory || curr_pwHisLen == 0) {
971 /* Only examine the minimum of the current history len and
972 the stored history len. Avoids race conditions. */
973 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
975 nt_pw = pdb_get_nt_passwd(sampass);
977 E_md4hash(plaintext, new_nt_p16);
979 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
980 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
981 pdb_get_username(sampass) ));
985 dump_data(100, new_nt_p16, NT_HASH_LEN);
986 dump_data(100, pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
988 memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
989 for (i=0; i<pwHisLen; i++) {
990 uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
991 const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
992 const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
993 PW_HISTORY_SALT_LEN];
994 if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
995 /* Ignore zero valued entries. */
998 /* Create salted versions of new to compare. */
999 E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
1001 if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1002 DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
1003 pdb_get_username(sampass) ));
1011 /***********************************************************
1012 Code to change the oem password. Changes both the lanman
1013 and NT hashes. Old_passwd is almost always NULL.
1014 NOTE this function is designed to be called as root. Check the old password
1015 is correct before calling. JRA.
1016 ************************************************************/
1018 NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, bool as_root, uint32 *samr_reject_reason)
1022 struct passwd *pass = NULL;
1023 const char *username = pdb_get_username(hnd);
1024 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1026 if (samr_reject_reason) {
1027 *samr_reject_reason = Undefined;
1030 /* check to see if the secdesc has previously been set to disallow */
1031 if (!pdb_get_pass_can_change(hnd)) {
1032 DEBUG(1, ("user %s does not have permissions to change password\n", username));
1033 if (samr_reject_reason) {
1034 *samr_reject_reason = REJECT_REASON_OTHER;
1036 return NT_STATUS_ACCOUNT_RESTRICTION;
1039 /* check to see if it is a Machine account and if the policy
1040 * denies machines to change the password. *
1041 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1042 if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1043 if (pdb_get_account_policy(AP_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1044 DEBUG(1, ("Machine %s cannot change password now, "
1045 "denied by Refuse Machine Password Change policy\n",
1047 if (samr_reject_reason) {
1048 *samr_reject_reason = REJECT_REASON_OTHER;
1050 return NT_STATUS_ACCOUNT_RESTRICTION;
1054 /* removed calculation here, becuase passdb now calculates
1055 based on policy. jmcd */
1056 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1057 DEBUG(1, ("user %s cannot change password now, must "
1058 "wait until %s\n", username,
1059 http_timestring(can_change_time)));
1060 if (samr_reject_reason) {
1061 *samr_reject_reason = REJECT_REASON_OTHER;
1063 return NT_STATUS_ACCOUNT_RESTRICTION;
1066 if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1067 DEBUG(1, ("user %s cannot change password - password too short\n",
1069 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1070 if (samr_reject_reason) {
1071 *samr_reject_reason = REJECT_REASON_TOO_SHORT;
1073 return NT_STATUS_PASSWORD_RESTRICTION;
1074 /* return NT_STATUS_PWD_TOO_SHORT; */
1077 if (check_passwd_history(hnd,new_passwd)) {
1078 if (samr_reject_reason) {
1079 *samr_reject_reason = REJECT_REASON_IN_HISTORY;
1081 return NT_STATUS_PASSWORD_RESTRICTION;
1084 pass = Get_Pwnam(username);
1086 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1087 return NT_STATUS_ACCESS_DENIED;
1090 /* Use external script to check password complexity */
1091 if (lp_check_password_script() && *(lp_check_password_script())) {
1094 check_ret = smbrunsecret(lp_check_password_script(), new_passwd);
1095 DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret));
1097 if (check_ret != 0) {
1098 DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
1099 if (samr_reject_reason) {
1100 *samr_reject_reason = REJECT_REASON_NOT_COMPLEX;
1102 return NT_STATUS_PASSWORD_RESTRICTION;
1107 * If unix password sync was requested, attempt to change
1108 * the /etc/passwd database first. Return failure if this cannot
1111 * This occurs before the oem change, because we don't want to
1112 * update it if chgpasswd failed.
1114 * Conditional on lp_unix_password_sync() because we don't want
1115 * to touch the unix db unless we have admin permission.
1118 if(lp_unix_password_sync() &&
1119 !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
1120 return NT_STATUS_ACCESS_DENIED;
1123 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1124 return NT_STATUS_ACCESS_DENIED;
1127 /* Now write it into the file. */
1128 return pdb_update_sam_account (hnd);