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 2 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, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 /* These comments regard the code to change the user's unix password: */
24 /* fork a child process to exec passwd and write to its
25 * tty to change a users password. This is running as the
26 * user who is attempting to change the password.
30 * This code was copied/borrowed and stolen from various sources.
31 * The primary source was the poppasswd.c from the authors of POPMail. This software
32 * was included as a client to change passwords using the 'passwd' program
33 * on the remote machine.
35 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
36 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
37 * and rights to modify, distribute or incorporate this change to the CAP suite or
38 * using it for any other reason are granted, so long as this disclaimer is left intact.
42 This code was hacked considerably for inclusion in Samba, primarily
43 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
44 of the "password chat" option, which allows the easy runtime
45 specification of the expected sequence of events to change a
51 extern struct passdb_ops pdb_ops;
53 static NTSTATUS check_oem_password(const char *user,
54 uchar password_encrypted_with_lm_hash[516],
55 const uchar old_lm_hash_encrypted[16],
56 uchar password_encrypted_with_nt_hash[516],
57 const uchar old_nt_hash_encrypted[16],
58 SAM_ACCOUNT **hnd, char *new_passwd,
61 #if ALLOW_CHANGE_PASSWORD
63 static int findpty(char **slave)
70 #if defined(HAVE_GRANTPT)
71 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
72 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
76 *slave = (char *)ptsname(master);
80 ("findpty: Unable to create master/slave pty pair.\n"));
81 /* Stop fd leak on error. */
88 ("findpty: Allocated slave pty %s\n", *slave));
92 #endif /* HAVE_GRANTPT */
94 fstrcpy(line, "/dev/ptyXX");
96 dirp = opendir("/dev");
99 while ((dpname = readdirname(dirp)) != NULL)
101 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
104 ("pty: try to open %s, line was %s\n", dpname,
108 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
110 DEBUG(3, ("pty: opened %s\n", line));
122 static int dochild(int master, const char *slavedev, const struct passwd *pass,
123 const char *passwordprogram, BOOL as_root)
126 struct termios stermios;
133 ("dochild: user doesn't exist in the UNIX password database.\n"));
140 gain_root_privilege();
142 /* Start new session - gets rid of controlling terminal. */
146 ("Weirdness, couldn't let go of controlling terminal\n"));
150 /* Open slave pty and acquire as new controlling terminal. */
151 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
153 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
157 ioctl(slave, I_PUSH, "ptem");
158 ioctl(slave, I_PUSH, "ldterm");
159 #elif defined(TIOCSCTTY)
160 if (ioctl(slave, TIOCSCTTY, 0) < 0)
162 DEBUG(3, ("Error in ioctl call for slave pty\n"));
170 /* Make slave stdin/out/err of child. */
172 if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
174 DEBUG(3, ("Could not re-direct stdin\n"));
177 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
179 DEBUG(3, ("Could not re-direct stdout\n"));
182 if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
184 DEBUG(3, ("Could not re-direct stderr\n"));
190 /* Set proper terminal attributes - no echo, canonical input processing,
191 no map NL to CR/NL on output. */
193 if (tcgetattr(0, &stermios) < 0)
196 ("could not read default terminal attributes on pty\n"));
199 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
200 stermios.c_lflag |= ICANON;
202 stermios.c_oflag &= ~(ONLCR);
204 if (tcsetattr(0, TCSANOW, &stermios) < 0)
206 DEBUG(3, ("could not set attributes of pty\n"));
210 /* make us completely into the right uid */
213 become_user_permanently(uid, gid);
217 ("Invoking '%s' as password change program.\n",
220 /* execl() password-change application */
221 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
223 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
229 static int expect(int master, char *issue, char *expected)
232 int attempts, timeout, nread, len;
235 for (attempts = 0; attempts < 2; attempts++) {
236 if (!strequal(issue, ".")) {
237 if (lp_passwd_chat_debug())
238 DEBUG(100, ("expect: sending [%s]\n", issue));
240 if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
241 DEBUG(2,("expect: (short) write returned %d\n", len ));
246 if (strequal(expected, "."))
249 /* Initial timeout. */
250 timeout = lp_passwd_chat_timeout() * 1000;
254 while ((len = read_socket_with_timeout(master, buffer + nread, 1,
255 sizeof(buffer) - nread - 1,
261 /* Eat leading/trailing whitespace before match. */
263 pstrcpy( str, buffer);
264 trim_char( str, ' ', ' ');
266 if ((match = (unix_wild_match(expected, str) == 0))) {
267 /* Now data has started to return, lower timeout. */
268 timeout = lp_passwd_chat_timeout() * 100;
273 if (lp_passwd_chat_debug())
274 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
275 expected, buffer, match ? "yes" : "no" ));
281 DEBUG(2, ("expect: %s\n", strerror(errno)));
286 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
290 static void pwd_sub(char *buf)
292 all_string_sub(buf, "\\n", "\n", 0);
293 all_string_sub(buf, "\\r", "\r", 0);
294 all_string_sub(buf, "\\s", " ", 0);
295 all_string_sub(buf, "\\t", "\t", 0);
298 static int talktochild(int master, const char *seq)
301 fstring issue, expected;
305 while (next_token(&seq, expected, NULL, sizeof(expected)))
310 if (!expect(master, issue, expected))
312 DEBUG(3, ("Response %d incorrect\n", count));
316 if (!next_token(&seq, issue, NULL, sizeof(issue)))
321 if (!strequal(issue, ".")) {
322 /* we have one final issue to send */
323 fstrcpy(expected, ".");
324 if (!expect(master, issue, expected))
331 static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
332 char *chatsequence, BOOL as_root)
341 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
345 /* allocate a pseudo-terminal device */
346 if ((master = findpty(&slavedev)) < 0) {
347 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
352 * We need to temporarily stop CatchChild from eating
353 * SIGCLD signals as it also eats the exit status code. JRA.
356 CatchChildLeaveStatus();
358 if ((pid = sys_fork()) < 0) {
359 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
365 /* we now have a pty */
366 if (pid > 0) { /* This is the parent process */
367 if ((chstat = talktochild(master, chatsequence)) == False) {
368 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
369 kill(pid, SIGKILL); /* be sure to end this process */
372 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
373 if (errno == EINTR) {
381 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
388 * Go back to ignoring children.
395 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
398 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
399 DEBUG(3, ("chat_with_program: The process exited with status %d \
400 while we were waiting\n", WEXITSTATUS(wstat)));
403 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
404 else if (WIFSIGNALLED(wstat)) {
405 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
406 while we were waiting\n", WTERMSIG(wstat)));
414 * Lose any oplock capabilities.
416 oplock_set_capability(False, False);
418 /* make sure it doesn't freeze */
424 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
425 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
426 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
432 * The child should never return from dochild() ....
435 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
440 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
441 (chstat ? "" : "un"), pass->pw_name));
445 BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
447 pstring passwordprogram;
448 pstring chatsequence;
455 DEBUG(1, ("chgpasswd: NULL username specfied !\n"));
458 pass = Get_Pwnam(name);
460 DEBUG(1, ("chgpasswd: Username does not exist in system !\n"));
468 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
471 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
474 /* Take the passed information and test it for minimum criteria */
475 /* Minimum password length */
476 if (strlen(newpass) < lp_min_passwd_length()) {
477 /* too short, must be at least MINPASSWDLENGTH */
478 DEBUG(0, ("chgpasswd: Password Change: user %s, New password is shorter than minimum password length = %d\n",
479 name, lp_min_passwd_length()));
480 return (False); /* inform the user */
483 /* Password is same as old password */
484 if (strcmp(oldpass, newpass) == 0) {
485 /* don't allow same password */
486 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
487 return (False); /* inform the user */
491 * Check the old and new passwords don't contain any control
495 len = strlen(oldpass);
496 for (i = 0; i < len; i++) {
497 if (iscntrl((int)oldpass[i])) {
498 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
503 len = strlen(newpass);
504 for (i = 0; i < len; i++) {
505 if (iscntrl((int)newpass[i])) {
506 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
512 if (lp_pam_password_change()) {
519 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
521 ret = smb_pam_passchange(name, oldpass, newpass);
531 /* A non-PAM password change just doen't make sense without a valid local user */
534 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
538 pstrcpy(passwordprogram, lp_passwd_program());
539 pstrcpy(chatsequence, lp_passwd_chat());
541 if (!*chatsequence) {
542 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
546 if (!*passwordprogram) {
547 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
552 /* The password program *must* contain the user name to work. Fail if not. */
553 if (strstr(passwordprogram, "%u") == NULL) {
554 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
555 the string %%u, and the given string %s does not.\n", passwordprogram ));
560 pstring_sub(passwordprogram, "%u", name);
561 /* note that we do NOT substitute the %o and %n in the password program
562 as this would open up a security hole where the user could use
563 a new password containing shell escape characters */
565 pstring_sub(chatsequence, "%u", name);
566 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
567 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
568 return (chat_with_program
569 (passwordprogram, pass, chatsequence, as_root));
572 #else /* ALLOW_CHANGE_PASSWORD */
574 BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
576 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
579 #endif /* ALLOW_CHANGE_PASSWORD */
581 /***********************************************************
582 Code to check the lanman hashed password.
583 ************************************************************/
585 BOOL check_lanman_password(char *user, uchar * pass1,
586 uchar * pass2, SAM_ACCOUNT **hnd)
588 uchar unenc_new_pw[16];
589 uchar unenc_old_pw[16];
590 SAM_ACCOUNT *sampass = NULL;
592 const uint8 *lanman_pw;
596 ret = pdb_getsampwnam(sampass, user);
600 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
601 pdb_free_sam(&sampass);
605 acct_ctrl = pdb_get_acct_ctrl (sampass);
606 lanman_pw = pdb_get_lanman_passwd (sampass);
608 if (acct_ctrl & ACB_DISABLED) {
609 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
610 pdb_free_sam(&sampass);
614 if (lanman_pw == NULL) {
615 if (acct_ctrl & ACB_PWNOTREQ) {
616 /* this saves the pointer for the caller */
620 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
621 pdb_free_sam(&sampass);
626 /* Get the new lanman hash. */
627 D_P16(lanman_pw, pass2, unenc_new_pw);
629 /* Use this to get the old lanman hash. */
630 D_P16(unenc_new_pw, pass1, unenc_old_pw);
632 /* Check that the two old passwords match. */
633 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
634 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
635 pdb_free_sam(&sampass);
639 /* this saves the pointer for the caller */
644 /***********************************************************
645 Code to change the lanman hashed password.
646 It nulls out the NT hashed password as it will
648 NOTE this function is designed to be called as root. Check the old password
649 is correct before calling. JRA.
650 ************************************************************/
652 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2)
654 static uchar null_pw[16];
655 uchar unenc_new_pw[16];
660 if (sampass == NULL) {
661 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
665 acct_ctrl = pdb_get_acct_ctrl(sampass);
666 pwd = pdb_get_lanman_passwd(sampass);
668 if (acct_ctrl & ACB_DISABLED) {
669 DEBUG(0,("change_lanman_password: account %s disabled.\n",
670 pdb_get_username(sampass)));
675 if (acct_ctrl & ACB_PWNOTREQ) {
677 memset(no_pw, '\0', 14);
678 E_P16(no_pw, null_pw);
680 /* Get the new lanman hash. */
681 D_P16(null_pw, pass2, unenc_new_pw);
683 DEBUG(0,("change_lanman_password: no lanman password !\n"));
687 /* Get the new lanman hash. */
688 D_P16(pwd, pass2, unenc_new_pw);
691 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
695 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
696 return False; /* We lose the NT hash. Sorry. */
699 if (!pdb_set_pass_changed_now (sampass)) {
700 pdb_free_sam(&sampass);
701 /* Not quite sure what this one qualifies as, but this will do */
705 /* Now flush the sam_passwd struct to persistent storage */
706 ret = pdb_update_sam_account (sampass);
711 /***********************************************************
712 Code to check and change the OEM hashed password.
713 ************************************************************/
715 NTSTATUS pass_oem_change(char *user,
716 uchar password_encrypted_with_lm_hash[516],
717 const uchar old_lm_hash_encrypted[16],
718 uchar password_encrypted_with_nt_hash[516],
719 const uchar old_nt_hash_encrypted[16])
722 SAM_ACCOUNT *sampass = NULL;
723 NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash,
724 old_lm_hash_encrypted,
725 password_encrypted_with_nt_hash,
726 old_nt_hash_encrypted,
727 &sampass, new_passwd, sizeof(new_passwd));
729 if (!NT_STATUS_IS_OK(nt_status))
732 /* We've already checked the old password here.... */
734 nt_status = change_oem_password(sampass, NULL, new_passwd, True);
737 memset(new_passwd, 0, sizeof(new_passwd));
739 pdb_free_sam(&sampass);
744 /***********************************************************
745 Decrypt and verify a user password change.
747 The 516 byte long buffers are encrypted with the old NT and
748 old LM passwords, and if the NT passwords are present, both
749 buffers contain a unicode string.
751 After decrypting the buffers, check the password is correct by
752 matching the old hashed passwords with the passwords in the passdb.
754 ************************************************************/
756 static NTSTATUS check_oem_password(const char *user,
757 uchar password_encrypted_with_lm_hash[516],
758 const uchar old_lm_hash_encrypted[16],
759 uchar password_encrypted_with_nt_hash[516],
760 const uchar old_nt_hash_encrypted[16],
761 SAM_ACCOUNT **hnd, char *new_passwd,
764 static uchar null_pw[16];
765 static uchar null_ntpw[16];
766 SAM_ACCOUNT *sampass = NULL;
767 char *password_encrypted;
768 const char *encryption_key;
769 const uint8 *lanman_pw, *nt_pw;
772 uchar new_nt_hash[16];
773 uchar old_nt_hash_plain[16];
774 uchar new_lm_hash[16];
775 uchar old_lm_hash_plain[16];
779 BOOL nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
780 BOOL lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
784 pdb_init_sam(&sampass);
787 ret = pdb_getsampwnam(sampass, user);
791 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
792 pdb_free_sam(&sampass);
793 return NT_STATUS_NO_SUCH_USER;
796 acct_ctrl = pdb_get_acct_ctrl(sampass);
798 if (acct_ctrl & ACB_DISABLED) {
799 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
800 pdb_free_sam(&sampass);
801 return NT_STATUS_ACCOUNT_DISABLED;
804 if (acct_ctrl & ACB_PWNOTREQ && lp_null_passwords()) {
805 /* construct a null password (in case one is needed */
808 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
813 /* save pointers to passwords so we don't have to keep looking them up */
814 if (lp_lanman_auth()) {
815 lanman_pw = pdb_get_lanman_passwd(sampass);
819 nt_pw = pdb_get_nt_passwd(sampass);
822 if (nt_pw && nt_pass_set) {
823 /* IDEAL Case: passwords are in unicode, and we can
824 * read use the password encrypted with the NT hash
826 password_encrypted = password_encrypted_with_nt_hash;
827 encryption_key = nt_pw;
828 } else if (lanman_pw && lm_pass_set) {
829 /* password may still be in unicode, but use LM hash version */
830 password_encrypted = password_encrypted_with_lm_hash;
831 encryption_key = lanman_pw;
832 } else if (nt_pass_set) {
833 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
835 pdb_free_sam(&sampass);
836 return NT_STATUS_WRONG_PASSWORD;
837 } else if (lm_pass_set) {
838 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
840 pdb_free_sam(&sampass);
841 return NT_STATUS_WRONG_PASSWORD;
843 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
845 pdb_free_sam(&sampass);
846 return NT_STATUS_WRONG_PASSWORD;
850 * Decrypt the password with the key
852 SamOEMhash( password_encrypted, encryption_key, 516);
854 if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len,
855 nt_pass_set ? STR_UNICODE : STR_ASCII)) {
856 pdb_free_sam(&sampass);
857 return NT_STATUS_WRONG_PASSWORD;
861 * To ensure we got the correct new password, hash it and
862 * use it as a key to test the passed old password.
866 /* NT passwords, verify the NT hash. */
868 /* Calculate the MD4 hash (NT compatible) of the password */
869 memset(new_nt_hash, '\0', 16);
870 E_md4hash(new_passwd, new_nt_hash);
874 * Now use new_nt_hash as the key to see if the old
877 D_P16(new_nt_hash, old_nt_hash_encrypted, old_nt_hash_plain);
879 if (memcmp(nt_pw, old_nt_hash_plain, 16)) {
880 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
881 pdb_free_sam(&sampass);
882 return NT_STATUS_WRONG_PASSWORD;
885 /* We could check the LM password here, but there is
886 * little point, we already know the password is
887 * correct, and the LM password might not even be
890 /* Further, LM hash generation algorithms
891 * differ with charset, so we could
892 * incorrectly fail a perfectly valid password
894 #ifdef DEBUG_PASSWORD
896 ("check_oem_password: password %s ok\n", new_passwd));
904 * Now use new_nt_hash as the key to see if the old
905 * LM password matches.
907 D_P16(new_nt_hash, old_lm_hash_encrypted, old_lm_hash_plain);
909 if (memcmp(lanman_pw, old_lm_hash_plain, 16)) {
910 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
911 pdb_free_sam(&sampass);
912 return NT_STATUS_WRONG_PASSWORD;
914 #ifdef DEBUG_PASSWORD
916 ("check_oem_password: password %s ok\n", new_passwd));
923 if (lanman_pw && lm_pass_set) {
925 E_deshash(new_passwd, new_lm_hash);
928 * Now use new_lm_hash as the key to see if the old
931 D_P16(new_lm_hash, old_lm_hash_encrypted, old_lm_hash_plain);
933 if (memcmp(lanman_pw, old_lm_hash_plain, 16)) {
934 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
935 pdb_free_sam(&sampass);
936 return NT_STATUS_WRONG_PASSWORD;
939 #ifdef DEBUG_PASSWORD
941 ("check_oem_password: password %s ok\n", new_passwd));
947 /* should not be reached */
948 pdb_free_sam(&sampass);
949 return NT_STATUS_WRONG_PASSWORD;
952 /***********************************************************
953 Code to change the oem password. Changes both the lanman
954 and NT hashes. Old_passwd is almost always NULL.
955 NOTE this function is designed to be called as root. Check the old password
956 is correct before calling. JRA.
957 ************************************************************/
959 NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root)
964 if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
965 DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
966 pdb_get_username(hnd), http_timestring(pdb_get_pass_can_change_time(hnd))));
967 return NT_STATUS_PASSWORD_RESTRICTION;
970 if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (strlen(new_passwd) < min_len)) {
971 DEBUG(1, ("user %s cannot change password - password too short\n",
972 pdb_get_username(hnd)));
973 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
974 return NT_STATUS_PASSWORD_RESTRICTION;
975 /* return NT_STATUS_PWD_TOO_SHORT; */
978 /* Take the passed information and test it for minimum criteria */
979 /* Minimum password length */
980 if (strlen(new_passwd) < lp_min_passwd_length()) {
981 /* too short, must be at least MINPASSWDLENGTH */
982 DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
983 pdb_get_username(hnd), lp_min_passwd_length()));
984 return NT_STATUS_PASSWORD_RESTRICTION;
985 /* return NT_STATUS_PWD_TOO_SHORT; */
988 /* TODO: Add cracklib support here */
991 * If unix password sync was requested, attempt to change
992 * the /etc/passwd database first. Return failure if this cannot
995 * This occurs before the oem change, because we don't want to
996 * update it if chgpasswd failed.
998 * Conditional on lp_unix_password_sync() because we don't want
999 * to touch the unix db unless we have admin permission.
1002 if(lp_unix_password_sync() &&
1003 !chgpasswd(pdb_get_username(hnd), old_passwd, new_passwd, as_root)) {
1004 return NT_STATUS_ACCESS_DENIED;
1007 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1008 return NT_STATUS_ACCESS_DENIED;
1011 /* Now write it into the file. */
1012 ret = pdb_update_sam_account (hnd);
1015 return NT_STATUS_ACCESS_DENIED;
1018 return NT_STATUS_OK;