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
));
157 ioctl(slave
, I_PUSH
, "ptem");
158 ioctl(slave
, I_PUSH
, "ldterm");
159 #elif defined(TIOCSCTTY)
160 if (ioctl(slave
, TIOCSCTTY
, 0) < 0)
162 DEBUG(3, ("Error in ioctl call for slave pty\n"));
170 /* Make slave stdin/out/err of child. */
172 if (sys_dup2(slave
, STDIN_FILENO
) != STDIN_FILENO
)
174 DEBUG(3, ("Could not re-direct stdin\n"));
177 if (sys_dup2(slave
, STDOUT_FILENO
) != STDOUT_FILENO
)
179 DEBUG(3, ("Could not re-direct stdout\n"));
182 if (sys_dup2(slave
, STDERR_FILENO
) != STDERR_FILENO
)
184 DEBUG(3, ("Could not re-direct stderr\n"));
190 /* Set proper terminal attributes - no echo, canonical input processing,
191 no map NL to CR/NL on output. */
193 if (tcgetattr(0, &stermios
) < 0)
196 ("could not read default terminal attributes on pty\n"));
199 stermios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
200 stermios
.c_lflag
|= ICANON
;
202 stermios
.c_oflag
&= ~(ONLCR
);
204 if (tcsetattr(0, TCSANOW
, &stermios
) < 0)
206 DEBUG(3, ("could not set attributes of pty\n"));
210 /* make us completely into the right uid */
213 become_user_permanently(uid
, gid
);
217 ("Invoking '%s' as password change program.\n",
220 /* execl() password-change application */
221 if (execl("/bin/sh", "sh", "-c", passwordprogram
, NULL
) < 0)
223 DEBUG(3, ("Bad status returned from %s\n", passwordprogram
));
229 static int expect(int master
, char *issue
, char *expected
)
232 int attempts
, timeout
, nread
, len
;
235 for (attempts
= 0; attempts
< 2; attempts
++) {
236 if (!strequal(issue
, ".")) {
237 if (lp_passwd_chat_debug())
238 DEBUG(100, ("expect: sending [%s]\n", issue
));
240 if ((len
= write(master
, issue
, strlen(issue
))) != strlen(issue
)) {
241 DEBUG(2,("expect: (short) write returned %d\n", len
));
246 if (strequal(expected
, "."))
249 /* Initial timeout. */
250 timeout
= lp_passwd_chat_timeout() * 1000;
254 while ((len
= read_socket_with_timeout(master
, buffer
+ nread
, 1,
255 sizeof(buffer
) - nread
- 1,
261 /* Eat leading/trailing whitespace before match. */
263 pstrcpy( str
, buffer
);
264 trim_char( str
, ' ', ' ');
266 if ((match
= (unix_wild_match(expected
, str
) == 0))) {
267 /* Now data has started to return, lower timeout. */
268 timeout
= lp_passwd_chat_timeout() * 100;
273 if (lp_passwd_chat_debug())
274 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
275 expected
, buffer
, match
? "yes" : "no" ));
281 DEBUG(2, ("expect: %s\n", strerror(errno
)));
286 DEBUG(10,("expect: returning %s\n", match
? "True" : "False" ));
290 static void pwd_sub(char *buf
)
292 all_string_sub(buf
, "\\n", "\n", 0);
293 all_string_sub(buf
, "\\r", "\r", 0);
294 all_string_sub(buf
, "\\s", " ", 0);
295 all_string_sub(buf
, "\\t", "\t", 0);
298 static int talktochild(int master
, const char *seq
)
301 fstring issue
, expected
;
305 while (next_token(&seq
, expected
, NULL
, sizeof(expected
)))
310 if (!expect(master
, issue
, expected
))
312 DEBUG(3, ("Response %d incorrect\n", count
));
316 if (!next_token(&seq
, issue
, NULL
, sizeof(issue
)))
321 if (!strequal(issue
, ".")) {
322 /* we have one final issue to send */
323 fstrcpy(expected
, ".");
324 if (!expect(master
, issue
, expected
))
331 static BOOL
chat_with_program(char *passwordprogram
, const struct passwd
*pass
,
332 char *chatsequence
, BOOL as_root
)
341 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
345 /* allocate a pseudo-terminal device */
346 if ((master
= findpty(&slavedev
)) < 0) {
347 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass
->pw_name
));
352 * We need to temporarily stop CatchChild from eating
353 * SIGCLD signals as it also eats the exit status code. JRA.
356 CatchChildLeaveStatus();
358 if ((pid
= sys_fork()) < 0) {
359 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass
->pw_name
));
365 /* we now have a pty */
366 if (pid
> 0) { /* This is the parent process */
367 if ((chstat
= talktochild(master
, chatsequence
)) == False
) {
368 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass
->pw_name
));
369 kill(pid
, SIGKILL
); /* be sure to end this process */
372 while ((wpid
= sys_waitpid(pid
, &wstat
, 0)) < 0) {
373 if (errno
== EINTR
) {
381 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
388 * Go back to ignoring children.
395 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
398 if (WIFEXITED(wstat
) && (WEXITSTATUS(wstat
) != 0)) {
399 DEBUG(3, ("chat_with_program: The process exited with status %d \
400 while we were waiting\n", WEXITSTATUS(wstat
)));
403 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
404 else if (WIFSIGNALLED(wstat
)) {
405 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
406 while we were waiting\n", WTERMSIG(wstat
)));
414 * Lose any oplock capabilities.
416 oplock_set_capability(False
, False
);
418 /* make sure it doesn't freeze */
424 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass
->pw_name
,
425 (int)getuid(), (int)getgid(), BOOLSTR(as_root
) ));
426 chstat
= dochild(master
, slavedev
, pass
, passwordprogram
, as_root
);
432 * The child should never return from dochild() ....
435 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat
));
440 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
441 (chstat
? "" : "un"), pass
->pw_name
));
445 BOOL
chgpasswd(const char *name
, const struct passwd
*pass
,
446 const char *oldpass
, const char *newpass
, BOOL as_root
)
448 pstring passwordprogram
;
449 pstring chatsequence
;
457 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root
), name
));
459 #ifdef DEBUG_PASSWORD
460 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass
, newpass
));
463 /* Take the passed information and test it for minimum criteria */
465 /* Password is same as old password */
466 if (strcmp(oldpass
, newpass
) == 0) {
467 /* don't allow same password */
468 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name
)); /* log the attempt */
469 return (False
); /* inform the user */
473 * Check the old and new passwords don't contain any control
477 len
= strlen(oldpass
);
478 for (i
= 0; i
< len
; i
++) {
479 if (iscntrl((int)oldpass
[i
])) {
480 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
485 len
= strlen(newpass
);
486 for (i
= 0; i
< len
; i
++) {
487 if (iscntrl((int)newpass
[i
])) {
488 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
494 if (lp_pam_password_change()) {
501 ret
= smb_pam_passchange(pass
->pw_name
, oldpass
, newpass
);
503 ret
= smb_pam_passchange(name
, oldpass
, newpass
);
513 /* A non-PAM password change just doen't make sense without a valid local user */
516 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name
));
520 pstrcpy(passwordprogram
, lp_passwd_program());
521 pstrcpy(chatsequence
, lp_passwd_chat());
523 if (!*chatsequence
) {
524 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
528 if (!*passwordprogram
) {
529 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
534 /* The password program *must* contain the user name to work. Fail if not. */
535 if (strstr_m(passwordprogram
, "%u") == NULL
) {
536 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
537 the string %%u, and the given string %s does not.\n", passwordprogram
));
542 pstring_sub(passwordprogram
, "%u", name
);
543 /* note that we do NOT substitute the %o and %n in the password program
544 as this would open up a security hole where the user could use
545 a new password containing shell escape characters */
547 pstring_sub(chatsequence
, "%u", name
);
548 all_string_sub(chatsequence
, "%o", oldpass
, sizeof(pstring
));
549 all_string_sub(chatsequence
, "%n", newpass
, sizeof(pstring
));
550 return (chat_with_program
551 (passwordprogram
, pass
, chatsequence
, as_root
));
554 #else /* ALLOW_CHANGE_PASSWORD */
556 BOOL
chgpasswd(const char *name
, const struct passwd
*pass
,
557 const char *oldpass
, const char *newpass
, BOOL as_root
)
559 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name
));
562 #endif /* ALLOW_CHANGE_PASSWORD */
564 /***********************************************************
565 Code to check the lanman hashed password.
566 ************************************************************/
568 BOOL
check_lanman_password(char *user
, uchar
* pass1
,
569 uchar
* pass2
, SAM_ACCOUNT
**hnd
)
571 uchar unenc_new_pw
[16];
572 uchar unenc_old_pw
[16];
573 SAM_ACCOUNT
*sampass
= NULL
;
575 const uint8
*lanman_pw
;
579 ret
= pdb_getsampwnam(sampass
, user
);
583 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
584 pdb_free_sam(&sampass
);
588 acct_ctrl
= pdb_get_acct_ctrl (sampass
);
589 lanman_pw
= pdb_get_lanman_passwd (sampass
);
591 if (acct_ctrl
& ACB_DISABLED
) {
592 DEBUG(0,("check_lanman_password: account %s disabled.\n", user
));
593 pdb_free_sam(&sampass
);
597 if (lanman_pw
== NULL
) {
598 if (acct_ctrl
& ACB_PWNOTREQ
) {
599 /* this saves the pointer for the caller */
603 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
604 pdb_free_sam(&sampass
);
609 /* Get the new lanman hash. */
610 D_P16(lanman_pw
, pass2
, unenc_new_pw
);
612 /* Use this to get the old lanman hash. */
613 D_P16(unenc_new_pw
, pass1
, unenc_old_pw
);
615 /* Check that the two old passwords match. */
616 if (memcmp(lanman_pw
, unenc_old_pw
, 16)) {
617 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
618 pdb_free_sam(&sampass
);
622 /* this saves the pointer for the caller */
627 /***********************************************************
628 Code to change the lanman hashed password.
629 It nulls out the NT hashed password as it will
631 NOTE this function is designed to be called as root. Check the old password
632 is correct before calling. JRA.
633 ************************************************************/
635 BOOL
change_lanman_password(SAM_ACCOUNT
*sampass
, uchar
*pass2
)
637 static uchar null_pw
[16];
638 uchar unenc_new_pw
[16];
643 if (sampass
== NULL
) {
644 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
648 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
649 pwd
= pdb_get_lanman_passwd(sampass
);
651 if (acct_ctrl
& ACB_DISABLED
) {
652 DEBUG(0,("change_lanman_password: account %s disabled.\n",
653 pdb_get_username(sampass
)));
658 if (acct_ctrl
& ACB_PWNOTREQ
) {
660 memset(no_pw
, '\0', 14);
661 E_P16(no_pw
, null_pw
);
663 /* Get the new lanman hash. */
664 D_P16(null_pw
, pass2
, unenc_new_pw
);
666 DEBUG(0,("change_lanman_password: no lanman password !\n"));
670 /* Get the new lanman hash. */
671 D_P16(pwd
, pass2
, unenc_new_pw
);
674 if (!pdb_set_lanman_passwd(sampass
, unenc_new_pw
, PDB_CHANGED
)) {
678 if (!pdb_set_nt_passwd (sampass
, NULL
, PDB_CHANGED
)) {
679 return False
; /* We lose the NT hash. Sorry. */
682 if (!pdb_set_pass_changed_now (sampass
)) {
683 pdb_free_sam(&sampass
);
684 /* Not quite sure what this one qualifies as, but this will do */
688 /* Now flush the sam_passwd struct to persistent storage */
689 ret
= pdb_update_sam_account (sampass
);
694 /***********************************************************
695 Code to check and change the OEM hashed password.
696 ************************************************************/
698 NTSTATUS
pass_oem_change(char *user
,
699 uchar password_encrypted_with_lm_hash
[516],
700 const uchar old_lm_hash_encrypted
[16],
701 uchar password_encrypted_with_nt_hash
[516],
702 const uchar old_nt_hash_encrypted
[16])
705 SAM_ACCOUNT
*sampass
= NULL
;
706 NTSTATUS nt_status
= check_oem_password(user
, password_encrypted_with_lm_hash
,
707 old_lm_hash_encrypted
,
708 password_encrypted_with_nt_hash
,
709 old_nt_hash_encrypted
,
710 &sampass
, new_passwd
, sizeof(new_passwd
));
712 if (!NT_STATUS_IS_OK(nt_status
))
715 /* We've already checked the old password here.... */
717 nt_status
= change_oem_password(sampass
, NULL
, new_passwd
, True
);
720 memset(new_passwd
, 0, sizeof(new_passwd
));
722 pdb_free_sam(&sampass
);
727 /***********************************************************
728 Decrypt and verify a user password change.
730 The 516 byte long buffers are encrypted with the old NT and
731 old LM passwords, and if the NT passwords are present, both
732 buffers contain a unicode string.
734 After decrypting the buffers, check the password is correct by
735 matching the old hashed passwords with the passwords in the passdb.
737 ************************************************************/
739 static NTSTATUS
check_oem_password(const char *user
,
740 uchar password_encrypted_with_lm_hash
[516],
741 const uchar old_lm_hash_encrypted
[16],
742 uchar password_encrypted_with_nt_hash
[516],
743 const uchar old_nt_hash_encrypted
[16],
744 SAM_ACCOUNT
**hnd
, char *new_passwd
,
747 static uchar null_pw
[16];
748 static uchar null_ntpw
[16];
749 SAM_ACCOUNT
*sampass
= NULL
;
750 uint8
*password_encrypted
;
751 const uint8
*encryption_key
;
752 const uint8
*lanman_pw
, *nt_pw
;
755 uchar new_nt_hash
[16];
756 uchar new_lm_hash
[16];
761 BOOL nt_pass_set
= (password_encrypted_with_nt_hash
&& old_nt_hash_encrypted
);
762 BOOL lm_pass_set
= (password_encrypted_with_lm_hash
&& old_lm_hash_encrypted
);
766 pdb_init_sam(&sampass
);
769 ret
= pdb_getsampwnam(sampass
, user
);
773 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
774 pdb_free_sam(&sampass
);
775 return NT_STATUS_NO_SUCH_USER
;
778 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
780 if (acct_ctrl
& ACB_DISABLED
) {
781 DEBUG(2,("check_lanman_password: account %s disabled.\n", user
));
782 pdb_free_sam(&sampass
);
783 return NT_STATUS_ACCOUNT_DISABLED
;
786 if ((acct_ctrl
& ACB_PWNOTREQ
) && lp_null_passwords()) {
787 /* construct a null password (in case one is needed */
790 nt_lm_owf_gen(no_pw
, null_ntpw
, null_pw
);
795 /* save pointers to passwords so we don't have to keep looking them up */
796 if (lp_lanman_auth()) {
797 lanman_pw
= pdb_get_lanman_passwd(sampass
);
801 nt_pw
= pdb_get_nt_passwd(sampass
);
804 if (nt_pw
&& nt_pass_set
) {
805 /* IDEAL Case: passwords are in unicode, and we can
806 * read use the password encrypted with the NT hash
808 password_encrypted
= password_encrypted_with_nt_hash
;
809 encryption_key
= nt_pw
;
810 } else if (lanman_pw
&& lm_pass_set
) {
811 /* password may still be in unicode, but use LM hash version */
812 password_encrypted
= password_encrypted_with_lm_hash
;
813 encryption_key
= lanman_pw
;
814 } else if (nt_pass_set
) {
815 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
817 pdb_free_sam(&sampass
);
818 return NT_STATUS_WRONG_PASSWORD
;
819 } else if (lm_pass_set
) {
820 if (lp_lanman_auth()) {
821 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
824 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
827 pdb_free_sam(&sampass
);
828 return NT_STATUS_WRONG_PASSWORD
;
830 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
832 pdb_free_sam(&sampass
);
833 return NT_STATUS_WRONG_PASSWORD
;
837 * Decrypt the password with the key
839 SamOEMhash( password_encrypted
, encryption_key
, 516);
841 if ( !decode_pw_buffer(password_encrypted
, new_passwd
, new_passwd_size
, &new_pw_len
,
842 nt_pass_set
? STR_UNICODE
: STR_ASCII
)) {
843 pdb_free_sam(&sampass
);
844 return NT_STATUS_WRONG_PASSWORD
;
848 * To ensure we got the correct new password, hash it and
849 * use it as a key to test the passed old password.
853 /* NT passwords, verify the NT hash. */
855 /* Calculate the MD4 hash (NT compatible) of the password */
856 memset(new_nt_hash
, '\0', 16);
857 E_md4hash(new_passwd
, new_nt_hash
);
861 * check the NT verifier
863 E_old_pw_hash(new_nt_hash
, nt_pw
, verifier
);
864 if (memcmp(verifier
, old_nt_hash_encrypted
, 16)) {
865 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
866 pdb_free_sam(&sampass
);
867 return NT_STATUS_WRONG_PASSWORD
;
870 /* We could check the LM password here, but there is
871 * little point, we already know the password is
872 * correct, and the LM password might not even be
875 /* Further, LM hash generation algorithms
876 * differ with charset, so we could
877 * incorrectly fail a perfectly valid password
879 #ifdef DEBUG_PASSWORD
881 ("check_oem_password: password %s ok\n", new_passwd
));
889 * check the lm verifier
891 E_old_pw_hash(new_nt_hash
, lanman_pw
, verifier
);
892 if (memcmp(verifier
, old_lm_hash_encrypted
, 16)) {
893 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
894 pdb_free_sam(&sampass
);
895 return NT_STATUS_WRONG_PASSWORD
;
897 #ifdef DEBUG_PASSWORD
899 ("check_oem_password: password %s ok\n", new_passwd
));
906 if (lanman_pw
&& lm_pass_set
) {
908 E_deshash(new_passwd
, new_lm_hash
);
911 * check the lm verifier
913 E_old_pw_hash(new_lm_hash
, lanman_pw
, verifier
);
914 if (memcmp(verifier
, old_lm_hash_encrypted
, 16)) {
915 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
916 pdb_free_sam(&sampass
);
917 return NT_STATUS_WRONG_PASSWORD
;
920 #ifdef DEBUG_PASSWORD
922 ("check_oem_password: password %s ok\n", new_passwd
));
928 /* should not be reached */
929 pdb_free_sam(&sampass
);
930 return NT_STATUS_WRONG_PASSWORD
;
933 /***********************************************************
934 This routine takes the given password and checks it against
935 the password history. Returns True if this password has been
936 found in the history list.
937 ************************************************************/
939 static BOOL
check_passwd_history(SAM_ACCOUNT
*sampass
, const char *plaintext
)
941 uchar new_nt_p16
[NT_HASH_LEN
];
942 uchar zero_md5_nt_pw
[SALTED_MD5_HASH_LEN
];
944 const uint8
*pwhistory
;
947 uint32 pwHisLen
, curr_pwHisLen
;
949 account_policy_get(AP_PASSWORD_HISTORY
, &pwHisLen
);
954 pwhistory
= pdb_get_pw_history(sampass
, &curr_pwHisLen
);
955 if (!pwhistory
|| curr_pwHisLen
== 0) {
959 /* Only examine the minimum of the current history len and
960 the stored history len. Avoids race conditions. */
961 pwHisLen
= MIN(pwHisLen
,curr_pwHisLen
);
963 nt_pw
= pdb_get_nt_passwd(sampass
);
965 E_md4hash(plaintext
, new_nt_p16
);
967 if (!memcmp(nt_pw
, new_nt_p16
, NT_HASH_LEN
)) {
968 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
969 pdb_get_username(sampass
) ));
973 dump_data(100, (const char *)new_nt_p16
, NT_HASH_LEN
);
974 dump_data(100, (const char *)pwhistory
, PW_HISTORY_ENTRY_LEN
*pwHisLen
);
976 memset(zero_md5_nt_pw
, '\0', SALTED_MD5_HASH_LEN
);
977 for (i
=0; i
<pwHisLen
; i
++) {
978 uchar new_nt_pw_salted_md5_hash
[SALTED_MD5_HASH_LEN
];
979 const uchar
*current_salt
= &pwhistory
[i
*PW_HISTORY_ENTRY_LEN
];
980 const uchar
*old_nt_pw_salted_md5_hash
= &pwhistory
[(i
*PW_HISTORY_ENTRY_LEN
)+
981 PW_HISTORY_SALT_LEN
];
982 if (!memcmp(zero_md5_nt_pw
, old_nt_pw_salted_md5_hash
, SALTED_MD5_HASH_LEN
)) {
983 /* Ignore zero valued entries. */
986 /* Create salted versions of new to compare. */
987 E_md5hash(current_salt
, new_nt_p16
, new_nt_pw_salted_md5_hash
);
989 if (!memcmp(new_nt_pw_salted_md5_hash
, old_nt_pw_salted_md5_hash
, SALTED_MD5_HASH_LEN
)) {
990 DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
991 pdb_get_username(sampass
) ));
999 /***********************************************************
1000 Code to change the oem password. Changes both the lanman
1001 and NT hashes. Old_passwd is almost always NULL.
1002 NOTE this function is designed to be called as root. Check the old password
1003 is correct before calling. JRA.
1004 ************************************************************/
1006 NTSTATUS
change_oem_password(SAM_ACCOUNT
*hnd
, char *old_passwd
, char *new_passwd
, BOOL as_root
)
1010 struct passwd
*pass
= NULL
;
1011 const char *username
= pdb_get_username(hnd
);
1012 time_t can_change_time
= pdb_get_pass_can_change_time(hnd
);
1014 if ((can_change_time
!= 0) && (time(NULL
) < can_change_time
)) {
1015 DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
1016 username
, http_timestring(can_change_time
)));
1017 return NT_STATUS_ACCOUNT_RESTRICTION
;
1020 if (account_policy_get(AP_MIN_PASSWORD_LEN
, &min_len
) && (str_charnum(new_passwd
) < min_len
)) {
1021 DEBUG(1, ("user %s cannot change password - password too short\n",
1023 DEBUGADD(1, (" account policy min password len = %d\n", min_len
));
1024 return NT_STATUS_PASSWORD_RESTRICTION
;
1025 /* return NT_STATUS_PWD_TOO_SHORT; */
1028 if (check_passwd_history(hnd
,new_passwd
)) {
1029 return NT_STATUS_PASSWORD_RESTRICTION
;
1032 pass
= Get_Pwnam(username
);
1034 DEBUG(1, ("check_oem_password: Username %s does not exist in system !?!\n", username
));
1035 return NT_STATUS_ACCESS_DENIED
;
1038 /* Use external script to check password complexity */
1039 if (lp_check_password_script() && *(lp_check_password_script())) {
1042 check_ret
= smbrunsecret(lp_check_password_script(), new_passwd
);
1043 DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret
));
1045 if (check_ret
!= 0) {
1046 DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
1047 return NT_STATUS_PASSWORD_RESTRICTION
;
1052 * If unix password sync was requested, attempt to change
1053 * the /etc/passwd database first. Return failure if this cannot
1056 * This occurs before the oem change, because we don't want to
1057 * update it if chgpasswd failed.
1059 * Conditional on lp_unix_password_sync() because we don't want
1060 * to touch the unix db unless we have admin permission.
1063 if(lp_unix_password_sync() &&
1064 !chgpasswd(username
, pass
, old_passwd
, new_passwd
, as_root
)) {
1065 return NT_STATUS_ACCESS_DENIED
;
1068 if (!pdb_set_plaintext_passwd (hnd
, new_passwd
)) {
1069 return NT_STATUS_ACCESS_DENIED
;
1072 /* Now write it into the file. */
1073 ret
= pdb_update_sam_account (hnd
);
1076 return NT_STATUS_ACCESS_DENIED
;
1079 return NT_STATUS_OK
;