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 oplock_set_capability(False, False);
411 /* make sure it doesn't freeze */
418 ("Dochild for user %s (uid=%d,gid=%d)\n", name,
419 (int)getuid(), (int)getgid()));
421 dochild(master, slavedev, name, passwordprogram,
428 * The child should never return from dochild() ....
432 ("chat_with_program: Error: dochild() returned %d\n",
439 ("Password change %ssuccessful for user %s\n",
440 (chstat ? "" : "un"), name));
445 BOOL chgpasswd(char *name, char *oldpass, char *newpass, BOOL as_root)
447 pstring passwordprogram;
448 pstring chatsequence;
453 DEBUG(3, ("Password change for user: %s\n", name));
456 DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass));
459 /* Take the passed information and test it for minimum criteria */
460 /* Minimum password length */
461 if (strlen(newpass) < lp_min_passwd_length()) /* too short, must be at least MINPASSWDLENGTH */
464 ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
465 name, lp_min_passwd_length()));
466 return (False); /* inform the user */
469 /* Password is same as old password */
470 if (strcmp(oldpass, newpass) == 0) /* don't allow same password */
473 ("Password Change: %s, New password is same as old\n", name)); /* log the attempt */
474 return (False); /* inform the user */
477 pstrcpy(passwordprogram, lp_passwd_program());
478 pstrcpy(chatsequence, lp_passwd_chat());
482 DEBUG(2, ("Null chat sequence - no password changing\n"));
486 if (!*passwordprogram)
488 DEBUG(2, ("Null password program - no password changing\n"));
493 * Check the old and new passwords don't contain any control
497 len = strlen(oldpass);
498 for (i = 0; i < len; i++)
500 if (iscntrl((int)oldpass[i]))
503 ("chat_with_program: oldpass contains control characters (disallowed).\n"));
508 len = strlen(newpass);
509 for (i = 0; i < len; i++)
511 if (iscntrl((int)newpass[i]))
514 ("chat_with_program: newpass contains control characters (disallowed).\n"));
519 pstring_sub(passwordprogram, "%u", name);
520 /* note that we do NOT substitute the %o and %n in the password program
521 as this would open up a security hole where the user could use
522 a new password containing shell escape characters */
524 pstring_sub(chatsequence, "%u", name);
525 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
526 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
527 return (chat_with_program
528 (passwordprogram, name, chatsequence, as_root));
531 #else /* ALLOW_CHANGE_PASSWORD */
532 BOOL chgpasswd(char *name, char *oldpass, char *newpass, BOOL as_root)
534 DEBUG(0, ("Password changing not compiled in (user=%s)\n", name));
537 #endif /* ALLOW_CHANGE_PASSWORD */
539 /***********************************************************
540 Code to check the lanman hashed password.
541 ************************************************************/
543 BOOL check_lanman_password(char *user, uchar * pass1,
544 uchar * pass2, struct smb_passwd **psmbpw)
546 static uchar null_pw[16];
547 uchar unenc_new_pw[16];
548 uchar unenc_old_pw[16];
549 struct smb_passwd *smbpw;
554 smbpw = getsmbpwnam(user);
560 ("check_lanman_password: getsmbpwnam returned NULL\n"));
564 if (smbpw->acct_ctrl & ACB_DISABLED)
567 ("check_lanman_password: account %s disabled.\n",
572 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
575 memset(no_pw, '\0', 14);
576 E_P16(no_pw, null_pw);
577 smbpw->smb_passwd = null_pw;
579 else if (smbpw->smb_passwd == NULL)
581 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
585 /* Get the new lanman hash. */
586 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
588 /* Use this to get the old lanman hash. */
589 D_P16(unenc_new_pw, pass1, unenc_old_pw);
591 /* Check that the two old passwords match. */
592 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
595 ("check_lanman_password: old password doesn't match.\n"));
603 /***********************************************************
604 Code to change the lanman hashed password.
605 It nulls out the NT hashed password as it will
607 ************************************************************/
609 BOOL change_lanman_password(struct smb_passwd *smbpw, uchar * pass1,
612 static uchar null_pw[16];
613 uchar unenc_new_pw[16];
619 ("change_lanman_password: no smb password entry.\n"));
623 if (smbpw->acct_ctrl & ACB_DISABLED)
626 ("change_lanman_password: account %s disabled.\n",
631 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
634 memset(no_pw, '\0', 14);
635 E_P16(no_pw, null_pw);
636 smbpw->smb_passwd = null_pw;
638 else if (smbpw->smb_passwd == NULL)
640 DEBUG(0, ("change_lanman_password: no lanman password !\n"));
644 /* Get the new lanman hash. */
645 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
647 smbpw->smb_passwd = unenc_new_pw;
648 smbpw->smb_nt_passwd = NULL; /* We lose the NT hash. Sorry. */
650 /* Now write it into the file. */
652 ret = mod_smbpwd_entry(smbpw, False);
658 /***********************************************************
659 Code to check and change the OEM hashed password.
660 ************************************************************/
661 BOOL pass_oem_change(char *user,
662 uchar * lmdata, uchar * lmhash,
663 uchar * ntdata, uchar * nthash)
666 struct smb_passwd *sampw;
667 BOOL ret = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
669 new_passwd, sizeof(new_passwd));
672 * At this point we have the new case-sensitive plaintext
673 * password in the fstring new_passwd. If we wanted to synchronise
674 * with UNIX passwords we would call a UNIX password changing
675 * function here. However it would have to be done as root
676 * as the plaintext of the old users password is not
680 if (ret && lp_unix_password_sync())
682 ret = chgpasswd(user, "", new_passwd, True);
687 ret = change_oem_password(sampw, new_passwd, False);
690 memset(new_passwd, 0, sizeof(new_passwd));
695 /***********************************************************
696 Code to check the OEM hashed password.
698 this function ignores the 516 byte nt OEM hashed password
699 but does use the lm OEM password to check the nt hashed-hash.
701 ************************************************************/
702 BOOL check_oem_password(char *user,
703 uchar * lmdata, uchar * lmhash,
704 uchar * ntdata, uchar * nthash,
705 struct smb_passwd **psmbpw, char *new_passwd,
708 static uchar null_pw[16];
709 static uchar null_ntpw[16];
710 struct smb_passwd *smbpw = NULL;
713 uchar unenc_old_ntpw[16];
715 uchar unenc_old_pw[16];
718 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
721 *psmbpw = smbpw = getsmbpwnam(user);
726 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
730 if (smbpw->acct_ctrl & ACB_DISABLED)
733 ("check_lanman_password: account %s disabled.\n",
738 /* construct a null password (in case one is needed */
741 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
743 /* check for null passwords */
744 if (smbpw->smb_passwd == NULL)
746 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
748 smbpw->smb_passwd = null_pw;
753 ("check_oem_password: no lanman password !\n"));
758 if (smbpw->smb_nt_passwd == NULL && nt_pass_set)
760 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
762 smbpw->smb_nt_passwd = null_pw;
767 ("check_oem_password: no ntlm password !\n"));
773 * Call the hash function to get the new password.
775 SamOEMhash((uchar *) lmdata, (uchar *) smbpw->smb_passwd, True);
778 * The length of the new password is in the last 4 bytes of
782 new_pw_len = IVAL(lmdata, 512);
783 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1)
786 ("check_oem_password: incorrect password length (%d).\n",
794 * nt passwords are in unicode
796 int uni_pw_len = new_pw_len;
800 dos_unistrn2((uint16 *)(&lmdata[512 - uni_pw_len]),
802 memcpy(new_passwd, pw, new_pw_len + 1);
806 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
807 new_passwd[new_pw_len] = '\0';
811 * To ensure we got the correct new password, hash it and
812 * use it as a key to test the passed old password.
815 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
820 * Now use new_p16 as the key to see if the old
823 D_P16(new_p16, lmhash, unenc_old_pw);
825 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
828 ("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(smbpw->smb_passwd, unenc_old_pw, 16))
849 ("check_oem_password: old lm password doesn't match.\n"));
853 if (memcmp(smbpw->smb_nt_passwd, unenc_old_ntpw, 16))
856 ("check_oem_password: old nt password doesn't match.\n"));
859 #ifdef DEBUG_PASSWORD
860 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
865 /***********************************************************
866 Code to change the oem password. Changes both the lanman
868 override = False, normal
869 override = True, override XXXXXXXXXX'd password
870 ************************************************************/
872 BOOL change_oem_password(struct smb_passwd *smbpw, char *new_passwd,
876 uchar new_nt_p16[16];
879 nt_lm_owf_gen(new_passwd, new_nt_p16, new_p16);
881 smbpw->smb_passwd = new_p16;
882 smbpw->smb_nt_passwd = new_nt_p16;
884 /* Now write it into the file. */
886 ret = mod_smbpwd_entry(smbpw, override);
889 memset(new_passwd, '\0', strlen(new_passwd));
894 /***********************************************************
895 Code to check a plaintext password against smbpasswd entries.
896 ***********************************************************/
898 BOOL check_plaintext_password(char *user, char *old_passwd,
899 int old_passwd_size, struct smb_passwd **psmbpw)
901 struct smb_passwd *smbpw = NULL;
902 uchar old_pw[16], old_ntpw[16];
905 *psmbpw = smbpw = getsmbpwnam(user);
911 ("check_plaintext_password: getsmbpwnam returned NULL\n"));
915 if (smbpw->acct_ctrl & ACB_DISABLED)
918 ("check_plaintext_password: account %s disabled.\n",
923 nt_lm_owf_gen(old_passwd, old_ntpw, old_pw);
925 #ifdef DEBUG_PASSWORD
926 DEBUG(100, ("check_plaintext_password: smbpw->smb_nt_passwd \n"));
927 dump_data(100, smbpw->smb_nt_passwd, 16);
928 DEBUG(100, ("check_plaintext_password: old_ntpw \n"));
929 dump_data(100, old_ntpw, 16);
930 DEBUG(100, ("check_plaintext_password: smbpw->smb_passwd \n"));
931 dump_data(100, smbpw->smb_passwd, 16);
932 DEBUG(100, ("check_plaintext_password: old_pw\n"));
933 dump_data(100, old_pw, 16);
936 if (memcmp(smbpw->smb_nt_passwd, old_ntpw, 16)
937 && memcmp(smbpw->smb_passwd, old_pw, 16))