2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) Andrew Bartlett 2001-2002
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 /* fork a child process to exec passwd and write to its
23 * tty to change a users password. This is running as the
24 * user who is attempting to change the password.
28 * This code was copied/borrowed and stolen from various sources.
29 * The primary source was the poppasswd.c from the authors of POPMail. This software
30 * was included as a client to change passwords using the 'passwd' program
31 * on the remote machine.
33 * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE
34 * is defined in the compiler directives located in the Makefile.
36 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
37 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
38 * and rights to modify, distribute or incorporate this change to the CAP suite or
39 * using it for any other reason are granted, so long as this disclaimer is left intact.
43 This code was hacked considerably for inclusion in Samba, primarily
44 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
45 of the "password chat" option, which allows the easy runtime
46 specification of the expected sequence of events to change a
54 #ifndef HAVE_CRACKLIB_DICTPATH
55 #ifndef CRACKLIB_DICTPATH
56 #define CRACKLIB_DICTPATH SAMBA_CRACKLIB_DICTPATH
60 extern struct passdb_ops pdb_ops
;
62 static NTSTATUS
check_oem_password(const char *user
,
63 uchar
* lmdata
, const uchar
* lmhash
,
64 const uchar
* ntdata
, const uchar
* nthash
,
65 SAM_ACCOUNT
**hnd
, char *new_passwd
,
68 #if ALLOW_CHANGE_PASSWORD
70 static int findpty(char **slave
)
77 #if defined(HAVE_GRANTPT)
78 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
79 if ((master
= sys_open("/dev/ptmx", O_RDWR
, 0)) >= 0)
83 *slave
= (char *)ptsname(master
);
87 ("findpty: Unable to create master/slave pty pair.\n"));
88 /* Stop fd leak on error. */
95 ("findpty: Allocated slave pty %s\n", *slave
));
99 #endif /* HAVE_GRANTPT */
101 fstrcpy(line
, "/dev/ptyXX");
103 dirp
= opendir("/dev");
106 while ((dpname
= readdirname(dirp
)) != NULL
)
108 if (strncmp(dpname
, "pty", 3) == 0 && strlen(dpname
) == 5)
111 ("pty: try to open %s, line was %s\n", dpname
,
115 if ((master
= sys_open(line
, O_RDWR
, 0)) >= 0)
117 DEBUG(3, ("pty: opened %s\n", line
));
129 static int dochild(int master
, const char *slavedev
, const struct passwd
*pass
,
130 const char *passwordprogram
, BOOL as_root
)
133 struct termios stermios
;
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
));
164 ioctl(slave
, I_PUSH
, "ptem");
165 ioctl(slave
, I_PUSH
, "ldterm");
166 #elif defined(TIOCSCTTY)
167 if (ioctl(slave
, TIOCSCTTY
, 0) < 0)
169 DEBUG(3, ("Error in ioctl call for slave pty\n"));
177 /* Make slave stdin/out/err of child. */
179 if (sys_dup2(slave
, STDIN_FILENO
) != STDIN_FILENO
)
181 DEBUG(3, ("Could not re-direct stdin\n"));
184 if (sys_dup2(slave
, STDOUT_FILENO
) != STDOUT_FILENO
)
186 DEBUG(3, ("Could not re-direct stdout\n"));
189 if (sys_dup2(slave
, STDERR_FILENO
) != STDERR_FILENO
)
191 DEBUG(3, ("Could not re-direct stderr\n"));
197 /* Set proper terminal attributes - no echo, canonical input processing,
198 no map NL to CR/NL on output. */
200 if (tcgetattr(0, &stermios
) < 0)
203 ("could not read default terminal attributes on pty\n"));
206 stermios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
207 stermios
.c_lflag
|= ICANON
;
209 stermios
.c_oflag
&= ~(ONLCR
);
211 if (tcsetattr(0, TCSANOW
, &stermios
) < 0)
213 DEBUG(3, ("could not set attributes of pty\n"));
217 /* make us completely into the right uid */
220 become_user_permanently(uid
, gid
);
224 ("Invoking '%s' as password change program.\n",
227 /* execl() password-change application */
228 if (execl("/bin/sh", "sh", "-c", passwordprogram
, NULL
) < 0)
230 DEBUG(3, ("Bad status returned from %s\n", passwordprogram
));
236 static int expect(int master
, char *issue
, char *expected
)
239 int attempts
, timeout
, nread
, len
;
242 for (attempts
= 0; attempts
< 2; attempts
++) {
243 if (!strequal(issue
, ".")) {
244 if (lp_passwd_chat_debug())
245 DEBUG(100, ("expect: sending [%s]\n", issue
));
247 if ((len
= write(master
, issue
, strlen(issue
))) != strlen(issue
)) {
248 DEBUG(2,("expect: (short) write returned %d\n", len
));
253 if (strequal(expected
, "."))
256 /* Initial timeout. */
257 timeout
= lp_passwd_chat_timeout() * 1000;
261 while ((len
= read_socket_with_timeout(master
, buffer
+ nread
, 1,
262 sizeof(buffer
) - nread
- 1,
268 /* Eat leading/trailing whitespace before match. */
270 pstrcpy( str
, buffer
);
271 trim_char( str
, ' ', ' ');
273 if ((match
= (unix_wild_match(expected
, str
) == 0))) {
274 /* Now data has started to return, lower timeout. */
275 timeout
= lp_passwd_chat_timeout() * 100;
280 if (lp_passwd_chat_debug())
281 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
282 expected
, buffer
, match
? "yes" : "no" ));
288 DEBUG(2, ("expect: %s\n", strerror(errno
)));
293 DEBUG(10,("expect: returning %s\n", match
? "True" : "False" ));
297 static void pwd_sub(char *buf
)
299 all_string_sub(buf
, "\\n", "\n", 0);
300 all_string_sub(buf
, "\\r", "\r", 0);
301 all_string_sub(buf
, "\\s", " ", 0);
302 all_string_sub(buf
, "\\t", "\t", 0);
305 static int talktochild(int master
, const char *seq
)
308 fstring issue
, expected
;
312 while (next_token(&seq
, expected
, NULL
, sizeof(expected
)))
317 if (!expect(master
, issue
, expected
))
319 DEBUG(3, ("Response %d incorrect\n", count
));
323 if (!next_token(&seq
, issue
, NULL
, sizeof(issue
)))
328 if (!strequal(issue
, ".")) {
329 /* we have one final issue to send */
330 fstrcpy(expected
, ".");
331 if (!expect(master
, issue
, expected
))
338 static BOOL
chat_with_program(char *passwordprogram
, struct passwd
*pass
,
339 char *chatsequence
, BOOL as_root
)
348 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
352 /* allocate a pseudo-terminal device */
353 if ((master
= findpty(&slavedev
)) < 0) {
354 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass
->pw_name
));
359 * We need to temporarily stop CatchChild from eating
360 * SIGCLD signals as it also eats the exit status code. JRA.
363 CatchChildLeaveStatus();
365 if ((pid
= sys_fork()) < 0) {
366 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass
->pw_name
));
372 /* we now have a pty */
373 if (pid
> 0) { /* This is the parent process */
374 if ((chstat
= talktochild(master
, chatsequence
)) == False
) {
375 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass
->pw_name
));
376 kill(pid
, SIGKILL
); /* be sure to end this process */
379 while ((wpid
= sys_waitpid(pid
, &wstat
, 0)) < 0) {
380 if (errno
== EINTR
) {
388 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
395 * Go back to ignoring children.
402 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
405 if (WIFEXITED(wstat
) && (WEXITSTATUS(wstat
) != 0)) {
406 DEBUG(3, ("chat_with_program: The process exited with status %d \
407 while we were waiting\n", WEXITSTATUS(wstat
)));
410 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
411 else if (WIFSIGNALLED(wstat
)) {
412 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
413 while we were waiting\n", WTERMSIG(wstat
)));
421 * Lose any oplock capabilities.
423 oplock_set_capability(False
, False
);
425 /* make sure it doesn't freeze */
431 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass
->pw_name
,
432 (int)getuid(), (int)getgid(), BOOLSTR(as_root
) ));
433 chstat
= dochild(master
, slavedev
, pass
, passwordprogram
, as_root
);
439 * The child should never return from dochild() ....
442 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat
));
447 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
448 (chstat
? "" : "un"), pass
->pw_name
));
452 BOOL
chgpasswd(const char *name
, const struct passwd
*pass
,
453 const char *oldpass
, const char *newpass
, BOOL as_root
)
455 pstring passwordprogram
;
456 pstring chatsequence
;
464 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root
), name
));
467 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass
, newpass
));
470 /* Take the passed information and test it for minimum criteria */
472 /* Password is same as old password */
473 if (strcmp(oldpass
, newpass
) == 0) {
474 /* don't allow same password */
475 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name
)); /* log the attempt */
476 return (False
); /* inform the user */
480 * Check the old and new passwords don't contain any control
484 len
= strlen(oldpass
);
485 for (i
= 0; i
< len
; i
++) {
486 if (iscntrl((int)oldpass
[i
])) {
487 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
492 len
= strlen(newpass
);
493 for (i
= 0; i
< len
; i
++) {
494 if (iscntrl((int)newpass
[i
])) {
495 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
501 if (lp_pam_password_change()) {
508 ret
= smb_pam_passchange(pass
->pw_name
, oldpass
, newpass
);
510 ret
= smb_pam_passchange(name
, oldpass
, newpass
);
520 /* A non-PAM password change just doen't make sense without a valid local user */
523 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name
));
527 pstrcpy(passwordprogram
, lp_passwd_program());
528 pstrcpy(chatsequence
, lp_passwd_chat());
530 if (!*chatsequence
) {
531 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
535 if (!*passwordprogram
) {
536 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
541 /* The password program *must* contain the user name to work. Fail if not. */
542 if (strstr(passwordprogram
, "%u") == NULL
) {
543 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
544 the string %%u, and the given string %s does not.\n", passwordprogram
));
549 pstring_sub(passwordprogram
, "%u", name
);
550 /* note that we do NOT substitute the %o and %n in the password program
551 as this would open up a security hole where the user could use
552 a new password containing shell escape characters */
554 pstring_sub(chatsequence
, "%u", name
);
555 all_string_sub(chatsequence
, "%o", oldpass
, sizeof(pstring
));
556 all_string_sub(chatsequence
, "%n", newpass
, sizeof(pstring
));
557 return (chat_with_program
558 (passwordprogram
, pass
, chatsequence
, as_root
));
561 #else /* ALLOW_CHANGE_PASSWORD */
563 BOOL
chgpasswd(const char *name
, const struct passwd
*pass
,
564 const char *oldpass
, const char *newpass
, BOOL as_root
)
566 DEBUG(0, ("chgpasswd: Password changing not compiled in (user=%s)\n", name
));
569 #endif /* ALLOW_CHANGE_PASSWORD */
571 /***********************************************************
572 Code to check the lanman hashed password.
573 ************************************************************/
575 BOOL
check_lanman_password(char *user
, uchar
* pass1
,
576 uchar
* pass2
, SAM_ACCOUNT
**hnd
)
578 uchar unenc_new_pw
[16];
579 uchar unenc_old_pw
[16];
580 SAM_ACCOUNT
*sampass
= NULL
;
582 const uint8
*lanman_pw
;
586 ret
= pdb_getsampwnam(sampass
, user
);
590 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
591 pdb_free_sam(&sampass
);
595 acct_ctrl
= pdb_get_acct_ctrl (sampass
);
596 lanman_pw
= pdb_get_lanman_passwd (sampass
);
598 if (acct_ctrl
& ACB_DISABLED
) {
599 DEBUG(0,("check_lanman_password: account %s disabled.\n", user
));
600 pdb_free_sam(&sampass
);
604 if (lanman_pw
== NULL
) {
605 if (acct_ctrl
& ACB_PWNOTREQ
) {
606 /* this saves the pointer for the caller */
610 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
611 pdb_free_sam(&sampass
);
616 /* Get the new lanman hash. */
617 D_P16(lanman_pw
, pass2
, unenc_new_pw
);
619 /* Use this to get the old lanman hash. */
620 D_P16(unenc_new_pw
, pass1
, unenc_old_pw
);
622 /* Check that the two old passwords match. */
623 if (memcmp(lanman_pw
, unenc_old_pw
, 16)) {
624 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
625 pdb_free_sam(&sampass
);
629 /* this saves the pointer for the caller */
634 /***********************************************************
635 Code to change the lanman hashed password.
636 It nulls out the NT hashed password as it will
638 NOTE this function is designed to be called as root. Check the old password
639 is correct before calling. JRA.
640 ************************************************************/
642 BOOL
change_lanman_password(SAM_ACCOUNT
*sampass
, uchar
*pass2
)
644 static uchar null_pw
[16];
645 uchar unenc_new_pw
[16];
650 if (sampass
== NULL
) {
651 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
655 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
656 pwd
= pdb_get_lanman_passwd(sampass
);
658 if (acct_ctrl
& ACB_DISABLED
) {
659 DEBUG(0,("change_lanman_password: account %s disabled.\n",
660 pdb_get_username(sampass
)));
665 if (acct_ctrl
& ACB_PWNOTREQ
) {
667 memset(no_pw
, '\0', 14);
668 E_P16(no_pw
, null_pw
);
670 /* Get the new lanman hash. */
671 D_P16(null_pw
, pass2
, unenc_new_pw
);
673 DEBUG(0,("change_lanman_password: no lanman password !\n"));
677 /* Get the new lanman hash. */
678 D_P16(pwd
, pass2
, unenc_new_pw
);
681 if (!pdb_set_lanman_passwd(sampass
, unenc_new_pw
, PDB_CHANGED
)) {
685 if (!pdb_set_nt_passwd (sampass
, NULL
, PDB_CHANGED
)) {
686 return False
; /* We lose the NT hash. Sorry. */
689 if (!pdb_set_pass_changed_now (sampass
)) {
690 pdb_free_sam(&sampass
);
691 /* Not quite sure what this one qualifies as, but this will do */
695 /* Now flush the sam_passwd struct to persistent storage */
696 ret
= pdb_update_sam_account (sampass
);
701 /***********************************************************
702 Code to check and change the OEM hashed password.
703 ************************************************************/
705 NTSTATUS
pass_oem_change(char *user
,
706 uchar
* lmdata
, uchar
* lmhash
,
707 uchar
* ntdata
, uchar
* nthash
)
710 SAM_ACCOUNT
*sampass
= NULL
;
711 NTSTATUS nt_status
= check_oem_password(user
, lmdata
, lmhash
, ntdata
, nthash
,
712 &sampass
, new_passwd
, sizeof(new_passwd
));
714 if (!NT_STATUS_IS_OK(nt_status
))
717 /* We've already checked the old password here.... */
719 nt_status
= change_oem_password(sampass
, NULL
, new_passwd
, True
);
722 memset(new_passwd
, 0, sizeof(new_passwd
));
724 pdb_free_sam(&sampass
);
729 /***********************************************************
730 Code to check the OEM hashed password.
732 this function ignores the 516 byte nt OEM hashed password
733 but does use the lm OEM password to check the nt hashed-hash.
735 ************************************************************/
737 static NTSTATUS
check_oem_password(const char *user
,
738 uchar
* lmdata
, const uchar
* lmhash
,
739 const uchar
* ntdata
, const uchar
* nthash
,
740 SAM_ACCOUNT
**hnd
, char *new_passwd
,
743 static uchar null_pw
[16];
744 static uchar null_ntpw
[16];
745 SAM_ACCOUNT
*sampass
= NULL
;
746 const uint8
*lanman_pw
, *nt_pw
;
750 uchar unenc_old_ntpw
[16];
752 uchar unenc_old_pw
[16];
756 BOOL nt_pass_set
= (ntdata
!= NULL
&& nthash
!= NULL
);
760 pdb_init_sam(&sampass
);
763 ret
= pdb_getsampwnam(sampass
, user
);
767 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
768 pdb_free_sam(&sampass
);
769 return NT_STATUS_WRONG_PASSWORD
;
771 TODO: check what Win2k returns for this:
772 return NT_STATUS_NO_SUCH_USER;
776 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
778 if (acct_ctrl
& ACB_DISABLED
) {
779 DEBUG(0,("check_lanman_password: account %s disabled.\n", user
));
780 pdb_free_sam(&sampass
);
781 return NT_STATUS_ACCOUNT_DISABLED
;
784 /* construct a null password (in case one is needed */
787 nt_lm_owf_gen(no_pw
, null_ntpw
, null_pw
);
789 /* save pointers to passwords so we don't have to keep looking them up */
790 lanman_pw
= pdb_get_lanman_passwd(sampass
);
791 nt_pw
= pdb_get_nt_passwd(sampass
);
793 /* check for null passwords */
794 if (lanman_pw
== NULL
) {
795 if (!(acct_ctrl
& ACB_PWNOTREQ
)) {
796 DEBUG(0,("check_oem_password: no lanman password !\n"));
797 pdb_free_sam(&sampass
);
798 return NT_STATUS_WRONG_PASSWORD
;
802 if (pdb_get_nt_passwd(sampass
) == NULL
&& nt_pass_set
) {
803 if (!(acct_ctrl
& ACB_PWNOTREQ
)) {
804 DEBUG(0,("check_oem_password: no ntlm password !\n"));
805 pdb_free_sam(&sampass
);
806 return NT_STATUS_WRONG_PASSWORD
;
811 * Call the hash function to get the new password.
813 SamOEMhash( lmdata
, lanman_pw
, 516);
816 * The length of the new password is in the last 4 bytes of
820 new_pw_len
= IVAL(lmdata
, 512);
822 if (new_pw_len
< 0 || new_pw_len
> new_passwd_size
- 1) {
823 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len
));
824 pdb_free_sam(&sampass
);
825 return NT_STATUS_WRONG_PASSWORD
;
830 * nt passwords are in unicode
832 pull_ucs2(NULL
, new_passwd
,
833 (const smb_ucs2_t
*)&lmdata
[512 - new_pw_len
],
834 new_passwd_size
, new_pw_len
, 0);
836 memcpy(new_passwd
, &lmdata
[512 - new_pw_len
], new_pw_len
);
837 new_passwd
[new_pw_len
] = 0;
841 * To ensure we got the correct new password, hash it and
842 * use it as a key to test the passed old password.
845 nt_lm_owf_gen(new_passwd
, new_ntp16
, new_p16
);
849 * Now use new_p16 as the key to see if the old
852 D_P16(new_p16
, lmhash
, unenc_old_pw
);
854 if (memcmp(lanman_pw
, unenc_old_pw
, 16)) {
855 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
856 pdb_free_sam(&sampass
);
857 return NT_STATUS_WRONG_PASSWORD
;
860 #ifdef DEBUG_PASSWORD
862 ("check_oem_password: password %s ok\n", new_passwd
));
869 * Now use new_p16 as the key to see if the old
872 D_P16(new_ntp16
, lmhash
, unenc_old_pw
);
873 D_P16(new_ntp16
, nthash
, unenc_old_ntpw
);
875 if (memcmp(lanman_pw
, unenc_old_pw
, 16)) {
876 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
877 pdb_free_sam(&sampass
);
878 return NT_STATUS_WRONG_PASSWORD
;
881 if (memcmp(nt_pw
, unenc_old_ntpw
, 16)) {
882 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
883 pdb_free_sam(&sampass
);
884 return NT_STATUS_WRONG_PASSWORD
;
886 #ifdef DEBUG_PASSWORD
887 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd
));
894 /***********************************************************
895 Code to change the oem password. Changes both the lanman
896 and NT hashes. Old_passwd is almost always NULL.
897 NOTE this function is designed to be called as root. Check the old password
898 is correct before calling. JRA.
899 ************************************************************/
901 NTSTATUS
change_oem_password(SAM_ACCOUNT
*hnd
, char *old_passwd
, char *new_passwd
, BOOL as_root
)
908 if (time(NULL
) < pdb_get_pass_can_change_time(hnd
)) {
909 DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
910 pdb_get_username(hnd
), http_timestring(pdb_get_pass_can_change_time(hnd
))));
911 return NT_STATUS_PASSWORD_RESTRICTION
;
914 if (account_policy_get(AP_MIN_PASSWORD_LEN
, &min_len
) && (strlen(new_passwd
) < min_len
)) {
915 DEBUG(1, ("user %s cannot change password - password too short\n",
916 pdb_get_username(hnd
)));
917 DEBUGADD(1, (" account policy min password len = %d\n", min_len
));
918 return NT_STATUS_PASSWORD_RESTRICTION
;
919 /* return NT_STATUS_PWD_TOO_SHORT; */
922 /* Take the passed information and test it for minimum criteria */
923 /* Minimum password length */
924 if (strlen(new_passwd
) < lp_min_passwd_length()) {
925 /* too short, must be at least MINPASSWDLENGTH */
926 DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
927 pdb_get_username(hnd
), lp_min_passwd_length()));
928 return NT_STATUS_PASSWORD_RESTRICTION
;
929 /* return NT_STATUS_PWD_TOO_SHORT; */
932 pass
= Get_Pwnam(pdb_get_username(hnd
));
934 DEBUG(1, ("check_oem_password: Username does not exist in system !?!\n"));
937 #ifdef HAVE_WORKING_CRACKLIB
939 /* if we can, become the user to overcome internal cracklib sillyness */
941 return NT_STATUS_UNSUCCESSFUL
;
943 set_sec_ctx(pass
->pw_uid
, pass
->pw_gid
, 0, NULL
, NULL
);
947 if (lp_use_cracklib()) {
948 const char *crack_check_reason
;
949 DEBUG(4, ("change_oem_password: Checking password for user [%s]"
950 " against cracklib. \n", pdb_get_username(hnd
)));
951 DEBUGADD(4, ("If this is your last message, then something is "
952 "wrong with cracklib, it might be missing it's "
953 "dictionaries at %s\n",
957 crack_check_reason
= FascistCheck(new_passwd
, (char *)CRACKLIB_DICTPATH
);
958 if (crack_check_reason
) {
959 DEBUG(1, ("Password Change: user [%s], "
960 "New password failed cracklib test - %s\n",
961 pdb_get_username(hnd
), crack_check_reason
));
963 /* get back to where we should be */
966 return NT_STATUS_PASSWORD_RESTRICTION
;
975 * If unix password sync was requested, attempt to change
976 * the /etc/passwd database first. Return failure if this cannot
979 * This occurs before the oem change, because we don't want to
980 * update it if chgpasswd failed.
982 * Conditional on lp_unix_password_sync() because we don't want
983 * to touch the unix db unless we have admin permission.
986 if(lp_unix_password_sync() &&
987 !chgpasswd(pdb_get_username(hnd
), pass
, old_passwd
, new_passwd
, as_root
)) {
988 return NT_STATUS_ACCESS_DENIED
;
991 if (!pdb_set_plaintext_passwd (hnd
, new_passwd
)) {
992 return NT_STATUS_ACCESS_DENIED
;
995 /* Now write it into the file. */
996 ret
= pdb_update_sam_account (hnd
);
999 return NT_STATUS_ACCESS_DENIED
;
1002 return NT_STATUS_OK
;