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 "../libcli/auth/libcli_auth.h"
52 #include "../lib/crypto/arcfour.h"
53 #include "rpc_server/samr/srv_samr_util.h"
55 #ifndef ALLOW_CHANGE_PASSWORD
56 #if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
57 #define ALLOW_CHANGE_PASSWORD 1
61 #if ALLOW_CHANGE_PASSWORD
63 static int findpty(char **slave)
67 SMB_STRUCT_DIR *dirp = NULL;
72 #if defined(HAVE_GRANTPT)
73 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
74 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) {
77 line = (char *)ptsname(master);
79 *slave = SMB_STRDUP(line);
84 ("findpty: Unable to create master/slave pty pair.\n"));
85 /* Stop fd leak on error. */
90 ("findpty: Allocated slave pty %s\n", *slave));
94 #endif /* HAVE_GRANTPT */
96 line = SMB_STRDUP("/dev/ptyXX");
101 dirp = sys_opendir("/dev");
107 while ((dpname = readdirname(dirp)) != NULL) {
108 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
110 ("pty: try to open %s, line was %s\n", dpname,
114 if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
115 DEBUG(3, ("pty: opened %s\n", line));
128 static int dochild(int master, const char *slavedev, const struct passwd *pass,
129 const char *passwordprogram, bool as_root)
132 struct termios stermios;
135 char * const eptrs[1] = { NULL };
140 ("dochild: user doesn't exist in the UNIX password database.\n"));
147 gain_root_privilege();
149 /* Start new session - gets rid of controlling terminal. */
153 ("Weirdness, couldn't let go of controlling terminal\n"));
157 /* Open slave pty and acquire as new controlling terminal. */
158 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
160 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
163 #if defined(TIOCSCTTY) && !defined(SUNOS5)
165 * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
166 * see the discussion under
167 * https://bugzilla.samba.org/show_bug.cgi?id=5366.
169 if (ioctl(slave, TIOCSCTTY, 0) < 0)
171 DEBUG(3, ("Error in ioctl call for slave pty\n"));
174 #elif defined(I_PUSH) && defined(I_FIND)
175 if (ioctl(slave, I_FIND, "ptem") == 0) {
176 ioctl(slave, I_PUSH, "ptem");
178 if (ioctl(slave, I_FIND, "ldterm") == 0) {
179 ioctl(slave, I_PUSH, "ldterm");
186 /* Make slave stdin/out/err of child. */
188 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
190 DEBUG(3, ("Could not re-direct stdin\n"));
193 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
195 DEBUG(3, ("Could not re-direct stdout\n"));
198 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
200 DEBUG(3, ("Could not re-direct stderr\n"));
206 /* Set proper terminal attributes - no echo, canonical input processing,
207 no map NL to CR/NL on output. */
209 if (tcgetattr(0, &stermios) < 0)
212 ("could not read default terminal attributes on pty\n"));
215 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
216 stermios.c_lflag |= ICANON;
218 stermios.c_oflag &= ~(ONLCR);
220 if (tcsetattr(0, TCSANOW, &stermios) < 0)
222 DEBUG(3, ("could not set attributes of pty\n"));
226 /* make us completely into the right uid */
229 become_user_permanently(uid, gid);
233 ("Invoking '%s' as password change program.\n",
236 /* execl() password-change application */
237 if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
239 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
245 static int expect(int master, char *issue, char *expected)
248 int attempts, timeout, nread;
252 for (attempts = 0; attempts < 2; attempts++) {
254 if (!strequal(issue, ".")) {
255 if (lp_passwd_chat_debug())
256 DEBUG(100, ("expect: sending [%s]\n", issue));
258 if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
259 DEBUG(2,("expect: (short) write returned %d\n",
265 if (strequal(expected, "."))
268 /* Initial timeout. */
269 timeout = lp_passwd_chat_timeout() * 1000;
274 status = read_fd_with_timeout(
275 master, buffer + nread, 1,
276 sizeof(buffer) - nread - 1,
279 if (!NT_STATUS_IS_OK(status)) {
280 DEBUG(2, ("expect: read error %s\n",
288 /* Eat leading/trailing whitespace before match. */
289 char *str = SMB_STRDUP(buffer);
291 DEBUG(2,("expect: ENOMEM\n"));
294 trim_char(str, ' ', ' ');
296 if ((match = unix_wild_match(expected, str)) == True) {
297 /* Now data has started to return, lower timeout. */
298 timeout = lp_passwd_chat_timeout() * 100;
304 if (lp_passwd_chat_debug())
305 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
306 expected, buffer, match ? "yes" : "no" ));
311 if (!NT_STATUS_IS_OK(status)) {
312 DEBUG(2, ("expect: %s\n", nt_errstr(status)));
317 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
321 static void pwd_sub(char *buf)
323 all_string_sub(buf, "\\n", "\n", 0);
324 all_string_sub(buf, "\\r", "\r", 0);
325 all_string_sub(buf, "\\s", " ", 0);
326 all_string_sub(buf, "\\t", "\t", 0);
329 static int talktochild(int master, const char *seq)
331 TALLOC_CTX *frame = talloc_stackframe();
336 issue = talloc_strdup(frame, ".");
342 while (next_token_talloc(frame, &seq, &expected, NULL)) {
346 if (!expect(master, issue, expected)) {
347 DEBUG(3, ("Response %d incorrect\n", count));
352 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
353 issue = talloc_strdup(frame, ".");
362 if (!strequal(issue, ".")) {
363 /* we have one final issue to send */
364 expected = talloc_strdup(frame, ".");
369 if (!expect(master, issue, expected)) {
378 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
379 char *chatsequence, bool as_root)
381 char *slavedev = NULL;
388 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
392 /* allocate a pseudo-terminal device */
393 if ((master = findpty(&slavedev)) < 0) {
394 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
399 * We need to temporarily stop CatchChild from eating
400 * SIGCLD signals as it also eats the exit status code. JRA.
403 CatchChildLeaveStatus();
405 if ((pid = sys_fork()) < 0) {
406 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
413 /* we now have a pty */
414 if (pid > 0) { /* This is the parent process */
415 /* Don't need this anymore in parent. */
418 if ((chstat = talktochild(master, chatsequence)) == False) {
419 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
420 kill(pid, SIGKILL); /* be sure to end this process */
423 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
424 if (errno == EINTR) {
432 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
439 * Go back to ignoring children.
446 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
449 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
450 DEBUG(3, ("chat_with_program: The process exited with status %d \
451 while we were waiting\n", WEXITSTATUS(wstat)));
454 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
455 else if (WIFSIGNALLED(wstat)) {
456 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
457 while we were waiting\n", WTERMSIG(wstat)));
465 * Lose any elevated privileges.
467 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
468 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
470 /* make sure it doesn't freeze */
476 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
477 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
478 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
484 * The child should never return from dochild() ....
487 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
492 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
493 (chstat ? "" : "un"), pass->pw_name));
497 bool chgpasswd(const char *name, const char *rhost, const struct passwd *pass,
498 const char *oldpass, const char *newpass, bool as_root)
500 char *passwordprogram = NULL;
501 char *chatsequence = NULL;
504 TALLOC_CTX *ctx = talloc_tos();
510 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
512 #ifdef DEBUG_PASSWORD
513 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
516 /* Take the passed information and test it for minimum criteria */
518 /* Password is same as old password */
519 if (strcmp(oldpass, newpass) == 0) {
520 /* don't allow same password */
521 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
522 return (False); /* inform the user */
526 * Check the old and new passwords don't contain any control
530 len = strlen(oldpass);
531 for (i = 0; i < len; i++) {
532 if (iscntrl((int)oldpass[i])) {
533 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
538 len = strlen(newpass);
539 for (i = 0; i < len; i++) {
540 if (iscntrl((int)newpass[i])) {
541 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
547 if (lp_pam_password_change()) {
549 #ifdef HAVE_SETLOCALE
550 const char *prevlocale = setlocale(LC_ALL, "C");
557 ret = smb_pam_passchange(pass->pw_name, rhost,
560 ret = smb_pam_passchange(name, rhost, oldpass,
567 #ifdef HAVE_SETLOCALE
568 setlocale(LC_ALL, prevlocale);
575 /* A non-PAM password change just doen't make sense without a valid local user */
578 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
582 passwordprogram = talloc_strdup(ctx, lp_passwd_program());
583 if (!passwordprogram || !*passwordprogram) {
584 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
587 chatsequence = talloc_strdup(ctx, lp_passwd_chat());
588 if (!chatsequence || !*chatsequence) {
589 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
594 /* The password program *must* contain the user name to work. Fail if not. */
595 if (strstr_m(passwordprogram, "%u") == NULL) {
596 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
597 the string %%u, and the given string %s does not.\n", passwordprogram ));
602 passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
603 if (!passwordprogram) {
607 /* note that we do NOT substitute the %o and %n in the password program
608 as this would open up a security hole where the user could use
609 a new password containing shell escape characters */
611 chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
615 chatsequence = talloc_all_string_sub(ctx,
622 chatsequence = talloc_all_string_sub(ctx,
626 return chat_with_program(passwordprogram,
632 #else /* ALLOW_CHANGE_PASSWORD */
634 bool chgpasswd(const char *name, const struct passwd *pass,
635 const char *oldpass, const char *newpass, bool as_root)
637 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
640 #endif /* ALLOW_CHANGE_PASSWORD */
642 /***********************************************************
643 Decrypt and verify a user password change.
645 The 516 byte long buffers are encrypted with the old NT and
646 old LM passwords, and if the NT passwords are present, both
647 buffers contain a unicode string.
649 After decrypting the buffers, check the password is correct by
650 matching the old hashed passwords with the passwords in the passdb.
652 ************************************************************/
654 static NTSTATUS check_oem_password(const char *user,
655 uchar password_encrypted_with_lm_hash[516],
656 const uchar old_lm_hash_encrypted[16],
657 uchar password_encrypted_with_nt_hash[516],
658 const uchar old_nt_hash_encrypted[16],
659 struct samu *sampass,
660 char **pp_new_passwd)
664 uint8 *password_encrypted;
665 const uint8 *encryption_key;
666 const uint8 *lanman_pw, *nt_pw;
669 uchar new_nt_hash[16];
670 uchar new_lm_hash[16];
674 bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
675 bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
677 acct_ctrl = pdb_get_acct_ctrl(sampass);
679 /* I am convinced this check here is wrong, it is valid to
680 * change a password of a user that has a disabled account - gd */
682 if (acct_ctrl & ACB_DISABLED) {
683 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
684 return NT_STATUS_ACCOUNT_DISABLED;
687 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
688 /* construct a null password (in case one is needed */
691 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
696 /* save pointers to passwords so we don't have to keep looking them up */
697 if (lp_lanman_auth()) {
698 lanman_pw = pdb_get_lanman_passwd(sampass);
702 nt_pw = pdb_get_nt_passwd(sampass);
705 if (nt_pw && nt_pass_set) {
706 /* IDEAL Case: passwords are in unicode, and we can
707 * read use the password encrypted with the NT hash
709 password_encrypted = password_encrypted_with_nt_hash;
710 encryption_key = nt_pw;
711 } else if (lanman_pw && lm_pass_set) {
712 /* password may still be in unicode, but use LM hash version */
713 password_encrypted = password_encrypted_with_lm_hash;
714 encryption_key = lanman_pw;
715 } else if (nt_pass_set) {
716 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
718 return NT_STATUS_WRONG_PASSWORD;
719 } else if (lm_pass_set) {
720 if (lp_lanman_auth()) {
721 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
724 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
727 return NT_STATUS_WRONG_PASSWORD;
729 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
731 return NT_STATUS_WRONG_PASSWORD;
735 * Decrypt the password with the key
737 arcfour_crypt( password_encrypted, encryption_key, 516);
739 if (!decode_pw_buffer(talloc_tos(),
743 nt_pass_set ? CH_UTF16 : CH_DOS)) {
744 return NT_STATUS_WRONG_PASSWORD;
748 * To ensure we got the correct new password, hash it and
749 * use it as a key to test the passed old password.
753 /* NT passwords, verify the NT hash. */
755 /* Calculate the MD4 hash (NT compatible) of the password */
756 memset(new_nt_hash, '\0', 16);
757 E_md4hash(*pp_new_passwd, new_nt_hash);
761 * check the NT verifier
763 E_old_pw_hash(new_nt_hash, nt_pw, verifier);
764 if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
765 DEBUG(0, ("check_oem_password: old nt "
766 "password doesn't match.\n"));
767 return NT_STATUS_WRONG_PASSWORD;
770 /* We could check the LM password here, but there is
771 * little point, we already know the password is
772 * correct, and the LM password might not even be
775 /* Further, LM hash generation algorithms
776 * differ with charset, so we could
777 * incorrectly fail a perfectly valid password
779 #ifdef DEBUG_PASSWORD
781 ("check_oem_password: password %s ok\n", *pp_new_passwd));
788 * check the lm verifier
790 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
791 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
792 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
793 return NT_STATUS_WRONG_PASSWORD;
795 #ifdef DEBUG_PASSWORD
797 ("check_oem_password: password %s ok\n", *pp_new_passwd));
803 if (lanman_pw && lm_pass_set) {
805 E_deshash(*pp_new_passwd, new_lm_hash);
808 * check the lm verifier
810 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
811 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
812 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
813 return NT_STATUS_WRONG_PASSWORD;
816 #ifdef DEBUG_PASSWORD
818 ("check_oem_password: password %s ok\n", *pp_new_passwd));
823 /* should not be reached */
824 return NT_STATUS_WRONG_PASSWORD;
827 static bool password_in_history(uint8_t nt_pw[NT_HASH_LEN],
828 uint32_t pw_history_len,
829 const uint8_t *pw_history)
831 static const uint8_t zero_md5_nt_pw[SALTED_MD5_HASH_LEN] = { 0, };
834 dump_data(100, nt_pw, NT_HASH_LEN);
835 dump_data(100, pw_history, PW_HISTORY_ENTRY_LEN * pw_history_len);
837 for (i=0; i<pw_history_len; i++) {
838 uint8_t new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
839 const uint8_t *current_salt;
840 const uint8_t *old_nt_pw_salted_md5_hash;
842 current_salt = &pw_history[i*PW_HISTORY_ENTRY_LEN];
843 old_nt_pw_salted_md5_hash = current_salt + PW_HISTORY_SALT_LEN;
845 if (memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash,
846 SALTED_MD5_HASH_LEN) == 0) {
847 /* Ignore zero valued entries. */
851 if (memcmp(zero_md5_nt_pw, current_salt,
852 PW_HISTORY_SALT_LEN) == 0)
855 * New format: zero salt and then plain nt hash.
856 * Directly compare the hashes.
858 if (memcmp(nt_pw, old_nt_pw_salted_md5_hash,
859 SALTED_MD5_HASH_LEN) == 0)
865 * Old format: md5sum of salted nt hash.
866 * Create salted version of new pw to compare.
868 E_md5hash(current_salt, nt_pw, new_nt_pw_salted_md5_hash);
870 if (memcmp(new_nt_pw_salted_md5_hash,
871 old_nt_pw_salted_md5_hash,
872 SALTED_MD5_HASH_LEN) == 0) {
880 /***********************************************************
881 This routine takes the given password and checks it against
882 the password history. Returns True if this password has been
883 found in the history list.
884 ************************************************************/
886 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
888 uchar new_nt_p16[NT_HASH_LEN];
890 const uint8 *pwhistory;
891 uint32 pwHisLen, curr_pwHisLen;
893 pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &pwHisLen);
898 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
899 if (!pwhistory || curr_pwHisLen == 0) {
903 /* Only examine the minimum of the current history len and
904 the stored history len. Avoids race conditions. */
905 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
907 nt_pw = pdb_get_nt_passwd(sampass);
909 E_md4hash(plaintext, new_nt_p16);
911 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
912 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
913 pdb_get_username(sampass) ));
917 if (password_in_history(new_nt_p16, pwHisLen, pwhistory)) {
918 DEBUG(1,("check_passwd_history: proposed new password for "
919 "user %s found in history list !\n",
920 pdb_get_username(sampass) ));
926 /***********************************************************
927 ************************************************************/
929 NTSTATUS check_password_complexity(const char *username,
930 const char *password,
931 enum samPwdChangeReason *samr_reject_reason)
933 TALLOC_CTX *tosctx = talloc_tos();
937 /* Use external script to check password complexity */
938 if ((lp_check_password_script() == NULL)
939 || (*(lp_check_password_script()) == '\0')) {
943 cmd = talloc_string_sub(tosctx, lp_check_password_script(), "%u",
946 return NT_STATUS_PASSWORD_RESTRICTION;
949 check_ret = smbrunsecret(cmd, password);
950 DEBUG(5,("check_password_complexity: check password script (%s) "
951 "returned [%d]\n", cmd, check_ret));
954 if (check_ret != 0) {
955 DEBUG(1,("check_password_complexity: "
956 "check password script said new password is not good "
958 if (samr_reject_reason) {
959 *samr_reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
961 return NT_STATUS_PASSWORD_RESTRICTION;
967 /***********************************************************
968 Code to change the oem password. Changes both the lanman
969 and NT hashes. Old_passwd is almost always NULL.
970 NOTE this function is designed to be called as root. Check the old password
971 is correct before calling. JRA.
972 ************************************************************/
974 static NTSTATUS change_oem_password(struct samu *hnd, const char *rhost,
975 char *old_passwd, char *new_passwd,
977 enum samPwdChangeReason *samr_reject_reason)
981 TALLOC_CTX *tosctx = talloc_tos();
982 struct passwd *pass = NULL;
983 const char *username = pdb_get_username(hnd);
984 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
987 if (samr_reject_reason) {
988 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
991 /* check to see if the secdesc has previously been set to disallow */
992 if (!pdb_get_pass_can_change(hnd)) {
993 DEBUG(1, ("user %s does not have permissions to change password\n", username));
994 if (samr_reject_reason) {
995 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
997 return NT_STATUS_ACCOUNT_RESTRICTION;
1000 /* check to see if it is a Machine account and if the policy
1001 * denies machines to change the password. *
1002 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1003 if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1004 if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1005 DEBUG(1, ("Machine %s cannot change password now, "
1006 "denied by Refuse Machine Password Change policy\n",
1008 if (samr_reject_reason) {
1009 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1011 return NT_STATUS_ACCOUNT_RESTRICTION;
1015 /* removed calculation here, because passdb now calculates
1016 based on policy. jmcd */
1017 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1018 DEBUG(1, ("user %s cannot change password now, must "
1019 "wait until %s\n", username,
1020 http_timestring(tosctx, can_change_time)));
1021 if (samr_reject_reason) {
1022 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1024 return NT_STATUS_ACCOUNT_RESTRICTION;
1027 if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1028 DEBUG(1, ("user %s cannot change password - password too short\n",
1030 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1031 if (samr_reject_reason) {
1032 *samr_reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1034 return NT_STATUS_PASSWORD_RESTRICTION;
1035 /* return NT_STATUS_PWD_TOO_SHORT; */
1038 if (check_passwd_history(hnd,new_passwd)) {
1039 if (samr_reject_reason) {
1040 *samr_reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1042 return NT_STATUS_PASSWORD_RESTRICTION;
1045 pass = Get_Pwnam_alloc(tosctx, username);
1047 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1048 return NT_STATUS_ACCESS_DENIED;
1051 status = check_password_complexity(username, new_passwd, samr_reject_reason);
1052 if (!NT_STATUS_IS_OK(status)) {
1058 * If unix password sync was requested, attempt to change
1059 * the /etc/passwd database first. Return failure if this cannot
1062 * This occurs before the oem change, because we don't want to
1063 * update it if chgpasswd failed.
1065 * Conditional on lp_unix_password_sync() because we don't want
1066 * to touch the unix db unless we have admin permission.
1069 if(lp_unix_password_sync() &&
1070 !chgpasswd(username, rhost, pass, old_passwd, new_passwd,
1073 return NT_STATUS_ACCESS_DENIED;
1078 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1079 return NT_STATUS_ACCESS_DENIED;
1082 /* Now write it into the file. */
1083 return pdb_update_sam_account (hnd);
1086 /***********************************************************
1087 Code to check and change the OEM hashed password.
1088 ************************************************************/
1090 NTSTATUS pass_oem_change(char *user, const char *rhost,
1091 uchar password_encrypted_with_lm_hash[516],
1092 const uchar old_lm_hash_encrypted[16],
1093 uchar password_encrypted_with_nt_hash[516],
1094 const uchar old_nt_hash_encrypted[16],
1095 enum samPwdChangeReason *reject_reason)
1097 char *new_passwd = NULL;
1098 struct samu *sampass = NULL;
1102 if (!(sampass = samu_new(NULL))) {
1103 return NT_STATUS_NO_MEMORY;
1107 ret = pdb_getsampwnam(sampass, user);
1111 DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
1112 TALLOC_FREE(sampass);
1113 return NT_STATUS_NO_SUCH_USER;
1116 nt_status = check_oem_password(user,
1117 password_encrypted_with_lm_hash,
1118 old_lm_hash_encrypted,
1119 password_encrypted_with_nt_hash,
1120 old_nt_hash_encrypted,
1124 if (!NT_STATUS_IS_OK(nt_status)) {
1125 TALLOC_FREE(sampass);
1129 /* We've already checked the old password here.... */
1131 nt_status = change_oem_password(sampass, rhost, NULL, new_passwd,
1132 True, reject_reason);
1135 memset(new_passwd, 0, strlen(new_passwd));
1137 TALLOC_FREE(sampass);