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
49 #include "../libcli/auth/libcli_auth.h"
50 #include "../lib/crypto/arcfour.h"
51 #include "rpc_server/srv_samr_util.h"
53 #if ALLOW_CHANGE_PASSWORD
55 static int findpty(char **slave
)
59 SMB_STRUCT_DIR
*dirp
= NULL
;
64 #if defined(HAVE_GRANTPT)
65 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
66 if ((master
= sys_open("/dev/ptmx", O_RDWR
, 0)) >= 0) {
69 line
= (char *)ptsname(master
);
71 *slave
= SMB_STRDUP(line
);
76 ("findpty: Unable to create master/slave pty pair.\n"));
77 /* Stop fd leak on error. */
82 ("findpty: Allocated slave pty %s\n", *slave
));
86 #endif /* HAVE_GRANTPT */
88 line
= SMB_STRDUP("/dev/ptyXX");
93 dirp
= sys_opendir("/dev");
99 while ((dpname
= readdirname(dirp
)) != NULL
) {
100 if (strncmp(dpname
, "pty", 3) == 0 && strlen(dpname
) == 5) {
102 ("pty: try to open %s, line was %s\n", dpname
,
106 if ((master
= sys_open(line
, O_RDWR
, 0)) >= 0) {
107 DEBUG(3, ("pty: opened %s\n", line
));
120 static int dochild(int master
, const char *slavedev
, const struct passwd
*pass
,
121 const char *passwordprogram
, bool as_root
)
124 struct termios stermios
;
127 char * const eptrs
[1] = { NULL
};
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(TIOCSCTTY) && !defined(SUNOS5)
157 * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
158 * see the discussion under
159 * https://bugzilla.samba.org/show_bug.cgi?id=5366.
161 if (ioctl(slave
, TIOCSCTTY
, 0) < 0)
163 DEBUG(3, ("Error in ioctl call for slave pty\n"));
166 #elif defined(I_PUSH) && defined(I_FIND)
167 if (ioctl(slave
, I_FIND
, "ptem") == 0) {
168 ioctl(slave
, I_PUSH
, "ptem");
170 if (ioctl(slave
, I_FIND
, "ldterm") == 0) {
171 ioctl(slave
, I_PUSH
, "ldterm");
178 /* Make slave stdin/out/err of child. */
180 if (dup2(slave
, STDIN_FILENO
) != STDIN_FILENO
)
182 DEBUG(3, ("Could not re-direct stdin\n"));
185 if (dup2(slave
, STDOUT_FILENO
) != STDOUT_FILENO
)
187 DEBUG(3, ("Could not re-direct stdout\n"));
190 if (dup2(slave
, STDERR_FILENO
) != STDERR_FILENO
)
192 DEBUG(3, ("Could not re-direct stderr\n"));
198 /* Set proper terminal attributes - no echo, canonical input processing,
199 no map NL to CR/NL on output. */
201 if (tcgetattr(0, &stermios
) < 0)
204 ("could not read default terminal attributes on pty\n"));
207 stermios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
208 stermios
.c_lflag
|= ICANON
;
210 stermios
.c_oflag
&= ~(ONLCR
);
212 if (tcsetattr(0, TCSANOW
, &stermios
) < 0)
214 DEBUG(3, ("could not set attributes of pty\n"));
218 /* make us completely into the right uid */
221 become_user_permanently(uid
, gid
);
225 ("Invoking '%s' as password change program.\n",
228 /* execl() password-change application */
229 if (execle("/bin/sh", "sh", "-c", passwordprogram
, NULL
, eptrs
) < 0)
231 DEBUG(3, ("Bad status returned from %s\n", passwordprogram
));
237 static int expect(int master
, char *issue
, char *expected
)
240 int attempts
, timeout
, nread
;
244 for (attempts
= 0; attempts
< 2; attempts
++) {
246 if (!strequal(issue
, ".")) {
247 if (lp_passwd_chat_debug())
248 DEBUG(100, ("expect: sending [%s]\n", issue
));
250 if ((len
= sys_write(master
, issue
, strlen(issue
))) != strlen(issue
)) {
251 DEBUG(2,("expect: (short) write returned %d\n",
257 if (strequal(expected
, "."))
260 /* Initial timeout. */
261 timeout
= lp_passwd_chat_timeout() * 1000;
266 status
= read_fd_with_timeout(
267 master
, buffer
+ nread
, 1,
268 sizeof(buffer
) - nread
- 1,
271 if (!NT_STATUS_IS_OK(status
)) {
272 DEBUG(2, ("expect: read error %s\n",
280 /* Eat leading/trailing whitespace before match. */
281 char *str
= SMB_STRDUP(buffer
);
283 DEBUG(2,("expect: ENOMEM\n"));
286 trim_char(str
, ' ', ' ');
288 if ((match
= unix_wild_match(expected
, str
)) == True
) {
289 /* Now data has started to return, lower timeout. */
290 timeout
= lp_passwd_chat_timeout() * 100;
296 if (lp_passwd_chat_debug())
297 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
298 expected
, buffer
, match
? "yes" : "no" ));
303 if (!NT_STATUS_IS_OK(status
)) {
304 DEBUG(2, ("expect: %s\n", nt_errstr(status
)));
309 DEBUG(10,("expect: returning %s\n", match
? "True" : "False" ));
313 static void pwd_sub(char *buf
)
315 all_string_sub(buf
, "\\n", "\n", 0);
316 all_string_sub(buf
, "\\r", "\r", 0);
317 all_string_sub(buf
, "\\s", " ", 0);
318 all_string_sub(buf
, "\\t", "\t", 0);
321 static int talktochild(int master
, const char *seq
)
323 TALLOC_CTX
*frame
= talloc_stackframe();
328 issue
= talloc_strdup(frame
, ".");
334 while (next_token_talloc(frame
, &seq
, &expected
, NULL
)) {
338 if (!expect(master
, issue
, expected
)) {
339 DEBUG(3, ("Response %d incorrect\n", count
));
344 if (!next_token_talloc(frame
, &seq
, &issue
, NULL
)) {
345 issue
= talloc_strdup(frame
, ".");
354 if (!strequal(issue
, ".")) {
355 /* we have one final issue to send */
356 expected
= talloc_strdup(frame
, ".");
361 if (!expect(master
, issue
, expected
)) {
370 static bool chat_with_program(char *passwordprogram
, const struct passwd
*pass
,
371 char *chatsequence
, bool as_root
)
373 char *slavedev
= NULL
;
380 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
384 /* allocate a pseudo-terminal device */
385 if ((master
= findpty(&slavedev
)) < 0) {
386 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass
->pw_name
));
391 * We need to temporarily stop CatchChild from eating
392 * SIGCLD signals as it also eats the exit status code. JRA.
395 CatchChildLeaveStatus();
397 if ((pid
= sys_fork()) < 0) {
398 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass
->pw_name
));
405 /* we now have a pty */
406 if (pid
> 0) { /* This is the parent process */
407 /* Don't need this anymore in parent. */
410 if ((chstat
= talktochild(master
, chatsequence
)) == False
) {
411 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass
->pw_name
));
412 kill(pid
, SIGKILL
); /* be sure to end this process */
415 while ((wpid
= sys_waitpid(pid
, &wstat
, 0)) < 0) {
416 if (errno
== EINTR
) {
424 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
431 * Go back to ignoring children.
438 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
441 if (WIFEXITED(wstat
) && (WEXITSTATUS(wstat
) != 0)) {
442 DEBUG(3, ("chat_with_program: The process exited with status %d \
443 while we were waiting\n", WEXITSTATUS(wstat
)));
446 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
447 else if (WIFSIGNALLED(wstat
)) {
448 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
449 while we were waiting\n", WTERMSIG(wstat
)));
457 * Lose any elevated privileges.
459 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY
);
460 drop_effective_capability(DMAPI_ACCESS_CAPABILITY
);
462 /* make sure it doesn't freeze */
468 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass
->pw_name
,
469 (int)getuid(), (int)getgid(), BOOLSTR(as_root
) ));
470 chstat
= dochild(master
, slavedev
, pass
, passwordprogram
, as_root
);
476 * The child should never return from dochild() ....
479 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat
));
484 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
485 (chstat
? "" : "un"), pass
->pw_name
));
489 bool chgpasswd(const char *name
, const char *rhost
, const struct passwd
*pass
,
490 const char *oldpass
, const char *newpass
, bool as_root
)
492 char *passwordprogram
= NULL
;
493 char *chatsequence
= NULL
;
496 TALLOC_CTX
*ctx
= talloc_tos();
502 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root
), name
));
504 #ifdef DEBUG_PASSWORD
505 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass
, newpass
));
508 /* Take the passed information and test it for minimum criteria */
510 /* Password is same as old password */
511 if (strcmp(oldpass
, newpass
) == 0) {
512 /* don't allow same password */
513 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name
)); /* log the attempt */
514 return (False
); /* inform the user */
518 * Check the old and new passwords don't contain any control
522 len
= strlen(oldpass
);
523 for (i
= 0; i
< len
; i
++) {
524 if (iscntrl((int)oldpass
[i
])) {
525 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
530 len
= strlen(newpass
);
531 for (i
= 0; i
< len
; i
++) {
532 if (iscntrl((int)newpass
[i
])) {
533 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
539 if (lp_pam_password_change()) {
541 #ifdef HAVE_SETLOCALE
542 const char *prevlocale
= setlocale(LC_ALL
, "C");
549 ret
= smb_pam_passchange(pass
->pw_name
, rhost
,
552 ret
= smb_pam_passchange(name
, rhost
, oldpass
,
559 #ifdef HAVE_SETLOCALE
560 setlocale(LC_ALL
, prevlocale
);
567 /* A non-PAM password change just doen't make sense without a valid local user */
570 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name
));
574 passwordprogram
= talloc_strdup(ctx
, lp_passwd_program());
575 if (!passwordprogram
|| !*passwordprogram
) {
576 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
579 chatsequence
= talloc_strdup(ctx
, lp_passwd_chat());
580 if (!chatsequence
|| !*chatsequence
) {
581 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
586 /* The password program *must* contain the user name to work. Fail if not. */
587 if (strstr_m(passwordprogram
, "%u") == NULL
) {
588 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
589 the string %%u, and the given string %s does not.\n", passwordprogram
));
594 passwordprogram
= talloc_string_sub(ctx
, passwordprogram
, "%u", name
);
595 if (!passwordprogram
) {
599 /* note that we do NOT substitute the %o and %n in the password program
600 as this would open up a security hole where the user could use
601 a new password containing shell escape characters */
603 chatsequence
= talloc_string_sub(ctx
, chatsequence
, "%u", name
);
607 chatsequence
= talloc_all_string_sub(ctx
,
614 chatsequence
= talloc_all_string_sub(ctx
,
618 return chat_with_program(passwordprogram
,
624 #else /* ALLOW_CHANGE_PASSWORD */
626 bool chgpasswd(const char *name
, const struct passwd
*pass
,
627 const char *oldpass
, const char *newpass
, bool as_root
)
629 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name
));
632 #endif /* ALLOW_CHANGE_PASSWORD */
634 /***********************************************************
635 Decrypt and verify a user password change.
637 The 516 byte long buffers are encrypted with the old NT and
638 old LM passwords, and if the NT passwords are present, both
639 buffers contain a unicode string.
641 After decrypting the buffers, check the password is correct by
642 matching the old hashed passwords with the passwords in the passdb.
644 ************************************************************/
646 static NTSTATUS
check_oem_password(const char *user
,
647 uchar password_encrypted_with_lm_hash
[516],
648 const uchar old_lm_hash_encrypted
[16],
649 uchar password_encrypted_with_nt_hash
[516],
650 const uchar old_nt_hash_encrypted
[16],
651 struct samu
*sampass
,
652 char **pp_new_passwd
)
656 uint8
*password_encrypted
;
657 const uint8
*encryption_key
;
658 const uint8
*lanman_pw
, *nt_pw
;
661 uchar new_nt_hash
[16];
662 uchar new_lm_hash
[16];
666 bool nt_pass_set
= (password_encrypted_with_nt_hash
&& old_nt_hash_encrypted
);
667 bool lm_pass_set
= (password_encrypted_with_lm_hash
&& old_lm_hash_encrypted
);
669 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
671 /* I am convinced this check here is wrong, it is valid to
672 * change a password of a user that has a disabled account - gd */
674 if (acct_ctrl
& ACB_DISABLED
) {
675 DEBUG(2,("check_lanman_password: account %s disabled.\n", user
));
676 return NT_STATUS_ACCOUNT_DISABLED
;
679 if ((acct_ctrl
& ACB_PWNOTREQ
) && lp_null_passwords()) {
680 /* construct a null password (in case one is needed */
683 nt_lm_owf_gen(no_pw
, null_ntpw
, null_pw
);
688 /* save pointers to passwords so we don't have to keep looking them up */
689 if (lp_lanman_auth()) {
690 lanman_pw
= pdb_get_lanman_passwd(sampass
);
694 nt_pw
= pdb_get_nt_passwd(sampass
);
697 if (nt_pw
&& nt_pass_set
) {
698 /* IDEAL Case: passwords are in unicode, and we can
699 * read use the password encrypted with the NT hash
701 password_encrypted
= password_encrypted_with_nt_hash
;
702 encryption_key
= nt_pw
;
703 } else if (lanman_pw
&& lm_pass_set
) {
704 /* password may still be in unicode, but use LM hash version */
705 password_encrypted
= password_encrypted_with_lm_hash
;
706 encryption_key
= lanman_pw
;
707 } else if (nt_pass_set
) {
708 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
710 return NT_STATUS_WRONG_PASSWORD
;
711 } else if (lm_pass_set
) {
712 if (lp_lanman_auth()) {
713 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
716 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
719 return NT_STATUS_WRONG_PASSWORD
;
721 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
723 return NT_STATUS_WRONG_PASSWORD
;
727 * Decrypt the password with the key
729 arcfour_crypt( password_encrypted
, encryption_key
, 516);
731 if (!decode_pw_buffer(talloc_tos(),
735 nt_pass_set
? CH_UTF16
: CH_DOS
)) {
736 return NT_STATUS_WRONG_PASSWORD
;
740 * To ensure we got the correct new password, hash it and
741 * use it as a key to test the passed old password.
745 /* NT passwords, verify the NT hash. */
747 /* Calculate the MD4 hash (NT compatible) of the password */
748 memset(new_nt_hash
, '\0', 16);
749 E_md4hash(*pp_new_passwd
, new_nt_hash
);
753 * check the NT verifier
755 E_old_pw_hash(new_nt_hash
, nt_pw
, verifier
);
756 if (memcmp(verifier
, old_nt_hash_encrypted
, 16)) {
757 DEBUG(0, ("check_oem_password: old nt "
758 "password doesn't match.\n"));
759 return NT_STATUS_WRONG_PASSWORD
;
762 /* We could check the LM password here, but there is
763 * little point, we already know the password is
764 * correct, and the LM password might not even be
767 /* Further, LM hash generation algorithms
768 * differ with charset, so we could
769 * incorrectly fail a perfectly valid password
771 #ifdef DEBUG_PASSWORD
773 ("check_oem_password: password %s ok\n", *pp_new_passwd
));
780 * check the lm verifier
782 E_old_pw_hash(new_nt_hash
, lanman_pw
, verifier
);
783 if (memcmp(verifier
, old_lm_hash_encrypted
, 16)) {
784 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
785 return NT_STATUS_WRONG_PASSWORD
;
787 #ifdef DEBUG_PASSWORD
789 ("check_oem_password: password %s ok\n", *pp_new_passwd
));
795 if (lanman_pw
&& lm_pass_set
) {
797 E_deshash(*pp_new_passwd
, new_lm_hash
);
800 * check the lm verifier
802 E_old_pw_hash(new_lm_hash
, lanman_pw
, verifier
);
803 if (memcmp(verifier
, old_lm_hash_encrypted
, 16)) {
804 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
805 return NT_STATUS_WRONG_PASSWORD
;
808 #ifdef DEBUG_PASSWORD
810 ("check_oem_password: password %s ok\n", *pp_new_passwd
));
815 /* should not be reached */
816 return NT_STATUS_WRONG_PASSWORD
;
819 static bool password_in_history(uint8_t nt_pw
[NT_HASH_LEN
],
820 uint32_t pw_history_len
,
821 const uint8_t *pw_history
)
823 static const uint8_t zero_md5_nt_pw
[SALTED_MD5_HASH_LEN
] = { 0, };
826 dump_data(100, nt_pw
, NT_HASH_LEN
);
827 dump_data(100, pw_history
, PW_HISTORY_ENTRY_LEN
* pw_history_len
);
829 for (i
=0; i
<pw_history_len
; i
++) {
830 uint8_t new_nt_pw_salted_md5_hash
[SALTED_MD5_HASH_LEN
];
831 const uint8_t *current_salt
;
832 const uint8_t *old_nt_pw_salted_md5_hash
;
834 current_salt
= &pw_history
[i
*PW_HISTORY_ENTRY_LEN
];
835 old_nt_pw_salted_md5_hash
= current_salt
+ PW_HISTORY_SALT_LEN
;
837 if (memcmp(zero_md5_nt_pw
, old_nt_pw_salted_md5_hash
,
838 SALTED_MD5_HASH_LEN
) == 0) {
839 /* Ignore zero valued entries. */
843 if (memcmp(zero_md5_nt_pw
, current_salt
,
844 PW_HISTORY_SALT_LEN
) == 0)
847 * New format: zero salt and then plain nt hash.
848 * Directly compare the hashes.
850 if (memcmp(nt_pw
, old_nt_pw_salted_md5_hash
,
851 SALTED_MD5_HASH_LEN
) == 0)
857 * Old format: md5sum of salted nt hash.
858 * Create salted version of new pw to compare.
860 E_md5hash(current_salt
, nt_pw
, new_nt_pw_salted_md5_hash
);
862 if (memcmp(new_nt_pw_salted_md5_hash
,
863 old_nt_pw_salted_md5_hash
,
864 SALTED_MD5_HASH_LEN
) == 0) {
872 /***********************************************************
873 This routine takes the given password and checks it against
874 the password history. Returns True if this password has been
875 found in the history list.
876 ************************************************************/
878 static bool check_passwd_history(struct samu
*sampass
, const char *plaintext
)
880 uchar new_nt_p16
[NT_HASH_LEN
];
882 const uint8
*pwhistory
;
883 uint32 pwHisLen
, curr_pwHisLen
;
885 pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY
, &pwHisLen
);
890 pwhistory
= pdb_get_pw_history(sampass
, &curr_pwHisLen
);
891 if (!pwhistory
|| curr_pwHisLen
== 0) {
895 /* Only examine the minimum of the current history len and
896 the stored history len. Avoids race conditions. */
897 pwHisLen
= MIN(pwHisLen
,curr_pwHisLen
);
899 nt_pw
= pdb_get_nt_passwd(sampass
);
901 E_md4hash(plaintext
, new_nt_p16
);
903 if (!memcmp(nt_pw
, new_nt_p16
, NT_HASH_LEN
)) {
904 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
905 pdb_get_username(sampass
) ));
909 if (password_in_history(new_nt_p16
, pwHisLen
, pwhistory
)) {
910 DEBUG(1,("check_passwd_history: proposed new password for "
911 "user %s found in history list !\n",
912 pdb_get_username(sampass
) ));
918 /***********************************************************
919 ************************************************************/
921 NTSTATUS
check_password_complexity(const char *username
,
922 const char *password
,
923 enum samPwdChangeReason
*samr_reject_reason
)
925 TALLOC_CTX
*tosctx
= talloc_tos();
929 /* Use external script to check password complexity */
930 if ((lp_check_password_script() == NULL
)
931 || (*(lp_check_password_script()) == '\0')) {
935 cmd
= talloc_string_sub(tosctx
, lp_check_password_script(), "%u",
938 return NT_STATUS_PASSWORD_RESTRICTION
;
941 check_ret
= smbrunsecret(cmd
, password
);
942 DEBUG(5,("check_password_complexity: check password script (%s) "
943 "returned [%d]\n", cmd
, check_ret
));
946 if (check_ret
!= 0) {
947 DEBUG(1,("check_password_complexity: "
948 "check password script said new password is not good "
950 if (samr_reject_reason
) {
951 *samr_reject_reason
= SAM_PWD_CHANGE_NOT_COMPLEX
;
953 return NT_STATUS_PASSWORD_RESTRICTION
;
959 /***********************************************************
960 Code to change the oem password. Changes both the lanman
961 and NT hashes. Old_passwd is almost always NULL.
962 NOTE this function is designed to be called as root. Check the old password
963 is correct before calling. JRA.
964 ************************************************************/
966 static NTSTATUS
change_oem_password(struct samu
*hnd
, const char *rhost
,
967 char *old_passwd
, char *new_passwd
,
969 enum samPwdChangeReason
*samr_reject_reason
)
973 TALLOC_CTX
*tosctx
= talloc_tos();
974 struct passwd
*pass
= NULL
;
975 const char *username
= pdb_get_username(hnd
);
976 time_t can_change_time
= pdb_get_pass_can_change_time(hnd
);
979 if (samr_reject_reason
) {
980 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
983 /* check to see if the secdesc has previously been set to disallow */
984 if (!pdb_get_pass_can_change(hnd
)) {
985 DEBUG(1, ("user %s does not have permissions to change password\n", username
));
986 if (samr_reject_reason
) {
987 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
989 return NT_STATUS_ACCOUNT_RESTRICTION
;
992 /* check to see if it is a Machine account and if the policy
993 * denies machines to change the password. *
994 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
995 if (pdb_get_acct_ctrl(hnd
) & ACB_WSTRUST
) {
996 if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE
, &refuse
) && refuse
) {
997 DEBUG(1, ("Machine %s cannot change password now, "
998 "denied by Refuse Machine Password Change policy\n",
1000 if (samr_reject_reason
) {
1001 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
1003 return NT_STATUS_ACCOUNT_RESTRICTION
;
1007 /* removed calculation here, because passdb now calculates
1008 based on policy. jmcd */
1009 if ((can_change_time
!= 0) && (time(NULL
) < can_change_time
)) {
1010 DEBUG(1, ("user %s cannot change password now, must "
1011 "wait until %s\n", username
,
1012 http_timestring(tosctx
, can_change_time
)));
1013 if (samr_reject_reason
) {
1014 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
1016 return NT_STATUS_ACCOUNT_RESTRICTION
;
1019 if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN
, &min_len
) && (str_charnum(new_passwd
) < min_len
)) {
1020 DEBUG(1, ("user %s cannot change password - password too short\n",
1022 DEBUGADD(1, (" account policy min password len = %d\n", min_len
));
1023 if (samr_reject_reason
) {
1024 *samr_reject_reason
= SAM_PWD_CHANGE_PASSWORD_TOO_SHORT
;
1026 return NT_STATUS_PASSWORD_RESTRICTION
;
1027 /* return NT_STATUS_PWD_TOO_SHORT; */
1030 if (check_passwd_history(hnd
,new_passwd
)) {
1031 if (samr_reject_reason
) {
1032 *samr_reject_reason
= SAM_PWD_CHANGE_PWD_IN_HISTORY
;
1034 return NT_STATUS_PASSWORD_RESTRICTION
;
1037 pass
= Get_Pwnam_alloc(tosctx
, username
);
1039 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username
));
1040 return NT_STATUS_ACCESS_DENIED
;
1043 status
= check_password_complexity(username
, new_passwd
, samr_reject_reason
);
1044 if (!NT_STATUS_IS_OK(status
)) {
1050 * If unix password sync was requested, attempt to change
1051 * the /etc/passwd database first. Return failure if this cannot
1054 * This occurs before the oem change, because we don't want to
1055 * update it if chgpasswd failed.
1057 * Conditional on lp_unix_password_sync() because we don't want
1058 * to touch the unix db unless we have admin permission.
1061 if(lp_unix_password_sync() &&
1062 !chgpasswd(username
, rhost
, pass
, old_passwd
, new_passwd
,
1065 return NT_STATUS_ACCESS_DENIED
;
1070 if (!pdb_set_plaintext_passwd (hnd
, new_passwd
)) {
1071 return NT_STATUS_ACCESS_DENIED
;
1074 /* Now write it into the file. */
1075 return pdb_update_sam_account (hnd
);
1078 /***********************************************************
1079 Code to check and change the OEM hashed password.
1080 ************************************************************/
1082 NTSTATUS
pass_oem_change(char *user
, const char *rhost
,
1083 uchar password_encrypted_with_lm_hash
[516],
1084 const uchar old_lm_hash_encrypted
[16],
1085 uchar password_encrypted_with_nt_hash
[516],
1086 const uchar old_nt_hash_encrypted
[16],
1087 enum samPwdChangeReason
*reject_reason
)
1089 char *new_passwd
= NULL
;
1090 struct samu
*sampass
= NULL
;
1094 if (!(sampass
= samu_new(NULL
))) {
1095 return NT_STATUS_NO_MEMORY
;
1099 ret
= pdb_getsampwnam(sampass
, user
);
1103 DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
1104 TALLOC_FREE(sampass
);
1105 return NT_STATUS_NO_SUCH_USER
;
1108 nt_status
= check_oem_password(user
,
1109 password_encrypted_with_lm_hash
,
1110 old_lm_hash_encrypted
,
1111 password_encrypted_with_nt_hash
,
1112 old_nt_hash_encrypted
,
1116 if (!NT_STATUS_IS_OK(nt_status
)) {
1117 TALLOC_FREE(sampass
);
1121 /* We've already checked the old password here.... */
1123 nt_status
= change_oem_password(sampass
, rhost
, NULL
, new_passwd
,
1124 True
, reject_reason
);
1127 memset(new_passwd
, 0, strlen(new_passwd
));
1129 TALLOC_FREE(sampass
);