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
56 static int findpty(char **slave)
63 #if defined(HAVE_GRANTPT)
64 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
65 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
69 *slave = (char *)ptsname(master);
73 ("findpty: Unable to create master/slave pty pair.\n"));
74 /* Stop fd leak on error. */
81 ("findpty: Allocated slave pty %s\n", *slave));
85 #endif /* HAVE_GRANTPT */
87 fstrcpy(line, "/dev/ptyXX");
89 dirp = opendir("/dev");
92 while ((dpname = readdirname(dirp)) != NULL)
94 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
97 ("pty: try to open %s, line was %s\n", dpname,
101 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
103 DEBUG(3, ("pty: opened %s\n", line));
115 static int dochild(int master, char *slavedev, char *name,
116 char *passwordprogram, BOOL as_root)
119 struct termios stermios;
120 struct passwd *pass = Get_Pwnam(name, True);
127 ("dochild: user name %s doesn't exist in the UNIX password database.\n",
135 gain_root_privilege();
137 /* Start new session - gets rid of controlling terminal. */
141 ("Weirdness, couldn't let go of controlling terminal\n"));
145 /* Open slave pty and acquire as new controlling terminal. */
146 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
148 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
152 ioctl(slave, I_PUSH, "ptem");
153 ioctl(slave, I_PUSH, "ldterm");
154 #elif defined(TIOCSCTTY)
155 if (ioctl(slave, TIOCSCTTY, 0) < 0)
157 DEBUG(3, ("Error in ioctl call for slave pty\n"));
165 /* Make slave stdin/out/err of child. */
167 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
169 DEBUG(3, ("Could not re-direct stdin\n"));
172 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
174 DEBUG(3, ("Could not re-direct stdout\n"));
177 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
179 DEBUG(3, ("Could not re-direct stderr\n"));
185 /* Set proper terminal attributes - no echo, canonical input processing,
186 no map NL to CR/NL on output. */
188 if (tcgetattr(0, &stermios) < 0)
191 ("could not read default terminal attributes on pty\n"));
194 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
195 stermios.c_lflag |= ICANON;
196 stermios.c_oflag &= ~(ONLCR);
197 if (tcsetattr(0, TCSANOW, &stermios) < 0)
199 DEBUG(3, ("could not set attributes of pty\n"));
203 /* make us completely into the right uid */
206 become_user_permanently(uid, gid);
210 ("Invoking '%s' as password change program.\n",
213 /* execl() password-change application */
214 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
216 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
222 static int expect(int master, char *issue, char *expected)
225 int attempts, timeout, nread, len;
228 for (attempts = 0; attempts < 2; attempts++)
230 if (!strequal(issue, "."))
232 if (lp_passwd_chat_debug())
233 DEBUG(100, ("expect: sending [%s]\n", issue));
235 write(master, issue, strlen(issue));
238 if (strequal(expected, "."))
245 while ((len = read_with_timeout(master, buffer + nread, 1,
246 sizeof(buffer) - nread - 1,
252 if ((match = unix_do_match(buffer, expected, False)))
256 if (lp_passwd_chat_debug())
257 DEBUG(100, ("expect: expected [%s] received [%s]\n",
265 DEBUG(2, ("expect: %s\n", strerror(errno)));
273 static void pwd_sub(char *buf)
275 all_string_sub(buf, "\\n", "\n", 0);
276 all_string_sub(buf, "\\r", "\r", 0);
277 all_string_sub(buf, "\\s", " ", 0);
278 all_string_sub(buf, "\\t", "\t", 0);
281 static int talktochild(int master, char *seq)
284 fstring issue, expected;
288 while (next_token(&seq, expected, NULL, sizeof(expected)))
293 if (!expect(master, issue, expected))
295 DEBUG(3, ("Response %d incorrect\n", count));
299 if (!next_token(&seq, issue, NULL, sizeof(issue)))
308 static BOOL chat_with_program(char *passwordprogram, char *name,
309 char *chatsequence, BOOL as_root)
317 /* allocate a pseudo-terminal device */
318 if ((master = findpty(&slavedev)) < 0)
321 ("Cannot Allocate pty for password change: %s\n",
327 * We need to temporarily stop CatchChild from eating
328 * SIGCLD signals as it also eats the exit status code. JRA.
331 CatchChildLeaveStatus();
333 if ((pid = fork()) < 0)
336 ("Cannot fork() child for password change: %s\n",
343 /* we now have a pty */
345 { /* This is the parent process */
346 if ((chstat = talktochild(master, chatsequence)) == False)
349 ("Child failed to change password: %s\n",
351 kill(pid, SIGKILL); /* be sure to end this process */
354 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0)
366 DEBUG(3, ("The process is no longer waiting!\n\n"));
373 * Go back to ignoring children.
382 ("We were waiting for the wrong process ID\n"));
385 if (WIFEXITED(wstat) == 0)
388 ("The process exited while we were waiting\n"));
391 if (WEXITSTATUS(wstat) != 0)
394 ("The status of the process exiting was %d\n",
405 * Lose any oplock capabilities.
407 set_process_capability(KERNEL_OPLOCK_CAPABILITY, False);
408 set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY,
411 /* make sure it doesn't freeze */
417 ("Dochild for user %s (uid=%d,gid=%d)\n", name,
418 (int)getuid(), (int)getgid()));
420 dochild(master, slavedev, name, passwordprogram,
424 * The child should never return from dochild() ....
428 ("chat_with_program: Error: dochild() returned %d\n",
435 ("Password change %ssuccessful for user %s\n",
436 (chstat ? "" : "un"), name));
441 BOOL chgpasswd(char *name, char *oldpass, char *newpass, BOOL as_root)
443 pstring passwordprogram;
444 pstring chatsequence;
449 DEBUG(3, ("Password change for user: %s\n", name));
452 DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass));
455 /* Take the passed information and test it for minimum criteria */
456 /* Minimum password length */
457 if (strlen(newpass) < lp_min_passwd_length()) /* too short, must be at least MINPASSWDLENGTH */
460 ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
461 name, lp_min_passwd_length()));
462 return (False); /* inform the user */
465 /* Password is same as old password */
466 if (strcmp(oldpass, newpass) == 0) /* don't allow same password */
469 ("Password Change: %s, New password is same as old\n", name)); /* log the attempt */
470 return (False); /* inform the user */
473 pstrcpy(passwordprogram, lp_passwd_program());
474 pstrcpy(chatsequence, lp_passwd_chat());
478 DEBUG(2, ("Null chat sequence - no password changing\n"));
482 if (!*passwordprogram)
484 DEBUG(2, ("Null password program - no password changing\n"));
489 * Check the old and new passwords don't contain any control
493 len = strlen(oldpass);
494 for (i = 0; i < len; i++)
496 if (iscntrl((int)oldpass[i]))
499 ("chat_with_program: oldpass contains control characters (disallowed).\n"));
504 len = strlen(newpass);
505 for (i = 0; i < len; i++)
507 if (iscntrl((int)newpass[i]))
510 ("chat_with_program: newpass contains control characters (disallowed).\n"));
515 pstring_sub(passwordprogram, "%u", name);
516 /* note that we do NOT substitute the %o and %n in the password program
517 as this would open up a security hole where the user could use
518 a new password containing shell escape characters */
520 pstring_sub(chatsequence, "%u", name);
521 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
522 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
523 return (chat_with_program
524 (passwordprogram, name, chatsequence, as_root));
527 #else /* ALLOW_CHANGE_PASSWORD */
528 BOOL chgpasswd(char *name, char *oldpass, char *newpass, BOOL as_root)
530 DEBUG(0, ("Password changing not compiled in (user=%s)\n", name));
533 #endif /* ALLOW_CHANGE_PASSWORD */
535 /***********************************************************
536 Code to check the lanman hashed password.
537 ************************************************************/
539 BOOL check_lanman_password(char *user, uchar * pass1,
540 uchar * pass2, struct smb_passwd **psmbpw)
542 static uchar null_pw[16];
543 uchar unenc_new_pw[16];
544 uchar unenc_old_pw[16];
545 struct smb_passwd *smbpw;
550 smbpw = getsmbpwnam(user);
556 ("check_lanman_password: getsmbpwnam returned NULL\n"));
560 if (smbpw->acct_ctrl & ACB_DISABLED)
563 ("check_lanman_password: account %s disabled.\n",
568 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
571 memset(no_pw, '\0', 14);
572 E_P16(no_pw, null_pw);
573 smbpw->smb_passwd = null_pw;
575 else if (smbpw->smb_passwd == NULL)
577 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
581 /* Get the new lanman hash. */
582 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
584 /* Use this to get the old lanman hash. */
585 D_P16(unenc_new_pw, pass1, unenc_old_pw);
587 /* Check that the two old passwords match. */
588 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
591 ("check_lanman_password: old password doesn't match.\n"));
599 /***********************************************************
600 Code to change the lanman hashed password.
601 It nulls out the NT hashed password as it will
603 ************************************************************/
605 BOOL change_lanman_password(struct smb_passwd *smbpw, uchar * pass1,
608 static uchar null_pw[16];
609 uchar unenc_new_pw[16];
615 ("change_lanman_password: no smb password entry.\n"));
619 if (smbpw->acct_ctrl & ACB_DISABLED)
622 ("change_lanman_password: account %s disabled.\n",
627 if ((smbpw->smb_passwd == NULL) && (smbpw->acct_ctrl & ACB_PWNOTREQ))
630 memset(no_pw, '\0', 14);
631 E_P16(no_pw, null_pw);
632 smbpw->smb_passwd = null_pw;
634 else if (smbpw->smb_passwd == NULL)
636 DEBUG(0, ("change_lanman_password: no lanman password !\n"));
640 /* Get the new lanman hash. */
641 D_P16(smbpw->smb_passwd, pass2, unenc_new_pw);
643 smbpw->smb_passwd = unenc_new_pw;
644 smbpw->smb_nt_passwd = NULL; /* We lose the NT hash. Sorry. */
646 /* Now write it into the file. */
648 ret = mod_smbpwd_entry(smbpw, False);
654 /***********************************************************
655 Code to check and change the OEM hashed password.
656 ************************************************************/
657 BOOL pass_oem_change(char *user,
658 uchar * lmdata, uchar * lmhash,
659 uchar * ntdata, uchar * nthash)
662 struct smb_passwd *sampw;
663 BOOL ret = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
665 new_passwd, sizeof(new_passwd));
668 * At this point we have the new case-sensitive plaintext
669 * password in the fstring new_passwd. If we wanted to synchronise
670 * with UNIX passwords we would call a UNIX password changing
671 * function here. However it would have to be done as root
672 * as the plaintext of the old users password is not
676 if (ret && lp_unix_password_sync())
678 ret = chgpasswd(user, "", new_passwd, True);
683 ret = change_oem_password(sampw, new_passwd, False);
686 memset(new_passwd, 0, sizeof(new_passwd));
691 /***********************************************************
692 Code to check the OEM hashed password.
694 this function ignores the 516 byte nt OEM hashed password
695 but does use the lm OEM password to check the nt hashed-hash.
697 ************************************************************/
698 BOOL check_oem_password(char *user,
699 uchar * lmdata, uchar * lmhash,
700 uchar * ntdata, uchar * nthash,
701 struct smb_passwd **psmbpw, char *new_passwd,
704 static uchar null_pw[16];
705 static uchar null_ntpw[16];
706 struct smb_passwd *smbpw = NULL;
709 uchar unenc_old_ntpw[16];
711 uchar unenc_old_pw[16];
714 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
717 *psmbpw = smbpw = getsmbpwnam(user);
718 unbecome_root(False);
722 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
726 if (smbpw->acct_ctrl & ACB_DISABLED)
729 ("check_lanman_password: account %s disabled.\n",
734 /* construct a null password (in case one is needed */
737 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
739 /* check for null passwords */
740 if (smbpw->smb_passwd == NULL)
742 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
744 smbpw->smb_passwd = null_pw;
749 ("check_oem_password: no lanman password !\n"));
754 if (smbpw->smb_nt_passwd == NULL && nt_pass_set)
756 if (smbpw->acct_ctrl & ACB_PWNOTREQ)
758 smbpw->smb_nt_passwd = null_pw;
763 ("check_oem_password: no ntlm password !\n"));
769 * Call the hash function to get the new password.
771 SamOEMhash((uchar *) lmdata, (uchar *) smbpw->smb_passwd, True);
774 * The length of the new password is in the last 4 bytes of
778 new_pw_len = IVAL(lmdata, 512);
779 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1)
782 ("check_oem_password: incorrect password length (%d).\n",
790 * nt passwords are in unicode
792 int uni_pw_len = new_pw_len;
796 dos_unistrn2((uint16 *)(&lmdata[512 - uni_pw_len]),
798 memcpy(new_passwd, pw, new_pw_len + 1);
802 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
803 new_passwd[new_pw_len] = '\0';
807 * To ensure we got the correct new password, hash it and
808 * use it as a key to test the passed old password.
811 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
816 * Now use new_p16 as the key to see if the old
819 D_P16(new_p16, lmhash, unenc_old_pw);
821 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
824 ("check_oem_password: old lm password doesn't match.\n"));
828 #ifdef DEBUG_PASSWORD
830 ("check_oem_password: password %s ok\n", new_passwd));
836 * Now use new_p16 as the key to see if the old
839 D_P16(new_ntp16, lmhash, unenc_old_pw);
840 D_P16(new_ntp16, nthash, unenc_old_ntpw);
842 if (memcmp(smbpw->smb_passwd, unenc_old_pw, 16))
845 ("check_oem_password: old lm password doesn't match.\n"));
849 if (memcmp(smbpw->smb_nt_passwd, unenc_old_ntpw, 16))
852 ("check_oem_password: old nt password doesn't match.\n"));
855 #ifdef DEBUG_PASSWORD
856 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
861 /***********************************************************
862 Code to change the oem password. Changes both the lanman
864 override = False, normal
865 override = True, override XXXXXXXXXX'd password
866 ************************************************************/
868 BOOL change_oem_password(struct smb_passwd *smbpw, char *new_passwd,
872 uchar new_nt_p16[16];
875 nt_lm_owf_gen(new_passwd, new_nt_p16, new_p16);
877 smbpw->smb_passwd = new_p16;
878 smbpw->smb_nt_passwd = new_nt_p16;
880 /* Now write it into the file. */
882 ret = mod_smbpwd_entry(smbpw, override);
885 memset(new_passwd, '\0', strlen(new_passwd));
890 /***********************************************************
891 Code to check a plaintext password against smbpasswd entries.
892 ***********************************************************/
894 BOOL check_plaintext_password(char *user, char *old_passwd,
895 int old_passwd_size, struct smb_passwd **psmbpw)
897 struct smb_passwd *smbpw = NULL;
898 uchar old_pw[16], old_ntpw[16];
901 *psmbpw = smbpw = getsmbpwnam(user);
902 unbecome_root(False);
907 ("check_plaintext_password: getsmbpwnam returned NULL\n"));
911 if (smbpw->acct_ctrl & ACB_DISABLED)
914 ("check_plaintext_password: account %s disabled.\n",
919 nt_lm_owf_gen(old_passwd, old_ntpw, old_pw);
921 #ifdef DEBUG_PASSWORD
922 DEBUG(100, ("check_plaintext_password: smbpw->smb_nt_passwd \n"));
923 dump_data(100, smbpw->smb_nt_passwd, 16);
924 DEBUG(100, ("check_plaintext_password: old_ntpw \n"));
925 dump_data(100, old_ntpw, 16);
926 DEBUG(100, ("check_plaintext_password: smbpw->smb_passwd \n"));
927 dump_data(100, smbpw->smb_passwd, 16);
928 DEBUG(100, ("check_plaintext_password: old_pw\n"));
929 dump_data(100, old_pw, 16);
932 if (memcmp(smbpw->smb_nt_passwd, old_ntpw, 16)
933 && memcmp(smbpw->smb_passwd, old_pw, 16))