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
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"
57 #include "lib/util/sys_rw.h"
59 #ifndef ALLOW_CHANGE_PASSWORD
60 #if (defined(HAVE_TERMIOS_H) && defined(HAVE_DUP2) && defined(HAVE_SETSID))
61 #define ALLOW_CHANGE_PASSWORD 1
65 #if ALLOW_CHANGE_PASSWORD
67 static int findpty(char **slave)
76 #if defined(HAVE_GRANTPT)
77 #if defined(HAVE_POSIX_OPENPT)
78 master = posix_openpt(O_RDWR|O_NOCTTY);
80 /* Try to open /dev/ptmx. If that fails, fall through to old method. */
81 master = open("/dev/ptmx", O_RDWR, 0);
86 line = (char *)ptsname(master);
88 *slave = SMB_STRDUP(line);
93 ("findpty: Unable to create master/slave pty pair.\n"));
94 /* Stop fd leak on error. */
99 ("findpty: Allocated slave pty %s\n", *slave));
103 #endif /* HAVE_GRANTPT */
105 line = SMB_STRDUP("/dev/ptyXX");
110 dirp = opendir("/dev");
116 while ((dpname = readdirname(dirp)) != NULL) {
117 if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) {
119 ("pty: try to open %s, line was %s\n", dpname,
123 if ((master = open(line, O_RDWR, 0)) >= 0) {
124 DEBUG(3, ("pty: opened %s\n", line));
137 static int dochild(int master, const char *slavedev, const struct passwd *pass,
138 const char *passwordprogram, bool as_root)
141 struct termios stermios;
144 char * const eptrs[1] = { NULL };
149 ("dochild: user doesn't exist in the UNIX password database.\n"));
156 gain_root_privilege();
158 /* Start new session - gets rid of controlling terminal. */
162 ("Weirdness, couldn't let go of controlling terminal\n"));
166 /* Open slave pty and acquire as new controlling terminal. */
167 if ((slave = open(slavedev, O_RDWR, 0)) < 0)
169 DEBUG(3, ("More weirdness, could not open %s\n", slavedev));
172 #if defined(TIOCSCTTY) && !defined(SUNOS5)
174 * On patched Solaris 10 TIOCSCTTY is defined but seems not to work,
175 * see the discussion under
176 * https://bugzilla.samba.org/show_bug.cgi?id=5366.
178 if (ioctl(slave, TIOCSCTTY, 0) < 0)
180 DEBUG(3, ("Error in ioctl call for slave pty\n"));
183 #elif defined(I_PUSH) && defined(I_FIND)
184 if (ioctl(slave, I_FIND, "ptem") == 0) {
185 ioctl(slave, I_PUSH, "ptem");
187 if (ioctl(slave, I_FIND, "ldterm") == 0) {
188 ioctl(slave, I_PUSH, "ldterm");
195 /* Make slave stdin/out/err of child. */
197 if (dup2(slave, STDIN_FILENO) != STDIN_FILENO)
199 DEBUG(3, ("Could not re-direct stdin\n"));
202 if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO)
204 DEBUG(3, ("Could not re-direct stdout\n"));
207 if (dup2(slave, STDERR_FILENO) != STDERR_FILENO)
209 DEBUG(3, ("Could not re-direct stderr\n"));
215 /* Set proper terminal attributes - no echo, canonical input processing,
216 no map NL to CR/NL on output. */
218 if (tcgetattr(0, &stermios) < 0)
221 ("could not read default terminal attributes on pty\n"));
224 stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
225 stermios.c_lflag |= ICANON;
227 stermios.c_oflag &= ~(ONLCR);
229 if (tcsetattr(0, TCSANOW, &stermios) < 0)
231 DEBUG(3, ("could not set attributes of pty\n"));
235 /* make us completely into the right uid */
238 become_user_permanently(uid, gid);
242 ("Invoking '%s' as password change program.\n",
245 /* execl() password-change application */
246 if (execle("/bin/sh", "sh", "-c", passwordprogram, NULL, eptrs) < 0)
248 DEBUG(3, ("Bad status returned from %s\n", passwordprogram));
254 static int expect(int master, char *issue, char *expected)
257 int attempts, timeout, nread;
261 for (attempts = 0; attempts < 2; attempts++) {
263 if (!strequal(issue, ".")) {
264 if (lp_passwd_chat_debug())
265 DEBUG(100, ("expect: sending [%s]\n", issue));
267 if ((len = sys_write(master, issue, strlen(issue))) != strlen(issue)) {
268 DEBUG(2,("expect: (short) write returned %d\n",
274 if (strequal(expected, "."))
277 /* Initial timeout. */
278 timeout = lp_passwd_chat_timeout() * 1000;
283 status = read_fd_with_timeout(
284 master, buffer + nread, 1,
285 sizeof(buffer) - nread - 1,
288 if (!NT_STATUS_IS_OK(status)) {
289 DEBUG(2, ("expect: read error %s\n",
297 /* Eat leading/trailing whitespace before match. */
298 char *str = SMB_STRDUP(buffer);
300 DEBUG(2,("expect: ENOMEM\n"));
303 trim_char(str, ' ', ' ');
305 if ((match = unix_wild_match(expected, str)) == True) {
306 /* Now data has started to return, lower timeout. */
307 timeout = lp_passwd_chat_timeout() * 100;
313 if (lp_passwd_chat_debug())
314 DEBUG(100, ("expect: expected [%s] received [%s] match %s\n",
315 expected, buffer, match ? "yes" : "no" ));
320 if (!NT_STATUS_IS_OK(status)) {
321 DEBUG(2, ("expect: %s\n", nt_errstr(status)));
326 DEBUG(10,("expect: returning %s\n", match ? "True" : "False" ));
330 static void pwd_sub(char *buf)
332 all_string_sub(buf, "\\n", "\n", 0);
333 all_string_sub(buf, "\\r", "\r", 0);
334 all_string_sub(buf, "\\s", " ", 0);
335 all_string_sub(buf, "\\t", "\t", 0);
338 static int talktochild(int master, const char *seq)
340 TALLOC_CTX *frame = talloc_stackframe();
345 issue = talloc_strdup(frame, ".");
351 while (next_token_talloc(frame, &seq, &expected, NULL)) {
355 if (!expect(master, issue, expected)) {
356 DEBUG(3, ("Response %d incorrect\n", count));
361 if (!next_token_talloc(frame, &seq, &issue, NULL)) {
362 issue = talloc_strdup(frame, ".");
371 if (!strequal(issue, ".")) {
372 /* we have one final issue to send */
373 expected = talloc_strdup(frame, ".");
378 if (!expect(master, issue, expected)) {
387 static bool chat_with_program(char *passwordprogram, const struct passwd *pass,
388 char *chatsequence, bool as_root)
390 char *slavedev = NULL;
395 void (*saved_handler)(int);
398 DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n"));
402 /* allocate a pseudo-terminal device */
403 if ((master = findpty(&slavedev)) < 0) {
404 DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name));
409 * We need to temporarily stop CatchChild from eating
410 * SIGCLD signals as it also eats the exit status code. JRA.
413 saved_handler = CatchChildLeaveStatus();
415 if ((pid = fork()) < 0) {
416 DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name));
419 (void)CatchSignal(SIGCLD, saved_handler);
423 /* we now have a pty */
424 if (pid > 0) { /* This is the parent process */
425 /* Don't need this anymore in parent. */
428 if ((chstat = talktochild(master, chatsequence)) == False) {
429 DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name));
430 kill(pid, SIGKILL); /* be sure to end this process */
433 while ((wpid = waitpid(pid, &wstat, 0)) < 0) {
434 if (errno == EINTR) {
442 DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n"));
444 (void)CatchSignal(SIGCLD, saved_handler);
449 * Go back to ignoring children.
451 (void)CatchSignal(SIGCLD, saved_handler);
456 DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n"));
459 if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) {
460 DEBUG(3, ("chat_with_program: The process exited with status %d \
461 while we were waiting\n", WEXITSTATUS(wstat)));
464 #if defined(WIFSIGNALLED) && defined(WTERMSIG)
465 else if (WIFSIGNALLED(wstat)) {
466 DEBUG(3, ("chat_with_program: The process was killed by signal %d \
467 while we were waiting\n", WTERMSIG(wstat)));
475 * Lose any elevated privileges.
477 drop_effective_capability(KERNEL_OPLOCK_CAPABILITY);
478 drop_effective_capability(DMAPI_ACCESS_CAPABILITY);
480 /* make sure it doesn't freeze */
486 DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name,
487 (int)getuid(), (int)getgid(), BOOLSTR(as_root) ));
488 chstat = dochild(master, slavedev, pass, passwordprogram, as_root);
494 * The child should never return from dochild() ....
497 DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat));
502 DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n",
503 (chstat ? "" : "un"), pass->pw_name));
507 bool chgpasswd(const char *name, const char *rhost, const struct passwd *pass,
508 const char *oldpass, const char *newpass, bool as_root)
510 char *passwordprogram = NULL;
511 char *chatsequence = NULL;
514 TALLOC_CTX *ctx = talloc_tos();
520 DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name));
522 #ifdef DEBUG_PASSWORD
523 DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass));
526 /* Take the passed information and test it for minimum criteria */
528 /* Password is same as old password */
529 if (strcmp(oldpass, newpass) == 0) {
530 /* don't allow same password */
531 DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */
532 return (False); /* inform the user */
536 * Check the old and new passwords don't contain any control
540 len = strlen(oldpass);
541 for (i = 0; i < len; i++) {
542 if (iscntrl((int)oldpass[i])) {
543 DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n"));
548 len = strlen(newpass);
549 for (i = 0; i < len; i++) {
550 if (iscntrl((int)newpass[i])) {
551 DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n"));
557 if (lp_pam_password_change()) {
559 #ifdef HAVE_SETLOCALE
560 const char *prevlocale = setlocale(LC_ALL, "C");
567 ret = smb_pam_passchange(pass->pw_name, rhost,
570 ret = smb_pam_passchange(name, rhost, oldpass,
577 #ifdef HAVE_SETLOCALE
578 setlocale(LC_ALL, prevlocale);
585 /* A non-PAM password change just doen't make sense without a valid local user */
588 DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name));
592 passwordprogram = lp_passwd_program(ctx);
593 if (!passwordprogram || !*passwordprogram) {
594 DEBUG(2, ("chgpasswd: Null password program - no password changing\n"));
597 chatsequence = lp_passwd_chat(ctx);
598 if (!chatsequence || !*chatsequence) {
599 DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n"));
604 /* The password program *must* contain the user name to work. Fail if not. */
605 if (strstr_m(passwordprogram, "%u") == NULL) {
606 DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \
607 the string %%u, and the given string %s does not.\n", passwordprogram ));
612 passwordprogram = talloc_string_sub(ctx, passwordprogram, "%u", name);
613 if (!passwordprogram) {
617 /* note that we do NOT substitute the %o and %n in the password program
618 as this would open up a security hole where the user could use
619 a new password containing shell escape characters */
621 chatsequence = talloc_string_sub(ctx, chatsequence, "%u", name);
625 chatsequence = talloc_all_string_sub(ctx,
632 chatsequence = talloc_all_string_sub(ctx,
636 return chat_with_program(passwordprogram,
642 #else /* ALLOW_CHANGE_PASSWORD */
644 bool chgpasswd(const char *name, const struct passwd *pass,
645 const char *oldpass, const char *newpass, bool as_root)
647 DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name));
650 #endif /* ALLOW_CHANGE_PASSWORD */
652 /***********************************************************
653 Decrypt and verify a user password change.
655 The 516 byte long buffers are encrypted with the old NT and
656 old LM passwords, and if the NT passwords are present, both
657 buffers contain a unicode string.
659 After decrypting the buffers, check the password is correct by
660 matching the old hashed passwords with the passwords in the passdb.
662 ************************************************************/
664 static NTSTATUS check_oem_password(const char *user,
665 uchar password_encrypted_with_lm_hash[516],
666 const uchar old_lm_hash_encrypted[16],
667 uchar password_encrypted_with_nt_hash[516],
668 const uchar old_nt_hash_encrypted[16],
669 struct samu *sampass,
670 char **pp_new_passwd)
674 uint8_t *password_encrypted;
675 const uint8_t *encryption_key;
676 const uint8_t *lanman_pw, *nt_pw;
679 uchar new_nt_hash[16];
680 uchar new_lm_hash[16];
684 bool nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted);
685 bool lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted);
686 enum ntlm_auth_level ntlm_auth_level = lp_ntlm_auth();
688 /* this call should be disabled without NTLM auth */
689 if (ntlm_auth_level == NTLM_AUTH_DISABLED) {
690 DBG_WARNING("NTLM password changes not"
691 "permitted by configuration.\n");
692 return NT_STATUS_NTLM_BLOCKED;
695 acct_ctrl = pdb_get_acct_ctrl(sampass);
697 /* I am convinced this check here is wrong, it is valid to
698 * change a password of a user that has a disabled account - gd */
700 if (acct_ctrl & ACB_DISABLED) {
701 DEBUG(2,("check_lanman_password: account %s disabled.\n", user));
702 return NT_STATUS_ACCOUNT_DISABLED;
705 if ((acct_ctrl & ACB_PWNOTREQ) && lp_null_passwords()) {
706 /* construct a null password (in case one is needed */
709 nt_lm_owf_gen(no_pw, null_ntpw, null_pw);
714 /* save pointers to passwords so we don't have to keep looking them up */
715 if (lp_lanman_auth()) {
716 lanman_pw = pdb_get_lanman_passwd(sampass);
720 nt_pw = pdb_get_nt_passwd(sampass);
723 if (nt_pw && nt_pass_set) {
724 /* IDEAL Case: passwords are in unicode, and we can
725 * read use the password encrypted with the NT hash
727 password_encrypted = password_encrypted_with_nt_hash;
728 encryption_key = nt_pw;
729 } else if (lanman_pw && lm_pass_set) {
730 /* password may still be in unicode, but use LM hash version */
731 password_encrypted = password_encrypted_with_lm_hash;
732 encryption_key = lanman_pw;
733 } else if (nt_pass_set) {
734 DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n",
736 return NT_STATUS_WRONG_PASSWORD;
737 } else if (lm_pass_set) {
738 if (lp_lanman_auth()) {
739 DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n",
742 DEBUG(1, ("LM password change supplied for user %s, but we have disabled LanMan authentication\n",
745 return NT_STATUS_WRONG_PASSWORD;
747 DEBUG(1, ("password change requested for user %s, but no password supplied!\n",
749 return NT_STATUS_WRONG_PASSWORD;
753 * Decrypt the password with the key
755 arcfour_crypt( password_encrypted, encryption_key, 516);
757 if (!decode_pw_buffer(talloc_tos(),
761 nt_pass_set ? CH_UTF16 : CH_DOS)) {
762 return NT_STATUS_WRONG_PASSWORD;
766 * To ensure we got the correct new password, hash it and
767 * use it as a key to test the passed old password.
771 /* NT passwords, verify the NT hash. */
773 /* Calculate the MD4 hash (NT compatible) of the password */
774 memset(new_nt_hash, '\0', 16);
775 E_md4hash(*pp_new_passwd, new_nt_hash);
779 * check the NT verifier
781 E_old_pw_hash(new_nt_hash, nt_pw, verifier);
782 if (memcmp(verifier, old_nt_hash_encrypted, 16)) {
783 DEBUG(0, ("check_oem_password: old nt "
784 "password doesn't match.\n"));
785 return NT_STATUS_WRONG_PASSWORD;
788 /* We could check the LM password here, but there is
789 * little point, we already know the password is
790 * correct, and the LM password might not even be
793 /* Further, LM hash generation algorithms
794 * differ with charset, so we could
795 * incorrectly fail a perfectly valid password
797 #ifdef DEBUG_PASSWORD
799 ("check_oem_password: password %s ok\n", *pp_new_passwd));
806 * check the lm verifier
808 E_old_pw_hash(new_nt_hash, lanman_pw, verifier);
809 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
810 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
811 return NT_STATUS_WRONG_PASSWORD;
813 #ifdef DEBUG_PASSWORD
815 ("check_oem_password: password %s ok\n", *pp_new_passwd));
821 if (lanman_pw && lm_pass_set) {
823 E_deshash(*pp_new_passwd, new_lm_hash);
826 * check the lm verifier
828 E_old_pw_hash(new_lm_hash, lanman_pw, verifier);
829 if (memcmp(verifier, old_lm_hash_encrypted, 16)) {
830 DEBUG(0,("check_oem_password: old lm password doesn't match.\n"));
831 return NT_STATUS_WRONG_PASSWORD;
834 #ifdef DEBUG_PASSWORD
836 ("check_oem_password: password %s ok\n", *pp_new_passwd));
841 /* should not be reached */
842 return NT_STATUS_WRONG_PASSWORD;
845 static bool password_in_history(uint8_t nt_pw[NT_HASH_LEN],
846 uint32_t pw_history_len,
847 const uint8_t *pw_history)
851 dump_data(100, nt_pw, NT_HASH_LEN);
852 dump_data(100, pw_history, PW_HISTORY_ENTRY_LEN * pw_history_len);
854 for (i=0; i<pw_history_len; i++) {
855 uint8_t new_nt_pw_salted_md5_hash[SALTED_MD5_HASH_LEN];
856 const uint8_t *current_salt;
857 const uint8_t *old_nt_pw_salted_md5_hash;
859 current_salt = &pw_history[i*PW_HISTORY_ENTRY_LEN];
860 old_nt_pw_salted_md5_hash = current_salt + PW_HISTORY_SALT_LEN;
862 if (all_zero(old_nt_pw_salted_md5_hash, SALTED_MD5_HASH_LEN)) {
863 /* Ignore zero valued entries. */
867 if (all_zero(current_salt, PW_HISTORY_SALT_LEN)) {
869 * New format: zero salt and then plain nt hash.
870 * Directly compare the hashes.
872 if (memcmp(nt_pw, old_nt_pw_salted_md5_hash,
873 SALTED_MD5_HASH_LEN) == 0)
879 * Old format: md5sum of salted nt hash.
880 * Create salted version of new pw to compare.
882 E_md5hash(current_salt, nt_pw, new_nt_pw_salted_md5_hash);
884 if (memcmp(new_nt_pw_salted_md5_hash,
885 old_nt_pw_salted_md5_hash,
886 SALTED_MD5_HASH_LEN) == 0) {
894 /***********************************************************
895 This routine takes the given password and checks it against
896 the password history. Returns True if this password has been
897 found in the history list.
898 ************************************************************/
900 static bool check_passwd_history(struct samu *sampass, const char *plaintext)
902 uchar new_nt_p16[NT_HASH_LEN];
903 const uint8_t *nt_pw;
904 const uint8_t *pwhistory;
905 uint32_t pwHisLen, curr_pwHisLen;
907 pdb_get_account_policy(PDB_POLICY_PASSWORD_HISTORY, &pwHisLen);
912 pwhistory = pdb_get_pw_history(sampass, &curr_pwHisLen);
913 if (!pwhistory || curr_pwHisLen == 0) {
917 /* Only examine the minimum of the current history len and
918 the stored history len. Avoids race conditions. */
919 pwHisLen = MIN(pwHisLen,curr_pwHisLen);
921 nt_pw = pdb_get_nt_passwd(sampass);
923 E_md4hash(plaintext, new_nt_p16);
925 if (!memcmp(nt_pw, new_nt_p16, NT_HASH_LEN)) {
926 DEBUG(10,("check_passwd_history: proposed new password for user %s is the same as the current password !\n",
927 pdb_get_username(sampass) ));
931 if (password_in_history(new_nt_p16, pwHisLen, pwhistory)) {
932 DEBUG(1,("check_passwd_history: proposed new password for "
933 "user %s found in history list !\n",
934 pdb_get_username(sampass) ));
940 /***********************************************************
941 ************************************************************/
943 NTSTATUS check_password_complexity(const char *username,
944 const char *password,
945 enum samPwdChangeReason *samr_reject_reason)
947 TALLOC_CTX *tosctx = talloc_tos();
951 /* Use external script to check password complexity */
952 if ((lp_check_password_script(tosctx) == NULL)
953 || (*(lp_check_password_script(tosctx)) == '\0')) {
957 cmd = talloc_string_sub(tosctx, lp_check_password_script(tosctx), "%u",
960 return NT_STATUS_PASSWORD_RESTRICTION;
963 check_ret = smbrunsecret(cmd, password);
964 DEBUG(5,("check_password_complexity: check password script (%s) "
965 "returned [%d]\n", cmd, check_ret));
968 if (check_ret != 0) {
969 DEBUG(1,("check_password_complexity: "
970 "check password script said new password is not good "
972 if (samr_reject_reason) {
973 *samr_reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
975 return NT_STATUS_PASSWORD_RESTRICTION;
981 /***********************************************************
982 Code to change the oem password. Changes both the lanman
983 and NT hashes. Old_passwd is almost always NULL.
984 NOTE this function is designed to be called as root. Check the old password
985 is correct before calling. JRA.
986 ************************************************************/
988 static NTSTATUS change_oem_password(struct samu *hnd, const char *rhost,
989 char *old_passwd, char *new_passwd,
991 enum samPwdChangeReason *samr_reject_reason)
995 TALLOC_CTX *tosctx = talloc_tos();
996 struct passwd *pass = NULL;
997 const char *username = pdb_get_username(hnd);
998 time_t can_change_time = pdb_get_pass_can_change_time(hnd);
1001 if (samr_reject_reason) {
1002 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1005 /* check to see if the secdesc has previously been set to disallow */
1006 if (!pdb_get_pass_can_change(hnd)) {
1007 DEBUG(1, ("user %s does not have permissions to change password\n", username));
1008 if (samr_reject_reason) {
1009 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1011 return NT_STATUS_ACCOUNT_RESTRICTION;
1014 /* check to see if it is a Machine account and if the policy
1015 * denies machines to change the password. *
1016 * Should we deny also SRVTRUST and/or DOMSTRUST ? .SSS. */
1017 if (pdb_get_acct_ctrl(hnd) & ACB_WSTRUST) {
1018 if (pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE, &refuse) && refuse) {
1019 DEBUG(1, ("Machine %s cannot change password now, "
1020 "denied by Refuse Machine Password Change policy\n",
1022 if (samr_reject_reason) {
1023 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1025 return NT_STATUS_ACCOUNT_RESTRICTION;
1029 /* removed calculation here, because passdb now calculates
1030 based on policy. jmcd */
1031 if ((can_change_time != 0) && (time(NULL) < can_change_time)) {
1032 DEBUG(1, ("user %s cannot change password now, must "
1033 "wait until %s\n", username,
1034 http_timestring(tosctx, can_change_time)));
1035 if (samr_reject_reason) {
1036 *samr_reject_reason = SAM_PWD_CHANGE_NO_ERROR;
1038 return NT_STATUS_ACCOUNT_RESTRICTION;
1041 if (pdb_get_account_policy(PDB_POLICY_MIN_PASSWORD_LEN, &min_len) && (str_charnum(new_passwd) < min_len)) {
1042 DEBUG(1, ("user %s cannot change password - password too short\n",
1044 DEBUGADD(1, (" account policy min password len = %d\n", min_len));
1045 if (samr_reject_reason) {
1046 *samr_reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
1048 return NT_STATUS_PASSWORD_RESTRICTION;
1049 /* return NT_STATUS_PWD_TOO_SHORT; */
1052 if (check_passwd_history(hnd,new_passwd)) {
1053 if (samr_reject_reason) {
1054 *samr_reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
1056 return NT_STATUS_PASSWORD_RESTRICTION;
1059 pass = Get_Pwnam_alloc(tosctx, username);
1061 DEBUG(1, ("change_oem_password: Username %s does not exist in system !?!\n", username));
1062 return NT_STATUS_ACCESS_DENIED;
1065 status = check_password_complexity(username, new_passwd, samr_reject_reason);
1066 if (!NT_STATUS_IS_OK(status)) {
1072 * If unix password sync was requested, attempt to change
1073 * the /etc/passwd database first. Return failure if this cannot
1076 * This occurs before the oem change, because we don't want to
1077 * update it if chgpasswd failed.
1079 * Conditional on lp_unix_password_sync() because we don't want
1080 * to touch the unix db unless we have admin permission.
1083 if(lp_unix_password_sync() &&
1084 !chgpasswd(username, rhost, pass, old_passwd, new_passwd,
1087 return NT_STATUS_ACCESS_DENIED;
1092 if (!pdb_set_plaintext_passwd (hnd, new_passwd)) {
1093 return NT_STATUS_ACCESS_DENIED;
1096 /* Now write it into the file. */
1097 return pdb_update_sam_account (hnd);
1100 /***********************************************************
1101 Code to check and change the OEM hashed password.
1102 ************************************************************/
1104 NTSTATUS pass_oem_change(char *user, const char *rhost,
1105 uchar password_encrypted_with_lm_hash[516],
1106 const uchar old_lm_hash_encrypted[16],
1107 uchar password_encrypted_with_nt_hash[516],
1108 const uchar old_nt_hash_encrypted[16],
1109 enum samPwdChangeReason *reject_reason)
1111 char *new_passwd = NULL;
1112 struct samu *sampass = NULL;
1115 bool updated_badpw = false;
1116 NTSTATUS update_login_attempts_status;
1118 if (!(sampass = samu_new(NULL))) {
1119 return NT_STATUS_NO_MEMORY;
1123 ret = pdb_getsampwnam(sampass, user);
1127 DEBUG(0,("pass_oem_change: getsmbpwnam returned NULL\n"));
1128 TALLOC_FREE(sampass);
1129 return NT_STATUS_NO_SUCH_USER;
1132 /* Quit if the account was locked out. */
1133 if (pdb_get_acct_ctrl(sampass) & ACB_AUTOLOCK) {
1134 DEBUG(3,("check_sam_security: Account for user %s was locked out.\n", user));
1135 TALLOC_FREE(sampass);
1136 return NT_STATUS_ACCOUNT_LOCKED_OUT;
1139 nt_status = check_oem_password(user,
1140 password_encrypted_with_lm_hash,
1141 old_lm_hash_encrypted,
1142 password_encrypted_with_nt_hash,
1143 old_nt_hash_encrypted,
1148 * Notify passdb backend of login success/failure. If not
1149 * NT_STATUS_OK the backend doesn't like the login
1151 update_login_attempts_status = pdb_update_login_attempts(sampass,
1152 NT_STATUS_IS_OK(nt_status));
1154 if (!NT_STATUS_IS_OK(nt_status)) {
1155 bool increment_bad_pw_count = false;
1157 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) &&
1158 (pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
1159 NT_STATUS_IS_OK(update_login_attempts_status))
1161 increment_bad_pw_count = true;
1164 if (increment_bad_pw_count) {
1165 pdb_increment_bad_password_count(sampass);
1166 updated_badpw = true;
1168 pdb_update_bad_password_count(sampass,
1173 if ((pdb_get_acct_ctrl(sampass) & ACB_NORMAL) &&
1174 (pdb_get_bad_password_count(sampass) > 0)){
1175 pdb_set_bad_password_count(sampass, 0, PDB_CHANGED);
1176 pdb_set_bad_password_time(sampass, 0, PDB_CHANGED);
1177 updated_badpw = true;
1181 if (updated_badpw) {
1182 NTSTATUS update_status;
1184 update_status = pdb_update_sam_account(sampass);
1187 if (!NT_STATUS_IS_OK(update_status)) {
1188 DEBUG(1, ("Failed to modify entry: %s\n",
1189 nt_errstr(update_status)));
1193 if (!NT_STATUS_IS_OK(nt_status)) {
1194 TALLOC_FREE(sampass);
1198 /* We've already checked the old password here.... */
1200 nt_status = change_oem_password(sampass, rhost, NULL, new_passwd,
1201 True, reject_reason);
1204 memset(new_passwd, 0, strlen(new_passwd));
1206 TALLOC_FREE(sampass);