ntlm_auth: Fix another typo in the test.
[Samba.git] / source / smbd / chgpasswd.c
blob668c8e20957ac9f991c19cffa7c63d382d7b4d36
1 /*
2 Unix SMB/CIFS implementation.
3 Samba utility functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) Andrew Bartlett 2001-2004
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 3 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, see <http://www.gnu.org/licenses/>.
21 /* These comments regard the code to change the user's unix password: */
23 /* fork a child process to exec passwd and write to its
24 * tty to change a users password. This is running as the
25 * user who is attempting to change the password.
29 * This code was copied/borrowed and stolen from various sources.
30 * The primary source was the poppasswd.c from the authors of POPMail. This software
31 * was included as a client to change passwords using the 'passwd' program
32 * on the remote machine.
34 * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson
35 * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences
36 * and rights to modify, distribute or incorporate this change to the CAP suite or
37 * using it for any other reason are granted, so long as this disclaimer is left intact.
41 This code was hacked considerably for inclusion in Samba, primarily
42 by Andrew.Tridgell@anu.edu.au. The biggest change was the addition
43 of the "password chat" option, which allows the easy runtime
44 specification of the expected sequence of events to change a
45 password.
48 #include "includes.h"
50 extern struct passdb_ops pdb_ops;
52 static NTSTATUS check_oem_password(const char *user,
53 uchar password_encrypted_with_lm_hash[516],
54 const uchar old_lm_hash_encrypted[16],
55 uchar password_encrypted_with_nt_hash[516],
56 const uchar old_nt_hash_encrypted[16],
57 struct samu **hnd,
58 char **pp_new_passwd);
60 #if ALLOW_CHANGE_PASSWORD
62 static int findpty(char **slave)
64 int master = -1;
65 char *line = NULL;
66 SMB_STRUCT_DIR *dirp = NULL;
67 const char *dpname;
69 *slave = NULL;
71 #if defined(HAVE_GRANTPT)
72 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
73 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) {
74 grantpt(master);
75 unlockpt(master);
76 line = (char *)ptsname(master);
77 if (line) {
78 *slave = SMB_STRDUP(line);
81 if (*slave == NULL) {
82 DEBUG(0,
83 ("findpty: Unable to create master/slave pty pair.\n"));
84 /* Stop fd leak on error. */
85 close(master);
86 return -1;
87 } else {
88 DEBUG(10,
89 ("findpty: Allocated slave pty %s\n", *slave));
90 return (master);
93 #endif /* HAVE_GRANTPT */
95 line = SMB_STRDUP("/dev/ptyXX");
96 if (!line) {
97 return (-1);
100 dirp = sys_opendir("/dev");
101 if (!dirp) {
102 SAFE_FREE(line);
103 return (-1);
106 while ((dpname = readdirname(dirp)) != NULL) {
107 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
108 DEBUG(3,
109 ("pty: try to open %s, line was %s\n", dpname,
110 line));
111 line[8] = dpname[3];
112 line[9] = dpname[4];
113 if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
114 DEBUG(3, ("pty: opened %s\n", line));
115 line[5] = 't';
116 *slave = line;
117 sys_closedir(dirp);
118 return (master);
122 sys_closedir(dirp);
123 SAFE_FREE(line);
124 return (-1);
127 static int dochild(int master, const char *slavedev, const struct passwd *pass,
128 const char *passwordprogram, bool as_root)
130 int slave;
131 struct termios stermios;
132 gid_t gid;
133 uid_t uid;
134 char * const eptrs[1] = { NULL };
136 if (pass == NULL)
138 DEBUG(0,
139 ("dochild: user doesn't exist in the UNIX password database.\n"));
140 return False;
143 gid = pass->pw_gid;
144 uid = pass->pw_uid;
146 gain_root_privilege();
148 /* Start new session - gets rid of controlling terminal. */
149 if (setsid() < 0)
151 DEBUG(3,
152 ("Weirdness, couldn't let go of controlling terminal\n"));
153 return (False);
156 /* Open slave pty and acquire as new controlling terminal. */
157 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
159 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
160 return (False);
162 #if defined(TIOCSCTTY)
163 if (ioctl(slave, TIOCSCTTY, 0) < 0)
165 DEBUG(3, ("Error in ioctl call for slave pty\n"));
166 /* return(False); */
168 #elif defined(I_PUSH) && defined(I_FIND)
169 if (ioctl(slave, I_FIND, "ptem") == 0) {
170 ioctl(slave, I_PUSH, "ptem");
172 if (ioctl(slave, I_FIND, "ldterm") == 0) {
173 ioctl(slave, I_PUSH, "ldterm");
175 #endif
177 /* Close master. */
178 close(master);
180 /* Make slave stdin/out/err of child. */
182 if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO)
184 DEBUG(3, ("Could not re-direct stdin\n"));
185 return (False);
187 if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
189 DEBUG(3, ("Could not re-direct stdout\n"));
190 return (False);
192 if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO)
194 DEBUG(3, ("Could not re-direct stderr\n"));
195 return (False);
197 if (slave > 2)
198 close(slave);
200 /* Set proper terminal attributes - no echo, canonical input processing,
201 no map NL to CR/NL on output. */
203 if (tcgetattr(0, &stermios) < 0)
205 DEBUG(3,
206 ("could not read default terminal attributes on pty\n"));
207 return (False);
209 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
210 stermios.c_lflag |= ICANON;
211 #ifdef ONLCR
212 stermios.c_oflag &= ~(ONLCR);
213 #endif
214 if (tcsetattr(0, TCSANOW, &stermios) < 0)
216 DEBUG(3, ("could not set attributes of pty\n"));
217 return (False);
220 /* make us completely into the right uid */
221 if (!as_root)
223 become_user_permanently(uid, gid);
226 DEBUG(10,
227 ("Invoking '%s' as password change program.\n",
228 passwordprogram));
230 /* execl() password-change application */
231 if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
233 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
234 return (False);
236 return (True);
239 static int expect(int master, char *issue, char *expected)
241 char buffer[1024];
242 int attempts, timeout, nread, len;
243 bool match = False;
245 for (attempts = 0; attempts < 2; attempts++) {
246 if (!strequal(issue, ".")) {
247 if (lp_passwd_chat_debug())
248 DEBUG(100, ("expect: sending [%s]\n", issue));
250 if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
251 DEBUG(2,("expect: (short) write returned %d\n", len ));
252 return False;
256 if (strequal(expected, "."))
257 return True;
259 /* Initial timeout. */
260 timeout = lp_passwd_chat_timeout() * 1000;
261 nread = 0;
262 buffer[nread] = 0;
264 while ((len = read_socket_with_timeout(master, buffer + nread, 1,
265 sizeof(buffer) - nread - 1,
266 timeout, NULL)) > 0) {
267 nread += len;
268 buffer[nread] = 0;
271 /* Eat leading/trailing whitespace before match. */
272 char *str = SMB_STRDUP(buffer);
273 if (!str) {
274 DEBUG(2,("expect: ENOMEM\n"));
275 return False;
277 trim_char(str, ' ', ' ');
279 if ((match = unix_wild_match(expected, str)) == True) {
280 /* Now data has started to return, lower timeout. */
281 timeout = lp_passwd_chat_timeout() * 100;
283 SAFE_FREE(str);
287 if (lp_passwd_chat_debug())
288 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
289 expected, buffer, match ? "yes" : "no" ));
291 if (match)
292 break;
294 if (len < 0) {
295 DEBUG(2, ("expect: %s\n", strerror(errno)));
296 return False;
300 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
301 return match;
304 static void pwd_sub(char *buf)
306 all_string_sub(buf, "\\n", "\n", 0);
307 all_string_sub(buf, "\\r", "\r", 0);
308 all_string_sub(buf, "\\s", " ", 0);
309 all_string_sub(buf, "\\t", "\t", 0);
312 static int talktochild(int master, const char *seq)
314 TALLOC_CTX *frame = talloc_stackframe();
315 int count = 0;
316 char *issue;
317 char *expected;
319 issue = talloc_strdup(frame, ".");
320 if (!issue) {
321 TALLOC_FREE(frame);
322 return false;
325 while (next_token_talloc(frame, &seq, &expected, NULL)) {
326 pwd_sub(expected);
327 count++;
329 if (!expect(master, issue, expected)) {
330 DEBUG(3, ("Response %d incorrect\n", count));
331 TALLOC_FREE(frame);
332 return false;
335 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
336 issue = talloc_strdup(frame, ".");
337 if (!issue) {
338 TALLOC_FREE(frame);
339 return false;
342 pwd_sub(issue);
345 if (!strequal(issue, ".")) {
346 /* we have one final issue to send */
347 expected = talloc_strdup(frame, ".");
348 if (!expected) {
349 TALLOC_FREE(frame);
350 return false;
352 if (!expect(master, issue, expected)) {
353 TALLOC_FREE(frame);
354 return False;
357 TALLOC_FREE(frame);
358 return (count > 0);
361 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
362 char *chatsequence, bool as_root)
364 char *slavedev = NULL;
365 int master;
366 pid_t pid, wpid;
367 int wstat;
368 bool chstat = False;
370 if (pass == NULL) {
371 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
372 return False;
375 /* allocate a pseudo-terminal device */
376 if ((master = findpty(&slavedev)) < 0) {
377 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
378 return (False);
382 * We need to temporarily stop CatchChild from eating
383 * SIGCLD signals as it also eats the exit status code. JRA.
386 CatchChildLeaveStatus();
388 if ((pid = sys_fork()) < 0) {
389 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
390 SAFE_FREE(slavedev);
391 close(master);
392 CatchChild();
393 return (False);
396 /* we now have a pty */
397 if (pid > 0) { /* This is the parent process */
398 /* Don't need this anymore in parent. */
399 SAFE_FREE(slavedev);
401 if ((chstat = talktochild(master, chatsequence)) == False) {
402 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
403 kill(pid, SIGKILL); /* be sure to end this process */
406 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
407 if (errno == EINTR) {
408 errno = 0;
409 continue;
411 break;
414 if (wpid < 0) {
415 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
416 close(master);
417 CatchChild();
418 return (False);
422 * Go back to ignoring children.
424 CatchChild();
426 close(master);
428 if (pid != wpid) {
429 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
430 return (False);
432 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
433 DEBUG(3, ("chat_with_program: The process exited with status %d \
434 while we were waiting\n", WEXITSTATUS(wstat)));
435 return (False);
437 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
438 else if (WIFSIGNALLED(wstat)) {
439 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
440 while we were waiting\n", WTERMSIG(wstat)));
441 return (False);
443 #endif
444 } else {
445 /* CHILD */
448 * Lose any elevated privileges.
450 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
451 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
453 /* make sure it doesn't freeze */
454 alarm(20);
456 if (as_root)
457 become_root();
459 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
460 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
461 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
463 if (as_root)
464 unbecome_root();
467 * The child should never return from dochild() ....
470 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
471 exit(1);
474 if (chstat)
475 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
476 (chstat ? "" : "un"), pass->pw_name));
477 return (chstat);
480 bool chgpasswd(const char *name, const struct passwd *pass,
481 const char *oldpass, const char *newpass, bool as_root)
483 char *passwordprogram = NULL;
484 char *chatsequence = NULL;
485 size_t i;
486 size_t len;
487 TALLOC_CTX *ctx = talloc_tos();
489 if (!oldpass) {
490 oldpass = "";
493 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
495 #ifdef DEBUG_PASSWORD
496 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
497 #endif
499 /* Take the passed information and test it for minimum criteria */
501 /* Password is same as old password */
502 if (strcmp(oldpass, newpass) == 0) {
503 /* don't allow same password */
504 DEBUG(2, ("chgpasswd: 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
510 * characters.
513 len = strlen(oldpass);
514 for (i = 0; i < len; i++) {
515 if (iscntrl((int)oldpass[i])) {
516 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
517 return False;
521 len = strlen(newpass);
522 for (i = 0; i < len; i++) {
523 if (iscntrl((int)newpass[i])) {
524 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
525 return False;
529 #ifdef WITH_PAM
530 if (lp_pam_password_change()) {
531 bool ret;
532 #ifdef HAVE_SETLOCALE
533 const char *prevlocale = setlocale(LC_ALL, "C");
534 #endif
536 if (as_root)
537 become_root();
539 if (pass) {
540 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
541 } else {
542 ret = smb_pam_passchange(name, oldpass, newpass);
545 if (as_root)
546 unbecome_root();
548 #ifdef HAVE_SETLOCALE
549 setlocale(LC_ALL, prevlocale);
550 #endif
552 return ret;
554 #endif
556 /* A non-PAM password change just doen't make sense without a valid local user */
558 if (pass == NULL) {
559 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
560 return false;
563 passwordprogram = talloc_strdup(ctx, lp_passwd_program());
564 if (!passwordprogram || !*passwordprogram) {
565 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
566 return false;
568 chatsequence = talloc_strdup(ctx, lp_passwd_chat());
569 if (!chatsequence || !*chatsequence) {
570 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
571 return false;
574 if (as_root) {
575 /* The password program *must* contain the user name to work. Fail if not. */
576 if (strstr_m(passwordprogram, "%u") == NULL) {
577 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
578 the string %%u, and the given string %s does not.\n", passwordprogram ));
579 return false;
583 passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
584 if (!passwordprogram) {
585 return false;
588 /* note that we do NOT substitute the %o and %n in the password program
589 as this would open up a security hole where the user could use
590 a new password containing shell escape characters */
592 chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
593 if (!chatsequence) {
594 return false;
596 chatsequence = talloc_all_string_sub(ctx,
597 chatsequence,
598 "%o",
599 oldpass);
600 if (!chatsequence) {
601 return false;
603 chatsequence = talloc_all_string_sub(ctx,
604 chatsequence,
605 "%n",
606 newpass);
607 return chat_with_program(passwordprogram,
608 pass,
609 chatsequence,
610 as_root);
613 #else /* ALLOW_CHANGE_PASSWORD */
615 bool chgpasswd(const char *name, const struct passwd *pass,
616 const char *oldpass, const char *newpass, bool as_root)
618 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
619 return (False);
621 #endif /* ALLOW_CHANGE_PASSWORD */
623 /***********************************************************
624 Code to check the lanman hashed password.
625 ************************************************************/
627 bool check_lanman_password(char *user, uchar * pass1,
628 uchar * pass2, struct samu **hnd)
630 uchar unenc_new_pw[16];
631 uchar unenc_old_pw[16];
632 struct samu *sampass = NULL;
633 uint32 acct_ctrl;
634 const uint8 *lanman_pw;
635 bool ret;
637 if ( !(sampass = samu_new(NULL)) ) {
638 DEBUG(0, ("samu_new() failed!\n"));
639 return False;
642 become_root();
643 ret = pdb_getsampwnam(sampass, user);
644 unbecome_root();
646 if (ret == False) {
647 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
648 TALLOC_FREE(sampass);
649 return False;
652 acct_ctrl = pdb_get_acct_ctrl (sampass);
653 lanman_pw = pdb_get_lanman_passwd (sampass);
655 if (acct_ctrl & ACB_DISABLED) {
656 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
657 TALLOC_FREE(sampass);
658 return False;
661 if (lanman_pw == NULL) {
662 if (acct_ctrl & ACB_PWNOTREQ) {
663 /* this saves the pointer for the caller */
664 *hnd = sampass;
665 return True;
666 } else {
667 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
668 TALLOC_FREE(sampass);
669 return False;
673 /* Get the new lanman hash. */
674 D_P16(lanman_pw, pass2, unenc_new_pw);
676 /* Use this to get the old lanman hash. */
677 D_P16(unenc_new_pw, pass1, unenc_old_pw);
679 /* Check that the two old passwords match. */
680 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
681 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
682 TALLOC_FREE(sampass);
683 return False;
686 /* this saves the pointer for the caller */
687 *hnd = sampass;
688 return True;
691 /***********************************************************
692 Code to change the lanman hashed password.
693 It nulls out the NT hashed password as it will
694 no longer be valid.
695 NOTE this function is designed to be called as root. Check the old password
696 is correct before calling. JRA.
697 ************************************************************/
699 bool change_lanman_password(struct samu *sampass, uchar *pass2)
701 static uchar null_pw[16];
702 uchar unenc_new_pw[16];
703 bool ret;
704 uint32 acct_ctrl;
705 const uint8 *pwd;
707 if (sampass == NULL) {
708 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
709 return False;
712 acct_ctrl = pdb_get_acct_ctrl(sampass);
713 pwd = pdb_get_lanman_passwd(sampass);
715 if (acct_ctrl & ACB_DISABLED) {
716 DEBUG(0,("change_lanman_password: account %s disabled.\n",
717 pdb_get_username(sampass)));
718 return False;
721 if (pwd == NULL) {
722 if (acct_ctrl & ACB_PWNOTREQ) {
723 uchar no_pw[14];
724 memset(no_pw, '\0', 14);
725 E_P16(no_pw, null_pw);
727 /* Get the new lanman hash. */
728 D_P16(null_pw, pass2, unenc_new_pw);
729 } else {
730 DEBUG(0,("change_lanman_password: no lanman password !\n"));
731 return False;
733 } else {
734 /* Get the new lanman hash. */
735 D_P16(pwd, pass2, unenc_new_pw);
738 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
739 return False;
742 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
743 return False; /* We lose the NT hash. Sorry. */
746 if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED)) {
747 TALLOC_FREE(sampass);
748 /* Not quite sure what this one qualifies as, but this will do */
749 return False;
752 /* Now flush the sam_passwd struct to persistent storage */
753 ret = NT_STATUS_IS_OK(pdb_update_sam_account (sampass));
755 return ret;
758 /***********************************************************
759 Code to check and change the OEM hashed password.
760 ************************************************************/
762 NTSTATUS pass_oem_change(char *user,
763 uchar password_encrypted_with_lm_hash[516],
764 const uchar old_lm_hash_encrypted[16],
765 uchar password_encrypted_with_nt_hash[516],
766 const uchar old_nt_hash_encrypted[16],
767 uint32 *reject_reason)
769 char *new_passwd = NULL;
770 struct samu *sampass = NULL;
771 NTSTATUS nt_status = check_oem_password(user,
772 password_encrypted_with_lm_hash,
773 old_lm_hash_encrypted,
774 password_encrypted_with_nt_hash,
775 old_nt_hash_encrypted,
776 &sampass,
777 &new_passwd);
779 if (!NT_STATUS_IS_OK(nt_status)) {
780 return nt_status;
783 /* We've already checked the old password here.... */
784 become_root();
785 nt_status = change_oem_password(sampass, NULL, new_passwd, True, reject_reason);
786 unbecome_root();
788 memset(new_passwd, 0, strlen(new_passwd));
790 TALLOC_FREE(sampass);
792 return nt_status;
795 /***********************************************************
796 Decrypt and verify a user password change.
798 The 516 byte long buffers are encrypted with the old NT and
799 old LM passwords, and if the NT passwords are present, both
800 buffers contain a unicode string.
802 After decrypting the buffers, check the password is correct by
803 matching the old hashed passwords with the passwords in the passdb.
805 ************************************************************/
807 static NTSTATUS check_oem_password(const char *user,
808 uchar password_encrypted_with_lm_hash[516],
809 const uchar old_lm_hash_encrypted[16],
810 uchar password_encrypted_with_nt_hash[516],
811 const uchar old_nt_hash_encrypted[16],
812 struct samu **hnd,
813 char **pp_new_passwd)
815 static uchar null_pw[16];
816 static uchar null_ntpw[16];
817 struct samu *sampass = NULL;
818 uint8 *password_encrypted;
819 const uint8 *encryption_key;
820 const uint8 *lanman_pw, *nt_pw;
821 uint32 acct_ctrl;
822 uint32 new_pw_len;
823 uchar new_nt_hash[16];
824 uchar new_lm_hash[16];
825 uchar verifier[16];
826 char no_pw[2];
827 bool ret;
829 bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
830 bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
832 *hnd = NULL;
834 if ( !(sampass = samu_new( NULL )) ) {
835 return NT_STATUS_NO_MEMORY;
838 become_root();
839 ret = pdb_getsampwnam(sampass, user);
840 unbecome_root();
842 if (ret == False) {
843 DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n"));
844 TALLOC_FREE(sampass);
845 return NT_STATUS_NO_SUCH_USER;
848 acct_ctrl = pdb_get_acct_ctrl(sampass);
850 if (acct_ctrl & ACB_DISABLED) {
851 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
852 TALLOC_FREE(sampass);
853 return NT_STATUS_ACCOUNT_DISABLED;
856 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
857 /* construct a null password (in case one is needed */
858 no_pw[0] = 0;
859 no_pw[1] = 0;
860 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
861 lanman_pw = null_pw;
862 nt_pw = null_pw;
864 } else {
865 /* save pointers to passwords so we don't have to keep looking them up */
866 if (lp_lanman_auth()) {
867 lanman_pw = pdb_get_lanman_passwd(sampass);
868 } else {
869 lanman_pw = NULL;
871 nt_pw = pdb_get_nt_passwd(sampass);
874 if (nt_pw && nt_pass_set) {
875 /* IDEAL Case: passwords are in unicode, and we can
876 * read use the password encrypted with the NT hash
878 password_encrypted = password_encrypted_with_nt_hash;
879 encryption_key = nt_pw;
880 } else if (lanman_pw && lm_pass_set) {
881 /* password may still be in unicode, but use LM hash version */
882 password_encrypted = password_encrypted_with_lm_hash;
883 encryption_key = lanman_pw;
884 } else if (nt_pass_set) {
885 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
886 user));
887 TALLOC_FREE(sampass);
888 return NT_STATUS_WRONG_PASSWORD;
889 } else if (lm_pass_set) {
890 if (lp_lanman_auth()) {
891 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
892 user));
893 } else {
894 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
895 user));
897 TALLOC_FREE(sampass);
898 return NT_STATUS_WRONG_PASSWORD;
899 } else {
900 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
901 user));
902 TALLOC_FREE(sampass);
903 return NT_STATUS_WRONG_PASSWORD;
907 * Decrypt the password with the key
909 SamOEMhash( password_encrypted, encryption_key, 516);
911 if (!decode_pw_buffer(talloc_tos(),
912 password_encrypted,
913 pp_new_passwd,
914 &new_pw_len,
915 nt_pass_set ? STR_UNICODE : STR_ASCII)) {
916 TALLOC_FREE(sampass);
917 return NT_STATUS_WRONG_PASSWORD;
921 * To ensure we got the correct new password, hash it and
922 * use it as a key to test the passed old password.
925 if (nt_pass_set) {
926 /* NT passwords, verify the NT hash. */
928 /* Calculate the MD4 hash (NT compatible) of the password */
929 memset(new_nt_hash, '\0', 16);
930 E_md4hash(*pp_new_passwd, new_nt_hash);
932 if (nt_pw) {
934 * check the NT verifier
936 E_old_pw_hash(new_nt_hash, nt_pw, verifier);
937 if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
938 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
939 TALLOC_FREE(sampass);
940 return NT_STATUS_WRONG_PASSWORD;
943 /* We could check the LM password here, but there is
944 * little point, we already know the password is
945 * correct, and the LM password might not even be
946 * present. */
948 /* Further, LM hash generation algorithms
949 * differ with charset, so we could
950 * incorrectly fail a perfectly valid password
951 * change */
952 #ifdef DEBUG_PASSWORD
953 DEBUG(100,
954 ("check_oem_password: password %s ok\n", *pp_new_passwd));
955 #endif
956 *hnd = sampass;
957 return NT_STATUS_OK;
960 if (lanman_pw) {
962 * check the lm verifier
964 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
965 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
966 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
967 TALLOC_FREE(sampass);
968 return NT_STATUS_WRONG_PASSWORD;
970 #ifdef DEBUG_PASSWORD
971 DEBUG(100,
972 ("check_oem_password: password %s ok\n", *pp_new_passwd));
973 #endif
974 *hnd = sampass;
975 return NT_STATUS_OK;
979 if (lanman_pw && lm_pass_set) {
981 E_deshash(*pp_new_passwd, new_lm_hash);
984 * check the lm verifier
986 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
987 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
988 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
989 TALLOC_FREE(sampass);
990 return NT_STATUS_WRONG_PASSWORD;
993 #ifdef DEBUG_PASSWORD
994 DEBUG(100,
995 ("check_oem_password: password %s ok\n", *pp_new_passwd));
996 #endif
997 *hnd = sampass;
998 return NT_STATUS_OK;
1001 /* should not be reached */
1002 TALLOC_FREE(sampass);
1003 return NT_STATUS_WRONG_PASSWORD;
1006 /***********************************************************
1007 This routine takes the given password and checks it against
1008 the password history. Returns True if this password has been
1009 found in the history list.
1010 ************************************************************/
1012 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
1014 uchar new_nt_p16[NT_HASH_LEN];
1015 uchar zero_md5_nt_pw[SALTED_MD5_HASH_LEN];
1016 const uint8 *nt_pw;
1017 const uint8 *pwhistory;
1018 bool found = False;
1019 int i;
1020 uint32 pwHisLen, curr_pwHisLen;
1022 pdb_get_account_policy(AP_PASSWORD_HISTORY, &pwHisLen);
1023 if (pwHisLen == 0) {
1024 return False;
1027 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
1028 if (!pwhistory || curr_pwHisLen == 0) {
1029 return False;
1032 /* Only examine the minimum of the current history len and
1033 the stored history len. Avoids race conditions. */
1034 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
1036 nt_pw = pdb_get_nt_passwd(sampass);
1038 E_md4hash(plaintext, new_nt_p16);
1040 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
1041 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
1042 pdb_get_username(sampass) ));
1043 return True;
1046 dump_data(100, new_nt_p16, NT_HASH_LEN);
1047 dump_data(100, pwhistory, PW_HISTORY_ENTRY_LEN*pwHisLen);
1049 memset(zero_md5_nt_pw, '\0', SALTED_MD5_HASH_LEN);
1050 for (i=0; i<pwHisLen; i++) {
1051 uchar new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
1052 const uchar *current_salt = &pwhistory[i*PW_HISTORY_ENTRY_LEN];
1053 const uchar *old_nt_pw_salted_md5_hash = &pwhistory[(i*PW_HISTORY_ENTRY_LEN)+
1054 PW_HISTORY_SALT_LEN];
1055 if (!memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1056 /* Ignore zero valued entries. */
1057 continue;
1059 /* Create salted versions of new to compare. */
1060 E_md5hash(current_salt, new_nt_p16, new_nt_pw_salted_md5_hash);
1062 if (!memcmp(new_nt_pw_salted_md5_hash, old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
1063 DEBUG(1,("check_passwd_history: proposed new password for user %s found in history list !\n",
1064 pdb_get_username(sampass) ));
1065 found = True;
1066 break;
1069 return found;
1072 /***********************************************************
1073 Code to change the oem password. Changes both the lanman
1074 and NT hashes. Old_passwd is almost always NULL.
1075 NOTE this function is designed to be called as root. Check the old password
1076 is correct before calling. JRA.
1077 ************************************************************/
1079 NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, bool as_root, uint32 *samr_reject_reason)
1081 uint32 min_len;
1082 uint32 refuse;
1083 struct passwd *pass = NULL;
1084 const char *username = pdb_get_username(hnd);
1085 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1087 if (samr_reject_reason) {
1088 *samr_reject_reason = Undefined;
1091 /* check to see if the secdesc has previously been set to disallow */
1092 if (!pdb_get_pass_can_change(hnd)) {
1093 DEBUG(1, ("user %s does not have permissions to change password\n", username));
1094 if (samr_reject_reason) {
1095 *samr_reject_reason = SAMR_REJECT_OTHER;
1097 return NT_STATUS_ACCOUNT_RESTRICTION;
1100 /* check to see if it is a Machine account and if the policy
1101 * denies machines to change the password. *
1102 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1103 if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1104 if (pdb_get_account_policy(AP_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1105 DEBUG(1, ("Machine %s cannot change password now, "
1106 "denied by Refuse Machine Password Change policy\n",
1107 username));
1108 if (samr_reject_reason) {
1109 *samr_reject_reason = SAMR_REJECT_OTHER;
1111 return NT_STATUS_ACCOUNT_RESTRICTION;
1115 /* removed calculation here, becuase passdb now calculates
1116 based on policy. jmcd */
1117 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1118 DEBUG(1, ("user %s cannot change password now, must "
1119 "wait until %s\n", username,
1120 http_timestring(can_change_time)));
1121 if (samr_reject_reason) {
1122 *samr_reject_reason = SAMR_REJECT_OTHER;
1124 return NT_STATUS_ACCOUNT_RESTRICTION;
1127 if (pdb_get_account_policy(AP_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1128 DEBUG(1, ("user %s cannot change password - password too short\n",
1129 username));
1130 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1131 if (samr_reject_reason) {
1132 *samr_reject_reason = SAMR_REJECT_TOO_SHORT;
1134 return NT_STATUS_PASSWORD_RESTRICTION;
1135 /* return NT_STATUS_PWD_TOO_SHORT; */
1138 if (check_passwd_history(hnd,new_passwd)) {
1139 if (samr_reject_reason) {
1140 *samr_reject_reason = SAMR_REJECT_IN_HISTORY;
1142 return NT_STATUS_PASSWORD_RESTRICTION;
1145 pass = Get_Pwnam_alloc(talloc_tos(), username);
1146 if (!pass) {
1147 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1148 return NT_STATUS_ACCESS_DENIED;
1151 /* Use external script to check password complexity */
1152 if (lp_check_password_script() && *(lp_check_password_script())) {
1153 int check_ret;
1155 check_ret = smbrunsecret(lp_check_password_script(), new_passwd);
1156 DEBUG(5, ("change_oem_password: check password script (%s) returned [%d]\n", lp_check_password_script(), check_ret));
1158 if (check_ret != 0) {
1159 DEBUG(1, ("change_oem_password: check password script said new password is not good enough!\n"));
1160 if (samr_reject_reason) {
1161 *samr_reject_reason = SAMR_REJECT_COMPLEXITY;
1163 TALLOC_FREE(pass);
1164 return NT_STATUS_PASSWORD_RESTRICTION;
1169 * If unix password sync was requested, attempt to change
1170 * the /etc/passwd database first. Return failure if this cannot
1171 * be done.
1173 * This occurs before the oem change, because we don't want to
1174 * update it if chgpasswd failed.
1176 * Conditional on lp_unix_password_sync() because we don't want
1177 * to touch the unix db unless we have admin permission.
1180 if(lp_unix_password_sync() &&
1181 !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
1182 TALLOC_FREE(pass);
1183 return NT_STATUS_ACCESS_DENIED;
1186 TALLOC_FREE(pass);
1188 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1189 return NT_STATUS_ACCESS_DENIED;
1192 /* Now write it into the file. */
1193 return pdb_update_sam_account (hnd);