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 2 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, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 /* These comments regard the code to change the user's unix password: */
24 /* fork a child process to exec passwd and write to its
25 * tty to change a users password. This is running as the
26 * user who is attempting to change the password.
30 * This code was copied/borrowed and stolen from various sources.
31 * The primary source was the poppasswd.c from the authors of POPMail. This software
32 * was included as a client to change passwords using the 'passwd' program
33 * on the remote machine.
35 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
36 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
37 * and rights to modify, distribute or incorporate this change to the CAP suite or
38 * using it for any other reason are granted, so long as this disclaimer is left intact.
42 This code was hacked considerably for inclusion in Samba, primarily
43 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
44 of the "password chat" option, which allows the easy runtime
45 specification of the expected sequence of events to change a
51 extern struct passdb_ops pdb_ops;
53 static NTSTATUS check_oem_password(const char *user,
54 uchar password_encrypted_with_lm_hash[516],
55 const uchar old_lm_hash_encrypted[16],
56 uchar password_encrypted_with_nt_hash[516],
57 const uchar old_nt_hash_encrypted[16],
58 SAM_ACCOUNT **hnd, char *new_passwd,
61 #if ALLOW_CHANGE_PASSWORD
63 static int findpty(char **slave)
70 #if defined(HAVE_GRANTPT)
71 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
72 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
76 *slave = (char *)ptsname(master);
80 ("findpty: Unable to create master/slave pty pair.\n"));
81 /* Stop fd leak on error. */
88 ("findpty: Allocated slave pty %s\n", *slave));
92 #endif /* HAVE_GRANTPT */
94 fstrcpy(line, "/dev/ptyXX");
96 dirp = sys_opendir("/dev");
99 while ((dpname = readdirname(dirp)) != NULL)
101 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
104 ("pty: try to open %s, line was %s\n", dpname,
108 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
110 DEBUG(3, ("pty: opened %s\n", line));
122 static int dochild(int master, const char *slavedev, const struct passwd *pass,
123 const char *passwordprogram, BOOL as_root)
126 struct termios stermios;
133 ("dochild: user doesn't exist in the UNIX password database.\n"));
140 gain_root_privilege();
142 /* Start new session - gets rid of controlling terminal. */
146 ("Weirdness, couldn't let go of controlling terminal\n"));
150 /* Open slave pty and acquire as new controlling terminal. */
151 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
153 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
156 #if defined(I_PUSH) && defined(I_FIND)
157 if (ioctl(slave, I_FIND, "ptem") == 0) {
158 ioctl(slave, I_PUSH, "ptem");
160 if (ioctl(slave, I_FIND, "ldterm") == 0) {
161 ioctl(slave, I_PUSH, "ldterm");
163 #elif defined(TIOCSCTTY)
164 if (ioctl(slave, TIOCSCTTY, 0) < 0)
166 DEBUG(3, ("Error in ioctl call for slave pty\n"));
174 /* Make slave stdin/out/err of child. */
176 if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
178 DEBUG(3, ("Could not re-direct stdin\n"));
181 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
183 DEBUG(3, ("Could not re-direct stdout\n"));
186 if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
188 DEBUG(3, ("Could not re-direct stderr\n"));
194 /* Set proper terminal attributes - no echo, canonical input processing,
195 no map NL to CR/NL on output. */
197 if (tcgetattr(0, &stermios) < 0)
200 ("could not read default terminal attributes on pty\n"));
203 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
204 stermios.c_lflag |= ICANON;
206 stermios.c_oflag &= ~(ONLCR);
208 if (tcsetattr(0, TCSANOW, &stermios) < 0)
210 DEBUG(3, ("could not set attributes of pty\n"));
214 /* make us completely into the right uid */
217 become_user_permanently(uid, gid);
221 ("Invoking '%s' as password change program.\n",
224 /* execl() password-change application */
225 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
227 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
233 static int expect(int master, char *issue, char *expected)
236 int attempts, timeout, nread, len;
239 for (attempts = 0; attempts < 2; attempts++) {
240 if (!strequal(issue, ".")) {
241 if (lp_passwd_chat_debug())
242 DEBUG(100, ("expect: sending [%s]\n", issue));
244 if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
245 DEBUG(2,("expect: (short) write returned %d\n", len ));
250 if (strequal(expected, "."))
253 /* Initial timeout. */
254 timeout = lp_passwd_chat_timeout() * 1000;
258 while ((len = read_socket_with_timeout(master, buffer + nread, 1,
259 sizeof(buffer) - nread - 1,
265 /* Eat leading/trailing whitespace before match. */
267 pstrcpy( str, buffer);
268 trim_char( str, ' ', ' ');
270 if ((match = unix_wild_match(expected, str)) == True) {
271 /* Now data has started to return, lower timeout. */
272 timeout = lp_passwd_chat_timeout() * 100;
277 if (lp_passwd_chat_debug())
278 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
279 expected, buffer, match ? "yes" : "no" ));
285 DEBUG(2, ("expect: %s\n", strerror(errno)));
290 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
294 static void pwd_sub(char *buf)
296 all_string_sub(buf, "\\n", "\n", 0);
297 all_string_sub(buf, "\\r", "\r", 0);
298 all_string_sub(buf, "\\s", " ", 0);
299 all_string_sub(buf, "\\t", "\t", 0);
302 static int talktochild(int master, const char *seq)
305 fstring issue, expected;
309 while (next_token(&seq, expected, NULL, sizeof(expected)))
314 if (!expect(master, issue, expected))
316 DEBUG(3, ("Response %d incorrect\n", count));
320 if (!next_token(&seq, issue, NULL, sizeof(issue)))
325 if (!strequal(issue, ".")) {
326 /* we have one final issue to send */
327 fstrcpy(expected, ".");
328 if (!expect(master, issue, expected))
335 static BOOL chat_with_program(char *passwordprogram, const struct passwd *pass,
336 char *chatsequence, BOOL as_root)
345 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
349 /* allocate a pseudo-terminal device */
350 if ((master = findpty(&slavedev)) < 0) {
351 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
356 * We need to temporarily stop CatchChild from eating
357 * SIGCLD signals as it also eats the exit status code. JRA.
360 CatchChildLeaveStatus();
362 if ((pid = sys_fork()) < 0) {
363 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
369 /* we now have a pty */
370 if (pid > 0) { /* This is the parent process */
371 if ((chstat = talktochild(master, chatsequence)) == False) {
372 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
373 kill(pid, SIGKILL); /* be sure to end this process */
376 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
377 if (errno == EINTR) {
385 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
392 * Go back to ignoring children.
399 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
402 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
403 DEBUG(3, ("chat_with_program: The process exited with status %d \
404 while we were waiting\n", WEXITSTATUS(wstat)));
407 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
408 else if (WIFSIGNALLED(wstat)) {
409 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
410 while we were waiting\n", WTERMSIG(wstat)));
418 * Lose any oplock capabilities.
420 oplock_set_capability(False, False);
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, SAM_ACCOUNT **hnd)
575 uchar unenc_new_pw[16];
576 uchar unenc_old_pw[16];
577 SAM_ACCOUNT *sampass = NULL;
579 const uint8 *lanman_pw;
583 ret = pdb_getsampwnam(sampass, user);
587 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
588 pdb_free_sam(&sampass);
592 acct_ctrl = pdb_get_acct_ctrl (sampass);
593 lanman_pw = pdb_get_lanman_passwd (sampass);
595 if (acct_ctrl & ACB_DISABLED) {
596 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
597 pdb_free_sam(&sampass);
601 if (lanman_pw == NULL) {
602 if (acct_ctrl & ACB_PWNOTREQ) {
603 /* this saves the pointer for the caller */
607 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
608 pdb_free_sam(&sampass);
613 /* Get the new lanman hash. */
614 D_P16(lanman_pw, pass2, unenc_new_pw);
616 /* Use this to get the old lanman hash. */
617 D_P16(unenc_new_pw, pass1, unenc_old_pw);
619 /* Check that the two old passwords match. */
620 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
621 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
622 pdb_free_sam(&sampass);
626 /* this saves the pointer for the caller */
631 /***********************************************************
632 Code to change the lanman hashed password.
633 It nulls out the NT hashed password as it will
635 NOTE this function is designed to be called as root. Check the old password
636 is correct before calling. JRA.
637 ************************************************************/
639 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2)
641 static uchar null_pw[16];
642 uchar unenc_new_pw[16];
647 if (sampass == NULL) {
648 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
652 acct_ctrl = pdb_get_acct_ctrl(sampass);
653 pwd = pdb_get_lanman_passwd(sampass);
655 if (acct_ctrl & ACB_DISABLED) {
656 DEBUG(0,("change_lanman_password: account %s disabled.\n",
657 pdb_get_username(sampass)));
662 if (acct_ctrl & ACB_PWNOTREQ) {
664 memset(no_pw, '\0', 14);
665 E_P16(no_pw, null_pw);
667 /* Get the new lanman hash. */
668 D_P16(null_pw, pass2, unenc_new_pw);
670 DEBUG(0,("change_lanman_password: no lanman password !\n"));
674 /* Get the new lanman hash. */
675 D_P16(pwd, pass2, unenc_new_pw);
678 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
682 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
683 return False; /* We lose the NT hash. Sorry. */
686 if (!pdb_set_pass_changed_now (sampass)) {
687 pdb_free_sam(&sampass);
688 /* Not quite sure what this one qualifies as, but this will do */
692 /* Now flush the sam_passwd struct to persistent storage */
693 ret = pdb_update_sam_account (sampass);
698 /***********************************************************
699 Code to check and change the OEM hashed password.
700 ************************************************************/
702 NTSTATUS pass_oem_change(char *user,
703 uchar password_encrypted_with_lm_hash[516],
704 const uchar old_lm_hash_encrypted[16],
705 uchar password_encrypted_with_nt_hash[516],
706 const uchar old_nt_hash_encrypted[16])
709 SAM_ACCOUNT *sampass = NULL;
710 NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash,
711 old_lm_hash_encrypted,
712 password_encrypted_with_nt_hash,
713 old_nt_hash_encrypted,
714 &sampass, new_passwd, sizeof(new_passwd));
716 if (!NT_STATUS_IS_OK(nt_status))
719 /* We've already checked the old password here.... */
721 nt_status = change_oem_password(sampass, NULL, new_passwd, True);
724 memset(new_passwd, 0, sizeof(new_passwd));
726 pdb_free_sam(&sampass);
731 /***********************************************************
732 Decrypt and verify a user password change.
734 The 516 byte long buffers are encrypted with the old NT and
735 old LM passwords, and if the NT passwords are present, both
736 buffers contain a unicode string.
738 After decrypting the buffers, check the password is correct by
739 matching the old hashed passwords with the passwords in the passdb.
741 ************************************************************/
743 static NTSTATUS check_oem_password(const char *user,
744 uchar password_encrypted_with_lm_hash[516],
745 const uchar old_lm_hash_encrypted[16],
746 uchar password_encrypted_with_nt_hash[516],
747 const uchar old_nt_hash_encrypted[16],
748 SAM_ACCOUNT **hnd, char *new_passwd,
751 static uchar null_pw[16];
752 static uchar null_ntpw[16];
753 SAM_ACCOUNT *sampass = NULL;
754 uint8 *password_encrypted;
755 const uint8 *encryption_key;
756 const uint8 *lanman_pw, *nt_pw;
759 uchar new_nt_hash[16];
760 uchar new_lm_hash[16];
765 BOOL nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
766 BOOL lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
770 pdb_init_sam(&sampass);
773 ret = pdb_getsampwnam(sampass, user);
777 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
778 pdb_free_sam(&sampass);
779 return NT_STATUS_NO_SUCH_USER;
782 acct_ctrl = pdb_get_acct_ctrl(sampass);
784 if (acct_ctrl & ACB_DISABLED) {
785 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
786 pdb_free_sam(&sampass);
787 return NT_STATUS_ACCOUNT_DISABLED;
790 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
791 /* construct a null password (in case one is needed */
794 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
799 /* save pointers to passwords so we don't have to keep looking them up */
800 if (lp_lanman_auth()) {
801 lanman_pw = pdb_get_lanman_passwd(sampass);
805 nt_pw = pdb_get_nt_passwd(sampass);
808 if (nt_pw && nt_pass_set) {
809 /* IDEAL Case: passwords are in unicode, and we can
810 * read use the password encrypted with the NT hash
812 password_encrypted = password_encrypted_with_nt_hash;
813 encryption_key = nt_pw;
814 } else if (lanman_pw && lm_pass_set) {
815 /* password may still be in unicode, but use LM hash version */
816 password_encrypted = password_encrypted_with_lm_hash;
817 encryption_key = lanman_pw;
818 } else if (nt_pass_set) {
819 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
821 pdb_free_sam(&sampass);
822 return NT_STATUS_WRONG_PASSWORD;
823 } else if (lm_pass_set) {
824 if (lp_lanman_auth()) {
825 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
828 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
831 pdb_free_sam(&sampass);
832 return NT_STATUS_WRONG_PASSWORD;
834 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
836 pdb_free_sam(&sampass);
837 return NT_STATUS_WRONG_PASSWORD;
841 * Decrypt the password with the key
843 SamOEMhash( password_encrypted, encryption_key, 516);
845 if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len,
846 nt_pass_set ? STR_UNICODE : STR_ASCII)) {
847 pdb_free_sam(&sampass);
848 return NT_STATUS_WRONG_PASSWORD;
852 * To ensure we got the correct new password, hash it and
853 * use it as a key to test the passed old password.
857 /* NT passwords, verify the NT hash. */
859 /* Calculate the MD4 hash (NT compatible) of the password */
860 memset(new_nt_hash, '\0', 16);
861 E_md4hash(new_passwd, new_nt_hash);
865 * check the NT verifier
867 E_old_pw_hash(new_nt_hash, nt_pw, verifier);
868 if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
869 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
870 pdb_free_sam(&sampass);
871 return NT_STATUS_WRONG_PASSWORD;
874 /* We could check the LM password here, but there is
875 * little point, we already know the password is
876 * correct, and the LM password might not even be
879 /* Further, LM hash generation algorithms
880 * differ with charset, so we could
881 * incorrectly fail a perfectly valid password
883 #ifdef DEBUG_PASSWORD
885 ("check_oem_password: password %s ok\n", new_passwd));
893 * check the lm verifier
895 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
896 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
897 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
898 pdb_free_sam(&sampass);
899 return NT_STATUS_WRONG_PASSWORD;
901 #ifdef DEBUG_PASSWORD
903 ("check_oem_password: password %s ok\n", new_passwd));
910 if (lanman_pw && lm_pass_set) {
912 E_deshash(new_passwd, new_lm_hash);
915 * check the lm verifier
917 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
918 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
919 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
920 pdb_free_sam(&sampass);
921 return NT_STATUS_WRONG_PASSWORD;
924 #ifdef DEBUG_PASSWORD
926 ("check_oem_password: password %s ok\n", new_passwd));
932 /* should not be reached */
933 pdb_free_sam(&sampass);
934 return NT_STATUS_WRONG_PASSWORD;
937 /***********************************************************
938 This routine takes the given password and checks it against
939 the password history. Returns True if this password has been
940 found in the history list.
941 ************************************************************/
943 static BOOL check_passwd_history(SAM_ACCOUNT *sampass, const char *plaintext)
945 uchar new_nt_p16[NT_HASH_LEN];
946 uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
948 const uint8 *pwhistory;
951 uint32 pwHisLen, curr_pwHisLen;
953 pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHisLen);
958 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
959 if (!pwhistory || curr_pwHisLen == 0) {
963 /* Only examine the minimum of the current history len and
964 the stored history len. Avoids race conditions. */
965 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
967 nt_pw = pdb_get_nt_passwd(sampass);
969 E_md4hash(plaintext, new_nt_p16);
971 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
972 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
973 pdb_get_username(sampass) ));
977 dump_data(100, (const char *)new_nt_p16, NT_HASH_LEN);
978 dump_data(100, (const char *)pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
980 memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
981 for (i=0; i<pwHisLen; i++) {
982 uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
983 const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
984 const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
985 PW_HISTORY_SALT_LEN];
986 if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
987 /* Ignore zero valued entries. */
990 /* Create salted versions of new to compare. */
991 E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
993 if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
994 DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
995 pdb_get_username(sampass) ));
1003 /***********************************************************
1004 Code to change the oem password. Changes both the lanman
1005 and NT hashes. Old_passwd is almost always NULL.
1006 NOTE this function is designed to be called as root. Check the old password
1007 is correct before calling. JRA.
1008 ************************************************************/
1010 NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
1013 uint32 min_len, min_age;
1014 struct passwd *pass = NULL;
1015 const char *username = pdb_get_username(hnd);
1016 time_t last_change_time = pdb_get_pass_last_set_time(hnd);
1017 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1019 if (pdb_get_account_policy(AP_MIN_PASSWORD_AGE, &min_age)) {
1021 * Windows calculates the minimum password age check
1022 * dynamically, it basically ignores the pwdcanchange
1023 * timestamp. Do likewise.
1025 if (last_change_time + min_age > time(NULL)) {
1026 DEBUG(1, ("user %s cannot change password now, must "
1027 "wait until %s\n", username,
1028 http_timestring(last_change_time+min_age)));
1029 return NT_STATUS_ACCOUNT_RESTRICTION;
1032 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1033 DEBUG(1, ("user %s cannot change password now, must "
1034 "wait until %s\n", username,
1035 http_timestring(can_change_time)));
1036 return NT_STATUS_ACCOUNT_RESTRICTION;
1040 if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1041 DEBUG(1, ("user %s cannot change password - password too short\n",
1043 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1044 return NT_STATUS_PASSWORD_RESTRICTION;
1045 /* return NT_STATUS_PWD_TOO_SHORT; */
1048 if (check_passwd_history(hnd,new_passwd)) {
1049 return NT_STATUS_PASSWORD_RESTRICTION;
1052 pass = Get_Pwnam(username);
1054 DEBUG(1, ("check_oem_password: Username %s does not exist in system !?!\n", username));
1055 return NT_STATUS_ACCESS_DENIED;
1058 /* Use external script to check password complexity */
1059 if (lp_check_password_script() && *(lp_check_password_script())) {
1062 check_ret = smbrunsecret(lp_check_password_script(), new_passwd);
1063 DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret));
1065 if (check_ret != 0) {
1066 DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
1067 return NT_STATUS_PASSWORD_RESTRICTION;
1072 * If unix password sync was requested, attempt to change
1073 * the /etc/passwd database first. Return failure if this cannot
1076 * This occurs before the oem change, because we don't want to
1077 * update it if chgpasswd failed.
1079 * Conditional on lp_unix_password_sync() because we don't want
1080 * to touch the unix db unless we have admin permission.
1083 if(lp_unix_password_sync() &&
1084 !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
1085 return NT_STATUS_ACCESS_DENIED;
1088 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1089 return NT_STATUS_ACCESS_DENIED;
1092 /* Now write it into the file. */
1093 ret = pdb_update_sam_account (hnd);
1096 return NT_STATUS_ACCESS_DENIED;
1099 return NT_STATUS_OK;