2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright (C) Andrew Tridgell 1992-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 /* fork a child process to exec passwd and write to its
22 * tty to change a users password. This is running as the
23 * user who is attempting to change the password.
27 * This code was copied/borrowed and stolen from various sources.
28 * The primary source was the poppasswd.c from the authors of POPMail. This software
29 * was included as a client to change passwords using the 'passwd' program
30 * on the remote machine.
32 * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE
33 * is defined in the compiler directives located in the Makefile.
35 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
36 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
37 * and rights to modify, distribute or incorporate this change to the CAP suite or
38 * using it for any other reason are granted, so long as this disclaimer is left intact.
42 This code was hacked considerably for inclusion in Samba, primarily
43 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
44 of the "password chat" option, which allows the easy runtime
45 specification of the expected sequence of events to change a
51 extern struct passdb_ops pdb_ops;
53 static BOOL check_oem_password(const char *user,
54 uchar * lmdata, const uchar * lmhash,
55 const uchar * ntdata, const uchar * nthash,
56 SAM_ACCOUNT **hnd, char *new_passwd,
59 #if ALLOW_CHANGE_PASSWORD
61 static int findpty(char **slave)
68 #if defined(HAVE_GRANTPT)
69 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
70 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0)
74 *slave = (char *)ptsname(master);
78 ("findpty: Unable to create master/slave pty pair.\n"));
79 /* Stop fd leak on error. */
86 ("findpty: Allocated slave pty %s\n", *slave));
90 #endif /* HAVE_GRANTPT */
92 fstrcpy(line, "/dev/ptyXX");
94 dirp = opendir("/dev");
97 while ((dpname = readdirname(dirp)) != NULL)
99 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5)
102 ("pty: try to open %s, line was %s\n", dpname,
106 if ((master = sys_open(line, O_RDWR, 0)) >= 0)
108 DEBUG(3, ("pty: opened %s\n", line));
120 static int dochild(int master, const char *slavedev, const struct passwd *pass,
121 const char *passwordprogram, BOOL as_root)
124 struct termios stermios;
131 ("dochild: user doesn't exist in the UNIX password database.\n"));
138 gain_root_privilege();
140 /* Start new session - gets rid of controlling terminal. */
144 ("Weirdness, couldn't let go of controlling terminal\n"));
148 /* Open slave pty and acquire as new controlling terminal. */
149 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
151 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
155 ioctl(slave, I_PUSH, "ptem");
156 ioctl(slave, I_PUSH, "ldterm");
157 #elif defined(TIOCSCTTY)
158 if (ioctl(slave, TIOCSCTTY, 0) < 0)
160 DEBUG(3, ("Error in ioctl call for slave pty\n"));
168 /* Make slave stdin/out/err of child. */
170 if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
172 DEBUG(3, ("Could not re-direct stdin\n"));
175 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
177 DEBUG(3, ("Could not re-direct stdout\n"));
180 if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
182 DEBUG(3, ("Could not re-direct stderr\n"));
188 /* Set proper terminal attributes - no echo, canonical input processing,
189 no map NL to CR/NL on output. */
191 if (tcgetattr(0, &stermios) < 0)
194 ("could not read default terminal attributes on pty\n"));
197 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
198 stermios.c_lflag |= ICANON;
199 stermios.c_oflag &= ~(ONLCR);
200 if (tcsetattr(0, TCSANOW, &stermios) < 0)
202 DEBUG(3, ("could not set attributes of pty\n"));
206 /* make us completely into the right uid */
209 become_user_permanently(uid, gid);
213 ("Invoking '%s' as password change program.\n",
216 /* execl() password-change application */
217 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
219 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
225 static int expect(int master, char *issue, char *expected)
228 int attempts, timeout, nread, len;
231 for (attempts = 0; attempts < 2; attempts++) {
232 if (!strequal(issue, ".")) {
233 if (lp_passwd_chat_debug())
234 DEBUG(100, ("expect: sending [%s]\n", issue));
236 if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
237 DEBUG(2,("expect: (short) write returned %d\n", len ));
242 if (strequal(expected, "."))
249 while ((len = read_with_timeout(master, buffer + nread, 1,
250 sizeof(buffer) - nread - 1,
256 /* Eat leading/trailing whitespace before match. */
258 pstrcpy( str, buffer);
259 trim_string( str, " ", " ");
261 if ((match = (unix_wild_match(expected, str) == 0)))
266 if (lp_passwd_chat_debug())
267 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
268 expected, buffer, match ? "yes" : "no" ));
274 DEBUG(2, ("expect: %s\n", strerror(errno)));
279 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
283 static void pwd_sub(char *buf)
285 all_string_sub(buf, "\\n", "\n", 0);
286 all_string_sub(buf, "\\r", "\r", 0);
287 all_string_sub(buf, "\\s", " ", 0);
288 all_string_sub(buf, "\\t", "\t", 0);
291 static int talktochild(int master, char *seq)
294 fstring issue, expected;
298 while (next_token(&seq, expected, NULL, sizeof(expected)))
303 if (!expect(master, issue, expected))
305 DEBUG(3, ("Response %d incorrect\n", count));
309 if (!next_token(&seq, issue, NULL, sizeof(issue)))
314 if (!strequal(issue, ".")) {
315 /* we have one final issue to send */
316 fstrcpy(expected, ".");
317 if (!expect(master, issue, expected))
324 static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
325 char *chatsequence, BOOL as_root)
336 ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
340 /* allocate a pseudo-terminal device */
341 if ((master = findpty(&slavedev)) < 0)
344 ("Cannot Allocate pty for password change: %s\n",
350 * We need to temporarily stop CatchChild from eating
351 * SIGCLD signals as it also eats the exit status code. JRA.
354 CatchChildLeaveStatus();
356 if ((pid = sys_fork()) < 0)
359 ("Cannot fork() child for password change: %s\n",
366 /* we now have a pty */
368 { /* This is the parent process */
369 if ((chstat = talktochild(master, chatsequence)) == False)
372 ("Child failed to change password: %s\n",
374 kill(pid, SIGKILL); /* be sure to end this process */
377 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0)
389 DEBUG(3, ("The process is no longer waiting!\n\n"));
396 * Go back to ignoring children.
405 ("We were waiting for the wrong process ID\n"));
408 if (WIFEXITED(wstat) == 0)
411 ("The process exited while we were waiting\n"));
414 if (WEXITSTATUS(wstat) != 0)
417 ("The status of the process exiting was %d\n",
428 * Lose any oplock capabilities.
430 oplock_set_capability(False, False);
432 /* make sure it doesn't freeze */
439 ("Dochild for user %s (uid=%d,gid=%d)\n", pass->pw_name,
440 (int)getuid(), (int)getgid()));
442 dochild(master, slavedev, pass, passwordprogram,
449 * The child should never return from dochild() ....
453 ("chat_with_program: Error: dochild() returned %d\n",
460 ("Password change %ssuccessful for user %s\n",
461 (chstat ? "" : "un"), pass->pw_name));
466 BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
468 pstring passwordprogram;
469 pstring chatsequence;
476 DEBUG(1, ("NULL username specfied to chgpasswd()!\n"));
479 DEBUG(3, ("Password change for user: %s\n", name));
482 DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass));
485 /* Take the passed information and test it for minimum criteria */
486 /* Minimum password length */
487 if (strlen(newpass) < lp_min_passwd_length()) {
488 /* too short, must be at least MINPASSWDLENGTH */
489 DEBUG(0, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
490 name, lp_min_passwd_length()));
491 return (False); /* inform the user */
494 /* Password is same as old password */
495 if (strcmp(oldpass, newpass) == 0) {
496 /* don't allow same password */
497 DEBUG(2, ("Password Change: %s, New password is same as old\n", name)); /* log the attempt */
498 return (False); /* inform the user */
502 * Check the old and new passwords don't contain any control
506 len = strlen(oldpass);
507 for (i = 0; i < len; i++) {
508 if (iscntrl((int)oldpass[i])) {
510 ("chat_with_program: oldpass contains control characters (disallowed).\n"));
515 len = strlen(newpass);
516 for (i = 0; i < len; i++) {
517 if (iscntrl((int)newpass[i])) {
519 ("chat_with_program: newpass contains control characters (disallowed).\n"));
524 pass = Get_Pwnam(name);
527 if (lp_pam_password_change()) {
534 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
536 ret = smb_pam_passchange(name, oldpass, newpass);
546 /* A non-PAM password change just doen't make sense without a valid local user */
551 ("chgpasswd: user %s doesn't exist in the UNIX password database.\n",
556 pstrcpy(passwordprogram, lp_passwd_program());
557 pstrcpy(chatsequence, lp_passwd_chat());
559 if (!*chatsequence) {
560 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
564 if (!*passwordprogram) {
565 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
570 /* The password program *must* contain the user name to work. Fail if not. */
571 if (strstr(passwordprogram, "%u") == NULL) {
572 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
573 the string %%u, and the given string %s does not.\n", passwordprogram ));
578 pstring_sub(passwordprogram, "%u", name);
579 /* note that we do NOT substitute the %o and %n in the password program
580 as this would open up a security hole where the user could use
581 a new password containing shell escape characters */
583 pstring_sub(chatsequence, "%u", name);
584 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
585 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
586 return (chat_with_program
587 (passwordprogram, pass, chatsequence, as_root));
590 #else /* ALLOW_CHANGE_PASSWORD */
592 BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
594 DEBUG(0, ("Password changing not compiled in (user=%s)\n", name));
597 #endif /* ALLOW_CHANGE_PASSWORD */
599 /***********************************************************
600 Code to check the lanman hashed password.
601 ************************************************************/
603 BOOL check_lanman_password(char *user, uchar * pass1,
604 uchar * pass2, SAM_ACCOUNT **hnd)
606 uchar unenc_new_pw[16];
607 uchar unenc_old_pw[16];
608 SAM_ACCOUNT *sampass = NULL;
610 const uint8 *lanman_pw;
614 ret = pdb_getsampwnam(sampass, user);
618 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
619 pdb_free_sam(&sampass);
623 acct_ctrl = pdb_get_acct_ctrl (sampass);
624 lanman_pw = pdb_get_lanman_passwd (sampass);
626 if (acct_ctrl & ACB_DISABLED) {
627 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
628 pdb_free_sam(&sampass);
632 if (lanman_pw == NULL) {
633 if (acct_ctrl & ACB_PWNOTREQ) {
634 /* this saves the pointer for the caller */
638 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
639 pdb_free_sam(&sampass);
644 /* Get the new lanman hash. */
645 D_P16(lanman_pw, pass2, unenc_new_pw);
647 /* Use this to get the old lanman hash. */
648 D_P16(unenc_new_pw, pass1, unenc_old_pw);
650 /* Check that the two old passwords match. */
651 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
652 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
653 pdb_free_sam(&sampass);
657 /* this saves the pointer for the caller */
662 /***********************************************************
663 Code to change the lanman hashed password.
664 It nulls out the NT hashed password as it will
666 ************************************************************/
668 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar * pass1,
671 static uchar null_pw[16];
672 uchar unenc_new_pw[16];
677 if (sampass == NULL) {
678 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
682 acct_ctrl = pdb_get_acct_ctrl(sampass);
683 pwd = pdb_get_lanman_passwd(sampass);
685 if (acct_ctrl & ACB_DISABLED) {
686 DEBUG(0,("change_lanman_password: account %s disabled.\n",
687 pdb_get_username(sampass)));
692 if (acct_ctrl & ACB_PWNOTREQ) {
694 memset(no_pw, '\0', 14);
695 E_P16(no_pw, null_pw);
697 /* Get the new lanman hash. */
698 D_P16(null_pw, pass2, unenc_new_pw);
700 DEBUG(0,("change_lanman_password: no lanman password !\n"));
704 /* Get the new lanman hash. */
705 D_P16(pwd, pass2, unenc_new_pw);
708 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw)) {
712 if (!pdb_set_nt_passwd (sampass, NULL)) {
713 return False; /* We lose the NT hash. Sorry. */
716 if (!pdb_set_pass_changed_now (sampass)) {
717 pdb_free_sam(&sampass);
718 /* Not quite sure what this one qualifies as, but this will do */
722 /* Now flush the sam_passwd struct to persistent storage */
724 ret = pdb_update_sam_account (sampass);
730 /***********************************************************
731 Code to check and change the OEM hashed password.
732 ************************************************************/
733 BOOL pass_oem_change(char *user,
734 uchar * lmdata, uchar * lmhash,
735 uchar * ntdata, uchar * nthash)
738 const char *unix_user;
739 SAM_ACCOUNT *sampass = NULL;
740 BOOL ret = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
741 &sampass, new_passwd, sizeof(new_passwd));
744 * At this point we have the new case-sensitive plaintext
745 * password in the fstring new_passwd. If we wanted to synchronise
746 * with UNIX passwords we would call a UNIX password changing
747 * function here. However it would have to be done as root
748 * as the plaintext of the old users password is not
752 unix_user = pdb_get_username(sampass);
754 if ((ret) && (unix_user) && (*unix_user) && lp_unix_password_sync())
755 ret = chgpasswd(unix_user, "", new_passwd, True);
758 ret = change_oem_password(sampass, new_passwd);
760 memset(new_passwd, 0, sizeof(new_passwd));
762 pdb_free_sam(&sampass);
767 /***********************************************************
768 Code to check the OEM hashed password.
770 this function ignores the 516 byte nt OEM hashed password
771 but does use the lm OEM password to check the nt hashed-hash.
773 ************************************************************/
774 static BOOL check_oem_password(const char *user,
775 uchar * lmdata, const uchar * lmhash,
776 const uchar * ntdata, const uchar * nthash,
777 SAM_ACCOUNT **hnd, char *new_passwd,
780 static uchar null_pw[16];
781 static uchar null_ntpw[16];
782 SAM_ACCOUNT *sampass = NULL;
783 const uint8 *lanman_pw, *nt_pw;
787 uchar unenc_old_ntpw[16];
789 uchar unenc_old_pw[16];
793 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
795 pdb_init_sam(&sampass);
798 ret = pdb_getsampwnam(sampass, user);
802 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
808 acct_ctrl = pdb_get_acct_ctrl(sampass);
810 if (acct_ctrl & ACB_DISABLED) {
811 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
815 /* construct a null password (in case one is needed */
818 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
820 /* save pointers to passwords so we don't have to keep looking them up */
821 lanman_pw = pdb_get_lanman_passwd(sampass);
822 nt_pw = pdb_get_nt_passwd (sampass);
824 /* check for null passwords */
825 if (lanman_pw == NULL) {
826 if (!(acct_ctrl & ACB_PWNOTREQ)) {
827 DEBUG(0,("check_oem_password: no lanman password !\n"));
832 if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) {
833 if (!(acct_ctrl & ACB_PWNOTREQ)) {
834 DEBUG(0,("check_oem_password: no ntlm password !\n"));
840 * Call the hash function to get the new password.
842 SamOEMhash( lmdata, lanman_pw, 516);
845 * The length of the new password is in the last 4 bytes of
849 new_pw_len = IVAL(lmdata, 512);
850 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
851 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
857 * nt passwords are in unicode
859 pull_ucs2(NULL, new_passwd,
860 (const smb_ucs2_t *)&lmdata[512 - new_pw_len],
861 new_passwd_size, new_pw_len, 0);
863 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
864 new_passwd[new_pw_len] = 0;
868 * To ensure we got the correct new password, hash it and
869 * use it as a key to test the passed old password.
872 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
877 * Now use new_p16 as the key to see if the old
880 D_P16(new_p16, lmhash, unenc_old_pw);
882 if (memcmp(lanman_pw, unenc_old_pw, 16))
884 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
888 #ifdef DEBUG_PASSWORD
890 ("check_oem_password: password %s ok\n", new_passwd));
896 * Now use new_p16 as the key to see if the old
899 D_P16(new_ntp16, lmhash, unenc_old_pw);
900 D_P16(new_ntp16, nthash, unenc_old_ntpw);
902 if (memcmp(lanman_pw, unenc_old_pw, 16))
904 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
908 if (memcmp(nt_pw, unenc_old_ntpw, 16))
910 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
913 #ifdef DEBUG_PASSWORD
914 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
919 /***********************************************************
920 Code to change the oem password. Changes both the lanman
922 ************************************************************/
924 BOOL change_oem_password(SAM_ACCOUNT *hnd, char *new_passwd)
928 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
932 /* Now write it into the file. */
934 ret = pdb_update_sam_account (hnd);