s3-rpc_client: move protos to cli_lsarpc.h
[Samba/ekacnet.git] / source3 / smbd / chgpasswd.c
blob735f57d0dfa96057bc8ce7f701903ffe1c652f5f
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"
49 #include "../libcli/auth/libcli_auth.h"
50 #include "../lib/crypto/arcfour.h"
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 *sampass,
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) && !defined(SUNOS5)
164 * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
165 * see the discussion under
166 * https://bugzilla.samba.org/show_bug.cgi?id=5366.
168 if (ioctl(slave, TIOCSCTTY, 0) < 0)
170 DEBUG(3, ("Error in ioctl call for slave pty\n"));
171 /* return(False); */
173 #elif defined(I_PUSH) && defined(I_FIND)
174 if (ioctl(slave, I_FIND, "ptem") == 0) {
175 ioctl(slave, I_PUSH, "ptem");
177 if (ioctl(slave, I_FIND, "ldterm") == 0) {
178 ioctl(slave, I_PUSH, "ldterm");
180 #endif
182 /* Close master. */
183 close(master);
185 /* Make slave stdin/out/err of child. */
187 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
189 DEBUG(3, ("Could not re-direct stdin\n"));
190 return (False);
192 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
194 DEBUG(3, ("Could not re-direct stdout\n"));
195 return (False);
197 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
199 DEBUG(3, ("Could not re-direct stderr\n"));
200 return (False);
202 if (slave > 2)
203 close(slave);
205 /* Set proper terminal attributes - no echo, canonical input processing,
206 no map NL to CR/NL on output. */
208 if (tcgetattr(0, &stermios) < 0)
210 DEBUG(3,
211 ("could not read default terminal attributes on pty\n"));
212 return (False);
214 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
215 stermios.c_lflag |= ICANON;
216 #ifdef ONLCR
217 stermios.c_oflag &= ~(ONLCR);
218 #endif
219 if (tcsetattr(0, TCSANOW, &stermios) < 0)
221 DEBUG(3, ("could not set attributes of pty\n"));
222 return (False);
225 /* make us completely into the right uid */
226 if (!as_root)
228 become_user_permanently(uid, gid);
231 DEBUG(10,
232 ("Invoking '%s' as password change program.\n",
233 passwordprogram));
235 /* execl() password-change application */
236 if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
238 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
239 return (False);
241 return (True);
244 static int expect(int master, char *issue, char *expected)
246 char buffer[1024];
247 int attempts, timeout, nread;
248 size_t len;
249 bool match = False;
251 for (attempts = 0; attempts < 2; attempts++) {
252 NTSTATUS status;
253 if (!strequal(issue, ".")) {
254 if (lp_passwd_chat_debug())
255 DEBUG(100, ("expect: sending [%s]\n", issue));
257 if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
258 DEBUG(2,("expect: (short) write returned %d\n",
259 (int)len ));
260 return False;
264 if (strequal(expected, "."))
265 return True;
267 /* Initial timeout. */
268 timeout = lp_passwd_chat_timeout() * 1000;
269 nread = 0;
270 buffer[nread] = 0;
272 while (True) {
273 status = read_fd_with_timeout(
274 master, buffer + nread, 1,
275 sizeof(buffer) - nread - 1,
276 timeout, &len);
278 if (!NT_STATUS_IS_OK(status)) {
279 break;
281 nread += len;
282 buffer[nread] = 0;
285 /* Eat leading/trailing whitespace before match. */
286 char *str = SMB_STRDUP(buffer);
287 if (!str) {
288 DEBUG(2,("expect: ENOMEM\n"));
289 return False;
291 trim_char(str, ' ', ' ');
293 if ((match = unix_wild_match(expected, str)) == True) {
294 /* Now data has started to return, lower timeout. */
295 timeout = lp_passwd_chat_timeout() * 100;
297 SAFE_FREE(str);
301 if (lp_passwd_chat_debug())
302 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
303 expected, buffer, match ? "yes" : "no" ));
305 if (match)
306 break;
308 if (!NT_STATUS_IS_OK(status)) {
309 DEBUG(2, ("expect: %s\n", nt_errstr(status)));
310 return False;
314 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
315 return match;
318 static void pwd_sub(char *buf)
320 all_string_sub(buf, "\\n", "\n", 0);
321 all_string_sub(buf, "\\r", "\r", 0);
322 all_string_sub(buf, "\\s", " ", 0);
323 all_string_sub(buf, "\\t", "\t", 0);
326 static int talktochild(int master, const char *seq)
328 TALLOC_CTX *frame = talloc_stackframe();
329 int count = 0;
330 char *issue;
331 char *expected;
333 issue = talloc_strdup(frame, ".");
334 if (!issue) {
335 TALLOC_FREE(frame);
336 return false;
339 while (next_token_talloc(frame, &seq, &expected, NULL)) {
340 pwd_sub(expected);
341 count++;
343 if (!expect(master, issue, expected)) {
344 DEBUG(3, ("Response %d incorrect\n", count));
345 TALLOC_FREE(frame);
346 return false;
349 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
350 issue = talloc_strdup(frame, ".");
351 if (!issue) {
352 TALLOC_FREE(frame);
353 return false;
356 pwd_sub(issue);
359 if (!strequal(issue, ".")) {
360 /* we have one final issue to send */
361 expected = talloc_strdup(frame, ".");
362 if (!expected) {
363 TALLOC_FREE(frame);
364 return false;
366 if (!expect(master, issue, expected)) {
367 TALLOC_FREE(frame);
368 return False;
371 TALLOC_FREE(frame);
372 return (count > 0);
375 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
376 char *chatsequence, bool as_root)
378 char *slavedev = NULL;
379 int master;
380 pid_t pid, wpid;
381 int wstat;
382 bool chstat = False;
384 if (pass == NULL) {
385 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
386 return False;
389 /* allocate a pseudo-terminal device */
390 if ((master = findpty(&slavedev)) < 0) {
391 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
392 return (False);
396 * We need to temporarily stop CatchChild from eating
397 * SIGCLD signals as it also eats the exit status code. JRA.
400 CatchChildLeaveStatus();
402 if ((pid = sys_fork()) < 0) {
403 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
404 SAFE_FREE(slavedev);
405 close(master);
406 CatchChild();
407 return (False);
410 /* we now have a pty */
411 if (pid > 0) { /* This is the parent process */
412 /* Don't need this anymore in parent. */
413 SAFE_FREE(slavedev);
415 if ((chstat = talktochild(master, chatsequence)) == False) {
416 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
417 kill(pid, SIGKILL); /* be sure to end this process */
420 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
421 if (errno == EINTR) {
422 errno = 0;
423 continue;
425 break;
428 if (wpid < 0) {
429 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
430 close(master);
431 CatchChild();
432 return (False);
436 * Go back to ignoring children.
438 CatchChild();
440 close(master);
442 if (pid != wpid) {
443 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
444 return (False);
446 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
447 DEBUG(3, ("chat_with_program: The process exited with status %d \
448 while we were waiting\n", WEXITSTATUS(wstat)));
449 return (False);
451 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
452 else if (WIFSIGNALLED(wstat)) {
453 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
454 while we were waiting\n", WTERMSIG(wstat)));
455 return (False);
457 #endif
458 } else {
459 /* CHILD */
462 * Lose any elevated privileges.
464 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
465 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
467 /* make sure it doesn't freeze */
468 alarm(20);
470 if (as_root)
471 become_root();
473 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
474 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
475 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
477 if (as_root)
478 unbecome_root();
481 * The child should never return from dochild() ....
484 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
485 exit(1);
488 if (chstat)
489 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
490 (chstat ? "" : "un"), pass->pw_name));
491 return (chstat);
494 bool chgpasswd(const char *name, const struct passwd *pass,
495 const char *oldpass, const char *newpass, bool as_root)
497 char *passwordprogram = NULL;
498 char *chatsequence = NULL;
499 size_t i;
500 size_t len;
501 TALLOC_CTX *ctx = talloc_tos();
503 if (!oldpass) {
504 oldpass = "";
507 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
509 #ifdef DEBUG_PASSWORD
510 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
511 #endif
513 /* Take the passed information and test it for minimum criteria */
515 /* Password is same as old password */
516 if (strcmp(oldpass, newpass) == 0) {
517 /* don't allow same password */
518 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
519 return (False); /* inform the user */
523 * Check the old and new passwords don't contain any control
524 * characters.
527 len = strlen(oldpass);
528 for (i = 0; i < len; i++) {
529 if (iscntrl((int)oldpass[i])) {
530 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
531 return False;
535 len = strlen(newpass);
536 for (i = 0; i < len; i++) {
537 if (iscntrl((int)newpass[i])) {
538 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
539 return False;
543 #ifdef WITH_PAM
544 if (lp_pam_password_change()) {
545 bool ret;
546 #ifdef HAVE_SETLOCALE
547 const char *prevlocale = setlocale(LC_ALL, "C");
548 #endif
550 if (as_root)
551 become_root();
553 if (pass) {
554 ret = smb_pam_passchange(pass->pw_name, oldpass, newpass);
555 } else {
556 ret = smb_pam_passchange(name, oldpass, newpass);
559 if (as_root)
560 unbecome_root();
562 #ifdef HAVE_SETLOCALE
563 setlocale(LC_ALL, prevlocale);
564 #endif
566 return ret;
568 #endif
570 /* A non-PAM password change just doen't make sense without a valid local user */
572 if (pass == NULL) {
573 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
574 return false;
577 passwordprogram = talloc_strdup(ctx, lp_passwd_program());
578 if (!passwordprogram || !*passwordprogram) {
579 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
580 return false;
582 chatsequence = talloc_strdup(ctx, lp_passwd_chat());
583 if (!chatsequence || !*chatsequence) {
584 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
585 return false;
588 if (as_root) {
589 /* The password program *must* contain the user name to work. Fail if not. */
590 if (strstr_m(passwordprogram, "%u") == NULL) {
591 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
592 the string %%u, and the given string %s does not.\n", passwordprogram ));
593 return false;
597 passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
598 if (!passwordprogram) {
599 return false;
602 /* note that we do NOT substitute the %o and %n in the password program
603 as this would open up a security hole where the user could use
604 a new password containing shell escape characters */
606 chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
607 if (!chatsequence) {
608 return false;
610 chatsequence = talloc_all_string_sub(ctx,
611 chatsequence,
612 "%o",
613 oldpass);
614 if (!chatsequence) {
615 return false;
617 chatsequence = talloc_all_string_sub(ctx,
618 chatsequence,
619 "%n",
620 newpass);
621 return chat_with_program(passwordprogram,
622 pass,
623 chatsequence,
624 as_root);
627 #else /* ALLOW_CHANGE_PASSWORD */
629 bool chgpasswd(const char *name, const struct passwd *pass,
630 const char *oldpass, const char *newpass, bool as_root)
632 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
633 return (False);
635 #endif /* ALLOW_CHANGE_PASSWORD */
637 /***********************************************************
638 Code to check the lanman hashed password.
639 ************************************************************/
641 bool check_lanman_password(char *user, uchar * pass1,
642 uchar * pass2, struct samu **hnd)
644 uchar unenc_new_pw[16];
645 uchar unenc_old_pw[16];
646 struct samu *sampass = NULL;
647 uint32 acct_ctrl;
648 const uint8 *lanman_pw;
649 bool ret;
651 if ( !(sampass = samu_new(NULL)) ) {
652 DEBUG(0, ("samu_new() failed!\n"));
653 return False;
656 become_root();
657 ret = pdb_getsampwnam(sampass, user);
658 unbecome_root();
660 if (ret == False) {
661 DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n"));
662 TALLOC_FREE(sampass);
663 return False;
666 acct_ctrl = pdb_get_acct_ctrl (sampass);
667 lanman_pw = pdb_get_lanman_passwd (sampass);
669 if (acct_ctrl & ACB_DISABLED) {
670 DEBUG(0,("check_lanman_password: account %s disabled.\n", user));
671 TALLOC_FREE(sampass);
672 return False;
675 if (lanman_pw == NULL) {
676 if (acct_ctrl & ACB_PWNOTREQ) {
677 /* this saves the pointer for the caller */
678 *hnd = sampass;
679 return True;
680 } else {
681 DEBUG(0, ("check_lanman_password: no lanman password !\n"));
682 TALLOC_FREE(sampass);
683 return False;
687 /* Get the new lanman hash. */
688 D_P16(lanman_pw, pass2, unenc_new_pw);
690 /* Use this to get the old lanman hash. */
691 D_P16(unenc_new_pw, pass1, unenc_old_pw);
693 /* Check that the two old passwords match. */
694 if (memcmp(lanman_pw, unenc_old_pw, 16)) {
695 DEBUG(0,("check_lanman_password: old password doesn't match.\n"));
696 TALLOC_FREE(sampass);
697 return False;
700 /* this saves the pointer for the caller */
701 *hnd = sampass;
702 return True;
705 /***********************************************************
706 Code to change the lanman hashed password.
707 It nulls out the NT hashed password as it will
708 no longer be valid.
709 NOTE this function is designed to be called as root. Check the old password
710 is correct before calling. JRA.
711 ************************************************************/
713 bool change_lanman_password(struct samu *sampass, uchar *pass2)
715 uchar null_pw[16];
716 uchar unenc_new_pw[16];
717 bool ret;
718 uint32 acct_ctrl;
719 const uint8 *pwd;
721 if (sampass == NULL) {
722 DEBUG(0,("change_lanman_password: no smb password entry.\n"));
723 return False;
726 acct_ctrl = pdb_get_acct_ctrl(sampass);
727 pwd = pdb_get_lanman_passwd(sampass);
729 if (acct_ctrl & ACB_DISABLED) {
730 DEBUG(0,("change_lanman_password: account %s disabled.\n",
731 pdb_get_username(sampass)));
732 return False;
735 if (pwd == NULL) {
736 if (acct_ctrl & ACB_PWNOTREQ) {
737 uchar no_pw[14];
739 ZERO_STRUCT(no_pw);
741 E_P16(no_pw, null_pw);
743 pwd = null_pw;
744 } else {
745 DEBUG(0,("change_lanman_password: no lanman password !\n"));
746 return False;
750 /* Get the new lanman hash. */
751 D_P16(pwd, pass2, unenc_new_pw);
753 if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) {
754 return False;
757 if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) {
758 return False; /* We lose the NT hash. Sorry. */
761 if (!pdb_set_pass_last_set_time (sampass, time(NULL), PDB_CHANGED)) {
762 TALLOC_FREE(sampass);
763 /* Not quite sure what this one qualifies as, but this will do */
764 return False;
767 /* Now flush the sam_passwd struct to persistent storage */
768 ret = NT_STATUS_IS_OK(pdb_update_sam_account (sampass));
770 return ret;
773 /***********************************************************
774 Code to check and change the OEM hashed password.
775 ************************************************************/
777 NTSTATUS pass_oem_change(char *user,
778 uchar password_encrypted_with_lm_hash[516],
779 const uchar old_lm_hash_encrypted[16],
780 uchar password_encrypted_with_nt_hash[516],
781 const uchar old_nt_hash_encrypted[16],
782 enum samPwdChangeReason *reject_reason)
784 char *new_passwd = NULL;
785 struct samu *sampass = NULL;
786 NTSTATUS nt_status;
787 bool ret = false;
789 if (!(sampass = samu_new(NULL))) {
790 return NT_STATUS_NO_MEMORY;
793 become_root();
794 ret = pdb_getsampwnam(sampass, user);
795 unbecome_root();
797 if (ret == false) {
798 DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
799 TALLOC_FREE(sampass);
800 return NT_STATUS_NO_SUCH_USER;
803 nt_status = check_oem_password(user,
804 password_encrypted_with_lm_hash,
805 old_lm_hash_encrypted,
806 password_encrypted_with_nt_hash,
807 old_nt_hash_encrypted,
808 sampass,
809 &new_passwd);
811 if (!NT_STATUS_IS_OK(nt_status)) {
812 TALLOC_FREE(sampass);
813 return nt_status;
816 /* We've already checked the old password here.... */
817 become_root();
818 nt_status = change_oem_password(sampass, NULL, new_passwd, True, reject_reason);
819 unbecome_root();
821 memset(new_passwd, 0, strlen(new_passwd));
823 TALLOC_FREE(sampass);
825 return nt_status;
828 /***********************************************************
829 Decrypt and verify a user password change.
831 The 516 byte long buffers are encrypted with the old NT and
832 old LM passwords, and if the NT passwords are present, both
833 buffers contain a unicode string.
835 After decrypting the buffers, check the password is correct by
836 matching the old hashed passwords with the passwords in the passdb.
838 ************************************************************/
840 static NTSTATUS check_oem_password(const char *user,
841 uchar password_encrypted_with_lm_hash[516],
842 const uchar old_lm_hash_encrypted[16],
843 uchar password_encrypted_with_nt_hash[516],
844 const uchar old_nt_hash_encrypted[16],
845 struct samu *sampass,
846 char **pp_new_passwd)
848 uchar null_pw[16];
849 uchar null_ntpw[16];
850 uint8 *password_encrypted;
851 const uint8 *encryption_key;
852 const uint8 *lanman_pw, *nt_pw;
853 uint32 acct_ctrl;
854 size_t new_pw_len;
855 uchar new_nt_hash[16];
856 uchar new_lm_hash[16];
857 uchar verifier[16];
858 char no_pw[2];
860 bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
861 bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
863 acct_ctrl = pdb_get_acct_ctrl(sampass);
864 #if 0
865 /* I am convinced this check here is wrong, it is valid to
866 * change a password of a user that has a disabled account - gd */
868 if (acct_ctrl & ACB_DISABLED) {
869 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
870 return NT_STATUS_ACCOUNT_DISABLED;
872 #endif
873 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
874 /* construct a null password (in case one is needed */
875 no_pw[0] = 0;
876 no_pw[1] = 0;
877 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
878 lanman_pw = null_pw;
879 nt_pw = null_pw;
881 } else {
882 /* save pointers to passwords so we don't have to keep looking them up */
883 if (lp_lanman_auth()) {
884 lanman_pw = pdb_get_lanman_passwd(sampass);
885 } else {
886 lanman_pw = NULL;
888 nt_pw = pdb_get_nt_passwd(sampass);
891 if (nt_pw && nt_pass_set) {
892 /* IDEAL Case: passwords are in unicode, and we can
893 * read use the password encrypted with the NT hash
895 password_encrypted = password_encrypted_with_nt_hash;
896 encryption_key = nt_pw;
897 } else if (lanman_pw && lm_pass_set) {
898 /* password may still be in unicode, but use LM hash version */
899 password_encrypted = password_encrypted_with_lm_hash;
900 encryption_key = lanman_pw;
901 } else if (nt_pass_set) {
902 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
903 user));
904 return NT_STATUS_WRONG_PASSWORD;
905 } else if (lm_pass_set) {
906 if (lp_lanman_auth()) {
907 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
908 user));
909 } else {
910 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
911 user));
913 return NT_STATUS_WRONG_PASSWORD;
914 } else {
915 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
916 user));
917 return NT_STATUS_WRONG_PASSWORD;
921 * Decrypt the password with the key
923 arcfour_crypt( password_encrypted, encryption_key, 516);
925 if (!decode_pw_buffer(talloc_tos(),
926 password_encrypted,
927 pp_new_passwd,
928 &new_pw_len,
929 nt_pass_set ? CH_UTF16 : CH_DOS)) {
930 return NT_STATUS_WRONG_PASSWORD;
934 * To ensure we got the correct new password, hash it and
935 * use it as a key to test the passed old password.
938 if (nt_pass_set) {
939 /* NT passwords, verify the NT hash. */
941 /* Calculate the MD4 hash (NT compatible) of the password */
942 memset(new_nt_hash, '\0', 16);
943 E_md4hash(*pp_new_passwd, new_nt_hash);
945 if (nt_pw) {
947 * check the NT verifier
949 E_old_pw_hash(new_nt_hash, nt_pw, verifier);
950 if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
951 DEBUG(0, ("check_oem_password: old nt "
952 "password doesn't match.\n"));
953 return NT_STATUS_WRONG_PASSWORD;
956 /* We could check the LM password here, but there is
957 * little point, we already know the password is
958 * correct, and the LM password might not even be
959 * present. */
961 /* Further, LM hash generation algorithms
962 * differ with charset, so we could
963 * incorrectly fail a perfectly valid password
964 * change */
965 #ifdef DEBUG_PASSWORD
966 DEBUG(100,
967 ("check_oem_password: password %s ok\n", *pp_new_passwd));
968 #endif
969 return NT_STATUS_OK;
972 if (lanman_pw) {
974 * check the lm verifier
976 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
977 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
978 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
979 return NT_STATUS_WRONG_PASSWORD;
981 #ifdef DEBUG_PASSWORD
982 DEBUG(100,
983 ("check_oem_password: password %s ok\n", *pp_new_passwd));
984 #endif
985 return NT_STATUS_OK;
989 if (lanman_pw && lm_pass_set) {
991 E_deshash(*pp_new_passwd, new_lm_hash);
994 * check the lm verifier
996 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
997 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
998 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
999 return NT_STATUS_WRONG_PASSWORD;
1002 #ifdef DEBUG_PASSWORD
1003 DEBUG(100,
1004 ("check_oem_password: password %s ok\n", *pp_new_passwd));
1005 #endif
1006 return NT_STATUS_OK;
1009 /* should not be reached */
1010 return NT_STATUS_WRONG_PASSWORD;
1013 bool password_in_history(uint8_t nt_pw[NT_HASH_LEN],
1014 uint32_t pw_history_len,
1015 const uint8_t *pw_history)
1017 static const uint8_t zero_md5_nt_pw[SALTED_MD5_HASH_LEN] = { 0, };
1018 int i;
1020 dump_data(100, nt_pw, NT_HASH_LEN);
1021 dump_data(100, pw_history, PW_HISTORY_ENTRY_LEN * pw_history_len);
1023 for (i=0; i<pw_history_len; i++) {
1024 uint8_t new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
1025 const uint8_t *current_salt;
1026 const uint8_t *old_nt_pw_salted_md5_hash;
1028 current_salt = &pw_history[i*PW_HISTORY_ENTRY_LEN];
1029 old_nt_pw_salted_md5_hash = current_salt + PW_HISTORY_SALT_LEN;
1031 if (memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash,
1032 SALTED_MD5_HASH_LEN) == 0) {
1033 /* Ignore zero valued entries. */
1034 continue;
1037 if (memcmp(zero_md5_nt_pw, current_salt,
1038 PW_HISTORY_SALT_LEN) == 0)
1041 * New format: zero salt and then plain nt hash.
1042 * Directly compare the hashes.
1044 if (memcmp(nt_pw, old_nt_pw_salted_md5_hash,
1045 SALTED_MD5_HASH_LEN) == 0)
1047 return true;
1049 } else {
1051 * Old format: md5sum of salted nt hash.
1052 * Create salted version of new pw to compare.
1054 E_md5hash(current_salt, nt_pw, new_nt_pw_salted_md5_hash);
1056 if (memcmp(new_nt_pw_salted_md5_hash,
1057 old_nt_pw_salted_md5_hash,
1058 SALTED_MD5_HASH_LEN) == 0) {
1059 return true;
1063 return false;
1066 /***********************************************************
1067 This routine takes the given password and checks it against
1068 the password history. Returns True if this password has been
1069 found in the history list.
1070 ************************************************************/
1072 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
1074 uchar new_nt_p16[NT_HASH_LEN];
1075 const uint8 *nt_pw;
1076 const uint8 *pwhistory;
1077 uint32 pwHisLen, curr_pwHisLen;
1079 pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &pwHisLen);
1080 if (pwHisLen == 0) {
1081 return False;
1084 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
1085 if (!pwhistory || curr_pwHisLen == 0) {
1086 return False;
1089 /* Only examine the minimum of the current history len and
1090 the stored history len. Avoids race conditions. */
1091 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
1093 nt_pw = pdb_get_nt_passwd(sampass);
1095 E_md4hash(plaintext, new_nt_p16);
1097 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
1098 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
1099 pdb_get_username(sampass) ));
1100 return True;
1103 if (password_in_history(new_nt_p16, pwHisLen, pwhistory)) {
1104 DEBUG(1,("check_passwd_history: proposed new password for "
1105 "user %s found in history list !\n",
1106 pdb_get_username(sampass) ));
1107 return true;
1109 return false;
1112 /***********************************************************
1113 ************************************************************/
1115 NTSTATUS check_password_complexity(const char *username,
1116 const char *password,
1117 enum samPwdChangeReason *samr_reject_reason)
1119 TALLOC_CTX *tosctx = talloc_tos();
1120 int check_ret;
1121 char *cmd;
1123 /* Use external script to check password complexity */
1124 if ((lp_check_password_script() == NULL)
1125 || (*(lp_check_password_script()) == '\0')) {
1126 return NT_STATUS_OK;
1129 cmd = talloc_string_sub(tosctx, lp_check_password_script(), "%u",
1130 username);
1131 if (!cmd) {
1132 return NT_STATUS_PASSWORD_RESTRICTION;
1135 check_ret = smbrunsecret(cmd, password);
1136 DEBUG(5,("check_password_complexity: check password script (%s) "
1137 "returned [%d]\n", cmd, check_ret));
1138 TALLOC_FREE(cmd);
1140 if (check_ret != 0) {
1141 DEBUG(1,("check_password_complexity: "
1142 "check password script said new password is not good "
1143 "enough!\n"));
1144 if (samr_reject_reason) {
1145 *samr_reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
1147 return NT_STATUS_PASSWORD_RESTRICTION;
1150 return NT_STATUS_OK;
1153 /***********************************************************
1154 Code to change the oem password. Changes both the lanman
1155 and NT hashes. Old_passwd is almost always NULL.
1156 NOTE this function is designed to be called as root. Check the old password
1157 is correct before calling. JRA.
1158 ************************************************************/
1160 NTSTATUS change_oem_password(struct samu *hnd, char *old_passwd, char *new_passwd, bool as_root, enum samPwdChangeReason *samr_reject_reason)
1162 uint32 min_len;
1163 uint32 refuse;
1164 TALLOC_CTX *tosctx = talloc_tos();
1165 struct passwd *pass = NULL;
1166 const char *username = pdb_get_username(hnd);
1167 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1168 NTSTATUS status;
1170 if (samr_reject_reason) {
1171 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1174 /* check to see if the secdesc has previously been set to disallow */
1175 if (!pdb_get_pass_can_change(hnd)) {
1176 DEBUG(1, ("user %s does not have permissions to change password\n", username));
1177 if (samr_reject_reason) {
1178 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1180 return NT_STATUS_ACCOUNT_RESTRICTION;
1183 /* check to see if it is a Machine account and if the policy
1184 * denies machines to change the password. *
1185 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1186 if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1187 if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1188 DEBUG(1, ("Machine %s cannot change password now, "
1189 "denied by Refuse Machine Password Change policy\n",
1190 username));
1191 if (samr_reject_reason) {
1192 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1194 return NT_STATUS_ACCOUNT_RESTRICTION;
1198 /* removed calculation here, because passdb now calculates
1199 based on policy. jmcd */
1200 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1201 DEBUG(1, ("user %s cannot change password now, must "
1202 "wait until %s\n", username,
1203 http_timestring(tosctx, can_change_time)));
1204 if (samr_reject_reason) {
1205 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1207 return NT_STATUS_ACCOUNT_RESTRICTION;
1210 if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1211 DEBUG(1, ("user %s cannot change password - password too short\n",
1212 username));
1213 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1214 if (samr_reject_reason) {
1215 *samr_reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1217 return NT_STATUS_PASSWORD_RESTRICTION;
1218 /* return NT_STATUS_PWD_TOO_SHORT; */
1221 if (check_passwd_history(hnd,new_passwd)) {
1222 if (samr_reject_reason) {
1223 *samr_reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1225 return NT_STATUS_PASSWORD_RESTRICTION;
1228 pass = Get_Pwnam_alloc(tosctx, username);
1229 if (!pass) {
1230 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1231 return NT_STATUS_ACCESS_DENIED;
1234 status = check_password_complexity(username, new_passwd, samr_reject_reason);
1235 if (!NT_STATUS_IS_OK(status)) {
1236 TALLOC_FREE(pass);
1237 return status;
1241 * If unix password sync was requested, attempt to change
1242 * the /etc/passwd database first. Return failure if this cannot
1243 * be done.
1245 * This occurs before the oem change, because we don't want to
1246 * update it if chgpasswd failed.
1248 * Conditional on lp_unix_password_sync() because we don't want
1249 * to touch the unix db unless we have admin permission.
1252 if(lp_unix_password_sync() &&
1253 !chgpasswd(username, pass, old_passwd, new_passwd, as_root)) {
1254 TALLOC_FREE(pass);
1255 return NT_STATUS_ACCESS_DENIED;
1258 TALLOC_FREE(pass);
1260 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1261 return NT_STATUS_ACCESS_DENIED;
1264 /* Now write it into the file. */
1265 return pdb_update_sam_account (hnd);