dsdb-acl: give error string if we can not obtain the schema
[Samba/gebeck_regimport.git] / source3 / rpc_server / samr / srv_samr_chgpasswd.c
blob51c0d0f96a701be1aedc930159e6ab08cddd9791
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 "system/terminal.h"
50 #include "system/passwd.h"
51 #include "system/filesys.h"
52 #include "../libcli/auth/libcli_auth.h"
53 #include "../lib/crypto/arcfour.h"
54 #include "rpc_server/samr/srv_samr_util.h"
55 #include "passdb.h"
56 #include "auth.h"
58 #ifndef ALLOW_CHANGE_PASSWORD
59 #if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
60 #define ALLOW_CHANGE_PASSWORD 1
61 #endif
62 #endif
64 #if ALLOW_CHANGE_PASSWORD
66 static int findpty(char **slave)
68 int master = -1;
69 char *line = NULL;
70 DIR *dirp = NULL;
71 const char *dpname;
73 *slave = NULL;
75 #if defined(HAVE_GRANTPT)
76 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
77 if ((master = open("/dev/ptmx", O_RDWR, 0)) >= 0) {
78 grantpt(master);
79 unlockpt(master);
80 line = (char *)ptsname(master);
81 if (line) {
82 *slave = SMB_STRDUP(line);
85 if (*slave == NULL) {
86 DEBUG(0,
87 ("findpty: Unable to create master/slave pty pair.\n"));
88 /* Stop fd leak on error. */
89 close(master);
90 return -1;
91 } else {
92 DEBUG(10,
93 ("findpty: Allocated slave pty %s\n", *slave));
94 return (master);
97 #endif /* HAVE_GRANTPT */
99 line = SMB_STRDUP("/dev/ptyXX");
100 if (!line) {
101 return (-1);
104 dirp = opendir("/dev");
105 if (!dirp) {
106 SAFE_FREE(line);
107 return (-1);
110 while ((dpname = readdirname(dirp)) != NULL) {
111 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
112 DEBUG(3,
113 ("pty: try to open %s, line was %s\n", dpname,
114 line));
115 line[8] = dpname[3];
116 line[9] = dpname[4];
117 if ((master = open(line, O_RDWR, 0)) >= 0) {
118 DEBUG(3, ("pty: opened %s\n", line));
119 line[5] = 't';
120 *slave = line;
121 closedir(dirp);
122 return (master);
126 closedir(dirp);
127 SAFE_FREE(line);
128 return (-1);
131 static int dochild(int master, const char *slavedev, const struct passwd *pass,
132 const char *passwordprogram, bool as_root)
134 int slave;
135 struct termios stermios;
136 gid_t gid;
137 uid_t uid;
138 char * const eptrs[1] = { NULL };
140 if (pass == NULL)
142 DEBUG(0,
143 ("dochild: user doesn't exist in the UNIX password database.\n"));
144 return False;
147 gid = pass->pw_gid;
148 uid = pass->pw_uid;
150 gain_root_privilege();
152 /* Start new session - gets rid of controlling terminal. */
153 if (setsid() < 0)
155 DEBUG(3,
156 ("Weirdness, couldn't let go of controlling terminal\n"));
157 return (False);
160 /* Open slave pty and acquire as new controlling terminal. */
161 if ((slave = open(slavedev, O_RDWR, 0)) < 0)
163 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
164 return (False);
166 #if defined(TIOCSCTTY) && !defined(SUNOS5)
168 * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
169 * see the discussion under
170 * https://bugzilla.samba.org/show_bug.cgi?id=5366.
172 if (ioctl(slave, TIOCSCTTY, 0) < 0)
174 DEBUG(3, ("Error in ioctl call for slave pty\n"));
175 /* return(False); */
177 #elif defined(I_PUSH) && defined(I_FIND)
178 if (ioctl(slave, I_FIND, "ptem") == 0) {
179 ioctl(slave, I_PUSH, "ptem");
181 if (ioctl(slave, I_FIND, "ldterm") == 0) {
182 ioctl(slave, I_PUSH, "ldterm");
184 #endif
186 /* Close master. */
187 close(master);
189 /* Make slave stdin/out/err of child. */
191 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
193 DEBUG(3, ("Could not re-direct stdin\n"));
194 return (False);
196 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
198 DEBUG(3, ("Could not re-direct stdout\n"));
199 return (False);
201 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
203 DEBUG(3, ("Could not re-direct stderr\n"));
204 return (False);
206 if (slave > 2)
207 close(slave);
209 /* Set proper terminal attributes - no echo, canonical input processing,
210 no map NL to CR/NL on output. */
212 if (tcgetattr(0, &stermios) < 0)
214 DEBUG(3,
215 ("could not read default terminal attributes on pty\n"));
216 return (False);
218 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
219 stermios.c_lflag |= ICANON;
220 #ifdef ONLCR
221 stermios.c_oflag &= ~(ONLCR);
222 #endif
223 if (tcsetattr(0, TCSANOW, &stermios) < 0)
225 DEBUG(3, ("could not set attributes of pty\n"));
226 return (False);
229 /* make us completely into the right uid */
230 if (!as_root)
232 become_user_permanently(uid, gid);
235 DEBUG(10,
236 ("Invoking '%s' as password change program.\n",
237 passwordprogram));
239 /* execl() password-change application */
240 if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
242 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
243 return (False);
245 return (True);
248 static int expect(int master, char *issue, char *expected)
250 char buffer[1024];
251 int attempts, timeout, nread;
252 size_t len;
253 bool match = False;
255 for (attempts = 0; attempts < 2; attempts++) {
256 NTSTATUS status;
257 if (!strequal(issue, ".")) {
258 if (lp_passwd_chat_debug())
259 DEBUG(100, ("expect: sending [%s]\n", issue));
261 if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
262 DEBUG(2,("expect: (short) write returned %d\n",
263 (int)len ));
264 return False;
268 if (strequal(expected, "."))
269 return True;
271 /* Initial timeout. */
272 timeout = lp_passwd_chat_timeout() * 1000;
273 nread = 0;
274 buffer[nread] = 0;
276 while (True) {
277 status = read_fd_with_timeout(
278 master, buffer + nread, 1,
279 sizeof(buffer) - nread - 1,
280 timeout, &len);
282 if (!NT_STATUS_IS_OK(status)) {
283 DEBUG(2, ("expect: read error %s\n",
284 nt_errstr(status)));
285 break;
287 nread += len;
288 buffer[nread] = 0;
291 /* Eat leading/trailing whitespace before match. */
292 char *str = SMB_STRDUP(buffer);
293 if (!str) {
294 DEBUG(2,("expect: ENOMEM\n"));
295 return False;
297 trim_char(str, ' ', ' ');
299 if ((match = unix_wild_match(expected, str)) == True) {
300 /* Now data has started to return, lower timeout. */
301 timeout = lp_passwd_chat_timeout() * 100;
303 SAFE_FREE(str);
307 if (lp_passwd_chat_debug())
308 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
309 expected, buffer, match ? "yes" : "no" ));
311 if (match)
312 break;
314 if (!NT_STATUS_IS_OK(status)) {
315 DEBUG(2, ("expect: %s\n", nt_errstr(status)));
316 return False;
320 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
321 return match;
324 static void pwd_sub(char *buf)
326 all_string_sub(buf, "\\n", "\n", 0);
327 all_string_sub(buf, "\\r", "\r", 0);
328 all_string_sub(buf, "\\s", " ", 0);
329 all_string_sub(buf, "\\t", "\t", 0);
332 static int talktochild(int master, const char *seq)
334 TALLOC_CTX *frame = talloc_stackframe();
335 int count = 0;
336 char *issue;
337 char *expected;
339 issue = talloc_strdup(frame, ".");
340 if (!issue) {
341 TALLOC_FREE(frame);
342 return false;
345 while (next_token_talloc(frame, &seq, &expected, NULL)) {
346 pwd_sub(expected);
347 count++;
349 if (!expect(master, issue, expected)) {
350 DEBUG(3, ("Response %d incorrect\n", count));
351 TALLOC_FREE(frame);
352 return false;
355 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
356 issue = talloc_strdup(frame, ".");
357 if (!issue) {
358 TALLOC_FREE(frame);
359 return false;
362 pwd_sub(issue);
365 if (!strequal(issue, ".")) {
366 /* we have one final issue to send */
367 expected = talloc_strdup(frame, ".");
368 if (!expected) {
369 TALLOC_FREE(frame);
370 return false;
372 if (!expect(master, issue, expected)) {
373 TALLOC_FREE(frame);
374 return False;
377 TALLOC_FREE(frame);
378 return (count > 0);
381 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
382 char *chatsequence, bool as_root)
384 char *slavedev = NULL;
385 int master;
386 pid_t pid, wpid;
387 int wstat;
388 bool chstat = False;
390 if (pass == NULL) {
391 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
392 return False;
395 /* allocate a pseudo-terminal device */
396 if ((master = findpty(&slavedev)) < 0) {
397 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
398 return (False);
402 * We need to temporarily stop CatchChild from eating
403 * SIGCLD signals as it also eats the exit status code. JRA.
406 CatchChildLeaveStatus();
408 if ((pid = fork()) < 0) {
409 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
410 SAFE_FREE(slavedev);
411 close(master);
412 CatchChild();
413 return (False);
416 /* we now have a pty */
417 if (pid > 0) { /* This is the parent process */
418 /* Don't need this anymore in parent. */
419 SAFE_FREE(slavedev);
421 if ((chstat = talktochild(master, chatsequence)) == False) {
422 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
423 kill(pid, SIGKILL); /* be sure to end this process */
426 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
427 if (errno == EINTR) {
428 errno = 0;
429 continue;
431 break;
434 if (wpid < 0) {
435 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
436 close(master);
437 CatchChild();
438 return (False);
442 * Go back to ignoring children.
444 CatchChild();
446 close(master);
448 if (pid != wpid) {
449 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
450 return (False);
452 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
453 DEBUG(3, ("chat_with_program: The process exited with status %d \
454 while we were waiting\n", WEXITSTATUS(wstat)));
455 return (False);
457 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
458 else if (WIFSIGNALLED(wstat)) {
459 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
460 while we were waiting\n", WTERMSIG(wstat)));
461 return (False);
463 #endif
464 } else {
465 /* CHILD */
468 * Lose any elevated privileges.
470 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
471 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
473 /* make sure it doesn't freeze */
474 alarm(20);
476 if (as_root)
477 become_root();
479 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
480 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
481 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
483 if (as_root)
484 unbecome_root();
487 * The child should never return from dochild() ....
490 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
491 exit(1);
494 if (chstat)
495 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
496 (chstat ? "" : "un"), pass->pw_name));
497 return (chstat);
500 bool chgpasswd(const char *name, const char *rhost, const struct passwd *pass,
501 const char *oldpass, const char *newpass, bool as_root)
503 char *passwordprogram = NULL;
504 char *chatsequence = NULL;
505 size_t i;
506 size_t len;
507 TALLOC_CTX *ctx = talloc_tos();
509 if (!oldpass) {
510 oldpass = "";
513 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
515 #ifdef DEBUG_PASSWORD
516 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
517 #endif
519 /* Take the passed information and test it for minimum criteria */
521 /* Password is same as old password */
522 if (strcmp(oldpass, newpass) == 0) {
523 /* don't allow same password */
524 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
525 return (False); /* inform the user */
529 * Check the old and new passwords don't contain any control
530 * characters.
533 len = strlen(oldpass);
534 for (i = 0; i < len; i++) {
535 if (iscntrl((int)oldpass[i])) {
536 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
537 return False;
541 len = strlen(newpass);
542 for (i = 0; i < len; i++) {
543 if (iscntrl((int)newpass[i])) {
544 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
545 return False;
549 #ifdef WITH_PAM
550 if (lp_pam_password_change()) {
551 bool ret;
552 #ifdef HAVE_SETLOCALE
553 const char *prevlocale = setlocale(LC_ALL, "C");
554 #endif
556 if (as_root)
557 become_root();
559 if (pass) {
560 ret = smb_pam_passchange(pass->pw_name, rhost,
561 oldpass, newpass);
562 } else {
563 ret = smb_pam_passchange(name, rhost, oldpass,
564 newpass);
567 if (as_root)
568 unbecome_root();
570 #ifdef HAVE_SETLOCALE
571 setlocale(LC_ALL, prevlocale);
572 #endif
574 return ret;
576 #endif
578 /* A non-PAM password change just doen't make sense without a valid local user */
580 if (pass == NULL) {
581 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
582 return false;
585 passwordprogram = lp_passwd_program(ctx);
586 if (!passwordprogram || !*passwordprogram) {
587 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
588 return false;
590 chatsequence = lp_passwd_chat(ctx);
591 if (!chatsequence || !*chatsequence) {
592 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
593 return false;
596 if (as_root) {
597 /* The password program *must* contain the user name to work. Fail if not. */
598 if (strstr_m(passwordprogram, "%u") == NULL) {
599 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
600 the string %%u, and the given string %s does not.\n", passwordprogram ));
601 return false;
605 passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
606 if (!passwordprogram) {
607 return false;
610 /* note that we do NOT substitute the %o and %n in the password program
611 as this would open up a security hole where the user could use
612 a new password containing shell escape characters */
614 chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
615 if (!chatsequence) {
616 return false;
618 chatsequence = talloc_all_string_sub(ctx,
619 chatsequence,
620 "%o",
621 oldpass);
622 if (!chatsequence) {
623 return false;
625 chatsequence = talloc_all_string_sub(ctx,
626 chatsequence,
627 "%n",
628 newpass);
629 return chat_with_program(passwordprogram,
630 pass,
631 chatsequence,
632 as_root);
635 #else /* ALLOW_CHANGE_PASSWORD */
637 bool chgpasswd(const char *name, const struct passwd *pass,
638 const char *oldpass, const char *newpass, bool as_root)
640 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
641 return (False);
643 #endif /* ALLOW_CHANGE_PASSWORD */
645 /***********************************************************
646 Decrypt and verify a user password change.
648 The 516 byte long buffers are encrypted with the old NT and
649 old LM passwords, and if the NT passwords are present, both
650 buffers contain a unicode string.
652 After decrypting the buffers, check the password is correct by
653 matching the old hashed passwords with the passwords in the passdb.
655 ************************************************************/
657 static NTSTATUS check_oem_password(const char *user,
658 uchar password_encrypted_with_lm_hash[516],
659 const uchar old_lm_hash_encrypted[16],
660 uchar password_encrypted_with_nt_hash[516],
661 const uchar old_nt_hash_encrypted[16],
662 struct samu *sampass,
663 char **pp_new_passwd)
665 uchar null_pw[16];
666 uchar null_ntpw[16];
667 uint8 *password_encrypted;
668 const uint8 *encryption_key;
669 const uint8 *lanman_pw, *nt_pw;
670 uint32 acct_ctrl;
671 size_t new_pw_len;
672 uchar new_nt_hash[16];
673 uchar new_lm_hash[16];
674 uchar verifier[16];
675 char no_pw[2];
677 bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
678 bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
680 acct_ctrl = pdb_get_acct_ctrl(sampass);
681 #if 0
682 /* I am convinced this check here is wrong, it is valid to
683 * change a password of a user that has a disabled account - gd */
685 if (acct_ctrl & ACB_DISABLED) {
686 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
687 return NT_STATUS_ACCOUNT_DISABLED;
689 #endif
690 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
691 /* construct a null password (in case one is needed */
692 no_pw[0] = 0;
693 no_pw[1] = 0;
694 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
695 lanman_pw = null_pw;
696 nt_pw = null_pw;
698 } else {
699 /* save pointers to passwords so we don't have to keep looking them up */
700 if (lp_lanman_auth()) {
701 lanman_pw = pdb_get_lanman_passwd(sampass);
702 } else {
703 lanman_pw = NULL;
705 nt_pw = pdb_get_nt_passwd(sampass);
708 if (nt_pw && nt_pass_set) {
709 /* IDEAL Case: passwords are in unicode, and we can
710 * read use the password encrypted with the NT hash
712 password_encrypted = password_encrypted_with_nt_hash;
713 encryption_key = nt_pw;
714 } else if (lanman_pw && lm_pass_set) {
715 /* password may still be in unicode, but use LM hash version */
716 password_encrypted = password_encrypted_with_lm_hash;
717 encryption_key = lanman_pw;
718 } else if (nt_pass_set) {
719 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
720 user));
721 return NT_STATUS_WRONG_PASSWORD;
722 } else if (lm_pass_set) {
723 if (lp_lanman_auth()) {
724 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
725 user));
726 } else {
727 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
728 user));
730 return NT_STATUS_WRONG_PASSWORD;
731 } else {
732 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
733 user));
734 return NT_STATUS_WRONG_PASSWORD;
738 * Decrypt the password with the key
740 arcfour_crypt( password_encrypted, encryption_key, 516);
742 if (!decode_pw_buffer(talloc_tos(),
743 password_encrypted,
744 pp_new_passwd,
745 &new_pw_len,
746 nt_pass_set ? CH_UTF16 : CH_DOS)) {
747 return NT_STATUS_WRONG_PASSWORD;
751 * To ensure we got the correct new password, hash it and
752 * use it as a key to test the passed old password.
755 if (nt_pass_set) {
756 /* NT passwords, verify the NT hash. */
758 /* Calculate the MD4 hash (NT compatible) of the password */
759 memset(new_nt_hash, '\0', 16);
760 E_md4hash(*pp_new_passwd, new_nt_hash);
762 if (nt_pw) {
764 * check the NT verifier
766 E_old_pw_hash(new_nt_hash, nt_pw, verifier);
767 if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
768 DEBUG(0, ("check_oem_password: old nt "
769 "password doesn't match.\n"));
770 return NT_STATUS_WRONG_PASSWORD;
773 /* We could check the LM password here, but there is
774 * little point, we already know the password is
775 * correct, and the LM password might not even be
776 * present. */
778 /* Further, LM hash generation algorithms
779 * differ with charset, so we could
780 * incorrectly fail a perfectly valid password
781 * change */
782 #ifdef DEBUG_PASSWORD
783 DEBUG(100,
784 ("check_oem_password: password %s ok\n", *pp_new_passwd));
785 #endif
786 return NT_STATUS_OK;
789 if (lanman_pw) {
791 * check the lm verifier
793 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
794 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
795 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
796 return NT_STATUS_WRONG_PASSWORD;
798 #ifdef DEBUG_PASSWORD
799 DEBUG(100,
800 ("check_oem_password: password %s ok\n", *pp_new_passwd));
801 #endif
802 return NT_STATUS_OK;
806 if (lanman_pw && lm_pass_set) {
808 E_deshash(*pp_new_passwd, new_lm_hash);
811 * check the lm verifier
813 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
814 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
815 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
816 return NT_STATUS_WRONG_PASSWORD;
819 #ifdef DEBUG_PASSWORD
820 DEBUG(100,
821 ("check_oem_password: password %s ok\n", *pp_new_passwd));
822 #endif
823 return NT_STATUS_OK;
826 /* should not be reached */
827 return NT_STATUS_WRONG_PASSWORD;
830 static bool password_in_history(uint8_t nt_pw[NT_HASH_LEN],
831 uint32_t pw_history_len,
832 const uint8_t *pw_history)
834 static const uint8_t zero_md5_nt_pw[SALTED_MD5_HASH_LEN] = { 0, };
835 int i;
837 dump_data(100, nt_pw, NT_HASH_LEN);
838 dump_data(100, pw_history, PW_HISTORY_ENTRY_LEN * pw_history_len);
840 for (i=0; i<pw_history_len; i++) {
841 uint8_t new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
842 const uint8_t *current_salt;
843 const uint8_t *old_nt_pw_salted_md5_hash;
845 current_salt = &pw_history[i*PW_HISTORY_ENTRY_LEN];
846 old_nt_pw_salted_md5_hash = current_salt + PW_HISTORY_SALT_LEN;
848 if (memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash,
849 SALTED_MD5_HASH_LEN) == 0) {
850 /* Ignore zero valued entries. */
851 continue;
854 if (memcmp(zero_md5_nt_pw, current_salt,
855 PW_HISTORY_SALT_LEN) == 0)
858 * New format: zero salt and then plain nt hash.
859 * Directly compare the hashes.
861 if (memcmp(nt_pw, old_nt_pw_salted_md5_hash,
862 SALTED_MD5_HASH_LEN) == 0)
864 return true;
866 } else {
868 * Old format: md5sum of salted nt hash.
869 * Create salted version of new pw to compare.
871 E_md5hash(current_salt, nt_pw, new_nt_pw_salted_md5_hash);
873 if (memcmp(new_nt_pw_salted_md5_hash,
874 old_nt_pw_salted_md5_hash,
875 SALTED_MD5_HASH_LEN) == 0) {
876 return true;
880 return false;
883 /***********************************************************
884 This routine takes the given password and checks it against
885 the password history. Returns True if this password has been
886 found in the history list.
887 ************************************************************/
889 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
891 uchar new_nt_p16[NT_HASH_LEN];
892 const uint8 *nt_pw;
893 const uint8 *pwhistory;
894 uint32 pwHisLen, curr_pwHisLen;
896 pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &pwHisLen);
897 if (pwHisLen == 0) {
898 return False;
901 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
902 if (!pwhistory || curr_pwHisLen == 0) {
903 return False;
906 /* Only examine the minimum of the current history len and
907 the stored history len. Avoids race conditions. */
908 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
910 nt_pw = pdb_get_nt_passwd(sampass);
912 E_md4hash(plaintext, new_nt_p16);
914 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
915 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
916 pdb_get_username(sampass) ));
917 return True;
920 if (password_in_history(new_nt_p16, pwHisLen, pwhistory)) {
921 DEBUG(1,("check_passwd_history: proposed new password for "
922 "user %s found in history list !\n",
923 pdb_get_username(sampass) ));
924 return true;
926 return false;
929 /***********************************************************
930 ************************************************************/
932 NTSTATUS check_password_complexity(const char *username,
933 const char *password,
934 enum samPwdChangeReason *samr_reject_reason)
936 TALLOC_CTX *tosctx = talloc_tos();
937 int check_ret;
938 char *cmd;
940 /* Use external script to check password complexity */
941 if ((lp_check_password_script(tosctx) == NULL)
942 || (*(lp_check_password_script(tosctx)) == '\0')) {
943 return NT_STATUS_OK;
946 cmd = talloc_string_sub(tosctx, lp_check_password_script(tosctx), "%u",
947 username);
948 if (!cmd) {
949 return NT_STATUS_PASSWORD_RESTRICTION;
952 check_ret = smbrunsecret(cmd, password);
953 DEBUG(5,("check_password_complexity: check password script (%s) "
954 "returned [%d]\n", cmd, check_ret));
955 TALLOC_FREE(cmd);
957 if (check_ret != 0) {
958 DEBUG(1,("check_password_complexity: "
959 "check password script said new password is not good "
960 "enough!\n"));
961 if (samr_reject_reason) {
962 *samr_reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
964 return NT_STATUS_PASSWORD_RESTRICTION;
967 return NT_STATUS_OK;
970 /***********************************************************
971 Code to change the oem password. Changes both the lanman
972 and NT hashes. Old_passwd is almost always NULL.
973 NOTE this function is designed to be called as root. Check the old password
974 is correct before calling. JRA.
975 ************************************************************/
977 static NTSTATUS change_oem_password(struct samu *hnd, const char *rhost,
978 char *old_passwd, char *new_passwd,
979 bool as_root,
980 enum samPwdChangeReason *samr_reject_reason)
982 uint32 min_len;
983 uint32 refuse;
984 TALLOC_CTX *tosctx = talloc_tos();
985 struct passwd *pass = NULL;
986 const char *username = pdb_get_username(hnd);
987 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
988 NTSTATUS status;
990 if (samr_reject_reason) {
991 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
994 /* check to see if the secdesc has previously been set to disallow */
995 if (!pdb_get_pass_can_change(hnd)) {
996 DEBUG(1, ("user %s does not have permissions to change password\n", username));
997 if (samr_reject_reason) {
998 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1000 return NT_STATUS_ACCOUNT_RESTRICTION;
1003 /* check to see if it is a Machine account and if the policy
1004 * denies machines to change the password. *
1005 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1006 if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1007 if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1008 DEBUG(1, ("Machine %s cannot change password now, "
1009 "denied by Refuse Machine Password Change policy\n",
1010 username));
1011 if (samr_reject_reason) {
1012 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1014 return NT_STATUS_ACCOUNT_RESTRICTION;
1018 /* removed calculation here, because passdb now calculates
1019 based on policy. jmcd */
1020 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1021 DEBUG(1, ("user %s cannot change password now, must "
1022 "wait until %s\n", username,
1023 http_timestring(tosctx, can_change_time)));
1024 if (samr_reject_reason) {
1025 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1027 return NT_STATUS_ACCOUNT_RESTRICTION;
1030 if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1031 DEBUG(1, ("user %s cannot change password - password too short\n",
1032 username));
1033 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1034 if (samr_reject_reason) {
1035 *samr_reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1037 return NT_STATUS_PASSWORD_RESTRICTION;
1038 /* return NT_STATUS_PWD_TOO_SHORT; */
1041 if (check_passwd_history(hnd,new_passwd)) {
1042 if (samr_reject_reason) {
1043 *samr_reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1045 return NT_STATUS_PASSWORD_RESTRICTION;
1048 pass = Get_Pwnam_alloc(tosctx, username);
1049 if (!pass) {
1050 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1051 return NT_STATUS_ACCESS_DENIED;
1054 status = check_password_complexity(username, new_passwd, samr_reject_reason);
1055 if (!NT_STATUS_IS_OK(status)) {
1056 TALLOC_FREE(pass);
1057 return status;
1061 * If unix password sync was requested, attempt to change
1062 * the /etc/passwd database first. Return failure if this cannot
1063 * be done.
1065 * This occurs before the oem change, because we don't want to
1066 * update it if chgpasswd failed.
1068 * Conditional on lp_unix_password_sync() because we don't want
1069 * to touch the unix db unless we have admin permission.
1072 if(lp_unix_password_sync() &&
1073 !chgpasswd(username, rhost, pass, old_passwd, new_passwd,
1074 as_root)) {
1075 TALLOC_FREE(pass);
1076 return NT_STATUS_ACCESS_DENIED;
1079 TALLOC_FREE(pass);
1081 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1082 return NT_STATUS_ACCESS_DENIED;
1085 /* Now write it into the file. */
1086 return pdb_update_sam_account (hnd);
1089 /***********************************************************
1090 Code to check and change the OEM hashed password.
1091 ************************************************************/
1093 NTSTATUS pass_oem_change(char *user, const char *rhost,
1094 uchar password_encrypted_with_lm_hash[516],
1095 const uchar old_lm_hash_encrypted[16],
1096 uchar password_encrypted_with_nt_hash[516],
1097 const uchar old_nt_hash_encrypted[16],
1098 enum samPwdChangeReason *reject_reason)
1100 char *new_passwd = NULL;
1101 struct samu *sampass = NULL;
1102 NTSTATUS nt_status;
1103 bool ret = false;
1105 if (!(sampass = samu_new(NULL))) {
1106 return NT_STATUS_NO_MEMORY;
1109 become_root();
1110 ret = pdb_getsampwnam(sampass, user);
1111 unbecome_root();
1113 if (ret == false) {
1114 DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
1115 TALLOC_FREE(sampass);
1116 return NT_STATUS_NO_SUCH_USER;
1119 nt_status = check_oem_password(user,
1120 password_encrypted_with_lm_hash,
1121 old_lm_hash_encrypted,
1122 password_encrypted_with_nt_hash,
1123 old_nt_hash_encrypted,
1124 sampass,
1125 &new_passwd);
1127 if (!NT_STATUS_IS_OK(nt_status)) {
1128 TALLOC_FREE(sampass);
1129 return nt_status;
1132 /* We've already checked the old password here.... */
1133 become_root();
1134 nt_status = change_oem_password(sampass, rhost, NULL, new_passwd,
1135 True, reject_reason);
1136 unbecome_root();
1138 memset(new_passwd, 0, strlen(new_passwd));
1140 TALLOC_FREE(sampass);
1142 return nt_status;