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 "system/terminal.h"
50 #include "system/passwd.h"
51 #include "system/filesys.h"
52 #include "../libcli/auth/libcli_auth.h"
53 #include "../lib/crypto/arcfour.h"
54 #include "rpc_server/samr/srv_samr_util.h"
58 #ifndef ALLOW_CHANGE_PASSWORD
59 #if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
60 #define ALLOW_CHANGE_PASSWORD 1
64 #if ALLOW_CHANGE_PASSWORD
66 static int findpty(char **slave
)
75 #if defined(HAVE_GRANTPT)
76 #if defined(HAVE_POSIX_OPENPT)
77 master
= posix_openpt(O_RDWR
|O_NOCTTY
);
79 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
80 master
= open("/dev/ptmx", O_RDWR
, 0);
85 line
= (char *)ptsname(master
);
87 *slave
= SMB_STRDUP(line
);
92 ("findpty: Unable to create master/slave pty pair.\n"));
93 /* Stop fd leak on error. */
98 ("findpty: Allocated slave pty %s\n", *slave
));
102 #endif /* HAVE_GRANTPT */
104 line
= SMB_STRDUP("/dev/ptyXX");
109 dirp
= opendir("/dev");
115 while ((dpname
= readdirname(dirp
)) != NULL
) {
116 if (strncmp(dpname
, "pty", 3) == 0 && strlen(dpname
) == 5) {
118 ("pty: try to open %s, line was %s\n", dpname
,
122 if ((master
= open(line
, O_RDWR
, 0)) >= 0) {
123 DEBUG(3, ("pty: opened %s\n", line
));
136 static int dochild(int master
, const char *slavedev
, const struct passwd
*pass
,
137 const char *passwordprogram
, bool as_root
)
140 struct termios stermios
;
143 char * const eptrs
[1] = { NULL
};
148 ("dochild: user doesn't exist in the UNIX password database.\n"));
155 gain_root_privilege();
157 /* Start new session - gets rid of controlling terminal. */
161 ("Weirdness, couldn't let go of controlling terminal\n"));
165 /* Open slave pty and acquire as new controlling terminal. */
166 if ((slave
= open(slavedev
, O_RDWR
, 0)) < 0)
168 DEBUG(3, ("More weirdness, could not open %s\n", slavedev
));
171 #if defined(TIOCSCTTY) && !defined(SUNOS5)
173 * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
174 * see the discussion under
175 * https://bugzilla.samba.org/show_bug.cgi?id=5366.
177 if (ioctl(slave
, TIOCSCTTY
, 0) < 0)
179 DEBUG(3, ("Error in ioctl call for slave pty\n"));
182 #elif defined(I_PUSH) && defined(I_FIND)
183 if (ioctl(slave
, I_FIND
, "ptem") == 0) {
184 ioctl(slave
, I_PUSH
, "ptem");
186 if (ioctl(slave
, I_FIND
, "ldterm") == 0) {
187 ioctl(slave
, I_PUSH
, "ldterm");
194 /* Make slave stdin/out/err of child. */
196 if (dup2(slave
, STDIN_FILENO
) != STDIN_FILENO
)
198 DEBUG(3, ("Could not re-direct stdin\n"));
201 if (dup2(slave
, STDOUT_FILENO
) != STDOUT_FILENO
)
203 DEBUG(3, ("Could not re-direct stdout\n"));
206 if (dup2(slave
, STDERR_FILENO
) != STDERR_FILENO
)
208 DEBUG(3, ("Could not re-direct stderr\n"));
214 /* Set proper terminal attributes - no echo, canonical input processing,
215 no map NL to CR/NL on output. */
217 if (tcgetattr(0, &stermios
) < 0)
220 ("could not read default terminal attributes on pty\n"));
223 stermios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
224 stermios
.c_lflag
|= ICANON
;
226 stermios
.c_oflag
&= ~(ONLCR
);
228 if (tcsetattr(0, TCSANOW
, &stermios
) < 0)
230 DEBUG(3, ("could not set attributes of pty\n"));
234 /* make us completely into the right uid */
237 become_user_permanently(uid
, gid
);
241 ("Invoking '%s' as password change program.\n",
244 /* execl() password-change application */
245 if (execle("/bin/sh", "sh", "-c", passwordprogram
, NULL
, eptrs
) < 0)
247 DEBUG(3, ("Bad status returned from %s\n", passwordprogram
));
253 static int expect(int master
, char *issue
, char *expected
)
256 int attempts
, timeout
, nread
;
260 for (attempts
= 0; attempts
< 2; attempts
++) {
262 if (!strequal(issue
, ".")) {
263 if (lp_passwd_chat_debug())
264 DEBUG(100, ("expect: sending [%s]\n", issue
));
266 if ((len
= sys_write(master
, issue
, strlen(issue
))) != strlen(issue
)) {
267 DEBUG(2,("expect: (short) write returned %d\n",
273 if (strequal(expected
, "."))
276 /* Initial timeout. */
277 timeout
= lp_passwd_chat_timeout() * 1000;
282 status
= read_fd_with_timeout(
283 master
, buffer
+ nread
, 1,
284 sizeof(buffer
) - nread
- 1,
287 if (!NT_STATUS_IS_OK(status
)) {
288 DEBUG(2, ("expect: read error %s\n",
296 /* Eat leading/trailing whitespace before match. */
297 char *str
= SMB_STRDUP(buffer
);
299 DEBUG(2,("expect: ENOMEM\n"));
302 trim_char(str
, ' ', ' ');
304 if ((match
= unix_wild_match(expected
, str
)) == True
) {
305 /* Now data has started to return, lower timeout. */
306 timeout
= lp_passwd_chat_timeout() * 100;
312 if (lp_passwd_chat_debug())
313 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
314 expected
, buffer
, match
? "yes" : "no" ));
319 if (!NT_STATUS_IS_OK(status
)) {
320 DEBUG(2, ("expect: %s\n", nt_errstr(status
)));
325 DEBUG(10,("expect: returning %s\n", match
? "True" : "False" ));
329 static void pwd_sub(char *buf
)
331 all_string_sub(buf
, "\\n", "\n", 0);
332 all_string_sub(buf
, "\\r", "\r", 0);
333 all_string_sub(buf
, "\\s", " ", 0);
334 all_string_sub(buf
, "\\t", "\t", 0);
337 static int talktochild(int master
, const char *seq
)
339 TALLOC_CTX
*frame
= talloc_stackframe();
344 issue
= talloc_strdup(frame
, ".");
350 while (next_token_talloc(frame
, &seq
, &expected
, NULL
)) {
354 if (!expect(master
, issue
, expected
)) {
355 DEBUG(3, ("Response %d incorrect\n", count
));
360 if (!next_token_talloc(frame
, &seq
, &issue
, NULL
)) {
361 issue
= talloc_strdup(frame
, ".");
370 if (!strequal(issue
, ".")) {
371 /* we have one final issue to send */
372 expected
= talloc_strdup(frame
, ".");
377 if (!expect(master
, issue
, expected
)) {
386 static bool chat_with_program(char *passwordprogram
, const struct passwd
*pass
,
387 char *chatsequence
, bool as_root
)
389 char *slavedev
= NULL
;
396 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
400 /* allocate a pseudo-terminal device */
401 if ((master
= findpty(&slavedev
)) < 0) {
402 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass
->pw_name
));
407 * We need to temporarily stop CatchChild from eating
408 * SIGCLD signals as it also eats the exit status code. JRA.
411 CatchChildLeaveStatus();
413 if ((pid
= fork()) < 0) {
414 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass
->pw_name
));
421 /* we now have a pty */
422 if (pid
> 0) { /* This is the parent process */
423 /* Don't need this anymore in parent. */
426 if ((chstat
= talktochild(master
, chatsequence
)) == False
) {
427 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass
->pw_name
));
428 kill(pid
, SIGKILL
); /* be sure to end this process */
431 while ((wpid
= sys_waitpid(pid
, &wstat
, 0)) < 0) {
432 if (errno
== EINTR
) {
440 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
447 * Go back to ignoring children.
454 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
457 if (WIFEXITED(wstat
) && (WEXITSTATUS(wstat
) != 0)) {
458 DEBUG(3, ("chat_with_program: The process exited with status %d \
459 while we were waiting\n", WEXITSTATUS(wstat
)));
462 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
463 else if (WIFSIGNALLED(wstat
)) {
464 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
465 while we were waiting\n", WTERMSIG(wstat
)));
473 * Lose any elevated privileges.
475 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY
);
476 drop_effective_capability(DMAPI_ACCESS_CAPABILITY
);
478 /* make sure it doesn't freeze */
484 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass
->pw_name
,
485 (int)getuid(), (int)getgid(), BOOLSTR(as_root
) ));
486 chstat
= dochild(master
, slavedev
, pass
, passwordprogram
, as_root
);
492 * The child should never return from dochild() ....
495 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat
));
500 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
501 (chstat
? "" : "un"), pass
->pw_name
));
505 bool chgpasswd(const char *name
, const char *rhost
, const struct passwd
*pass
,
506 const char *oldpass
, const char *newpass
, bool as_root
)
508 char *passwordprogram
= NULL
;
509 char *chatsequence
= NULL
;
512 TALLOC_CTX
*ctx
= talloc_tos();
518 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root
), name
));
520 #ifdef DEBUG_PASSWORD
521 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass
, newpass
));
524 /* Take the passed information and test it for minimum criteria */
526 /* Password is same as old password */
527 if (strcmp(oldpass
, newpass
) == 0) {
528 /* don't allow same password */
529 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name
)); /* log the attempt */
530 return (False
); /* inform the user */
534 * Check the old and new passwords don't contain any control
538 len
= strlen(oldpass
);
539 for (i
= 0; i
< len
; i
++) {
540 if (iscntrl((int)oldpass
[i
])) {
541 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
546 len
= strlen(newpass
);
547 for (i
= 0; i
< len
; i
++) {
548 if (iscntrl((int)newpass
[i
])) {
549 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
555 if (lp_pam_password_change()) {
557 #ifdef HAVE_SETLOCALE
558 const char *prevlocale
= setlocale(LC_ALL
, "C");
565 ret
= smb_pam_passchange(pass
->pw_name
, rhost
,
568 ret
= smb_pam_passchange(name
, rhost
, oldpass
,
575 #ifdef HAVE_SETLOCALE
576 setlocale(LC_ALL
, prevlocale
);
583 /* A non-PAM password change just doen't make sense without a valid local user */
586 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name
));
590 passwordprogram
= lp_passwd_program(ctx
);
591 if (!passwordprogram
|| !*passwordprogram
) {
592 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
595 chatsequence
= lp_passwd_chat(ctx
);
596 if (!chatsequence
|| !*chatsequence
) {
597 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
602 /* The password program *must* contain the user name to work. Fail if not. */
603 if (strstr_m(passwordprogram
, "%u") == NULL
) {
604 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
605 the string %%u, and the given string %s does not.\n", passwordprogram
));
610 passwordprogram
= talloc_string_sub(ctx
, passwordprogram
, "%u", name
);
611 if (!passwordprogram
) {
615 /* note that we do NOT substitute the %o and %n in the password program
616 as this would open up a security hole where the user could use
617 a new password containing shell escape characters */
619 chatsequence
= talloc_string_sub(ctx
, chatsequence
, "%u", name
);
623 chatsequence
= talloc_all_string_sub(ctx
,
630 chatsequence
= talloc_all_string_sub(ctx
,
634 return chat_with_program(passwordprogram
,
640 #else /* ALLOW_CHANGE_PASSWORD */
642 bool chgpasswd(const char *name
, const struct passwd
*pass
,
643 const char *oldpass
, const char *newpass
, bool as_root
)
645 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name
));
648 #endif /* ALLOW_CHANGE_PASSWORD */
650 /***********************************************************
651 Decrypt and verify a user password change.
653 The 516 byte long buffers are encrypted with the old NT and
654 old LM passwords, and if the NT passwords are present, both
655 buffers contain a unicode string.
657 After decrypting the buffers, check the password is correct by
658 matching the old hashed passwords with the passwords in the passdb.
660 ************************************************************/
662 static NTSTATUS
check_oem_password(const char *user
,
663 uchar password_encrypted_with_lm_hash
[516],
664 const uchar old_lm_hash_encrypted
[16],
665 uchar password_encrypted_with_nt_hash
[516],
666 const uchar old_nt_hash_encrypted
[16],
667 struct samu
*sampass
,
668 char **pp_new_passwd
)
672 uint8
*password_encrypted
;
673 const uint8
*encryption_key
;
674 const uint8
*lanman_pw
, *nt_pw
;
677 uchar new_nt_hash
[16];
678 uchar new_lm_hash
[16];
682 bool nt_pass_set
= (password_encrypted_with_nt_hash
&& old_nt_hash_encrypted
);
683 bool lm_pass_set
= (password_encrypted_with_lm_hash
&& old_lm_hash_encrypted
);
685 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
687 /* I am convinced this check here is wrong, it is valid to
688 * change a password of a user that has a disabled account - gd */
690 if (acct_ctrl
& ACB_DISABLED
) {
691 DEBUG(2,("check_lanman_password: account %s disabled.\n", user
));
692 return NT_STATUS_ACCOUNT_DISABLED
;
695 if ((acct_ctrl
& ACB_PWNOTREQ
) && lp_null_passwords()) {
696 /* construct a null password (in case one is needed */
699 nt_lm_owf_gen(no_pw
, null_ntpw
, null_pw
);
704 /* save pointers to passwords so we don't have to keep looking them up */
705 if (lp_lanman_auth()) {
706 lanman_pw
= pdb_get_lanman_passwd(sampass
);
710 nt_pw
= pdb_get_nt_passwd(sampass
);
713 if (nt_pw
&& nt_pass_set
) {
714 /* IDEAL Case: passwords are in unicode, and we can
715 * read use the password encrypted with the NT hash
717 password_encrypted
= password_encrypted_with_nt_hash
;
718 encryption_key
= nt_pw
;
719 } else if (lanman_pw
&& lm_pass_set
) {
720 /* password may still be in unicode, but use LM hash version */
721 password_encrypted
= password_encrypted_with_lm_hash
;
722 encryption_key
= lanman_pw
;
723 } else if (nt_pass_set
) {
724 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
726 return NT_STATUS_WRONG_PASSWORD
;
727 } else if (lm_pass_set
) {
728 if (lp_lanman_auth()) {
729 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
732 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
735 return NT_STATUS_WRONG_PASSWORD
;
737 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
739 return NT_STATUS_WRONG_PASSWORD
;
743 * Decrypt the password with the key
745 arcfour_crypt( password_encrypted
, encryption_key
, 516);
747 if (!decode_pw_buffer(talloc_tos(),
751 nt_pass_set
? CH_UTF16
: CH_DOS
)) {
752 return NT_STATUS_WRONG_PASSWORD
;
756 * To ensure we got the correct new password, hash it and
757 * use it as a key to test the passed old password.
761 /* NT passwords, verify the NT hash. */
763 /* Calculate the MD4 hash (NT compatible) of the password */
764 memset(new_nt_hash
, '\0', 16);
765 E_md4hash(*pp_new_passwd
, new_nt_hash
);
769 * check the NT verifier
771 E_old_pw_hash(new_nt_hash
, nt_pw
, verifier
);
772 if (memcmp(verifier
, old_nt_hash_encrypted
, 16)) {
773 DEBUG(0, ("check_oem_password: old nt "
774 "password doesn't match.\n"));
775 return NT_STATUS_WRONG_PASSWORD
;
778 /* We could check the LM password here, but there is
779 * little point, we already know the password is
780 * correct, and the LM password might not even be
783 /* Further, LM hash generation algorithms
784 * differ with charset, so we could
785 * incorrectly fail a perfectly valid password
787 #ifdef DEBUG_PASSWORD
789 ("check_oem_password: password %s ok\n", *pp_new_passwd
));
796 * check the lm verifier
798 E_old_pw_hash(new_nt_hash
, lanman_pw
, verifier
);
799 if (memcmp(verifier
, old_lm_hash_encrypted
, 16)) {
800 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
801 return NT_STATUS_WRONG_PASSWORD
;
803 #ifdef DEBUG_PASSWORD
805 ("check_oem_password: password %s ok\n", *pp_new_passwd
));
811 if (lanman_pw
&& lm_pass_set
) {
813 E_deshash(*pp_new_passwd
, new_lm_hash
);
816 * check the lm verifier
818 E_old_pw_hash(new_lm_hash
, lanman_pw
, verifier
);
819 if (memcmp(verifier
, old_lm_hash_encrypted
, 16)) {
820 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
821 return NT_STATUS_WRONG_PASSWORD
;
824 #ifdef DEBUG_PASSWORD
826 ("check_oem_password: password %s ok\n", *pp_new_passwd
));
831 /* should not be reached */
832 return NT_STATUS_WRONG_PASSWORD
;
835 static bool password_in_history(uint8_t nt_pw
[NT_HASH_LEN
],
836 uint32_t pw_history_len
,
837 const uint8_t *pw_history
)
839 static const uint8_t zero_md5_nt_pw
[SALTED_MD5_HASH_LEN
] = { 0, };
842 dump_data(100, nt_pw
, NT_HASH_LEN
);
843 dump_data(100, pw_history
, PW_HISTORY_ENTRY_LEN
* pw_history_len
);
845 for (i
=0; i
<pw_history_len
; i
++) {
846 uint8_t new_nt_pw_salted_md5_hash
[SALTED_MD5_HASH_LEN
];
847 const uint8_t *current_salt
;
848 const uint8_t *old_nt_pw_salted_md5_hash
;
850 current_salt
= &pw_history
[i
*PW_HISTORY_ENTRY_LEN
];
851 old_nt_pw_salted_md5_hash
= current_salt
+ PW_HISTORY_SALT_LEN
;
853 if (memcmp(zero_md5_nt_pw
, old_nt_pw_salted_md5_hash
,
854 SALTED_MD5_HASH_LEN
) == 0) {
855 /* Ignore zero valued entries. */
859 if (memcmp(zero_md5_nt_pw
, current_salt
,
860 PW_HISTORY_SALT_LEN
) == 0)
863 * New format: zero salt and then plain nt hash.
864 * Directly compare the hashes.
866 if (memcmp(nt_pw
, old_nt_pw_salted_md5_hash
,
867 SALTED_MD5_HASH_LEN
) == 0)
873 * Old format: md5sum of salted nt hash.
874 * Create salted version of new pw to compare.
876 E_md5hash(current_salt
, nt_pw
, new_nt_pw_salted_md5_hash
);
878 if (memcmp(new_nt_pw_salted_md5_hash
,
879 old_nt_pw_salted_md5_hash
,
880 SALTED_MD5_HASH_LEN
) == 0) {
888 /***********************************************************
889 This routine takes the given password and checks it against
890 the password history. Returns True if this password has been
891 found in the history list.
892 ************************************************************/
894 static bool check_passwd_history(struct samu
*sampass
, const char *plaintext
)
896 uchar new_nt_p16
[NT_HASH_LEN
];
898 const uint8
*pwhistory
;
899 uint32 pwHisLen
, curr_pwHisLen
;
901 pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY
, &pwHisLen
);
906 pwhistory
= pdb_get_pw_history(sampass
, &curr_pwHisLen
);
907 if (!pwhistory
|| curr_pwHisLen
== 0) {
911 /* Only examine the minimum of the current history len and
912 the stored history len. Avoids race conditions. */
913 pwHisLen
= MIN(pwHisLen
,curr_pwHisLen
);
915 nt_pw
= pdb_get_nt_passwd(sampass
);
917 E_md4hash(plaintext
, new_nt_p16
);
919 if (!memcmp(nt_pw
, new_nt_p16
, NT_HASH_LEN
)) {
920 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
921 pdb_get_username(sampass
) ));
925 if (password_in_history(new_nt_p16
, pwHisLen
, pwhistory
)) {
926 DEBUG(1,("check_passwd_history: proposed new password for "
927 "user %s found in history list !\n",
928 pdb_get_username(sampass
) ));
934 /***********************************************************
935 ************************************************************/
937 NTSTATUS
check_password_complexity(const char *username
,
938 const char *password
,
939 enum samPwdChangeReason
*samr_reject_reason
)
941 TALLOC_CTX
*tosctx
= talloc_tos();
945 /* Use external script to check password complexity */
946 if ((lp_check_password_script(tosctx
) == NULL
)
947 || (*(lp_check_password_script(tosctx
)) == '\0')) {
951 cmd
= talloc_string_sub(tosctx
, lp_check_password_script(tosctx
), "%u",
954 return NT_STATUS_PASSWORD_RESTRICTION
;
957 check_ret
= smbrunsecret(cmd
, password
);
958 DEBUG(5,("check_password_complexity: check password script (%s) "
959 "returned [%d]\n", cmd
, check_ret
));
962 if (check_ret
!= 0) {
963 DEBUG(1,("check_password_complexity: "
964 "check password script said new password is not good "
966 if (samr_reject_reason
) {
967 *samr_reject_reason
= SAM_PWD_CHANGE_NOT_COMPLEX
;
969 return NT_STATUS_PASSWORD_RESTRICTION
;
975 /***********************************************************
976 Code to change the oem password. Changes both the lanman
977 and NT hashes. Old_passwd is almost always NULL.
978 NOTE this function is designed to be called as root. Check the old password
979 is correct before calling. JRA.
980 ************************************************************/
982 static NTSTATUS
change_oem_password(struct samu
*hnd
, const char *rhost
,
983 char *old_passwd
, char *new_passwd
,
985 enum samPwdChangeReason
*samr_reject_reason
)
989 TALLOC_CTX
*tosctx
= talloc_tos();
990 struct passwd
*pass
= NULL
;
991 const char *username
= pdb_get_username(hnd
);
992 time_t can_change_time
= pdb_get_pass_can_change_time(hnd
);
995 if (samr_reject_reason
) {
996 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
999 /* check to see if the secdesc has previously been set to disallow */
1000 if (!pdb_get_pass_can_change(hnd
)) {
1001 DEBUG(1, ("user %s does not have permissions to change password\n", username
));
1002 if (samr_reject_reason
) {
1003 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
1005 return NT_STATUS_ACCOUNT_RESTRICTION
;
1008 /* check to see if it is a Machine account and if the policy
1009 * denies machines to change the password. *
1010 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1011 if (pdb_get_acct_ctrl(hnd
) & ACB_WSTRUST
) {
1012 if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE
, &refuse
) && refuse
) {
1013 DEBUG(1, ("Machine %s cannot change password now, "
1014 "denied by Refuse Machine Password Change policy\n",
1016 if (samr_reject_reason
) {
1017 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
1019 return NT_STATUS_ACCOUNT_RESTRICTION
;
1023 /* removed calculation here, because passdb now calculates
1024 based on policy. jmcd */
1025 if ((can_change_time
!= 0) && (time(NULL
) < can_change_time
)) {
1026 DEBUG(1, ("user %s cannot change password now, must "
1027 "wait until %s\n", username
,
1028 http_timestring(tosctx
, can_change_time
)));
1029 if (samr_reject_reason
) {
1030 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
1032 return NT_STATUS_ACCOUNT_RESTRICTION
;
1035 if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN
, &min_len
) && (str_charnum(new_passwd
) < min_len
)) {
1036 DEBUG(1, ("user %s cannot change password - password too short\n",
1038 DEBUGADD(1, (" account policy min password len = %d\n", min_len
));
1039 if (samr_reject_reason
) {
1040 *samr_reject_reason
= SAM_PWD_CHANGE_PASSWORD_TOO_SHORT
;
1042 return NT_STATUS_PASSWORD_RESTRICTION
;
1043 /* return NT_STATUS_PWD_TOO_SHORT; */
1046 if (check_passwd_history(hnd
,new_passwd
)) {
1047 if (samr_reject_reason
) {
1048 *samr_reject_reason
= SAM_PWD_CHANGE_PWD_IN_HISTORY
;
1050 return NT_STATUS_PASSWORD_RESTRICTION
;
1053 pass
= Get_Pwnam_alloc(tosctx
, username
);
1055 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username
));
1056 return NT_STATUS_ACCESS_DENIED
;
1059 status
= check_password_complexity(username
, new_passwd
, samr_reject_reason
);
1060 if (!NT_STATUS_IS_OK(status
)) {
1066 * If unix password sync was requested, attempt to change
1067 * the /etc/passwd database first. Return failure if this cannot
1070 * This occurs before the oem change, because we don't want to
1071 * update it if chgpasswd failed.
1073 * Conditional on lp_unix_password_sync() because we don't want
1074 * to touch the unix db unless we have admin permission.
1077 if(lp_unix_password_sync() &&
1078 !chgpasswd(username
, rhost
, pass
, old_passwd
, new_passwd
,
1081 return NT_STATUS_ACCESS_DENIED
;
1086 if (!pdb_set_plaintext_passwd (hnd
, new_passwd
)) {
1087 return NT_STATUS_ACCESS_DENIED
;
1090 /* Now write it into the file. */
1091 return pdb_update_sam_account (hnd
);
1094 /***********************************************************
1095 Code to check and change the OEM hashed password.
1096 ************************************************************/
1098 NTSTATUS
pass_oem_change(char *user
, const char *rhost
,
1099 uchar password_encrypted_with_lm_hash
[516],
1100 const uchar old_lm_hash_encrypted
[16],
1101 uchar password_encrypted_with_nt_hash
[516],
1102 const uchar old_nt_hash_encrypted
[16],
1103 enum samPwdChangeReason
*reject_reason
)
1105 char *new_passwd
= NULL
;
1106 struct samu
*sampass
= NULL
;
1110 if (!(sampass
= samu_new(NULL
))) {
1111 return NT_STATUS_NO_MEMORY
;
1115 ret
= pdb_getsampwnam(sampass
, user
);
1119 DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
1120 TALLOC_FREE(sampass
);
1121 return NT_STATUS_NO_SUCH_USER
;
1124 nt_status
= check_oem_password(user
,
1125 password_encrypted_with_lm_hash
,
1126 old_lm_hash_encrypted
,
1127 password_encrypted_with_nt_hash
,
1128 old_nt_hash_encrypted
,
1132 if (!NT_STATUS_IS_OK(nt_status
)) {
1133 TALLOC_FREE(sampass
);
1137 /* We've already checked the old password here.... */
1139 nt_status
= change_oem_password(sampass
, rhost
, NULL
, new_passwd
,
1140 True
, reject_reason
);
1143 memset(new_passwd
, 0, strlen(new_passwd
));
1145 TALLOC_FREE(sampass
);