4 Unix SMB/Netbios implementation.
6 Samba utility functions
7 Copyright (C) Andrew Tridgell 1992-1998
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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 routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE
36 * is defined in the compiler directives located in the Makefile.
38 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
39 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
40 * and rights to modify, distribute or incorporate this change to the CAP suite or
41 * using it for any other reason are granted, so long as this disclaimer is left intact.
45 This code was hacked considerably for inclusion in Samba, primarily
46 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
47 of the "password chat" option, which allows the easy runtime
48 specification of the expected sequence of events to change a
54 extern int DEBUGLEVEL;
56 #if ALLOW_CHANGE_PASSWORD
58 static int findpty(char **slave)
65 #if defined(HAVE_GRANTPT)
66 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
67 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
71 *slave = (char *)ptsname(master);
75 ("findpty: Unable to create master/slave pty pair.\n"));
76 /* Stop fd leak on error. */
83 ("findpty: Allocated slave pty %s\n", *slave));
87 #endif /* HAVE_GRANTPT */
89 fstrcpy(line, "/dev/ptyXX");
91 dirp = opendir("/dev");
94 while ((dpname = readdirname(dirp)) != NULL)
96 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
99 ("pty: try to open %s, line was %s\n", dpname,
103 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
105 DEBUG(3, ("pty: opened %s\n", line));
117 static int dochild(int master, char *slavedev, char *name,
118 char *passwordprogram, BOOL as_root)
121 struct termios stermios;
122 struct passwd *pass = Get_Pwnam(name, True);
129 ("dochild: user name %s doesn't exist in the UNIX password database.\n",
137 gain_root_privilege();
139 /* Start new session - gets rid of controlling terminal. */
143 ("Weirdness, couldn't let go of controlling terminal\n"));
147 /* Open slave pty and acquire as new controlling terminal. */
148 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
150 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
154 ioctl(slave, I_PUSH, "ptem");
155 ioctl(slave, I_PUSH, "ldterm");
156 #elif defined(TIOCSCTTY)
157 if (ioctl(slave, TIOCSCTTY, 0) < 0)
159 DEBUG(3, ("Error in ioctl call for slave pty\n"));
167 /* Make slave stdin/out/err of child. */
169 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
171 DEBUG(3, ("Could not re-direct stdin\n"));
174 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
176 DEBUG(3, ("Could not re-direct stdout\n"));
179 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
181 DEBUG(3, ("Could not re-direct stderr\n"));
187 /* Set proper terminal attributes - no echo, canonical input processing,
188 no map NL to CR/NL on output. */
190 if (tcgetattr(0, &stermios) < 0)
193 ("could not read default terminal attributes on pty\n"));
196 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
197 stermios.c_lflag |= ICANON;
198 stermios.c_oflag &= ~(ONLCR);
199 if (tcsetattr(0, TCSANOW, &stermios) < 0)
201 DEBUG(3, ("could not set attributes of pty\n"));
205 /* make us completely into the right uid */
208 become_user_permanently(uid, gid);
212 ("Invoking '%s' as password change program.\n",
215 /* execl() password-change application */
216 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
218 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
224 static int expect(int master, char *issue, char *expected)
227 int attempts, timeout, nread, len;
230 for (attempts = 0; attempts < 2; attempts++)
232 if (!strequal(issue, "."))
234 if (lp_passwd_chat_debug())
235 DEBUG(100, ("expect: sending [%s]\n", issue));
237 write(master, issue, strlen(issue));
240 if (strequal(expected, "."))
247 while ((len = read_with_timeout(master, buffer + nread, 1,
248 sizeof(buffer) - nread - 1,
254 if ((match = (ms_fnmatch(expected, buffer) == 0)))
258 if (lp_passwd_chat_debug())
259 DEBUG(100, ("expect: expected [%s] received [%s]\n",
267 DEBUG(2, ("expect: %s\n", strerror(errno)));
275 static void pwd_sub(char *buf)
277 all_string_sub(buf, "\\n", "\n", 0);
278 all_string_sub(buf, "\\r", "\r", 0);
279 all_string_sub(buf, "\\s", " ", 0);
280 all_string_sub(buf, "\\t", "\t", 0);
283 static int talktochild(int master, char *seq)
286 fstring issue, expected;
290 while (next_token(&seq, expected, NULL, sizeof(expected)))
295 if (!expect(master, issue, expected))
297 DEBUG(3, ("Response %d incorrect\n", count));
301 if (!next_token(&seq, issue, NULL, sizeof(issue)))
310 static BOOL chat_with_program(char *passwordprogram, char *name,
311 char *chatsequence, BOOL as_root)
319 /* allocate a pseudo-terminal device */
320 if ((master = findpty(&slavedev)) < 0)
323 ("Cannot Allocate pty for password change: %s\n",
329 * We need to temporarily stop CatchChild from eating
330 * SIGCLD signals as it also eats the exit status code. JRA.
333 CatchChildLeaveStatus();
335 if ((pid = sys_fork()) < 0)
338 ("Cannot fork() child for password change: %s\n",
345 /* we now have a pty */
347 { /* This is the parent process */
348 if ((chstat = talktochild(master, chatsequence)) == False)
351 ("Child failed to change password: %s\n",
353 kill(pid, SIGKILL); /* be sure to end this process */
356 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0)
368 DEBUG(3, ("The process is no longer waiting!\n\n"));
375 * Go back to ignoring children.
384 ("We were waiting for the wrong process ID\n"));
387 if (WIFEXITED(wstat) == 0)
390 ("The process exited while we were waiting\n"));
393 if (WEXITSTATUS(wstat) != 0)
396 ("The status of the process exiting was %d\n",
407 * Lose any oplock capabilities.
409 set_process_capability(KERNEL_OPLOCK_CAPABILITY, False);
410 set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY,
413 /* make sure it doesn't freeze */
419 ("Dochild for user %s (uid=%d,gid=%d)\n", name,
420 (int)getuid(), (int)getgid()));
422 dochild(master, slavedev, name, passwordprogram,
426 * The child should never return from dochild() ....
430 ("chat_with_program: Error: dochild() returned %d\n",
437 ("Password change %ssuccessful for user %s\n",
438 (chstat ? "" : "un"), name));
443 BOOL chgpasswd(char *name, char *oldpass, char *newpass, BOOL as_root)
445 pstring passwordprogram;
446 pstring chatsequence;
451 DEBUG(3, ("Password change for user: %s\n", name));
454 DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass));
457 /* Take the passed information and test it for minimum criteria */
458 /* Minimum password length */
459 if (strlen(newpass) < lp_min_passwd_length()) /* too short, must be at least MINPASSWDLENGTH */
462 ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
463 name, lp_min_passwd_length()));
464 return (False); /* inform the user */
467 /* Password is same as old password */
468 if (strcmp(oldpass, newpass) == 0) /* don't allow same password */
471 ("Password Change: %s, New password is same as old\n", name)); /* log the attempt */
472 return (False); /* inform the user */
475 pstrcpy(passwordprogram, lp_passwd_program());
476 pstrcpy(chatsequence, lp_passwd_chat());
480 DEBUG(2, ("Null chat sequence - no password changing\n"));
484 if (!*passwordprogram)
486 DEBUG(2, ("Null password program - no password changing\n"));
491 * Check the old and new passwords don't contain any control
495 len = strlen(oldpass);
496 for (i = 0; i < len; i++)
498 if (iscntrl((int)oldpass[i]))
501 ("chat_with_program: oldpass contains control characters (disallowed).\n"));
506 len = strlen(newpass);
507 for (i = 0; i < len; i++)
509 if (iscntrl((int)newpass[i]))
512 ("chat_with_program: newpass contains control characters (disallowed).\n"));
517 pstring_sub(passwordprogram, "%u", name);
518 /* note that we do NOT substitute the %o and %n in the password program
519 as this would open up a security hole where the user could use
520 a new password containing shell escape characters */
522 pstring_sub(chatsequence, "%u", name);
523 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
524 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
525 return (chat_with_program
526 (passwordprogram, name, chatsequence, as_root));
529 #else /* ALLOW_CHANGE_PASSWORD */
530 BOOL chgpasswd(char *name, char *oldpass, char *newpass, BOOL as_root)
532 DEBUG(0, ("Password changing not compiled in (user=%s)\n", name));
535 #endif /* ALLOW_CHANGE_PASSWORD */
537 /***********************************************************
538 Code to check the lanman hashed password.
539 ************************************************************/
541 BOOL check_lanman_password(char *user, uchar * pass1,
542 uchar * pass2, struct smb_passwd **psmbpw)
544 static uchar null_pw[16];
545 uchar unenc_new_pw[16];
546 uchar unenc_old_pw[16];
547 struct smb_passwd *smbpw;
552 smbpw = getsmbpwnam(user);
558 ("check_lanman_password: getsmbpwnam returned NULL\n"));
562 if (smbpw->acct_ctrl & ACB_DISABLED)
565 ("check_lanman_password: account %s disabled.\n",
570 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
573 memset(no_pw, '\0', 14);
574 E_P16(no_pw, null_pw);
575 smbpw->smb_passwd = null_pw;
577 else if (smbpw->smb_passwd == NULL)
579 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
583 /* Get the new lanman hash. */
584 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
586 /* Use this to get the old lanman hash. */
587 D_P16(unenc_new_pw, pass1, unenc_old_pw);
589 /* Check that the two old passwords match. */
590 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
593 ("check_lanman_password: old password doesn't match.\n"));
601 /***********************************************************
602 Code to change the lanman hashed password.
603 It nulls out the NT hashed password as it will
605 ************************************************************/
607 BOOL change_lanman_password(struct smb_passwd *smbpw, uchar * pass1,
610 static uchar null_pw[16];
611 uchar unenc_new_pw[16];
617 ("change_lanman_password: no smb password entry.\n"));
621 if (smbpw->acct_ctrl & ACB_DISABLED)
624 ("change_lanman_password: account %s disabled.\n",
629 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
632 memset(no_pw, '\0', 14);
633 E_P16(no_pw, null_pw);
634 smbpw->smb_passwd = null_pw;
636 else if (smbpw->smb_passwd == NULL)
638 DEBUG(0, ("change_lanman_password: no lanman password !\n"));
642 /* Get the new lanman hash. */
643 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
645 smbpw->smb_passwd = unenc_new_pw;
646 smbpw->smb_nt_passwd = NULL; /* We lose the NT hash. Sorry. */
648 /* Now write it into the file. */
650 ret = mod_smbpwd_entry(smbpw, False);
656 /***********************************************************
657 Code to check and change the OEM hashed password.
658 ************************************************************/
659 BOOL pass_oem_change(char *user,
660 uchar * lmdata, uchar * lmhash,
661 uchar * ntdata, uchar * nthash)
664 struct smb_passwd *sampw;
665 BOOL ret = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
667 new_passwd, sizeof(new_passwd));
670 * At this point we have the new case-sensitive plaintext
671 * password in the fstring new_passwd. If we wanted to synchronise
672 * with UNIX passwords we would call a UNIX password changing
673 * function here. However it would have to be done as root
674 * as the plaintext of the old users password is not
678 if (ret && lp_unix_password_sync())
680 ret = chgpasswd(user, "", new_passwd, True);
685 ret = change_oem_password(sampw, new_passwd, False);
688 memset(new_passwd, 0, sizeof(new_passwd));
693 /***********************************************************
694 Code to check the OEM hashed password.
696 this function ignores the 516 byte nt OEM hashed password
697 but does use the lm OEM password to check the nt hashed-hash.
699 ************************************************************/
700 BOOL check_oem_password(char *user,
701 uchar * lmdata, uchar * lmhash,
702 uchar * ntdata, uchar * nthash,
703 struct smb_passwd **psmbpw, char *new_passwd,
706 static uchar null_pw[16];
707 static uchar null_ntpw[16];
708 struct smb_passwd *smbpw = NULL;
711 uchar unenc_old_ntpw[16];
713 uchar unenc_old_pw[16];
716 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
719 *psmbpw = smbpw = getsmbpwnam(user);
720 unbecome_root(False);
724 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
728 if (smbpw->acct_ctrl & ACB_DISABLED)
731 ("check_lanman_password: account %s disabled.\n",
736 /* construct a null password (in case one is needed */
739 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
741 /* check for null passwords */
742 if (smbpw->smb_passwd == NULL)
744 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
746 smbpw->smb_passwd = null_pw;
751 ("check_oem_password: no lanman password !\n"));
756 if (smbpw->smb_nt_passwd == NULL && nt_pass_set)
758 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
760 smbpw->smb_nt_passwd = null_pw;
765 ("check_oem_password: no ntlm password !\n"));
771 * Call the hash function to get the new password.
773 SamOEMhash((uchar *) lmdata, (uchar *) smbpw->smb_passwd, True);
776 * The length of the new password is in the last 4 bytes of
780 new_pw_len = IVAL(lmdata, 512);
781 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1)
784 ("check_oem_password: incorrect password length (%d).\n",
792 * nt passwords are in unicode
794 int uni_pw_len = new_pw_len;
798 dos_unistrn2((uint16 *)(&lmdata[512 - uni_pw_len]),
800 memcpy(new_passwd, pw, new_pw_len + 1);
804 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
805 new_passwd[new_pw_len] = '\0';
809 * To ensure we got the correct new password, hash it and
810 * use it as a key to test the passed old password.
813 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
818 * Now use new_p16 as the key to see if the old
821 D_P16(new_p16, lmhash, unenc_old_pw);
823 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
826 ("check_oem_password: old lm password doesn't match.\n"));
830 #ifdef DEBUG_PASSWORD
832 ("check_oem_password: password %s ok\n", new_passwd));
838 * Now use new_p16 as the key to see if the old
841 D_P16(new_ntp16, lmhash, unenc_old_pw);
842 D_P16(new_ntp16, nthash, unenc_old_ntpw);
844 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
847 ("check_oem_password: old lm password doesn't match.\n"));
851 if (memcmp(smbpw->smb_nt_passwd, unenc_old_ntpw, 16))
854 ("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 override = False, normal
867 override = True, override XXXXXXXXXX'd password
868 ************************************************************/
870 BOOL change_oem_password(struct smb_passwd *smbpw, char *new_passwd,
874 uchar new_nt_p16[16];
877 nt_lm_owf_gen(new_passwd, new_nt_p16, new_p16);
879 smbpw->smb_passwd = new_p16;
880 smbpw->smb_nt_passwd = new_nt_p16;
882 /* Now write it into the file. */
884 ret = mod_smbpwd_entry(smbpw, override);
887 memset(new_passwd, '\0', strlen(new_passwd));
892 /***********************************************************
893 Code to check a plaintext password against smbpasswd entries.
894 ***********************************************************/
896 BOOL check_plaintext_password(char *user, char *old_passwd,
897 int old_passwd_size, struct smb_passwd **psmbpw)
899 struct smb_passwd *smbpw = NULL;
900 uchar old_pw[16], old_ntpw[16];
903 *psmbpw = smbpw = getsmbpwnam(user);
904 unbecome_root(False);
909 ("check_plaintext_password: getsmbpwnam returned NULL\n"));
913 if (smbpw->acct_ctrl & ACB_DISABLED)
916 ("check_plaintext_password: account %s disabled.\n",
921 nt_lm_owf_gen(old_passwd, old_ntpw, old_pw);
923 #ifdef DEBUG_PASSWORD
924 DEBUG(100, ("check_plaintext_password: smbpw->smb_nt_passwd \n"));
925 dump_data(100, smbpw->smb_nt_passwd, 16);
926 DEBUG(100, ("check_plaintext_password: old_ntpw \n"));
927 dump_data(100, old_ntpw, 16);
928 DEBUG(100, ("check_plaintext_password: smbpw->smb_passwd \n"));
929 dump_data(100, smbpw->smb_passwd, 16);
930 DEBUG(100, ("check_plaintext_password: old_pw\n"));
931 dump_data(100, old_pw, 16);
934 if (memcmp(smbpw->smb_nt_passwd, old_ntpw, 16)
935 && memcmp(smbpw->smb_passwd, old_pw, 16))