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 struct passdb_ops pdb_ops;
54 static BOOL check_oem_password(char *user,
55 uchar * lmdata, uchar * lmhash,
56 uchar * ntdata, uchar * nthash,
57 SAM_ACCOUNT **hnd, char *new_passwd,
60 #if ALLOW_CHANGE_PASSWORD
62 static int findpty(char **slave)
69 #if defined(HAVE_GRANTPT)
70 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
71 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
75 *slave = (char *)ptsname(master);
79 ("findpty: Unable to create master/slave pty pair.\n"));
80 /* Stop fd leak on error. */
87 ("findpty: Allocated slave pty %s\n", *slave));
91 #endif /* HAVE_GRANTPT */
93 fstrcpy(line, "/dev/ptyXX");
95 dirp = opendir("/dev");
98 while ((dpname = readdirname(dirp)) != NULL)
100 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
103 ("pty: try to open %s, line was %s\n", dpname,
107 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
109 DEBUG(3, ("pty: opened %s\n", line));
121 static int dochild(int master, char *slavedev, char *name,
122 char *passwordprogram, BOOL as_root)
125 struct termios stermios;
126 struct passwd *pass = Get_Pwnam(name, True);
133 ("dochild: user name %s doesn't exist in the UNIX password database.\n",
141 gain_root_privilege();
143 /* Start new session - gets rid of controlling terminal. */
147 ("Weirdness, couldn't let go of controlling terminal\n"));
151 /* Open slave pty and acquire as new controlling terminal. */
152 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
154 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
158 ioctl(slave, I_PUSH, "ptem");
159 ioctl(slave, I_PUSH, "ldterm");
160 #elif defined(TIOCSCTTY)
161 if (ioctl(slave, TIOCSCTTY, 0) < 0)
163 DEBUG(3, ("Error in ioctl call for slave pty\n"));
171 /* Make slave stdin/out/err of child. */
173 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
175 DEBUG(3, ("Could not re-direct stdin\n"));
178 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
180 DEBUG(3, ("Could not re-direct stdout\n"));
183 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
185 DEBUG(3, ("Could not re-direct stderr\n"));
191 /* Set proper terminal attributes - no echo, canonical input processing,
192 no map NL to CR/NL on output. */
194 if (tcgetattr(0, &stermios) < 0)
197 ("could not read default terminal attributes on pty\n"));
200 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
201 stermios.c_lflag |= ICANON;
202 stermios.c_oflag &= ~(ONLCR);
203 if (tcsetattr(0, TCSANOW, &stermios) < 0)
205 DEBUG(3, ("could not set attributes of pty\n"));
209 /* make us completely into the right uid */
212 become_user_permanently(uid, gid);
216 ("Invoking '%s' as password change program.\n",
219 /* execl() password-change application */
220 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
222 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
228 static int expect(int master, char *issue, char *expected)
231 int attempts, timeout, nread, len;
234 for (attempts = 0; attempts < 2; attempts++)
236 if (!strequal(issue, "."))
238 if (lp_passwd_chat_debug())
239 DEBUG(100, ("expect: sending [%s]\n", issue));
241 write(master, issue, strlen(issue));
244 if (strequal(expected, "."))
251 while ((len = read_with_timeout(master, buffer + nread, 1,
252 sizeof(buffer) - nread - 1,
258 if ((match = (wild_match(expected, buffer) == 0)))
262 if (lp_passwd_chat_debug())
263 DEBUG(100, ("expect: expected [%s] received [%s]\n",
271 DEBUG(2, ("expect: %s\n", strerror(errno)));
279 static void pwd_sub(char *buf)
281 all_string_sub(buf, "\\n", "\n", 0);
282 all_string_sub(buf, "\\r", "\r", 0);
283 all_string_sub(buf, "\\s", " ", 0);
284 all_string_sub(buf, "\\t", "\t", 0);
287 static int talktochild(int master, char *seq)
290 fstring issue, expected;
294 while (next_token(&seq, expected, NULL, sizeof(expected)))
299 if (!expect(master, issue, expected))
301 DEBUG(3, ("Response %d incorrect\n", count));
305 if (!next_token(&seq, issue, NULL, sizeof(issue)))
314 static BOOL chat_with_program(char *passwordprogram, char *name,
315 char *chatsequence, BOOL as_root)
323 /* allocate a pseudo-terminal device */
324 if ((master = findpty(&slavedev)) < 0)
327 ("Cannot Allocate pty for password change: %s\n",
333 * We need to temporarily stop CatchChild from eating
334 * SIGCLD signals as it also eats the exit status code. JRA.
337 CatchChildLeaveStatus();
339 if ((pid = sys_fork()) < 0)
342 ("Cannot fork() child for password change: %s\n",
349 /* we now have a pty */
351 { /* This is the parent process */
352 if ((chstat = talktochild(master, chatsequence)) == False)
355 ("Child failed to change password: %s\n",
357 kill(pid, SIGKILL); /* be sure to end this process */
360 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0)
372 DEBUG(3, ("The process is no longer waiting!\n\n"));
379 * Go back to ignoring children.
388 ("We were waiting for the wrong process ID\n"));
391 if (WIFEXITED(wstat) == 0)
394 ("The process exited while we were waiting\n"));
397 if (WEXITSTATUS(wstat) != 0)
400 ("The status of the process exiting was %d\n",
411 * Lose any oplock capabilities.
413 oplock_set_capability(False, False);
415 /* make sure it doesn't freeze */
422 ("Dochild for user %s (uid=%d,gid=%d)\n", name,
423 (int)getuid(), (int)getgid()));
425 dochild(master, slavedev, name, passwordprogram,
432 * The child should never return from dochild() ....
436 ("chat_with_program: Error: dochild() returned %d\n",
443 ("Password change %ssuccessful for user %s\n",
444 (chstat ? "" : "un"), name));
449 BOOL chgpasswd(char *name, char *oldpass, char *newpass, BOOL as_root)
451 pstring passwordprogram;
452 pstring chatsequence;
457 DEBUG(3, ("Password change for user: %s\n", name));
460 DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass));
463 /* Take the passed information and test it for minimum criteria */
464 /* Minimum password length */
465 if (strlen(newpass) < lp_min_passwd_length()) {
466 /* too short, must be at least MINPASSWDLENGTH */
467 DEBUG(0, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
468 name, lp_min_passwd_length()));
469 return (False); /* inform the user */
472 /* Password is same as old password */
473 if (strcmp(oldpass, newpass) == 0) {
474 /* don't allow same password */
475 DEBUG(2, ("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])) {
488 ("chat_with_program: oldpass contains control characters (disallowed).\n"));
493 len = strlen(newpass);
494 for (i = 0; i < len; i++) {
495 if (iscntrl((int)newpass[i])) {
497 ("chat_with_program: newpass contains control characters (disallowed).\n"));
503 if (lp_pam_password_change()) {
509 ret = smb_pam_passchange(name, oldpass, newpass);
518 pstrcpy(passwordprogram, lp_passwd_program());
519 pstrcpy(chatsequence, lp_passwd_chat());
521 if (!*chatsequence) {
522 DEBUG(2, ("Null chat sequence - no password changing\n"));
526 if (!*passwordprogram) {
527 DEBUG(2, ("Null password program - no password changing\n"));
531 pstring_sub(passwordprogram, "%u", name);
532 /* note that we do NOT substitute the %o and %n in the password program
533 as this would open up a security hole where the user could use
534 a new password containing shell escape characters */
536 pstring_sub(chatsequence, "%u", name);
537 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
538 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
539 return (chat_with_program
540 (passwordprogram, name, chatsequence, as_root));
543 #else /* ALLOW_CHANGE_PASSWORD */
545 BOOL chgpasswd(char *name, char *oldpass, char *newpass, BOOL as_root)
547 DEBUG(0, ("Password changing not compiled in (user=%s)\n", name));
550 #endif /* ALLOW_CHANGE_PASSWORD */
552 /***********************************************************
553 Code to check the lanman hashed password.
554 ************************************************************/
556 BOOL check_lanman_password(char *user, uchar * pass1,
557 uchar * pass2, SAM_ACCOUNT **hnd)
559 uchar unenc_new_pw[16];
560 uchar unenc_old_pw[16];
561 SAM_ACCOUNT *sampass = NULL;
567 ret = pdb_getsampwnam(sampass, user);
571 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
572 pdb_free_sam(&sampass);
576 acct_ctrl = pdb_get_acct_ctrl (sampass);
577 lanman_pw = pdb_get_lanman_passwd (sampass);
579 if (acct_ctrl & ACB_DISABLED) {
580 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
581 pdb_free_sam(&sampass);
585 if (lanman_pw == NULL) {
586 if (acct_ctrl & ACB_PWNOTREQ) {
587 /* this saves the pointer for the caller */
591 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
592 pdb_free_sam(&sampass);
597 /* Get the new lanman hash. */
598 D_P16(lanman_pw, pass2, unenc_new_pw);
600 /* Use this to get the old lanman hash. */
601 D_P16(unenc_new_pw, pass1, unenc_old_pw);
603 /* Check that the two old passwords match. */
604 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
605 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
606 pdb_free_sam(&sampass);
610 /* this saves the pointer for the caller */
615 /***********************************************************
616 Code to change the lanman hashed password.
617 It nulls out the NT hashed password as it will
619 ************************************************************/
621 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar * pass1,
624 static uchar null_pw[16];
625 uchar unenc_new_pw[16];
630 if (sampass == NULL) {
631 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
635 acct_ctrl = pdb_get_acct_ctrl(sampass);
636 pwd = pdb_get_lanman_passwd(sampass);
638 if (acct_ctrl & ACB_DISABLED) {
639 DEBUG(0,("change_lanman_password: account %s disabled.\n",
640 pdb_get_username(sampass)));
645 if (acct_ctrl & ACB_PWNOTREQ) {
647 memset(no_pw, '\0', 14);
648 E_P16(no_pw, null_pw);
650 /* Get the new lanman hash. */
651 D_P16(null_pw, pass2, unenc_new_pw);
653 DEBUG(0,("change_lanman_password: no lanman password !\n"));
657 /* Get the new lanman hash. */
658 D_P16(pwd, pass2, unenc_new_pw);
661 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw)) {
665 if (!pdb_set_nt_passwd (sampass, NULL)) {
666 return False; /* We lose the NT hash. Sorry. */
669 /* Now flush the sam_passwd struct to persistent storage */
671 ret = pdb_update_sam_account (sampass, 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 SAM_ACCOUNT *sampass = NULL;
686 BOOL ret = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
687 &sampass, new_passwd, sizeof(new_passwd));
690 * At this point we have the new case-sensitive plaintext
691 * password in the fstring new_passwd. If we wanted to synchronise
692 * with UNIX passwords we would call a UNIX password changing
693 * function here. However it would have to be done as root
694 * as the plaintext of the old users password is not
698 if ((ret) && lp_unix_password_sync())
699 ret = chgpasswd(user, "", new_passwd, True);
702 ret = change_oem_password(sampass, new_passwd);
704 memset(new_passwd, 0, sizeof(new_passwd));
706 pdb_free_sam(&sampass);
711 /***********************************************************
712 Code to check the OEM hashed password.
714 this function ignores the 516 byte nt OEM hashed password
715 but does use the lm OEM password to check the nt hashed-hash.
717 ************************************************************/
718 static BOOL check_oem_password(char *user,
719 uchar * lmdata, uchar * lmhash,
720 uchar * ntdata, uchar * nthash,
721 SAM_ACCOUNT **hnd, char *new_passwd,
724 static uchar null_pw[16];
725 static uchar null_ntpw[16];
726 SAM_ACCOUNT *sampass = NULL;
727 uint8 *lanman_pw, *nt_pw;
731 uchar unenc_old_ntpw[16];
733 uchar unenc_old_pw[16];
737 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
739 pdb_init_sam(&sampass);
742 ret = pdb_getsampwnam(sampass, user);
746 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
752 acct_ctrl = pdb_get_acct_ctrl(sampass);
754 if (acct_ctrl & ACB_DISABLED) {
755 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
759 /* construct a null password (in case one is needed */
762 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
764 /* save pointers to passwords so we don't have to keep looking them up */
765 lanman_pw = pdb_get_lanman_passwd(sampass);
766 nt_pw = pdb_get_nt_passwd (sampass);
768 /* check for null passwords */
769 if (lanman_pw == NULL) {
770 if (!(acct_ctrl & ACB_PWNOTREQ)) {
771 DEBUG(0,("check_oem_password: no lanman password !\n"));
776 if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) {
777 if (!(acct_ctrl & ACB_PWNOTREQ)) {
778 DEBUG(0,("check_oem_password: no ntlm password !\n"));
784 * Call the hash function to get the new password.
786 SamOEMhash((uchar *) lmdata, (uchar *)lanman_pw, 516);
789 * The length of the new password is in the last 4 bytes of
793 new_pw_len = IVAL(lmdata, 512);
794 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
795 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
801 * nt passwords are in unicode
803 pull_ucs2(NULL, new_passwd,
804 (const smb_ucs2_t *)&lmdata[512 - new_pw_len],
805 new_passwd_size, new_pw_len, 0);
807 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
808 new_passwd[new_pw_len] = 0;
812 * To ensure we got the correct new password, hash it and
813 * use it as a key to test the passed old password.
816 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
821 * Now use new_p16 as the key to see if the old
824 D_P16(new_p16, lmhash, unenc_old_pw);
826 if (memcmp(lanman_pw, unenc_old_pw, 16))
828 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
832 #ifdef DEBUG_PASSWORD
834 ("check_oem_password: password %s ok\n", new_passwd));
840 * Now use new_p16 as the key to see if the old
843 D_P16(new_ntp16, lmhash, unenc_old_pw);
844 D_P16(new_ntp16, nthash, unenc_old_ntpw);
846 if (memcmp(lanman_pw, unenc_old_pw, 16))
848 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
852 if (memcmp(nt_pw, unenc_old_ntpw, 16))
854 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
857 #ifdef DEBUG_PASSWORD
858 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
863 /***********************************************************
864 Code to change the oem password. Changes both the lanman
866 ************************************************************/
868 BOOL change_oem_password(SAM_ACCOUNT *hnd, char *new_passwd)
872 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
876 /* Now write it into the file. */
878 ret = pdb_update_sam_account (hnd, False);
884 /***********************************************************
885 Code to check a plaintext password against smbpasswd entries.
886 ***********************************************************/
888 BOOL check_plaintext_password(char *user, char *old_passwd,
889 int old_passwd_size, SAM_ACCOUNT **hnd)
891 SAM_ACCOUNT *sampass = NULL;
892 uchar old_pw[16], old_ntpw[16];
895 pdb_init_sam(&sampass);
898 ret = pdb_getsampwnam(sampass, user);
905 DEBUG(0,("check_plaintext_password: getsmbpwnam returned NULL\n"));
909 if (pdb_get_acct_ctrl(sampass) & ACB_DISABLED)
911 DEBUG(0,("check_plaintext_password: account %s disabled.\n", user));
915 nt_lm_owf_gen(old_passwd, old_ntpw, old_pw);
917 #ifdef DEBUG_PASSWORD
918 DEBUG(100, ("check_plaintext_password: nt_passwd \n"));
919 dump_data(100, pdb_get_nt_passwd(sampass), 16);
920 DEBUG(100, ("check_plaintext_password: old_ntpw \n"));
921 dump_data(100, old_ntpw, 16);
922 DEBUG(100, ("check_plaintext_password: lanman_passwd \n"));
923 dump_data(100, pdb_get_lanman_passwd(sampass), 16);
924 DEBUG(100, ("check_plaintext_password: old_pw\n"));
925 dump_data(100, old_pw, 16);
928 if (memcmp(pdb_get_nt_passwd(sampass), old_ntpw, 16)
929 && memcmp(pdb_get_lanman_passwd(sampass), old_pw, 16))