2 Unix SMB/Netbios implementation.
4 Samba utility functions
5 Copyright (C) Andrew Tridgell 1992-1998
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
52 extern int DEBUGLEVEL
;
54 #if ALLOW_CHANGE_PASSWORD
57 BOOL
chgpasswd(char *name
, char *oldpass
, char *newpass
, BOOL as_root
)
64 ret
= smb_pam_passchange(name
, oldpass
, newpass
);
74 static int findpty(char **slave
)
81 #if defined(HAVE_GRANTPT)
82 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
83 if ((master
= sys_open("/dev/ptmx", O_RDWR
, 0)) >= 0)
87 *slave
= (char *)ptsname(master
);
91 ("findpty: Unable to create master/slave pty pair.\n"));
92 /* Stop fd leak on error. */
99 ("findpty: Allocated slave pty %s\n", *slave
));
103 #endif /* HAVE_GRANTPT */
105 fstrcpy(line
, "/dev/ptyXX");
107 dirp
= opendir("/dev");
110 while ((dpname
= readdirname(dirp
)) != NULL
)
112 if (strncmp(dpname
, "pty", 3) == 0 && strlen(dpname
) == 5)
115 ("pty: try to open %s, line was %s\n", dpname
,
119 if ((master
= sys_open(line
, O_RDWR
, 0)) >= 0)
121 DEBUG(3, ("pty: opened %s\n", line
));
133 static int dochild(int master
, char *slavedev
, char *name
,
134 char *passwordprogram
, BOOL as_root
)
137 struct termios stermios
;
138 struct passwd
*pass
= Get_Pwnam(name
, True
);
145 ("dochild: user name %s doesn't exist in the UNIX password database.\n",
153 gain_root_privilege();
155 /* Start new session - gets rid of controlling terminal. */
159 ("Weirdness, couldn't let go of controlling terminal\n"));
163 /* Open slave pty and acquire as new controlling terminal. */
164 if ((slave
= sys_open(slavedev
, O_RDWR
, 0)) < 0)
166 DEBUG(3, ("More weirdness, could not open %s\n", slavedev
));
170 ioctl(slave
, I_PUSH
, "ptem");
171 ioctl(slave
, I_PUSH
, "ldterm");
172 #elif defined(TIOCSCTTY)
173 if (ioctl(slave
, TIOCSCTTY
, 0) < 0)
175 DEBUG(3, ("Error in ioctl call for slave pty\n"));
183 /* Make slave stdin/out/err of child. */
185 if (dup2(slave
, STDIN_FILENO
) != STDIN_FILENO
)
187 DEBUG(3, ("Could not re-direct stdin\n"));
190 if (dup2(slave
, STDOUT_FILENO
) != STDOUT_FILENO
)
192 DEBUG(3, ("Could not re-direct stdout\n"));
195 if (dup2(slave
, STDERR_FILENO
) != STDERR_FILENO
)
197 DEBUG(3, ("Could not re-direct stderr\n"));
203 /* Set proper terminal attributes - no echo, canonical input processing,
204 no map NL to CR/NL on output. */
206 if (tcgetattr(0, &stermios
) < 0)
209 ("could not read default terminal attributes on pty\n"));
212 stermios
.c_lflag
&= ~(ECHO
| ECHOE
| ECHOK
| ECHONL
);
213 stermios
.c_lflag
|= ICANON
;
214 stermios
.c_oflag
&= ~(ONLCR
);
215 if (tcsetattr(0, TCSANOW
, &stermios
) < 0)
217 DEBUG(3, ("could not set attributes of pty\n"));
221 /* make us completely into the right uid */
224 become_user_permanently(uid
, gid
);
228 ("Invoking '%s' as password change program.\n",
231 /* execl() password-change application */
232 if (execl("/bin/sh", "sh", "-c", passwordprogram
, NULL
) < 0)
234 DEBUG(3, ("Bad status returned from %s\n", passwordprogram
));
240 static int expect(int master
, char *issue
, char *expected
)
243 int attempts
, timeout
, nread
, len
;
246 for (attempts
= 0; attempts
< 2; attempts
++)
248 if (!strequal(issue
, "."))
250 if (lp_passwd_chat_debug())
251 DEBUG(100, ("expect: sending [%s]\n", issue
));
253 write(master
, issue
, strlen(issue
));
256 if (strequal(expected
, "."))
263 while ((len
= read_with_timeout(master
, buffer
+ nread
, 1,
264 sizeof(buffer
) - nread
- 1,
270 if ((match
= (ms_fnmatch(expected
, buffer
) == 0)))
274 if (lp_passwd_chat_debug())
275 DEBUG(100, ("expect: expected [%s] received [%s]\n",
283 DEBUG(2, ("expect: %s\n", strerror(errno
)));
291 static void pwd_sub(char *buf
)
293 all_string_sub(buf
, "\\n", "\n", 0);
294 all_string_sub(buf
, "\\r", "\r", 0);
295 all_string_sub(buf
, "\\s", " ", 0);
296 all_string_sub(buf
, "\\t", "\t", 0);
299 static int talktochild(int master
, char *seq
)
302 fstring issue
, expected
;
306 while (next_token(&seq
, expected
, NULL
, sizeof(expected
)))
311 if (!expect(master
, issue
, expected
))
313 DEBUG(3, ("Response %d incorrect\n", count
));
317 if (!next_token(&seq
, issue
, NULL
, sizeof(issue
)))
326 static BOOL
chat_with_program(char *passwordprogram
, char *name
,
327 char *chatsequence
, BOOL as_root
)
335 /* allocate a pseudo-terminal device */
336 if ((master
= findpty(&slavedev
)) < 0)
339 ("Cannot Allocate pty for password change: %s\n",
345 * We need to temporarily stop CatchChild from eating
346 * SIGCLD signals as it also eats the exit status code. JRA.
349 CatchChildLeaveStatus();
351 if ((pid
= sys_fork()) < 0)
354 ("Cannot fork() child for password change: %s\n",
361 /* we now have a pty */
363 { /* This is the parent process */
364 if ((chstat
= talktochild(master
, chatsequence
)) == False
)
367 ("Child failed to change password: %s\n",
369 kill(pid
, SIGKILL
); /* be sure to end this process */
372 while ((wpid
= sys_waitpid(pid
, &wstat
, 0)) < 0)
384 DEBUG(3, ("The process is no longer waiting!\n\n"));
391 * Go back to ignoring children.
400 ("We were waiting for the wrong process ID\n"));
403 if (WIFEXITED(wstat
) == 0)
406 ("The process exited while we were waiting\n"));
409 if (WEXITSTATUS(wstat
) != 0)
412 ("The status of the process exiting was %d\n",
423 * Lose any oplock capabilities.
425 oplock_set_capability(False
, False
);
427 /* make sure it doesn't freeze */
434 ("Dochild for user %s (uid=%d,gid=%d)\n", name
,
435 (int)getuid(), (int)getgid()));
437 dochild(master
, slavedev
, name
, passwordprogram
,
444 * The child should never return from dochild() ....
448 ("chat_with_program: Error: dochild() returned %d\n",
455 ("Password change %ssuccessful for user %s\n",
456 (chstat
? "" : "un"), name
));
461 BOOL
chgpasswd(char *name
, char *oldpass
, char *newpass
, BOOL as_root
)
463 pstring passwordprogram
;
464 pstring chatsequence
;
469 DEBUG(3, ("Password change for user: %s\n", name
));
472 DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass
, newpass
));
475 /* Take the passed information and test it for minimum criteria */
476 /* Minimum password length */
477 if (strlen(newpass
) < lp_min_passwd_length()) /* too short, must be at least MINPASSWDLENGTH */
480 ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
481 name
, lp_min_passwd_length()));
482 return (False
); /* inform the user */
485 /* Password is same as old password */
486 if (strcmp(oldpass
, newpass
) == 0) /* don't allow same password */
489 ("Password Change: %s, New password is same as old\n", name
)); /* log the attempt */
490 return (False
); /* inform the user */
493 pstrcpy(passwordprogram
, lp_passwd_program());
494 pstrcpy(chatsequence
, lp_passwd_chat());
498 DEBUG(2, ("Null chat sequence - no password changing\n"));
502 if (!*passwordprogram
)
504 DEBUG(2, ("Null password program - no password changing\n"));
509 * Check the old and new passwords don't contain any control
513 len
= strlen(oldpass
);
514 for (i
= 0; i
< len
; i
++)
516 if (iscntrl((int)oldpass
[i
]))
519 ("chat_with_program: oldpass contains control characters (disallowed).\n"));
524 len
= strlen(newpass
);
525 for (i
= 0; i
< len
; i
++)
527 if (iscntrl((int)newpass
[i
]))
530 ("chat_with_program: newpass contains control characters (disallowed).\n"));
535 pstring_sub(passwordprogram
, "%u", name
);
536 /* note that we do NOT substitute the %o and %n in the password program
537 as this would open up a security hole where the user could use
538 a new password containing shell escape characters */
540 pstring_sub(chatsequence
, "%u", name
);
541 all_string_sub(chatsequence
, "%o", oldpass
, sizeof(pstring
));
542 all_string_sub(chatsequence
, "%n", newpass
, sizeof(pstring
));
543 return (chat_with_program
544 (passwordprogram
, name
, chatsequence
, as_root
));
547 #endif /* WITH_PAM */
549 #else /* ALLOW_CHANGE_PASSWORD */
551 BOOL
chgpasswd(char *name
, char *oldpass
, char *newpass
, BOOL as_root
)
553 DEBUG(0, ("Password changing not compiled in (user=%s)\n", name
));
556 #endif /* ALLOW_CHANGE_PASSWORD */
558 /***********************************************************
559 Code to check the lanman hashed password.
560 ************************************************************/
562 BOOL
check_lanman_password(char *user
, uchar
* pass1
,
563 uchar
* pass2
, struct smb_passwd
**psmbpw
)
565 static uchar null_pw
[16];
566 uchar unenc_new_pw
[16];
567 uchar unenc_old_pw
[16];
568 struct smb_passwd
*smbpw
;
573 smbpw
= getsmbpwnam(user
);
579 ("check_lanman_password: getsmbpwnam returned NULL\n"));
583 if (smbpw
->acct_ctrl
& ACB_DISABLED
)
586 ("check_lanman_password: account %s disabled.\n",
591 if ((smbpw
->smb_passwd
== NULL
) && (smbpw
->acct_ctrl
& ACB_PWNOTREQ
))
594 memset(no_pw
, '\0', 14);
595 E_P16(no_pw
, null_pw
);
596 smbpw
->smb_passwd
= null_pw
;
598 else if (smbpw
->smb_passwd
== NULL
)
600 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
604 /* Get the new lanman hash. */
605 D_P16(smbpw
->smb_passwd
, pass2
, unenc_new_pw
);
607 /* Use this to get the old lanman hash. */
608 D_P16(unenc_new_pw
, pass1
, unenc_old_pw
);
610 /* Check that the two old passwords match. */
611 if (memcmp(smbpw
->smb_passwd
, unenc_old_pw
, 16))
614 ("check_lanman_password: old password doesn't match.\n"));
622 /***********************************************************
623 Code to change the lanman hashed password.
624 It nulls out the NT hashed password as it will
626 ************************************************************/
628 BOOL
change_lanman_password(struct smb_passwd
*smbpw
, uchar
* pass1
,
631 static uchar null_pw
[16];
632 uchar unenc_new_pw
[16];
638 ("change_lanman_password: no smb password entry.\n"));
642 if (smbpw
->acct_ctrl
& ACB_DISABLED
)
645 ("change_lanman_password: account %s disabled.\n",
650 if ((smbpw
->smb_passwd
== NULL
) && (smbpw
->acct_ctrl
& ACB_PWNOTREQ
))
653 memset(no_pw
, '\0', 14);
654 E_P16(no_pw
, null_pw
);
655 smbpw
->smb_passwd
= null_pw
;
657 else if (smbpw
->smb_passwd
== NULL
)
659 DEBUG(0, ("change_lanman_password: no lanman password !\n"));
663 /* Get the new lanman hash. */
664 D_P16(smbpw
->smb_passwd
, pass2
, unenc_new_pw
);
666 smbpw
->smb_passwd
= unenc_new_pw
;
667 smbpw
->smb_nt_passwd
= NULL
; /* We lose the NT hash. Sorry. */
669 /* Now write it into the file. */
671 ret
= mod_smbpwd_entry(smbpw
, False
);
677 /***********************************************************
678 Code to check and change the OEM hashed password.
679 ************************************************************/
680 BOOL
pass_oem_change(char *user
,
681 uchar
* lmdata
, uchar
* lmhash
,
682 uchar
* ntdata
, uchar
* nthash
)
685 struct smb_passwd
*sampw
;
686 BOOL ret
= check_oem_password(user
, lmdata
, lmhash
, ntdata
, nthash
,
688 new_passwd
, sizeof(new_passwd
));
691 * At this point we have the new case-sensitive plaintext
692 * password in the fstring new_passwd. If we wanted to synchronise
693 * with UNIX passwords we would call a UNIX password changing
694 * function here. However it would have to be done as root
695 * as the plaintext of the old users password is not
699 if (ret
&& lp_unix_password_sync())
701 ret
= chgpasswd(user
, "", new_passwd
, True
);
706 ret
= change_oem_password(sampw
, new_passwd
, False
);
709 memset(new_passwd
, 0, sizeof(new_passwd
));
714 /***********************************************************
715 Code to check the OEM hashed password.
717 this function ignores the 516 byte nt OEM hashed password
718 but does use the lm OEM password to check the nt hashed-hash.
720 ************************************************************/
721 BOOL
check_oem_password(char *user
,
722 uchar
* lmdata
, uchar
* lmhash
,
723 uchar
* ntdata
, uchar
* nthash
,
724 struct smb_passwd
**psmbpw
, char *new_passwd
,
727 static uchar null_pw
[16];
728 static uchar null_ntpw
[16];
729 struct smb_passwd
*smbpw
= NULL
;
732 uchar unenc_old_ntpw
[16];
734 uchar unenc_old_pw
[16];
737 BOOL nt_pass_set
= (ntdata
!= NULL
&& nthash
!= NULL
);
740 *psmbpw
= smbpw
= getsmbpwnam(user
);
745 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
749 if (smbpw
->acct_ctrl
& ACB_DISABLED
)
752 ("check_lanman_password: account %s disabled.\n",
757 /* construct a null password (in case one is needed */
760 nt_lm_owf_gen(no_pw
, null_ntpw
, null_pw
);
762 /* check for null passwords */
763 if (smbpw
->smb_passwd
== NULL
)
765 if (smbpw
->acct_ctrl
& ACB_PWNOTREQ
)
767 smbpw
->smb_passwd
= null_pw
;
772 ("check_oem_password: no lanman password !\n"));
777 if (smbpw
->smb_nt_passwd
== NULL
&& nt_pass_set
)
779 if (smbpw
->acct_ctrl
& ACB_PWNOTREQ
)
781 smbpw
->smb_nt_passwd
= null_pw
;
786 ("check_oem_password: no ntlm password !\n"));
792 * Call the hash function to get the new password.
794 SamOEMhash((uchar
*) lmdata
, (uchar
*) smbpw
->smb_passwd
, True
);
797 * The length of the new password is in the last 4 bytes of
801 new_pw_len
= IVAL(lmdata
, 512);
802 if (new_pw_len
< 0 || new_pw_len
> new_passwd_size
- 1)
805 ("check_oem_password: incorrect password length (%d).\n",
813 * nt passwords are in unicode
815 int uni_pw_len
= new_pw_len
;
819 dos_unistrn2((uint16
*)(&lmdata
[512 - uni_pw_len
]),
821 memcpy(new_passwd
, pw
, new_pw_len
+ 1);
825 memcpy(new_passwd
, &lmdata
[512 - new_pw_len
], new_pw_len
);
826 new_passwd
[new_pw_len
] = '\0';
830 * To ensure we got the correct new password, hash it and
831 * use it as a key to test the passed old password.
834 nt_lm_owf_gen(new_passwd
, new_ntp16
, new_p16
);
839 * Now use new_p16 as the key to see if the old
842 D_P16(new_p16
, lmhash
, unenc_old_pw
);
844 if (memcmp(smbpw
->smb_passwd
, unenc_old_pw
, 16))
847 ("check_oem_password: old lm password doesn't match.\n"));
851 #ifdef DEBUG_PASSWORD
853 ("check_oem_password: password %s ok\n", new_passwd
));
859 * Now use new_p16 as the key to see if the old
862 D_P16(new_ntp16
, lmhash
, unenc_old_pw
);
863 D_P16(new_ntp16
, nthash
, unenc_old_ntpw
);
865 if (memcmp(smbpw
->smb_passwd
, unenc_old_pw
, 16))
868 ("check_oem_password: old lm password doesn't match.\n"));
872 if (memcmp(smbpw
->smb_nt_passwd
, unenc_old_ntpw
, 16))
875 ("check_oem_password: old nt password doesn't match.\n"));
878 #ifdef DEBUG_PASSWORD
879 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd
));
884 /***********************************************************
885 Code to change the oem password. Changes both the lanman
887 override = False, normal
888 override = True, override XXXXXXXXXX'd password
889 ************************************************************/
891 BOOL
change_oem_password(struct smb_passwd
*smbpw
, char *new_passwd
,
895 uchar new_nt_p16
[16];
898 nt_lm_owf_gen(new_passwd
, new_nt_p16
, new_p16
);
900 smbpw
->smb_passwd
= new_p16
;
901 smbpw
->smb_nt_passwd
= new_nt_p16
;
903 /* Now write it into the file. */
905 ret
= mod_smbpwd_entry(smbpw
, override
);
908 memset(new_passwd
, '\0', strlen(new_passwd
));
913 /***********************************************************
914 Code to check a plaintext password against smbpasswd entries.
915 ***********************************************************/
917 BOOL
check_plaintext_password(char *user
, char *old_passwd
,
918 int old_passwd_size
, struct smb_passwd
**psmbpw
)
920 struct smb_passwd
*smbpw
= NULL
;
921 uchar old_pw
[16], old_ntpw
[16];
924 *psmbpw
= smbpw
= getsmbpwnam(user
);
930 ("check_plaintext_password: getsmbpwnam returned NULL\n"));
934 if (smbpw
->acct_ctrl
& ACB_DISABLED
)
937 ("check_plaintext_password: account %s disabled.\n",
942 nt_lm_owf_gen(old_passwd
, old_ntpw
, old_pw
);
944 #ifdef DEBUG_PASSWORD
945 DEBUG(100, ("check_plaintext_password: smbpw->smb_nt_passwd \n"));
946 dump_data(100, smbpw
->smb_nt_passwd
, 16);
947 DEBUG(100, ("check_plaintext_password: old_ntpw \n"));
948 dump_data(100, old_ntpw
, 16);
949 DEBUG(100, ("check_plaintext_password: smbpw->smb_passwd \n"));
950 dump_data(100, smbpw
->smb_passwd
, 16);
951 DEBUG(100, ("check_plaintext_password: old_pw\n"));
952 dump_data(100, old_pw
, 16);
955 if (memcmp(smbpw
->smb_nt_passwd
, old_ntpw
, 16)
956 && memcmp(smbpw
->smb_passwd
, old_pw
, 16))