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 "../libcli/auth/libcli_auth.h"
51 #include "../lib/crypto/arcfour.h"
52 #include "rpc_server/samr/srv_samr_util.h"
54 #ifndef ALLOW_CHANGE_PASSWORD
55 #if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
56 #define ALLOW_CHANGE_PASSWORD 1
60 #if ALLOW_CHANGE_PASSWORD
62 static int findpty(char **slave)
66 SMB_STRUCT_DIR *dirp = NULL;
71 #if defined(HAVE_GRANTPT)
72 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
73 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) {
76 line = (char *)ptsname(master);
78 *slave = SMB_STRDUP(line);
83 ("findpty: Unable to create master/slave pty pair.\n"));
84 /* Stop fd leak on error. */
89 ("findpty: Allocated slave pty %s\n", *slave));
93 #endif /* HAVE_GRANTPT */
95 line = SMB_STRDUP("/dev/ptyXX");
100 dirp = sys_opendir("/dev");
106 while ((dpname = readdirname(dirp)) != NULL) {
107 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
109 ("pty: try to open %s, line was %s\n", dpname,
113 if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
114 DEBUG(3, ("pty: opened %s\n", line));
127 static int dochild(int master, const char *slavedev, const struct passwd *pass,
128 const char *passwordprogram, bool as_root)
131 struct termios stermios;
134 char * const eptrs[1] = { NULL };
139 ("dochild: user doesn't exist in the UNIX password database.\n"));
146 gain_root_privilege();
148 /* Start new session - gets rid of controlling terminal. */
152 ("Weirdness, couldn't let go of controlling terminal\n"));
156 /* Open slave pty and acquire as new controlling terminal. */
157 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
159 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
162 #if defined(TIOCSCTTY) && !defined(SUNOS5)
164 * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
165 * see the discussion under
166 * https://bugzilla.samba.org/show_bug.cgi?id=5366.
168 if (ioctl(slave, TIOCSCTTY, 0) < 0)
170 DEBUG(3, ("Error in ioctl call for slave pty\n"));
173 #elif defined(I_PUSH) && defined(I_FIND)
174 if (ioctl(slave, I_FIND, "ptem") == 0) {
175 ioctl(slave, I_PUSH, "ptem");
177 if (ioctl(slave, I_FIND, "ldterm") == 0) {
178 ioctl(slave, I_PUSH, "ldterm");
185 /* Make slave stdin/out/err of child. */
187 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
189 DEBUG(3, ("Could not re-direct stdin\n"));
192 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
194 DEBUG(3, ("Could not re-direct stdout\n"));
197 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
199 DEBUG(3, ("Could not re-direct stderr\n"));
205 /* Set proper terminal attributes - no echo, canonical input processing,
206 no map NL to CR/NL on output. */
208 if (tcgetattr(0, &stermios) < 0)
211 ("could not read default terminal attributes on pty\n"));
214 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
215 stermios.c_lflag |= ICANON;
217 stermios.c_oflag &= ~(ONLCR);
219 if (tcsetattr(0, TCSANOW, &stermios) < 0)
221 DEBUG(3, ("could not set attributes of pty\n"));
225 /* make us completely into the right uid */
228 become_user_permanently(uid, gid);
232 ("Invoking '%s' as password change program.\n",
235 /* execl() password-change application */
236 if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
238 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
244 static int expect(int master, char *issue, char *expected)
247 int attempts, timeout, nread;
251 for (attempts = 0; attempts < 2; attempts++) {
253 if (!strequal(issue, ".")) {
254 if (lp_passwd_chat_debug())
255 DEBUG(100, ("expect: sending [%s]\n", issue));
257 if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
258 DEBUG(2,("expect: (short) write returned %d\n",
264 if (strequal(expected, "."))
267 /* Initial timeout. */
268 timeout = lp_passwd_chat_timeout() * 1000;
273 status = read_fd_with_timeout(
274 master, buffer + nread, 1,
275 sizeof(buffer) - nread - 1,
278 if (!NT_STATUS_IS_OK(status)) {
279 DEBUG(2, ("expect: read error %s\n",
287 /* Eat leading/trailing whitespace before match. */
288 char *str = SMB_STRDUP(buffer);
290 DEBUG(2,("expect: ENOMEM\n"));
293 trim_char(str, ' ', ' ');
295 if ((match = unix_wild_match(expected, str)) == True) {
296 /* Now data has started to return, lower timeout. */
297 timeout = lp_passwd_chat_timeout() * 100;
303 if (lp_passwd_chat_debug())
304 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
305 expected, buffer, match ? "yes" : "no" ));
310 if (!NT_STATUS_IS_OK(status)) {
311 DEBUG(2, ("expect: %s\n", nt_errstr(status)));
316 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
320 static void pwd_sub(char *buf)
322 all_string_sub(buf, "\\n", "\n", 0);
323 all_string_sub(buf, "\\r", "\r", 0);
324 all_string_sub(buf, "\\s", " ", 0);
325 all_string_sub(buf, "\\t", "\t", 0);
328 static int talktochild(int master, const char *seq)
330 TALLOC_CTX *frame = talloc_stackframe();
335 issue = talloc_strdup(frame, ".");
341 while (next_token_talloc(frame, &seq, &expected, NULL)) {
345 if (!expect(master, issue, expected)) {
346 DEBUG(3, ("Response %d incorrect\n", count));
351 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
352 issue = talloc_strdup(frame, ".");
361 if (!strequal(issue, ".")) {
362 /* we have one final issue to send */
363 expected = talloc_strdup(frame, ".");
368 if (!expect(master, issue, expected)) {
377 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
378 char *chatsequence, bool as_root)
380 char *slavedev = NULL;
387 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
391 /* allocate a pseudo-terminal device */
392 if ((master = findpty(&slavedev)) < 0) {
393 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
398 * We need to temporarily stop CatchChild from eating
399 * SIGCLD signals as it also eats the exit status code. JRA.
402 CatchChildLeaveStatus();
404 if ((pid = sys_fork()) < 0) {
405 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
412 /* we now have a pty */
413 if (pid > 0) { /* This is the parent process */
414 /* Don't need this anymore in parent. */
417 if ((chstat = talktochild(master, chatsequence)) == False) {
418 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
419 kill(pid, SIGKILL); /* be sure to end this process */
422 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
423 if (errno == EINTR) {
431 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
438 * Go back to ignoring children.
445 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
448 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
449 DEBUG(3, ("chat_with_program: The process exited with status %d \
450 while we were waiting\n", WEXITSTATUS(wstat)));
453 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
454 else if (WIFSIGNALLED(wstat)) {
455 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
456 while we were waiting\n", WTERMSIG(wstat)));
464 * Lose any elevated privileges.
466 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
467 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
469 /* make sure it doesn't freeze */
475 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
476 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
477 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
483 * The child should never return from dochild() ....
486 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
491 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
492 (chstat ? "" : "un"), pass->pw_name));
496 bool chgpasswd(const char *name, const char *rhost, const struct passwd *pass,
497 const char *oldpass, const char *newpass, bool as_root)
499 char *passwordprogram = NULL;
500 char *chatsequence = NULL;
503 TALLOC_CTX *ctx = talloc_tos();
509 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
511 #ifdef DEBUG_PASSWORD
512 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
515 /* Take the passed information and test it for minimum criteria */
517 /* Password is same as old password */
518 if (strcmp(oldpass, newpass) == 0) {
519 /* don't allow same password */
520 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
521 return (False); /* inform the user */
525 * Check the old and new passwords don't contain any control
529 len = strlen(oldpass);
530 for (i = 0; i < len; i++) {
531 if (iscntrl((int)oldpass[i])) {
532 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
537 len = strlen(newpass);
538 for (i = 0; i < len; i++) {
539 if (iscntrl((int)newpass[i])) {
540 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
546 if (lp_pam_password_change()) {
548 #ifdef HAVE_SETLOCALE
549 const char *prevlocale = setlocale(LC_ALL, "C");
556 ret = smb_pam_passchange(pass->pw_name, rhost,
559 ret = smb_pam_passchange(name, rhost, oldpass,
566 #ifdef HAVE_SETLOCALE
567 setlocale(LC_ALL, prevlocale);
574 /* A non-PAM password change just doen't make sense without a valid local user */
577 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
581 passwordprogram = talloc_strdup(ctx, lp_passwd_program());
582 if (!passwordprogram || !*passwordprogram) {
583 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
586 chatsequence = talloc_strdup(ctx, lp_passwd_chat());
587 if (!chatsequence || !*chatsequence) {
588 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
593 /* The password program *must* contain the user name to work. Fail if not. */
594 if (strstr_m(passwordprogram, "%u") == NULL) {
595 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
596 the string %%u, and the given string %s does not.\n", passwordprogram ));
601 passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
602 if (!passwordprogram) {
606 /* note that we do NOT substitute the %o and %n in the password program
607 as this would open up a security hole where the user could use
608 a new password containing shell escape characters */
610 chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
614 chatsequence = talloc_all_string_sub(ctx,
621 chatsequence = talloc_all_string_sub(ctx,
625 return chat_with_program(passwordprogram,
631 #else /* ALLOW_CHANGE_PASSWORD */
633 bool chgpasswd(const char *name, const struct passwd *pass,
634 const char *oldpass, const char *newpass, bool as_root)
636 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
639 #endif /* ALLOW_CHANGE_PASSWORD */
641 /***********************************************************
642 Decrypt and verify a user password change.
644 The 516 byte long buffers are encrypted with the old NT and
645 old LM passwords, and if the NT passwords are present, both
646 buffers contain a unicode string.
648 After decrypting the buffers, check the password is correct by
649 matching the old hashed passwords with the passwords in the passdb.
651 ************************************************************/
653 static NTSTATUS check_oem_password(const char *user,
654 uchar password_encrypted_with_lm_hash[516],
655 const uchar old_lm_hash_encrypted[16],
656 uchar password_encrypted_with_nt_hash[516],
657 const uchar old_nt_hash_encrypted[16],
658 struct samu *sampass,
659 char **pp_new_passwd)
663 uint8 *password_encrypted;
664 const uint8 *encryption_key;
665 const uint8 *lanman_pw, *nt_pw;
668 uchar new_nt_hash[16];
669 uchar new_lm_hash[16];
673 bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
674 bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
676 acct_ctrl = pdb_get_acct_ctrl(sampass);
678 /* I am convinced this check here is wrong, it is valid to
679 * change a password of a user that has a disabled account - gd */
681 if (acct_ctrl & ACB_DISABLED) {
682 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
683 return NT_STATUS_ACCOUNT_DISABLED;
686 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
687 /* construct a null password (in case one is needed */
690 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
695 /* save pointers to passwords so we don't have to keep looking them up */
696 if (lp_lanman_auth()) {
697 lanman_pw = pdb_get_lanman_passwd(sampass);
701 nt_pw = pdb_get_nt_passwd(sampass);
704 if (nt_pw && nt_pass_set) {
705 /* IDEAL Case: passwords are in unicode, and we can
706 * read use the password encrypted with the NT hash
708 password_encrypted = password_encrypted_with_nt_hash;
709 encryption_key = nt_pw;
710 } else if (lanman_pw && lm_pass_set) {
711 /* password may still be in unicode, but use LM hash version */
712 password_encrypted = password_encrypted_with_lm_hash;
713 encryption_key = lanman_pw;
714 } else if (nt_pass_set) {
715 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
717 return NT_STATUS_WRONG_PASSWORD;
718 } else if (lm_pass_set) {
719 if (lp_lanman_auth()) {
720 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
723 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
726 return NT_STATUS_WRONG_PASSWORD;
728 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
730 return NT_STATUS_WRONG_PASSWORD;
734 * Decrypt the password with the key
736 arcfour_crypt( password_encrypted, encryption_key, 516);
738 if (!decode_pw_buffer(talloc_tos(),
742 nt_pass_set ? CH_UTF16 : CH_DOS)) {
743 return NT_STATUS_WRONG_PASSWORD;
747 * To ensure we got the correct new password, hash it and
748 * use it as a key to test the passed old password.
752 /* NT passwords, verify the NT hash. */
754 /* Calculate the MD4 hash (NT compatible) of the password */
755 memset(new_nt_hash, '\0', 16);
756 E_md4hash(*pp_new_passwd, new_nt_hash);
760 * check the NT verifier
762 E_old_pw_hash(new_nt_hash, nt_pw, verifier);
763 if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
764 DEBUG(0, ("check_oem_password: old nt "
765 "password doesn't match.\n"));
766 return NT_STATUS_WRONG_PASSWORD;
769 /* We could check the LM password here, but there is
770 * little point, we already know the password is
771 * correct, and the LM password might not even be
774 /* Further, LM hash generation algorithms
775 * differ with charset, so we could
776 * incorrectly fail a perfectly valid password
778 #ifdef DEBUG_PASSWORD
780 ("check_oem_password: password %s ok\n", *pp_new_passwd));
787 * check the lm verifier
789 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
790 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
791 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
792 return NT_STATUS_WRONG_PASSWORD;
794 #ifdef DEBUG_PASSWORD
796 ("check_oem_password: password %s ok\n", *pp_new_passwd));
802 if (lanman_pw && lm_pass_set) {
804 E_deshash(*pp_new_passwd, new_lm_hash);
807 * check the lm verifier
809 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
810 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
811 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
812 return NT_STATUS_WRONG_PASSWORD;
815 #ifdef DEBUG_PASSWORD
817 ("check_oem_password: password %s ok\n", *pp_new_passwd));
822 /* should not be reached */
823 return NT_STATUS_WRONG_PASSWORD;
826 static bool password_in_history(uint8_t nt_pw[NT_HASH_LEN],
827 uint32_t pw_history_len,
828 const uint8_t *pw_history)
830 static const uint8_t zero_md5_nt_pw[SALTED_MD5_HASH_LEN] = { 0, };
833 dump_data(100, nt_pw, NT_HASH_LEN);
834 dump_data(100, pw_history, PW_HISTORY_ENTRY_LEN * pw_history_len);
836 for (i=0; i<pw_history_len; i++) {
837 uint8_t new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
838 const uint8_t *current_salt;
839 const uint8_t *old_nt_pw_salted_md5_hash;
841 current_salt = &pw_history[i*PW_HISTORY_ENTRY_LEN];
842 old_nt_pw_salted_md5_hash = current_salt + PW_HISTORY_SALT_LEN;
844 if (memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash,
845 SALTED_MD5_HASH_LEN) == 0) {
846 /* Ignore zero valued entries. */
850 if (memcmp(zero_md5_nt_pw, current_salt,
851 PW_HISTORY_SALT_LEN) == 0)
854 * New format: zero salt and then plain nt hash.
855 * Directly compare the hashes.
857 if (memcmp(nt_pw, old_nt_pw_salted_md5_hash,
858 SALTED_MD5_HASH_LEN) == 0)
864 * Old format: md5sum of salted nt hash.
865 * Create salted version of new pw to compare.
867 E_md5hash(current_salt, nt_pw, new_nt_pw_salted_md5_hash);
869 if (memcmp(new_nt_pw_salted_md5_hash,
870 old_nt_pw_salted_md5_hash,
871 SALTED_MD5_HASH_LEN) == 0) {
879 /***********************************************************
880 This routine takes the given password and checks it against
881 the password history. Returns True if this password has been
882 found in the history list.
883 ************************************************************/
885 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
887 uchar new_nt_p16[NT_HASH_LEN];
889 const uint8 *pwhistory;
890 uint32 pwHisLen, curr_pwHisLen;
892 pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &pwHisLen);
897 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
898 if (!pwhistory || curr_pwHisLen == 0) {
902 /* Only examine the minimum of the current history len and
903 the stored history len. Avoids race conditions. */
904 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
906 nt_pw = pdb_get_nt_passwd(sampass);
908 E_md4hash(plaintext, new_nt_p16);
910 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
911 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
912 pdb_get_username(sampass) ));
916 if (password_in_history(new_nt_p16, pwHisLen, pwhistory)) {
917 DEBUG(1,("check_passwd_history: proposed new password for "
918 "user %s found in history list !\n",
919 pdb_get_username(sampass) ));
925 /***********************************************************
926 ************************************************************/
928 NTSTATUS check_password_complexity(const char *username,
929 const char *password,
930 enum samPwdChangeReason *samr_reject_reason)
932 TALLOC_CTX *tosctx = talloc_tos();
936 /* Use external script to check password complexity */
937 if ((lp_check_password_script() == NULL)
938 || (*(lp_check_password_script()) == '\0')) {
942 cmd = talloc_string_sub(tosctx, lp_check_password_script(), "%u",
945 return NT_STATUS_PASSWORD_RESTRICTION;
948 check_ret = smbrunsecret(cmd, password);
949 DEBUG(5,("check_password_complexity: check password script (%s) "
950 "returned [%d]\n", cmd, check_ret));
953 if (check_ret != 0) {
954 DEBUG(1,("check_password_complexity: "
955 "check password script said new password is not good "
957 if (samr_reject_reason) {
958 *samr_reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
960 return NT_STATUS_PASSWORD_RESTRICTION;
966 /***********************************************************
967 Code to change the oem password. Changes both the lanman
968 and NT hashes. Old_passwd is almost always NULL.
969 NOTE this function is designed to be called as root. Check the old password
970 is correct before calling. JRA.
971 ************************************************************/
973 static NTSTATUS change_oem_password(struct samu *hnd, const char *rhost,
974 char *old_passwd, char *new_passwd,
976 enum samPwdChangeReason *samr_reject_reason)
980 TALLOC_CTX *tosctx = talloc_tos();
981 struct passwd *pass = NULL;
982 const char *username = pdb_get_username(hnd);
983 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
986 if (samr_reject_reason) {
987 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
990 /* check to see if the secdesc has previously been set to disallow */
991 if (!pdb_get_pass_can_change(hnd)) {
992 DEBUG(1, ("user %s does not have permissions to change password\n", username));
993 if (samr_reject_reason) {
994 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
996 return NT_STATUS_ACCOUNT_RESTRICTION;
999 /* check to see if it is a Machine account and if the policy
1000 * denies machines to change the password. *
1001 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1002 if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1003 if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1004 DEBUG(1, ("Machine %s cannot change password now, "
1005 "denied by Refuse Machine Password Change policy\n",
1007 if (samr_reject_reason) {
1008 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1010 return NT_STATUS_ACCOUNT_RESTRICTION;
1014 /* removed calculation here, because passdb now calculates
1015 based on policy. jmcd */
1016 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1017 DEBUG(1, ("user %s cannot change password now, must "
1018 "wait until %s\n", username,
1019 http_timestring(tosctx, can_change_time)));
1020 if (samr_reject_reason) {
1021 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1023 return NT_STATUS_ACCOUNT_RESTRICTION;
1026 if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1027 DEBUG(1, ("user %s cannot change password - password too short\n",
1029 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1030 if (samr_reject_reason) {
1031 *samr_reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1033 return NT_STATUS_PASSWORD_RESTRICTION;
1034 /* return NT_STATUS_PWD_TOO_SHORT; */
1037 if (check_passwd_history(hnd,new_passwd)) {
1038 if (samr_reject_reason) {
1039 *samr_reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1041 return NT_STATUS_PASSWORD_RESTRICTION;
1044 pass = Get_Pwnam_alloc(tosctx, username);
1046 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1047 return NT_STATUS_ACCESS_DENIED;
1050 status = check_password_complexity(username, new_passwd, samr_reject_reason);
1051 if (!NT_STATUS_IS_OK(status)) {
1057 * If unix password sync was requested, attempt to change
1058 * the /etc/passwd database first. Return failure if this cannot
1061 * This occurs before the oem change, because we don't want to
1062 * update it if chgpasswd failed.
1064 * Conditional on lp_unix_password_sync() because we don't want
1065 * to touch the unix db unless we have admin permission.
1068 if(lp_unix_password_sync() &&
1069 !chgpasswd(username, rhost, pass, old_passwd, new_passwd,
1072 return NT_STATUS_ACCESS_DENIED;
1077 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1078 return NT_STATUS_ACCESS_DENIED;
1081 /* Now write it into the file. */
1082 return pdb_update_sam_account (hnd);
1085 /***********************************************************
1086 Code to check and change the OEM hashed password.
1087 ************************************************************/
1089 NTSTATUS pass_oem_change(char *user, const char *rhost,
1090 uchar password_encrypted_with_lm_hash[516],
1091 const uchar old_lm_hash_encrypted[16],
1092 uchar password_encrypted_with_nt_hash[516],
1093 const uchar old_nt_hash_encrypted[16],
1094 enum samPwdChangeReason *reject_reason)
1096 char *new_passwd = NULL;
1097 struct samu *sampass = NULL;
1101 if (!(sampass = samu_new(NULL))) {
1102 return NT_STATUS_NO_MEMORY;
1106 ret = pdb_getsampwnam(sampass, user);
1110 DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
1111 TALLOC_FREE(sampass);
1112 return NT_STATUS_NO_SUCH_USER;
1115 nt_status = check_oem_password(user,
1116 password_encrypted_with_lm_hash,
1117 old_lm_hash_encrypted,
1118 password_encrypted_with_nt_hash,
1119 old_nt_hash_encrypted,
1123 if (!NT_STATUS_IS_OK(nt_status)) {
1124 TALLOC_FREE(sampass);
1128 /* We've already checked the old password here.... */
1130 nt_status = change_oem_password(sampass, rhost, NULL, new_passwd,
1131 True, reject_reason);
1134 memset(new_passwd, 0, strlen(new_passwd));
1136 TALLOC_FREE(sampass);