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 #ifdef HAVE_WORKING_CRACKLIB
54 #ifndef HAVE_CRACKLIB_DICTPATH
55 #ifndef CRACKLIB_DICTPATH
56 #define CRACKLIB_DICTPATH SAMBA_CRACKLIB_DICTPATH
61 extern struct passdb_ops pdb_ops
;
63 static NTSTATUS
check_oem_password(const char *user
,
64 uchar password_encrypted_with_lm_hash
[516],
65 const uchar old_lm_hash_encrypted
[16],
66 uchar password_encrypted_with_nt_hash
[516],
67 const uchar old_nt_hash_encrypted
[16],
68 SAM_ACCOUNT
**hnd
, char *new_passwd
,
71 #if ALLOW_CHANGE_PASSWORD
73 static int findpty(char **slave
)
80 #if defined(HAVE_GRANTPT)
81 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
82 if ((master
= sys_open("/dev/ptmx", O_RDWR
, 0)) >= 0)
86 *slave
= (char *)ptsname(master
);
90 ("findpty: Unable to create master/slave pty pair.\n"));
91 /* Stop fd leak on error. */
98 ("findpty: Allocated slave pty %s\n", *slave
));
102 #endif /* HAVE_GRANTPT */
104 fstrcpy(line
, "/dev/ptyXX");
106 dirp
= opendir("/dev");
109 while ((dpname
= readdirname(dirp
)) != NULL
)
111 if (strncmp(dpname
, "pty", 3) == 0 && strlen(dpname
) == 5)
114 ("pty: try to open %s, line was %s\n", dpname
,
118 if ((master
= sys_open(line
, O_RDWR
, 0)) >= 0)
120 DEBUG(3, ("pty: opened %s\n", line
));
132 static int dochild(int master
, const char *slavedev
, const struct passwd
*pass
,
133 const char *passwordprogram
, BOOL as_root
)
136 struct termios stermios
;
143 ("dochild: user doesn't exist in the UNIX password database.\n"));
150 gain_root_privilege();
152 /* Start new session - gets rid of controlling terminal. */
156 ("Weirdness, couldn't let go of controlling terminal\n"));
160 /* Open slave pty and acquire as new controlling terminal. */
161 if ((slave
= sys_open(slavedev
, O_RDWR
, 0)) < 0)
163 DEBUG(3, ("More weirdness, could not open %s\n", slavedev
));
167 ioctl(slave
, I_PUSH
, "ptem");
168 ioctl(slave
, I_PUSH
, "ldterm");
169 #elif defined(TIOCSCTTY)
170 if (ioctl(slave
, TIOCSCTTY
, 0) < 0)
172 DEBUG(3, ("Error in ioctl call for slave pty\n"));
180 /* Make slave stdin/out/err of child. */
182 if (sys_dup2(slave
, STDIN_FILENO
) != STDIN_FILENO
)
184 DEBUG(3, ("Could not re-direct stdin\n"));
187 if (sys_dup2(slave
, STDOUT_FILENO
) != STDOUT_FILENO
)
189 DEBUG(3, ("Could not re-direct stdout\n"));
192 if (sys_dup2(slave
, STDERR_FILENO
) != STDERR_FILENO
)
194 DEBUG(3, ("Could not re-direct stderr\n"));
200 /* Set proper terminal attributes - no echo, canonical input processing,
201 no map NL to CR/NL on output. */
203 if (tcgetattr(0, &stermios
) < 0)
206 ("could not read default terminal attributes on pty\n"));
209 stermios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
210 stermios
.c_lflag
|= ICANON
;
212 stermios
.c_oflag
&= ~(ONLCR
);
214 if (tcsetattr(0, TCSANOW
, &stermios
) < 0)
216 DEBUG(3, ("could not set attributes of pty\n"));
220 /* make us completely into the right uid */
223 become_user_permanently(uid
, gid
);
227 ("Invoking '%s' as password change program.\n",
230 /* execl() password-change application */
231 if (execl("/bin/sh", "sh", "-c", passwordprogram
, NULL
) < 0)
233 DEBUG(3, ("Bad status returned from %s\n", passwordprogram
));
239 static int expect(int master
, char *issue
, char *expected
)
242 int attempts
, timeout
, nread
, len
;
245 for (attempts
= 0; attempts
< 2; attempts
++) {
246 if (!strequal(issue
, ".")) {
247 if (lp_passwd_chat_debug())
248 DEBUG(100, ("expect: sending [%s]\n", issue
));
250 if ((len
= write(master
, issue
, strlen(issue
))) != strlen(issue
)) {
251 DEBUG(2,("expect: (short) write returned %d\n", len
));
256 if (strequal(expected
, "."))
259 /* Initial timeout. */
260 timeout
= lp_passwd_chat_timeout() * 1000;
264 while ((len
= read_socket_with_timeout(master
, buffer
+ nread
, 1,
265 sizeof(buffer
) - nread
- 1,
271 /* Eat leading/trailing whitespace before match. */
273 pstrcpy( str
, buffer
);
274 trim_char( str
, ' ', ' ');
276 if ((match
= (unix_wild_match(expected
, str
) == 0))) {
277 /* Now data has started to return, lower timeout. */
278 timeout
= lp_passwd_chat_timeout() * 100;
283 if (lp_passwd_chat_debug())
284 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
285 expected
, buffer
, match
? "yes" : "no" ));
291 DEBUG(2, ("expect: %s\n", strerror(errno
)));
296 DEBUG(10,("expect: returning %s\n", match
? "True" : "False" ));
300 static void pwd_sub(char *buf
)
302 all_string_sub(buf
, "\\n", "\n", 0);
303 all_string_sub(buf
, "\\r", "\r", 0);
304 all_string_sub(buf
, "\\s", " ", 0);
305 all_string_sub(buf
, "\\t", "\t", 0);
308 static int talktochild(int master
, const char *seq
)
311 fstring issue
, expected
;
315 while (next_token(&seq
, expected
, NULL
, sizeof(expected
)))
320 if (!expect(master
, issue
, expected
))
322 DEBUG(3, ("Response %d incorrect\n", count
));
326 if (!next_token(&seq
, issue
, NULL
, sizeof(issue
)))
331 if (!strequal(issue
, ".")) {
332 /* we have one final issue to send */
333 fstrcpy(expected
, ".");
334 if (!expect(master
, issue
, expected
))
341 static BOOL
chat_with_program(char *passwordprogram
, struct passwd
*pass
,
342 char *chatsequence
, BOOL as_root
)
351 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
355 /* allocate a pseudo-terminal device */
356 if ((master
= findpty(&slavedev
)) < 0) {
357 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass
->pw_name
));
362 * We need to temporarily stop CatchChild from eating
363 * SIGCLD signals as it also eats the exit status code. JRA.
366 CatchChildLeaveStatus();
368 if ((pid
= sys_fork()) < 0) {
369 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass
->pw_name
));
375 /* we now have a pty */
376 if (pid
> 0) { /* This is the parent process */
377 if ((chstat
= talktochild(master
, chatsequence
)) == False
) {
378 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass
->pw_name
));
379 kill(pid
, SIGKILL
); /* be sure to end this process */
382 while ((wpid
= sys_waitpid(pid
, &wstat
, 0)) < 0) {
383 if (errno
== EINTR
) {
391 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
398 * Go back to ignoring children.
405 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
408 if (WIFEXITED(wstat
) && (WEXITSTATUS(wstat
) != 0)) {
409 DEBUG(3, ("chat_with_program: The process exited with status %d \
410 while we were waiting\n", WEXITSTATUS(wstat
)));
413 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
414 else if (WIFSIGNALLED(wstat
)) {
415 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
416 while we were waiting\n", WTERMSIG(wstat
)));
424 * Lose any oplock capabilities.
426 oplock_set_capability(False
, False
);
428 /* make sure it doesn't freeze */
434 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass
->pw_name
,
435 (int)getuid(), (int)getgid(), BOOLSTR(as_root
) ));
436 chstat
= dochild(master
, slavedev
, pass
, passwordprogram
, as_root
);
442 * The child should never return from dochild() ....
445 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat
));
450 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
451 (chstat
? "" : "un"), pass
->pw_name
));
455 BOOL
chgpasswd(const char *name
, const struct passwd
*pass
,
456 const char *oldpass
, const char *newpass
, BOOL as_root
)
458 pstring passwordprogram
;
459 pstring chatsequence
;
467 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root
), name
));
470 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass
, newpass
));
473 /* Take the passed information and test it for minimum criteria */
475 /* Password is same as old password */
476 if (strcmp(oldpass
, newpass
) == 0) {
477 /* don't allow same password */
478 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name
)); /* log the attempt */
479 return (False
); /* inform the user */
483 * Check the old and new passwords don't contain any control
487 len
= strlen(oldpass
);
488 for (i
= 0; i
< len
; i
++) {
489 if (iscntrl((int)oldpass
[i
])) {
490 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
495 len
= strlen(newpass
);
496 for (i
= 0; i
< len
; i
++) {
497 if (iscntrl((int)newpass
[i
])) {
498 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
504 if (lp_pam_password_change()) {
511 ret
= smb_pam_passchange(pass
->pw_name
, oldpass
, newpass
);
513 ret
= smb_pam_passchange(name
, oldpass
, newpass
);
523 /* A non-PAM password change just doen't make sense without a valid local user */
526 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name
));
530 pstrcpy(passwordprogram
, lp_passwd_program());
531 pstrcpy(chatsequence
, lp_passwd_chat());
533 if (!*chatsequence
) {
534 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
538 if (!*passwordprogram
) {
539 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
544 /* The password program *must* contain the user name to work. Fail if not. */
545 if (strstr_m(passwordprogram
, "%u") == NULL
) {
546 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
547 the string %%u, and the given string %s does not.\n", passwordprogram
));
552 pstring_sub(passwordprogram
, "%u", name
);
553 /* note that we do NOT substitute the %o and %n in the password program
554 as this would open up a security hole where the user could use
555 a new password containing shell escape characters */
557 pstring_sub(chatsequence
, "%u", name
);
558 all_string_sub(chatsequence
, "%o", oldpass
, sizeof(pstring
));
559 all_string_sub(chatsequence
, "%n", newpass
, sizeof(pstring
));
560 return (chat_with_program
561 (passwordprogram
, pass
, chatsequence
, as_root
));
564 #else /* ALLOW_CHANGE_PASSWORD */
566 BOOL
chgpasswd(const char *name
, const struct passwd
*pass
,
567 const char *oldpass
, const char *newpass
, BOOL as_root
)
569 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name
));
572 #endif /* ALLOW_CHANGE_PASSWORD */
574 /***********************************************************
575 Code to check the lanman hashed password.
576 ************************************************************/
578 BOOL
check_lanman_password(char *user
, uchar
* pass1
,
579 uchar
* pass2
, SAM_ACCOUNT
**hnd
)
581 uchar unenc_new_pw
[16];
582 uchar unenc_old_pw
[16];
583 SAM_ACCOUNT
*sampass
= NULL
;
585 const uint8
*lanman_pw
;
589 ret
= pdb_getsampwnam(sampass
, user
);
593 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
594 pdb_free_sam(&sampass
);
598 acct_ctrl
= pdb_get_acct_ctrl (sampass
);
599 lanman_pw
= pdb_get_lanman_passwd (sampass
);
601 if (acct_ctrl
& ACB_DISABLED
) {
602 DEBUG(0,("check_lanman_password: account %s disabled.\n", user
));
603 pdb_free_sam(&sampass
);
607 if (lanman_pw
== NULL
) {
608 if (acct_ctrl
& ACB_PWNOTREQ
) {
609 /* this saves the pointer for the caller */
613 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
614 pdb_free_sam(&sampass
);
619 /* Get the new lanman hash. */
620 D_P16(lanman_pw
, pass2
, unenc_new_pw
);
622 /* Use this to get the old lanman hash. */
623 D_P16(unenc_new_pw
, pass1
, unenc_old_pw
);
625 /* Check that the two old passwords match. */
626 if (memcmp(lanman_pw
, unenc_old_pw
, 16)) {
627 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
628 pdb_free_sam(&sampass
);
632 /* this saves the pointer for the caller */
637 /***********************************************************
638 Code to change the lanman hashed password.
639 It nulls out the NT hashed password as it will
641 NOTE this function is designed to be called as root. Check the old password
642 is correct before calling. JRA.
643 ************************************************************/
645 BOOL
change_lanman_password(SAM_ACCOUNT
*sampass
, uchar
*pass2
)
647 static uchar null_pw
[16];
648 uchar unenc_new_pw
[16];
653 if (sampass
== NULL
) {
654 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
658 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
659 pwd
= pdb_get_lanman_passwd(sampass
);
661 if (acct_ctrl
& ACB_DISABLED
) {
662 DEBUG(0,("change_lanman_password: account %s disabled.\n",
663 pdb_get_username(sampass
)));
668 if (acct_ctrl
& ACB_PWNOTREQ
) {
670 memset(no_pw
, '\0', 14);
671 E_P16(no_pw
, null_pw
);
673 /* Get the new lanman hash. */
674 D_P16(null_pw
, pass2
, unenc_new_pw
);
676 DEBUG(0,("change_lanman_password: no lanman password !\n"));
680 /* Get the new lanman hash. */
681 D_P16(pwd
, pass2
, unenc_new_pw
);
684 if (!pdb_set_lanman_passwd(sampass
, unenc_new_pw
, PDB_CHANGED
)) {
688 if (!pdb_set_nt_passwd (sampass
, NULL
, PDB_CHANGED
)) {
689 return False
; /* We lose the NT hash. Sorry. */
692 if (!pdb_set_pass_changed_now (sampass
)) {
693 pdb_free_sam(&sampass
);
694 /* Not quite sure what this one qualifies as, but this will do */
698 /* Now flush the sam_passwd struct to persistent storage */
699 ret
= pdb_update_sam_account (sampass
);
704 /***********************************************************
705 Code to check and change the OEM hashed password.
706 ************************************************************/
708 NTSTATUS
pass_oem_change(char *user
,
709 uchar password_encrypted_with_lm_hash
[516],
710 const uchar old_lm_hash_encrypted
[16],
711 uchar password_encrypted_with_nt_hash
[516],
712 const uchar old_nt_hash_encrypted
[16])
715 SAM_ACCOUNT
*sampass
= NULL
;
716 NTSTATUS nt_status
= check_oem_password(user
, password_encrypted_with_lm_hash
,
717 old_lm_hash_encrypted
,
718 password_encrypted_with_nt_hash
,
719 old_nt_hash_encrypted
,
720 &sampass
, new_passwd
, sizeof(new_passwd
));
722 if (!NT_STATUS_IS_OK(nt_status
))
725 /* We've already checked the old password here.... */
727 nt_status
= change_oem_password(sampass
, NULL
, new_passwd
, True
);
730 memset(new_passwd
, 0, sizeof(new_passwd
));
732 pdb_free_sam(&sampass
);
737 /***********************************************************
738 Decrypt and verify a user password change.
740 The 516 byte long buffers are encrypted with the old NT and
741 old LM passwords, and if the NT passwords are present, both
742 buffers contain a unicode string.
744 After decrypting the buffers, check the password is correct by
745 matching the old hashed passwords with the passwords in the passdb.
747 ************************************************************/
749 static NTSTATUS
check_oem_password(const char *user
,
750 uchar password_encrypted_with_lm_hash
[516],
751 const uchar old_lm_hash_encrypted
[16],
752 uchar password_encrypted_with_nt_hash
[516],
753 const uchar old_nt_hash_encrypted
[16],
754 SAM_ACCOUNT
**hnd
, char *new_passwd
,
757 static uchar null_pw
[16];
758 static uchar null_ntpw
[16];
759 SAM_ACCOUNT
*sampass
= NULL
;
760 char *password_encrypted
;
761 const char *encryption_key
;
762 const uint8
*lanman_pw
, *nt_pw
;
765 uchar new_nt_hash
[16];
766 uchar old_nt_hash_plain
[16];
767 uchar new_lm_hash
[16];
768 uchar old_lm_hash_plain
[16];
772 BOOL nt_pass_set
= (password_encrypted_with_nt_hash
&& old_nt_hash_encrypted
);
773 BOOL lm_pass_set
= (password_encrypted_with_lm_hash
&& old_lm_hash_encrypted
);
777 pdb_init_sam(&sampass
);
780 ret
= pdb_getsampwnam(sampass
, user
);
784 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
785 pdb_free_sam(&sampass
);
786 return NT_STATUS_NO_SUCH_USER
;
789 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
791 if (acct_ctrl
& ACB_DISABLED
) {
792 DEBUG(2,("check_lanman_password: account %s disabled.\n", user
));
793 pdb_free_sam(&sampass
);
794 return NT_STATUS_ACCOUNT_DISABLED
;
797 if (acct_ctrl
& ACB_PWNOTREQ
&& lp_null_passwords()) {
798 /* construct a null password (in case one is needed */
801 nt_lm_owf_gen(no_pw
, null_ntpw
, null_pw
);
806 /* save pointers to passwords so we don't have to keep looking them up */
807 if (lp_lanman_auth()) {
808 lanman_pw
= pdb_get_lanman_passwd(sampass
);
812 nt_pw
= pdb_get_nt_passwd(sampass
);
815 if (nt_pw
&& nt_pass_set
) {
816 /* IDEAL Case: passwords are in unicode, and we can
817 * read use the password encrypted with the NT hash
819 password_encrypted
= password_encrypted_with_nt_hash
;
820 encryption_key
= nt_pw
;
821 } else if (lanman_pw
&& lm_pass_set
) {
822 /* password may still be in unicode, but use LM hash version */
823 password_encrypted
= password_encrypted_with_lm_hash
;
824 encryption_key
= lanman_pw
;
825 } else if (nt_pass_set
) {
826 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
828 pdb_free_sam(&sampass
);
829 return NT_STATUS_WRONG_PASSWORD
;
830 } else if (lm_pass_set
) {
831 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
833 pdb_free_sam(&sampass
);
834 return NT_STATUS_WRONG_PASSWORD
;
836 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
838 pdb_free_sam(&sampass
);
839 return NT_STATUS_WRONG_PASSWORD
;
843 * Decrypt the password with the key
845 SamOEMhash( password_encrypted
, encryption_key
, 516);
847 if ( !decode_pw_buffer(password_encrypted
, new_passwd
, new_passwd_size
, &new_pw_len
,
848 nt_pass_set
? STR_UNICODE
: STR_ASCII
)) {
849 pdb_free_sam(&sampass
);
850 return NT_STATUS_WRONG_PASSWORD
;
854 * To ensure we got the correct new password, hash it and
855 * use it as a key to test the passed old password.
859 /* NT passwords, verify the NT hash. */
861 /* Calculate the MD4 hash (NT compatible) of the password */
862 memset(new_nt_hash
, '\0', 16);
863 E_md4hash(new_passwd
, new_nt_hash
);
867 * Now use new_nt_hash as the key to see if the old
870 D_P16(new_nt_hash
, old_nt_hash_encrypted
, old_nt_hash_plain
);
872 if (memcmp(nt_pw
, old_nt_hash_plain
, 16)) {
873 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
874 pdb_free_sam(&sampass
);
875 return NT_STATUS_WRONG_PASSWORD
;
878 /* We could check the LM password here, but there is
879 * little point, we already know the password is
880 * correct, and the LM password might not even be
883 /* Further, LM hash generation algorithms
884 * differ with charset, so we could
885 * incorrectly fail a perfectly valid password
887 #ifdef DEBUG_PASSWORD
889 ("check_oem_password: password %s ok\n", new_passwd
));
897 * Now use new_nt_hash as the key to see if the old
898 * LM password matches.
900 D_P16(new_nt_hash
, old_lm_hash_encrypted
, old_lm_hash_plain
);
902 if (memcmp(lanman_pw
, old_lm_hash_plain
, 16)) {
903 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
904 pdb_free_sam(&sampass
);
905 return NT_STATUS_WRONG_PASSWORD
;
907 #ifdef DEBUG_PASSWORD
909 ("check_oem_password: password %s ok\n", new_passwd
));
916 if (lanman_pw
&& lm_pass_set
) {
918 E_deshash(new_passwd
, new_lm_hash
);
921 * Now use new_lm_hash as the key to see if the old
924 D_P16(new_lm_hash
, old_lm_hash_encrypted
, old_lm_hash_plain
);
926 if (memcmp(lanman_pw
, old_lm_hash_plain
, 16)) {
927 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
928 pdb_free_sam(&sampass
);
929 return NT_STATUS_WRONG_PASSWORD
;
932 #ifdef DEBUG_PASSWORD
934 ("check_oem_password: password %s ok\n", new_passwd
));
940 /* should not be reached */
941 pdb_free_sam(&sampass
);
942 return NT_STATUS_WRONG_PASSWORD
;
945 /***********************************************************
946 Code to change the oem password. Changes both the lanman
947 and NT hashes. Old_passwd is almost always NULL.
948 NOTE this function is designed to be called as root. Check the old password
949 is correct before calling. JRA.
950 ************************************************************/
952 NTSTATUS
change_oem_password(SAM_ACCOUNT
*hnd
, char *old_passwd
, char *new_passwd
, BOOL as_root
)
959 if (time(NULL
) < pdb_get_pass_can_change_time(hnd
)) {
960 DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
961 pdb_get_username(hnd
), http_timestring(pdb_get_pass_can_change_time(hnd
))));
962 return NT_STATUS_PASSWORD_RESTRICTION
;
965 if (account_policy_get(AP_MIN_PASSWORD_LEN
, &min_len
) && (strlen(new_passwd
) < min_len
)) {
966 DEBUG(1, ("user %s cannot change password - password too short\n",
967 pdb_get_username(hnd
)));
968 DEBUGADD(1, (" account policy min password len = %d\n", min_len
));
969 return NT_STATUS_PASSWORD_RESTRICTION
;
970 /* return NT_STATUS_PWD_TOO_SHORT; */
973 /* Take the passed information and test it for minimum criteria */
974 /* Minimum password length */
975 if (strlen(new_passwd
) < lp_min_passwd_length()) {
976 /* too short, must be at least MINPASSWDLENGTH */
977 DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
978 pdb_get_username(hnd
), lp_min_passwd_length()));
979 return NT_STATUS_PASSWORD_RESTRICTION
;
980 /* return NT_STATUS_PWD_TOO_SHORT; */
983 pass
= Get_Pwnam(pdb_get_username(hnd
));
985 DEBUG(1, ("check_oem_password: Username does not exist in system !?!\n"));
988 #ifdef HAVE_WORKING_CRACKLIB
990 /* if we can, become the user to overcome internal cracklib sillyness */
992 return NT_STATUS_UNSUCCESSFUL
;
994 set_sec_ctx(pass
->pw_uid
, pass
->pw_gid
, 0, NULL
, NULL
);
998 if (lp_use_cracklib()) {
999 const char *crack_check_reason
;
1000 DEBUG(4, ("change_oem_password: Checking password for user [%s]"
1001 " against cracklib. \n", pdb_get_username(hnd
)));
1002 DEBUGADD(4, ("If this is your last message, then something is "
1003 "wrong with cracklib, it might be missing it's "
1004 "dictionaries at %s\n",
1005 CRACKLIB_DICTPATH
));
1008 crack_check_reason
= FascistCheck(new_passwd
, (char *)CRACKLIB_DICTPATH
);
1009 if (crack_check_reason
) {
1010 DEBUG(1, ("Password Change: user [%s], "
1011 "New password failed cracklib test - %s\n",
1012 pdb_get_username(hnd
), crack_check_reason
));
1014 /* get back to where we should be */
1017 return NT_STATUS_PASSWORD_RESTRICTION
;
1026 * If unix password sync was requested, attempt to change
1027 * the /etc/passwd database first. Return failure if this cannot
1030 * This occurs before the oem change, because we don't want to
1031 * update it if chgpasswd failed.
1033 * Conditional on lp_unix_password_sync() because we don't want
1034 * to touch the unix db unless we have admin permission.
1037 if(lp_unix_password_sync() &&
1038 !chgpasswd(pdb_get_username(hnd
), pass
, old_passwd
, new_passwd
, as_root
)) {
1039 return NT_STATUS_ACCESS_DENIED
;
1042 if (!pdb_set_plaintext_passwd (hnd
, new_passwd
)) {
1043 return NT_STATUS_ACCESS_DENIED
;
1046 /* Now write it into the file. */
1047 ret
= pdb_update_sam_account (hnd
);
1050 return NT_STATUS_ACCESS_DENIED
;
1053 return NT_STATUS_OK
;