2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright (C) Andrew Tridgell 1992-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 /* fork a child process to exec passwd and write to its
22 * tty to change a users password. This is running as the
23 * user who is attempting to change the password.
27 * This code was copied/borrowed and stolen from various sources.
28 * The primary source was the poppasswd.c from the authors of POPMail. This software
29 * was included as a client to change passwords using the 'passwd' program
30 * on the remote machine.
32 * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE
33 * is defined in the compiler directives located in the Makefile.
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 BOOL
check_oem_password(const char *user
,
54 uchar
* lmdata
, const uchar
* lmhash
,
55 const uchar
* ntdata
, const uchar
* nthash
,
56 SAM_ACCOUNT
**hnd
, char *new_passwd
,
59 #if ALLOW_CHANGE_PASSWORD
61 static int findpty(char **slave
)
68 #if defined(HAVE_GRANTPT)
69 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
70 if ((master
= sys_open("/dev/ptmx", O_RDWR
, 0)) >= 0)
74 *slave
= (char *)ptsname(master
);
78 ("findpty: Unable to create master/slave pty pair.\n"));
79 /* Stop fd leak on error. */
86 ("findpty: Allocated slave pty %s\n", *slave
));
90 #endif /* HAVE_GRANTPT */
92 fstrcpy(line
, "/dev/ptyXX");
94 dirp
= opendir("/dev");
97 while ((dpname
= readdirname(dirp
)) != NULL
)
99 if (strncmp(dpname
, "pty", 3) == 0 && strlen(dpname
) == 5)
102 ("pty: try to open %s, line was %s\n", dpname
,
106 if ((master
= sys_open(line
, O_RDWR
, 0)) >= 0)
108 DEBUG(3, ("pty: opened %s\n", line
));
120 static int dochild(int master
, const char *slavedev
, const struct passwd
*pass
,
121 const char *passwordprogram
, BOOL as_root
)
124 struct termios stermios
;
131 ("dochild: user doesn't exist in the UNIX password database.\n"));
138 gain_root_privilege();
140 /* Start new session - gets rid of controlling terminal. */
144 ("Weirdness, couldn't let go of controlling terminal\n"));
148 /* Open slave pty and acquire as new controlling terminal. */
149 if ((slave
= sys_open(slavedev
, O_RDWR
, 0)) < 0)
151 DEBUG(3, ("More weirdness, could not open %s\n", slavedev
));
155 ioctl(slave
, I_PUSH
, "ptem");
156 ioctl(slave
, I_PUSH
, "ldterm");
157 #elif defined(TIOCSCTTY)
158 if (ioctl(slave
, TIOCSCTTY
, 0) < 0)
160 DEBUG(3, ("Error in ioctl call for slave pty\n"));
168 /* Make slave stdin/out/err of child. */
170 if (sys_dup2(slave
, STDIN_FILENO
) != STDIN_FILENO
)
172 DEBUG(3, ("Could not re-direct stdin\n"));
175 if (sys_dup2(slave
, STDOUT_FILENO
) != STDOUT_FILENO
)
177 DEBUG(3, ("Could not re-direct stdout\n"));
180 if (sys_dup2(slave
, STDERR_FILENO
) != STDERR_FILENO
)
182 DEBUG(3, ("Could not re-direct stderr\n"));
188 /* Set proper terminal attributes - no echo, canonical input processing,
189 no map NL to CR/NL on output. */
191 if (tcgetattr(0, &stermios
) < 0)
194 ("could not read default terminal attributes on pty\n"));
197 stermios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
198 stermios
.c_lflag
|= ICANON
;
200 stermios
.c_oflag
&= ~(ONLCR
);
202 if (tcsetattr(0, TCSANOW
, &stermios
) < 0)
204 DEBUG(3, ("could not set attributes of pty\n"));
208 /* make us completely into the right uid */
211 become_user_permanently(uid
, gid
);
215 ("Invoking '%s' as password change program.\n",
218 /* execl() password-change application */
219 if (execl("/bin/sh", "sh", "-c", passwordprogram
, NULL
) < 0)
221 DEBUG(3, ("Bad status returned from %s\n", passwordprogram
));
227 static int expect(int master
, char *issue
, char *expected
)
230 int attempts
, timeout
, nread
, len
;
233 for (attempts
= 0; attempts
< 2; attempts
++) {
234 if (!strequal(issue
, ".")) {
235 if (lp_passwd_chat_debug())
236 DEBUG(100, ("expect: sending [%s]\n", issue
));
238 if ((len
= write(master
, issue
, strlen(issue
))) != strlen(issue
)) {
239 DEBUG(2,("expect: (short) write returned %d\n", len
));
244 if (strequal(expected
, "."))
251 while ((len
= read_with_timeout(master
, buffer
+ nread
, 1,
252 sizeof(buffer
) - nread
- 1,
258 /* Eat leading/trailing whitespace before match. */
260 pstrcpy( str
, buffer
);
261 trim_string( str
, " ", " ");
263 if ((match
= (unix_wild_match(expected
, str
) == 0)))
268 if (lp_passwd_chat_debug())
269 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
270 expected
, buffer
, match
? "yes" : "no" ));
276 DEBUG(2, ("expect: %s\n", strerror(errno
)));
281 DEBUG(10,("expect: returning %s\n", match
? "True" : "False" ));
285 static void pwd_sub(char *buf
)
287 all_string_sub(buf
, "\\n", "\n", 0);
288 all_string_sub(buf
, "\\r", "\r", 0);
289 all_string_sub(buf
, "\\s", " ", 0);
290 all_string_sub(buf
, "\\t", "\t", 0);
293 static int talktochild(int master
, const char *seq
)
296 fstring issue
, expected
;
300 while (next_token(&seq
, expected
, NULL
, sizeof(expected
)))
305 if (!expect(master
, issue
, expected
))
307 DEBUG(3, ("Response %d incorrect\n", count
));
311 if (!next_token(&seq
, issue
, NULL
, sizeof(issue
)))
316 if (!strequal(issue
, ".")) {
317 /* we have one final issue to send */
318 fstrcpy(expected
, ".");
319 if (!expect(master
, issue
, expected
))
326 static BOOL
chat_with_program(char *passwordprogram
, struct passwd
*pass
,
327 char *chatsequence
, BOOL as_root
)
338 ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
342 /* allocate a pseudo-terminal device */
343 if ((master
= findpty(&slavedev
)) < 0)
346 ("Cannot Allocate pty for password change: %s\n",
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)
361 ("Cannot fork() child for password change: %s\n",
368 /* we now have a pty */
370 { /* This is the parent process */
371 if ((chstat
= talktochild(master
, chatsequence
)) == False
)
374 ("Child failed to change password: %s\n",
376 kill(pid
, SIGKILL
); /* be sure to end this process */
379 while ((wpid
= sys_waitpid(pid
, &wstat
, 0)) < 0)
391 DEBUG(3, ("The process is no longer waiting!\n\n"));
398 * Go back to ignoring children.
407 ("We were waiting for the wrong process ID\n"));
410 if (WIFEXITED(wstat
) == 0)
413 ("The process exited while we were waiting\n"));
416 if (WEXITSTATUS(wstat
) != 0)
419 ("The status of the process exiting was %d\n",
430 * Lose any oplock capabilities.
432 oplock_set_capability(False
, False
);
434 /* make sure it doesn't freeze */
441 ("Dochild for user %s (uid=%d,gid=%d)\n", pass
->pw_name
,
442 (int)getuid(), (int)getgid()));
444 dochild(master
, slavedev
, pass
, passwordprogram
,
451 * The child should never return from dochild() ....
455 ("chat_with_program: Error: dochild() returned %d\n",
462 ("Password change %ssuccessful for user %s\n",
463 (chstat
? "" : "un"), pass
->pw_name
));
468 BOOL
chgpasswd(const char *name
, const char *oldpass
, const char *newpass
, BOOL as_root
)
470 pstring passwordprogram
;
471 pstring chatsequence
;
478 DEBUG(1, ("NULL username specfied to chgpasswd()!\n"));
481 DEBUG(3, ("Password change for user: %s\n", name
));
484 DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass
, newpass
));
487 /* Take the passed information and test it for minimum criteria */
488 /* Minimum password length */
489 if (strlen(newpass
) < lp_min_passwd_length()) {
490 /* too short, must be at least MINPASSWDLENGTH */
491 DEBUG(0, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
492 name
, lp_min_passwd_length()));
493 return (False
); /* inform the user */
496 /* Password is same as old password */
497 if (strcmp(oldpass
, newpass
) == 0) {
498 /* don't allow same password */
499 DEBUG(2, ("Password Change: %s, New password is same as old\n", name
)); /* log the attempt */
500 return (False
); /* inform the user */
504 * Check the old and new passwords don't contain any control
508 len
= strlen(oldpass
);
509 for (i
= 0; i
< len
; i
++) {
510 if (iscntrl((int)oldpass
[i
])) {
512 ("chat_with_program: oldpass contains control characters (disallowed).\n"));
517 len
= strlen(newpass
);
518 for (i
= 0; i
< len
; i
++) {
519 if (iscntrl((int)newpass
[i
])) {
521 ("chat_with_program: newpass contains control characters (disallowed).\n"));
526 pass
= Get_Pwnam(name
);
529 if (lp_pam_password_change()) {
536 ret
= smb_pam_passchange(pass
->pw_name
, oldpass
, newpass
);
538 ret
= smb_pam_passchange(name
, oldpass
, newpass
);
548 /* A non-PAM password change just doen't make sense without a valid local user */
553 ("chgpasswd: user %s doesn't exist in the UNIX password database.\n",
558 pstrcpy(passwordprogram
, lp_passwd_program());
559 pstrcpy(chatsequence
, lp_passwd_chat());
561 if (!*chatsequence
) {
562 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
566 if (!*passwordprogram
) {
567 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
572 /* The password program *must* contain the user name to work. Fail if not. */
573 if (strstr(passwordprogram
, "%u") == NULL
) {
574 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
575 the string %%u, and the given string %s does not.\n", passwordprogram
));
580 pstring_sub(passwordprogram
, "%u", name
);
581 /* note that we do NOT substitute the %o and %n in the password program
582 as this would open up a security hole where the user could use
583 a new password containing shell escape characters */
585 pstring_sub(chatsequence
, "%u", name
);
586 all_string_sub(chatsequence
, "%o", oldpass
, sizeof(pstring
));
587 all_string_sub(chatsequence
, "%n", newpass
, sizeof(pstring
));
588 return (chat_with_program
589 (passwordprogram
, pass
, chatsequence
, as_root
));
592 #else /* ALLOW_CHANGE_PASSWORD */
594 BOOL
chgpasswd(const char *name
, const char *oldpass
, const char *newpass
, BOOL as_root
)
596 DEBUG(0, ("Password changing not compiled in (user=%s)\n", name
));
599 #endif /* ALLOW_CHANGE_PASSWORD */
601 /***********************************************************
602 Code to check the lanman hashed password.
603 ************************************************************/
605 BOOL
check_lanman_password(char *user
, uchar
* pass1
,
606 uchar
* pass2
, SAM_ACCOUNT
**hnd
)
608 uchar unenc_new_pw
[16];
609 uchar unenc_old_pw
[16];
610 SAM_ACCOUNT
*sampass
= NULL
;
612 const uint8
*lanman_pw
;
616 ret
= pdb_getsampwnam(sampass
, user
);
620 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
621 pdb_free_sam(&sampass
);
625 acct_ctrl
= pdb_get_acct_ctrl (sampass
);
626 lanman_pw
= pdb_get_lanman_passwd (sampass
);
628 if (acct_ctrl
& ACB_DISABLED
) {
629 DEBUG(0,("check_lanman_password: account %s disabled.\n", user
));
630 pdb_free_sam(&sampass
);
634 if (lanman_pw
== NULL
) {
635 if (acct_ctrl
& ACB_PWNOTREQ
) {
636 /* this saves the pointer for the caller */
640 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
641 pdb_free_sam(&sampass
);
646 /* Get the new lanman hash. */
647 D_P16(lanman_pw
, pass2
, unenc_new_pw
);
649 /* Use this to get the old lanman hash. */
650 D_P16(unenc_new_pw
, pass1
, unenc_old_pw
);
652 /* Check that the two old passwords match. */
653 if (memcmp(lanman_pw
, unenc_old_pw
, 16)) {
654 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
655 pdb_free_sam(&sampass
);
659 /* this saves the pointer for the caller */
664 /***********************************************************
665 Code to change the lanman hashed password.
666 It nulls out the NT hashed password as it will
668 ************************************************************/
670 BOOL
change_lanman_password(SAM_ACCOUNT
*sampass
, uchar
* pass1
,
673 static uchar null_pw
[16];
674 uchar unenc_new_pw
[16];
679 if (sampass
== NULL
) {
680 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
684 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
685 pwd
= pdb_get_lanman_passwd(sampass
);
687 if (acct_ctrl
& ACB_DISABLED
) {
688 DEBUG(0,("change_lanman_password: account %s disabled.\n",
689 pdb_get_username(sampass
)));
694 if (acct_ctrl
& ACB_PWNOTREQ
) {
696 memset(no_pw
, '\0', 14);
697 E_P16(no_pw
, null_pw
);
699 /* Get the new lanman hash. */
700 D_P16(null_pw
, pass2
, unenc_new_pw
);
702 DEBUG(0,("change_lanman_password: no lanman password !\n"));
706 /* Get the new lanman hash. */
707 D_P16(pwd
, pass2
, unenc_new_pw
);
710 if (!pdb_set_lanman_passwd(sampass
, unenc_new_pw
, PDB_CHANGED
)) {
714 if (!pdb_set_nt_passwd (sampass
, NULL
, PDB_CHANGED
)) {
715 return False
; /* We lose the NT hash. Sorry. */
718 if (!pdb_set_pass_changed_now (sampass
)) {
719 pdb_free_sam(&sampass
);
720 /* Not quite sure what this one qualifies as, but this will do */
724 /* Now flush the sam_passwd struct to persistent storage */
726 ret
= pdb_update_sam_account (sampass
);
732 /***********************************************************
733 Code to check and change the OEM hashed password.
734 ************************************************************/
735 BOOL
pass_oem_change(char *user
,
736 uchar
* lmdata
, uchar
* lmhash
,
737 uchar
* ntdata
, uchar
* nthash
)
740 const char *unix_user
;
741 SAM_ACCOUNT
*sampass
= NULL
;
742 BOOL ret
= check_oem_password(user
, lmdata
, lmhash
, ntdata
, nthash
,
743 &sampass
, new_passwd
, sizeof(new_passwd
));
746 * At this point we have the new case-sensitive plaintext
747 * password in the fstring new_passwd. If we wanted to synchronise
748 * with UNIX passwords we would call a UNIX password changing
749 * function here. However it would have to be done as root
750 * as the plaintext of the old users password is not
754 unix_user
= pdb_get_username(sampass
);
756 if ((ret
) && (unix_user
) && (*unix_user
) && lp_unix_password_sync())
757 ret
= chgpasswd(unix_user
, "", new_passwd
, True
);
760 ret
= change_oem_password(sampass
, new_passwd
);
762 memset(new_passwd
, 0, sizeof(new_passwd
));
764 pdb_free_sam(&sampass
);
769 /***********************************************************
770 Code to check the OEM hashed password.
772 this function ignores the 516 byte nt OEM hashed password
773 but does use the lm OEM password to check the nt hashed-hash.
775 ************************************************************/
776 static BOOL
check_oem_password(const char *user
,
777 uchar
* lmdata
, const uchar
* lmhash
,
778 const uchar
* ntdata
, const uchar
* nthash
,
779 SAM_ACCOUNT
**hnd
, char *new_passwd
,
782 static uchar null_pw
[16];
783 static uchar null_ntpw
[16];
784 SAM_ACCOUNT
*sampass
= NULL
;
785 const uint8
*lanman_pw
, *nt_pw
;
789 uchar unenc_old_ntpw
[16];
791 uchar unenc_old_pw
[16];
795 BOOL nt_pass_set
= (ntdata
!= NULL
&& nthash
!= NULL
);
797 pdb_init_sam(&sampass
);
800 ret
= pdb_getsampwnam(sampass
, user
);
804 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
810 acct_ctrl
= pdb_get_acct_ctrl(sampass
);
812 if (acct_ctrl
& ACB_DISABLED
) {
813 DEBUG(0,("check_lanman_password: account %s disabled.\n", user
));
817 /* construct a null password (in case one is needed */
820 nt_lm_owf_gen(no_pw
, null_ntpw
, null_pw
);
822 /* save pointers to passwords so we don't have to keep looking them up */
823 lanman_pw
= pdb_get_lanman_passwd(sampass
);
824 nt_pw
= pdb_get_nt_passwd (sampass
);
826 /* check for null passwords */
827 if (lanman_pw
== NULL
) {
828 if (!(acct_ctrl
& ACB_PWNOTREQ
)) {
829 DEBUG(0,("check_oem_password: no lanman password !\n"));
834 if (pdb_get_nt_passwd(sampass
) == NULL
&& nt_pass_set
) {
835 if (!(acct_ctrl
& ACB_PWNOTREQ
)) {
836 DEBUG(0,("check_oem_password: no ntlm password !\n"));
842 * Call the hash function to get the new password.
844 SamOEMhash( lmdata
, lanman_pw
, 516);
847 * The length of the new password is in the last 4 bytes of
851 new_pw_len
= IVAL(lmdata
, 512);
852 if (new_pw_len
< 0 || new_pw_len
> new_passwd_size
- 1) {
853 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len
));
859 * nt passwords are in unicode
861 pull_ucs2(NULL
, new_passwd
,
862 (const smb_ucs2_t
*)&lmdata
[512 - new_pw_len
],
863 new_passwd_size
, new_pw_len
, 0);
865 memcpy(new_passwd
, &lmdata
[512 - new_pw_len
], new_pw_len
);
866 new_passwd
[new_pw_len
] = 0;
870 * To ensure we got the correct new password, hash it and
871 * use it as a key to test the passed old password.
874 nt_lm_owf_gen(new_passwd
, new_ntp16
, new_p16
);
879 * Now use new_p16 as the key to see if the old
882 D_P16(new_p16
, lmhash
, unenc_old_pw
);
884 if (memcmp(lanman_pw
, unenc_old_pw
, 16))
886 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
890 #ifdef DEBUG_PASSWORD
892 ("check_oem_password: password %s ok\n", new_passwd
));
898 * Now use new_p16 as the key to see if the old
901 D_P16(new_ntp16
, lmhash
, unenc_old_pw
);
902 D_P16(new_ntp16
, nthash
, unenc_old_ntpw
);
904 if (memcmp(lanman_pw
, unenc_old_pw
, 16))
906 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
910 if (memcmp(nt_pw
, unenc_old_ntpw
, 16))
912 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
915 #ifdef DEBUG_PASSWORD
916 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd
));
921 /***********************************************************
922 Code to change the oem password. Changes both the lanman
924 ************************************************************/
926 BOOL
change_oem_password(SAM_ACCOUNT
*hnd
, char *new_passwd
)
930 if (!pdb_set_plaintext_passwd (hnd
, new_passwd
)) {
934 /* Now write it into the file. */
936 ret
= pdb_update_sam_account (hnd
);