2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) Andrew Bartlett 2001-2002
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 NTSTATUS 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 (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
173 DEBUG(3, ("Could not re-direct stdin\n"));
176 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
178 DEBUG(3, ("Could not re-direct stdout\n"));
181 if (sys_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;
201 stermios.c_oflag &= ~(ONLCR);
203 if (tcsetattr(0, TCSANOW, &stermios) < 0)
205 DEBUG(3, ("could not set attributes of pty\n"));
209 /* make us completely into the right uid */
212 become_user_permanently(uid, gid);
216 ("Invoking '%s' as password change program.\n",
219 /* execl() password-change application */
220 if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0)
222 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
228 static int expect(int master, char *issue, char *expected)
231 int attempts, timeout, nread, len;
234 for (attempts = 0; attempts < 2; attempts++) {
235 if (!strequal(issue, ".")) {
236 if (lp_passwd_chat_debug())
237 DEBUG(100, ("expect: sending [%s]\n", issue));
239 if ((len = write(master, issue, strlen(issue))) != strlen(issue)) {
240 DEBUG(2,("expect: (short) write returned %d\n", len ));
245 if (strequal(expected, "."))
252 while ((len = read_with_timeout(master, buffer + nread, 1,
253 sizeof(buffer) - nread - 1,
259 /* Eat leading/trailing whitespace before match. */
261 pstrcpy( str, buffer);
262 trim_string( str, " ", " ");
264 if ((match = (unix_wild_match(expected, str) == 0)))
269 if (lp_passwd_chat_debug())
270 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
271 expected, buffer, match ? "yes" : "no" ));
277 DEBUG(2, ("expect: %s\n", strerror(errno)));
282 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
286 static void pwd_sub(char *buf)
288 all_string_sub(buf, "\\n", "\n", 0);
289 all_string_sub(buf, "\\r", "\r", 0);
290 all_string_sub(buf, "\\s", " ", 0);
291 all_string_sub(buf, "\\t", "\t", 0);
294 static int talktochild(int master, const char *seq)
297 fstring issue, expected;
301 while (next_token(&seq, expected, NULL, sizeof(expected)))
306 if (!expect(master, issue, expected))
308 DEBUG(3, ("Response %d incorrect\n", count));
312 if (!next_token(&seq, issue, NULL, sizeof(issue)))
317 if (!strequal(issue, ".")) {
318 /* we have one final issue to send */
319 fstrcpy(expected, ".");
320 if (!expect(master, issue, expected))
327 static BOOL chat_with_program(char *passwordprogram, struct passwd *pass,
328 char *chatsequence, BOOL as_root)
339 ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
343 /* allocate a pseudo-terminal device */
344 if ((master = findpty(&slavedev)) < 0)
347 ("Cannot Allocate pty for password change: %s\n",
353 * We need to temporarily stop CatchChild from eating
354 * SIGCLD signals as it also eats the exit status code. JRA.
357 CatchChildLeaveStatus();
359 if ((pid = sys_fork()) < 0)
362 ("Cannot fork() child for password change: %s\n",
369 /* we now have a pty */
371 { /* This is the parent process */
372 if ((chstat = talktochild(master, chatsequence)) == False)
375 ("Child failed to change password: %s\n",
377 kill(pid, SIGKILL); /* be sure to end this process */
380 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0)
392 DEBUG(3, ("The process is no longer waiting!\n\n"));
399 * Go back to ignoring children.
408 ("We were waiting for the wrong process ID\n"));
411 if (WIFEXITED(wstat) == 0)
414 ("The process exited while we were waiting\n"));
417 if (WEXITSTATUS(wstat) != 0)
420 ("The status of the process exiting was %d\n",
431 * Lose any oplock capabilities.
433 oplock_set_capability(False, False);
435 /* make sure it doesn't freeze */
442 ("Dochild for user %s (uid=%d,gid=%d)\n", pass->pw_name,
443 (int)getuid(), (int)getgid()));
445 dochild(master, slavedev, pass, passwordprogram,
452 * The child should never return from dochild() ....
456 ("chat_with_program: Error: dochild() returned %d\n",
463 ("Password change %ssuccessful for user %s\n",
464 (chstat ? "" : "un"), pass->pw_name));
469 BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
471 pstring passwordprogram;
472 pstring chatsequence;
479 DEBUG(1, ("NULL username specfied to chgpasswd()!\n"));
486 DEBUG(3, ("Password change for user: %s\n", name));
489 DEBUG(100, ("Passwords: old=%s new=%s\n", oldpass, newpass));
492 /* Take the passed information and test it for minimum criteria */
493 /* Minimum password length */
494 if (strlen(newpass) < lp_min_passwd_length()) {
495 /* too short, must be at least MINPASSWDLENGTH */
496 DEBUG(0, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
497 name, lp_min_passwd_length()));
498 return (False); /* inform the user */
501 /* Password is same as old password */
502 if (strcmp(oldpass, newpass) == 0) {
503 /* don't allow same password */
504 DEBUG(2, ("Password Change: %s, New password is same as old\n", name)); /* log the attempt */
505 return (False); /* inform the user */
509 * Check the old and new passwords don't contain any control
513 len = strlen(oldpass);
514 for (i = 0; i < len; i++) {
515 if (iscntrl((int)oldpass[i])) {
517 ("chat_with_program: oldpass contains control characters (disallowed).\n"));
522 len = strlen(newpass);
523 for (i = 0; i < len; i++) {
524 if (iscntrl((int)newpass[i])) {
526 ("chat_with_program: newpass contains control characters (disallowed).\n"));
531 pass = Get_Pwnam(name);
534 if (lp_pam_password_change()) {
541 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
543 ret = smb_pam_passchange(name, oldpass, newpass);
553 /* A non-PAM password change just doen't make sense without a valid local user */
558 ("chgpasswd: user %s doesn't exist in the UNIX password database.\n",
563 pstrcpy(passwordprogram, lp_passwd_program());
564 pstrcpy(chatsequence, lp_passwd_chat());
566 if (!*chatsequence) {
567 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
571 if (!*passwordprogram) {
572 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
577 /* The password program *must* contain the user name to work. Fail if not. */
578 if (strstr(passwordprogram, "%u") == NULL) {
579 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
580 the string %%u, and the given string %s does not.\n", passwordprogram ));
585 pstring_sub(passwordprogram, "%u", name);
586 /* note that we do NOT substitute the %o and %n in the password program
587 as this would open up a security hole where the user could use
588 a new password containing shell escape characters */
590 pstring_sub(chatsequence, "%u", name);
591 all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring));
592 all_string_sub(chatsequence, "%n", newpass, sizeof(pstring));
593 return (chat_with_program
594 (passwordprogram, pass, chatsequence, as_root));
597 #else /* ALLOW_CHANGE_PASSWORD */
599 BOOL chgpasswd(const char *name, const char *oldpass, const char *newpass, BOOL as_root)
601 DEBUG(0, ("Password changing not compiled in (user=%s)\n", name));
604 #endif /* ALLOW_CHANGE_PASSWORD */
606 /***********************************************************
607 Code to check the lanman hashed password.
608 ************************************************************/
610 BOOL check_lanman_password(char *user, uchar * pass1,
611 uchar * pass2, SAM_ACCOUNT **hnd)
613 uchar unenc_new_pw[16];
614 uchar unenc_old_pw[16];
615 SAM_ACCOUNT *sampass = NULL;
617 const uint8 *lanman_pw;
621 ret = pdb_getsampwnam(sampass, user);
625 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
626 pdb_free_sam(&sampass);
630 acct_ctrl = pdb_get_acct_ctrl (sampass);
631 lanman_pw = pdb_get_lanman_passwd (sampass);
633 if (acct_ctrl & ACB_DISABLED) {
634 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
635 pdb_free_sam(&sampass);
639 if (lanman_pw == NULL) {
640 if (acct_ctrl & ACB_PWNOTREQ) {
641 /* this saves the pointer for the caller */
645 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
646 pdb_free_sam(&sampass);
651 /* Get the new lanman hash. */
652 D_P16(lanman_pw, pass2, unenc_new_pw);
654 /* Use this to get the old lanman hash. */
655 D_P16(unenc_new_pw, pass1, unenc_old_pw);
657 /* Check that the two old passwords match. */
658 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
659 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
660 pdb_free_sam(&sampass);
664 /* this saves the pointer for the caller */
669 /***********************************************************
670 Code to change the lanman hashed password.
671 It nulls out the NT hashed password as it will
673 ************************************************************/
675 BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar * pass1,
678 static uchar null_pw[16];
679 uchar unenc_new_pw[16];
684 if (sampass == NULL) {
685 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
689 acct_ctrl = pdb_get_acct_ctrl(sampass);
690 pwd = pdb_get_lanman_passwd(sampass);
692 if (acct_ctrl & ACB_DISABLED) {
693 DEBUG(0,("change_lanman_password: account %s disabled.\n",
694 pdb_get_username(sampass)));
699 if (acct_ctrl & ACB_PWNOTREQ) {
701 memset(no_pw, '\0', 14);
702 E_P16(no_pw, null_pw);
704 /* Get the new lanman hash. */
705 D_P16(null_pw, pass2, unenc_new_pw);
707 DEBUG(0,("change_lanman_password: no lanman password !\n"));
711 /* Get the new lanman hash. */
712 D_P16(pwd, pass2, unenc_new_pw);
715 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
719 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
720 return False; /* We lose the NT hash. Sorry. */
723 if (!pdb_set_pass_changed_now (sampass)) {
724 pdb_free_sam(&sampass);
725 /* Not quite sure what this one qualifies as, but this will do */
729 /* Now flush the sam_passwd struct to persistent storage */
731 ret = pdb_update_sam_account (sampass);
737 /***********************************************************
738 Code to check and change the OEM hashed password.
739 ************************************************************/
740 NTSTATUS pass_oem_change(char *user,
741 uchar * lmdata, uchar * lmhash,
742 uchar * ntdata, uchar * nthash)
745 const char *unix_user;
746 SAM_ACCOUNT *sampass = NULL;
748 = check_oem_password(user, lmdata, lmhash, ntdata, nthash,
749 &sampass, new_passwd, sizeof(new_passwd));
751 if (!NT_STATUS_IS_OK(nt_status))
755 * At this point we have the new case-sensitive plaintext
756 * password in the fstring new_passwd. If we wanted to synchronise
757 * with UNIX passwords we would call a UNIX password changing
758 * function here. However it would have to be done as root
759 * as the plaintext of the old users password is not
763 unix_user = pdb_get_username(sampass);
765 nt_status = change_oem_password(sampass, NULL, new_passwd);
767 memset(new_passwd, 0, sizeof(new_passwd));
769 pdb_free_sam(&sampass);
774 /***********************************************************
775 Code to check the OEM hashed password.
777 this function ignores the 516 byte nt OEM hashed password
778 but does use the lm OEM password to check the nt hashed-hash.
780 ************************************************************/
782 static NTSTATUS check_oem_password(const char *user,
783 uchar * lmdata, const uchar * lmhash,
784 const uchar * ntdata, const uchar * nthash,
785 SAM_ACCOUNT **hnd, char *new_passwd,
788 static uchar null_pw[16];
789 static uchar null_ntpw[16];
790 SAM_ACCOUNT *sampass = NULL;
791 const uint8 *lanman_pw, *nt_pw;
795 uchar unenc_old_ntpw[16];
797 uchar unenc_old_pw[16];
801 BOOL nt_pass_set = (ntdata != NULL && nthash != NULL);
803 pdb_init_sam(&sampass);
806 ret = pdb_getsampwnam(sampass, user);
810 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
811 pdb_free_sam(&sampass);
812 return NT_STATUS_WRONG_PASSWORD;
814 TODO: check what Win2k returns for this:
815 return NT_STATUS_NO_SUCH_USER;
821 acct_ctrl = pdb_get_acct_ctrl(sampass);
823 if (acct_ctrl & ACB_DISABLED) {
824 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
825 pdb_free_sam(&sampass);
826 return NT_STATUS_ACCOUNT_DISABLED;
829 /* construct a null password (in case one is needed */
832 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
834 /* save pointers to passwords so we don't have to keep looking them up */
835 lanman_pw = pdb_get_lanman_passwd(sampass);
836 nt_pw = pdb_get_nt_passwd(sampass);
838 /* check for null passwords */
839 if (lanman_pw == NULL) {
840 if (!(acct_ctrl & ACB_PWNOTREQ)) {
841 DEBUG(0,("check_oem_password: no lanman password !\n"));
842 pdb_free_sam(&sampass);
843 return NT_STATUS_WRONG_PASSWORD;
847 if (pdb_get_nt_passwd(sampass) == NULL && nt_pass_set) {
848 if (!(acct_ctrl & ACB_PWNOTREQ)) {
849 DEBUG(0,("check_oem_password: no ntlm password !\n"));
850 pdb_free_sam(&sampass);
851 return NT_STATUS_WRONG_PASSWORD;
856 * Call the hash function to get the new password.
858 SamOEMhash( lmdata, lanman_pw, 516);
861 * The length of the new password is in the last 4 bytes of
865 new_pw_len = IVAL(lmdata, 512);
866 if (new_pw_len < 0 || new_pw_len > new_passwd_size - 1) {
867 DEBUG(0,("check_oem_password: incorrect password length (%d).\n", new_pw_len));
868 pdb_free_sam(&sampass);
869 return NT_STATUS_WRONG_PASSWORD;
874 * nt passwords are in unicode
876 pull_ucs2(NULL, new_passwd,
877 (const smb_ucs2_t *)&lmdata[512 - new_pw_len],
878 new_passwd_size, new_pw_len, 0);
880 memcpy(new_passwd, &lmdata[512 - new_pw_len], new_pw_len);
881 new_passwd[new_pw_len] = 0;
885 * To ensure we got the correct new password, hash it and
886 * use it as a key to test the passed old password.
889 nt_lm_owf_gen(new_passwd, new_ntp16, new_p16);
894 * Now use new_p16 as the key to see if the old
897 D_P16(new_p16, lmhash, unenc_old_pw);
899 if (memcmp(lanman_pw, unenc_old_pw, 16))
901 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
902 pdb_free_sam(&sampass);
903 return NT_STATUS_WRONG_PASSWORD;
906 #ifdef DEBUG_PASSWORD
908 ("check_oem_password: password %s ok\n", new_passwd));
910 pdb_free_sam(&sampass);
915 * Now use new_p16 as the key to see if the old
918 D_P16(new_ntp16, lmhash, unenc_old_pw);
919 D_P16(new_ntp16, nthash, unenc_old_ntpw);
921 if (memcmp(lanman_pw, unenc_old_pw, 16))
923 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
924 pdb_free_sam(&sampass);
925 return NT_STATUS_WRONG_PASSWORD;
928 if (memcmp(nt_pw, unenc_old_ntpw, 16))
930 DEBUG(0,("check_oem_password: old nt password doesn't match.\n"));
931 pdb_free_sam(&sampass);
932 return NT_STATUS_WRONG_PASSWORD;
934 #ifdef DEBUG_PASSWORD
935 DEBUG(100, ("check_oem_password: password %s ok\n", new_passwd));
938 pdb_free_sam(&sampass);
942 /***********************************************************
943 Code to change the oem password. Changes both the lanman
944 and NT hashes. Old_passwd is almost always NULL.
945 ************************************************************/
947 NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd)
952 if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
953 DEBUG(1, ("user %s cannot change password now, must wait until %s\n",
954 pdb_get_username(hnd), http_timestring(pdb_get_pass_can_change_time(hnd))));
955 return NT_STATUS_PASSWORD_RESTRICTION;
958 if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (strlen(new_passwd) < min_len)) {
959 DEBUG(1, ("user %s cannot change password - password too short\n",
960 pdb_get_username(hnd)));
961 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
962 return NT_STATUS_PASSWORD_RESTRICTION;
963 /* return NT_STATUS_PWD_TOO_SHORT; */
966 /* Take the passed information and test it for minimum criteria */
967 /* Minimum password length */
968 if (strlen(new_passwd) < lp_min_passwd_length()) {
969 /* too short, must be at least MINPASSWDLENGTH */
970 DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n",
971 pdb_get_username(hnd), lp_min_passwd_length()));
972 return NT_STATUS_PASSWORD_RESTRICTION;
973 /* return NT_STATUS_PWD_TOO_SHORT; */
976 /* TODO: Add cracklib support here */
979 * If unix password sync was requested, attempt to change
980 * the /etc/passwd database first. Return failure if this cannot
983 * This occurs before the oem change, becouse we don't want to
984 * update it if chgpasswd failed.
986 * Conditional on lp_unix_password_sync() becouse we don't want
987 * to touch the unix db unless we have admin permission.
990 if(lp_unix_password_sync() && IS_SAM_UNIX_USER(hnd)
991 && !chgpasswd(pdb_get_username(hnd),
992 old_passwd, new_passwd, False)) {
993 return NT_STATUS_ACCESS_DENIED;
996 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
997 return NT_STATUS_ACCESS_DENIED;
1000 /* Now write it into the file. */
1002 ret = pdb_update_sam_account (hnd);
1006 return NT_STATUS_ACCESS_DENIED;
1009 return NT_STATUS_OK;