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(const char *user,
55 uchar * lmdata, const uchar * lmhash,
56 const uchar * ntdata, const 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, const char *slavedev, const struct passwd *pass,
122 const char *passwordprogram, BOOL as_root)
125 struct termios stermios;
132 ("dochild: user doesn't exist in the UNIX password database.\n"));
139 gain_root_privilege();
141 /* Start new session - gets rid of controlling terminal. */
145 ("Weirdness, couldn't let go of controlling terminal\n"));
149 /* Open slave pty and acquire as new controlling terminal. */
150 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
152 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
156 ioctl(slave, I_PUSH, "ptem");
157 ioctl(slave, I_PUSH, "ldterm");
158 #elif defined(TIOCSCTTY)
159 if (ioctl(slave, TIOCSCTTY, 0) < 0)
161 DEBUG(3, ("Error in ioctl call for slave pty\n"));
169 /* Make slave stdin/out/err of child. */
171 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
173 DEBUG(3, ("Could not re-direct stdin\n"));
176 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
178 DEBUG(3, ("Could not re-direct stdout\n"));
181 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
183 DEBUG(3, ("Could not re-direct stderr\n"));
189 /* Set proper terminal attributes - no echo, canonical input processing,
190 no map NL to CR/NL on output. */
192 if (tcgetattr(0, &stermios) < 0)
195 ("could not read default terminal attributes on pty\n"));
198 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
199 stermios.c_lflag |= ICANON;
200 stermios.c_oflag &= ~(ONLCR);
201 if (tcsetattr(0, TCSANOW, &stermios) < 0)
203 DEBUG(3, ("could not set attributes of pty\n"));
207 /* make us completely into the right uid */
210 become_user_permanently(uid, gid);
214 ("Invoking '%s' as password change program.\n",
217 /* execl() password-change application */
218 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
220 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
226 static int expect(int master, char *issue, char *expected)
229 int attempts, timeout, nread, len;
232 for (attempts = 0; attempts < 2; attempts++) {
233 if (!strequal(issue, ".")) {
234 if (lp_passwd_chat_debug())
235 DEBUG(100, ("expect: sending [%s]\n", issue));
237 if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
238 DEBUG(2,("expect: (short) write returned %d\n", len ));
243 if (strequal(expected, "."))
250 while ((len = read_with_timeout(master, buffer + nread, 1,
251 sizeof(buffer) - nread - 1,
257 /* Eat leading/trailing whitespace before match. */
259 pstrcpy( str, buffer);
260 trim_string( str, " ", " ");
262 if ((match = (unix_wild_match(expected, str) == 0)))
267 if (lp_passwd_chat_debug())
268 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
269 expected, buffer, match ? "yes" : "no" ));
275 DEBUG(2, ("expect: %s\n", strerror(errno)));
280 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
284 static void pwd_sub(char *buf)
286 all_string_sub(buf, "\\n", "\n", 0);
287 all_string_sub(buf, "\\r", "\r", 0);
288 all_string_sub(buf, "\\s", " ", 0);
289 all_string_sub(buf, "\\t", "\t", 0);
292 static int talktochild(int master, char *seq)
295 fstring issue, expected;
299 while (next_token(&seq, expected, NULL, sizeof(expected)))
304 if (!expect(master, issue, expected))
306 DEBUG(3, ("Response %d incorrect\n", count));
310 if (!next_token(&seq, issue, NULL, sizeof(issue)))
315 if (!strequal(issue, ".")) {
316 /* we have one final issue to send */
317 fstrcpy(expected, ".");
318 if (!expect(master, issue, expected))
325 static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
326 char *chatsequence, BOOL as_root)
337 ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
341 /* allocate a pseudo-terminal device */
342 if ((master = findpty(&slavedev)) < 0)
345 ("Cannot Allocate pty for password change: %s\n",
351 * We need to temporarily stop CatchChild from eating
352 * SIGCLD signals as it also eats the exit status code. JRA.
355 CatchChildLeaveStatus();
357 if ((pid = sys_fork()) < 0)
360 ("Cannot fork() child for password change: %s\n",
367 /* we now have a pty */
369 { /* This is the parent process */
370 if ((chstat = talktochild(master, chatsequence)) == False)
373 ("Child failed to change password: %s\n",
375 kill(pid, SIGKILL); /* be sure to end this process */
378 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0)
390 DEBUG(3, ("The process is no longer waiting!\n\n"));
397 * Go back to ignoring children.
406 ("We were waiting for the wrong process ID\n"));
409 if (WIFEXITED(wstat) == 0)
412 ("The process exited while we were waiting\n"));
415 if (WEXITSTATUS(wstat) != 0)
418 ("The status of the process exiting was %d\n",
429 * Lose any oplock capabilities.
431 oplock_set_capability(False, False);
433 /* make sure it doesn't freeze */
440 ("Dochild for user %s (uid=%d,gid=%d)\n", pass->pw_name,
441 (int)getuid(), (int)getgid()));
443 dochild(master, slavedev, pass, passwordprogram,
450 * The child should never return from dochild() ....
454 ("chat_with_program: Error: dochild() returned %d\n",
461 ("Password change %ssuccessful for user %s\n",
462 (chstat ? "" : "un"), pass->pw_name));
467 BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
469 pstring passwordprogram;
470 pstring chatsequence;
476 DEBUG(3, ("Password change for user: %s\n", name));
479 DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass));
482 /* Take the passed information and test it for minimum criteria */
483 /* Minimum password length */
484 if (strlen(newpass) < lp_min_passwd_length()) {
485 /* too short, must be at least MINPASSWDLENGTH */
486 DEBUG(0, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
487 name, lp_min_passwd_length()));
488 return (False); /* inform the user */
491 /* Password is same as old password */
492 if (strcmp(oldpass, newpass) == 0) {
493 /* don't allow same password */
494 DEBUG(2, ("Password Change: %s, New password is same as old\n", name)); /* log the attempt */
495 return (False); /* inform the user */
499 * Check the old and new passwords don't contain any control
503 len = strlen(oldpass);
504 for (i = 0; i < len; i++) {
505 if (iscntrl((int)oldpass[i])) {
507 ("chat_with_program: oldpass contains control characters (disallowed).\n"));
512 len = strlen(newpass);
513 for (i = 0; i < len; i++) {
514 if (iscntrl((int)newpass[i])) {
516 ("chat_with_program: newpass contains control characters (disallowed).\n"));
521 pass = Get_Pwnam(name);
524 if (lp_pam_password_change()) {
531 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
533 ret = smb_pam_passchange(name, oldpass, newpass);
543 /* A non-PAM password change just doen't make sense without a valid local user */
548 ("chgpasswd: user %s doesn't exist in the UNIX password database.\n",
553 pstrcpy(passwordprogram, lp_passwd_program());
554 pstrcpy(chatsequence, lp_passwd_chat());
556 if (!*chatsequence) {
557 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
561 if (!*passwordprogram) {
562 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
567 /* The password program *must* contain the user name to work. Fail if not. */
568 if (strstr(passwordprogram, "%u") == NULL) {
569 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
570 the string %%u, and the given string %s does not.\n", passwordprogram ));
575 pstring_sub(passwordprogram, "%u", name);
576 /* note that we do NOT substitute the %o and %n in the password program
577 as this would open up a security hole where the user could use
578 a new password containing shell escape characters */
580 pstring_sub(chatsequence, "%u", name);
581 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
582 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
583 return (chat_with_program
584 (passwordprogram, pass, chatsequence, as_root));
587 #else /* ALLOW_CHANGE_PASSWORD */
589 BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
591 DEBUG(0, ("Password changing not compiled in (user=%s)\n", name));
594 #endif /* ALLOW_CHANGE_PASSWORD */
596 /***********************************************************
597 Code to check the lanman hashed password.
598 ************************************************************/
600 BOOL check_lanman_password(char *user, uchar * pass1,
601 uchar * pass2, SAM_ACCOUNT **hnd)
603 uchar unenc_new_pw[16];
604 uchar unenc_old_pw[16];
605 SAM_ACCOUNT *sampass = NULL;
607 const uint8 *lanman_pw;
611 ret = pdb_getsampwnam(sampass, user);
615 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
616 pdb_free_sam(&sampass);
620 acct_ctrl = pdb_get_acct_ctrl (sampass);
621 lanman_pw = pdb_get_lanman_passwd (sampass);
623 if (acct_ctrl & ACB_DISABLED) {
624 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
625 pdb_free_sam(&sampass);
629 if (lanman_pw == NULL) {
630 if (acct_ctrl & ACB_PWNOTREQ) {
631 /* this saves the pointer for the caller */
635 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
636 pdb_free_sam(&sampass);
641 /* Get the new lanman hash. */
642 D_P16(lanman_pw, pass2, unenc_new_pw);
644 /* Use this to get the old lanman hash. */
645 D_P16(unenc_new_pw, pass1, unenc_old_pw);
647 /* Check that the two old passwords match. */
648 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
649 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
650 pdb_free_sam(&sampass);
654 /* this saves the pointer for the caller */
659 /***********************************************************
660 Code to change the lanman hashed password.
661 It nulls out the NT hashed password as it will
663 ************************************************************/
665 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar * pass1,
668 static uchar null_pw[16];
669 uchar unenc_new_pw[16];
674 if (sampass == NULL) {
675 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
679 acct_ctrl = pdb_get_acct_ctrl(sampass);
680 pwd = pdb_get_lanman_passwd(sampass);
682 if (acct_ctrl & ACB_DISABLED) {
683 DEBUG(0,("change_lanman_password: account %s disabled.\n",
684 pdb_get_username(sampass)));
689 if (acct_ctrl & ACB_PWNOTREQ) {
691 memset(no_pw, '\0', 14);
692 E_P16(no_pw, null_pw);
694 /* Get the new lanman hash. */
695 D_P16(null_pw, pass2, unenc_new_pw);
697 DEBUG(0,("change_lanman_password: no lanman password !\n"));
701 /* Get the new lanman hash. */
702 D_P16(pwd, pass2, unenc_new_pw);
705 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw)) {
709 if (!pdb_set_nt_passwd (sampass, NULL)) {
710 return False; /* We lose the NT hash. Sorry. */
713 if (!pdb_set_pass_changed_now (sampass)) {
714 pdb_free_sam(&sampass);
715 /* Not quite sure what this one qualifies as, but this will do */
719 /* Now flush the sam_passwd struct to persistent storage */
721 ret = pdb_update_sam_account (sampass);
727 /***********************************************************
728 Code to check and change the OEM hashed password.
729 ************************************************************/
730 BOOL pass_oem_change(char *user,
731 uchar * lmdata, uchar * lmhash,
732 uchar * ntdata, uchar * nthash)
735 const char *unix_user;
736 SAM_ACCOUNT *sampass = NULL;
737 BOOL ret = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
738 &sampass, new_passwd, sizeof(new_passwd));
741 * At this point we have the new case-sensitive plaintext
742 * password in the fstring new_passwd. If we wanted to synchronise
743 * with UNIX passwords we would call a UNIX password changing
744 * function here. However it would have to be done as root
745 * as the plaintext of the old users password is not
749 unix_user = pdb_get_username(sampass);
751 if ((ret) && (unix_user) && (*unix_user) && lp_unix_password_sync())
752 ret = chgpasswd(unix_user, "", new_passwd, True);
755 ret = change_oem_password(sampass, new_passwd);
757 memset(new_passwd, 0, sizeof(new_passwd));
759 pdb_free_sam(&sampass);
764 /***********************************************************
765 Code to check the OEM hashed password.
767 this function ignores the 516 byte nt OEM hashed password
768 but does use the lm OEM password to check the nt hashed-hash.
770 ************************************************************/
771 static BOOL check_oem_password(const char *user,
772 uchar * lmdata, const uchar * lmhash,
773 const uchar * ntdata, const uchar * nthash,
774 SAM_ACCOUNT **hnd, char *new_passwd,
777 static uchar null_pw[16];
778 static uchar null_ntpw[16];
779 SAM_ACCOUNT *sampass = NULL;
780 const uint8 *lanman_pw, *nt_pw;
784 uchar unenc_old_ntpw[16];
786 uchar unenc_old_pw[16];
790 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
792 pdb_init_sam(&sampass);
795 ret = pdb_getsampwnam(sampass, user);
799 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
805 acct_ctrl = pdb_get_acct_ctrl(sampass);
807 if (acct_ctrl & ACB_DISABLED) {
808 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
812 /* construct a null password (in case one is needed */
815 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
817 /* save pointers to passwords so we don't have to keep looking them up */
818 lanman_pw = pdb_get_lanman_passwd(sampass);
819 nt_pw = pdb_get_nt_passwd (sampass);
821 /* check for null passwords */
822 if (lanman_pw == NULL) {
823 if (!(acct_ctrl & ACB_PWNOTREQ)) {
824 DEBUG(0,("check_oem_password: no lanman password !\n"));
829 if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) {
830 if (!(acct_ctrl & ACB_PWNOTREQ)) {
831 DEBUG(0,("check_oem_password: no ntlm password !\n"));
837 * Call the hash function to get the new password.
839 SamOEMhash( lmdata, lanman_pw, 516);
842 * The length of the new password is in the last 4 bytes of
846 new_pw_len = IVAL(lmdata, 512);
847 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
848 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
854 * nt passwords are in unicode
856 pull_ucs2(NULL, new_passwd,
857 (const smb_ucs2_t *)&lmdata[512 - new_pw_len],
858 new_passwd_size, new_pw_len, 0);
860 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
861 new_passwd[new_pw_len] = 0;
865 * To ensure we got the correct new password, hash it and
866 * use it as a key to test the passed old password.
869 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
874 * Now use new_p16 as the key to see if the old
877 D_P16(new_p16, lmhash, unenc_old_pw);
879 if (memcmp(lanman_pw, unenc_old_pw, 16))
881 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
885 #ifdef DEBUG_PASSWORD
887 ("check_oem_password: password %s ok\n", new_passwd));
893 * Now use new_p16 as the key to see if the old
896 D_P16(new_ntp16, lmhash, unenc_old_pw);
897 D_P16(new_ntp16, nthash, unenc_old_ntpw);
899 if (memcmp(lanman_pw, unenc_old_pw, 16))
901 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
905 if (memcmp(nt_pw, unenc_old_ntpw, 16))
907 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
910 #ifdef DEBUG_PASSWORD
911 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
916 /***********************************************************
917 Code to change the oem password. Changes both the lanman
919 ************************************************************/
921 BOOL change_oem_password(SAM_ACCOUNT *hnd, char *new_passwd)
925 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
929 /* Now write it into the file. */
931 ret = pdb_update_sam_account (hnd);