s3: use monotonic clock for time deltas in namequery functions
[Samba/ita.git] / source3 / rpc_server / srv_samr_chgpasswd.c
blobd31215b321b403dd18071e308364ce5b9b434f0c
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"
51 #include "rpc_server/srv_samr_util.h"
53 #if ALLOW_CHANGE_PASSWORD
55 static int findpty(char **slave)
57 int master = -1;
58 char *line = NULL;
59 SMB_STRUCT_DIR *dirp = NULL;
60 const char *dpname;
62 *slave = NULL;
64 #if defined(HAVE_GRANTPT)
65 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
66 if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) {
67 grantpt(master);
68 unlockpt(master);
69 line = (char *)ptsname(master);
70 if (line) {
71 *slave = SMB_STRDUP(line);
74 if (*slave == NULL) {
75 DEBUG(0,
76 ("findpty: Unable to create master/slave pty pair.\n"));
77 /* Stop fd leak on error. */
78 close(master);
79 return -1;
80 } else {
81 DEBUG(10,
82 ("findpty: Allocated slave pty %s\n", *slave));
83 return (master);
86 #endif /* HAVE_GRANTPT */
88 line = SMB_STRDUP("/dev/ptyXX");
89 if (!line) {
90 return (-1);
93 dirp = sys_opendir("/dev");
94 if (!dirp) {
95 SAFE_FREE(line);
96 return (-1);
99 while ((dpname = readdirname(dirp)) != NULL) {
100 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
101 DEBUG(3,
102 ("pty: try to open %s, line was %s\n", dpname,
103 line));
104 line[8] = dpname[3];
105 line[9] = dpname[4];
106 if ((master = sys_open(line, O_RDWR, 0)) >= 0) {
107 DEBUG(3, ("pty: opened %s\n", line));
108 line[5] = 't';
109 *slave = line;
110 sys_closedir(dirp);
111 return (master);
115 sys_closedir(dirp);
116 SAFE_FREE(line);
117 return (-1);
120 static int dochild(int master, const char *slavedev, const struct passwd *pass,
121 const char *passwordprogram, bool as_root)
123 int slave;
124 struct termios stermios;
125 gid_t gid;
126 uid_t uid;
127 char * const eptrs[1] = { NULL };
129 if (pass == NULL)
131 DEBUG(0,
132 ("dochild: user doesn't exist in the UNIX password database.\n"));
133 return False;
136 gid = pass->pw_gid;
137 uid = pass->pw_uid;
139 gain_root_privilege();
141 /* Start new session - gets rid of controlling terminal. */
142 if (setsid() < 0)
144 DEBUG(3,
145 ("Weirdness, couldn't let go of controlling terminal\n"));
146 return (False);
149 /* Open slave pty and acquire as new controlling terminal. */
150 if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0)
152 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
153 return (False);
155 #if defined(TIOCSCTTY) && !defined(SUNOS5)
157 * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
158 * see the discussion under
159 * https://bugzilla.samba.org/show_bug.cgi?id=5366.
161 if (ioctl(slave, TIOCSCTTY, 0) < 0)
163 DEBUG(3, ("Error in ioctl call for slave pty\n"));
164 /* return(False); */
166 #elif defined(I_PUSH) && defined(I_FIND)
167 if (ioctl(slave, I_FIND, "ptem") == 0) {
168 ioctl(slave, I_PUSH, "ptem");
170 if (ioctl(slave, I_FIND, "ldterm") == 0) {
171 ioctl(slave, I_PUSH, "ldterm");
173 #endif
175 /* Close master. */
176 close(master);
178 /* Make slave stdin/out/err of child. */
180 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
182 DEBUG(3, ("Could not re-direct stdin\n"));
183 return (False);
185 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
187 DEBUG(3, ("Could not re-direct stdout\n"));
188 return (False);
190 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
192 DEBUG(3, ("Could not re-direct stderr\n"));
193 return (False);
195 if (slave > 2)
196 close(slave);
198 /* Set proper terminal attributes - no echo, canonical input processing,
199 no map NL to CR/NL on output. */
201 if (tcgetattr(0, &stermios) < 0)
203 DEBUG(3,
204 ("could not read default terminal attributes on pty\n"));
205 return (False);
207 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
208 stermios.c_lflag |= ICANON;
209 #ifdef ONLCR
210 stermios.c_oflag &= ~(ONLCR);
211 #endif
212 if (tcsetattr(0, TCSANOW, &stermios) < 0)
214 DEBUG(3, ("could not set attributes of pty\n"));
215 return (False);
218 /* make us completely into the right uid */
219 if (!as_root)
221 become_user_permanently(uid, gid);
224 DEBUG(10,
225 ("Invoking '%s' as password change program.\n",
226 passwordprogram));
228 /* execl() password-change application */
229 if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
231 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
232 return (False);
234 return (True);
237 static int expect(int master, char *issue, char *expected)
239 char buffer[1024];
240 int attempts, timeout, nread;
241 size_t len;
242 bool match = False;
244 for (attempts = 0; attempts < 2; attempts++) {
245 NTSTATUS status;
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",
252 (int)len ));
253 return False;
257 if (strequal(expected, "."))
258 return True;
260 /* Initial timeout. */
261 timeout = lp_passwd_chat_timeout() * 1000;
262 nread = 0;
263 buffer[nread] = 0;
265 while (True) {
266 status = read_fd_with_timeout(
267 master, buffer + nread, 1,
268 sizeof(buffer) - nread - 1,
269 timeout, &len);
271 if (!NT_STATUS_IS_OK(status)) {
272 DEBUG(2, ("expect: read error %s\n",
273 nt_errstr(status)));
274 break;
276 nread += len;
277 buffer[nread] = 0;
280 /* Eat leading/trailing whitespace before match. */
281 char *str = SMB_STRDUP(buffer);
282 if (!str) {
283 DEBUG(2,("expect: ENOMEM\n"));
284 return False;
286 trim_char(str, ' ', ' ');
288 if ((match = unix_wild_match(expected, str)) == True) {
289 /* Now data has started to return, lower timeout. */
290 timeout = lp_passwd_chat_timeout() * 100;
292 SAFE_FREE(str);
296 if (lp_passwd_chat_debug())
297 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
298 expected, buffer, match ? "yes" : "no" ));
300 if (match)
301 break;
303 if (!NT_STATUS_IS_OK(status)) {
304 DEBUG(2, ("expect: %s\n", nt_errstr(status)));
305 return False;
309 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
310 return match;
313 static void pwd_sub(char *buf)
315 all_string_sub(buf, "\\n", "\n", 0);
316 all_string_sub(buf, "\\r", "\r", 0);
317 all_string_sub(buf, "\\s", " ", 0);
318 all_string_sub(buf, "\\t", "\t", 0);
321 static int talktochild(int master, const char *seq)
323 TALLOC_CTX *frame = talloc_stackframe();
324 int count = 0;
325 char *issue;
326 char *expected;
328 issue = talloc_strdup(frame, ".");
329 if (!issue) {
330 TALLOC_FREE(frame);
331 return false;
334 while (next_token_talloc(frame, &seq, &expected, NULL)) {
335 pwd_sub(expected);
336 count++;
338 if (!expect(master, issue, expected)) {
339 DEBUG(3, ("Response %d incorrect\n", count));
340 TALLOC_FREE(frame);
341 return false;
344 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
345 issue = talloc_strdup(frame, ".");
346 if (!issue) {
347 TALLOC_FREE(frame);
348 return false;
351 pwd_sub(issue);
354 if (!strequal(issue, ".")) {
355 /* we have one final issue to send */
356 expected = talloc_strdup(frame, ".");
357 if (!expected) {
358 TALLOC_FREE(frame);
359 return false;
361 if (!expect(master, issue, expected)) {
362 TALLOC_FREE(frame);
363 return False;
366 TALLOC_FREE(frame);
367 return (count > 0);
370 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
371 char *chatsequence, bool as_root)
373 char *slavedev = NULL;
374 int master;
375 pid_t pid, wpid;
376 int wstat;
377 bool chstat = False;
379 if (pass == NULL) {
380 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
381 return False;
384 /* allocate a pseudo-terminal device */
385 if ((master = findpty(&slavedev)) < 0) {
386 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
387 return (False);
391 * We need to temporarily stop CatchChild from eating
392 * SIGCLD signals as it also eats the exit status code. JRA.
395 CatchChildLeaveStatus();
397 if ((pid = sys_fork()) < 0) {
398 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
399 SAFE_FREE(slavedev);
400 close(master);
401 CatchChild();
402 return (False);
405 /* we now have a pty */
406 if (pid > 0) { /* This is the parent process */
407 /* Don't need this anymore in parent. */
408 SAFE_FREE(slavedev);
410 if ((chstat = talktochild(master, chatsequence)) == False) {
411 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
412 kill(pid, SIGKILL); /* be sure to end this process */
415 while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) {
416 if (errno == EINTR) {
417 errno = 0;
418 continue;
420 break;
423 if (wpid < 0) {
424 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
425 close(master);
426 CatchChild();
427 return (False);
431 * Go back to ignoring children.
433 CatchChild();
435 close(master);
437 if (pid != wpid) {
438 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
439 return (False);
441 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
442 DEBUG(3, ("chat_with_program: The process exited with status %d \
443 while we were waiting\n", WEXITSTATUS(wstat)));
444 return (False);
446 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
447 else if (WIFSIGNALLED(wstat)) {
448 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
449 while we were waiting\n", WTERMSIG(wstat)));
450 return (False);
452 #endif
453 } else {
454 /* CHILD */
457 * Lose any elevated privileges.
459 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
460 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
462 /* make sure it doesn't freeze */
463 alarm(20);
465 if (as_root)
466 become_root();
468 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
469 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
470 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
472 if (as_root)
473 unbecome_root();
476 * The child should never return from dochild() ....
479 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
480 exit(1);
483 if (chstat)
484 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
485 (chstat ? "" : "un"), pass->pw_name));
486 return (chstat);
489 bool chgpasswd(const char *name, const char *rhost, const struct passwd *pass,
490 const char *oldpass, const char *newpass, bool as_root)
492 char *passwordprogram = NULL;
493 char *chatsequence = NULL;
494 size_t i;
495 size_t len;
496 TALLOC_CTX *ctx = talloc_tos();
498 if (!oldpass) {
499 oldpass = "";
502 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
504 #ifdef DEBUG_PASSWORD
505 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
506 #endif
508 /* Take the passed information and test it for minimum criteria */
510 /* Password is same as old password */
511 if (strcmp(oldpass, newpass) == 0) {
512 /* don't allow same password */
513 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
514 return (False); /* inform the user */
518 * Check the old and new passwords don't contain any control
519 * characters.
522 len = strlen(oldpass);
523 for (i = 0; i < len; i++) {
524 if (iscntrl((int)oldpass[i])) {
525 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
526 return False;
530 len = strlen(newpass);
531 for (i = 0; i < len; i++) {
532 if (iscntrl((int)newpass[i])) {
533 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
534 return False;
538 #ifdef WITH_PAM
539 if (lp_pam_password_change()) {
540 bool ret;
541 #ifdef HAVE_SETLOCALE
542 const char *prevlocale = setlocale(LC_ALL, "C");
543 #endif
545 if (as_root)
546 become_root();
548 if (pass) {
549 ret = smb_pam_passchange(pass->pw_name, rhost,
550 oldpass, newpass);
551 } else {
552 ret = smb_pam_passchange(name, rhost, oldpass,
553 newpass);
556 if (as_root)
557 unbecome_root();
559 #ifdef HAVE_SETLOCALE
560 setlocale(LC_ALL, prevlocale);
561 #endif
563 return ret;
565 #endif
567 /* A non-PAM password change just doen't make sense without a valid local user */
569 if (pass == NULL) {
570 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
571 return false;
574 passwordprogram = talloc_strdup(ctx, lp_passwd_program());
575 if (!passwordprogram || !*passwordprogram) {
576 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
577 return false;
579 chatsequence = talloc_strdup(ctx, lp_passwd_chat());
580 if (!chatsequence || !*chatsequence) {
581 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
582 return false;
585 if (as_root) {
586 /* The password program *must* contain the user name to work. Fail if not. */
587 if (strstr_m(passwordprogram, "%u") == NULL) {
588 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
589 the string %%u, and the given string %s does not.\n", passwordprogram ));
590 return false;
594 passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
595 if (!passwordprogram) {
596 return false;
599 /* note that we do NOT substitute the %o and %n in the password program
600 as this would open up a security hole where the user could use
601 a new password containing shell escape characters */
603 chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
604 if (!chatsequence) {
605 return false;
607 chatsequence = talloc_all_string_sub(ctx,
608 chatsequence,
609 "%o",
610 oldpass);
611 if (!chatsequence) {
612 return false;
614 chatsequence = talloc_all_string_sub(ctx,
615 chatsequence,
616 "%n",
617 newpass);
618 return chat_with_program(passwordprogram,
619 pass,
620 chatsequence,
621 as_root);
624 #else /* ALLOW_CHANGE_PASSWORD */
626 bool chgpasswd(const char *name, const struct passwd *pass,
627 const char *oldpass, const char *newpass, bool as_root)
629 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
630 return (False);
632 #endif /* ALLOW_CHANGE_PASSWORD */
634 /***********************************************************
635 Decrypt and verify a user password change.
637 The 516 byte long buffers are encrypted with the old NT and
638 old LM passwords, and if the NT passwords are present, both
639 buffers contain a unicode string.
641 After decrypting the buffers, check the password is correct by
642 matching the old hashed passwords with the passwords in the passdb.
644 ************************************************************/
646 static NTSTATUS check_oem_password(const char *user,
647 uchar password_encrypted_with_lm_hash[516],
648 const uchar old_lm_hash_encrypted[16],
649 uchar password_encrypted_with_nt_hash[516],
650 const uchar old_nt_hash_encrypted[16],
651 struct samu *sampass,
652 char **pp_new_passwd)
654 uchar null_pw[16];
655 uchar null_ntpw[16];
656 uint8 *password_encrypted;
657 const uint8 *encryption_key;
658 const uint8 *lanman_pw, *nt_pw;
659 uint32 acct_ctrl;
660 size_t new_pw_len;
661 uchar new_nt_hash[16];
662 uchar new_lm_hash[16];
663 uchar verifier[16];
664 char no_pw[2];
666 bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
667 bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
669 acct_ctrl = pdb_get_acct_ctrl(sampass);
670 #if 0
671 /* I am convinced this check here is wrong, it is valid to
672 * change a password of a user that has a disabled account - gd */
674 if (acct_ctrl & ACB_DISABLED) {
675 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
676 return NT_STATUS_ACCOUNT_DISABLED;
678 #endif
679 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
680 /* construct a null password (in case one is needed */
681 no_pw[0] = 0;
682 no_pw[1] = 0;
683 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
684 lanman_pw = null_pw;
685 nt_pw = null_pw;
687 } else {
688 /* save pointers to passwords so we don't have to keep looking them up */
689 if (lp_lanman_auth()) {
690 lanman_pw = pdb_get_lanman_passwd(sampass);
691 } else {
692 lanman_pw = NULL;
694 nt_pw = pdb_get_nt_passwd(sampass);
697 if (nt_pw && nt_pass_set) {
698 /* IDEAL Case: passwords are in unicode, and we can
699 * read use the password encrypted with the NT hash
701 password_encrypted = password_encrypted_with_nt_hash;
702 encryption_key = nt_pw;
703 } else if (lanman_pw && lm_pass_set) {
704 /* password may still be in unicode, but use LM hash version */
705 password_encrypted = password_encrypted_with_lm_hash;
706 encryption_key = lanman_pw;
707 } else if (nt_pass_set) {
708 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
709 user));
710 return NT_STATUS_WRONG_PASSWORD;
711 } else if (lm_pass_set) {
712 if (lp_lanman_auth()) {
713 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
714 user));
715 } else {
716 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
717 user));
719 return NT_STATUS_WRONG_PASSWORD;
720 } else {
721 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
722 user));
723 return NT_STATUS_WRONG_PASSWORD;
727 * Decrypt the password with the key
729 arcfour_crypt( password_encrypted, encryption_key, 516);
731 if (!decode_pw_buffer(talloc_tos(),
732 password_encrypted,
733 pp_new_passwd,
734 &new_pw_len,
735 nt_pass_set ? CH_UTF16 : CH_DOS)) {
736 return NT_STATUS_WRONG_PASSWORD;
740 * To ensure we got the correct new password, hash it and
741 * use it as a key to test the passed old password.
744 if (nt_pass_set) {
745 /* NT passwords, verify the NT hash. */
747 /* Calculate the MD4 hash (NT compatible) of the password */
748 memset(new_nt_hash, '\0', 16);
749 E_md4hash(*pp_new_passwd, new_nt_hash);
751 if (nt_pw) {
753 * check the NT verifier
755 E_old_pw_hash(new_nt_hash, nt_pw, verifier);
756 if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
757 DEBUG(0, ("check_oem_password: old nt "
758 "password doesn't match.\n"));
759 return NT_STATUS_WRONG_PASSWORD;
762 /* We could check the LM password here, but there is
763 * little point, we already know the password is
764 * correct, and the LM password might not even be
765 * present. */
767 /* Further, LM hash generation algorithms
768 * differ with charset, so we could
769 * incorrectly fail a perfectly valid password
770 * change */
771 #ifdef DEBUG_PASSWORD
772 DEBUG(100,
773 ("check_oem_password: password %s ok\n", *pp_new_passwd));
774 #endif
775 return NT_STATUS_OK;
778 if (lanman_pw) {
780 * check the lm verifier
782 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
783 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
784 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
785 return NT_STATUS_WRONG_PASSWORD;
787 #ifdef DEBUG_PASSWORD
788 DEBUG(100,
789 ("check_oem_password: password %s ok\n", *pp_new_passwd));
790 #endif
791 return NT_STATUS_OK;
795 if (lanman_pw && lm_pass_set) {
797 E_deshash(*pp_new_passwd, new_lm_hash);
800 * check the lm verifier
802 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
803 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
804 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
805 return NT_STATUS_WRONG_PASSWORD;
808 #ifdef DEBUG_PASSWORD
809 DEBUG(100,
810 ("check_oem_password: password %s ok\n", *pp_new_passwd));
811 #endif
812 return NT_STATUS_OK;
815 /* should not be reached */
816 return NT_STATUS_WRONG_PASSWORD;
819 static bool password_in_history(uint8_t nt_pw[NT_HASH_LEN],
820 uint32_t pw_history_len,
821 const uint8_t *pw_history)
823 static const uint8_t zero_md5_nt_pw[SALTED_MD5_HASH_LEN] = { 0, };
824 int i;
826 dump_data(100, nt_pw, NT_HASH_LEN);
827 dump_data(100, pw_history, PW_HISTORY_ENTRY_LEN * pw_history_len);
829 for (i=0; i<pw_history_len; i++) {
830 uint8_t new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
831 const uint8_t *current_salt;
832 const uint8_t *old_nt_pw_salted_md5_hash;
834 current_salt = &pw_history[i*PW_HISTORY_ENTRY_LEN];
835 old_nt_pw_salted_md5_hash = current_salt + PW_HISTORY_SALT_LEN;
837 if (memcmp(zero_md5_nt_pw, old_nt_pw_salted_md5_hash,
838 SALTED_MD5_HASH_LEN) == 0) {
839 /* Ignore zero valued entries. */
840 continue;
843 if (memcmp(zero_md5_nt_pw, current_salt,
844 PW_HISTORY_SALT_LEN) == 0)
847 * New format: zero salt and then plain nt hash.
848 * Directly compare the hashes.
850 if (memcmp(nt_pw, old_nt_pw_salted_md5_hash,
851 SALTED_MD5_HASH_LEN) == 0)
853 return true;
855 } else {
857 * Old format: md5sum of salted nt hash.
858 * Create salted version of new pw to compare.
860 E_md5hash(current_salt, nt_pw, new_nt_pw_salted_md5_hash);
862 if (memcmp(new_nt_pw_salted_md5_hash,
863 old_nt_pw_salted_md5_hash,
864 SALTED_MD5_HASH_LEN) == 0) {
865 return true;
869 return false;
872 /***********************************************************
873 This routine takes the given password and checks it against
874 the password history. Returns True if this password has been
875 found in the history list.
876 ************************************************************/
878 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
880 uchar new_nt_p16[NT_HASH_LEN];
881 const uint8 *nt_pw;
882 const uint8 *pwhistory;
883 uint32 pwHisLen, curr_pwHisLen;
885 pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &pwHisLen);
886 if (pwHisLen == 0) {
887 return False;
890 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
891 if (!pwhistory || curr_pwHisLen == 0) {
892 return False;
895 /* Only examine the minimum of the current history len and
896 the stored history len. Avoids race conditions. */
897 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
899 nt_pw = pdb_get_nt_passwd(sampass);
901 E_md4hash(plaintext, new_nt_p16);
903 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
904 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
905 pdb_get_username(sampass) ));
906 return True;
909 if (password_in_history(new_nt_p16, pwHisLen, pwhistory)) {
910 DEBUG(1,("check_passwd_history: proposed new password for "
911 "user %s found in history list !\n",
912 pdb_get_username(sampass) ));
913 return true;
915 return false;
918 /***********************************************************
919 ************************************************************/
921 NTSTATUS check_password_complexity(const char *username,
922 const char *password,
923 enum samPwdChangeReason *samr_reject_reason)
925 TALLOC_CTX *tosctx = talloc_tos();
926 int check_ret;
927 char *cmd;
929 /* Use external script to check password complexity */
930 if ((lp_check_password_script() == NULL)
931 || (*(lp_check_password_script()) == '\0')) {
932 return NT_STATUS_OK;
935 cmd = talloc_string_sub(tosctx, lp_check_password_script(), "%u",
936 username);
937 if (!cmd) {
938 return NT_STATUS_PASSWORD_RESTRICTION;
941 check_ret = smbrunsecret(cmd, password);
942 DEBUG(5,("check_password_complexity: check password script (%s) "
943 "returned [%d]\n", cmd, check_ret));
944 TALLOC_FREE(cmd);
946 if (check_ret != 0) {
947 DEBUG(1,("check_password_complexity: "
948 "check password script said new password is not good "
949 "enough!\n"));
950 if (samr_reject_reason) {
951 *samr_reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
953 return NT_STATUS_PASSWORD_RESTRICTION;
956 return NT_STATUS_OK;
959 /***********************************************************
960 Code to change the oem password. Changes both the lanman
961 and NT hashes. Old_passwd is almost always NULL.
962 NOTE this function is designed to be called as root. Check the old password
963 is correct before calling. JRA.
964 ************************************************************/
966 static NTSTATUS change_oem_password(struct samu *hnd, const char *rhost,
967 char *old_passwd, char *new_passwd,
968 bool as_root,
969 enum samPwdChangeReason *samr_reject_reason)
971 uint32 min_len;
972 uint32 refuse;
973 TALLOC_CTX *tosctx = talloc_tos();
974 struct passwd *pass = NULL;
975 const char *username = pdb_get_username(hnd);
976 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
977 NTSTATUS status;
979 if (samr_reject_reason) {
980 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
983 /* check to see if the secdesc has previously been set to disallow */
984 if (!pdb_get_pass_can_change(hnd)) {
985 DEBUG(1, ("user %s does not have permissions to change password\n", username));
986 if (samr_reject_reason) {
987 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
989 return NT_STATUS_ACCOUNT_RESTRICTION;
992 /* check to see if it is a Machine account and if the policy
993 * denies machines to change the password. *
994 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
995 if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
996 if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
997 DEBUG(1, ("Machine %s cannot change password now, "
998 "denied by Refuse Machine Password Change policy\n",
999 username));
1000 if (samr_reject_reason) {
1001 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1003 return NT_STATUS_ACCOUNT_RESTRICTION;
1007 /* removed calculation here, because passdb now calculates
1008 based on policy. jmcd */
1009 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1010 DEBUG(1, ("user %s cannot change password now, must "
1011 "wait until %s\n", username,
1012 http_timestring(tosctx, can_change_time)));
1013 if (samr_reject_reason) {
1014 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1016 return NT_STATUS_ACCOUNT_RESTRICTION;
1019 if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1020 DEBUG(1, ("user %s cannot change password - password too short\n",
1021 username));
1022 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1023 if (samr_reject_reason) {
1024 *samr_reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1026 return NT_STATUS_PASSWORD_RESTRICTION;
1027 /* return NT_STATUS_PWD_TOO_SHORT; */
1030 if (check_passwd_history(hnd,new_passwd)) {
1031 if (samr_reject_reason) {
1032 *samr_reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1034 return NT_STATUS_PASSWORD_RESTRICTION;
1037 pass = Get_Pwnam_alloc(tosctx, username);
1038 if (!pass) {
1039 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1040 return NT_STATUS_ACCESS_DENIED;
1043 status = check_password_complexity(username, new_passwd, samr_reject_reason);
1044 if (!NT_STATUS_IS_OK(status)) {
1045 TALLOC_FREE(pass);
1046 return status;
1050 * If unix password sync was requested, attempt to change
1051 * the /etc/passwd database first. Return failure if this cannot
1052 * be done.
1054 * This occurs before the oem change, because we don't want to
1055 * update it if chgpasswd failed.
1057 * Conditional on lp_unix_password_sync() because we don't want
1058 * to touch the unix db unless we have admin permission.
1061 if(lp_unix_password_sync() &&
1062 !chgpasswd(username, rhost, pass, old_passwd, new_passwd,
1063 as_root)) {
1064 TALLOC_FREE(pass);
1065 return NT_STATUS_ACCESS_DENIED;
1068 TALLOC_FREE(pass);
1070 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1071 return NT_STATUS_ACCESS_DENIED;
1074 /* Now write it into the file. */
1075 return pdb_update_sam_account (hnd);
1078 /***********************************************************
1079 Code to check and change the OEM hashed password.
1080 ************************************************************/
1082 NTSTATUS pass_oem_change(char *user, const char *rhost,
1083 uchar password_encrypted_with_lm_hash[516],
1084 const uchar old_lm_hash_encrypted[16],
1085 uchar password_encrypted_with_nt_hash[516],
1086 const uchar old_nt_hash_encrypted[16],
1087 enum samPwdChangeReason *reject_reason)
1089 char *new_passwd = NULL;
1090 struct samu *sampass = NULL;
1091 NTSTATUS nt_status;
1092 bool ret = false;
1094 if (!(sampass = samu_new(NULL))) {
1095 return NT_STATUS_NO_MEMORY;
1098 become_root();
1099 ret = pdb_getsampwnam(sampass, user);
1100 unbecome_root();
1102 if (ret == false) {
1103 DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
1104 TALLOC_FREE(sampass);
1105 return NT_STATUS_NO_SUCH_USER;
1108 nt_status = check_oem_password(user,
1109 password_encrypted_with_lm_hash,
1110 old_lm_hash_encrypted,
1111 password_encrypted_with_nt_hash,
1112 old_nt_hash_encrypted,
1113 sampass,
1114 &new_passwd);
1116 if (!NT_STATUS_IS_OK(nt_status)) {
1117 TALLOC_FREE(sampass);
1118 return nt_status;
1121 /* We've already checked the old password here.... */
1122 become_root();
1123 nt_status = change_oem_password(sampass, rhost, NULL, new_passwd,
1124 True, reject_reason);
1125 unbecome_root();
1127 memset(new_passwd, 0, strlen(new_passwd));
1129 TALLOC_FREE(sampass);
1131 return nt_status;