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
)) {
278 /* Eat leading/trailing whitespace before match. */
279 char *str
= SMB_STRDUP(buffer
);
281 DEBUG(2,("expect: ENOMEM\n"));
284 trim_char(str
, ' ', ' ');
286 if ((match
= unix_wild_match(expected
, str
)) == True
) {
287 /* Now data has started to return, lower timeout. */
288 timeout
= lp_passwd_chat_timeout() * 100;
294 if (lp_passwd_chat_debug())
295 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
296 expected
, buffer
, match
? "yes" : "no" ));
301 if (!NT_STATUS_IS_OK(status
)) {
302 DEBUG(2, ("expect: %s\n", nt_errstr(status
)));
307 DEBUG(10,("expect: returning %s\n", match
? "True" : "False" ));
311 static void pwd_sub(char *buf
)
313 all_string_sub(buf
, "\\n", "\n", 0);
314 all_string_sub(buf
, "\\r", "\r", 0);
315 all_string_sub(buf
, "\\s", " ", 0);
316 all_string_sub(buf
, "\\t", "\t", 0);
319 static int talktochild(int master
, const char *seq
)
321 TALLOC_CTX
*frame
= talloc_stackframe();
326 issue
= talloc_strdup(frame
, ".");
332 while (next_token_talloc(frame
, &seq
, &expected
, NULL
)) {
336 if (!expect(master
, issue
, expected
)) {
337 DEBUG(3, ("Response %d incorrect\n", count
));
342 if (!next_token_talloc(frame
, &seq
, &issue
, NULL
)) {
343 issue
= talloc_strdup(frame
, ".");
352 if (!strequal(issue
, ".")) {
353 /* we have one final issue to send */
354 expected
= talloc_strdup(frame
, ".");
359 if (!expect(master
, issue
, expected
)) {
368 static bool chat_with_program(char *passwordprogram
, const struct passwd
*pass
,
369 char *chatsequence
, bool as_root
)
371 char *slavedev
= NULL
;
378 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
382 /* allocate a pseudo-terminal device */
383 if ((master
= findpty(&slavedev
)) < 0) {
384 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass
->pw_name
));
389 * We need to temporarily stop CatchChild from eating
390 * SIGCLD signals as it also eats the exit status code. JRA.
393 CatchChildLeaveStatus();
395 if ((pid
= sys_fork()) < 0) {
396 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass
->pw_name
));
403 /* we now have a pty */
404 if (pid
> 0) { /* This is the parent process */
405 /* Don't need this anymore in parent. */
408 if ((chstat
= talktochild(master
, chatsequence
)) == False
) {
409 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass
->pw_name
));
410 kill(pid
, SIGKILL
); /* be sure to end this process */
413 while ((wpid
= sys_waitpid(pid
, &wstat
, 0)) < 0) {
414 if (errno
== EINTR
) {
422 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
429 * Go back to ignoring children.
436 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
439 if (WIFEXITED(wstat
) && (WEXITSTATUS(wstat
) != 0)) {
440 DEBUG(3, ("chat_with_program: The process exited with status %d \
441 while we were waiting\n", WEXITSTATUS(wstat
)));
444 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
445 else if (WIFSIGNALLED(wstat
)) {
446 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
447 while we were waiting\n", WTERMSIG(wstat
)));
455 * Lose any elevated privileges.
457 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY
);
458 drop_effective_capability(DMAPI_ACCESS_CAPABILITY
);
460 /* make sure it doesn't freeze */
466 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass
->pw_name
,
467 (int)getuid(), (int)getgid(), BOOLSTR(as_root
) ));
468 chstat
= dochild(master
, slavedev
, pass
, passwordprogram
, as_root
);
474 * The child should never return from dochild() ....
477 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat
));
482 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
483 (chstat
? "" : "un"), pass
->pw_name
));
487 bool chgpasswd(const char *name
, const struct passwd
*pass
,
488 const char *oldpass
, const char *newpass
, bool as_root
)
490 char *passwordprogram
= NULL
;
491 char *chatsequence
= NULL
;
494 TALLOC_CTX
*ctx
= talloc_tos();
500 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root
), name
));
502 #ifdef DEBUG_PASSWORD
503 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass
, newpass
));
506 /* Take the passed information and test it for minimum criteria */
508 /* Password is same as old password */
509 if (strcmp(oldpass
, newpass
) == 0) {
510 /* don't allow same password */
511 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name
)); /* log the attempt */
512 return (False
); /* inform the user */
516 * Check the old and new passwords don't contain any control
520 len
= strlen(oldpass
);
521 for (i
= 0; i
< len
; i
++) {
522 if (iscntrl((int)oldpass
[i
])) {
523 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
528 len
= strlen(newpass
);
529 for (i
= 0; i
< len
; i
++) {
530 if (iscntrl((int)newpass
[i
])) {
531 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
537 if (lp_pam_password_change()) {
539 #ifdef HAVE_SETLOCALE
540 const char *prevlocale
= setlocale(LC_ALL
, "C");
547 ret
= smb_pam_passchange(pass
->pw_name
, oldpass
, newpass
);
549 ret
= smb_pam_passchange(name
, oldpass
, newpass
);
555 #ifdef HAVE_SETLOCALE
556 setlocale(LC_ALL
, prevlocale
);
563 /* A non-PAM password change just doen't make sense without a valid local user */
566 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name
));
570 passwordprogram
= talloc_strdup(ctx
, lp_passwd_program());
571 if (!passwordprogram
|| !*passwordprogram
) {
572 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
575 chatsequence
= talloc_strdup(ctx
, lp_passwd_chat());
576 if (!chatsequence
|| !*chatsequence
) {
577 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
582 /* The password program *must* contain the user name to work. Fail if not. */
583 if (strstr_m(passwordprogram
, "%u") == NULL
) {
584 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
585 the string %%u, and the given string %s does not.\n", passwordprogram
));
590 passwordprogram
= talloc_string_sub(ctx
, passwordprogram
, "%u", name
);
591 if (!passwordprogram
) {
595 /* note that we do NOT substitute the %o and %n in the password program
596 as this would open up a security hole where the user could use
597 a new password containing shell escape characters */
599 chatsequence
= talloc_string_sub(ctx
, chatsequence
, "%u", name
);
603 chatsequence
= talloc_all_string_sub(ctx
,
610 chatsequence
= talloc_all_string_sub(ctx
,
614 return chat_with_program(passwordprogram
,
620 #else /* ALLOW_CHANGE_PASSWORD */
622 bool chgpasswd(const char *name
, const struct passwd
*pass
,
623 const char *oldpass
, const char *newpass
, bool as_root
)
625 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name
));
628 #endif /* ALLOW_CHANGE_PASSWORD */
630 /***********************************************************
631 Decrypt and verify a user password change.
633 The 516 byte long buffers are encrypted with the old NT and
634 old LM passwords, and if the NT passwords are present, both
635 buffers contain a unicode string.
637 After decrypting the buffers, check the password is correct by
638 matching the old hashed passwords with the passwords in the passdb.
640 ************************************************************/
642 static NTSTATUS
check_oem_password(const char *user
,
643 uchar password_encrypted_with_lm_hash
[516],
644 const uchar old_lm_hash_encrypted
[16],
645 uchar password_encrypted_with_nt_hash
[516],
646 const uchar old_nt_hash_encrypted
[16],
647 struct samu
*sampass
,
648 char **pp_new_passwd
)
652 uint8
*password_encrypted
;
653 const uint8
*encryption_key
;
654 const uint8
*lanman_pw
, *nt_pw
;
657 uchar new_nt_hash
[16];
658 uchar new_lm_hash
[16];
662 bool nt_pass_set
= (password_encrypted_with_nt_hash
&& old_nt_hash_encrypted
);
663 bool lm_pass_set
= (password_encrypted_with_lm_hash
&& old_lm_hash_encrypted
);
665 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
667 /* I am convinced this check here is wrong, it is valid to
668 * change a password of a user that has a disabled account - gd */
670 if (acct_ctrl
& ACB_DISABLED
) {
671 DEBUG(2,("check_lanman_password: account %s disabled.\n", user
));
672 return NT_STATUS_ACCOUNT_DISABLED
;
675 if ((acct_ctrl
& ACB_PWNOTREQ
) && lp_null_passwords()) {
676 /* construct a null password (in case one is needed */
679 nt_lm_owf_gen(no_pw
, null_ntpw
, null_pw
);
684 /* save pointers to passwords so we don't have to keep looking them up */
685 if (lp_lanman_auth()) {
686 lanman_pw
= pdb_get_lanman_passwd(sampass
);
690 nt_pw
= pdb_get_nt_passwd(sampass
);
693 if (nt_pw
&& nt_pass_set
) {
694 /* IDEAL Case: passwords are in unicode, and we can
695 * read use the password encrypted with the NT hash
697 password_encrypted
= password_encrypted_with_nt_hash
;
698 encryption_key
= nt_pw
;
699 } else if (lanman_pw
&& lm_pass_set
) {
700 /* password may still be in unicode, but use LM hash version */
701 password_encrypted
= password_encrypted_with_lm_hash
;
702 encryption_key
= lanman_pw
;
703 } else if (nt_pass_set
) {
704 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
706 return NT_STATUS_WRONG_PASSWORD
;
707 } else if (lm_pass_set
) {
708 if (lp_lanman_auth()) {
709 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
712 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
715 return NT_STATUS_WRONG_PASSWORD
;
717 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
719 return NT_STATUS_WRONG_PASSWORD
;
723 * Decrypt the password with the key
725 arcfour_crypt( password_encrypted
, encryption_key
, 516);
727 if (!decode_pw_buffer(talloc_tos(),
731 nt_pass_set
? CH_UTF16
: CH_DOS
)) {
732 return NT_STATUS_WRONG_PASSWORD
;
736 * To ensure we got the correct new password, hash it and
737 * use it as a key to test the passed old password.
741 /* NT passwords, verify the NT hash. */
743 /* Calculate the MD4 hash (NT compatible) of the password */
744 memset(new_nt_hash
, '\0', 16);
745 E_md4hash(*pp_new_passwd
, new_nt_hash
);
749 * check the NT verifier
751 E_old_pw_hash(new_nt_hash
, nt_pw
, verifier
);
752 if (memcmp(verifier
, old_nt_hash_encrypted
, 16)) {
753 DEBUG(0, ("check_oem_password: old nt "
754 "password doesn't match.\n"));
755 return NT_STATUS_WRONG_PASSWORD
;
758 /* We could check the LM password here, but there is
759 * little point, we already know the password is
760 * correct, and the LM password might not even be
763 /* Further, LM hash generation algorithms
764 * differ with charset, so we could
765 * incorrectly fail a perfectly valid password
767 #ifdef DEBUG_PASSWORD
769 ("check_oem_password: password %s ok\n", *pp_new_passwd
));
776 * check the lm verifier
778 E_old_pw_hash(new_nt_hash
, lanman_pw
, verifier
);
779 if (memcmp(verifier
, old_lm_hash_encrypted
, 16)) {
780 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
781 return NT_STATUS_WRONG_PASSWORD
;
783 #ifdef DEBUG_PASSWORD
785 ("check_oem_password: password %s ok\n", *pp_new_passwd
));
791 if (lanman_pw
&& lm_pass_set
) {
793 E_deshash(*pp_new_passwd
, new_lm_hash
);
796 * check the lm verifier
798 E_old_pw_hash(new_lm_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
;
804 #ifdef DEBUG_PASSWORD
806 ("check_oem_password: password %s ok\n", *pp_new_passwd
));
811 /* should not be reached */
812 return NT_STATUS_WRONG_PASSWORD
;
815 static bool password_in_history(uint8_t nt_pw
[NT_HASH_LEN
],
816 uint32_t pw_history_len
,
817 const uint8_t *pw_history
)
819 static const uint8_t zero_md5_nt_pw
[SALTED_MD5_HASH_LEN
] = { 0, };
822 dump_data(100, nt_pw
, NT_HASH_LEN
);
823 dump_data(100, pw_history
, PW_HISTORY_ENTRY_LEN
* pw_history_len
);
825 for (i
=0; i
<pw_history_len
; i
++) {
826 uint8_t new_nt_pw_salted_md5_hash
[SALTED_MD5_HASH_LEN
];
827 const uint8_t *current_salt
;
828 const uint8_t *old_nt_pw_salted_md5_hash
;
830 current_salt
= &pw_history
[i
*PW_HISTORY_ENTRY_LEN
];
831 old_nt_pw_salted_md5_hash
= current_salt
+ PW_HISTORY_SALT_LEN
;
833 if (memcmp(zero_md5_nt_pw
, old_nt_pw_salted_md5_hash
,
834 SALTED_MD5_HASH_LEN
) == 0) {
835 /* Ignore zero valued entries. */
839 if (memcmp(zero_md5_nt_pw
, current_salt
,
840 PW_HISTORY_SALT_LEN
) == 0)
843 * New format: zero salt and then plain nt hash.
844 * Directly compare the hashes.
846 if (memcmp(nt_pw
, old_nt_pw_salted_md5_hash
,
847 SALTED_MD5_HASH_LEN
) == 0)
853 * Old format: md5sum of salted nt hash.
854 * Create salted version of new pw to compare.
856 E_md5hash(current_salt
, nt_pw
, new_nt_pw_salted_md5_hash
);
858 if (memcmp(new_nt_pw_salted_md5_hash
,
859 old_nt_pw_salted_md5_hash
,
860 SALTED_MD5_HASH_LEN
) == 0) {
868 /***********************************************************
869 This routine takes the given password and checks it against
870 the password history. Returns True if this password has been
871 found in the history list.
872 ************************************************************/
874 static bool check_passwd_history(struct samu
*sampass
, const char *plaintext
)
876 uchar new_nt_p16
[NT_HASH_LEN
];
878 const uint8
*pwhistory
;
879 uint32 pwHisLen
, curr_pwHisLen
;
881 pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY
, &pwHisLen
);
886 pwhistory
= pdb_get_pw_history(sampass
, &curr_pwHisLen
);
887 if (!pwhistory
|| curr_pwHisLen
== 0) {
891 /* Only examine the minimum of the current history len and
892 the stored history len. Avoids race conditions. */
893 pwHisLen
= MIN(pwHisLen
,curr_pwHisLen
);
895 nt_pw
= pdb_get_nt_passwd(sampass
);
897 E_md4hash(plaintext
, new_nt_p16
);
899 if (!memcmp(nt_pw
, new_nt_p16
, NT_HASH_LEN
)) {
900 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
901 pdb_get_username(sampass
) ));
905 if (password_in_history(new_nt_p16
, pwHisLen
, pwhistory
)) {
906 DEBUG(1,("check_passwd_history: proposed new password for "
907 "user %s found in history list !\n",
908 pdb_get_username(sampass
) ));
914 /***********************************************************
915 ************************************************************/
917 NTSTATUS
check_password_complexity(const char *username
,
918 const char *password
,
919 enum samPwdChangeReason
*samr_reject_reason
)
921 TALLOC_CTX
*tosctx
= talloc_tos();
925 /* Use external script to check password complexity */
926 if ((lp_check_password_script() == NULL
)
927 || (*(lp_check_password_script()) == '\0')) {
931 cmd
= talloc_string_sub(tosctx
, lp_check_password_script(), "%u",
934 return NT_STATUS_PASSWORD_RESTRICTION
;
937 check_ret
= smbrunsecret(cmd
, password
);
938 DEBUG(5,("check_password_complexity: check password script (%s) "
939 "returned [%d]\n", cmd
, check_ret
));
942 if (check_ret
!= 0) {
943 DEBUG(1,("check_password_complexity: "
944 "check password script said new password is not good "
946 if (samr_reject_reason
) {
947 *samr_reject_reason
= SAM_PWD_CHANGE_NOT_COMPLEX
;
949 return NT_STATUS_PASSWORD_RESTRICTION
;
955 /***********************************************************
956 Code to change the oem password. Changes both the lanman
957 and NT hashes. Old_passwd is almost always NULL.
958 NOTE this function is designed to be called as root. Check the old password
959 is correct before calling. JRA.
960 ************************************************************/
962 static NTSTATUS
change_oem_password(struct samu
*hnd
, char *old_passwd
, char *new_passwd
, bool as_root
, enum samPwdChangeReason
*samr_reject_reason
)
966 TALLOC_CTX
*tosctx
= talloc_tos();
967 struct passwd
*pass
= NULL
;
968 const char *username
= pdb_get_username(hnd
);
969 time_t can_change_time
= pdb_get_pass_can_change_time(hnd
);
972 if (samr_reject_reason
) {
973 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
976 /* check to see if the secdesc has previously been set to disallow */
977 if (!pdb_get_pass_can_change(hnd
)) {
978 DEBUG(1, ("user %s does not have permissions to change password\n", username
));
979 if (samr_reject_reason
) {
980 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
982 return NT_STATUS_ACCOUNT_RESTRICTION
;
985 /* check to see if it is a Machine account and if the policy
986 * denies machines to change the password. *
987 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
988 if (pdb_get_acct_ctrl(hnd
) & ACB_WSTRUST
) {
989 if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE
, &refuse
) && refuse
) {
990 DEBUG(1, ("Machine %s cannot change password now, "
991 "denied by Refuse Machine Password Change policy\n",
993 if (samr_reject_reason
) {
994 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
996 return NT_STATUS_ACCOUNT_RESTRICTION
;
1000 /* removed calculation here, because passdb now calculates
1001 based on policy. jmcd */
1002 if ((can_change_time
!= 0) && (time(NULL
) < can_change_time
)) {
1003 DEBUG(1, ("user %s cannot change password now, must "
1004 "wait until %s\n", username
,
1005 http_timestring(tosctx
, can_change_time
)));
1006 if (samr_reject_reason
) {
1007 *samr_reject_reason
= SAM_PWD_CHANGE_NO_ERROR
;
1009 return NT_STATUS_ACCOUNT_RESTRICTION
;
1012 if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN
, &min_len
) && (str_charnum(new_passwd
) < min_len
)) {
1013 DEBUG(1, ("user %s cannot change password - password too short\n",
1015 DEBUGADD(1, (" account policy min password len = %d\n", min_len
));
1016 if (samr_reject_reason
) {
1017 *samr_reject_reason
= SAM_PWD_CHANGE_PASSWORD_TOO_SHORT
;
1019 return NT_STATUS_PASSWORD_RESTRICTION
;
1020 /* return NT_STATUS_PWD_TOO_SHORT; */
1023 if (check_passwd_history(hnd
,new_passwd
)) {
1024 if (samr_reject_reason
) {
1025 *samr_reject_reason
= SAM_PWD_CHANGE_PWD_IN_HISTORY
;
1027 return NT_STATUS_PASSWORD_RESTRICTION
;
1030 pass
= Get_Pwnam_alloc(tosctx
, username
);
1032 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username
));
1033 return NT_STATUS_ACCESS_DENIED
;
1036 status
= check_password_complexity(username
, new_passwd
, samr_reject_reason
);
1037 if (!NT_STATUS_IS_OK(status
)) {
1043 * If unix password sync was requested, attempt to change
1044 * the /etc/passwd database first. Return failure if this cannot
1047 * This occurs before the oem change, because we don't want to
1048 * update it if chgpasswd failed.
1050 * Conditional on lp_unix_password_sync() because we don't want
1051 * to touch the unix db unless we have admin permission.
1054 if(lp_unix_password_sync() &&
1055 !chgpasswd(username
, pass
, old_passwd
, new_passwd
, as_root
)) {
1057 return NT_STATUS_ACCESS_DENIED
;
1062 if (!pdb_set_plaintext_passwd (hnd
, new_passwd
)) {
1063 return NT_STATUS_ACCESS_DENIED
;
1066 /* Now write it into the file. */
1067 return pdb_update_sam_account (hnd
);
1070 /***********************************************************
1071 Code to check and change the OEM hashed password.
1072 ************************************************************/
1074 NTSTATUS
pass_oem_change(char *user
,
1075 uchar password_encrypted_with_lm_hash
[516],
1076 const uchar old_lm_hash_encrypted
[16],
1077 uchar password_encrypted_with_nt_hash
[516],
1078 const uchar old_nt_hash_encrypted
[16],
1079 enum samPwdChangeReason
*reject_reason
)
1081 char *new_passwd
= NULL
;
1082 struct samu
*sampass
= NULL
;
1086 if (!(sampass
= samu_new(NULL
))) {
1087 return NT_STATUS_NO_MEMORY
;
1091 ret
= pdb_getsampwnam(sampass
, user
);
1095 DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
1096 TALLOC_FREE(sampass
);
1097 return NT_STATUS_NO_SUCH_USER
;
1100 nt_status
= check_oem_password(user
,
1101 password_encrypted_with_lm_hash
,
1102 old_lm_hash_encrypted
,
1103 password_encrypted_with_nt_hash
,
1104 old_nt_hash_encrypted
,
1108 if (!NT_STATUS_IS_OK(nt_status
)) {
1109 TALLOC_FREE(sampass
);
1113 /* We've already checked the old password here.... */
1115 nt_status
= change_oem_password(sampass
, NULL
, new_passwd
, True
, reject_reason
);
1118 memset(new_passwd
, 0, strlen(new_passwd
));
1120 TALLOC_FREE(sampass
);